diff options
author | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2014-08-02 16:22:28 +0900 |
---|---|---|
committer | Alexis La Goutte <alexis.lagoutte@gmail.com> | 2014-08-03 14:20:43 +0000 |
commit | af6ea5722375327460f12c0ff6a6389c4f92a7ed (patch) | |
tree | 27c2dc66251d5868dcc9aa78f7c26540bd26fa2b /epan/nghttp2 | |
parent | 5ced8933b9d2352e98da70d76c6f6deb8b001fbc (diff) |
http2: Update to h2-14
* Expand frame length field to 24 bits
* Add new SETTINGS:
- SETTINGS_MAX_FRAME_SIZE
- SETTINGS_MAX_HEADER_LIST_SIZE
* Update libnghttp2 HPACK
* Remove END_SEGMENT flag
Change-Id: I5906322ad5a4d61c963ed95fada9415e66e146da
Reviewed-on: https://code.wireshark.org/review/3357
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
Diffstat (limited to 'epan/nghttp2')
-rw-r--r-- | epan/nghttp2/nghttp2.h | 104 | ||||
-rw-r--r-- | epan/nghttp2/nghttp2_hd.c | 795 | ||||
-rw-r--r-- | epan/nghttp2/nghttp2_hd.h | 44 | ||||
-rw-r--r-- | epan/nghttp2/nghttp2_helper.c | 2 | ||||
-rw-r--r-- | epan/nghttp2/nghttp2ver.h | 4 |
5 files changed, 368 insertions, 581 deletions
diff --git a/epan/nghttp2/nghttp2.h b/epan/nghttp2/nghttp2.h index 6c1f03b55d..0334fcfcac 100644 --- a/epan/nghttp2/nghttp2.h +++ b/epan/nghttp2/nghttp2.h @@ -43,7 +43,7 @@ extern "C" { * The protocol version identification string of this library * supports. This identifier is used if HTTP/2 is used over TLS. */ -#define NGHTTP2_PROTO_VERSION_ID "h2-13" +#define NGHTTP2_PROTO_VERSION_ID "h2-14" /** * @macro * @@ -58,7 +58,7 @@ extern "C" { * supports. This identifier is used if HTTP/2 is used over cleartext * TCP. */ -#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c-13" +#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c-14" /** * @macro @@ -479,10 +479,6 @@ typedef enum { */ NGHTTP2_FLAG_ACK = 0x01, /** - * The END_SEGMENT flag. - */ - NGHTTP2_FLAG_END_SEGMENT = 0x02, - /** * The PADDED flag. */ NGHTTP2_FLAG_PADDED = 0x08, @@ -512,7 +508,15 @@ typedef enum { /** * SETTINGS_INITIAL_WINDOW_SIZE */ - NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04 + NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04, + /** + * SETTINGS_MAX_FRAME_SIZE + */ + NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05, + /** + * SETTINGS_MAX_HEADER_LIST_SIZE + */ + NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06 } nghttp2_settings_id; /* Note: If we add SETTINGS, update the capacity of NGHTTP2_INBOUND_NUM_IV as well */ @@ -726,7 +730,9 @@ typedef enum { NGHTTP2_HCAT_PUSH_RESPONSE = 2, /** * The HEADERS frame which does not apply for the above categories, - * which is analogous to HEADERS in SPDY. + * which is analogous to HEADERS in SPDY. If non-final response + * (e.g., status 1xx) is used, final response HEADERS frame will be + * categorized here. */ NGHTTP2_HCAT_HEADERS = 3 } nghttp2_headers_category; @@ -1511,27 +1517,13 @@ void nghttp2_option_del(nghttp2_option *option); * @function * * This option prevents the library from sending WINDOW_UPDATE for a - * stream automatically. If this option is set to nonzero, the - * library won't send WINDOW_UPDATE for a stream and the application - * is responsible for sending WINDOW_UPDATE using - * `nghttp2_submit_window_update`. By default, this option is set to - * zero. - */ -void nghttp2_option_set_no_auto_stream_window_update(nghttp2_option *option, - int val); - -/** - * @function - * - * This option prevents the library from sending WINDOW_UPDATE for a * connection automatically. If this option is set to nonzero, the - * library won't send WINDOW_UPDATE for a connection and the - * application is responsible for sending WINDOW_UPDATE with stream ID - * 0 using `nghttp2_submit_window_update`. By default, this option is - * set to zero. + * library won't send WINDOW_UPDATE for DATA until application calls + * `nghttp2_session_consume()` to indicate the consumed amount of + * data. Don't use `nghttp2_submit_window_update()` for this purpose. + * By default, this option is set to zero. */ -void nghttp2_option_set_no_auto_connection_window_update -(nghttp2_option *option, int val); +void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); /** * @function @@ -2072,6 +2064,28 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, /** * @function * + * Tells the |session| that |size| bytes for a stream denoted by + * |stream_id| were consumed by application and are ready to + * WINDOW_UPDATE. This function is intended to be used without + * automatic window update (see + * `nghttp2_option_set_no_auto_window_update()`). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, + size_t size); + +/** + * @function + * * Performs post-process of HTTP Upgrade request. This function can * be called from both client and server, but the behavior is very * different in each other. @@ -2368,8 +2382,7 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, * Submits one or more DATA frames to the stream |stream_id|. The * data to be sent are provided by |data_prd|. If |flags| contains * :enum:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM - * flag set. If |flags| contains :enum:`NGHTTP2_FLAG_END_SEGMENT`, - * the last DATA frame has END_SEGMENT flag set. + * flag set. * * This function does not take ownership of the |data_prd|. The * function copies the members of the |data_prd|. @@ -2611,12 +2624,11 @@ int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, * difference. * * If the |window_size_increment| is negative, the local window size - * is decreased by -|window_size_increment|. If - * :enum:`NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE` (or - * :enum:`NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE` if |stream_id| - * is 0) is not set and the library decided that the WINDOW_UPDATE - * should be submitted, then WINDOW_UPDATE is queued with the current - * received bytes count. + * is decreased by -|window_size_increment|. If automatic + * WINDOW_UPDATE is enabled + * (`nghttp2_option_set_no_auto_window_update()`), and the library + * decided that the WINDOW_UPDATE should be submitted, then + * WINDOW_UPDATE is queued with the current received bytes count. * * If the |window_size_increment| is 0, the function does nothing and * returns 0. @@ -2643,7 +2655,7 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, * * Only the server can send the ALTSVC frame. If |session| is * initialized as client, this function fails and returns - * :enum:`NGHTTP2_ERR_INVALID_STATE`. + * :enum:`NGHTTP2_ERR_PROTO`. * * If the |protocol_id_len| is 0, the |protocol_id| could be ``NULL``. * @@ -2764,8 +2776,7 @@ int nghttp2_is_fatal(int lib_error); * @function * * Returns nonzero if HTTP header field name |name| of length |len| is - * valid according to - * http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-25#section-3.2 + * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 * * Because this is a header field name in HTTP2, the upper cased alphabet * is treated as error. @@ -2777,10 +2788,7 @@ int nghttp2_check_header_name(const uint8_t *name, size_t len); * * Returns nonzero if HTTP header field value |value| of length |len| * is valid according to - * http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-25#section-3.2 - * - * Because this is HTTP2 header field value, it can contain NULL - * character (0x00). + * http://tools.ietf.org/html/rfc7230#section-3.2 */ int nghttp2_check_header_value(const uint8_t *value, size_t len); @@ -2824,18 +2832,6 @@ void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); /** * @function * - * Sets the availability of reference set in the |deflater|. If - * |no_refset| is nonzero, the deflater will first emit "Reference Set - * Emptying" in the each subsequent invocation of - * `nghttp2_hd_deflate_hd()` to clear up reference set. By default, - * the deflater uses reference set. - */ -void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater, - uint8_t no_refset); - -/** - * @function - * * Changes header table size of the |deflater| to * |settings_hd_table_bufsize_max| bytes. This may trigger eviction * in the dynamic table. diff --git a/epan/nghttp2/nghttp2_hd.c b/epan/nghttp2/nghttp2_hd.c index 8234fc1f13..0c2a9ecdef 100644 --- a/epan/nghttp2/nghttp2_hd.c +++ b/epan/nghttp2/nghttp2_hd.c @@ -32,84 +32,75 @@ #include "nghttp2_int.h" /* Make scalar initialization form of nghttp2_nv */ -#define MAKE_STATIC_ENT(I, N, V, NH, VH) \ - { { { (uint8_t*)N, (uint8_t*)V, sizeof(N) - 1, sizeof(V) - 1, 0 }, \ - NH, VH, 1, NGHTTP2_HD_FLAG_NONE }, I } - -/* Sorted by hash(name) and its table index */ -static nghttp2_hd_static_entry static_table[] = { - MAKE_STATIC_ENT(20, "age", "", 96511u, 0u), - MAKE_STATIC_ENT(59, "via", "", 116750u, 0u), - MAKE_STATIC_ENT(32, "date", "", 3076014u, 0u), - MAKE_STATIC_ENT(33, "etag", "", 3123477u, 0u), - MAKE_STATIC_ENT(36, "from", "", 3151786u, 0u), - MAKE_STATIC_ENT(37, "host", "", 3208616u, 0u), - MAKE_STATIC_ENT(44, "link", "", 3321850u, 0u), - MAKE_STATIC_ENT(58, "vary", "", 3612210u, 0u), - MAKE_STATIC_ENT(38, "if-match", "", 34533653u, 0u), - MAKE_STATIC_ENT(41, "if-range", "", 39145613u, 0u), - MAKE_STATIC_ENT(3, ":path", "/", 56997727u, 47u), - MAKE_STATIC_ENT(4, ":path", "/index.html", 56997727u, 2144181430u), - MAKE_STATIC_ENT(21, "allow", "", 92906313u, 0u), - MAKE_STATIC_ENT(49, "range", "", 108280125u, 0u), - MAKE_STATIC_ENT(14, "accept-charset", "", 124285319u, 0u), - MAKE_STATIC_ENT(43, "last-modified", "", 150043680u, 0u), - MAKE_STATIC_ENT(48, "proxy-authorization", "", 329532250u, 0u), - MAKE_STATIC_ENT(57, "user-agent", "", 486342275u, 0u), - MAKE_STATIC_ENT(40, "if-none-match", "", 646073760u, 0u), - MAKE_STATIC_ENT(30, "content-type", "", 785670158u, 0u), - MAKE_STATIC_ENT(16, "accept-language", "", 802785917u, 0u), - MAKE_STATIC_ENT(50, "referer", "", 1085069613u, 0u), - MAKE_STATIC_ENT(51, "refresh", "", 1085444827u, 0u), - MAKE_STATIC_ENT(55, "strict-transport-security", "", 1153852136u, 0u), - MAKE_STATIC_ENT(54, "set-cookie", "", 1237214767u, 0u), - MAKE_STATIC_ENT(56, "transfer-encoding", "", 1274458357u, 0u), - MAKE_STATIC_ENT(17, "accept-ranges", "", 1397189435u, 0u), - MAKE_STATIC_ENT(42, "if-unmodified-since", "", 1454068927u, 0u), - MAKE_STATIC_ENT(46, "max-forwards", "", 1619948695u, 0u), - MAKE_STATIC_ENT(45, "location", "", 1901043637u, 0u), - MAKE_STATIC_ENT(52, "retry-after", "", 1933352567u, 0u), - MAKE_STATIC_ENT(25, "content-encoding", "", 2095084583u, 0u), - MAKE_STATIC_ENT(28, "content-location", "", 2284906121u, 0u), - MAKE_STATIC_ENT(39, "if-modified-since", "", 2302095846u, 0u), - MAKE_STATIC_ENT(18, "accept", "", 2871506184u, 0u), - MAKE_STATIC_ENT(29, "content-range", "", 2878374633u, 0u), - MAKE_STATIC_ENT(22, "authorization", "", 2909397113u, 0u), - MAKE_STATIC_ENT(31, "cookie", "", 2940209764u, 0u), - MAKE_STATIC_ENT(0, ":authority", "", 2962729033u, 0u), - MAKE_STATIC_ENT(35, "expires", "", 2985731892u, 0u), - MAKE_STATIC_ENT(34, "expect", "", 3005803609u, 0u), - MAKE_STATIC_ENT(24, "content-disposition", "", 3027699811u, 0u), - MAKE_STATIC_ENT(26, "content-language", "", 3065240108u, 0u), - MAKE_STATIC_ENT(1, ":method", "GET", 3153018267u, 70454u), - MAKE_STATIC_ENT(2, ":method", "POST", 3153018267u, 2461856u), - MAKE_STATIC_ENT(27, "content-length", "", 3162187450u, 0u), - MAKE_STATIC_ENT(19, "access-control-allow-origin", "", 3297999203u, 0u), - MAKE_STATIC_ENT(5, ":scheme", "http", 3322585695u, 3213448u), - MAKE_STATIC_ENT(6, ":scheme", "https", 3322585695u, 99617003u), - MAKE_STATIC_ENT(7, ":status", "200", 3338091692u, 49586u), - MAKE_STATIC_ENT(8, ":status", "204", 3338091692u, 49590u), - MAKE_STATIC_ENT(9, ":status", "206", 3338091692u, 49592u), - MAKE_STATIC_ENT(10, ":status", "304", 3338091692u, 50551u), - MAKE_STATIC_ENT(11, ":status", "400", 3338091692u, 51508u), - MAKE_STATIC_ENT(12, ":status", "404", 3338091692u, 51512u), - MAKE_STATIC_ENT(13, ":status", "500", 3338091692u, 52469u), - MAKE_STATIC_ENT(53, "server", "", 3389140803u, 0u), - MAKE_STATIC_ENT(47, "proxy-authenticate", "", 3993199572u, 0u), - MAKE_STATIC_ENT(60, "www-authenticate", "", 4051929931u, 0u), - MAKE_STATIC_ENT(23, "cache-control", "", 4086191634u, 0u), - MAKE_STATIC_ENT(15, "accept-encoding", "gzip, deflate", 4127597688u, 1733326877u), +#define MAKE_ENT(N, V, NH, VH) \ + { { (uint8_t*)N, (uint8_t*)V, sizeof(N) - 1, sizeof(V) - 1, 0}, \ + NH, VH, 1, NGHTTP2_HD_FLAG_NONE } + +static nghttp2_hd_entry static_table[] = { + MAKE_ENT(":authority", "", 2962729033u, 0u), + MAKE_ENT(":method", "GET", 3153018267u, 70454u), + MAKE_ENT(":method", "POST", 3153018267u, 2461856u), + MAKE_ENT(":path", "/", 56997727u, 47u), + MAKE_ENT(":path", "/index.html", 56997727u, 2144181430u), + MAKE_ENT(":scheme", "http", 3322585695u, 3213448u), + MAKE_ENT(":scheme", "https", 3322585695u, 99617003u), + MAKE_ENT(":status", "200", 3338091692u, 49586u), + MAKE_ENT(":status", "204", 3338091692u, 49590u), + MAKE_ENT(":status", "206", 3338091692u, 49592u), + MAKE_ENT(":status", "304", 3338091692u, 50551u), + MAKE_ENT(":status", "400", 3338091692u, 51508u), + MAKE_ENT(":status", "404", 3338091692u, 51512u), + MAKE_ENT(":status", "500", 3338091692u, 52469u), + MAKE_ENT("accept-charset", "", 124285319u, 0u), + MAKE_ENT("accept-encoding", "gzip, deflate", 4127597688u, 1733326877u), + MAKE_ENT("accept-language", "", 802785917u, 0u), + MAKE_ENT("accept-ranges", "", 1397189435u, 0u), + MAKE_ENT("accept", "", 2871506184u, 0u), + MAKE_ENT("access-control-allow-origin", "", 3297999203u, 0u), + MAKE_ENT("age", "", 96511u, 0u), + MAKE_ENT("allow", "", 92906313u, 0u), + MAKE_ENT("authorization", "", 2909397113u, 0u), + MAKE_ENT("cache-control", "", 4086191634u, 0u), + MAKE_ENT("content-disposition", "", 3027699811u, 0u), + MAKE_ENT("content-encoding", "", 2095084583u, 0u), + MAKE_ENT("content-language", "", 3065240108u, 0u), + MAKE_ENT("content-length", "", 3162187450u, 0u), + MAKE_ENT("content-location", "", 2284906121u, 0u), + MAKE_ENT("content-range", "", 2878374633u, 0u), + MAKE_ENT("content-type", "", 785670158u, 0u), + MAKE_ENT("cookie", "", 2940209764u, 0u), + MAKE_ENT("date", "", 3076014u, 0u), + MAKE_ENT("etag", "", 3123477u, 0u), + MAKE_ENT("expect", "", 3005803609u, 0u), + MAKE_ENT("expires", "", 2985731892u, 0u), + MAKE_ENT("from", "", 3151786u, 0u), + MAKE_ENT("host", "", 3208616u, 0u), + MAKE_ENT("if-match", "", 34533653u, 0u), + MAKE_ENT("if-modified-since", "", 2302095846u, 0u), + MAKE_ENT("if-none-match", "", 646073760u, 0u), + MAKE_ENT("if-range", "", 39145613u, 0u), + MAKE_ENT("if-unmodified-since", "", 1454068927u, 0u), + MAKE_ENT("last-modified", "", 150043680u, 0u), + MAKE_ENT("link", "", 3321850u, 0u), + MAKE_ENT("location", "", 1901043637u, 0u), + MAKE_ENT("max-forwards", "", 1619948695u, 0u), + MAKE_ENT("proxy-authenticate", "", 3993199572u, 0u), + MAKE_ENT("proxy-authorization", "", 329532250u, 0u), + MAKE_ENT("range", "", 108280125u, 0u), + MAKE_ENT("referer", "", 1085069613u, 0u), + MAKE_ENT("refresh", "", 1085444827u, 0u), + MAKE_ENT("retry-after", "", 1933352567u, 0u), + MAKE_ENT("server", "", 3389140803u, 0u), + MAKE_ENT("set-cookie", "", 1237214767u, 0u), + MAKE_ENT("strict-transport-security", "", 1153852136u, 0u), + MAKE_ENT("transfer-encoding", "", 1274458357u, 0u), + MAKE_ENT("user-agent", "", 486342275u, 0u), + MAKE_ENT("vary", "", 3612210u, 0u), + MAKE_ENT("via", "", 116750u, 0u), + MAKE_ENT("www-authenticate", "", 4051929931u, 0u), }; -/* Index to the position in static_table */ -const size_t static_table_index[] = { - 38, 43, 44, 10, 11, 47, 48, 49, 50, 51, 52, 53, 54, 55, 14, 60, - 20, 26, 34, 46, 0 , 12, 36, 59, 41, 31, 42, 45, 32, 35, 19, 37, - 2 , 3 , 40, 39, 4 , 5 , 8 , 33, 18, 9 , 27, 15, 6 , 29, 28, 57, - 16, 13, 21, 22, 30, 56, 24, 23, 25, 17, 7 , 1 , 58 -}; - -static const size_t STATIC_TABLE_LENGTH = +const size_t NGHTTP2_STATIC_TABLE_LENGTH = sizeof(static_table)/sizeof(static_table[0]); static int memeq(const void *s1, const void *s2, size_t n) @@ -238,7 +229,7 @@ static int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize) return 0; } for(size = 1; size < bufsize; size <<= 1); - buffer = ( nghttp2_hd_entry **)malloc(sizeof(nghttp2_hd_entry*) * size); + buffer = (nghttp2_hd_entry **)malloc(sizeof(nghttp2_hd_entry*) * size); if(buffer == NULL) { return NGHTTP2_ERR_NOMEM; } @@ -290,11 +281,9 @@ static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) --ringbuf->len; } -static int hd_context_init(nghttp2_hd_context *context, - nghttp2_hd_role role) +static int hd_context_init(nghttp2_hd_context *context) { int rv; - context->role = role; context->bad = 0; context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; rv = hd_ringbuf_init @@ -323,11 +312,10 @@ int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, size_t deflate_hd_table_bufsize_max) { int rv; - rv = hd_context_init(&deflater->ctx, NGHTTP2_HD_ROLE_DEFLATE); + rv = hd_context_init(&deflater->ctx); if(rv != 0) { return rv; } - deflater->no_refset = 0; if(deflate_hd_table_bufsize_max < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) { deflater->notify_table_size_change = 1; @@ -337,6 +325,7 @@ int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, } deflater->deflate_hd_table_bufsize_max = deflate_hd_table_bufsize_max; + deflater->min_hd_table_bufsize_max = UINT32_MAX; return 0; } @@ -345,7 +334,7 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater) { int rv; - rv = hd_context_init(&inflater->ctx, NGHTTP2_HD_ROLE_INFLATE); + rv = hd_context_init(&inflater->ctx); if(rv != 0) { goto fail; } @@ -355,7 +344,6 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater) inflater->ent_keep = NULL; inflater->nv_keep = NULL; - inflater->end_headers_index = 0; inflater->opcode = NGHTTP2_HD_OPCODE_NONE; inflater->state = NGHTTP2_HD_STATE_OPCODE; @@ -369,10 +357,10 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater) inflater->huffman_encoded = 0; inflater->index = 0; inflater->left = 0; + inflater->shift = 0; inflater->newnamelen = 0; inflater->index_required = 0; inflater->no_index = 0; - inflater->ent_name = NULL; return 0; @@ -408,12 +396,6 @@ void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) hd_context_free(&inflater->ctx); } -void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater, - uint8_t no_refset) -{ - deflater->no_refset = no_refset; -} - static size_t entry_room(size_t namelen, size_t valuelen) { return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; @@ -426,10 +408,8 @@ static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent) DEBUGF(fprintf(stderr, ": ")); DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr)); DEBUGF(fprintf(stderr, "\n")); - /* ent->ref may be 0. This happens if the careless stupid encoder - emits literal block larger than header table capacity with - indexing. */ - ent->flags |= NGHTTP2_HD_FLAG_EMIT; + /* ent->ref may be 0. This happens if the encoder emits literal + block larger than header table capacity with indexing. */ *nv_out = ent->nv; return 0; } @@ -470,15 +450,19 @@ static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { size_t k = (1 << prefix) - 1; size_t len = 0; + *buf &= ~k; - if(n >= k) { - *buf++ |= k; - n -= k; - ++len; - } else { + + if(n < k) { *buf++ |= n; + return 1; } + + *buf++ |= k; + n -= k; + ++len; + do { ++len; if(n >= 128) { @@ -493,10 +477,10 @@ static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) } /* - * Decodes |prefx| prefixed integer stored from |in|. The |last| + * Decodes |prefix| prefixed integer stored from |in|. The |last| * represents the 1 beyond the last of the valid contiguous memory - * region from |in|. The decoded integer must be strictly less than 1 - * << 16. + * region from |in|. The decoded integer must be less than or equal + * to UINT32_MAX. * * If the |initial| is nonzero, it is used as a initial value, this * function assumes the |in| starts with intermediate data. @@ -504,65 +488,69 @@ static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) * An entire integer is decoded successfully, decoded, the |*final| is * set to nonzero. * - * This function returns the next byte of read byte. This function - * stores the decoded integer in |*res| if it succeed, including - * partial decoding, or stores -1 in |*res|, indicating decoding - * error. + * This function stores the decoded integer in |*res| if it succeed, + * including partial decoding (in this case, number of shift to make + * in the next call will be stored in |*shift_ptr|) and returns number + * of bytes processed, or returns -1, indicating decoding error. */ -static uint8_t* decode_length(ssize_t *res, int *final, ssize_t initial, - uint8_t *in, uint8_t *last, size_t prefix) +static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *final, + uint32_t initial, size_t shift, + uint8_t *in, uint8_t *last, size_t prefix) { - int k = (1 << prefix) - 1, r; - ssize_t n = initial; + uint32_t k = (1 << prefix) - 1; + uint32_t n = initial; + uint8_t *start = in; + + *shift_ptr = 0; *final = 0; + if(n == 0) { - if((*in & k) == k) { - n = k; - } else { + if((*in & k) != k) { *res = (*in) & k; *final = 1; - return in + 1; + return 1; } + + n = k; + if(++in == last) { *res = n; - return in; + return (ssize_t)(in - start); } } - for(r = 0; in != last; ++in, r += 7) { - n += (*in & 0x7f) << r; - if(n >= (1 << 16)) { - *res = -1; - return in + 1; + + for(; in != last; ++in, shift += 7) { + uint32_t add = *in & 0x7f; + + if((UINT32_MAX >> shift) < add) { + DEBUGF(fprintf(stderr, "inflate: integer overflow on shift\n")); + return -1; } + + add <<= shift; + + if(UINT32_MAX - add < n) { + DEBUGF(fprintf(stderr, "inflate: integer overflow on addition\n")); + return -1; + } + + n += add; + if((*in & (1 << 7)) == 0) { break; } } + + *shift_ptr = shift; + if(in == last) { *res = n; - return in; - } - if(*in & (1 << 7)) { - *res = -1; - return in + 1; + return (ssize_t)(in - start); } + *res = n; *final = 1; - return in + 1; -} - -static int emit_clear_refset(nghttp2_bufs *bufs) -{ - int rv; - - DEBUGF(fprintf(stderr, "deflatehd: emit clear refset\n")); - - rv = nghttp2_bufs_addb(bufs, 0x30u); - if(rv != 0) { - return rv; - } - - return 0; + return (ssize_t)(in + 1 - start); } static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) @@ -574,7 +562,7 @@ static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) DEBUGF(fprintf(stderr, "deflatehd: emit table_size=%zu\n", table_size)); - blocklen = count_encoded_length(table_size, 4); + blocklen = count_encoded_length(table_size, 5); if(sizeof(sb) < blocklen) { return NGHTTP2_ERR_HEADER_COMP; @@ -584,7 +572,7 @@ static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) *bufp = 0x20u; - encode_length(bufp, table_size, 4); + encode_length(bufp, table_size, 5); rv = nghttp2_bufs_add(bufs, sb, blocklen); if(rv != 0) { @@ -622,14 +610,22 @@ static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) return 0; } -static int emit_string(nghttp2_bufs *bufs, - size_t enclen, int huffman, - const uint8_t *str, size_t len) +static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { int rv; uint8_t sb[16]; uint8_t *bufp; size_t blocklen; + size_t enclen; + int huffman = 0; + + enclen = nghttp2_hd_huff_encode_count(str, len); + + if(enclen < len) { + huffman = 1; + } else { + enclen = len; + } blocklen = count_encoded_length(enclen, 7); @@ -681,9 +677,7 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, { int rv; uint8_t *bufp; - size_t encvallen; size_t blocklen; - int huffman; uint8_t sb[16]; size_t prefixlen; int no_index; @@ -701,13 +695,7 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, "indexing=%d, no_index=%d\n", idx, nv->valuelen, inc_indexing, no_index)); - encvallen = nghttp2_hd_huff_encode_count(nv->value, nv->valuelen); blocklen = count_encoded_length(idx + 1, prefixlen); - huffman = encvallen < nv->valuelen; - - if(!huffman) { - encvallen = nv->valuelen; - } if(sizeof(sb) < blocklen) { return NGHTTP2_ERR_HEADER_COMP; @@ -724,7 +712,7 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, return rv; } - rv = emit_string(bufs, encvallen, huffman, nv->value, nv->valuelen); + rv = emit_string(bufs, nv->value, nv->valuelen); if(rv != 0) { return rv; } @@ -736,10 +724,6 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, int inc_indexing) { int rv; - size_t encnamelen; - size_t encvallen; - int name_huffman; - int value_huffman; int no_index; no_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0; @@ -749,29 +733,17 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, "indexing=%d, no_index=%d\n", nv->namelen, nv->valuelen, inc_indexing, no_index)); - encnamelen = nghttp2_hd_huff_encode_count(nv->name, nv->namelen); - encvallen = nghttp2_hd_huff_encode_count(nv->value, nv->valuelen); - name_huffman = encnamelen < nv->namelen; - value_huffman = encvallen < nv->valuelen; - - if(!name_huffman) { - encnamelen = nv->namelen; - } - if(!value_huffman) { - encvallen = nv->valuelen; - } - rv = nghttp2_bufs_addb(bufs, pack_first_byte(inc_indexing, no_index)); if(rv != 0) { return rv; } - rv = emit_string(bufs, encnamelen, name_huffman, nv->name, nv->namelen); + rv = emit_string(bufs, nv->name, nv->namelen); if(rv != 0) { return rv; } - rv = emit_string(bufs, encvallen, value_huffman, nv->value, nv->valuelen); + rv = emit_string(bufs, nv->value, nv->valuelen); if(rv != 0) { return rv; } @@ -779,25 +751,7 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, return 0; } -/* - * Emit common header with |index| by toggle off and on (thus 2 - * indexed representation emissions). - */ -static int emit_implicit(nghttp2_bufs *bufs, size_t idx) -{ - int i, rv; - - for(i = 0; i < 2; ++i) { - rv = emit_indexed_block(bufs, idx); - if(rv != 0) { - return rv; - } - } - return 0; -} - static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context, - nghttp2_bufs *bufs, const nghttp2_nv *nv, uint8_t entry_flags) { @@ -814,17 +768,7 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context, nghttp2_hd_entry* ent = hd_ringbuf_get(&context->hd_table, idx); context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); - if(context->role == NGHTTP2_HD_ROLE_DEFLATE) { - if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) { - /* Emit common header just before it slips away from the - table. If we don't do this, we have to emit it in literal - representation which hurts compression. */ - rv = emit_implicit(bufs, idx); - if(rv != 0) { - return NULL; - } - } - } + DEBUGF(fprintf(stderr, "hpack: remove item from header table: ")); DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr)); DEBUGF(fprintf(stderr, ": ")); @@ -872,8 +816,6 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context, } context->hd_table_bufsize += room; - - new_ent->flags |= NGHTTP2_HD_FLAG_REFSET; } return new_ent; } @@ -901,52 +843,44 @@ static search_result search_hd_table(nghttp2_hd_context *context, size_t i; uint32_t name_hash = hash(nv->name, nv->namelen); uint32_t value_hash = hash(nv->value, nv->valuelen); - ssize_t left = -1, right = (ssize_t)STATIC_TABLE_LENGTH; int use_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) == 0; - if(use_index) { - for(i = 0; i < context->hd_table.len; ++i) { - nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i); - if(ent->name_hash == name_hash && name_eq(&ent->nv, nv)) { - if(res.index == -1) { - res.index = (ssize_t)i; - } - if(ent->value_hash == value_hash && value_eq(&ent->nv, nv)) { - res.index = (ssize_t)i; - res.name_value_match = 1; - return res; - } - } + for(i = 0; i < NGHTTP2_STATIC_TABLE_LENGTH; ++i) { + nghttp2_hd_entry *ent = &static_table[i]; + if(ent->name_hash != name_hash || !name_eq(&ent->nv, nv)) { + continue; } - } - while(right - left > 1) { - ssize_t mid = (left + right) / 2; - nghttp2_hd_entry *ent = &static_table[mid].ent; - if(ent->name_hash < name_hash) { - left = mid; - } else { - right = mid; + if(res.index == -1) { + res.index = (ssize_t)i; } - } - for(i = right; i < STATIC_TABLE_LENGTH; ++i) { - nghttp2_hd_entry *ent = &static_table[i].ent; - if(ent->name_hash != name_hash) { - break; + if(use_index && + ent->value_hash == value_hash && value_eq(&ent->nv, nv)) { + res.index = (ssize_t)i; + res.name_value_match = 1; + return res; } - if(name_eq(&ent->nv, nv)) { + } + + if(!use_index) { + return res; + } + + for(i = 0; i < context->hd_table.len; ++i) { + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i); + if(ent->name_hash == name_hash && name_eq(&ent->nv, nv)) { if(res.index == -1) { - res.index = (ssize_t)(context->hd_table.len + static_table[i].index); + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); } - if(use_index && - ent->value_hash == value_hash && value_eq(&ent->nv, nv)) { - res.index = (ssize_t)(context->hd_table.len + static_table[i].index); + if(ent->value_hash == value_hash && value_eq(&ent->nv, nv)) { + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); res.name_value_match = 1; return res; } } } + return res; } @@ -973,6 +907,9 @@ int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, deflater->ctx.hd_table_bufsize_max = next_bufsize; + deflater->min_hd_table_bufsize_max = + nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize); + deflater->notify_table_size_change = 1; hd_context_shrink_table_size(&deflater->ctx); @@ -988,32 +925,22 @@ int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, return 0; } -static void clear_refset(nghttp2_hd_context *context) -{ - size_t i; - for(i = 0; i < context->hd_table.len; ++i) { - nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i); - ent->flags &= ~NGHTTP2_HD_FLAG_REFSET; - } -} - #define INDEX_RANGE_VALID(context, idx) \ - ((idx) < (context)->hd_table.len + STATIC_TABLE_LENGTH) + ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH) static size_t get_max_index(nghttp2_hd_context *context) { - return context->hd_table.len + STATIC_TABLE_LENGTH - 1; + return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH - 1; } nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) { assert(INDEX_RANGE_VALID(context, idx)); - if(idx < context->hd_table.len) { - return hd_ringbuf_get(&context->hd_table, idx); + if(idx >= NGHTTP2_STATIC_TABLE_LENGTH) { + return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH); } else { - return - &static_table[static_table_index[idx - context->hd_table.len]].ent; + return &static_table[idx]; } } @@ -1044,8 +971,9 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, const nghttp2_nv *nv) { int rv; - nghttp2_hd_entry *ent; search_result res; + ssize_t idx = -1; + int incidx = 0; DEBUGF(fprintf(stderr, "deflatehd: deflating ")); DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr)); @@ -1055,142 +983,55 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, res = search_hd_table(&deflater->ctx, nv); - if(res.index != -1 && res.name_value_match) { - size_t idx = res.index; + idx = res.index; - DEBUGF(fprintf(stderr, "deflatehd: name/value match index=%zd\n", - res.index)); + if(res.name_value_match) { - ent = nghttp2_hd_table_get(&deflater->ctx, idx); - if(idx >= deflater->ctx.hd_table.len) { - nghttp2_hd_entry *new_ent; - - /* It is important to first add entry to the header table and - let eviction go. If NGHTTP2_HD_FLAG_IMPLICIT_EMIT entry is - evicted, it must be emitted before the |nv|. */ - new_ent = add_hd_table_incremental(&deflater->ctx, bufs, &ent->nv, - NGHTTP2_HD_FLAG_NONE); - if(!new_ent) { - return NGHTTP2_ERR_HEADER_COMP; - } - if(new_ent->ref == 0) { - nghttp2_hd_entry_free(new_ent); - free(new_ent); - new_ent = NULL; - } else { - /* new_ent->ref > 0 means that new_ent is in the reference - set */ - new_ent->flags |= NGHTTP2_HD_FLAG_EMIT; - } - rv = emit_indexed_block(bufs, idx); - if(rv != 0) { - return rv; - } - } else if((ent->flags & NGHTTP2_HD_FLAG_REFSET) == 0) { - ent->flags |= NGHTTP2_HD_FLAG_REFSET | NGHTTP2_HD_FLAG_EMIT; - rv = emit_indexed_block(bufs, idx); - if(rv != 0) { - return rv; - } - } else { - int num_emits = 0; - if(ent->flags & NGHTTP2_HD_FLAG_EMIT) { - /* occurrences of the same indexed representation. Emit index - twice. */ - num_emits = 2; - } else if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) { - /* ent was implicitly emitted because it is the common - header field. To support occurrences of the same indexed - representation, we have to emit 4 times. This is because - "implicitly emitted" means actually not emitted at - all. So first 2 emits performs 1st header appears in the - reference set. And another 2 emits are done for 2nd - (current) header. */ - ent->flags ^= NGHTTP2_HD_FLAG_IMPLICIT_EMIT; - ent->flags |= NGHTTP2_HD_FLAG_EMIT; - num_emits = 4; - } else { - /* This is common header and not emitted in the current - run. Just mark IMPLICIT_EMIT, in the hope that we are not - required to emit anything for this. We will emit toggle - off/on for this entry if it is removed from the header - table. */ - ent->flags |= NGHTTP2_HD_FLAG_IMPLICIT_EMIT; - } - for(; num_emits > 0; --num_emits) { - rv = emit_indexed_block(bufs, idx); - if(rv != 0) { - return rv; - } - } - } - } else { - ssize_t idx = -1; - int incidx = 0; - if(res.index != -1) { - DEBUGF(fprintf(stderr, "deflatehd: name match index=%zd\n", - res.index)); + DEBUGF(fprintf(stderr, "deflatehd: name/value match index=%zd\n", idx)); - idx = res.index; - } - if(hd_deflate_should_indexing(deflater, nv)) { - nghttp2_hd_entry *new_ent; - if(idx >= (ssize_t)deflater->ctx.hd_table.len) { - nghttp2_nv nv_indname; - nv_indname = *nv; - nv_indname.name = nghttp2_hd_table_get(&deflater->ctx, idx)->nv.name; - new_ent = add_hd_table_incremental(&deflater->ctx, bufs, &nv_indname, - NGHTTP2_HD_FLAG_VALUE_ALLOC); - } else { - new_ent = add_hd_table_incremental(&deflater->ctx, bufs, nv, - NGHTTP2_HD_FLAG_NAME_ALLOC | - NGHTTP2_HD_FLAG_VALUE_ALLOC); - } - if(!new_ent) { - return NGHTTP2_ERR_HEADER_COMP; - } - if(new_ent->ref == 0) { - nghttp2_hd_entry_free(new_ent); - free(new_ent); - } else { - /* new_ent->ref > 0 means that new_ent is in the reference - set. */ - new_ent->flags |= NGHTTP2_HD_FLAG_EMIT; - } - incidx = 1; - } - if(idx == -1) { - rv = emit_newname_block(bufs, nv, incidx); - } else { - rv = emit_indname_block(bufs, idx, nv, incidx); - } + rv = emit_indexed_block(bufs, idx); if(rv != 0) { return rv; } - } - return 0; -} -static int deflate_post_process_hd_entry(nghttp2_hd_entry *ent, - size_t idx, - nghttp2_bufs *bufs) -{ - int rv; + return 0; + } - if((ent->flags & NGHTTP2_HD_FLAG_REFSET) && - (ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) == 0 && - (ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) { - /* This entry is not present in the current header set and must - be removed. */ - ent->flags ^= NGHTTP2_HD_FLAG_REFSET; + if(res.index != -1) { + DEBUGF(fprintf(stderr, "deflatehd: name match index=%zd\n", + res.index)); + } - rv = emit_indexed_block(bufs, idx); - if(rv != 0) { - return rv; + if(hd_deflate_should_indexing(deflater, nv)) { + nghttp2_hd_entry *new_ent; + if(idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) { + nghttp2_nv nv_indname; + nv_indname = *nv; + nv_indname.name = nghttp2_hd_table_get(&deflater->ctx, idx)->nv.name; + new_ent = add_hd_table_incremental(&deflater->ctx, &nv_indname, + NGHTTP2_HD_FLAG_VALUE_ALLOC); + } else { + new_ent = add_hd_table_incremental(&deflater->ctx, nv, + NGHTTP2_HD_FLAG_NAME_ALLOC | + NGHTTP2_HD_FLAG_VALUE_ALLOC); } + if(!new_ent) { + return NGHTTP2_ERR_HEADER_COMP; + } + if(new_ent->ref == 0) { + nghttp2_hd_entry_free(new_ent); + free(new_ent); + } + incidx = 1; + } + if(idx == -1) { + rv = emit_newname_block(bufs, nv, incidx); + } else { + rv = emit_indname_block(bufs, idx, nv, incidx); + } + if(rv != 0) { + return rv; } - - ent->flags &= ~(NGHTTP2_HD_FLAG_EMIT | NGHTTP2_HD_FLAG_IMPLICIT_EMIT); return 0; } @@ -1207,23 +1048,29 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, } if(deflater->notify_table_size_change) { + size_t min_hd_table_bufsize_max; + + min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max; deflater->notify_table_size_change = 0; + deflater->min_hd_table_bufsize_max = UINT32_MAX; - rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max); + if(deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { - if(rv != 0) { - goto fail; + rv = emit_table_size(bufs, min_hd_table_bufsize_max); + + if(rv != 0) { + goto fail; + } } - } - if(deflater->no_refset) { - rv = emit_clear_refset(bufs); + rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max); + if(rv != 0) { goto fail; } - clear_refset(&deflater->ctx); } + for(i = 0; i < nvlen; ++i) { rv = deflate_nv(deflater, bufs, &nv[i]); if(rv != 0) { @@ -1234,15 +1081,6 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, DEBUGF(fprintf(stderr, "deflatehd: all input name/value pairs were deflated\n")); - for(i = 0; i < deflater->ctx.hd_table.len; ++i) { - nghttp2_hd_entry *ent = hd_ringbuf_get(&deflater->ctx.hd_table, i); - - rv = deflate_post_process_hd_entry(ent, i, bufs); - if(rv != 0) { - goto fail; - } - } - return 0; fail: DEBUGF(fprintf(stderr, "deflatehd: error return %d\n", rv)); @@ -1284,15 +1122,14 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen) { - size_t n; + size_t n = 0; size_t i; - - /* Possible Reference Set Emptying */ - n = 1; + (void)deflater; /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - - 1 using 4 bit prefix requires 6 bytes. */ - n += 6; + 1 using 4 bit prefix requires 6 bytes. We may emit this at most + twice. */ + n += 12; /* Use Literal Header Field without indexing - New Name, since it is most space consuming format. Also we choose the less one between @@ -1307,9 +1144,6 @@ size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, n += nva[i].namelen + nva[i].valuelen; } - /* Add possible reference set toggle off */ - n += deflater->ctx.hd_table.len; - return n; } @@ -1369,20 +1203,31 @@ static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, uint8_t *in, uint8_t *last, size_t prefix, size_t maxlen) { - uint8_t *nin; + ssize_t rv; + uint32_t out; + *rfin = 0; - nin = decode_length(&inflater->left, rfin, inflater->left, in, last, prefix); - if(inflater->left == -1) { - DEBUGF(fprintf(stderr, "inflatehd: invalid integer\n")); + + rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left, + inflater->shift, in, last, prefix); + + if(rv == -1) { + DEBUGF(fprintf(stderr, "inflatehd: integer decoding failed\n")); return NGHTTP2_ERR_HEADER_COMP; } - if((size_t)inflater->left > maxlen) { + + if(out > maxlen) { DEBUGF(fprintf(stderr, "inflatehd: integer exceeded the maximum value %zu\n", maxlen)); return NGHTTP2_ERR_HEADER_COMP; } - return (ssize_t)(nin - in); + + inflater->left = out; + + DEBUGF(fprintf(stderr, "inflatehd: decoded integer is %u\n", out)); + + return rv; } /* @@ -1406,7 +1251,7 @@ static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, { ssize_t readlen; int final = 0; - if(last - in >= inflater->left) { + if((size_t)(last - in) >= inflater->left) { last = in + inflater->left; final = 1; } @@ -1417,7 +1262,7 @@ static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, DEBUGF(fprintf(stderr, "inflatehd: huffman decoding failed\n")); return readlen; } - inflater->left -= readlen; + inflater->left -= (size_t)readlen; return readlen; } @@ -1440,12 +1285,12 @@ static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, uint8_t *in, uint8_t *last) { int rv; - size_t len = nghttp2_min(last - in, inflater->left); + size_t len = nghttp2_min((size_t)(last - in), inflater->left); rv = nghttp2_bufs_add(bufs, in, len); if(rv != 0) { return rv; } - inflater->left -= (ssize_t)len; + inflater->left -= len; return (ssize_t)len; } @@ -1464,29 +1309,10 @@ static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out) { nghttp2_hd_entry *ent = nghttp2_hd_table_get(&inflater->ctx, inflater->index); - if(inflater->index >= inflater->ctx.hd_table.len) { - nghttp2_hd_entry *new_ent; - new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &ent->nv, - NGHTTP2_HD_FLAG_NONE); - if(!new_ent) { - return NGHTTP2_ERR_NOMEM; - } - /* new_ent->ref == 0 may be hold */ - emit_indexed_header(nv_out, new_ent); - inflater->ent_keep = new_ent; - return 0; - } - ent->flags ^= NGHTTP2_HD_FLAG_REFSET; - if(ent->flags & NGHTTP2_HD_FLAG_REFSET) { - emit_indexed_header(nv_out, ent); - return 0; - } - DEBUGF(fprintf(stderr, "inflatehd: toggle off item: ")); - DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr)); - DEBUGF(fprintf(stderr, ": ")); - DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr)); - DEBUGF(fprintf(stderr, "\n")); - return 1; + + emit_indexed_header(nv_out, ent); + + return 0; } static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, @@ -1555,7 +1381,7 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, management. */ ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; - new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &nv, ent_flags); + new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_flags); if(new_ent) { emit_indexed_header(nv_out, new_ent); @@ -1592,6 +1418,7 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, { int rv; nghttp2_nv nv; + nghttp2_hd_entry *ent_name; rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); if(rv != 0) { @@ -1604,8 +1431,10 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, nv.flags = NGHTTP2_NV_FLAG_NONE; } - nv.name = inflater->ent_name->nv.name; - nv.namelen = inflater->ent_name->nv.namelen; + ent_name = nghttp2_hd_table_get(&inflater->ctx, inflater->index); + + nv.name = ent_name->nv.name; + nv.namelen = ent_name->nv.namelen; if(inflater->index_required) { nghttp2_hd_entry *new_ent; @@ -1613,24 +1442,22 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, int static_name; ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT; - static_name = inflater->index >= inflater->ctx.hd_table.len; + static_name = inflater->index < NGHTTP2_STATIC_TABLE_LENGTH; if(!static_name) { ent_flags |= NGHTTP2_HD_FLAG_NAME_ALLOC; /* For entry in static table, we must not touch ref, because it is shared by threads */ - ++inflater->ent_name->ref; + ++ent_name->ref; } - new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &nv, ent_flags); + new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_flags); - if(!static_name && --inflater->ent_name->ref == 0) { - nghttp2_hd_entry_free(inflater->ent_name); - free(inflater->ent_name); + if(!static_name && --ent_name->ref == 0) { + nghttp2_hd_entry_free(ent_name); + free(ent_name); } - inflater->ent_name = NULL; - if(new_ent) { emit_indexed_header(nv_out, new_ent); @@ -1671,20 +1498,10 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, for(; in != last;) { switch(inflater->state) { case NGHTTP2_HD_STATE_OPCODE: - if((*in & 0xf0u) == 0x20u) { + if((*in & 0xe0u) == 0x20u) { DEBUGF(fprintf(stderr, "inflatehd: header table size change\n")); inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE; - } else if((*in & 0xf0u) == 0x30u) { - if(*in != 0x30u) { - rv = NGHTTP2_ERR_HEADER_COMP; - goto fail; - } - - DEBUGF(fprintf(stderr, "inflatehd: clearing reference set\n")); - inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; - inflater->state = NGHTTP2_HD_STATE_CLEAR_REFSET; - ++in; } else if(*in & 0x80u) { DEBUGF(fprintf(stderr, "inflatehd: indexed repr\n")); inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; @@ -1702,7 +1519,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_READ_INDEX; } inflater->index_required = (*in & 0x40) != 0; - inflater->no_index = (*in & 0x10u) != 0; + inflater->no_index = (*in & 0xf0u) == 0x10u; DEBUGF(fprintf(stderr, "inflatehd: indexing required=%d, no_index=%d\n", inflater->index_required, @@ -1712,15 +1529,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, } } inflater->left = 0; - break; - case NGHTTP2_HD_STATE_CLEAR_REFSET: - clear_refset(&inflater->ctx); - inflater->state = NGHTTP2_HD_STATE_OPCODE; - + inflater->shift = 0; break; case NGHTTP2_HD_STATE_READ_TABLE_SIZE: rfin = 0; - rv = hd_inflate_read_len(inflater, &rfin, in, last, 4, + rv = hd_inflate_read_len(inflater, &rfin, in, last, 5, inflater->settings_hd_table_bufsize_max); if(rv < 0) { goto fail; @@ -1729,7 +1542,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, if(!rfin) { goto almost_ok; } - DEBUGF(fprintf(stderr, "inflatehd: table_size=%zd\n", inflater->left)); + DEBUGF(fprintf(stderr, "inflatehd: table_size=%zu\n", inflater->left)); inflater->ctx.hd_table_bufsize_max = inflater->left; hd_context_shrink_table_size(&inflater->ctx); inflater->state = NGHTTP2_HD_STATE_OPCODE; @@ -1754,19 +1567,20 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, in += rv; + if(!rfin) { + goto almost_ok; + } + if(inflater->left == 0) { rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } - if(!rfin) { - goto almost_ok; - } - DEBUGF(fprintf(stderr, "inflatehd: index=%zd\n", inflater->left)); + DEBUGF(fprintf(stderr, "inflatehd: index=%zu\n", inflater->left)); if(inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { inflater->index = inflater->left; - assert(inflater->index > 0); --inflater->index; + rv = hd_inflate_commit_indexed(inflater, nv_out); if(rv < 0) { goto fail; @@ -1779,10 +1593,8 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, } } else { inflater->index = inflater->left; - assert(inflater->index > 0); --inflater->index; - inflater->ent_name = nghttp2_hd_table_get(&inflater->ctx, - inflater->index); + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; } break; @@ -1791,6 +1603,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, hd_inflate_set_huffman_encoded(inflater, in); inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN; inflater->left = 0; + inflater->shift = 0; DEBUGF(fprintf(stderr, "inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0)); /* Fall through */ @@ -1804,7 +1617,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, in += rv; if(!rfin) { DEBUGF(fprintf(stderr, - "inflatehd: integer not fully decoded. current=%zd\n", + "inflatehd: integer not fully decoded. current=%zu\n", inflater->left)); goto almost_ok; @@ -1830,7 +1643,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, if(inflater->left) { DEBUGF(fprintf(stderr, - "inflatehd: still %zd bytes to go\n", inflater->left)); + "inflatehd: still %zu bytes to go\n", inflater->left)); goto almost_ok; } @@ -1851,7 +1664,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); if(inflater->left) { DEBUGF(fprintf(stderr, - "inflatehd: still %zd bytes to go\n", inflater->left)); + "inflatehd: still %zu bytes to go\n", inflater->left)); goto almost_ok; } @@ -1865,6 +1678,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, hd_inflate_set_huffman_encoded(inflater, in); inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN; inflater->left = 0; + inflater->shift = 0; DEBUGF(fprintf(stderr, "inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0)); /* Fall through */ @@ -1882,7 +1696,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, goto almost_ok; } - DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zd\n", inflater->left)); + DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zu\n", inflater->left)); if(inflater->left == 0) { if(inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { rv = hd_inflate_commit_newname(inflater, nv_out); @@ -1917,7 +1731,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, if(inflater->left) { DEBUGF(fprintf(stderr, - "inflatehd: still %zd bytes to go\n", inflater->left)); + "inflatehd: still %zu bytes to go\n", inflater->left)); goto almost_ok; } @@ -1950,7 +1764,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, if(inflater->left) { DEBUGF(fprintf(stderr, - "inflatehd: still %zd bytes to go\n", inflater->left)); + "inflatehd: still %zu bytes to go\n", inflater->left)); goto almost_ok; } @@ -1985,20 +1799,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, goto fail; } - for(; inflater->end_headers_index < inflater->ctx.hd_table.len; - ++inflater->end_headers_index) { - nghttp2_hd_entry *ent; - ent = hd_ringbuf_get(&inflater->ctx.hd_table, - inflater->end_headers_index); - - if((ent->flags & NGHTTP2_HD_FLAG_REFSET) && - (ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) { - emit_indexed_header(nv_out, ent); - *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); - } - ent->flags &= ~NGHTTP2_HD_FLAG_EMIT; - } *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; } return (ssize_t)(in - first); @@ -2023,7 +1823,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) { hd_inflate_keep_free(inflater); - inflater->end_headers_index = 0; return 0; } @@ -2075,3 +1874,11 @@ int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { return emit_table_size(bufs, table_size); } + +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final, + uint32_t initial, size_t shift, + uint8_t *in, uint8_t *last, size_t prefix) +{ + return decode_length(res, shift_ptr, final, initial, shift, in, last, + prefix); +} diff --git a/epan/nghttp2/nghttp2_hd.h b/epan/nghttp2/nghttp2_hd.h index 1a8dd041ed..51bc0a0283 100644 --- a/epan/nghttp2/nghttp2_hd.h +++ b/epan/nghttp2/nghttp2_hd.h @@ -47,10 +47,8 @@ encoder only uses the memory up to this value. */ #define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) -typedef enum { - NGHTTP2_HD_ROLE_DEFLATE, - NGHTTP2_HD_ROLE_INFLATE -} nghttp2_hd_role; +/* Exported for unit test */ +extern const size_t NGHTTP2_STATIC_TABLE_LENGTH; typedef enum { NGHTTP2_HD_FLAG_NONE = 0, @@ -58,18 +56,12 @@ typedef enum { NGHTTP2_HD_FLAG_NAME_ALLOC = 1, /* Indicates value was dynamically allocated and must be freed */ NGHTTP2_HD_FLAG_VALUE_ALLOC = 1 << 1, - /* Indicates that the entry is in the reference set */ - NGHTTP2_HD_FLAG_REFSET = 1 << 2, - /* Indicates that the entry is emitted in the current header - processing. */ - NGHTTP2_HD_FLAG_EMIT = 1 << 3, - NGHTTP2_HD_FLAG_IMPLICIT_EMIT = 1 << 4, /* Indicates that the name was gifted to the entry and no copying necessary. */ - NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 5, + NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 2, /* Indicates that the value was gifted to the entry and no copying necessary. */ - NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 6 + NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 3 } nghttp2_hd_flags; typedef struct { @@ -82,11 +74,6 @@ typedef struct { } nghttp2_hd_entry; typedef struct { - nghttp2_hd_entry ent; - size_t index; -} nghttp2_hd_static_entry; - -typedef struct { nghttp2_hd_entry **buffer; size_t mask; size_t first; @@ -102,7 +89,6 @@ typedef enum { typedef enum { NGHTTP2_HD_STATE_OPCODE, - NGHTTP2_HD_STATE_CLEAR_REFSET, NGHTTP2_HD_STATE_READ_TABLE_SIZE, NGHTTP2_HD_STATE_READ_INDEX, NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN, @@ -124,8 +110,6 @@ typedef struct { size_t hd_table_bufsize; /* The effective header table size. */ size_t hd_table_bufsize_max; - /* Role of this context; deflate or infalte */ - nghttp2_hd_role role; /* If inflate/deflate error occurred, this value is set to 1 and further invocation of inflate/deflate will fail with NGHTTP2_ERR_HEADER_COMP. */ @@ -136,9 +120,8 @@ struct nghttp2_hd_deflater { nghttp2_hd_context ctx; /* The upper limit of the header table size the deflater accepts. */ size_t deflate_hd_table_bufsize_max; - /* Set to this nonzero to clear reference set on each deflation each - time. */ - uint8_t no_refset; + /* Minimum header table size notified in the next context update */ + size_t min_hd_table_bufsize_max; /* If nonzero, send header table size using encoding context update in the next deflate process */ uint8_t notify_table_size_change; @@ -157,22 +140,18 @@ struct nghttp2_hd_inflater { /* Pointer to the name/value pair buffer which is used in the current header emission. */ uint8_t *nv_keep; - /* Pointers to the name/value pair which is referred as indexed - name. This entry must be in header table. */ - nghttp2_hd_entry *ent_name; /* The number of bytes to read */ - ssize_t left; + size_t left; /* The index in indexed repr or indexed name */ size_t index; - /* The index of header table to toggle off the entry from reference - set at the end of decompression. */ - size_t end_headers_index; /* The length of new name encoded in literal. For huffman encoded string, this is the length after it is decoded. */ size_t newnamelen; /* The maximum header table size the inflater supports. This is the same value transmitted in SETTINGS_HEADER_TABLE_SIZE */ size_t settings_hd_table_bufsize_max; + /* The number of next shift to decode integer */ + size_t shift; nghttp2_hd_opcode opcode; nghttp2_hd_inflate_state state; /* nonzero if string is huffman encoded */ @@ -295,6 +274,11 @@ int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index); +/* For unittesting purpose */ +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final, + uint32_t initial, size_t shift, + uint8_t *in, uint8_t *last, size_t prefix); + /* Huffman encoding/decoding functions */ /* diff --git a/epan/nghttp2/nghttp2_helper.c b/epan/nghttp2/nghttp2_helper.c index caf8bdb926..a0fcaf35ef 100644 --- a/epan/nghttp2/nghttp2_helper.c +++ b/epan/nghttp2/nghttp2_helper.c @@ -324,7 +324,7 @@ int nghttp2_check_header_name(const uint8_t *name, size_t len) } static int VALID_HD_VALUE_CHARS[] = { - 1 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, diff --git a/epan/nghttp2/nghttp2ver.h b/epan/nghttp2/nghttp2ver.h index 2218d90157..cb360bd541 100644 --- a/epan/nghttp2/nghttp2ver.h +++ b/epan/nghttp2/nghttp2ver.h @@ -29,7 +29,7 @@ * @macro * Version number of the nghttp2 library release */ -#define NGHTTP2_VERSION "0.5.0" +#define NGHTTP2_VERSION "0.5.2-DEV" /** * @macro @@ -37,6 +37,6 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define NGHTTP2_VERSION_NUM 0x000500 +#define NGHTTP2_VERSION_NUM 0x000502 #endif /* NGHTTP2VER_H */ |