1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2015 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "nghttp2_http.h"
26
27 #include <string.h>
28 #include <assert.h>
29 #include <stdio.h>
30
31 #include "nghttp2_hd.h"
32 #include "nghttp2_helper.h"
33 #include "nghttp2_extpri.h"
34 #include "sfparse.h"
35
downcase(uint8_t c)36 static uint8_t downcase(uint8_t c) {
37 return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38 }
39
memieq(const void * a,const void * b,size_t n)40 static int memieq(const void *a, const void *b, size_t n) {
41 size_t i;
42 const uint8_t *aa = a, *bb = b;
43
44 for (i = 0; i < n; ++i) {
45 if (downcase(aa[i]) != downcase(bb[i])) {
46 return 0;
47 }
48 }
49 return 1;
50 }
51
52 #define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
53
parse_uint(const uint8_t * s,size_t len)54 static int64_t parse_uint(const uint8_t *s, size_t len) {
55 int64_t n = 0;
56 size_t i;
57 if (len == 0) {
58 return -1;
59 }
60 for (i = 0; i < len; ++i) {
61 if ('0' <= s[i] && s[i] <= '9') {
62 if (n > INT64_MAX / 10) {
63 return -1;
64 }
65 n *= 10;
66 if (n > INT64_MAX - (s[i] - '0')) {
67 return -1;
68 }
69 n += s[i] - '0';
70 continue;
71 }
72 return -1;
73 }
74 return n;
75 }
76
check_pseudo_header(nghttp2_stream * stream,const nghttp2_hd_nv * nv,uint32_t flag)77 static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
78 uint32_t flag) {
79 if ((stream->http_flags & flag) || nv->value->len == 0) {
80 return 0;
81 }
82 stream->http_flags = stream->http_flags | flag;
83 return 1;
84 }
85
expect_response_body(nghttp2_stream * stream)86 static int expect_response_body(nghttp2_stream *stream) {
87 return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
88 stream->status_code / 100 != 1 && stream->status_code != 304 &&
89 stream->status_code != 204;
90 }
91
92 /* For "http" or "https" URIs, OPTIONS request may have "*" in :path
93 header field to represent system-wide OPTIONS request. Otherwise,
94 :path header field value must start with "/". This function must
95 be called after ":method" header field was received. This function
96 returns nonzero if path is valid.*/
check_path(nghttp2_stream * stream)97 static int check_path(nghttp2_stream *stream) {
98 return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
99 ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
100 ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
101 (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
102 }
103
http_request_on_header(nghttp2_stream * stream,nghttp2_hd_nv * nv,int trailer,int connect_protocol)104 static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
105 int trailer, int connect_protocol) {
106 nghttp2_extpri extpri;
107
108 if (nv->name->base[0] == ':') {
109 if (trailer ||
110 (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
111 return NGHTTP2_ERR_HTTP_HEADER;
112 }
113 }
114
115 switch (nv->token) {
116 case NGHTTP2_TOKEN__AUTHORITY:
117 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
118 return NGHTTP2_ERR_HTTP_HEADER;
119 }
120 break;
121 case NGHTTP2_TOKEN__METHOD:
122 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
123 return NGHTTP2_ERR_HTTP_HEADER;
124 }
125 switch (nv->value->len) {
126 case 4:
127 if (lstreq("HEAD", nv->value->base, nv->value->len)) {
128 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
129 }
130 break;
131 case 7:
132 switch (nv->value->base[6]) {
133 case 'T':
134 if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
135 if (stream->stream_id % 2 == 0) {
136 /* we won't allow CONNECT for push */
137 return NGHTTP2_ERR_HTTP_HEADER;
138 }
139 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
140 }
141 break;
142 case 'S':
143 if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
144 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
145 }
146 break;
147 }
148 break;
149 }
150 break;
151 case NGHTTP2_TOKEN__PATH:
152 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
153 return NGHTTP2_ERR_HTTP_HEADER;
154 }
155 if (nv->value->base[0] == '/') {
156 stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
157 } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
158 stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
159 }
160 break;
161 case NGHTTP2_TOKEN__SCHEME:
162 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
163 return NGHTTP2_ERR_HTTP_HEADER;
164 }
165 if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
166 (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
167 stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
168 }
169 break;
170 case NGHTTP2_TOKEN__PROTOCOL:
171 if (!connect_protocol) {
172 return NGHTTP2_ERR_HTTP_HEADER;
173 }
174
175 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
176 return NGHTTP2_ERR_HTTP_HEADER;
177 }
178 break;
179 case NGHTTP2_TOKEN_HOST:
180 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
181 return NGHTTP2_ERR_HTTP_HEADER;
182 }
183 break;
184 case NGHTTP2_TOKEN_CONTENT_LENGTH: {
185 if (stream->content_length != -1) {
186 return NGHTTP2_ERR_HTTP_HEADER;
187 }
188 stream->content_length = parse_uint(nv->value->base, nv->value->len);
189 if (stream->content_length == -1) {
190 return NGHTTP2_ERR_HTTP_HEADER;
191 }
192 break;
193 }
194 /* disallowed header fields */
195 case NGHTTP2_TOKEN_CONNECTION:
196 case NGHTTP2_TOKEN_KEEP_ALIVE:
197 case NGHTTP2_TOKEN_PROXY_CONNECTION:
198 case NGHTTP2_TOKEN_TRANSFER_ENCODING:
199 case NGHTTP2_TOKEN_UPGRADE:
200 return NGHTTP2_ERR_HTTP_HEADER;
201 case NGHTTP2_TOKEN_TE:
202 if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
203 return NGHTTP2_ERR_HTTP_HEADER;
204 }
205 break;
206 case NGHTTP2_TOKEN_PRIORITY:
207 if (!trailer &&
208 /* Do not parse the header field in PUSH_PROMISE. */
209 (stream->stream_id & 1) &&
210 (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) &&
211 !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
212 nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
213 if (nghttp2_http_parse_priority(&extpri, nv->value->base,
214 nv->value->len) == 0) {
215 stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
216 stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
217 } else {
218 stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
219 stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
220 }
221 }
222 break;
223 default:
224 if (nv->name->base[0] == ':') {
225 return NGHTTP2_ERR_HTTP_HEADER;
226 }
227 }
228
229 if (nv->name->base[0] != ':') {
230 stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
231 }
232
233 return 0;
234 }
235
http_response_on_header(nghttp2_stream * stream,nghttp2_hd_nv * nv,int trailer)236 static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
237 int trailer) {
238 if (nv->name->base[0] == ':') {
239 if (trailer ||
240 (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
241 return NGHTTP2_ERR_HTTP_HEADER;
242 }
243 }
244
245 switch (nv->token) {
246 case NGHTTP2_TOKEN__STATUS: {
247 if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
248 return NGHTTP2_ERR_HTTP_HEADER;
249 }
250 if (nv->value->len != 3) {
251 return NGHTTP2_ERR_HTTP_HEADER;
252 }
253 stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
254 if (stream->status_code == -1 || stream->status_code == 101) {
255 return NGHTTP2_ERR_HTTP_HEADER;
256 }
257 break;
258 }
259 case NGHTTP2_TOKEN_CONTENT_LENGTH: {
260 if (stream->status_code == 204) {
261 /* content-length header field in 204 response is prohibited by
262 RFC 7230. But some widely used servers send content-length:
263 0. Until they get fixed, we ignore it. */
264 if (stream->content_length != -1) {
265 /* Found multiple content-length field */
266 return NGHTTP2_ERR_HTTP_HEADER;
267 }
268 if (!lstrieq("0", nv->value->base, nv->value->len)) {
269 return NGHTTP2_ERR_HTTP_HEADER;
270 }
271 stream->content_length = 0;
272 return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
273 }
274 if (stream->status_code / 100 == 1) {
275 return NGHTTP2_ERR_HTTP_HEADER;
276 }
277 /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
278 if (stream->status_code / 100 == 2 &&
279 (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
280 return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
281 }
282 if (stream->content_length != -1) {
283 return NGHTTP2_ERR_HTTP_HEADER;
284 }
285 stream->content_length = parse_uint(nv->value->base, nv->value->len);
286 if (stream->content_length == -1) {
287 return NGHTTP2_ERR_HTTP_HEADER;
288 }
289 break;
290 }
291 /* disallowed header fields */
292 case NGHTTP2_TOKEN_CONNECTION:
293 case NGHTTP2_TOKEN_KEEP_ALIVE:
294 case NGHTTP2_TOKEN_PROXY_CONNECTION:
295 case NGHTTP2_TOKEN_TRANSFER_ENCODING:
296 case NGHTTP2_TOKEN_UPGRADE:
297 return NGHTTP2_ERR_HTTP_HEADER;
298 case NGHTTP2_TOKEN_TE:
299 if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
300 return NGHTTP2_ERR_HTTP_HEADER;
301 }
302 break;
303 default:
304 if (nv->name->base[0] == ':') {
305 return NGHTTP2_ERR_HTTP_HEADER;
306 }
307 }
308
309 if (nv->name->base[0] != ':') {
310 stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
311 }
312
313 return 0;
314 }
315
check_scheme(const uint8_t * value,size_t len)316 static int check_scheme(const uint8_t *value, size_t len) {
317 const uint8_t *last;
318 if (len == 0) {
319 return 0;
320 }
321
322 if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
323 return 0;
324 }
325
326 last = value + len;
327 ++value;
328
329 for (; value != last; ++value) {
330 if (!(('A' <= *value && *value <= 'Z') ||
331 ('a' <= *value && *value <= 'z') ||
332 ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
333 *value == '.')) {
334 return 0;
335 }
336 }
337 return 1;
338 }
339
lws(const uint8_t * s,size_t n)340 static int lws(const uint8_t *s, size_t n) {
341 size_t i;
342 for (i = 0; i < n; ++i) {
343 if (s[i] != ' ' && s[i] != '\t') {
344 return 0;
345 }
346 }
347 return 1;
348 }
349
nghttp2_http_on_header(nghttp2_session * session,nghttp2_stream * stream,nghttp2_frame * frame,nghttp2_hd_nv * nv,int trailer)350 int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
351 nghttp2_frame *frame, nghttp2_hd_nv *nv,
352 int trailer) {
353 int rv;
354
355 /* We are strict for pseudo header field. One bad character should
356 lead to fail. OTOH, we should be a bit forgiving for regular
357 headers, since existing public internet has so much illegal
358 headers floating around and if we kill the stream because of
359 this, we may disrupt many web sites and/or libraries. So we
360 become conservative here, and just ignore those illegal regular
361 headers. */
362 if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
363 size_t i;
364 if (nv->name->len > 0 && nv->name->base[0] == ':') {
365 return NGHTTP2_ERR_HTTP_HEADER;
366 }
367 /* header field name must be lower-cased without exception */
368 for (i = 0; i < nv->name->len; ++i) {
369 uint8_t c = nv->name->base[i];
370 if ('A' <= c && c <= 'Z') {
371 return NGHTTP2_ERR_HTTP_HEADER;
372 }
373 }
374 /* When ignoring regular headers, we set this flag so that we
375 still enforce header field ordering rule for pseudo header
376 fields. */
377 stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
378 return NGHTTP2_ERR_IGN_HTTP_HEADER;
379 }
380
381 switch (nv->token) {
382 case NGHTTP2_TOKEN__METHOD:
383 rv = nghttp2_check_method(nv->value->base, nv->value->len);
384 break;
385 case NGHTTP2_TOKEN__PATH:
386 rv = nghttp2_check_path(nv->value->base, nv->value->len);
387 break;
388 case NGHTTP2_TOKEN__AUTHORITY:
389 case NGHTTP2_TOKEN_HOST:
390 if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
391 rv = nghttp2_check_authority(nv->value->base, nv->value->len);
392 } else if (
393 stream->flags &
394 NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
395 rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
396 } else {
397 rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
398 }
399 break;
400 case NGHTTP2_TOKEN__SCHEME:
401 rv = check_scheme(nv->value->base, nv->value->len);
402 break;
403 case NGHTTP2_TOKEN__PROTOCOL:
404 /* Check the value consists of just white spaces, which was done
405 in check_pseudo_header before
406 nghttp2_check_header_value_rfc9113 has been introduced. */
407 if ((stream->flags &
408 NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
409 lws(nv->value->base, nv->value->len)) {
410 rv = 0;
411 break;
412 }
413 /* fall through */
414 default:
415 if (stream->flags &
416 NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
417 rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
418 } else {
419 rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
420 }
421 }
422
423 if (rv == 0) {
424 assert(nv->name->len > 0);
425 if (nv->name->base[0] == ':') {
426 return NGHTTP2_ERR_HTTP_HEADER;
427 }
428 /* When ignoring regular headers, we set this flag so that we
429 still enforce header field ordering rule for pseudo header
430 fields. */
431 stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
432 return NGHTTP2_ERR_IGN_HTTP_HEADER;
433 }
434
435 if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
436 return http_request_on_header(stream, nv, trailer,
437 session->server &&
438 session->pending_enable_connect_protocol);
439 }
440
441 return http_response_on_header(stream, nv, trailer);
442 }
443
nghttp2_http_on_request_headers(nghttp2_stream * stream,nghttp2_frame * frame)444 int nghttp2_http_on_request_headers(nghttp2_stream *stream,
445 nghttp2_frame *frame) {
446 if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
447 (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
448 if ((stream->http_flags &
449 (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
450 (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
451 return -1;
452 }
453 stream->content_length = -1;
454 } else {
455 if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
456 NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
457 (stream->http_flags &
458 (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
459 return -1;
460 }
461 if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
462 ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
463 (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
464 return -1;
465 }
466 if (!check_path(stream)) {
467 return -1;
468 }
469 }
470
471 if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
472 /* we are going to reuse data fields for upcoming response. Clear
473 them now, except for method flags. */
474 stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
475 stream->content_length = -1;
476 }
477
478 return 0;
479 }
480
nghttp2_http_on_response_headers(nghttp2_stream * stream)481 int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
482 if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
483 return -1;
484 }
485
486 if (stream->status_code / 100 == 1) {
487 /* non-final response */
488 stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
489 NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
490 stream->content_length = -1;
491 stream->status_code = -1;
492 return 0;
493 }
494
495 stream->http_flags =
496 stream->http_flags & (uint32_t)~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
497
498 if (!expect_response_body(stream)) {
499 stream->content_length = 0;
500 } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
501 NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
502 stream->content_length = -1;
503 }
504
505 return 0;
506 }
507
nghttp2_http_on_trailer_headers(nghttp2_stream * stream,nghttp2_frame * frame)508 int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
509 nghttp2_frame *frame) {
510 (void)stream;
511
512 if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
513 return -1;
514 }
515
516 return 0;
517 }
518
nghttp2_http_on_remote_end_stream(nghttp2_stream * stream)519 int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
520 if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
521 return -1;
522 }
523
524 if (stream->content_length != -1 &&
525 stream->content_length != stream->recv_content_length) {
526 return -1;
527 }
528
529 return 0;
530 }
531
nghttp2_http_on_data_chunk(nghttp2_stream * stream,size_t n)532 int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
533 stream->recv_content_length += (int64_t)n;
534
535 if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
536 (stream->content_length != -1 &&
537 stream->recv_content_length > stream->content_length)) {
538 return -1;
539 }
540
541 return 0;
542 }
543
nghttp2_http_record_request_method(nghttp2_stream * stream,nghttp2_frame * frame)544 void nghttp2_http_record_request_method(nghttp2_stream *stream,
545 nghttp2_frame *frame) {
546 const nghttp2_nv *nva;
547 size_t nvlen;
548 size_t i;
549
550 switch (frame->hd.type) {
551 case NGHTTP2_HEADERS:
552 nva = frame->headers.nva;
553 nvlen = frame->headers.nvlen;
554 break;
555 case NGHTTP2_PUSH_PROMISE:
556 nva = frame->push_promise.nva;
557 nvlen = frame->push_promise.nvlen;
558 break;
559 default:
560 return;
561 }
562
563 /* TODO we should do this strictly. */
564 for (i = 0; i < nvlen; ++i) {
565 const nghttp2_nv *nv = &nva[i];
566 if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
567 memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
568 continue;
569 }
570 if (lstreq("CONNECT", nv->value, nv->valuelen)) {
571 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
572 return;
573 }
574 if (lstreq("HEAD", nv->value, nv->valuelen)) {
575 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
576 return;
577 }
578 return;
579 }
580 }
581
nghttp2_http_parse_priority(nghttp2_extpri * dest,const uint8_t * value,size_t valuelen)582 int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
583 size_t valuelen) {
584 nghttp2_extpri pri = *dest;
585 sf_parser sfp;
586 sf_vec key;
587 sf_value val;
588 int rv;
589
590 sf_parser_init(&sfp, value, valuelen);
591
592 for (;;) {
593 rv = sf_parser_dict(&sfp, &key, &val);
594 if (rv != 0) {
595 if (rv == SF_ERR_EOF) {
596 break;
597 }
598
599 return NGHTTP2_ERR_INVALID_ARGUMENT;
600 }
601
602 if (key.len != 1) {
603 continue;
604 }
605
606 switch (key.base[0]) {
607 case 'i':
608 if (val.type != SF_TYPE_BOOLEAN) {
609 return NGHTTP2_ERR_INVALID_ARGUMENT;
610 }
611
612 pri.inc = val.boolean;
613
614 break;
615 case 'u':
616 if (val.type != SF_TYPE_INTEGER ||
617 val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
618 NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
619 return NGHTTP2_ERR_INVALID_ARGUMENT;
620 }
621
622 pri.urgency = (uint32_t)val.integer;
623
624 break;
625 }
626 }
627
628 *dest = pri;
629
630 return 0;
631 }
632