You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

nginx_hpack_push.patch 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  1. diff --git a/auto/modules b/auto/modules
  2. index 73a9bae9..67fe2cb3 100644
  3. --- a/auto/modules
  4. +++ b/auto/modules
  5. @@ -438,6 +438,10 @@ if [ $HTTP = YES ]; then
  6. . auto/module
  7. fi
  8. + if [ $HTTP_V2_HPACK_ENC = YES ]; then
  9. + have=NGX_HTTP_V2_HPACK_ENC . auto/have
  10. + fi
  11. +
  12. if :; then
  13. ngx_module_name=ngx_http_static_module
  14. ngx_module_incs=
  15. diff --git a/auto/options b/auto/options
  16. index 59f0449d..4abd20ac 100644
  17. --- a/auto/options
  18. +++ b/auto/options
  19. @@ -59,6 +59,7 @@ HTTP_CHARSET=YES
  20. HTTP_GZIP=YES
  21. HTTP_SSL=NO
  22. HTTP_V2=NO
  23. +HTTP_V2_HPACK_ENC=NO
  24. HTTP_SSI=YES
  25. HTTP_POSTPONE=NO
  26. HTTP_REALIP=NO
  27. @@ -223,6 +224,7 @@ $0: warning: the \"--with-ipv6\" option is deprecated"
  28. --with-http_ssl_module) HTTP_SSL=YES ;;
  29. --with-http_v2_module) HTTP_V2=YES ;;
  30. + --with-http_v2_hpack_enc) HTTP_V2_HPACK_ENC=YES ;;
  31. --with-http_realip_module) HTTP_REALIP=YES ;;
  32. --with-http_addition_module) HTTP_ADDITION=YES ;;
  33. --with-http_xslt_module) HTTP_XSLT=YES ;;
  34. @@ -434,6 +436,7 @@ cat << END
  35. --with-http_ssl_module enable ngx_http_ssl_module
  36. --with-http_v2_module enable ngx_http_v2_module
  37. + --with-http_v2_hpack_enc enable ngx_http_v2_hpack_enc
  38. --with-http_realip_module enable ngx_http_realip_module
  39. --with-http_addition_module enable ngx_http_addition_module
  40. --with-http_xslt_module enable ngx_http_xslt_module
  41. diff --git a/src/core/ngx_murmurhash.c b/src/core/ngx_murmurhash.c
  42. index 5ade658d..4932f20d 100644
  43. --- a/src/core/ngx_murmurhash.c
  44. +++ b/src/core/ngx_murmurhash.c
  45. @@ -50,3 +50,63 @@ ngx_murmur_hash2(u_char *data, size_t len)
  46. return h;
  47. }
  48. +
  49. +
  50. +uint64_t
  51. +ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed)
  52. +{
  53. + uint64_t h, k;
  54. +
  55. + h = seed ^ len;
  56. +
  57. + while (len >= 8) {
  58. + k = data[0];
  59. + k |= data[1] << 8;
  60. + k |= data[2] << 16;
  61. + k |= data[3] << 24;
  62. + k |= (uint64_t)data[4] << 32;
  63. + k |= (uint64_t)data[5] << 40;
  64. + k |= (uint64_t)data[6] << 48;
  65. + k |= (uint64_t)data[7] << 56;
  66. +
  67. + k *= 0xc6a4a7935bd1e995ull;
  68. + k ^= k >> 47;
  69. + k *= 0xc6a4a7935bd1e995ull;
  70. +
  71. + h ^= k;
  72. + h *= 0xc6a4a7935bd1e995ull;
  73. +
  74. + data += 8;
  75. + len -= 8;
  76. + }
  77. +
  78. + switch (len) {
  79. + case 7:
  80. + h ^= (uint64_t)data[6] << 48;
  81. + /* fall through */
  82. + case 6:
  83. + h ^= (uint64_t)data[5] << 40;
  84. + /* fall through */
  85. + case 5:
  86. + h ^= (uint64_t)data[4] << 32;
  87. + /* fall through */
  88. + case 4:
  89. + h ^= data[3] << 24;
  90. + /* fall through */
  91. + case 3:
  92. + h ^= data[2] << 16;
  93. + /* fall through */
  94. + case 2:
  95. + h ^= data[1] << 8;
  96. + /* fall through */
  97. + case 1:
  98. + h ^= data[0];
  99. + h *= 0xc6a4a7935bd1e995ull;
  100. + }
  101. +
  102. + h ^= h >> 47;
  103. + h *= 0xc6a4a7935bd1e995ull;
  104. + h ^= h >> 47;
  105. +
  106. + return h;
  107. +}
  108. diff --git a/src/core/ngx_murmurhash.h b/src/core/ngx_murmurhash.h
  109. index 54e867d3..322b3df9 100644
  110. --- a/src/core/ngx_murmurhash.h
  111. +++ b/src/core/ngx_murmurhash.h
  112. @@ -15,5 +15,7 @@
  113. uint32_t ngx_murmur_hash2(u_char *data, size_t len);
  114. +uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed);
  115. +
  116. #endif /* _NGX_MURMURHASH_H_INCLUDED_ */
  117. diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
  118. index 77ebb847..303d157f 100644
  119. --- a/src/http/v2/ngx_http_v2.c
  120. +++ b/src/http/v2/ngx_http_v2.c
  121. @@ -272,6 +272,8 @@ ngx_http_v2_init(ngx_event_t *rev)
  122. h2c->table_update = 1;
  123. + h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE;
  124. +
  125. h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);
  126. h2c->concurrent_pushes = h2scf->concurrent_pushes;
  127. @@ -2075,6 +2077,17 @@ ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,
  128. h2c->concurrent_pushes = ngx_min(value, h2scf->concurrent_pushes);
  129. break;
  130. + case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:
  131. +
  132. + if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {
  133. + h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE;
  134. + } else {
  135. + h2c->max_hpack_table_size = value;
  136. + }
  137. +
  138. + h2c->indicate_resize = 1;
  139. + break;
  140. +
  141. default:
  142. break;
  143. }
  144. diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h
  145. index ebd0e77c..b152b504 100644
  146. --- a/src/http/v2/ngx_http_v2.h
  147. +++ b/src/http/v2/ngx_http_v2.h
  148. @@ -52,6 +52,14 @@
  149. #define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1)
  150. #define NGX_HTTP_V2_DEFAULT_WINDOW 65535
  151. +#define HPACK_ENC_HTABLE_SZ 128 /* better to keep a PoT < 64k */
  152. +#define HPACK_ENC_HTABLE_ENTRIES ((HPACK_ENC_HTABLE_SZ * 100) / 128)
  153. +#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ 10 /* 10 is sufficient for most */
  154. +#define HPACK_ENC_MAX_ENTRY 512 /* longest header size to match */
  155. +
  156. +#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE 4096
  157. +#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE 16384 /* < 64k */
  158. +
  159. #define NGX_HTTP_V2_DEFAULT_WEIGHT 16
  160. @@ -115,6 +123,46 @@ typedef struct {
  161. } ngx_http_v2_hpack_t;
  162. +#if (NGX_HTTP_V2_HPACK_ENC)
  163. +typedef struct {
  164. + uint64_t hash_val;
  165. + uint32_t index;
  166. + uint16_t pos;
  167. + uint16_t klen, vlen;
  168. + uint16_t size;
  169. + uint16_t next;
  170. +} ngx_http_v2_hpack_enc_entry_t;
  171. +
  172. +
  173. +typedef struct {
  174. + uint64_t hash_val;
  175. + uint32_t index;
  176. + uint16_t pos;
  177. + uint16_t klen;
  178. +} ngx_http_v2_hpack_name_entry_t;
  179. +
  180. +
  181. +typedef struct {
  182. + size_t size; /* size as defined in RFC 7541 */
  183. + uint32_t top; /* the last entry */
  184. + uint32_t pos;
  185. + uint16_t n_elems; /* number of elements */
  186. + uint16_t base; /* index of the oldest entry */
  187. + uint16_t last; /* index of the newest entry */
  188. +
  189. + /* hash table for dynamic entries, instead using a generic hash table,
  190. + which would be too slow to process a significant amount of headers,
  191. + this table is not determenistic, and might ocasionally fail to insert
  192. + a value, at the cost of slightly worse compression, but significantly
  193. + faster performance */
  194. + ngx_http_v2_hpack_enc_entry_t htable[HPACK_ENC_HTABLE_SZ];
  195. + ngx_http_v2_hpack_name_entry_t heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ];
  196. + u_char storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE +
  197. + HPACK_ENC_MAX_ENTRY];
  198. +} ngx_http_v2_hpack_enc_t;
  199. +#endif
  200. +
  201. +
  202. struct ngx_http_v2_connection_s {
  203. ngx_connection_t *connection;
  204. ngx_http_connection_t *http_connection;
  205. @@ -130,6 +178,8 @@ struct ngx_http_v2_connection_s {
  206. size_t frame_size;
  207. + size_t max_hpack_table_size;
  208. +
  209. ngx_queue_t waiting;
  210. ngx_http_v2_state_t state;
  211. @@ -157,6 +207,11 @@ struct ngx_http_v2_connection_s {
  212. unsigned blocked:1;
  213. unsigned goaway:1;
  214. unsigned push_disabled:1;
  215. + unsigned indicate_resize:1;
  216. +
  217. +#if (NGX_HTTP_V2_HPACK_ENC)
  218. + ngx_http_v2_hpack_enc_t hpack_enc;
  219. +#endif
  220. };
  221. @@ -198,6 +253,8 @@ struct ngx_http_v2_stream_s {
  222. ngx_array_t *cookies;
  223. + size_t header_limit;
  224. +
  225. ngx_pool_t *pool;
  226. unsigned waiting:1;
  227. @@ -410,4 +467,35 @@ u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,
  228. u_char *tmp, ngx_uint_t lower);
  229. +u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,
  230. + u_char *tmp, ngx_uint_t lower);
  231. +
  232. +u_char *
  233. +ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value);
  234. +
  235. +#define ngx_http_v2_write_name(dst, src, len, tmp) \
  236. + ngx_http_v2_string_encode(dst, src, len, tmp, 1)
  237. +#define ngx_http_v2_write_value(dst, src, len, tmp) \
  238. + ngx_http_v2_string_encode(dst, src, len, tmp, 0)
  239. +
  240. +u_char *
  241. +ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,
  242. + u_char *key, size_t key_len, u_char *value, size_t value_len,
  243. + u_char *tmp);
  244. +
  245. +void
  246. +ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c);
  247. +
  248. +#define ngx_http_v2_write_header_str(key, value) \
  249. + ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \
  250. + (u_char *) value, sizeof(value) - 1, tmp);
  251. +
  252. +#define ngx_http_v2_write_header_tbl(key, val) \
  253. + ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \
  254. + val.data, val.len, tmp);
  255. +
  256. +#define ngx_http_v2_write_header_pot(key, val) \
  257. + ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \
  258. + val->data, val->len, tmp);
  259. +
  260. #endif /* _NGX_HTTP_V2_H_INCLUDED_ */
  261. diff --git a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c
  262. index ac792084..d1fb7217 100644
  263. --- a/src/http/v2/ngx_http_v2_encode.c
  264. +++ b/src/http/v2/ngx_http_v2_encode.c
  265. @@ -10,7 +10,7 @@
  266. #include <ngx_http.h>
  267. -static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
  268. +u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
  269. ngx_uint_t value);
  270. @@ -40,7 +40,7 @@ ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp,
  271. }
  272. -static u_char *
  273. +u_char *
  274. ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)
  275. {
  276. if (value < prefix) {
  277. diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c
  278. index 029e8ece..d293f06c 100644
  279. --- a/src/http/v2/ngx_http_v2_filter_module.c
  280. +++ b/src/http/v2/ngx_http_v2_filter_module.c
  281. @@ -23,10 +23,53 @@
  282. #define ngx_http_v2_literal_size(h) \
  283. (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)
  284. +#define ngx_http_v2_indexed(i) (128 + (i))
  285. +#define ngx_http_v2_inc_indexed(i) (64 + (i))
  286. +
  287. +#define NGX_HTTP_V2_ENCODE_RAW 0
  288. +#define NGX_HTTP_V2_ENCODE_HUFF 0x80
  289. +
  290. +#define NGX_HTTP_V2_AUTHORITY_INDEX 1
  291. +#define NGX_HTTP_V2_METHOD_GET_INDEX 2
  292. +#define NGX_HTTP_V2_PATH_INDEX 4
  293. +
  294. +#define NGX_HTTP_V2_SCHEME_HTTP_INDEX 6
  295. +#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX 7
  296. +
  297. +#define NGX_HTTP_V2_STATUS_INDEX 8
  298. +#define NGX_HTTP_V2_STATUS_200_INDEX 8
  299. +#define NGX_HTTP_V2_STATUS_204_INDEX 9
  300. +#define NGX_HTTP_V2_STATUS_206_INDEX 10
  301. +#define NGX_HTTP_V2_STATUS_304_INDEX 11
  302. +#define NGX_HTTP_V2_STATUS_400_INDEX 12
  303. +#define NGX_HTTP_V2_STATUS_404_INDEX 13
  304. +#define NGX_HTTP_V2_STATUS_500_INDEX 14
  305. +
  306. +#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16
  307. +#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17
  308. +#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28
  309. +#define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31
  310. +#define NGX_HTTP_V2_DATE_INDEX 33
  311. +#define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44
  312. +#define NGX_HTTP_V2_LOCATION_INDEX 46
  313. +#define NGX_HTTP_V2_SERVER_INDEX 54
  314. +#define NGX_HTTP_V2_USER_AGENT_INDEX 58
  315. +#define NGX_HTTP_V2_VARY_INDEX 59
  316. #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1
  317. +static const struct {
  318. + u_char *name;
  319. + u_char const len;
  320. +} push_header[] = {
  321. + { (u_char*)":authority" , 10 },
  322. + { (u_char*)"accept-encoding" , 15 },
  323. + { (u_char*)"accept-language" , 15 },
  324. + { (u_char*)"user-agent" , 10 }
  325. +};
  326. +
  327. +
  328. typedef struct {
  329. ngx_str_t name;
  330. u_char index;
  331. @@ -155,11 +198,9 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
  332. #endif
  333. static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);
  334. - static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];
  335. static size_t nginx_ver_build_len =
  336. ngx_http_v2_literal_size(NGINX_VER_BUILD);
  337. - static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];
  338. stream = r->stream;
  339. @@ -435,7 +476,7 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
  340. }
  341. tmp = ngx_palloc(r->pool, tmp_len);
  342. - pos = ngx_pnalloc(r->pool, len);
  343. + pos = ngx_pnalloc(r->pool, len + 15 + 1);
  344. if (pos == NULL || tmp == NULL) {
  345. return NGX_ERROR;
  346. @@ -443,11 +484,16 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
  347. start = pos;
  348. - if (h2c->table_update) {
  349. - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  350. - "http2 table size update: 0");
  351. - *pos++ = (1 << 5) | 0;
  352. - h2c->table_update = 0;
  353. + h2c = r->stream->connection;
  354. +
  355. + if (h2c->indicate_resize) {
  356. + *pos = 32;
  357. + pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),
  358. + h2c->max_hpack_table_size);
  359. + h2c->indicate_resize = 0;
  360. +#if (NGX_HTTP_V2_HPACK_ENC)
  361. + ngx_http_v2_table_resize(h2c);
  362. +#endif
  363. }
  364. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  365. @@ -458,67 +504,28 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
  366. *pos++ = status;
  367. } else {
  368. - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);
  369. - *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;
  370. - pos = ngx_sprintf(pos, "%03ui", r->headers_out.status);
  371. + ngx_sprintf(pos + 8, "%O3ui", r->headers_out.status);
  372. + pos = ngx_http_v2_write_header(h2c, pos, (u_char *)":status",
  373. + sizeof(":status") - 1, pos + 8, 3, tmp);
  374. }
  375. if (r->headers_out.server == NULL) {
  376. -
  377. - if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
  378. - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  379. - "http2 output header: \"server: %s\"",
  380. - NGINX_VER);
  381. -
  382. - } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
  383. - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  384. - "http2 output header: \"server: %s\"",
  385. - NGINX_VER_BUILD);
  386. -
  387. - } else {
  388. - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  389. - "http2 output header: \"server: nginx\"");
  390. - }
  391. -
  392. - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);
  393. -
  394. if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
  395. - if (nginx_ver[0] == '\0') {
  396. - p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,
  397. - sizeof(NGINX_VER) - 1, tmp);
  398. - nginx_ver_len = p - nginx_ver;
  399. - }
  400. -
  401. - pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);
  402. + pos = ngx_http_v2_write_header_str("server", NGINX_VER);
  403. } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
  404. - if (nginx_ver_build[0] == '\0') {
  405. - p = ngx_http_v2_write_value(nginx_ver_build,
  406. - (u_char *) NGINX_VER_BUILD,
  407. - sizeof(NGINX_VER_BUILD) - 1, tmp);
  408. - nginx_ver_build_len = p - nginx_ver_build;
  409. - }
  410. -
  411. - pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);
  412. + pos = ngx_http_v2_write_header_str("server", NGINX_VER_BUILD);
  413. } else {
  414. - pos = ngx_cpymem(pos, nginx, sizeof(nginx));
  415. + pos = ngx_http_v2_write_header_str("server", "nginx");
  416. }
  417. }
  418. if (r->headers_out.date == NULL) {
  419. - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  420. - "http2 output header: \"date: %V\"",
  421. - &ngx_cached_http_time);
  422. -
  423. - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);
  424. - pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,
  425. - ngx_cached_http_time.len, tmp);
  426. + pos = ngx_http_v2_write_header_tbl("date", ngx_cached_http_time);
  427. }
  428. if (r->headers_out.content_type.len) {
  429. - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);
  430. -
  431. if (r->headers_out.content_type_len == r->headers_out.content_type.len
  432. && r->headers_out.charset.len)
  433. {
  434. @@ -544,64 +551,36 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
  435. r->headers_out.content_type.data = p - len;
  436. }
  437. - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  438. - "http2 output header: \"content-type: %V\"",
  439. - &r->headers_out.content_type);
  440. -
  441. - pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,
  442. - r->headers_out.content_type.len, tmp);
  443. + pos = ngx_http_v2_write_header_tbl("content-type",
  444. + r->headers_out.content_type);
  445. }
  446. if (r->headers_out.content_length == NULL
  447. && r->headers_out.content_length_n >= 0)
  448. {
  449. - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  450. - "http2 output header: \"content-length: %O\"",
  451. - r->headers_out.content_length_n);
  452. -
  453. - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);
  454. -
  455. - p = pos;
  456. - pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n);
  457. - *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);
  458. + p = ngx_sprintf(pos + 15, "%O", r->headers_out.content_length_n);
  459. + pos = ngx_http_v2_write_header(h2c, pos, (u_char *)"content-length",
  460. + sizeof("content-length") - 1, pos + 15,
  461. + p - (pos + 15), tmp);
  462. }
  463. if (r->headers_out.last_modified == NULL
  464. && r->headers_out.last_modified_time != -1)
  465. {
  466. - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);
  467. -
  468. - ngx_http_time(pos, r->headers_out.last_modified_time);
  469. + ngx_http_time(pos + 14, r->headers_out.last_modified_time);
  470. len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1;
  471. -
  472. - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  473. - "http2 output header: \"last-modified: %*s\"",
  474. - len, pos);
  475. -
  476. - /*
  477. - * Date will always be encoded using huffman in the temporary buffer,
  478. - * so it's safe here to use src and dst pointing to the same address.
  479. - */
  480. - pos = ngx_http_v2_write_value(pos, pos, len, tmp);
  481. + pos = ngx_http_v2_write_header(h2c, pos, (u_char *)"last-modified",
  482. + sizeof("last-modified") - 1, pos + 14,
  483. + len, tmp);
  484. }
  485. if (r->headers_out.location && r->headers_out.location->value.len) {
  486. - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  487. - "http2 output header: \"location: %V\"",
  488. - &r->headers_out.location->value);
  489. -
  490. - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);
  491. - pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,
  492. - r->headers_out.location->value.len, tmp);
  493. + pos = ngx_http_v2_write_header_tbl("location", r->headers_out.location->value);
  494. }
  495. #if (NGX_HTTP_GZIP)
  496. if (r->gzip_vary) {
  497. - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  498. - "http2 output header: \"vary: Accept-Encoding\"");
  499. -
  500. - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);
  501. - pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));
  502. + pos = ngx_http_v2_write_header_str("vary", "Accept-Encoding");
  503. }
  504. #endif
  505. @@ -624,23 +603,10 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
  506. continue;
  507. }
  508. -#if (NGX_DEBUG)
  509. - if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
  510. - ngx_strlow(tmp, header[i].key.data, header[i].key.len);
  511. -
  512. - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  513. - "http2 output header: \"%*s: %V\"",
  514. - header[i].key.len, tmp, &header[i].value);
  515. - }
  516. -#endif
  517. -
  518. - *pos++ = 0;
  519. + pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data,
  520. + header[i].key.len, header[i].value.data,
  521. + header[i].value.len, tmp);
  522. - pos = ngx_http_v2_write_name(pos, header[i].key.data,
  523. - header[i].key.len, tmp);
  524. -
  525. - pos = ngx_http_v2_write_value(pos, header[i].value.data,
  526. - header[i].value.len, tmp);
  527. }
  528. fin = r->header_only
  529. @@ -998,6 +964,7 @@ ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
  530. for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
  531. len += binary[i].len;
  532. + len += push_header[i].len + 1;
  533. }
  534. pos = ngx_pnalloc(r->pool, len);
  535. @@ -1007,12 +974,17 @@ ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
  536. start = pos;
  537. - if (h2c->table_update) {
  538. - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  539. - "http2 table size update: 0");
  540. - *pos++ = (1 << 5) | 0;
  541. - h2c->table_update = 0;
  542. - }
  543. + h2c = r->stream->connection;
  544. +
  545. + if (h2c->indicate_resize) {
  546. + *pos = 32;
  547. + pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),
  548. + h2c->max_hpack_table_size);
  549. + h2c->indicate_resize = 0;
  550. +#if (NGX_HTTP_V2_HPACK_ENC)
  551. + ngx_http_v2_table_resize(h2c);
  552. +#endif
  553. + }
  554. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  555. "http2 push header: \":method: GET\"");
  556. @@ -1022,8 +994,7 @@ ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
  557. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  558. "http2 push header: \":path: %V\"", path);
  559. - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
  560. - pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);
  561. + pos = ngx_http_v2_write_header_pot(":path", path);
  562. #if (NGX_HTTP_SSL)
  563. if (fc->ssl) {
  564. @@ -1046,11 +1017,15 @@ ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
  565. continue;
  566. }
  567. + value = &(*h)->value;
  568. +
  569. ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
  570. "http2 push header: \"%V: %V\"",
  571. &ph[i].name, &(*h)->value);
  572. - pos = ngx_cpymem(pos, binary[i].data, binary[i].len);
  573. + pos = ngx_http_v2_write_header(h2c, pos,
  574. + push_header[i].name, push_header[i].len, value->data, value->len,
  575. + tmp);
  576. }
  577. frame = ngx_http_v2_create_push_frame(r, start, pos);
  578. diff --git a/src/http/v2/ngx_http_v2_table.c b/src/http/v2/ngx_http_v2_table.c
  579. index 7d49803f..b9ee2048 100644
  580. --- a/src/http/v2/ngx_http_v2_table.c
  581. +++ b/src/http/v2/ngx_http_v2_table.c
  582. @@ -361,3 +361,434 @@ ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size)
  583. return NGX_OK;
  584. }
  585. +
  586. +
  587. +#if (NGX_HTTP_V2_HPACK_ENC)
  588. +
  589. +static ngx_int_t
  590. +hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len);
  591. +
  592. +static ngx_int_t
  593. +hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,
  594. + uint8_t *key, size_t key_len);
  595. +
  596. +
  597. +void
  598. +ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c)
  599. +{
  600. + ngx_http_v2_hpack_enc_entry_t *table;
  601. + uint64_t idx;
  602. +
  603. + table = h2c->hpack_enc.htable;
  604. +
  605. + while (h2c->hpack_enc.size > h2c->max_hpack_table_size) {
  606. + idx = h2c->hpack_enc.base;
  607. + h2c->hpack_enc.base = table[idx].next;
  608. + h2c->hpack_enc.size -= table[idx].size;
  609. + table[idx].hash_val = 0;
  610. + h2c->hpack_enc.n_elems--;
  611. + }
  612. +}
  613. +
  614. +
  615. +/* checks if a header is in the hpack table - if so returns the table entry,
  616. + otherwise encodes and inserts into the table and returns 0,
  617. + if failed to insert into table, returns -1 */
  618. +static ngx_int_t
  619. +ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c,
  620. + size_t key_len, size_t val_len, uint8_t *key, uint8_t *val,
  621. + ngx_int_t *header_idx)
  622. +{
  623. + uint64_t hash_val, key_hash, idx, lru;
  624. + int i;
  625. + size_t size = key_len + val_len + 32;
  626. + uint8_t *storage = h2c->hpack_enc.storage;
  627. +
  628. + ngx_http_v2_hpack_enc_entry_t *table;
  629. + ngx_http_v2_hpack_name_entry_t *name;
  630. +
  631. + *header_idx = NGX_ERROR;
  632. + /* step 1: compute the hash value of header */
  633. + if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) {
  634. + return NGX_ERROR;
  635. + }
  636. +
  637. + key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234);
  638. + hash_val = ngx_murmur_hash2_64(val, val_len, key_hash);
  639. +
  640. + if (hash_val == 0) {
  641. + return NGX_ERROR;
  642. + }
  643. +
  644. + /* step 2: check if full header in the table */
  645. + idx = hash_val;
  646. + i = -1;
  647. + while (idx) {
  648. + /* at most 8 locations are checked, but most will be done in 1 or 2 */
  649. + table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ];
  650. + if (table->hash_val == hash_val
  651. + && table->klen == key_len
  652. + && table->vlen == val_len
  653. + && ngx_memcmp(key, storage + table->pos, key_len) == 0
  654. + && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0)
  655. + {
  656. + return (h2c->hpack_enc.top - table->index) + 61;
  657. + }
  658. +
  659. + if (table->hash_val == 0 && i == -1) {
  660. + i = idx % HPACK_ENC_HTABLE_SZ;
  661. + break;
  662. + }
  663. +
  664. + idx >>= 8;
  665. + }
  666. +
  667. + /* step 3: check if key is in one of the tables */
  668. + *header_idx = hpack_get_static_index(h2c, key, key_len);
  669. +
  670. + if (i == -1) {
  671. + return NGX_ERROR;
  672. + }
  673. +
  674. + if (*header_idx == NGX_ERROR) {
  675. + *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len);
  676. + }
  677. +
  678. + /* step 4: store the new entry */
  679. + table = h2c->hpack_enc.htable;
  680. +
  681. + if (h2c->hpack_enc.top == 0xffffffff) {
  682. + /* just to be on the safe side, avoid overflow */
  683. + ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t));
  684. + }
  685. +
  686. + while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size)
  687. + || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) {
  688. + /* make space for the new entry first */
  689. + idx = h2c->hpack_enc.base;
  690. + h2c->hpack_enc.base = table[idx].next;
  691. + h2c->hpack_enc.size -= table[idx].size;
  692. + table[idx].hash_val = 0;
  693. + h2c->hpack_enc.n_elems--;
  694. + }
  695. +
  696. + table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val,
  697. + .index = h2c->hpack_enc.top,
  698. + .pos = h2c->hpack_enc.pos,
  699. + .klen = key_len,
  700. + .vlen = val_len,
  701. + .size = size,
  702. + .next = 0};
  703. +
  704. + table[h2c->hpack_enc.last].next = i;
  705. + if (h2c->hpack_enc.n_elems == 0) {
  706. + h2c->hpack_enc.base = i;
  707. + }
  708. +
  709. + h2c->hpack_enc.last = i;
  710. + h2c->hpack_enc.top++;
  711. + h2c->hpack_enc.size += size;
  712. + h2c->hpack_enc.n_elems++;
  713. +
  714. + /* update header name lookup */
  715. + if (*header_idx == NGX_ERROR ) {
  716. + lru = h2c->hpack_enc.top;
  717. +
  718. + for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {
  719. +
  720. + name = &h2c->hpack_enc.heads[i];
  721. +
  722. + if ( name->hash_val == 0 || (name->hash_val == key_hash
  723. + && ngx_memcmp(storage + name->pos, key, key_len) == 0) )
  724. + {
  725. + name->hash_val = key_hash;
  726. + name->pos = h2c->hpack_enc.pos;
  727. + name->index = h2c->hpack_enc.top - 1;
  728. + break;
  729. + }
  730. +
  731. + if (lru > name->index) {
  732. + lru = name->index;
  733. + idx = i;
  734. + }
  735. + }
  736. +
  737. + if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) {
  738. + name = &h2c->hpack_enc.heads[idx];
  739. + name->hash_val = hash_val;
  740. + name->pos = h2c->hpack_enc.pos;
  741. + name->index = h2c->hpack_enc.top - 1;
  742. + }
  743. + }
  744. +
  745. + ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len);
  746. + ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len);
  747. +
  748. + h2c->hpack_enc.pos += size;
  749. + if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {
  750. + h2c->hpack_enc.pos = 0;
  751. + }
  752. +
  753. + return NGX_OK;
  754. +}
  755. +
  756. +
  757. +u_char *
  758. +ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,
  759. + u_char *key, size_t key_len,
  760. + u_char *value, size_t value_len,
  761. + u_char *tmp)
  762. +{
  763. + ngx_int_t idx, header_idx;
  764. +
  765. + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  766. + "http2 output header: %*s: %*s", key_len, key, value_len,
  767. + value);
  768. +
  769. + /* attempt to find the value in the dynamic table */
  770. + idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value,
  771. + &header_idx);
  772. +
  773. + if (idx > 0) {
  774. + /* positive index indicates success */
  775. + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  776. + "http2 hpack encode: Indexed Header Field: %ud", idx);
  777. +
  778. + *pos = 128;
  779. + pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx);
  780. +
  781. + } else {
  782. +
  783. + if (header_idx == NGX_ERROR) { /* if key is not present */
  784. +
  785. + if (idx == NGX_ERROR) { /* if header was not added */
  786. + *pos++ = 0;
  787. +
  788. + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  789. + "http2 hpack encode: Literal Header Field without"
  790. + " Indexing — New Name");
  791. + } else { /* if header was added */
  792. + *pos++ = 64;
  793. +
  794. + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  795. + "http2 hpack encode: Literal Header Field with "
  796. + "Incremental Indexing — New Name");
  797. + }
  798. +
  799. + pos = ngx_http_v2_write_name(pos, key, key_len, tmp);
  800. +
  801. + } else { /* if key is present */
  802. +
  803. + if (idx == NGX_ERROR) {
  804. + *pos = 0;
  805. + pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx);
  806. +
  807. + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  808. + "http2 hpack encode: Literal Header Field without"
  809. + " Indexing — Indexed Name: %ud", header_idx);
  810. + } else {
  811. + *pos = 64;
  812. + pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx);
  813. +
  814. + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  815. + "http2 hpack encode: Literal Header Field with "
  816. + "Incremental Indexing — Indexed Name: %ud", header_idx);
  817. + }
  818. + }
  819. +
  820. + pos = ngx_http_v2_write_value(pos, value, value_len, tmp);
  821. + }
  822. +
  823. + return pos;
  824. +}
  825. +
  826. +
  827. +static ngx_int_t
  828. +hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,
  829. + uint8_t *key, size_t key_len)
  830. +{
  831. + ngx_http_v2_hpack_name_entry_t *name;
  832. + int i;
  833. +
  834. + for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {
  835. + name = &h2c->hpack_enc.heads[i];
  836. +
  837. + if (name->hash_val == key_hash
  838. + && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0)
  839. + {
  840. + if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) {
  841. + return (h2c->hpack_enc.top - name->index) + 61;
  842. + }
  843. + break;
  844. + }
  845. + }
  846. +
  847. + return NGX_ERROR;
  848. +}
  849. +
  850. +
  851. +/* decide if a given header is present in the static dictionary, this could be
  852. + done in several ways, but it seems the fastest one is "exhaustive" search */
  853. +static ngx_int_t
  854. +hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len)
  855. +{
  856. + /* the static dictionary of response only headers,
  857. + although response headers can be put by origin,
  858. + that would be rare */
  859. + static const struct {
  860. + u_char len;
  861. + const u_char val[28];
  862. + u_char idx;
  863. + } server_headers[] = {
  864. + { 3, "age", 21},//0
  865. + { 3, "via", 60},
  866. + { 4, "date", 33},//2
  867. + { 4, "etag", 34},
  868. + { 4, "link", 45},
  869. + { 4, "vary", 59},
  870. + { 5, "allow", 22},//6
  871. + { 6, "server", 54},//7
  872. + { 7, "expires", 36},//8
  873. + { 7, "refresh", 52},
  874. + { 8, "location", 46},//10
  875. + {10, "set-cookie", 55},//11
  876. + {11, "retry-after", 53},//12
  877. + {12, "content-type", 31},//13
  878. + {13, "content-range", 30},//14
  879. + {13, "accept-ranges", 18},
  880. + {13, "cache-control", 24},
  881. + {13, "last-modified", 44},
  882. + {14, "content-length", 28},//18
  883. + {16, "content-encoding", 26},//19
  884. + {16, "content-language", 27},
  885. + {16, "content-location", 29},
  886. + {16, "www-authenticate", 61},
  887. + {17, "transfer-encoding", 57},//23
  888. + {18, "proxy-authenticate", 48},//24
  889. + {19, "content-disposition", 25},//25
  890. + {25, "strict-transport-security", 56},//26
  891. + {27, "access-control-allow-origin", 20},//27
  892. + {99, "", 99},
  893. + }, *header;
  894. +
  895. + /* for a given length, where to start the search
  896. + since minimal length is 3, the table has a -3
  897. + offset */
  898. + static const int8_t start_at[] = {
  899. + [3-3] = 0,
  900. + [4-3] = 2,
  901. + [5-3] = 6,
  902. + [6-3] = 7,
  903. + [7-3] = 8,
  904. + [8-3] = 10,
  905. + [9-3] = -1,
  906. + [10-3] = 11,
  907. + [11-3] = 12,
  908. + [12-3] = 13,
  909. + [13-3] = 14,
  910. + [14-3] = 18,
  911. + [15-3] = -1,
  912. + [16-3] = 19,
  913. + [17-3] = 23,
  914. + [18-3] = 24,
  915. + [19-3] = 25,
  916. + [20-3] = -1,
  917. + [21-3] = -1,
  918. + [22-3] = -1,
  919. + [23-3] = -1,
  920. + [24-3] = -1,
  921. + [25-3] = 26,
  922. + [26-3] = -1,
  923. + [27-3] = 27,
  924. + };
  925. +
  926. + uint64_t pref;
  927. + size_t save_len = len, i;
  928. + int8_t start;
  929. +
  930. + /* early exit for out of bounds lengths */
  931. + if (len < 3 || len > 27) {
  932. + return NGX_ERROR;
  933. + }
  934. +
  935. + start = start_at[len - 3];
  936. + if (start == -1) {
  937. + /* exit for non existent lengths */
  938. + return NGX_ERROR;
  939. + }
  940. +
  941. + header = &server_headers[start_at[len - 3]];
  942. +
  943. + /* load first 8 bytes of key, for fast comparison */
  944. + if (len < 8) {
  945. + pref = 0;
  946. + if (len >= 4) {
  947. + pref = *(uint32_t *)(val + len - 4) | 0x20202020;
  948. + len -= 4;
  949. + }
  950. + while (len > 0) { /* 3 iterations at most */
  951. + pref = (pref << 8) ^ (val[len - 1] | 0x20);
  952. + len--;
  953. + }
  954. + } else {
  955. + pref = *(uint64_t *)val | 0x2020202020202020;
  956. + len -= 8;
  957. + }
  958. +
  959. + /* iterate over headers with the right length */
  960. + while (header->len == save_len) {
  961. + /* quickly compare the first 8 bytes, most tests will end here */
  962. + if (pref != *(uint64_t *) header->val) {
  963. + header++;
  964. + continue;
  965. + }
  966. +
  967. + if (len == 0) {
  968. + /* len == 0, indicates prefix held the entire key */
  969. + return header->idx;
  970. + }
  971. + /* for longer keys compare the rest */
  972. + i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */
  973. +
  974. + while (i + 8 <= save_len) { /* 3 iterations at most */
  975. + if ( *(uint64_t *)&header->val[i]
  976. + != (*(uint64_t *) &val[i]| 0x2020202020202020) )
  977. + {
  978. + header++;
  979. + i = 0;
  980. + break;
  981. + }
  982. + i += 8;
  983. + }
  984. +
  985. + if (i == 0) {
  986. + continue;
  987. + }
  988. +
  989. + /* found the corresponding entry in the static dictionary */
  990. + return header->idx;
  991. + }
  992. +
  993. + return NGX_ERROR;
  994. +}
  995. +
  996. +#else
  997. +
  998. +u_char *
  999. +ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,
  1000. + u_char *key, size_t key_len,
  1001. + u_char *value, size_t value_len,
  1002. + u_char *tmp)
  1003. +{
  1004. + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  1005. + "http2 output header: %*s: %*s", key_len, key, value_len,
  1006. + value);
  1007. +
  1008. + *pos++ = 64;
  1009. + pos = ngx_http_v2_write_name(pos, key, key_len, tmp);
  1010. + pos = ngx_http_v2_write_value(pos, value, value_len, tmp);
  1011. +
  1012. + return pos;
  1013. +}
  1014. +
  1015. +#endif