• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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