diff --git a/genlibtokenlookup.py b/genlibtokenlookup.py index 25591aa9..d7ac7f47 100755 --- a/genlibtokenlookup.py +++ b/genlibtokenlookup.py @@ -1,19 +1,72 @@ #!/usr/bin/env python HEADERS = [ - ':authority', - ':method', - ':path', - ':scheme', - ':status', - "content-length", - "host", - "te", - 'connection', - 'keep-alive', - 'proxy-connection', - 'transfer-encoding', - 'upgrade' + (':authority', 0), + (':method', 1), + (':method', 2), + (':path', 3), + (':path', 4), + (':scheme', 5), + (':scheme', 6), + (':status', 7), + (':status', 8), + (':status', 9), + (':status', 10), + (':status', 11), + (':status', 12), + (':status', 13), + ('accept-charset', 14), + ('accept-encoding', 15), + ('accept-language', 16), + ('accept-ranges', 17), + ('accept', 18), + ('access-control-allow-origin', 19), + ('age', 20), + ('allow', 21), + ('authorization', 22), + ('cache-control', 23), + ('content-disposition', 24), + ('content-encoding', 25), + ('content-language', 26), + ('content-length', 27), + ('content-location', 28), + ('content-range', 29), + ('content-type', 30), + ('cookie', 31), + ('date', 32), + ('etag', 33), + ('expect', 34), + ('expires', 35), + ('from', 36), + ('host', 37), + ('if-match', 38), + ('if-modified-since', 39), + ('if-none-match', 40), + ('if-range', 41), + ('if-unmodified-since', 42), + ('last-modified', 43), + ('link', 44), + ('location', 45), + ('max-forwards', 46), + ('proxy-authenticate', 47), + ('proxy-authorization', 48), + ('range', 49), + ('referer', 50), + ('refresh', 51), + ('retry-after', 52), + ('server', 53), + ('set-cookie', 54), + ('strict-transport-security', 55), + ('transfer-encoding', 56), + ('user-agent', 57), + ('vary', 58), + ('via', 59), + ('www-authenticate', 60), + ('te', None), + ('connection', None), + ('keep-alive',None), + ('proxy-connection', None), + ('upgrade', None), ] def to_enum_hd(k): @@ -27,7 +80,7 @@ def to_enum_hd(k): def build_header(headers): res = {} - for k in headers: + for k, _ in headers: size = len(k) if size not in res: res[size] = {} @@ -40,18 +93,20 @@ def build_header(headers): return res def gen_enum(): - print '''\ -typedef enum {''' - for k in sorted(HEADERS): - print '''\ - {},'''.format(to_enum_hd(k)) - print '''\ - NGHTTP2_TOKEN_MAXIDX, -} nghttp2_token;''' + name = '' + print 'typedef enum {' + for k, token in HEADERS: + if token is None: + print ' {},'.format(to_enum_hd(k)) + else: + if name != k: + name = k + print ' {} = {},'.format(to_enum_hd(k), token) + print '} nghttp2_token;' def gen_index_header(): print '''\ -static int lookup_token(const uint8_t *name, size_t namelen) { +static inline int lookup_token(const uint8_t *name, size_t namelen) { switch (namelen) {''' b = build_header(HEADERS) for size in sorted(b.keys()): @@ -66,7 +121,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) { case '{}':'''.format(c) for k in headers: print '''\ - if (streq("{}", name, {})) {{ + if (lstreq("{}", name, {})) {{ return {}; }}'''.format(k[:-1], size - 1, to_enum_hd(k)) print '''\ diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c index fbebe28c..e023229e 100644 --- a/lib/nghttp2_hd.c +++ b/lib/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 inline 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 @@ -183,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: @@ -406,19 +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) { +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) { +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; } @@ -723,8 +1079,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; @@ -758,8 +1113,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; @@ -800,11 +1154,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 { @@ -813,19 +1171,46 @@ 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, int indexing_mode) { - 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; + if (token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { + res = search_static_table(nv, token, indexing_mode); + if (res.name_value_match) { + return res; + } + } + /* Search dynamic table first, so that we can find recently used entry first */ if (indexing_mode != NGHTTP2_HD_NEVER_INDEXING) { 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 (ent->token != token || (token == -1 && !name_eq(&ent->nv, nv))) { continue; } @@ -833,7 +1218,7 @@ static search_result search_hd_table(nghttp2_hd_context *context, res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); } - if (ent->value_hash == value_hash && value_eq(&ent->nv, nv)) { + if (value_eq(&ent->nv, nv)) { res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); res.name_value_match = 1; return res; @@ -841,35 +1226,6 @@ static search_result search_hd_table(nghttp2_hd_context *context, } } - 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 = right; i < STATIC_TABLE_LENGTH; ++i) { - nghttp2_hd_entry *ent = &static_table[i].ent; - if (ent->name_hash != name_hash) { - break; - } - - if (name_eq(&ent->nv, nv)) { - if (res.index == -1) { - res.index = (ssize_t)(static_table[i].index); - } - if (indexing_mode != NGHTTP2_HD_NEVER_INDEXING && - 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; - } - } - } - return res; } @@ -929,23 +1285,22 @@ 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_decide_indexing(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nv) { - if (entry_room(nv->namelen, nv->valuelen) > - deflater->ctx.hd_table_bufsize_max * 3 / 4 || - 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")) { + 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 NGHTTP2_HD_WITHOUT_INDEXING; } + return NGHTTP2_HD_WITH_INDEXING; } @@ -955,26 +1310,27 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, search_result res; ssize_t idx; int indexing_mode; - uint32_t name_hash = hash(nv->name, nv->namelen); - uint32_t value_hash = hash(nv->value, nv->valuelen); + int token; nghttp2_mem *mem; DEBUGF(fprintf(stderr, "deflatehd: deflating %s: %s\n", nv->name, nv->value)); mem = deflater->ctx.mem; + 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 = name_match(nv, "authorization") || - (name_match(nv, "cookie") && nv->valuelen < 20) || - (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) - ? NGHTTP2_HD_NEVER_INDEXING - : hd_deflate_decide_indexing(deflater, nv); + 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, name_hash, value_hash, indexing_mode); + res = search_hd_table(&deflater->ctx, nv, token, indexing_mode); idx = res.index; @@ -1000,13 +1356,12 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, 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; @@ -1303,10 +1658,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; } @@ -1388,7 +1743,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 +1770,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 +1785,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 +1806,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; @@ -1491,8 +1845,8 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, ++ent_name->ref; } - 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); @@ -1500,7 +1854,7 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, } 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 +1866,7 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, return NGHTTP2_ERR_NOMEM; } - emit_literal_header(nv_out, &nv); + emit_literal_header(nv_out, token_out, &nv); if (nv.value != inflater->nvbufs.head->buf.pos) { inflater->nv_keep = nv.value; @@ -1524,6 +1878,16 @@ 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; @@ -1536,6 +1900,7 @@ 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 || busy;) { busy = 0; @@ -1622,7 +1987,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; } @@ -1781,9 +2146,9 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, } 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) { @@ -1818,9 +2183,9 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, } 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) { diff --git a/lib/nghttp2_hd.h b/lib/nghttp2_hd.h index ef9fa3ac..2d53d57a 100644 --- a/lib/nghttp2_hd.h +++ b/lib/nghttp2_hd.h @@ -49,7 +49,67 @@ #define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) /* Exported for unit test */ -extern const size_t NGHTTP2_STATIC_TABLE_LENGTH; +#define NGHTTP2_STATIC_TABLE_LENGTH 61 + +typedef enum { + NGHTTP2_TOKEN__AUTHORITY = 0, + NGHTTP2_TOKEN__METHOD = 1, + NGHTTP2_TOKEN__PATH = 3, + NGHTTP2_TOKEN__SCHEME = 5, + NGHTTP2_TOKEN__STATUS = 7, + NGHTTP2_TOKEN_ACCEPT_CHARSET = 14, + NGHTTP2_TOKEN_ACCEPT_ENCODING = 15, + NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16, + NGHTTP2_TOKEN_ACCEPT_RANGES = 17, + NGHTTP2_TOKEN_ACCEPT = 18, + NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19, + NGHTTP2_TOKEN_AGE = 20, + NGHTTP2_TOKEN_ALLOW = 21, + NGHTTP2_TOKEN_AUTHORIZATION = 22, + NGHTTP2_TOKEN_CACHE_CONTROL = 23, + NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24, + NGHTTP2_TOKEN_CONTENT_ENCODING = 25, + NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26, + NGHTTP2_TOKEN_CONTENT_LENGTH = 27, + NGHTTP2_TOKEN_CONTENT_LOCATION = 28, + NGHTTP2_TOKEN_CONTENT_RANGE = 29, + NGHTTP2_TOKEN_CONTENT_TYPE = 30, + NGHTTP2_TOKEN_COOKIE = 31, + NGHTTP2_TOKEN_DATE = 32, + NGHTTP2_TOKEN_ETAG = 33, + NGHTTP2_TOKEN_EXPECT = 34, + NGHTTP2_TOKEN_EXPIRES = 35, + NGHTTP2_TOKEN_FROM = 36, + NGHTTP2_TOKEN_HOST = 37, + NGHTTP2_TOKEN_IF_MATCH = 38, + NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39, + NGHTTP2_TOKEN_IF_NONE_MATCH = 40, + NGHTTP2_TOKEN_IF_RANGE = 41, + NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42, + NGHTTP2_TOKEN_LAST_MODIFIED = 43, + NGHTTP2_TOKEN_LINK = 44, + NGHTTP2_TOKEN_LOCATION = 45, + NGHTTP2_TOKEN_MAX_FORWARDS = 46, + NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47, + NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48, + NGHTTP2_TOKEN_RANGE = 49, + NGHTTP2_TOKEN_REFERER = 50, + NGHTTP2_TOKEN_REFRESH = 51, + NGHTTP2_TOKEN_RETRY_AFTER = 52, + NGHTTP2_TOKEN_SERVER = 53, + NGHTTP2_TOKEN_SET_COOKIE = 54, + NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55, + NGHTTP2_TOKEN_TRANSFER_ENCODING = 56, + NGHTTP2_TOKEN_USER_AGENT = 57, + NGHTTP2_TOKEN_VARY = 58, + NGHTTP2_TOKEN_VIA = 59, + NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, + NGHTTP2_TOKEN_TE, + NGHTTP2_TOKEN_CONNECTION, + NGHTTP2_TOKEN_KEEP_ALIVE, + NGHTTP2_TOKEN_PROXY_CONNECTION, + NGHTTP2_TOKEN_UPGRADE, +} nghttp2_token; typedef enum { NGHTTP2_HD_FLAG_NONE = 0, @@ -67,18 +127,14 @@ typedef enum { typedef struct { nghttp2_nv nv; - uint32_t name_hash; - uint32_t value_hash; + /* nghttp2_token value for nv.name. It could be -1 if we have no + token for that header field name. */ + int token; /* Reference count */ uint8_t ref; uint8_t flags; } 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; @@ -182,9 +238,8 @@ struct nghttp2_hd_inflater { * set in the |flags|, the content pointed by the |name| with length * |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit * set in the |flags|, the content pointed by the |value| with length - * |valuelen| is copied. The |name_hash| and |value_hash| are hash - * value for |name| and |value| respectively. The hash function is - * defined in nghttp2_hd.c. + * |valuelen| is copied. The |token| is enum number looked up by + * |name|. It could be -1 if we don't have that enum value. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -194,8 +249,7 @@ struct nghttp2_hd_inflater { */ 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); void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem); @@ -277,6 +331,18 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); */ void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); +/* + * Similar to nghttp2_hd_inflate_hd(), but this takes additional + * output parameter |token|. On successful header emission, it + * contains nghttp2_token value for nv_out->name. It could be -1 if + * we don't have enum value for the name. Other than that return + * values and semantics are the same as nghttp2_hd_inflate_hd(). + */ +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + int *token, uint8_t *in, size_t inlen, + int in_final); + /* For unittesting purpose */ int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, nghttp2_nv *nv, int indexing_mode); diff --git a/lib/nghttp2_helper.h b/lib/nghttp2_helper.h index 54422a8f..0a7f8c81 100644 --- a/lib/nghttp2_helper.h +++ b/lib/nghttp2_helper.h @@ -29,12 +29,16 @@ #include #endif /* HAVE_CONFIG_H */ +#include + #include #include "nghttp2_mem.h" #define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) #define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) +#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) + /* * Copies 2 byte unsigned integer |n| in host byte order to |buf| in * network byte order. diff --git a/lib/nghttp2_http.c b/lib/nghttp2_http.c index ae49fd63..695307e1 100644 --- a/lib/nghttp2_http.c +++ b/lib/nghttp2_http.c @@ -28,11 +28,8 @@ #include #include -static int memeq(const void *a, const void *b, size_t n) { - return memcmp(a, b, n) == 0; -} - -#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N))) +#include "nghttp2_hd.h" +#include "nghttp2_helper.h" static char downcase(char c) { return 'A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c; @@ -50,129 +47,7 @@ static int memieq(const void *a, const void *b, size_t n) { return 1; } -#define strieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) - -typedef enum { - NGHTTP2_TOKEN__AUTHORITY, - NGHTTP2_TOKEN__METHOD, - NGHTTP2_TOKEN__PATH, - NGHTTP2_TOKEN__SCHEME, - NGHTTP2_TOKEN__STATUS, - NGHTTP2_TOKEN_CONNECTION, - NGHTTP2_TOKEN_CONTENT_LENGTH, - NGHTTP2_TOKEN_HOST, - NGHTTP2_TOKEN_KEEP_ALIVE, - NGHTTP2_TOKEN_PROXY_CONNECTION, - NGHTTP2_TOKEN_TE, - NGHTTP2_TOKEN_TRANSFER_ENCODING, - NGHTTP2_TOKEN_UPGRADE, - NGHTTP2_TOKEN_MAXIDX, -} nghttp2_token; - -/* - * 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 (streq("t", name, 1)) { - return NGHTTP2_TOKEN_TE; - } - break; - } - break; - case 4: - switch (name[3]) { - case 't': - if (streq("hos", name, 3)) { - return NGHTTP2_TOKEN_HOST; - } - break; - } - break; - case 5: - switch (name[4]) { - case 'h': - if (streq(":pat", name, 4)) { - return NGHTTP2_TOKEN__PATH; - } - break; - } - break; - case 7: - switch (name[6]) { - case 'd': - if (streq(":metho", name, 6)) { - return NGHTTP2_TOKEN__METHOD; - } - break; - case 'e': - if (streq(":schem", name, 6)) { - return NGHTTP2_TOKEN__SCHEME; - } - if (streq("upgrad", name, 6)) { - return NGHTTP2_TOKEN_UPGRADE; - } - break; - case 's': - if (streq(":statu", name, 6)) { - return NGHTTP2_TOKEN__STATUS; - } - break; - } - break; - case 10: - switch (name[9]) { - case 'e': - if (streq("keep-aliv", name, 9)) { - return NGHTTP2_TOKEN_KEEP_ALIVE; - } - break; - case 'n': - if (streq("connectio", name, 9)) { - return NGHTTP2_TOKEN_CONNECTION; - } - break; - case 'y': - if (streq(":authorit", name, 9)) { - return NGHTTP2_TOKEN__AUTHORITY; - } - break; - } - break; - case 14: - switch (name[13]) { - case 'h': - if (streq("content-lengt", name, 13)) { - return NGHTTP2_TOKEN_CONTENT_LENGTH; - } - break; - } - break; - case 16: - switch (name[15]) { - case 'n': - if (streq("proxy-connectio", name, 15)) { - return NGHTTP2_TOKEN_PROXY_CONNECTION; - } - break; - } - break; - case 17: - switch (name[16]) { - case 'g': - if (streq("transfer-encodin", name, 16)) { - return NGHTTP2_TOKEN_TRANSFER_ENCODING; - } - break; - } - break; - } - return -1; -} +#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) static int64_t parse_uint(const uint8_t *s, size_t len) { int64_t n = 0; @@ -238,9 +113,7 @@ static int check_path(nghttp2_stream *stream) { } static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, - int trailer) { - int token; - + int token, int trailer) { if (nv->name[0] == ':') { if (trailer || (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { @@ -248,8 +121,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, } } - token = lookup_token(nv->name, nv->namelen); - switch (token) { case NGHTTP2_TOKEN__AUTHORITY: if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) { @@ -262,14 +133,14 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, } switch (nv->valuelen) { case 4: - if (streq("HEAD", nv->value, nv->valuelen)) { + if (lstreq("HEAD", nv->value, nv->valuelen)) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; } break; case 7: switch (nv->value[6]) { case 'T': - if (streq("CONNECT", nv->value, nv->valuelen)) { + if (lstreq("CONNECT", nv->value, nv->valuelen)) { if (stream->stream_id % 2 == 0) { /* we won't allow CONNECT for push */ return NGHTTP2_ERR_HTTP_HEADER; @@ -282,7 +153,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, } break; case 'S': - if (streq("OPTIONS", nv->value, nv->valuelen)) { + if (lstreq("OPTIONS", nv->value, nv->valuelen)) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS; } break; @@ -338,7 +209,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, case NGHTTP2_TOKEN_UPGRADE: return NGHTTP2_ERR_HTTP_HEADER; case NGHTTP2_TOKEN_TE: - if (!strieq("trailers", nv->value, nv->valuelen)) { + if (!lstrieq("trailers", nv->value, nv->valuelen)) { return NGHTTP2_ERR_HTTP_HEADER; } break; @@ -356,9 +227,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, } static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, - int trailer) { - int token; - + int token, int trailer) { if (nv->name[0] == ':') { if (trailer || (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { @@ -366,8 +235,6 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, } } - token = lookup_token(nv->name, nv->namelen); - switch (token) { case NGHTTP2_TOKEN__STATUS: { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) { @@ -400,7 +267,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, case NGHTTP2_TOKEN_UPGRADE: return NGHTTP2_ERR_HTTP_HEADER; case NGHTTP2_TOKEN_TE: - if (!strieq("trailers", nv->value, nv->valuelen)) { + if (!lstrieq("trailers", nv->value, nv->valuelen)) { return NGHTTP2_ERR_HTTP_HEADER; } break; @@ -418,7 +285,8 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, } int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, - nghttp2_frame *frame, nghttp2_nv *nv, int trailer) { + nghttp2_frame *frame, nghttp2_nv *nv, int token, + int trailer) { /* We are strict for pseudo header field. One bad character should lead to fail. OTOH, we should be a bit forgiving for regular headers, since existing public internet has so much illegal @@ -458,10 +326,10 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, } if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { - return http_request_on_header(stream, nv, trailer); + return http_request_on_header(stream, nv, token, trailer); } - return http_response_on_header(stream, nv, trailer); + return http_response_on_header(stream, nv, token, trailer); } int nghttp2_http_on_request_headers(nghttp2_stream *stream, @@ -574,14 +442,15 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream, /* TODO we should do this strictly. */ for (i = 0; i < nvlen; ++i) { const nghttp2_nv *nv = &nva[i]; - if (lookup_token(nv->name, nv->namelen) != NGHTTP2_TOKEN__METHOD) { + if (!(nv->namelen == 7 && nv->name[6] == 'd' && + memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { continue; } - if (streq("CONNECT", nv->value, nv->valuelen)) { + if (lstreq("CONNECT", nv->value, nv->valuelen)) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; return; } - if (streq("HEAD", nv->value, nv->valuelen)) { + if (lstreq("HEAD", nv->value, nv->valuelen)) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; return; } diff --git a/lib/nghttp2_http.h b/lib/nghttp2_http.h index f7966c67..f782058f 100644 --- a/lib/nghttp2_http.h +++ b/lib/nghttp2_http.h @@ -36,7 +36,8 @@ /* * This function is called when HTTP header field |nv| in |frame| is * received for |stream|. This function will validate |nv| against - * the current state of stream. + * the current state of stream. The |token| is nghttp2_token value + * for nv->name, or -1 if we don't have enum value for the name. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -48,7 +49,8 @@ * if it was not received because of compatibility reasons. */ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, - nghttp2_frame *frame, nghttp2_nv *nv, int trailer); + nghttp2_frame *frame, nghttp2_nv *nv, int token, + int trailer); /* * This function is called when request header is received. This diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 238b9d00..a8c4d857 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -3230,6 +3230,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, nghttp2_stream *stream; nghttp2_stream *subject_stream; int trailer = 0; + int token; *readlen_ptr = 0; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); @@ -3245,8 +3246,8 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, DEBUGF(fprintf(stderr, "recv: decoding header block %zu bytes\n", inlen)); for (;;) { inflate_flags = 0; - proclen = nghttp2_hd_inflate_hd(&session->hd_inflater, &nv, &inflate_flags, - in, inlen, final); + proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags, + &token, in, inlen, final); if (nghttp2_is_fatal((int)proclen)) { return (int)proclen; } @@ -3281,7 +3282,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { rv = 0; if (subject_stream && session_enforce_http_messaging(session)) { - rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, + rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, token, trailer); if (rv == NGHTTP2_ERR_HTTP_HEADER) { DEBUGF(fprintf( diff --git a/mkstatichdtbl.py b/mkstatichdtbl.py index b6b6e38b..fbe97a61 100755 --- a/mkstatichdtbl.py +++ b/mkstatichdtbl.py @@ -10,39 +10,17 @@ from __future__ import unicode_literals import re, sys -def hash(s): - h = 0 - for c in s: - h = h * 31 + ord(c) - return h & ((1 << 32) - 1) - entries = [] for line in sys.stdin: m = re.match(r'(\d+)\s+(\S+)\s+(\S.*)?', line) val = m.group(3).strip() if m.group(3) else '' - entries.append((hash(m.group(2)), int(m.group(1)), m.group(2), val)) - -entries.sort() - -print '/* Sorted by hash(name) and its table index */' -print 'static nghttp2_hd_static_entry static_table[] = {' -for ent in entries: - print 'MAKE_STATIC_ENT({}, "{}", "{}", {}u, {}u),'\ - .format(ent[1] - 1, ent[2], ent[3], ent[0], hash(ent[3])) -print '};' - -print '' - -print '/* Index to the position in static_table */' -print 'const size_t static_table_index[] = {' -for i in range(len(entries)): - for j, ent in enumerate(entries): - if ent[1] - 1 == i: - sys.stdout.write('{: <2d},'.format(j)) - break - if (i + 1) % 16 == 0: - sys.stdout.write('\n') - else: - sys.stdout.write(' ') + entries.append((int(m.group(1)), m.group(2), val)) +print 'static nghttp2_hd_entry static_table[] = {' +idx = 0 +for i, ent in enumerate(entries): + if entries[idx][1] != ent[1]: + idx = i + print 'MAKE_STATIC_ENT("{}", "{}", {}),'\ + .format(ent[1], ent[2], entries[idx][0] - 1) print '};'