• 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 
350 /* Generated by genauthoritychartbl.py, but '@' is not allowed */
351 static char VALID_AUTHORITY_CHARS[] = {
352   0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
353   0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
354   0 /* BS   */, 0 /* HT   */, 0 /* LF   */, 0 /* VT   */,
355   0 /* FF   */, 0 /* CR   */, 0 /* SO   */, 0 /* SI   */,
356   0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
357   0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */,
358   0 /* CAN  */, 0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */,
359   0 /* FS   */, 0 /* GS   */, 0 /* RS   */, 0 /* US   */,
360   0 /* SPC  */, 1 /* !    */, 0 /* "    */, 0 /* #    */,
361   1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
362   1 /* (    */, 1 /* )    */, 1 /* *    */, 1 /* +    */,
363   1 /* ,    */, 1 /* -    */, 1 /* .    */, 0 /* /    */,
364   1 /* 0    */, 1 /* 1    */, 1 /* 2    */, 1 /* 3    */,
365   1 /* 4    */, 1 /* 5    */, 1 /* 6    */, 1 /* 7    */,
366   1 /* 8    */, 1 /* 9    */, 1 /* :    */, 1 /* ;    */,
367   0 /* <    */, 1 /* =    */, 0 /* >    */, 0 /* ?    */,
368   0 /* @    */, 1 /* A    */, 1 /* B    */, 1 /* C    */,
369   1 /* D    */, 1 /* E    */, 1 /* F    */, 1 /* G    */,
370   1 /* H    */, 1 /* I    */, 1 /* J    */, 1 /* K    */,
371   1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,
372   1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */,
373   1 /* T    */, 1 /* U    */, 1 /* V    */, 1 /* W    */,
374   1 /* X    */, 1 /* Y    */, 1 /* Z    */, 1 /* [    */,
375   0 /* \    */, 1 /* ]    */, 0 /* ^    */, 1 /* _    */,
376   0 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
377   1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */,
378   1 /* h    */, 1 /* i    */, 1 /* j    */, 1 /* k    */,
379   1 /* l    */, 1 /* m    */, 1 /* n    */, 1 /* o    */,
380   1 /* p    */, 1 /* q    */, 1 /* r    */, 1 /* s    */,
381   1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
382   1 /* x    */, 1 /* y    */, 1 /* z    */, 0 /* {    */,
383   0 /* |    */, 0 /* }    */, 1 /* ~    */, 0 /* DEL  */,
384   0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
385   0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
386   0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
387   0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
388   0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
389   0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
390   0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
391   0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
392   0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
393   0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
394   0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
395   0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
396   0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
397   0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
398   0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
399   0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
400   0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
401   0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
402   0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
403   0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
404   0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
405   0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
406   0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
407   0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
408   0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
409   0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
410   0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
411   0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
412   0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
413   0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
414   0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
415   0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
416 };
417 
check_authority(const uint8_t * value,size_t len)418 static int check_authority(const uint8_t *value, size_t len) {
419   const uint8_t *last;
420   for (last = value + len; value != last; ++value) {
421     if (!VALID_AUTHORITY_CHARS[*value]) {
422       return 0;
423     }
424   }
425   return 1;
426 }
427 
nghttp2_http_on_header(nghttp2_session * session,nghttp2_stream * stream,nghttp2_frame * frame,nghttp2_hd_nv * nv,int trailer)428 int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
429                            nghttp2_frame *frame, nghttp2_hd_nv *nv,
430                            int trailer) {
431   int rv;
432 
433   /* We are strict for pseudo header field.  One bad character should
434      lead to fail.  OTOH, we should be a bit forgiving for regular
435      headers, since existing public internet has so much illegal
436      headers floating around and if we kill the stream because of
437      this, we may disrupt many web sites and/or libraries.  So we
438      become conservative here, and just ignore those illegal regular
439      headers. */
440   if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
441     size_t i;
442     if (nv->name->len > 0 && nv->name->base[0] == ':') {
443       return NGHTTP2_ERR_HTTP_HEADER;
444     }
445     /* header field name must be lower-cased without exception */
446     for (i = 0; i < nv->name->len; ++i) {
447       uint8_t c = nv->name->base[i];
448       if ('A' <= c && c <= 'Z') {
449         return NGHTTP2_ERR_HTTP_HEADER;
450       }
451     }
452     /* When ignoring regular headers, we set this flag so that we
453        still enforce header field ordering rule for pseudo header
454        fields. */
455     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
456     return NGHTTP2_ERR_IGN_HTTP_HEADER;
457   }
458 
459   switch (nv->token) {
460   case NGHTTP2_TOKEN__METHOD:
461     rv = nghttp2_check_method(nv->value->base, nv->value->len);
462     break;
463   case NGHTTP2_TOKEN__PATH:
464     rv = nghttp2_check_path(nv->value->base, nv->value->len);
465     break;
466   case NGHTTP2_TOKEN__AUTHORITY:
467   case NGHTTP2_TOKEN_HOST:
468     if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
469       rv = check_authority(nv->value->base, nv->value->len);
470     } else if (
471       stream->flags &
472       NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
473       rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
474     } else {
475       rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
476     }
477     break;
478   case NGHTTP2_TOKEN__SCHEME:
479     rv = check_scheme(nv->value->base, nv->value->len);
480     break;
481   case NGHTTP2_TOKEN__PROTOCOL:
482     /* Check the value consists of just white spaces, which was done
483        in check_pseudo_header before
484        nghttp2_check_header_value_rfc9113 has been introduced. */
485     if ((stream->flags &
486          NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
487         lws(nv->value->base, nv->value->len)) {
488       rv = 0;
489       break;
490     }
491     /* fall through */
492   default:
493     if (stream->flags &
494         NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
495       rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
496     } else {
497       rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
498     }
499   }
500 
501   if (rv == 0) {
502     assert(nv->name->len > 0);
503     if (nv->name->base[0] == ':') {
504       return NGHTTP2_ERR_HTTP_HEADER;
505     }
506     /* When ignoring regular headers, we set this flag so that we
507        still enforce header field ordering rule for pseudo header
508        fields. */
509     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
510     return NGHTTP2_ERR_IGN_HTTP_HEADER;
511   }
512 
513   if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
514     return http_request_on_header(stream, nv, trailer,
515                                   session->server &&
516                                     session->pending_enable_connect_protocol);
517   }
518 
519   return http_response_on_header(stream, nv, trailer);
520 }
521 
nghttp2_http_on_request_headers(nghttp2_stream * stream,nghttp2_frame * frame)522 int nghttp2_http_on_request_headers(nghttp2_stream *stream,
523                                     nghttp2_frame *frame) {
524   if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
525       (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
526     if ((stream->http_flags &
527          (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
528         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
529       return -1;
530     }
531     stream->content_length = -1;
532   } else {
533     if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
534           NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
535         (stream->http_flags &
536          (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
537       return -1;
538     }
539     if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
540         ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
541          (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
542       return -1;
543     }
544     if (!check_path(stream)) {
545       return -1;
546     }
547   }
548 
549   if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
550     /* we are going to reuse data fields for upcoming response.  Clear
551        them now, except for method flags. */
552     stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
553     stream->content_length = -1;
554   }
555 
556   return 0;
557 }
558 
nghttp2_http_on_response_headers(nghttp2_stream * stream)559 int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
560   if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
561     return -1;
562   }
563 
564   if (stream->status_code / 100 == 1) {
565     /* non-final response */
566     stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
567                          NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
568     stream->content_length = -1;
569     stream->status_code = -1;
570     return 0;
571   }
572 
573   stream->http_flags =
574     stream->http_flags & (uint32_t)~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
575 
576   if (!expect_response_body(stream)) {
577     stream->content_length = 0;
578   } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
579                                    NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
580     stream->content_length = -1;
581   }
582 
583   return 0;
584 }
585 
nghttp2_http_on_trailer_headers(nghttp2_stream * stream,nghttp2_frame * frame)586 int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
587                                     nghttp2_frame *frame) {
588   (void)stream;
589 
590   if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
591     return -1;
592   }
593 
594   return 0;
595 }
596 
nghttp2_http_on_remote_end_stream(nghttp2_stream * stream)597 int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
598   if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
599     return -1;
600   }
601 
602   if (stream->content_length != -1 &&
603       stream->content_length != stream->recv_content_length) {
604     return -1;
605   }
606 
607   return 0;
608 }
609 
nghttp2_http_on_data_chunk(nghttp2_stream * stream,size_t n)610 int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
611   stream->recv_content_length += (int64_t)n;
612 
613   if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
614       (stream->content_length != -1 &&
615        stream->recv_content_length > stream->content_length)) {
616     return -1;
617   }
618 
619   return 0;
620 }
621 
nghttp2_http_record_request_method(nghttp2_stream * stream,nghttp2_frame * frame)622 void nghttp2_http_record_request_method(nghttp2_stream *stream,
623                                         nghttp2_frame *frame) {
624   const nghttp2_nv *nva;
625   size_t nvlen;
626   size_t i;
627 
628   switch (frame->hd.type) {
629   case NGHTTP2_HEADERS:
630     nva = frame->headers.nva;
631     nvlen = frame->headers.nvlen;
632     break;
633   case NGHTTP2_PUSH_PROMISE:
634     nva = frame->push_promise.nva;
635     nvlen = frame->push_promise.nvlen;
636     break;
637   default:
638     return;
639   }
640 
641   /* TODO we should do this strictly. */
642   for (i = 0; i < nvlen; ++i) {
643     const nghttp2_nv *nv = &nva[i];
644     if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
645           memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
646       continue;
647     }
648     if (lstreq("CONNECT", nv->value, nv->valuelen)) {
649       stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
650       return;
651     }
652     if (lstreq("HEAD", nv->value, nv->valuelen)) {
653       stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
654       return;
655     }
656     return;
657   }
658 }
659 
nghttp2_http_parse_priority(nghttp2_extpri * dest,const uint8_t * value,size_t valuelen)660 int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
661                                 size_t valuelen) {
662   nghttp2_extpri pri = *dest;
663   sf_parser sfp;
664   sf_vec key;
665   sf_value val;
666   int rv;
667 
668   sf_parser_init(&sfp, value, valuelen);
669 
670   for (;;) {
671     rv = sf_parser_dict(&sfp, &key, &val);
672     if (rv != 0) {
673       if (rv == SF_ERR_EOF) {
674         break;
675       }
676 
677       return NGHTTP2_ERR_INVALID_ARGUMENT;
678     }
679 
680     if (key.len != 1) {
681       continue;
682     }
683 
684     switch (key.base[0]) {
685     case 'i':
686       if (val.type != SF_TYPE_BOOLEAN) {
687         return NGHTTP2_ERR_INVALID_ARGUMENT;
688       }
689 
690       pri.inc = val.boolean;
691 
692       break;
693     case 'u':
694       if (val.type != SF_TYPE_INTEGER ||
695           val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
696           NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
697         return NGHTTP2_ERR_INVALID_ARGUMENT;
698       }
699 
700       pri.urgency = (uint32_t)val.integer;
701 
702       break;
703     }
704   }
705 
706   *dest = pri;
707 
708   return 0;
709 }
710