From 43b230685fc7376535c62e07327acc240d499984 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 5 Nov 2015 00:44:14 +0900 Subject: [PATCH] Introduce NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE --- lib/includes/nghttp2/nghttp2.h | 84 +++++++++++++++++++++++++++++----- lib/nghttp2_frame.c | 56 +++++++++++++++-------- 2 files changed, 111 insertions(+), 29 deletions(-) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 9aecc1dc..b4d79429 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -430,7 +430,19 @@ typedef enum { * Header Field never Indexed" representation must be used in HPACK * encoding). Other implementation calls this bit as "sensitive". */ - NGHTTP2_NV_FLAG_NO_INDEX = 0x01 + NGHTTP2_NV_FLAG_NO_INDEX = 0x01, + /** + * This flag is set solely by application. If this flag is set, the + * library does not make a copy of header field name. This could + * improve performance. + */ + NGHTTP2_NV_FLAG_NO_COPY_NAME = 0x02, + /** + * This flag is set solely by application. If this flag is set, the + * library does not make a copy of header field value. This could + * improve performance. + */ + NGHTTP2_NV_FLAG_NO_COPY_VALUE = 0x04 } nghttp2_nv_flag; /** @@ -442,17 +454,27 @@ typedef struct { /** * The |name| byte string. If this struct is presented from library * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is - * guaranteed to be NULL-terminated. When application is - * constructing this struct, |name| is not required to be + * guaranteed to be NULL-terminated. For some callbacks + * (:type:`nghttp2_before_frame_send_callback`, + * :type:`nghttp2_on_frame_send_callback`, and + * :type:`nghttp2_on_frame_not_send_callback`), it may not be + * NULL-terminated if header field is passed from application with + * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`). When application + * is constructing this struct, |name| is not required to be * NULL-terminated. */ uint8_t *name; /** * The |value| byte string. If this struct is presented from * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value| - * is guaranteed to be NULL-terminated. When application is - * constructing this struct, |value| is not required to be - * NULL-terminated. + * is guaranteed to be NULL-terminated. For some callbacks + * (:type:`nghttp2_before_frame_send_callback`, + * :type:`nghttp2_on_frame_send_callback`, and + * :type:`nghttp2_on_frame_not_send_callback`), it may not be + * NULL-terminated if header field is passed from application with + * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE`). When + * application is constructing this struct, |value| is not required + * to be NULL-terminated. */ uint8_t *value; /** @@ -2960,7 +2982,15 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. * * HTTP/2 specification has requirement about header fields in the * request HEADERS. See the specification for more details. @@ -3016,7 +3046,15 @@ NGHTTP2_EXTERN int32_t * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. * * HTTP/2 specification has requirement about header fields in the * response HEADERS. See the specification for more details. @@ -3073,7 +3111,15 @@ nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. * * For server, trailer must be followed by response HEADERS or * response DATA. The library does not check that response HEADERS @@ -3149,7 +3195,15 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. * * The |stream_user_data| is a pointer to an arbitrary data which is * associated to the stream this frame will open. Therefore it is @@ -3347,7 +3401,15 @@ NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. * * The |promised_stream_user_data| is a pointer to an arbitrary data * which is associated to the promised stream this frame will open and diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c index e324b9c9..30b4f396 100644 --- a/lib/nghttp2_frame.c +++ b/lib/nghttp2_frame.c @@ -741,21 +741,26 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { size_t i; - uint8_t *data; + uint8_t *data = NULL; size_t buflen = 0; nghttp2_nv *p; - for (i = 0; i < nvlen; ++i) { - /* + 2 for null-termination */ - buflen += nva[i].namelen + nva[i].valuelen + 2; - } - if (nvlen == 0) { *nva_ptr = NULL; return 0; } + for (i = 0; i < nvlen; ++i) { + /* + 1 for null-termination */ + if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) { + buflen += nva[i].namelen + 1; + } + if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) { + buflen += nva[i].valuelen + 1; + } + } + buflen += sizeof(nghttp2_nv) * nvlen; *nva_ptr = nghttp2_mem_malloc(mem, buflen); @@ -765,22 +770,37 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, } p = *nva_ptr; - data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; + + if (buflen > sizeof(nghttp2_nv) * nvlen) { + data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; + } for (i = 0; i < nvlen; ++i) { p->flags = nva[i].flags; - memcpy(data, nva[i].name, nva[i].namelen); - p->name = data; - p->namelen = nva[i].namelen; - data[p->namelen] = '\0'; - nghttp2_downcase(p->name, p->namelen); - data += nva[i].namelen + 1; - memcpy(data, nva[i].value, nva[i].valuelen); - p->value = data; - p->valuelen = nva[i].valuelen; - data[p->valuelen] = '\0'; - data += nva[i].valuelen + 1; + if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) { + p->name = nva[i].name; + p->namelen = nva[i].namelen; + } else { + memcpy(data, nva[i].name, nva[i].namelen); + p->name = data; + p->namelen = nva[i].namelen; + data[p->namelen] = '\0'; + nghttp2_downcase(p->name, p->namelen); + data += nva[i].namelen + 1; + } + + if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) { + p->value = nva[i].value; + p->valuelen = nva[i].valuelen; + } else { + memcpy(data, nva[i].value, nva[i].valuelen); + p->value = data; + p->valuelen = nva[i].valuelen; + data[p->valuelen] = '\0'; + data += nva[i].valuelen + 1; + } + ++p; } return 0;