/src/libwebsockets/lib/roles/h2/hpack.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * libwebsockets - small server side websockets and web server implementation |
3 | | * |
4 | | * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> |
5 | | * |
6 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | | * of this software and associated documentation files (the "Software"), to |
8 | | * deal in the Software without restriction, including without limitation the |
9 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
10 | | * sell copies of the Software, and to permit persons to whom the Software is |
11 | | * furnished to do so, subject to the following conditions: |
12 | | * |
13 | | * The above copyright notice and this permission notice shall be included in |
14 | | * all copies or substantial portions of the Software. |
15 | | * |
16 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
21 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
22 | | * IN THE SOFTWARE. |
23 | | */ |
24 | | |
25 | | #include "private-lib-core.h" |
26 | | |
27 | | /* |
28 | | * Official static header table for HPACK |
29 | | * +-------+-----------------------------+---------------+ |
30 | | | 1 | :authority | | |
31 | | | 2 | :method | GET | |
32 | | | 3 | :method | POST | |
33 | | | 4 | :path | / | |
34 | | | 5 | :path | /index.html | |
35 | | | 6 | :scheme | http | |
36 | | | 7 | :scheme | https | |
37 | | | 8 | :status | 200 | |
38 | | | 9 | :status | 204 | |
39 | | | 10 | :status | 206 | |
40 | | | 11 | :status | 304 | |
41 | | | 12 | :status | 400 | |
42 | | | 13 | :status | 404 | |
43 | | | 14 | :status | 500 | |
44 | | | 15 | accept-charset | | |
45 | | | 16 | accept-encoding | gzip, deflate | |
46 | | | 17 | accept-language | | |
47 | | | 18 | accept-ranges | | |
48 | | | 19 | accept | | |
49 | | | 20 | access-control-allow-origin | | |
50 | | | 21 | age | | |
51 | | | 22 | allow | | |
52 | | | 23 | authorization | | |
53 | | | 24 | cache-control | | |
54 | | | 25 | content-disposition | | |
55 | | | 26 | content-encoding | | |
56 | | | 27 | content-language | | |
57 | | | 28 | content-length | | |
58 | | | 29 | content-location | | |
59 | | | 30 | content-range | | |
60 | | | 31 | content-type | | |
61 | | | 32 | cookie | | |
62 | | | 33 | date | | |
63 | | | 34 | etag | | |
64 | | | 35 | expect | | |
65 | | | 36 | expires | | |
66 | | | 37 | from | | |
67 | | | 38 | host | | |
68 | | | 39 | if-match | | |
69 | | | 40 | if-modified-since | | |
70 | | | 41 | if-none-match | | |
71 | | | 42 | if-range | | |
72 | | | 43 | if-unmodified-since | | |
73 | | | 44 | last-modified | | |
74 | | | 45 | link | | |
75 | | | 46 | location | | |
76 | | | 47 | max-forwards | | |
77 | | | 48 | proxy-authenticate | | |
78 | | | 49 | proxy-authorization | | |
79 | | | 50 | range | | |
80 | | | 51 | referer | | |
81 | | | 52 | refresh | | |
82 | | | 53 | retry-after | | |
83 | | | 54 | server | | |
84 | | | 55 | set-cookie | | |
85 | | | 56 | strict-transport-security | | |
86 | | | 57 | transfer-encoding | | |
87 | | | 58 | user-agent | | |
88 | | | 59 | vary | | |
89 | | | 60 | via | | |
90 | | | 61 | www-authenticate | | |
91 | | +-------+-----------------------------+---------------+ |
92 | | */ |
93 | | |
94 | | static const uint8_t static_hdr_len[62] = { |
95 | | 0, /* starts at 1 */ |
96 | | 10, 7, 7, 5, 5, 7, 7, 7, 7, 7, |
97 | | 7, 7, 7, 7, 14, 15, 15, 13, 6, 27, |
98 | | 3, 5, 13, 13, 19, 16, 16, 14, 16, 13, |
99 | | 12, 6, 4, 4, 6, 7, 4, 4, 8, 17, |
100 | | 13, 8, 19, 13, 4, 8, 12, 18, 19, 5, |
101 | | 7, 7, 11, 6, 10, 25, 17, 10, 4, 3, |
102 | | 16 |
103 | | }; |
104 | | |
105 | | static const unsigned char static_token[] = { |
106 | | 0, |
107 | | WSI_TOKEN_HTTP_COLON_AUTHORITY, |
108 | | WSI_TOKEN_HTTP_COLON_METHOD, |
109 | | WSI_TOKEN_HTTP_COLON_METHOD, |
110 | | WSI_TOKEN_HTTP_COLON_PATH, |
111 | | WSI_TOKEN_HTTP_COLON_PATH, |
112 | | WSI_TOKEN_HTTP_COLON_SCHEME, |
113 | | WSI_TOKEN_HTTP_COLON_SCHEME, |
114 | | WSI_TOKEN_HTTP_COLON_STATUS, |
115 | | WSI_TOKEN_HTTP_COLON_STATUS, |
116 | | WSI_TOKEN_HTTP_COLON_STATUS, |
117 | | WSI_TOKEN_HTTP_COLON_STATUS, |
118 | | WSI_TOKEN_HTTP_COLON_STATUS, |
119 | | WSI_TOKEN_HTTP_COLON_STATUS, |
120 | | WSI_TOKEN_HTTP_COLON_STATUS, |
121 | | WSI_TOKEN_HTTP_ACCEPT_CHARSET, |
122 | | WSI_TOKEN_HTTP_ACCEPT_ENCODING, |
123 | | WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, |
124 | | WSI_TOKEN_HTTP_ACCEPT_RANGES, |
125 | | WSI_TOKEN_HTTP_ACCEPT, |
126 | | WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN, |
127 | | WSI_TOKEN_HTTP_AGE, |
128 | | WSI_TOKEN_HTTP_ALLOW, |
129 | | WSI_TOKEN_HTTP_AUTHORIZATION, |
130 | | WSI_TOKEN_HTTP_CACHE_CONTROL, |
131 | | WSI_TOKEN_HTTP_CONTENT_DISPOSITION, |
132 | | WSI_TOKEN_HTTP_CONTENT_ENCODING, |
133 | | WSI_TOKEN_HTTP_CONTENT_LANGUAGE, |
134 | | WSI_TOKEN_HTTP_CONTENT_LENGTH, |
135 | | WSI_TOKEN_HTTP_CONTENT_LOCATION, |
136 | | WSI_TOKEN_HTTP_CONTENT_RANGE, |
137 | | WSI_TOKEN_HTTP_CONTENT_TYPE, |
138 | | WSI_TOKEN_HTTP_COOKIE, |
139 | | WSI_TOKEN_HTTP_DATE, |
140 | | WSI_TOKEN_HTTP_ETAG, |
141 | | WSI_TOKEN_HTTP_EXPECT, |
142 | | WSI_TOKEN_HTTP_EXPIRES, |
143 | | WSI_TOKEN_HTTP_FROM, |
144 | | WSI_TOKEN_HOST, |
145 | | WSI_TOKEN_HTTP_IF_MATCH, |
146 | | WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, |
147 | | WSI_TOKEN_HTTP_IF_NONE_MATCH, |
148 | | WSI_TOKEN_HTTP_IF_RANGE, |
149 | | WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE, |
150 | | WSI_TOKEN_HTTP_LAST_MODIFIED, |
151 | | WSI_TOKEN_HTTP_LINK, |
152 | | WSI_TOKEN_HTTP_LOCATION, |
153 | | WSI_TOKEN_HTTP_MAX_FORWARDS, |
154 | | WSI_TOKEN_HTTP_PROXY_AUTHENTICATE, |
155 | | WSI_TOKEN_HTTP_PROXY_AUTHORIZATION, |
156 | | WSI_TOKEN_HTTP_RANGE, |
157 | | WSI_TOKEN_HTTP_REFERER, |
158 | | WSI_TOKEN_HTTP_REFRESH, |
159 | | WSI_TOKEN_HTTP_RETRY_AFTER, |
160 | | WSI_TOKEN_HTTP_SERVER, |
161 | | WSI_TOKEN_HTTP_SET_COOKIE, |
162 | | WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY, |
163 | | WSI_TOKEN_HTTP_TRANSFER_ENCODING, |
164 | | WSI_TOKEN_HTTP_USER_AGENT, |
165 | | WSI_TOKEN_HTTP_VARY, |
166 | | WSI_TOKEN_HTTP_VIA, |
167 | | WSI_TOKEN_HTTP_WWW_AUTHENTICATE, |
168 | | }; |
169 | | |
170 | | /* some of the entries imply values as well as header names */ |
171 | | |
172 | | static const char * const http2_canned[] = { |
173 | | "", |
174 | | "", |
175 | | "GET", |
176 | | "POST", |
177 | | "/", |
178 | | "/index.html", |
179 | | "http", |
180 | | "https", |
181 | | "200", |
182 | | "204", |
183 | | "206", |
184 | | "304", |
185 | | "400", |
186 | | "404", |
187 | | "500", |
188 | | "", |
189 | | "gzip, deflate" |
190 | | }; |
191 | | |
192 | | /* see minihuf.c */ |
193 | | |
194 | | #include "huftable.h" |
195 | | |
196 | | static int huftable_decode(int pos, char c) |
197 | 0 | { |
198 | 0 | int q = pos + !!c; |
199 | |
|
200 | 0 | if (lextable_terms[q >> 3] & (1 << (q & 7))) /* terminal */ |
201 | 0 | return lextable[q] | 0x8000; |
202 | | |
203 | 0 | return pos + (lextable[q] << 1); |
204 | 0 | } |
205 | | |
206 | | static int lws_frag_start(struct lws *wsi, int hdr_token_idx) |
207 | 0 | { |
208 | 0 | struct allocated_headers *ah = wsi->http.ah; |
209 | |
|
210 | 0 | if (!ah) { |
211 | 0 | lwsl_notice("%s: no ah\n", __func__); |
212 | 0 | return 1; |
213 | 0 | } |
214 | | |
215 | 0 | ah->hdr_token_idx = -1; |
216 | |
|
217 | 0 | lwsl_header("%s: token %d ah->pos = %d, ah->nfrag = %d\n", |
218 | 0 | __func__, hdr_token_idx, ah->pos, ah->nfrag); |
219 | |
|
220 | 0 | if (!hdr_token_idx) { |
221 | 0 | lwsl_err("%s: zero hdr_token_idx\n", __func__); |
222 | 0 | return 1; |
223 | 0 | } |
224 | | |
225 | 0 | if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frag_index)) { |
226 | 0 | lwsl_err("%s: frag index %d too big\n", __func__, ah->nfrag); |
227 | 0 | return 1; |
228 | 0 | } |
229 | | |
230 | 0 | if ((hdr_token_idx == WSI_TOKEN_HTTP_COLON_AUTHORITY || |
231 | 0 | hdr_token_idx == WSI_TOKEN_HTTP_COLON_METHOD || |
232 | 0 | hdr_token_idx == WSI_TOKEN_HTTP_COLON_PATH || |
233 | 0 | hdr_token_idx == WSI_TOKEN_COLON_PROTOCOL || |
234 | 0 | hdr_token_idx == WSI_TOKEN_HTTP_COLON_SCHEME) && |
235 | 0 | ah->frag_index[hdr_token_idx]) { |
236 | 0 | if (!(ah->frags[ah->frag_index[hdr_token_idx]].flags & 1)) { |
237 | 0 | lws_h2_goaway(lws_get_network_wsi(wsi), |
238 | 0 | H2_ERR_PROTOCOL_ERROR, |
239 | 0 | "Duplicated pseudoheader"); |
240 | 0 | return 1; |
241 | 0 | } |
242 | 0 | } |
243 | | |
244 | 0 | if (ah->nfrag == 0) |
245 | 0 | ah->nfrag = 1; |
246 | |
|
247 | 0 | ah->frags[ah->nfrag].offset = ah->pos; |
248 | 0 | ah->frags[ah->nfrag].len = 0; |
249 | 0 | ah->frags[ah->nfrag].nfrag = 0; |
250 | 0 | ah->frags[ah->nfrag].flags = 2; /* we had reason to set it */ |
251 | |
|
252 | 0 | ah->hdr_token_idx = hdr_token_idx; |
253 | | |
254 | | /* |
255 | | * Okay, but we could be, eg, the second or subsequent cookie: header |
256 | | */ |
257 | |
|
258 | 0 | if (ah->frag_index[hdr_token_idx]) { |
259 | 0 | int n; |
260 | | |
261 | | /* find the last fragment for this header... */ |
262 | 0 | n = ah->frag_index[hdr_token_idx]; |
263 | 0 | while (ah->frags[n].nfrag) |
264 | 0 | n = ah->frags[n].nfrag; |
265 | | /* and point it to continue in our continuation fragment */ |
266 | 0 | ah->frags[n].nfrag = ah->nfrag; |
267 | 0 | } else |
268 | 0 | ah->frag_index[hdr_token_idx] = ah->nfrag; |
269 | |
|
270 | 0 | return 0; |
271 | 0 | } |
272 | | |
273 | | static int lws_frag_append(struct lws *wsi, unsigned char c) |
274 | 0 | { |
275 | 0 | struct allocated_headers *ah = wsi->http.ah; |
276 | |
|
277 | 0 | ah->data[ah->pos++] = (char)c; |
278 | 0 | ah->frags[ah->nfrag].len++; |
279 | |
|
280 | 0 | return (unsigned int)ah->pos >= wsi->a.context->max_http_header_data; |
281 | 0 | } |
282 | | |
283 | | static int lws_frag_end(struct lws *wsi) |
284 | 0 | { |
285 | 0 | lwsl_header("%s\n", __func__); |
286 | 0 | if (lws_frag_append(wsi, 0)) |
287 | 0 | return 1; |
288 | | |
289 | | /* don't account for the terminating NUL in the logical length */ |
290 | 0 | wsi->http.ah->frags[wsi->http.ah->nfrag].len--; |
291 | |
|
292 | 0 | wsi->http.ah->nfrag++; |
293 | 0 | return 0; |
294 | 0 | } |
295 | | |
296 | | int |
297 | | lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h) |
298 | 0 | { |
299 | 0 | struct allocated_headers *ah = wsi->http.ah; |
300 | 0 | int n; |
301 | |
|
302 | 0 | if (!ah) |
303 | 0 | return 0; |
304 | | |
305 | 0 | n = ah->frag_index[h]; |
306 | 0 | if (!n) |
307 | 0 | return 0; |
308 | | |
309 | 0 | return !!(ah->frags[n].flags & 2); |
310 | 0 | } |
311 | | |
312 | | static void lws_dump_header(struct lws *wsi, int hdr) |
313 | 0 | { |
314 | 0 | char s[200]; |
315 | 0 | const unsigned char *p; |
316 | 0 | int len; |
317 | |
|
318 | 0 | if (hdr == LWS_HPACK_IGNORE_ENTRY) { |
319 | 0 | lwsl_notice("hdr tok ignored\n"); |
320 | 0 | return; |
321 | 0 | } |
322 | | |
323 | 0 | (void)p; |
324 | |
|
325 | 0 | len = lws_hdr_copy(wsi, s, sizeof(s) - 1, (enum lws_token_indexes)hdr); |
326 | 0 | if (len < 0) |
327 | 0 | strcpy(s, "(too big to show)"); |
328 | 0 | else |
329 | 0 | s[len] = '\0'; |
330 | 0 | #if defined(_DEBUG) |
331 | 0 | p = lws_token_to_string((enum lws_token_indexes)hdr); |
332 | 0 | lwsl_header(" hdr tok %d (%s) = '%s' (len %d)\n", hdr, |
333 | 0 | p ? (char *)p : (char *)"null", s, len); |
334 | 0 | #endif |
335 | 0 | } |
336 | | |
337 | | /* |
338 | | * dynamic table |
339 | | * |
340 | | * [ 0 .... num_entries - 1] |
341 | | * |
342 | | * Starts filling at 0+ |
343 | | * |
344 | | * #62 is *most recently entered* |
345 | | * |
346 | | * Number of entries is not restricted, but aggregated size of the entry |
347 | | * payloads is. Unfortunately the way HPACK does this is specific to an |
348 | | * imagined implementation, and lws implementation is much more efficient |
349 | | * (ignoring unknown headers and using the lws token index for the header |
350 | | * name part). |
351 | | */ |
352 | | |
353 | | /* |
354 | | * returns 0 if dynamic entry (arg and len are filled) |
355 | | * returns -1 if failure |
356 | | * returns nonzero token index if actually static token |
357 | | */ |
358 | | static int |
359 | | lws_token_from_index(struct lws *wsi, int index, const char **arg, int *len, |
360 | | uint32_t *hdr_len) |
361 | 0 | { |
362 | 0 | struct hpack_dynamic_table *dyn; |
363 | |
|
364 | 0 | if (index == LWS_HPACK_IGNORE_ENTRY) |
365 | 0 | return LWS_HPACK_IGNORE_ENTRY; |
366 | | |
367 | | /* dynamic table only belongs to network wsi */ |
368 | 0 | wsi = lws_get_network_wsi(wsi); |
369 | 0 | if (!wsi->h2.h2n) |
370 | 0 | return -1; |
371 | | |
372 | 0 | dyn = &wsi->h2.h2n->hpack_dyn_table; |
373 | |
|
374 | 0 | if (index < 0) |
375 | 0 | return -1; |
376 | | |
377 | 0 | if (index < (int)LWS_ARRAY_SIZE(static_token)) { |
378 | 0 | if (arg && index < (int)LWS_ARRAY_SIZE(http2_canned)) { |
379 | 0 | *arg = http2_canned[index]; |
380 | 0 | *len = (int)strlen(http2_canned[index]); |
381 | 0 | } |
382 | 0 | if (hdr_len) |
383 | 0 | *hdr_len = static_hdr_len[index]; |
384 | |
|
385 | 0 | return static_token[index]; |
386 | 0 | } |
387 | | |
388 | 0 | if (!dyn) { |
389 | 0 | lwsl_notice("no dynamic table\n"); |
390 | 0 | return -1; |
391 | 0 | } |
392 | | |
393 | 0 | if (index >= (int)LWS_ARRAY_SIZE(static_token) + dyn->used_entries) { |
394 | 0 | lwsl_info(" %s: adjusted index %d >= %d\n", __func__, index, |
395 | 0 | (int)LWS_ARRAY_SIZE(static_token) + dyn->used_entries); |
396 | 0 | lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR, |
397 | 0 | "index out of range"); |
398 | 0 | return -1; |
399 | 0 | } |
400 | | |
401 | 0 | index -= (int)LWS_ARRAY_SIZE(static_token); |
402 | 0 | index = lws_safe_modulo(dyn->pos - 1 - index, dyn->num_entries); |
403 | 0 | if (index < 0) |
404 | 0 | index += dyn->num_entries; |
405 | |
|
406 | 0 | lwsl_header("%s: dyn index %d, tok %d\n", __func__, index, |
407 | 0 | dyn->entries[index].lws_hdr_idx); |
408 | |
|
409 | 0 | if (arg && len) { |
410 | 0 | *arg = dyn->entries[index].value; |
411 | 0 | *len = dyn->entries[index].value_len; |
412 | 0 | } |
413 | |
|
414 | 0 | if (hdr_len) |
415 | 0 | *hdr_len = dyn->entries[index].hdr_len; |
416 | |
|
417 | 0 | return dyn->entries[index].lws_hdr_idx; |
418 | 0 | } |
419 | | |
420 | | static int |
421 | | lws_h2_dynamic_table_dump(struct lws *wsi) |
422 | 0 | { |
423 | | #if 0 |
424 | | struct lws *nwsi = lws_get_network_wsi(wsi); |
425 | | struct hpack_dynamic_table *dyn; |
426 | | int n, m; |
427 | | const char *p; |
428 | | |
429 | | if (!nwsi->h2.h2n) |
430 | | return 1; |
431 | | dyn = &nwsi->h2.h2n->hpack_dyn_table; |
432 | | |
433 | | lwsl_header("Dump dyn table for nwsi %s (%d / %d members, pos = %d, " |
434 | | "start index %d, virt used %d / %d)\n", lws_wsi_tag(nwsi), |
435 | | dyn->used_entries, dyn->num_entries, dyn->pos, |
436 | | (uint32_t)LWS_ARRAY_SIZE(static_token), |
437 | | dyn->virtual_payload_usage, dyn->virtual_payload_max); |
438 | | |
439 | | for (n = 0; n < dyn->used_entries; n++) { |
440 | | m = lws_safe_modulo(dyn->pos - 1 - n, dyn->num_entries); |
441 | | if (m < 0) |
442 | | m += dyn->num_entries; |
443 | | if (dyn->entries[m].lws_hdr_idx != LWS_HPACK_IGNORE_ENTRY) |
444 | | p = (const char *)lws_token_to_string( |
445 | | dyn->entries[m].lws_hdr_idx); |
446 | | else |
447 | | p = "(ignored)"; |
448 | | lwsl_header(" %3d: tok %s: (len %d) val '%s'\n", |
449 | | (int)(n + LWS_ARRAY_SIZE(static_token)), p, |
450 | | dyn->entries[m].hdr_len, dyn->entries[m].value ? |
451 | | dyn->entries[m].value : "null"); |
452 | | } |
453 | | #endif |
454 | 0 | return 0; |
455 | 0 | } |
456 | | |
457 | | static void |
458 | | lws_dynamic_free(struct hpack_dynamic_table *dyn, int idx) |
459 | 0 | { |
460 | 0 | lwsl_header("freeing %d for reuse\n", idx); |
461 | 0 | dyn->virtual_payload_usage = (uint32_t)((unsigned int)dyn->virtual_payload_usage - (unsigned int)(dyn->entries[idx].value_len + |
462 | 0 | dyn->entries[idx].hdr_len)); |
463 | 0 | lws_free_set_NULL(dyn->entries[idx].value); |
464 | 0 | dyn->entries[idx].value = NULL; |
465 | 0 | dyn->entries[idx].value_len = 0; |
466 | 0 | dyn->entries[idx].hdr_len = 0; |
467 | 0 | dyn->entries[idx].lws_hdr_idx = LWS_HPACK_IGNORE_ENTRY; |
468 | 0 | dyn->used_entries--; |
469 | 0 | } |
470 | | |
471 | | /* |
472 | | * There are two address spaces, 1) internal ringbuffer and 2) HPACK indexes. |
473 | | * |
474 | | * Internal ringbuffer: |
475 | | * |
476 | | * The internal ringbuffer wraps as we keep filling it, dyn->pos points to |
477 | | * the next index to be written. |
478 | | * |
479 | | * HPACK indexes: |
480 | | * |
481 | | * The last-written entry becomes entry 0, the previously-last-written entry |
482 | | * becomes entry 1 etc. |
483 | | */ |
484 | | |
485 | | static int |
486 | | lws_dynamic_token_insert(struct lws *wsi, int hdr_len, |
487 | | int lws_hdr_index, char *arg, size_t len) |
488 | 0 | { |
489 | 0 | struct hpack_dynamic_table *dyn; |
490 | 0 | int new_index; |
491 | | |
492 | | /* dynamic table only belongs to network wsi */ |
493 | 0 | wsi = lws_get_network_wsi(wsi); |
494 | 0 | if (!wsi->h2.h2n) |
495 | 0 | return 1; |
496 | 0 | dyn = &wsi->h2.h2n->hpack_dyn_table; |
497 | |
|
498 | 0 | if (!dyn->entries) { |
499 | 0 | lwsl_err("%s: unsized dyn table\n", __func__); |
500 | |
|
501 | 0 | return 1; |
502 | 0 | } |
503 | 0 | lws_h2_dynamic_table_dump(wsi); |
504 | |
|
505 | 0 | new_index = lws_safe_modulo(dyn->pos, dyn->num_entries); |
506 | 0 | if (dyn->num_entries && dyn->used_entries == dyn->num_entries) { |
507 | 0 | if (dyn->virtual_payload_usage < dyn->virtual_payload_max) |
508 | 0 | lwsl_err("Dropping header content before limit!\n"); |
509 | | /* we have to drop the oldest to make space */ |
510 | 0 | lws_dynamic_free(dyn, new_index); |
511 | 0 | } |
512 | | |
513 | | /* |
514 | | * evict guys to make room, allowing for some overage. We have to |
515 | | * take care about getting a single huge header, and evicting |
516 | | * everything |
517 | | */ |
518 | |
|
519 | 0 | while (dyn->virtual_payload_usage && |
520 | 0 | dyn->used_entries && |
521 | 0 | dyn->virtual_payload_usage + (unsigned int)hdr_len + len > |
522 | 0 | dyn->virtual_payload_max + 1024) { |
523 | 0 | int n = lws_safe_modulo(dyn->pos - dyn->used_entries, |
524 | 0 | dyn->num_entries); |
525 | 0 | if (n < 0) |
526 | 0 | n += dyn->num_entries; |
527 | 0 | lws_dynamic_free(dyn, n); |
528 | 0 | } |
529 | |
|
530 | 0 | if (dyn->used_entries < dyn->num_entries) |
531 | 0 | dyn->used_entries++; |
532 | |
|
533 | 0 | dyn->entries[new_index].value_len = 0; |
534 | |
|
535 | 0 | if (lws_hdr_index != LWS_HPACK_IGNORE_ENTRY) { |
536 | 0 | if (dyn->entries[new_index].value) |
537 | 0 | lws_free_set_NULL(dyn->entries[new_index].value); |
538 | 0 | dyn->entries[new_index].value = |
539 | 0 | lws_malloc(len + 1, "hpack dyn"); |
540 | 0 | if (!dyn->entries[new_index].value) |
541 | 0 | return 1; |
542 | | |
543 | 0 | memcpy(dyn->entries[new_index].value, arg, len); |
544 | 0 | dyn->entries[new_index].value[len] = '\0'; |
545 | 0 | dyn->entries[new_index].value_len = (uint16_t)len; |
546 | 0 | } else |
547 | 0 | dyn->entries[new_index].value = NULL; |
548 | | |
549 | 0 | dyn->entries[new_index].lws_hdr_idx = (uint16_t)lws_hdr_index; |
550 | 0 | dyn->entries[new_index].hdr_len = (uint16_t)hdr_len; |
551 | |
|
552 | 0 | dyn->virtual_payload_usage = (uint32_t)(dyn->virtual_payload_usage + |
553 | 0 | (unsigned int)hdr_len + len); |
554 | |
|
555 | 0 | lwsl_info("%s: index %ld: lws_hdr_index 0x%x, hdr len %d, '%s' len %d\n", |
556 | 0 | __func__, (long)LWS_ARRAY_SIZE(static_token), |
557 | 0 | lws_hdr_index, hdr_len, dyn->entries[new_index].value ? |
558 | 0 | dyn->entries[new_index].value : "null", (int)len); |
559 | |
|
560 | 0 | dyn->pos = (uint16_t)lws_safe_modulo(dyn->pos + 1, dyn->num_entries); |
561 | |
|
562 | 0 | lws_h2_dynamic_table_dump(wsi); |
563 | |
|
564 | 0 | return 0; |
565 | 0 | } |
566 | | |
567 | | int |
568 | | lws_hpack_dynamic_size(struct lws *wsi, int size) |
569 | 0 | { |
570 | 0 | struct hpack_dynamic_table *dyn; |
571 | 0 | struct hpack_dt_entry *dte; |
572 | 0 | struct lws *nwsi; |
573 | 0 | int min, n = 0, m; |
574 | | |
575 | | /* |
576 | | * "size" here is coming from the http/2 SETTING |
577 | | * SETTINGS_HEADER_TABLE_SIZE. This is a (virtual, in our case) |
578 | | * linear buffer containing dynamic header names and values... when it |
579 | | * is full, old entries are evicted. |
580 | | * |
581 | | * We encode the header as an lws_hdr_idx, which is all the rest of |
582 | | * lws cares about; if there is no matching header we store an empty |
583 | | * entry in the dyn table as a placeholder. |
584 | | * |
585 | | * So to make the two systems work together we keep an accounting of |
586 | | * what we are using to decide when to evict... we must only evict |
587 | | * things when the remote peer's accounting also makes him feel he |
588 | | * should evict something. |
589 | | */ |
590 | |
|
591 | 0 | nwsi = lws_get_network_wsi(wsi); |
592 | 0 | if (!nwsi->h2.h2n) |
593 | 0 | goto bail; |
594 | | |
595 | 0 | dyn = &nwsi->h2.h2n->hpack_dyn_table; |
596 | 0 | lwsl_info("%s: from %d to %d, lim %u\n", __func__, |
597 | 0 | (int)dyn->num_entries, size, |
598 | 0 | (unsigned int)nwsi->a.vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE]); |
599 | |
|
600 | 0 | if (!size) { |
601 | 0 | size = dyn->num_entries * 8; |
602 | 0 | lws_hpack_destroy_dynamic_header(wsi); |
603 | 0 | } |
604 | |
|
605 | 0 | if (size > (int)nwsi->a.vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE]) { |
606 | 0 | lwsl_info("rejecting hpack dyn size %u vs %u\n", size, |
607 | 0 | (unsigned int)nwsi->a.vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE]); |
608 | | |
609 | | // this seems necessary to work with some browsers |
610 | |
|
611 | 0 | if (nwsi->a.vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE] == 65536 && |
612 | 0 | size == 65537) { /* h2spec */ |
613 | 0 | lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, |
614 | 0 | "Asked for header table bigger than we told"); |
615 | 0 | goto bail; |
616 | 0 | } |
617 | | |
618 | 0 | size = (int)nwsi->a.vhost->h2.set.s[H2SET_HEADER_TABLE_SIZE]; |
619 | 0 | } |
620 | | |
621 | 0 | dyn->virtual_payload_max = (uint32_t)size; |
622 | |
|
623 | 0 | size = size / 8; |
624 | 0 | min = size; |
625 | 0 | if (min > dyn->used_entries) |
626 | 0 | min = dyn->used_entries; |
627 | |
|
628 | 0 | if (size == dyn->num_entries) |
629 | 0 | return 0; |
630 | | |
631 | 0 | if (dyn->num_entries < min) |
632 | 0 | min = dyn->num_entries; |
633 | | |
634 | | // lwsl_notice("dte requested size %d\n", size); |
635 | |
|
636 | 0 | dte = lws_zalloc(sizeof(*dte) * (unsigned int)(size + 1), "dynamic table entries"); |
637 | 0 | if (!dte) |
638 | 0 | goto bail; |
639 | | |
640 | 0 | while (dyn->virtual_payload_usage && dyn->used_entries && |
641 | 0 | dyn->virtual_payload_usage > dyn->virtual_payload_max) { |
642 | 0 | n = lws_safe_modulo(dyn->pos - dyn->used_entries, dyn->num_entries); |
643 | 0 | if (n < 0) |
644 | 0 | n += dyn->num_entries; |
645 | 0 | lws_dynamic_free(dyn, n); |
646 | 0 | } |
647 | |
|
648 | 0 | if (min > dyn->used_entries) |
649 | 0 | min = dyn->used_entries; |
650 | |
|
651 | 0 | if (dyn->entries) { |
652 | 0 | for (n = 0; n < min; n++) { |
653 | 0 | m = (dyn->pos - dyn->used_entries + n) % |
654 | 0 | dyn->num_entries; |
655 | 0 | if (m < 0) |
656 | 0 | m += dyn->num_entries; |
657 | 0 | dte[n] = dyn->entries[m]; |
658 | 0 | } |
659 | |
|
660 | 0 | lws_free(dyn->entries); |
661 | 0 | } |
662 | |
|
663 | 0 | dyn->entries = dte; |
664 | 0 | dyn->num_entries = (uint16_t)size; |
665 | 0 | dyn->used_entries = (uint16_t)min; |
666 | 0 | if (size) |
667 | 0 | dyn->pos = (uint16_t)lws_safe_modulo(min, size); |
668 | 0 | else |
669 | 0 | dyn->pos = 0; |
670 | |
|
671 | 0 | lws_h2_dynamic_table_dump(wsi); |
672 | |
|
673 | 0 | return 0; |
674 | | |
675 | 0 | bail: |
676 | 0 | lwsl_info("%s: failed to resize to %d\n", __func__, size); |
677 | |
|
678 | 0 | return 1; |
679 | 0 | } |
680 | | |
681 | | void |
682 | | lws_hpack_destroy_dynamic_header(struct lws *wsi) |
683 | 0 | { |
684 | 0 | struct hpack_dynamic_table *dyn; |
685 | 0 | int n; |
686 | |
|
687 | 0 | if (!wsi->h2.h2n) |
688 | 0 | return; |
689 | | |
690 | 0 | dyn = &wsi->h2.h2n->hpack_dyn_table; |
691 | |
|
692 | 0 | if (!dyn->entries) |
693 | 0 | return; |
694 | | |
695 | 0 | for (n = 0; n < dyn->num_entries; n++) |
696 | 0 | if (dyn->entries[n].value) |
697 | 0 | lws_free_set_NULL(dyn->entries[n].value); |
698 | |
|
699 | 0 | lws_free_set_NULL(dyn->entries); |
700 | 0 | } |
701 | | |
702 | | static int |
703 | | lws_hpack_use_idx_hdr(struct lws *wsi, int idx, int known_token) |
704 | 0 | { |
705 | 0 | const char *arg = NULL; |
706 | 0 | int len = 0; |
707 | 0 | const char *p = NULL; |
708 | 0 | int tok = lws_token_from_index(wsi, idx, &arg, &len, NULL); |
709 | |
|
710 | 0 | if (tok == LWS_HPACK_IGNORE_ENTRY) { |
711 | 0 | lwsl_header("%s: lws_token says ignore, returning\n", __func__); |
712 | 0 | return 0; |
713 | 0 | } |
714 | | |
715 | 0 | if (tok == -1) { |
716 | 0 | lwsl_info("%s: idx %d mapped to tok %d\n", __func__, idx, tok); |
717 | 0 | return 1; |
718 | 0 | } |
719 | | |
720 | 0 | if (arg) { |
721 | | /* dynamic result */ |
722 | 0 | if (known_token > 0) |
723 | 0 | tok = known_token; |
724 | 0 | lwsl_header("%s: dyn: idx %d '%s' tok %d\n", __func__, idx, arg, |
725 | 0 | tok); |
726 | 0 | } else |
727 | 0 | lwsl_header("writing indexed hdr %d (tok %d '%s')\n", idx, tok, |
728 | 0 | lws_token_to_string((enum lws_token_indexes)tok)); |
729 | |
|
730 | 0 | if (tok == LWS_HPACK_IGNORE_ENTRY) |
731 | 0 | return 0; |
732 | | |
733 | 0 | if (arg) |
734 | 0 | p = arg; |
735 | |
|
736 | 0 | if (idx < (int)LWS_ARRAY_SIZE(http2_canned)) |
737 | 0 | p = http2_canned[idx]; |
738 | |
|
739 | 0 | if (lws_frag_start(wsi, tok)) |
740 | 0 | return 1; |
741 | | |
742 | 0 | if (p) |
743 | 0 | while (*p && len--) |
744 | 0 | if (lws_frag_append(wsi, (unsigned char)*p++)) |
745 | 0 | return 1; |
746 | | |
747 | 0 | if (lws_frag_end(wsi)) |
748 | 0 | return 1; |
749 | | |
750 | 0 | lws_dump_header(wsi, tok); |
751 | |
|
752 | 0 | return 0; |
753 | 0 | } |
754 | | |
755 | | #if !defined(LWS_HTTP_HEADERS_ALL) && !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) |
756 | | static uint8_t lws_header_implies_psuedoheader_map[] = { |
757 | | 0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
758 | | }; |
759 | | #endif |
760 | | #if !defined(LWS_HTTP_HEADERS_ALL) && defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) |
761 | | static uint8_t lws_header_implies_psuedoheader_map[] = { |
762 | | 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
763 | | }; |
764 | | #endif |
765 | | #if !defined(LWS_HTTP_HEADERS_ALL) && !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) |
766 | | static uint8_t lws_header_implies_psuedoheader_map[] = { |
767 | | 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
768 | | }; |
769 | | #endif |
770 | | #if !defined(LWS_HTTP_HEADERS_ALL) && defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && !defined(LWS_ROLE_H2) |
771 | | static uint8_t lws_header_implies_psuedoheader_map[] = { |
772 | | 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x10,0x00,0x00,0x00,0x00,0x00,0x00, |
773 | | }; |
774 | | #endif |
775 | | #if !defined(LWS_HTTP_HEADERS_ALL) && !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) |
776 | | static uint8_t lws_header_implies_psuedoheader_map[] = { |
777 | | 0x03,0x00,0x80,0x0f,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
778 | | }; |
779 | | #endif |
780 | | #if !defined(LWS_HTTP_HEADERS_ALL) && defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && !defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) |
781 | | static uint8_t lws_header_implies_psuedoheader_map[] = { |
782 | | 0x07,0x00,0x00,0x3e,0x00,0x00,0x00,0x80,0x03,0x09,0x00,0x00,0x00,0x00,0x00,0x00, |
783 | | }; |
784 | | #endif |
785 | | #if !defined(LWS_HTTP_HEADERS_ALL) && !defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2) |
786 | | static uint8_t lws_header_implies_psuedoheader_map[] = { |
787 | | 0x03,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x00,0x00,0x00, |
788 | | }; |
789 | | #endif |
790 | | #if defined(LWS_HTTP_HEADERS_ALL) || ( defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && defined(LWS_ROLE_WS) && defined(LWS_ROLE_H2)) |
791 | | static uint8_t lws_header_implies_psuedoheader_map[] = { |
792 | | 0x07,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,0x0e,0x24,0x00,0x00,0x00,0x00,0x00, |
793 | | }; |
794 | | #endif |
795 | | |
796 | | |
797 | | static int |
798 | | lws_hpack_handle_pseudo_rules(struct lws *nwsi, struct lws *wsi, int m) |
799 | 0 | { |
800 | 0 | if (m == LWS_HPACK_IGNORE_ENTRY || m == -1) |
801 | 0 | return 0; |
802 | | |
803 | 0 | if (wsi->seen_nonpseudoheader && |
804 | 0 | (lws_header_implies_psuedoheader_map[m >> 3] & (1 << (m & 7)))) { |
805 | |
|
806 | 0 | lwsl_info("lws tok %d seems to be a pseudoheader\n", m); |
807 | | |
808 | | /* |
809 | | * it's not legal to see a |
810 | | * pseudoheader after normal |
811 | | * headers |
812 | | */ |
813 | 0 | lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, |
814 | 0 | "Pseudoheader after normal hdrs"); |
815 | 0 | return 1; |
816 | 0 | } |
817 | | |
818 | 0 | if (!(lws_header_implies_psuedoheader_map[m >> 3] & (1 << (m & 7)))) |
819 | 0 | wsi->seen_nonpseudoheader = 1; |
820 | |
|
821 | 0 | return 0; |
822 | 0 | } |
823 | | |
824 | | int lws_hpack_interpret(struct lws *wsi, unsigned char c) |
825 | 0 | { |
826 | 0 | struct lws *nwsi = lws_get_network_wsi(wsi); |
827 | 0 | struct lws_h2_netconn *h2n = nwsi->h2.h2n; |
828 | 0 | struct allocated_headers *ah = wsi->http.ah; |
829 | 0 | unsigned int prev; |
830 | 0 | unsigned char c1; |
831 | 0 | int n, m, plen; |
832 | |
|
833 | 0 | if (!h2n) |
834 | 0 | return -1; |
835 | | |
836 | | /* |
837 | | * HPKT_INDEXED_HDR_7 1xxxxxxx: just "header field" |
838 | | * HPKT_INDEXED_HDR_6_VALUE_INCR 01xxxxxx: NEW indexed hdr + val |
839 | | * HPKT_LITERAL_HDR_VALUE_INCR 01000000: NEW literal hdr + val |
840 | | * HPKT_INDEXED_HDR_4_VALUE 0000xxxx: indexed hdr + val |
841 | | * HPKT_INDEXED_HDR_4_VALUE_NEVER 0001xxxx: NEVER NEW indexed hdr + val |
842 | | * HPKT_LITERAL_HDR_VALUE 00000000: literal hdr + val |
843 | | * HPKT_LITERAL_HDR_VALUE_NEVER 00010000: NEVER NEW literal hdr + val |
844 | | */ |
845 | 0 | switch (h2n->hpack) { |
846 | | |
847 | 0 | case HPKS_TYPE: |
848 | 0 | h2n->is_first_header_char = 1; |
849 | 0 | h2n->huff_pad = 0; |
850 | 0 | h2n->zero_huff_padding = 0; |
851 | 0 | h2n->last_action_dyntable_resize = 0; |
852 | 0 | h2n->ext_count = 0; |
853 | 0 | h2n->hpack_hdr_len = 0; |
854 | 0 | h2n->unknown_header = 0; |
855 | 0 | ah->parser_state = 255; |
856 | |
|
857 | 0 | if (c & 0x80) { /* 1.... indexed header field only */ |
858 | | /* just a possibly-extended integer */ |
859 | 0 | h2n->hpack_type = HPKT_INDEXED_HDR_7; |
860 | 0 | lwsl_header("HPKT_INDEXED_HDR_7 hdr %d\n", c & 0x7f); |
861 | 0 | lws_h2_dynamic_table_dump(wsi); |
862 | |
|
863 | 0 | h2n->hdr_idx = c & 0x7f; |
864 | 0 | if ((c & 0x7f) == 0x7f) { |
865 | 0 | h2n->hpack_len = 0; |
866 | 0 | h2n->hpack_m = 0x7f; |
867 | 0 | h2n->hpack = HPKS_IDX_EXT; |
868 | 0 | break; |
869 | 0 | } |
870 | 0 | if (!h2n->hdr_idx) { |
871 | 0 | lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, |
872 | 0 | "hdr index 0 seen"); |
873 | 0 | return 1; |
874 | 0 | } |
875 | | |
876 | 0 | m = lws_token_from_index(wsi, (int)h2n->hdr_idx, |
877 | 0 | NULL, NULL, NULL); |
878 | 0 | if (lws_hpack_handle_pseudo_rules(nwsi, wsi, m)) |
879 | 0 | return 1; |
880 | | |
881 | 0 | lwsl_header("HPKT_INDEXED_HDR_7: hdr %d\n", c & 0x7f); |
882 | 0 | if (lws_hpack_use_idx_hdr(wsi, c & 0x7f, -1)) { |
883 | 0 | lwsl_header("%s: idx hdr wr fail\n", __func__); |
884 | 0 | return 1; |
885 | 0 | } |
886 | | /* stay at same state */ |
887 | 0 | break; |
888 | 0 | } |
889 | 0 | if (c & 0x40) { /* 01.... indexed or literal header incr idx */ |
890 | | /* |
891 | | * [possibly-ext hdr idx (6) | new literal hdr name] |
892 | | * H + possibly-ext value length |
893 | | * literal value |
894 | | */ |
895 | 0 | h2n->hdr_idx = 0; |
896 | 0 | if (c == 0x40) { /* literal header */ |
897 | 0 | lwsl_header(" HPKT_LITERAL_HDR_VALUE_INCR\n"); |
898 | 0 | h2n->hpack_type = HPKT_LITERAL_HDR_VALUE_INCR; |
899 | 0 | h2n->value = 0; |
900 | 0 | h2n->hpack_len = 0; |
901 | 0 | h2n->hpack = HPKS_HLEN; |
902 | 0 | break; |
903 | 0 | } |
904 | | /* indexed header */ |
905 | 0 | h2n->hpack_type = HPKT_INDEXED_HDR_6_VALUE_INCR; |
906 | 0 | lwsl_header(" HPKT_INDEXED_HDR_6_VALUE_INCR (hdr %d)\n", |
907 | 0 | c & 0x3f); |
908 | 0 | h2n->hdr_idx = c & 0x3f; |
909 | 0 | if ((c & 0x3f) == 0x3f) { |
910 | 0 | h2n->hpack_m = 0x3f; |
911 | 0 | h2n->hpack_len = 0; |
912 | 0 | h2n->hpack = HPKS_IDX_EXT; |
913 | 0 | break; |
914 | 0 | } |
915 | | |
916 | 0 | h2n->value = 1; |
917 | 0 | h2n->hpack = HPKS_HLEN; |
918 | 0 | if (!h2n->hdr_idx) { |
919 | 0 | lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, |
920 | 0 | "hdr index 0 seen"); |
921 | 0 | return 1; |
922 | 0 | } |
923 | 0 | break; |
924 | 0 | } |
925 | 0 | switch(c & 0xf0) { |
926 | 0 | case 0x10: /* literal header never index */ |
927 | 0 | case 0: /* literal header without indexing */ |
928 | | /* |
929 | | * follows 0x40 except 4-bit hdr idx |
930 | | * and don't add to index |
931 | | */ |
932 | 0 | if (c == 0) { /* literal name */ |
933 | 0 | h2n->hpack_type = HPKT_LITERAL_HDR_VALUE; |
934 | 0 | lwsl_header(" HPKT_LITERAL_HDR_VALUE\n"); |
935 | 0 | h2n->hpack = HPKS_HLEN; |
936 | 0 | h2n->value = 0; |
937 | 0 | break; |
938 | 0 | } |
939 | 0 | if (c == 0x10) { /* literal name NEVER */ |
940 | 0 | h2n->hpack_type = HPKT_LITERAL_HDR_VALUE_NEVER; |
941 | 0 | lwsl_header(" HPKT_LITERAL_HDR_VALUE_NEVER\n"); |
942 | 0 | h2n->hpack = HPKS_HLEN; |
943 | 0 | h2n->value = 0; |
944 | 0 | break; |
945 | 0 | } |
946 | 0 | lwsl_header("indexed\n"); |
947 | | /* indexed name */ |
948 | 0 | if (c & 0x10) { |
949 | 0 | h2n->hpack_type = HPKT_INDEXED_HDR_4_VALUE_NEVER; |
950 | 0 | lwsl_header("HPKT_LITERAL_HDR_4_VALUE_NEVER\n"); |
951 | 0 | } else { |
952 | 0 | h2n->hpack_type = HPKT_INDEXED_HDR_4_VALUE; |
953 | 0 | lwsl_header(" HPKT_INDEXED_HDR_4_VALUE\n"); |
954 | 0 | } |
955 | 0 | h2n->hdr_idx = 0; |
956 | 0 | if ((c & 0xf) == 0xf) { |
957 | 0 | h2n->hpack_len = c & 0xf; |
958 | 0 | h2n->hpack_m = 0xf; |
959 | 0 | h2n->hpack_len = 0; |
960 | 0 | h2n->hpack = HPKS_IDX_EXT; |
961 | 0 | break; |
962 | 0 | } |
963 | 0 | h2n->hdr_idx = c & 0xf; |
964 | 0 | h2n->value = 1; |
965 | 0 | h2n->hpack = HPKS_HLEN; |
966 | 0 | break; |
967 | | |
968 | 0 | case 0x20: |
969 | 0 | case 0x30: /* header table size update */ |
970 | | /* possibly-extended size value (5) */ |
971 | 0 | lwsl_header("HPKT_SIZE_5 %x\n", c &0x1f); |
972 | 0 | h2n->hpack_type = HPKT_SIZE_5; |
973 | 0 | h2n->hpack_len = c & 0x1f; |
974 | 0 | if (h2n->hpack_len == 0x1f) { |
975 | 0 | h2n->hpack_m = 0x1f; |
976 | 0 | h2n->hpack_len = 0; |
977 | 0 | h2n->hpack = HPKS_IDX_EXT; |
978 | 0 | break; |
979 | 0 | } |
980 | 0 | h2n->last_action_dyntable_resize = 1; |
981 | 0 | if (lws_hpack_dynamic_size(wsi, (int)h2n->hpack_len)) |
982 | 0 | return 1; |
983 | 0 | break; |
984 | 0 | } |
985 | 0 | break; |
986 | | |
987 | 0 | case HPKS_IDX_EXT: |
988 | 0 | h2n->hpack_len = (uint32_t)((unsigned int)h2n->hpack_len | |
989 | 0 | (unsigned int)((c & 0x7f) << h2n->ext_count)); |
990 | 0 | h2n->ext_count = (uint8_t)(h2n->ext_count + 7); |
991 | 0 | if (c & 0x80) /* extended int not complete yet */ |
992 | 0 | break; |
993 | | |
994 | | /* extended integer done */ |
995 | 0 | h2n->hpack_len += h2n->hpack_m; |
996 | 0 | lwsl_header("HPKS_IDX_EXT: hpack_len %u\n", (unsigned int)h2n->hpack_len); |
997 | |
|
998 | 0 | switch (h2n->hpack_type) { |
999 | 0 | case HPKT_INDEXED_HDR_7: |
1000 | 0 | if (lws_hpack_use_idx_hdr(wsi, (int)h2n->hpack_len, |
1001 | 0 | (int)h2n->hdr_idx)) { |
1002 | 0 | lwsl_notice("%s: hd7 use fail\n", __func__); |
1003 | 0 | return 1; |
1004 | 0 | } |
1005 | 0 | h2n->hpack = HPKS_TYPE; |
1006 | 0 | break; |
1007 | | |
1008 | 0 | case HPKT_SIZE_5: |
1009 | 0 | h2n->last_action_dyntable_resize = 1; |
1010 | 0 | if (lws_hpack_dynamic_size(wsi, (int)h2n->hpack_len)) |
1011 | 0 | return 1; |
1012 | 0 | h2n->hpack = HPKS_TYPE; |
1013 | 0 | break; |
1014 | | |
1015 | 0 | default: |
1016 | 0 | h2n->hdr_idx = h2n->hpack_len; |
1017 | 0 | if (!h2n->hdr_idx) { |
1018 | 0 | lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, |
1019 | 0 | "extended header index was 0"); |
1020 | 0 | return 1; |
1021 | 0 | } |
1022 | 0 | h2n->value = 1; |
1023 | 0 | h2n->hpack = HPKS_HLEN; |
1024 | 0 | break; |
1025 | 0 | } |
1026 | 0 | break; |
1027 | | |
1028 | 0 | case HPKS_HLEN: /* [ H | 7+ ] */ |
1029 | 0 | h2n->huff = !!(c & 0x80); |
1030 | 0 | h2n->hpack_pos = 0; |
1031 | 0 | h2n->hpack_len = c & 0x7f; |
1032 | |
|
1033 | 0 | if (h2n->hpack_len == 0x7f) { |
1034 | 0 | h2n->hpack_m = 0x7f; |
1035 | 0 | h2n->hpack_len = 0; |
1036 | 0 | h2n->ext_count = 0; |
1037 | 0 | h2n->hpack = HPKS_HLEN_EXT; |
1038 | 0 | break; |
1039 | 0 | } |
1040 | | |
1041 | 0 | if (h2n->value && !h2n->hpack_len) { |
1042 | 0 | lwsl_debug("%s: zero-length header data\n", __func__); |
1043 | 0 | h2n->hpack = HPKS_TYPE; |
1044 | 0 | goto fin; |
1045 | 0 | } |
1046 | | |
1047 | 0 | pre_data: |
1048 | 0 | h2n->hpack = HPKS_DATA; |
1049 | 0 | if (!h2n->value || !h2n->hdr_idx) { |
1050 | 0 | ah->parser_state = WSI_TOKEN_NAME_PART; |
1051 | 0 | ah->lextable_pos = 0; |
1052 | 0 | h2n->unknown_header = 0; |
1053 | 0 | break; |
1054 | 0 | } |
1055 | | |
1056 | 0 | if (h2n->hpack_type == HPKT_LITERAL_HDR_VALUE || |
1057 | 0 | h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR || |
1058 | 0 | h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER) { |
1059 | 0 | n = ah->parser_state; |
1060 | 0 | if (n == 255) { |
1061 | 0 | n = -1; |
1062 | 0 | h2n->hdr_idx = (uint32_t)-1; |
1063 | 0 | } else |
1064 | 0 | h2n->hdr_idx = 1; |
1065 | 0 | } else { |
1066 | 0 | n = lws_token_from_index(wsi, (int)h2n->hdr_idx, NULL, |
1067 | 0 | NULL, NULL); |
1068 | 0 | lwsl_header(" lws_tok_from_idx(%u) says %d\n", |
1069 | 0 | (unsigned int)h2n->hdr_idx, n); |
1070 | 0 | } |
1071 | |
|
1072 | 0 | if (n == LWS_HPACK_IGNORE_ENTRY || n == -1) |
1073 | 0 | h2n->hdr_idx = LWS_HPACK_IGNORE_ENTRY; |
1074 | |
|
1075 | 0 | switch (h2n->hpack_type) { |
1076 | | /* |
1077 | | * hpack types with literal headers were parsed by the lws |
1078 | | * header SM... on recognition of a known lws header, it does |
1079 | | * the correct lws_frag_start() for us already. Other types |
1080 | | * (ie, indexed header) need us to do it here. |
1081 | | */ |
1082 | 0 | case HPKT_LITERAL_HDR_VALUE_INCR: |
1083 | 0 | case HPKT_LITERAL_HDR_VALUE: |
1084 | 0 | case HPKT_LITERAL_HDR_VALUE_NEVER: |
1085 | 0 | break; |
1086 | 0 | default: |
1087 | 0 | if (n != -1 && n != LWS_HPACK_IGNORE_ENTRY && |
1088 | 0 | lws_frag_start(wsi, n)) { |
1089 | 0 | lwsl_header("%s: frag start failed\n", |
1090 | 0 | __func__); |
1091 | 0 | return 1; |
1092 | 0 | } |
1093 | 0 | break; |
1094 | 0 | } |
1095 | 0 | break; |
1096 | | |
1097 | 0 | case HPKS_HLEN_EXT: |
1098 | 0 | h2n->hpack_len = (uint32_t)((unsigned int)h2n->hpack_len | |
1099 | 0 | (unsigned int)((c & 0x7f) << h2n->ext_count)); |
1100 | 0 | h2n->ext_count = (uint8_t)(h2n->ext_count + 7); |
1101 | 0 | if (c & 0x80) /* extended integer not complete yet */ |
1102 | 0 | break; |
1103 | | |
1104 | 0 | h2n->hpack_len += h2n->hpack_m; |
1105 | 0 | goto pre_data; |
1106 | | |
1107 | 0 | case HPKS_DATA: |
1108 | | //lwsl_header(" 0x%02X huff %d\n", c, h2n->huff); |
1109 | 0 | c1 = c; |
1110 | |
|
1111 | 0 | for (n = 0; n < 8; n++) { |
1112 | 0 | if (h2n->huff) { |
1113 | 0 | char b = (c >> 7) & 1; |
1114 | 0 | prev = h2n->hpack_pos; |
1115 | 0 | h2n->hpack_pos = (uint16_t)huftable_decode( |
1116 | 0 | (int)h2n->hpack_pos, b); |
1117 | 0 | c = (unsigned char)(c << 1); |
1118 | 0 | if (h2n->hpack_pos == 0xffff) { |
1119 | 0 | lwsl_notice("Huffman err\n"); |
1120 | 0 | return 1; |
1121 | 0 | } |
1122 | 0 | if (!(h2n->hpack_pos & 0x8000)) { |
1123 | 0 | if (!b) |
1124 | 0 | h2n->zero_huff_padding = 1; |
1125 | 0 | h2n->huff_pad++; |
1126 | 0 | continue; |
1127 | 0 | } |
1128 | 0 | c1 = (uint8_t)(h2n->hpack_pos & 0x7fff); |
1129 | 0 | h2n->hpack_pos = 0; |
1130 | 0 | h2n->huff_pad = 0; |
1131 | 0 | h2n->zero_huff_padding = 0; |
1132 | | |
1133 | | /* EOS |11111111|11111111|11111111|111111 */ |
1134 | 0 | if (!c1 && prev == HUFTABLE_0x100_PREV) { |
1135 | 0 | lws_h2_goaway(nwsi, |
1136 | 0 | H2_ERR_COMPRESSION_ERROR, |
1137 | 0 | "Huffman EOT seen"); |
1138 | 0 | return 1; |
1139 | 0 | } |
1140 | 0 | } else |
1141 | 0 | n = 8; |
1142 | | |
1143 | 0 | if (h2n->value) { /* value */ |
1144 | |
|
1145 | 0 | if (h2n->hdr_idx && |
1146 | 0 | h2n->hdr_idx != LWS_HPACK_IGNORE_ENTRY) { |
1147 | |
|
1148 | 0 | if (ah->hdr_token_idx == |
1149 | 0 | WSI_TOKEN_HTTP_COLON_PATH) { |
1150 | |
|
1151 | 0 | switch (lws_parse_urldecode( |
1152 | 0 | wsi, &c1)) { |
1153 | 0 | case LPUR_CONTINUE: |
1154 | 0 | break; |
1155 | 0 | case LPUR_SWALLOW: |
1156 | 0 | goto swallow; |
1157 | 0 | case LPUR_EXCESSIVE: |
1158 | 0 | case LPUR_FORBID: |
1159 | 0 | lws_h2_goaway(nwsi, |
1160 | 0 | H2_ERR_PROTOCOL_ERROR, |
1161 | 0 | "Evil URI"); |
1162 | 0 | return 1; |
1163 | | |
1164 | 0 | default: |
1165 | 0 | return -1; |
1166 | 0 | } |
1167 | 0 | } |
1168 | 0 | if (lws_frag_append(wsi, c1)) { |
1169 | 0 | lwsl_notice( |
1170 | 0 | "%s: frag app fail\n", |
1171 | 0 | __func__); |
1172 | 0 | return 1; |
1173 | 0 | } |
1174 | 0 | } //else |
1175 | | //lwsl_header("ignoring %c\n", c1); |
1176 | 0 | } else { |
1177 | | /* |
1178 | | * Convert name using existing parser, |
1179 | | * If h2n->unknown_header == 0, result is |
1180 | | * in wsi->parser_state |
1181 | | * using WSI_TOKEN_GET_URI. |
1182 | | * |
1183 | | * If unknown header h2n->unknown_header |
1184 | | * will be set. |
1185 | | */ |
1186 | 0 | h2n->hpack_hdr_len++; |
1187 | 0 | if (h2n->is_first_header_char) { |
1188 | 0 | h2n->is_first_header_char = 0; |
1189 | 0 | h2n->first_hdr_char = (char)c1; |
1190 | 0 | } |
1191 | 0 | lwsl_header("parser: %c\n", c1); |
1192 | | /* uppercase header names illegal */ |
1193 | 0 | if (c1 >= 'A' && c1 <= 'Z') { |
1194 | 0 | lws_h2_goaway(nwsi, |
1195 | 0 | H2_ERR_COMPRESSION_ERROR, |
1196 | 0 | "Uppercase literal hpack hdr"); |
1197 | 0 | return 1; |
1198 | 0 | } |
1199 | 0 | plen = 1; |
1200 | 0 | if (!h2n->unknown_header && |
1201 | 0 | lws_parse(wsi, &c1, &plen)) |
1202 | 0 | h2n->unknown_header = 1; |
1203 | 0 | } |
1204 | 0 | swallow: |
1205 | 0 | (void)n; |
1206 | 0 | } // for n |
1207 | | |
1208 | 0 | if (--h2n->hpack_len) |
1209 | 0 | break; |
1210 | | |
1211 | | /* |
1212 | | * The header (h2n->value = 0) or the payload (h2n->value = 1) |
1213 | | * is complete. |
1214 | | */ |
1215 | | |
1216 | 0 | if (h2n->huff && (h2n->huff_pad > 7 || |
1217 | 0 | (h2n->zero_huff_padding && h2n->huff_pad))) { |
1218 | 0 | lwsl_info("zero_huff_padding: %d huff_pad: %d\n", |
1219 | 0 | h2n->zero_huff_padding, h2n->huff_pad); |
1220 | 0 | lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR, |
1221 | 0 | "Huffman padding excessive or wrong"); |
1222 | 0 | return 1; |
1223 | 0 | } |
1224 | 0 | fin: |
1225 | 0 | if (!h2n->value && ( |
1226 | 0 | h2n->hpack_type == HPKT_LITERAL_HDR_VALUE || |
1227 | 0 | h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR || |
1228 | 0 | h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER)) { |
1229 | 0 | h2n->hdr_idx = LWS_HPACK_IGNORE_ENTRY; |
1230 | 0 | lwsl_header("wsi->parser_state: %d\n", |
1231 | 0 | ah->parser_state); |
1232 | |
|
1233 | 0 | if (ah->parser_state == WSI_TOKEN_NAME_PART) { |
1234 | | /* h2 headers come without the colon */ |
1235 | 0 | c1 = ':'; |
1236 | 0 | plen = 1; |
1237 | 0 | n = lws_parse(wsi, &c1, &plen); |
1238 | 0 | (void)n; |
1239 | 0 | } |
1240 | |
|
1241 | 0 | if (ah->parser_state == WSI_TOKEN_NAME_PART || |
1242 | 0 | #if defined(LWS_WITH_CUSTOM_HEADERS) |
1243 | 0 | ah->parser_state == WSI_TOKEN_UNKNOWN_VALUE_PART || |
1244 | 0 | #endif |
1245 | 0 | ah->parser_state == WSI_TOKEN_SKIPPING) { |
1246 | 0 | h2n->unknown_header = 1; |
1247 | 0 | ah->parser_state = 0xff; |
1248 | 0 | wsi->seen_nonpseudoheader = 1; |
1249 | 0 | } |
1250 | 0 | } |
1251 | | |
1252 | | /* we have the header */ |
1253 | 0 | if (!h2n->value) { |
1254 | 0 | h2n->value = 1; |
1255 | 0 | h2n->hpack = HPKS_HLEN; |
1256 | 0 | h2n->huff_pad = 0; |
1257 | 0 | h2n->zero_huff_padding = 0; |
1258 | 0 | h2n->ext_count = 0; |
1259 | 0 | break; |
1260 | 0 | } |
1261 | | |
1262 | | /* |
1263 | | * we have got both the header and value |
1264 | | */ |
1265 | | |
1266 | 0 | m = -1; |
1267 | 0 | switch (h2n->hpack_type) { |
1268 | | /* |
1269 | | * These are the only two that insert to the dyntable |
1270 | | */ |
1271 | | /* NEW indexed hdr with value */ |
1272 | 0 | case HPKT_INDEXED_HDR_6_VALUE_INCR: |
1273 | | /* header length is determined by known index */ |
1274 | 0 | m = lws_token_from_index(wsi, (int)h2n->hdr_idx, NULL, NULL, |
1275 | 0 | &h2n->hpack_hdr_len); |
1276 | 0 | if (m < 0) |
1277 | | /* |
1278 | | * The peer may only send known 6-bit indexes, |
1279 | | * there's still the possibility it sends an unset |
1280 | | * dynamic index that we can't succeed to look up |
1281 | | */ |
1282 | 0 | return 1; |
1283 | 0 | goto add_it; |
1284 | | /* NEW literal hdr with value */ |
1285 | 0 | case HPKT_LITERAL_HDR_VALUE_INCR: |
1286 | | /* |
1287 | | * hdr is a new literal, so length is already in |
1288 | | * h2n->hpack_hdr_len |
1289 | | */ |
1290 | 0 | m = ah->parser_state; |
1291 | 0 | if (h2n->unknown_header || |
1292 | 0 | ah->parser_state == WSI_TOKEN_NAME_PART || |
1293 | 0 | ah->parser_state == WSI_TOKEN_SKIPPING) { |
1294 | 0 | if (h2n->first_hdr_char == ':') { |
1295 | 0 | lwsl_info("HPKT_LITERAL_HDR_VALUE_INCR:" |
1296 | 0 | " end state %d unk hdr %d\n", |
1297 | 0 | ah->parser_state, |
1298 | 0 | h2n->unknown_header); |
1299 | | /* unknown pseudoheaders are illegal */ |
1300 | 0 | lws_h2_goaway(nwsi, |
1301 | 0 | H2_ERR_PROTOCOL_ERROR, |
1302 | 0 | "Unknown pseudoheader"); |
1303 | 0 | return 1; |
1304 | 0 | } |
1305 | 0 | m = LWS_HPACK_IGNORE_ENTRY; |
1306 | 0 | } |
1307 | 0 | add_it: |
1308 | | /* |
1309 | | * mark us as having been set at the time of dynamic |
1310 | | * token insertion. |
1311 | | */ |
1312 | 0 | ah->frags[ah->nfrag].flags |= 1; |
1313 | |
|
1314 | 0 | if (lws_dynamic_token_insert(wsi, (int)h2n->hpack_hdr_len, m, |
1315 | 0 | &ah->data[ah->frags[ah->nfrag].offset], |
1316 | 0 | ah->frags[ah->nfrag].len)) { |
1317 | 0 | lwsl_notice("%s: tok_insert fail\n", __func__); |
1318 | 0 | return 1; |
1319 | 0 | } |
1320 | 0 | break; |
1321 | | |
1322 | 0 | default: |
1323 | 0 | break; |
1324 | 0 | } |
1325 | | |
1326 | 0 | if (h2n->hdr_idx != LWS_HPACK_IGNORE_ENTRY && lws_frag_end(wsi)) |
1327 | 0 | return 1; |
1328 | | |
1329 | 0 | if (h2n->hpack_type != HPKT_INDEXED_HDR_6_VALUE_INCR) { |
1330 | |
|
1331 | 0 | if (h2n->hpack_type == HPKT_LITERAL_HDR_VALUE || |
1332 | 0 | h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR || |
1333 | 0 | h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER) { |
1334 | 0 | m = ah->parser_state; |
1335 | 0 | if (m == 255) |
1336 | 0 | m = -1; |
1337 | 0 | } else |
1338 | 0 | m = lws_token_from_index(wsi, (int)h2n->hdr_idx, |
1339 | 0 | NULL, NULL, NULL); |
1340 | 0 | } |
1341 | |
|
1342 | 0 | if (m != -1 && m != LWS_HPACK_IGNORE_ENTRY) |
1343 | 0 | lws_dump_header(wsi, m); |
1344 | |
|
1345 | 0 | if (lws_hpack_handle_pseudo_rules(nwsi, wsi, m)) |
1346 | 0 | return 1; |
1347 | | |
1348 | 0 | h2n->is_first_header_char = 1; |
1349 | 0 | h2n->hpack = HPKS_TYPE; |
1350 | 0 | break; |
1351 | 0 | } |
1352 | | |
1353 | 0 | return 0; |
1354 | 0 | } |
1355 | | |
1356 | | |
1357 | | |
1358 | | static unsigned int |
1359 | | lws_h2_num_start(int starting_bits, unsigned long num) |
1360 | 0 | { |
1361 | 0 | unsigned int mask = (unsigned int)((1 << starting_bits) - 1); |
1362 | |
|
1363 | 0 | if (num < mask) |
1364 | 0 | return (unsigned int)num; |
1365 | | |
1366 | 0 | return mask; |
1367 | 0 | } |
1368 | | |
1369 | | static int |
1370 | | lws_h2_num(int starting_bits, unsigned long num, |
1371 | | unsigned char **p, unsigned char *end) |
1372 | 0 | { |
1373 | 0 | unsigned int mask = (unsigned int)((1 << starting_bits) - 1); |
1374 | |
|
1375 | 0 | if (num < mask) |
1376 | 0 | return 0; |
1377 | | |
1378 | 0 | num -= mask; |
1379 | 0 | do { |
1380 | 0 | if (num > 127) |
1381 | 0 | *((*p)++) = (uint8_t)(0x80 | (num & 0x7f)); |
1382 | 0 | else |
1383 | 0 | *((*p)++) = (uint8_t)(0x00 | (num & 0x7f)); |
1384 | 0 | if (*p >= end) |
1385 | 0 | return 1; |
1386 | 0 | num >>= 7; |
1387 | 0 | } while (num); |
1388 | | |
1389 | 0 | return 0; |
1390 | 0 | } |
1391 | | |
1392 | | int lws_add_http2_header_by_name(struct lws *wsi, const unsigned char *name, |
1393 | | const unsigned char *value, int length, |
1394 | | unsigned char **p, unsigned char *end) |
1395 | 0 | { |
1396 | 0 | int len; |
1397 | |
|
1398 | 0 | #if defined(_DEBUG) |
1399 | | /* value does not have to be NUL-terminated... %.*s not available on |
1400 | | * all platforms */ |
1401 | 0 | if (value) { |
1402 | 0 | lws_strnncpy((char *)*p, (const char *)value, length, |
1403 | 0 | lws_ptr_diff(end, (*p))); |
1404 | |
|
1405 | 0 | lwsl_header("%s: %p %s:%s (len %d)\n", __func__, *p, name, |
1406 | 0 | (const char *)*p, length); |
1407 | 0 | } else { |
1408 | 0 | lwsl_err("%s: %p dummy copy %s (len %d)\n", __func__, *p, name, length); |
1409 | 0 | } |
1410 | 0 | #endif |
1411 | |
|
1412 | 0 | len = (int)strlen((char *)name); |
1413 | 0 | if (len) |
1414 | 0 | if (name[len - 1] == ':') |
1415 | 0 | len--; |
1416 | |
|
1417 | 0 | if (wsi->mux_substream && !strncmp((const char *)name, |
1418 | 0 | "transfer-encoding", (unsigned int)len)) { |
1419 | 0 | lwsl_header("rejecting %s\n", name); |
1420 | |
|
1421 | 0 | return 0; |
1422 | 0 | } |
1423 | | |
1424 | 0 | if (end - *p < len + length + 8) |
1425 | 0 | return 1; |
1426 | | |
1427 | 0 | *((*p)++) = 0; /* literal hdr, literal name, */ |
1428 | |
|
1429 | 0 | *((*p)++) = (uint8_t)(0 | (uint8_t)lws_h2_num_start(7, (unsigned long)len)); /* non-HUF */ |
1430 | 0 | if (lws_h2_num(7, (unsigned long)len, p, end)) |
1431 | 0 | return 1; |
1432 | | |
1433 | | /* upper-case header names are verboten in h2, but OK on h1, so |
1434 | | * they're not illegal per se. Silently convert them for h2... */ |
1435 | | |
1436 | 0 | while(len--) |
1437 | 0 | *((*p)++) = (uint8_t)tolower((int)*name++); |
1438 | |
|
1439 | 0 | *((*p)++) = (uint8_t)(0 | (uint8_t)lws_h2_num_start(7, (unsigned long)length)); /* non-HUF */ |
1440 | 0 | if (lws_h2_num(7, (unsigned long)length, p, end)) |
1441 | 0 | return 1; |
1442 | | |
1443 | 0 | if (value) |
1444 | 0 | memcpy(*p, value, (unsigned int)length); |
1445 | 0 | *p += length; |
1446 | |
|
1447 | 0 | return 0; |
1448 | 0 | } |
1449 | | |
1450 | | int lws_add_http2_header_by_token(struct lws *wsi, enum lws_token_indexes token, |
1451 | | const unsigned char *value, int length, |
1452 | | unsigned char **p, unsigned char *end) |
1453 | 0 | { |
1454 | 0 | const unsigned char *name; |
1455 | |
|
1456 | 0 | name = lws_token_to_string(token); |
1457 | 0 | if (!name) |
1458 | 0 | return 1; |
1459 | | |
1460 | 0 | return lws_add_http2_header_by_name(wsi, name, value, length, p, end); |
1461 | 0 | } |
1462 | | |
1463 | | int lws_add_http2_header_status(struct lws *wsi, unsigned int code, |
1464 | | unsigned char **p, unsigned char *end) |
1465 | 0 | { |
1466 | 0 | unsigned char status[12]; |
1467 | 0 | int n; |
1468 | |
|
1469 | 0 | wsi->h2.send_END_STREAM = 0; // !!(code >= 400); |
1470 | |
|
1471 | 0 | n = sprintf((char *)status, "%u", code); |
1472 | 0 | if (lws_add_http2_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_STATUS, |
1473 | 0 | status, n, p, end)) |
1474 | | |
1475 | 0 | return 1; |
1476 | | |
1477 | 0 | return 0; |
1478 | 0 | } |