diff options
author | Alexis La Goutte <alexis.lagoutte@gmail.com> | 2015-04-29 23:12:12 +0200 |
---|---|---|
committer | Alexis La Goutte <alexis.lagoutte@gmail.com> | 2015-04-30 20:02:43 +0000 |
commit | d0e34312b8f401d10f35f49710cbabbd96f59c38 (patch) | |
tree | bbe3923535405756a0b6a999ad42c9d5eda0fede /epan/nghttp2/nghttp2_hd.c | |
parent | 12ec6c4482950dafab1701e709a0a0fee37e399c (diff) |
HTTP2: Update to libnghttp2 0.7.13
Change-Id: I69589a90077a9b009f2e1a45531059ebd61a0450
Reviewed-on: https://code.wireshark.org/review/8242
Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com>
Reviewed-by: Pascal Quantin <pascal.quantin@gmail.com>
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
Diffstat (limited to 'epan/nghttp2/nghttp2_hd.c')
-rw-r--r-- | epan/nghttp2/nghttp2_hd.c | 1001 |
1 files changed, 709 insertions, 292 deletions
diff --git a/epan/nghttp2/nghttp2_hd.c b/epan/nghttp2/nghttp2_hd.c index 81726cd5f1..2f0c00228a 100644 --- a/epan/nghttp2/nghttp2_hd.c +++ b/epan/nghttp2/nghttp2_hd.c @@ -31,118 +31,472 @@ #include "nghttp2_helper.h" #include "nghttp2_int.h" -#define STATIC_TABLE_LENGTH 61 - -/* Make scalar initialization form of nghttp2_nv */ -#define MAKE_STATIC_ENT(I, N, V, NH, VH) \ +/* Make scalar initialization form of nghttp2_hd_entry */ +#define MAKE_STATIC_ENT(N, V, T) \ { \ - { \ - { (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \ - , (NH), (VH), 1, NGHTTP2_HD_FLAG_NONE \ - } \ - , I \ + { (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \ + , (T), 1, NGHTTP2_HD_FLAG_NONE \ } /* Generated by mkstatictbl.py */ -/* 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), +/* 3rd parameter is nghttp2_token value for header field name. We use + first enum value if same header names are repeated (e.g., + :status). */ +static nghttp2_hd_entry static_table[] = { + MAKE_STATIC_ENT(":authority", "", 0), + MAKE_STATIC_ENT(":method", "GET", 1), + MAKE_STATIC_ENT(":method", "POST", 1), + MAKE_STATIC_ENT(":path", "/", 3), + MAKE_STATIC_ENT(":path", "/index.html", 3), + MAKE_STATIC_ENT(":scheme", "http", 5), + MAKE_STATIC_ENT(":scheme", "https", 5), + MAKE_STATIC_ENT(":status", "200", 7), + MAKE_STATIC_ENT(":status", "204", 7), + MAKE_STATIC_ENT(":status", "206", 7), + MAKE_STATIC_ENT(":status", "304", 7), + MAKE_STATIC_ENT(":status", "400", 7), + MAKE_STATIC_ENT(":status", "404", 7), + MAKE_STATIC_ENT(":status", "500", 7), + MAKE_STATIC_ENT("accept-charset", "", 14), + MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15), + MAKE_STATIC_ENT("accept-language", "", 16), + MAKE_STATIC_ENT("accept-ranges", "", 17), + MAKE_STATIC_ENT("accept", "", 18), + MAKE_STATIC_ENT("access-control-allow-origin", "", 19), + MAKE_STATIC_ENT("age", "", 20), + MAKE_STATIC_ENT("allow", "", 21), + MAKE_STATIC_ENT("authorization", "", 22), + MAKE_STATIC_ENT("cache-control", "", 23), + MAKE_STATIC_ENT("content-disposition", "", 24), + MAKE_STATIC_ENT("content-encoding", "", 25), + MAKE_STATIC_ENT("content-language", "", 26), + MAKE_STATIC_ENT("content-length", "", 27), + MAKE_STATIC_ENT("content-location", "", 28), + MAKE_STATIC_ENT("content-range", "", 29), + MAKE_STATIC_ENT("content-type", "", 30), + MAKE_STATIC_ENT("cookie", "", 31), + MAKE_STATIC_ENT("date", "", 32), + MAKE_STATIC_ENT("etag", "", 33), + MAKE_STATIC_ENT("expect", "", 34), + MAKE_STATIC_ENT("expires", "", 35), + MAKE_STATIC_ENT("from", "", 36), + MAKE_STATIC_ENT("host", "", 37), + MAKE_STATIC_ENT("if-match", "", 38), + MAKE_STATIC_ENT("if-modified-since", "", 39), + MAKE_STATIC_ENT("if-none-match", "", 40), + MAKE_STATIC_ENT("if-range", "", 41), + MAKE_STATIC_ENT("if-unmodified-since", "", 42), + MAKE_STATIC_ENT("last-modified", "", 43), + MAKE_STATIC_ENT("link", "", 44), + MAKE_STATIC_ENT("location", "", 45), + MAKE_STATIC_ENT("max-forwards", "", 46), + MAKE_STATIC_ENT("proxy-authenticate", "", 47), + MAKE_STATIC_ENT("proxy-authorization", "", 48), + MAKE_STATIC_ENT("range", "", 49), + MAKE_STATIC_ENT("referer", "", 50), + MAKE_STATIC_ENT("refresh", "", 51), + MAKE_STATIC_ENT("retry-after", "", 52), + MAKE_STATIC_ENT("server", "", 53), + MAKE_STATIC_ENT("set-cookie", "", 54), + MAKE_STATIC_ENT("strict-transport-security", "", 55), + MAKE_STATIC_ENT("transfer-encoding", "", 56), + MAKE_STATIC_ENT("user-agent", "", 57), + MAKE_STATIC_ENT("vary", "", 58), + MAKE_STATIC_ENT("via", "", 59), + MAKE_STATIC_ENT("www-authenticate", "", 60), }; -/* 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}; - -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) { - const uint8_t *a = (const uint8_t *)s1, *b = (const uint8_t *)s2; - uint8_t c = 0; - while (n > 0) { - c |= (*a++) ^ (*b++); - --n; - } - return c == 0; + return memcmp(s1, s2, n) == 0; } -static uint32_t hash(const uint8_t *s, size_t n) { - uint32_t h = 0; - while (n > 0) { - h = h * 31 + *s++; - --n; +/* + * This function was generated by genlibtokenlookup.py. Inspired by + * h2o header lookup. https://github.com/h2o/h2o + */ +static int lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (lstreq("t", name, 1)) { + return NGHTTP2_TOKEN_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'a': + if (lstreq("vi", name, 2)) { + return NGHTTP2_TOKEN_VIA; + } + break; + case 'e': + if (lstreq("ag", name, 2)) { + return NGHTTP2_TOKEN_AGE; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'e': + if (lstreq("dat", name, 3)) { + return NGHTTP2_TOKEN_DATE; + } + break; + case 'g': + if (lstreq("eta", name, 3)) { + return NGHTTP2_TOKEN_ETAG; + } + break; + case 'k': + if (lstreq("lin", name, 3)) { + return NGHTTP2_TOKEN_LINK; + } + break; + case 'm': + if (lstreq("fro", name, 3)) { + return NGHTTP2_TOKEN_FROM; + } + break; + case 't': + if (lstreq("hos", name, 3)) { + return NGHTTP2_TOKEN_HOST; + } + break; + case 'y': + if (lstreq("var", name, 3)) { + return NGHTTP2_TOKEN_VARY; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'e': + if (lstreq("rang", name, 4)) { + return NGHTTP2_TOKEN_RANGE; + } + break; + case 'h': + if (lstreq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + if (lstreq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + break; + case 'w': + if (lstreq("allo", name, 4)) { + return NGHTTP2_TOKEN_ALLOW; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (lstreq("cooki", name, 5)) { + return NGHTTP2_TOKEN_COOKIE; + } + break; + case 'r': + if (lstreq("serve", name, 5)) { + return NGHTTP2_TOKEN_SERVER; + } + break; + case 't': + if (lstreq("accep", name, 5)) { + return NGHTTP2_TOKEN_ACCEPT; + } + if (lstreq("expec", name, 5)) { + return NGHTTP2_TOKEN_EXPECT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'd': + if (lstreq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + if (lstreq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + break; + case 'e': + if (lstreq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (lstreq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (lstreq("upgrad", name, 6)) { + return NGHTTP2_TOKEN_UPGRADE; + } + break; + case 'h': + if (lstreq("refres", name, 6)) { + return NGHTTP2_TOKEN_REFRESH; + } + break; + case 'r': + if (lstreq("refere", name, 6)) { + return NGHTTP2_TOKEN_REFERER; + } + break; + case 's': + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq("expire", name, 6)) { + return NGHTTP2_TOKEN_EXPIRES; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'e': + if (lstreq("if-rang", name, 7)) { + return NGHTTP2_TOKEN_IF_RANGE; + } + break; + case 'h': + if (lstreq("if-matc", name, 7)) { + return NGHTTP2_TOKEN_IF_MATCH; + } + break; + case 'n': + if (lstreq("locatio", name, 7)) { + return NGHTTP2_TOKEN_LOCATION; + } + break; + } + break; + case 10: + switch (name[9]) { + case 'e': + if (lstreq("keep-aliv", name, 9)) { + return NGHTTP2_TOKEN_KEEP_ALIVE; + } + if (lstreq("set-cooki", name, 9)) { + return NGHTTP2_TOKEN_SET_COOKIE; + } + break; + case 'n': + if (lstreq("connectio", name, 9)) { + return NGHTTP2_TOKEN_CONNECTION; + } + break; + case 't': + if (lstreq("user-agen", name, 9)) { + return NGHTTP2_TOKEN_USER_AGENT; + } + break; + case 'y': + if (lstreq(":authorit", name, 9)) { + return NGHTTP2_TOKEN__AUTHORITY; + } + break; + } + break; + case 11: + switch (name[10]) { + case 'r': + if (lstreq("retry-afte", name, 10)) { + return NGHTTP2_TOKEN_RETRY_AFTER; + } + break; + } + break; + case 12: + switch (name[11]) { + case 'e': + if (lstreq("content-typ", name, 11)) { + return NGHTTP2_TOKEN_CONTENT_TYPE; + } + break; + case 's': + if (lstreq("max-forward", name, 11)) { + return NGHTTP2_TOKEN_MAX_FORWARDS; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'd': + if (lstreq("last-modifie", name, 12)) { + return NGHTTP2_TOKEN_LAST_MODIFIED; + } + break; + case 'e': + if (lstreq("content-rang", name, 12)) { + return NGHTTP2_TOKEN_CONTENT_RANGE; + } + break; + case 'h': + if (lstreq("if-none-matc", name, 12)) { + return NGHTTP2_TOKEN_IF_NONE_MATCH; + } + break; + case 'l': + if (lstreq("cache-contro", name, 12)) { + return NGHTTP2_TOKEN_CACHE_CONTROL; + } + break; + case 'n': + if (lstreq("authorizatio", name, 12)) { + return NGHTTP2_TOKEN_AUTHORIZATION; + } + break; + case 's': + if (lstreq("accept-range", name, 12)) { + return NGHTTP2_TOKEN_ACCEPT_RANGES; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (lstreq("content-lengt", name, 13)) { + return NGHTTP2_TOKEN_CONTENT_LENGTH; + } + break; + case 't': + if (lstreq("accept-charse", name, 13)) { + return NGHTTP2_TOKEN_ACCEPT_CHARSET; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (lstreq("accept-languag", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (lstreq("accept-encodin", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_ENCODING; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'e': + if (lstreq("content-languag", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LANGUAGE; + } + if (lstreq("www-authenticat", name, 15)) { + return NGHTTP2_TOKEN_WWW_AUTHENTICATE; + } + break; + case 'g': + if (lstreq("content-encodin", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_ENCODING; + } + break; + case 'n': + if (lstreq("content-locatio", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LOCATION; + } + if (lstreq("proxy-connectio", name, 15)) { + return NGHTTP2_TOKEN_PROXY_CONNECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (lstreq("if-modified-sinc", name, 16)) { + return NGHTTP2_TOKEN_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (lstreq("transfer-encodin", name, 16)) { + return NGHTTP2_TOKEN_TRANSFER_ENCODING; + } + break; + } + break; + case 18: + switch (name[17]) { + case 'e': + if (lstreq("proxy-authenticat", name, 17)) { + return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; + } + break; + } + break; + case 19: + switch (name[18]) { + case 'e': + if (lstreq("if-unmodified-sinc", name, 18)) { + return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE; + } + break; + case 'n': + if (lstreq("content-dispositio", name, 18)) { + return NGHTTP2_TOKEN_CONTENT_DISPOSITION; + } + if (lstreq("proxy-authorizatio", name, 18)) { + return NGHTTP2_TOKEN_PROXY_AUTHORIZATION; + } + break; + } + break; + case 25: + switch (name[24]) { + case 'y': + if (lstreq("strict-transport-securit", name, 24)) { + return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; + } + break; + } + break; + case 27: + switch (name[26]) { + case 'n': + if (lstreq("access-control-allow-origi", name, 26)) { + return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; + } + break; + } + break; } - return h; + return -1; } int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, size_t namelen, uint8_t *value, size_t valuelen, - uint32_t name_hash, uint32_t value_hash, - nghttp2_mem *mem) { + int token, nghttp2_mem *mem) { int rv = 0; /* Since nghttp2_hd_entry is used for indexing, ent->nv.flags always @@ -152,10 +506,11 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) && (flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) { if (namelen == 0) { - /* We should not allow empty header field name */ - ent->nv.name = NULL; + flags &= ~NGHTTP2_HD_FLAG_NAME_ALLOC; + ent->nv.name = (uint8_t *)""; } else { - ent->nv.name = (uint8_t *)nghttp2_memdup(name, namelen, mem); + /* copy including terminating NULL byte */ + ent->nv.name = (uint8_t *)nghttp2_memdup(name, namelen + 1, mem); if (ent->nv.name == NULL) { rv = NGHTTP2_ERR_NOMEM; goto fail; @@ -167,9 +522,11 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) && (flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) { if (valuelen == 0) { - ent->nv.value = NULL; + flags &= ~NGHTTP2_HD_FLAG_VALUE_ALLOC; + ent->nv.value = (uint8_t *)""; } else { - ent->nv.value = (uint8_t *)nghttp2_memdup(value, valuelen, mem); + /* copy including terminating NULL byte */ + ent->nv.value = (uint8_t *)nghttp2_memdup(value, valuelen + 1, mem); if (ent->nv.value == NULL) { rv = NGHTTP2_ERR_NOMEM; goto fail2; @@ -180,12 +537,10 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, } ent->nv.namelen = namelen; ent->nv.valuelen = valuelen; + ent->token = token; ent->ref = 1; ent->flags = flags; - ent->name_hash = name_hash; - ent->value_hash = value_hash; - return 0; fail2: @@ -403,25 +758,23 @@ static size_t entry_room(size_t namelen, size_t valuelen) { return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; } -static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent) { - DEBUGF(fprintf(stderr, "inflatehd: header emission: ")); - 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")); +static int emit_indexed_header(nghttp2_nv *nv_out, int *token_out, + nghttp2_hd_entry *ent) { + DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", ent->nv.name, + ent->nv.value)); /* ent->ref may be 0. This happens if the encoder emits literal block larger than header table capacity with indexing. */ *nv_out = ent->nv; + *token_out = ent->token; return 0; } -static int emit_literal_header(nghttp2_nv *nv_out, nghttp2_nv *nv) { - DEBUGF(fprintf(stderr, "inflatehd: header emission: ")); - DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr)); - DEBUGF(fprintf(stderr, ": ")); - DEBUGF(fwrite(nv->value, nv->valuelen, 1, stderr)); - DEBUGF(fprintf(stderr, "\n")); +static int emit_literal_header(nghttp2_nv *nv_out, int *token_out, + nghttp2_nv *nv) { + DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", nv->name, + nv->value)); *nv_out = *nv; + *token_out = lookup_token(nv->name, nv->namelen); return 0; } @@ -642,38 +995,39 @@ static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { return rv; } -static uint8_t pack_first_byte(int inc_indexing, int no_index) { - if (inc_indexing) { +static uint8_t pack_first_byte(int indexing_mode) { + switch (indexing_mode) { + case NGHTTP2_HD_WITH_INDEXING: return 0x40u; - } - - if (no_index) { + case NGHTTP2_HD_WITHOUT_INDEXING: + return 0; + case NGHTTP2_HD_NEVER_INDEXING: return 0x10u; + default: + assert(0); } - + /* This is required to compile with android NDK r10d + + --enable-werror */ return 0; } static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, - const nghttp2_nv *nv, int inc_indexing) { + const nghttp2_nv *nv, int indexing_mode) { int rv; uint8_t *bufp; size_t blocklen; uint8_t sb[16]; size_t prefixlen; - int no_index; - no_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0; - - if (inc_indexing) { + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { prefixlen = 6; } else { prefixlen = 4; } DEBUGF(fprintf(stderr, "deflatehd: emit indname index=%zu, valuelen=%zu, " - "indexing=%d, no_index=%d\n", - idx, nv->valuelen, inc_indexing, no_index)); + "indexing_mode=%d\n", + idx, nv->valuelen, indexing_mode)); blocklen = count_encoded_length(idx + 1, prefixlen); @@ -683,7 +1037,7 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, bufp = sb; - *bufp = pack_first_byte(inc_indexing, no_index); + *bufp = pack_first_byte(indexing_mode); encode_length(bufp, idx + 1, prefixlen); @@ -701,17 +1055,14 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, } static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, - int inc_indexing) { + int indexing_mode) { int rv; - int no_index; - - no_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0; DEBUGF(fprintf(stderr, "deflatehd: emit newname namelen=%zu, valuelen=%zu, " - "indexing=%d, no_index=%d\n", - nv->namelen, nv->valuelen, inc_indexing, no_index)); + "indexing_mode=%d\n", + nv->namelen, nv->valuelen, indexing_mode)); - rv = nghttp2_bufs_addb(bufs, pack_first_byte(inc_indexing, no_index)); + rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); if (rv != 0) { return rv; } @@ -731,8 +1082,7 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, const nghttp2_nv *nv, - uint32_t name_hash, - uint32_t value_hash, + int token, uint8_t entry_flags) { int rv; nghttp2_hd_entry *new_ent; @@ -750,11 +1100,9 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); - DEBUGF(fprintf(stderr, "hpack: remove item from header table: ")); - 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")); + DEBUGF(fprintf(stderr, "hpack: remove item from header table: %s: %s\n", + ent->nv.name, ent->nv.value)); + hd_ringbuf_pop_back(&context->hd_table); if (--ent->ref == 0) { nghttp2_hd_entry_free(ent, mem); @@ -768,8 +1116,7 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, } rv = nghttp2_hd_entry_init(new_ent, entry_flags, nv->name, nv->namelen, - nv->value, nv->valuelen, name_hash, value_hash, - mem); + nv->value, nv->valuelen, token, mem); if (rv != 0) { nghttp2_mem_free(mem, new_ent); return NULL; @@ -810,11 +1157,15 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, } static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) { - return a->namelen == b->namelen && memeq(a->name, b->name, a->namelen); + return a->namelen == b->namelen && + a->name[a->namelen - 1] == b->name[a->namelen - 1] && + memeq(a->name, b->name, a->namelen); } static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) { - return a->valuelen == b->valuelen && memeq(a->value, b->value, a->valuelen); + return a->valuelen == b->valuelen && + a->value[a->valuelen - 1] == b->value[a->valuelen - 1] && + memeq(a->value, b->value, a->valuelen); } typedef struct { @@ -823,61 +1174,54 @@ typedef struct { uint8_t name_value_match; } search_result; +static search_result search_static_table(const nghttp2_nv *nv, int token, + int indexing_mode) { + search_result res = {token, 0}; + int i; + + if (indexing_mode == NGHTTP2_HD_NEVER_INDEXING) { + return res; + } + + for (i = token; + i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token; + ++i) { + if (value_eq(&static_table[i].nv, nv)) { + res.index = i; + res.name_value_match = 1; + return res; + } + } + return res; +} + static search_result search_hd_table(nghttp2_hd_context *context, - const nghttp2_nv *nv, uint32_t name_hash, - uint32_t value_hash) { - ssize_t left = -1, right = (ssize_t)STATIC_TABLE_LENGTH; + const nghttp2_nv *nv, int token, + int indexing_mode) { search_result res = {-1, 0}; size_t i; - int use_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) == 0; - - /* Search dynamic table first, so that we can find recently used - entry first */ - 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)) { - continue; - } - if (res.index == -1) { - res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); - } - - 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; - } + if (token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { + res = search_static_table(nv, token, indexing_mode); + if (res.name_value_match) { + return res; } } - 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; + for (i = 0; i < context->hd_table.len; ++i) { + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i); + if (ent->token != token || (token == -1 && !name_eq(&ent->nv, nv))) { + continue; } - } - for (i = right; i < STATIC_TABLE_LENGTH; ++i) { - nghttp2_hd_entry *ent = &static_table[i].ent; - if (ent->name_hash != name_hash) { - break; + if (res.index == -1) { + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); } - if (name_eq(&ent->nv, nv)) { - if (res.index == -1) { - res.index = (ssize_t)(static_table[i].index); - } - if (use_index && ent->value_hash == value_hash && - value_eq(&ent->nv, nv)) { - res.index = (ssize_t)(static_table[i].index); - res.name_value_match = 1; - return res; - } + if (indexing_mode != NGHTTP2_HD_NEVER_INDEXING && value_eq(&ent->nv, nv)) { + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); + res.name_value_match = 1; + return res; } } @@ -940,29 +1284,23 @@ nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context, return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH); } else { - return &static_table[static_table_index[idx]].ent; + return &static_table[idx]; } } -#define name_match(NV, NAME) \ - (nv->namelen == sizeof(NAME) - 1 && memeq(nv->name, NAME, sizeof(NAME) - 1)) - -static int hd_deflate_should_indexing(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nv) { - if ((nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) || +static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nv, int token) { + if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE || + token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG || + token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE || + token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || + token == NGHTTP2_TOKEN_SET_COOKIE || entry_room(nv->namelen, nv->valuelen) > deflater->ctx.hd_table_bufsize_max * 3 / 4) { - return 0; + return NGHTTP2_HD_WITHOUT_INDEXING; } -#ifdef NGHTTP2_XHD - return !name_match(nv, NGHTTP2_XHD); -#else /* !NGHTTP2_XHD */ - return !name_match(nv, ":path") && !name_match(nv, "content-length") && - !name_match(nv, "set-cookie") && !name_match(nv, "etag") && - !name_match(nv, "if-modified-since") && - !name_match(nv, "if-none-match") && !name_match(nv, "location") && - !name_match(nv, "age"); -#endif /* !NGHTTP2_XHD */ + + return NGHTTP2_HD_WITH_INDEXING; } static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, @@ -970,20 +1308,28 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, int rv; search_result res; ssize_t idx; - int incidx = 0; - uint32_t name_hash = hash(nv->name, nv->namelen); - uint32_t value_hash = hash(nv->value, nv->valuelen); + int indexing_mode; + int token; nghttp2_mem *mem; - DEBUGF(fprintf(stderr, "deflatehd: deflating ")); - DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr)); - DEBUGF(fprintf(stderr, ": ")); - DEBUGF(fwrite(nv->value, nv->valuelen, 1, stderr)); - DEBUGF(fprintf(stderr, "\n")); + DEBUGF(fprintf(stderr, "deflatehd: deflating %s: %s\n", nv->name, nv->value)); mem = deflater->ctx.mem; - res = search_hd_table(&deflater->ctx, nv, name_hash, value_hash); + token = lookup_token(nv->name, nv->namelen); + + /* Don't index authorization header field since it may contain low + entropy secret data (e.g., id/password). Also cookie header + field with less than 20 bytes value is also never indexed. This + is the same criteria used in Firefox codebase. */ + indexing_mode = + token == NGHTTP2_TOKEN_AUTHORIZATION || + (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || + (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) + ? NGHTTP2_HD_NEVER_INDEXING + : hd_deflate_decide_indexing(deflater, nv, token); + + res = search_hd_table(&deflater->ctx, nv, token, indexing_mode); idx = res.index; @@ -1003,19 +1349,18 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, DEBUGF(fprintf(stderr, "deflatehd: name match index=%zd\n", res.index)); } - if (hd_deflate_should_indexing(deflater, nv)) { + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { 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, name_hash, - value_hash, NGHTTP2_HD_FLAG_VALUE_ALLOC); + new_ent = add_hd_table_incremental(&deflater->ctx, &nv_indname, token, + NGHTTP2_HD_FLAG_VALUE_ALLOC); } else { - new_ent = add_hd_table_incremental( - &deflater->ctx, nv, name_hash, value_hash, - NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC); + new_ent = add_hd_table_incremental(&deflater->ctx, nv, token, + NGHTTP2_HD_FLAG_NAME_ALLOC | + NGHTTP2_HD_FLAG_VALUE_ALLOC); } if (!new_ent) { return NGHTTP2_ERR_HEADER_COMP; @@ -1024,12 +1369,11 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, nghttp2_hd_entry_free(new_ent, mem); nghttp2_mem_free(mem, new_ent); } - incidx = 1; } if (idx == -1) { - rv = emit_newname_block(bufs, nv, incidx); + rv = emit_newname_block(bufs, nv, indexing_mode); } else { - rv = emit_indname_block(bufs, idx, nv, incidx); + rv = emit_indname_block(bufs, idx, nv, indexing_mode); } if (rv != 0) { return rv; @@ -1313,10 +1657,10 @@ static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, * Out of memory */ static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out) { + nghttp2_nv *nv_out, int *token_out) { nghttp2_hd_entry *ent = nghttp2_hd_table_get(&inflater->ctx, inflater->index); - emit_indexed_header(nv_out, ent); + emit_indexed_header(nv_out, token_out, ent); return 0; } @@ -1337,18 +1681,24 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, return NGHTTP2_ERR_NOMEM; } + nghttp2_bufs_reset(&inflater->nvbufs); + buflen = rv; if (value_only) { + /* we don't use this value, so no need to NULL-terminate */ nv->name = NULL; nv->namelen = 0; + + nv->value = buf; + nv->valuelen = buflen - 1; } else { nv->name = buf; nv->namelen = inflater->newnamelen; - } - nv->value = buf + nv->namelen; - nv->valuelen = buflen - nv->namelen; + nv->value = buf + nv->namelen + 1; + nv->valuelen = buflen - nv->namelen - 2; + } return 0; } @@ -1360,15 +1710,19 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, pbuf = &inflater->nvbufs.head->buf; if (value_only) { + /* we don't use this value, so no need to NULL-terminate */ nv->name = NULL; nv->namelen = 0; + + nv->value = pbuf->pos; + nv->valuelen = nghttp2_buf_len(pbuf) - 1; } else { nv->name = pbuf->pos; nv->namelen = inflater->newnamelen; - } - nv->value = pbuf->pos + nv->namelen; - nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen; + nv->value = pbuf->pos + nv->namelen + 1; + nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen - 2; + } /* Resetting does not change the content of first buffer */ nghttp2_bufs_reset(&inflater->nvbufs); @@ -1376,6 +1730,42 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, return 0; } +static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv, + nghttp2_hd_entry *ent_name) { + size_t rv; + size_t buflen; + uint8_t *buf; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + /* Allocate buffer including name in ent_name, plus terminating + NULL. */ + buflen = ent_name->nv.namelen + 1 + nghttp2_bufs_len(&inflater->nvbufs); + + buf = (uint8_t *)nghttp2_mem_malloc(mem, buflen); + if (buf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + /* Copy including terminal NULL */ + memcpy(buf, ent_name->nv.name, ent_name->nv.namelen + 1); + rv = nghttp2_bufs_remove_copy(&inflater->nvbufs, + buf + ent_name->nv.namelen + 1); + assert(ent_name->nv.namelen + 1 + rv == buflen); + + nghttp2_bufs_reset(&inflater->nvbufs); + + nv->name = buf; + nv->namelen = ent_name->nv.namelen; + + nv->value = buf + nv->namelen + 1; + nv->valuelen = buflen - nv->namelen - 2; + + return 0; +} + /* * Finalize literal header representation - new name- reception. If * header is emitted, |*nv_out| is filled with that value and 0 is @@ -1388,7 +1778,7 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, * Out of memory */ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out) { + nghttp2_nv *nv_out, int *token_out) { int rv; nghttp2_nv nv; nghttp2_mem *mem; @@ -1415,12 +1805,11 @@ 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, &nv, hash(nv.name, nv.namelen), - hash(nv.value, nv.valuelen), ent_flags); + new_ent = add_hd_table_incremental( + &inflater->ctx, &nv, lookup_token(nv.name, nv.namelen), ent_flags); if (new_ent) { - emit_indexed_header(nv_out, new_ent); + emit_indexed_header(nv_out, token_out, new_ent); inflater->ent_keep = new_ent; return 0; @@ -1431,7 +1820,7 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, return NGHTTP2_ERR_NOMEM; } - emit_literal_header(nv_out, &nv); + emit_literal_header(nv_out, token_out, &nv); if (nv.name != inflater->nvbufs.head->buf.pos) { inflater->nv_keep = nv.name; @@ -1452,7 +1841,7 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, * Out of memory */ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out) { + nghttp2_nv *nv_out, int *token_out) { int rv; nghttp2_nv nv; nghttp2_hd_entry *ent_name; @@ -1460,11 +1849,6 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, mem = inflater->ctx.mem; - rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); - if (rv != 0) { - return NGHTTP2_ERR_NOMEM; - } - if (inflater->no_index) { nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; } else { @@ -1473,34 +1857,36 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, 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; uint8_t ent_flags; - int static_name; - ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT; - static_name = inflater->index < NGHTTP2_STATIC_TABLE_LENGTH; + if (inflater->index < NGHTTP2_STATIC_TABLE_LENGTH) { + /* We don't copy name in static table */ + rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + nv.name = ent_name->nv.name; + nv.namelen = ent_name->nv.namelen; - 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 */ - ++ent_name->ref; + ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT; + } else { + rv = hd_inflate_remove_bufs_with_name(inflater, &nv, ent_name); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + /* nv->name and nv->value are in the same buffer. */ + ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; } - new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->name_hash, - hash(nv.value, nv.valuelen), ent_flags); + new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token, + ent_flags); - if (!static_name && --ent_name->ref == 0) { - nghttp2_hd_entry_free(ent_name, mem); - nghttp2_mem_free(mem, ent_name); - } + /* At this point, ent_name might be deleted. */ if (new_ent) { - emit_indexed_header(nv_out, new_ent); + emit_indexed_header(nv_out, token_out, new_ent); inflater->ent_keep = new_ent; @@ -1512,7 +1898,15 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, return NGHTTP2_ERR_NOMEM; } - emit_literal_header(nv_out, &nv); + rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + + nv.name = ent_name->nv.name; + nv.namelen = ent_name->nv.namelen; + + emit_literal_header(nv_out, token_out, &nv); if (nv.value != inflater->nvbufs.head->buf.pos) { inflater->nv_keep = nv.value; @@ -1524,10 +1918,21 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, uint8_t *in, size_t inlen, int in_final) { + int token; + + return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, &token, in, + inlen, in_final); +} + +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + int *token_out, uint8_t *in, size_t inlen, + int in_final) { ssize_t rv = 0; uint8_t *first = in; uint8_t *last = in + inlen; int rfin = 0; + int busy = 0; if (inflater->ctx.bad) { return NGHTTP2_ERR_HEADER_COMP; @@ -1535,8 +1940,10 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state)); hd_inflate_keep_free(inflater); + *token_out = -1; *inflate_flags = NGHTTP2_HD_INFLATE_NONE; - for (; in != last;) { + for (; in != last || busy;) { + busy = 0; switch (inflater->state) { case NGHTTP2_HD_STATE_OPCODE: if ((*in & 0xe0u) == 0x20u) { @@ -1620,7 +2027,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, inflater->index = inflater->left; --inflater->index; - rv = hd_inflate_commit_indexed(inflater, nv_out); + rv = hd_inflate_commit_indexed(inflater, nv_out, token_out); if (rv < 0) { goto fail; } @@ -1688,6 +2095,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; break; @@ -1709,6 +2121,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; break; @@ -1734,19 +2151,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, } 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); - } else { - rv = hd_inflate_commit_indname(inflater, nv_out); - } - if (rv != 0) { - goto fail; - } - inflater->state = NGHTTP2_HD_STATE_OPCODE; - *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); - } if (inflater->huffman_encoded) { nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); @@ -1755,6 +2159,9 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, } else { inflater->state = NGHTTP2_HD_STATE_READ_VALUE; } + + busy = 1; + break; case NGHTTP2_HD_STATE_READ_VALUEHUFF: rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last); @@ -1773,10 +2180,15 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, goto almost_ok; } + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - rv = hd_inflate_commit_newname(inflater, nv_out); + rv = hd_inflate_commit_newname(inflater, nv_out, token_out); } else { - rv = hd_inflate_commit_indname(inflater, nv_out); + rv = hd_inflate_commit_indname(inflater, nv_out, token_out); } if (rv != 0) { @@ -1805,10 +2217,15 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, goto almost_ok; } + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - rv = hd_inflate_commit_newname(inflater, nv_out); + rv = hd_inflate_commit_newname(inflater, nv_out, token_out); } else { - rv = hd_inflate_commit_indname(inflater, nv_out); + rv = hd_inflate_commit_indname(inflater, nv_out, token_out); } if (rv != 0) { @@ -1904,14 +2321,14 @@ void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { } int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, - nghttp2_nv *nv, int inc_indexing) { + nghttp2_nv *nv, int indexing_mode) { - return emit_indname_block(bufs, idx, nv, inc_indexing); + return emit_indname_block(bufs, idx, nv, indexing_mode); } int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, - int inc_indexing) { - return emit_newname_block(bufs, nv, inc_indexing); + int indexing_mode) { + return emit_newname_block(bufs, nv, indexing_mode); } int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { |