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 
downcase(uint8_t c)34 static uint8_t downcase(uint8_t c)
35 {
36     return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
37 }
38 
memieq(const void * a,const void * b,size_t n)39 static int memieq(const void *a, const void *b, size_t n)
40 {
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 {
56     int64_t n = 0;
57     size_t i;
58     if (len == 0) {
59         return -1;
60     }
61     for (i = 0; i < len; ++i) {
62         if ('0' <= s[i] && s[i] <= '9') {
63             if (n > INT64_MAX / 10) {
64                 return -1;
65             }
66             n *= 10;
67             if (n > INT64_MAX - (s[i] - '0')) {
68                 return -1;
69             }
70             n += s[i] - '0';
71             continue;
72         }
73         return -1;
74     }
75     return n;
76 }
77 
lws(const uint8_t * s,size_t n)78 static int lws(const uint8_t *s, size_t n)
79 {
80     size_t i;
81     for (i = 0; i < n; ++i) {
82         if (s[i] != ' ' && s[i] != '\t') {
83             return 0;
84         }
85     }
86     return 1;
87 }
88 
check_pseudo_header(nghttp2_stream * stream,const nghttp2_hd_nv * nv,int flag)89 static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
90                                int flag)
91 {
92     if (stream->http_flags & flag) {
93         return 0;
94     }
95     if (lws(nv->value->base, nv->value->len)) {
96         return 0;
97     }
98     stream->http_flags = (uint16_t)(stream->http_flags | flag);
99     return 1;
100 }
101 
expect_response_body(nghttp2_stream * stream)102 static int expect_response_body(nghttp2_stream *stream)
103 {
104     return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
105            stream->status_code / 100 != 1 && stream->status_code != 304 &&
106            stream->status_code != 204;
107 }
108 
109 /* For "http" or "https" URIs, OPTIONS request may have "*" in :path
110    header field to represent system-wide OPTIONS request.  Otherwise,
111    :path header field value must start with "/".  This function must
112    be called after ":method" header field was received.  This function
113    returns nonzero if path is valid.*/
check_path(nghttp2_stream * stream)114 static int check_path(nghttp2_stream *stream)
115 {
116     return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
117            ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
118             ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
119              (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
120 }
121 
http_request_on_header(nghttp2_stream * stream,nghttp2_hd_nv * nv,int trailer)122 static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
123                                   int trailer)
124 {
125     if (nv->name->base[0] == ':') {
126         if (trailer ||
127             (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
128             return NGHTTP2_ERR_HTTP_HEADER;
129         }
130     }
131 
132     switch (nv->token) {
133     case NGHTTP2_TOKEN__AUTHORITY:
134         if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
135             return NGHTTP2_ERR_HTTP_HEADER;
136         }
137         break;
138     case NGHTTP2_TOKEN__METHOD:
139         if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
140             return NGHTTP2_ERR_HTTP_HEADER;
141         }
142         switch (nv->value->len) {
143         case 4:
144             if (lstreq("HEAD", nv->value->base, nv->value->len)) {
145                 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
146             }
147             break;
148         case 7:
149             switch (nv->value->base[6]) {
150             case 'T':
151                 if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
152                     if (stream->stream_id % 2 == 0) {
153                         /* we won't allow CONNECT for push */
154                         return NGHTTP2_ERR_HTTP_HEADER;
155                     }
156                     stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
157                     if (stream->http_flags &
158                         (NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
159                         return NGHTTP2_ERR_HTTP_HEADER;
160                     }
161                 }
162                 break;
163             case 'S':
164                 if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
165                     stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
166                 }
167                 break;
168             }
169             break;
170         }
171         break;
172     case NGHTTP2_TOKEN__PATH:
173         if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
174             return NGHTTP2_ERR_HTTP_HEADER;
175         }
176         if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
177             return NGHTTP2_ERR_HTTP_HEADER;
178         }
179         if (nv->value->base[0] == '/') {
180             stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
181         } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
182             stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
183         }
184         break;
185     case NGHTTP2_TOKEN__SCHEME:
186         if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
187             return NGHTTP2_ERR_HTTP_HEADER;
188         }
189         if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
190             return NGHTTP2_ERR_HTTP_HEADER;
191         }
192         if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
193             (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
194             stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
195         }
196         break;
197     case NGHTTP2_TOKEN_HOST:
198         if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
199             return NGHTTP2_ERR_HTTP_HEADER;
200         }
201         break;
202     case NGHTTP2_TOKEN_CONTENT_LENGTH:
203         {
204             if (stream->content_length != -1) {
205                 return NGHTTP2_ERR_HTTP_HEADER;
206             }
207             stream->content_length =
208                 parse_uint(nv->value->base, nv->value->len);
209             if (stream->content_length == -1) {
210                 return NGHTTP2_ERR_HTTP_HEADER;
211             }
212             break;
213         }
214     /* disallowed header fields */
215     case NGHTTP2_TOKEN_CONNECTION:
216     case NGHTTP2_TOKEN_KEEP_ALIVE:
217     case NGHTTP2_TOKEN_PROXY_CONNECTION:
218     case NGHTTP2_TOKEN_TRANSFER_ENCODING:
219     case NGHTTP2_TOKEN_UPGRADE:
220         return NGHTTP2_ERR_HTTP_HEADER;
221     case NGHTTP2_TOKEN_TE:
222         if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
223             return NGHTTP2_ERR_HTTP_HEADER;
224         }
225         break;
226     default:
227         if (nv->name->base[0] == ':') {
228             return NGHTTP2_ERR_HTTP_HEADER;
229         }
230     }
231 
232     if (nv->name->base[0] != ':') {
233         stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
234     }
235 
236     return 0;
237 }
238 
http_response_on_header(nghttp2_stream * stream,nghttp2_hd_nv * nv,int trailer)239 static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
240                                    int trailer)
241 {
242     if (nv->name->base[0] == ':') {
243         if (trailer ||
244             (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
245             return NGHTTP2_ERR_HTTP_HEADER;
246         }
247     }
248 
249     switch (nv->token) {
250     case NGHTTP2_TOKEN__STATUS:
251         {
252             if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
253                 return NGHTTP2_ERR_HTTP_HEADER;
254             }
255             if (nv->value->len != 3) {
256                 return NGHTTP2_ERR_HTTP_HEADER;
257             }
258             stream->status_code =
259                 (int16_t)parse_uint(nv->value->base, nv->value->len);
260             if (stream->status_code == -1 || stream->status_code == 101) {
261                 return NGHTTP2_ERR_HTTP_HEADER;
262             }
263             break;
264         }
265     case NGHTTP2_TOKEN_CONTENT_LENGTH:
266         {
267             if (stream->status_code == 204) {
268                 /* content-length header field in 204 response is prohibited by
269                    RFC 7230.  But some widely used servers send content-length:
270                    0.  Until they get fixed, we ignore it. */
271                 if (stream->content_length != -1) {
272                     /* Found multiple content-length field */
273                     return NGHTTP2_ERR_HTTP_HEADER;
274                 }
275                 if (!lstrieq("0", nv->value->base, nv->value->len)) {
276                     return NGHTTP2_ERR_HTTP_HEADER;
277                 }
278                 stream->content_length = 0;
279                 return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
280             }
281             if (stream->status_code / 100 == 1 ||
282                 (stream->status_code == 200 &&
283                  (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT))) {
284                 return NGHTTP2_ERR_HTTP_HEADER;
285             }
286             if (stream->content_length != -1) {
287                 return NGHTTP2_ERR_HTTP_HEADER;
288             }
289             stream->content_length =
290                 parse_uint(nv->value->base, nv->value->len);
291             if (stream->content_length == -1) {
292                 return NGHTTP2_ERR_HTTP_HEADER;
293             }
294             break;
295         }
296     /* disallowed header fields */
297     case NGHTTP2_TOKEN_CONNECTION:
298     case NGHTTP2_TOKEN_KEEP_ALIVE:
299     case NGHTTP2_TOKEN_PROXY_CONNECTION:
300     case NGHTTP2_TOKEN_TRANSFER_ENCODING:
301     case NGHTTP2_TOKEN_UPGRADE:
302         return NGHTTP2_ERR_HTTP_HEADER;
303     case NGHTTP2_TOKEN_TE:
304         if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
305             return NGHTTP2_ERR_HTTP_HEADER;
306         }
307         break;
308     default:
309         if (nv->name->base[0] == ':') {
310             return NGHTTP2_ERR_HTTP_HEADER;
311         }
312     }
313 
314     if (nv->name->base[0] != ':') {
315         stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
316     }
317 
318     return 0;
319 }
320 
321 /* Generated by genauthroitychartbl.py */
322 static char VALID_AUTHORITY_CHARS[] = {
323     0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
324     0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
325     0 /* BS   */, 0 /* HT   */, 0 /* LF   */, 0 /* VT   */,
326     0 /* FF   */, 0 /* CR   */, 0 /* SO   */, 0 /* SI   */,
327     0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
328     0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */,
329     0 /* CAN  */, 0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */,
330     0 /* FS   */, 0 /* GS   */, 0 /* RS   */, 0 /* US   */,
331     0 /* SPC  */, 1 /* !    */, 0 /* "    */, 0 /* #    */,
332     1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
333     1 /* (    */, 1 /* )    */, 1 /* *    */, 1 /* +    */,
334     1 /* ,    */, 1 /* -    */, 1 /* .    */, 0 /* /    */,
335     1 /* 0    */, 1 /* 1    */, 1 /* 2    */, 1 /* 3    */,
336     1 /* 4    */, 1 /* 5    */, 1 /* 6    */, 1 /* 7    */,
337     1 /* 8    */, 1 /* 9    */, 1 /* :    */, 1 /* ;    */,
338     0 /* <    */, 1 /* =    */, 0 /* >    */, 0 /* ?    */,
339     1 /* @    */, 1 /* A    */, 1 /* B    */, 1 /* C    */,
340     1 /* D    */, 1 /* E    */, 1 /* F    */, 1 /* G    */,
341     1 /* H    */, 1 /* I    */, 1 /* J    */, 1 /* K    */,
342     1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,
343     1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */,
344     1 /* T    */, 1 /* U    */, 1 /* V    */, 1 /* W    */,
345     1 /* X    */, 1 /* Y    */, 1 /* Z    */, 1 /* [    */,
346     0 /* \    */, 1 /* ]    */, 0 /* ^    */, 1 /* _    */,
347     0 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
348     1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */,
349     1 /* h    */, 1 /* i    */, 1 /* j    */, 1 /* k    */,
350     1 /* l    */, 1 /* m    */, 1 /* n    */, 1 /* o    */,
351     1 /* p    */, 1 /* q    */, 1 /* r    */, 1 /* s    */,
352     1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
353     1 /* x    */, 1 /* y    */, 1 /* z    */, 0 /* {    */,
354     0 /* |    */, 0 /* }    */, 1 /* ~    */, 0 /* DEL  */,
355     0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
356     0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
357     0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
358     0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
359     0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
360     0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
361     0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
362     0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
363     0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
364     0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
365     0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
366     0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
367     0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
368     0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
369     0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
370     0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
371     0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
372     0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
373     0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
374     0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
375     0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
376     0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
377     0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
378     0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
379     0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
380     0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
381     0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
382     0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
383     0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
384     0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
385     0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
386     0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
387 };
388 
check_authority(const uint8_t * value,size_t len)389 static int check_authority(const uint8_t *value, size_t len)
390 {
391     const uint8_t *last;
392     for (last = value + len; value != last; ++value) {
393         if (!VALID_AUTHORITY_CHARS[*value]) {
394             return 0;
395         }
396     }
397     return 1;
398 }
399 
check_scheme(const uint8_t * value,size_t len)400 static int check_scheme(const uint8_t *value, size_t len)
401 {
402     const uint8_t *last;
403     if (len == 0) {
404         return 0;
405     }
406 
407     if (!(('A' <= *value && *value <= 'Z') ||
408           ('a' <= *value && *value <= 'z'))) {
409         return 0;
410     }
411 
412     last = value + len;
413     ++value;
414 
415     for (; value != last; ++value) {
416         if (!(('A' <= *value && *value <= 'Z') ||
417               ('a' <= *value && *value <= 'z') ||
418               ('0' <= *value && *value <= '9') || *value == '+' ||
419               *value == '-' || *value == '.')) {
420             return 0;
421         }
422     }
423     return 1;
424 }
425 
nghttp2_http_on_header(nghttp2_session * session,nghttp2_stream * stream,nghttp2_frame * frame,nghttp2_hd_nv * nv,int trailer)426 int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
427                            nghttp2_frame *frame, nghttp2_hd_nv *nv, int trailer)
428 {
429     int rv;
430 
431     /* We are strict for pseudo header field.  One bad character should
432        lead to fail.  OTOH, we should be a bit forgiving for regular
433        headers, since existing public internet has so much illegal
434        headers floating around and if we kill the stream because of
435        this, we may disrupt many web sites and/or libraries.  So we
436        become conservative here, and just ignore those illegal regular
437        headers. */
438     if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
439         size_t i;
440         if (nv->name->len > 0 && nv->name->base[0] == ':') {
441             return NGHTTP2_ERR_HTTP_HEADER;
442         }
443         /* header field name must be lower-cased without exception */
444         for (i = 0; i < nv->name->len; ++i) {
445             uint8_t c = nv->name->base[i];
446             if ('A' <= c && c <= 'Z') {
447                 return NGHTTP2_ERR_HTTP_HEADER;
448             }
449         }
450         /* When ignoring regular headers, we set this flag so that we
451            still enforce header field ordering rule for pseudo header
452            fields. */
453         stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
454         return NGHTTP2_ERR_IGN_HTTP_HEADER;
455     }
456 
457     if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
458         nv->token == NGHTTP2_TOKEN_HOST) {
459         rv = check_authority(nv->value->base, nv->value->len);
460     } else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
461         rv = check_scheme(nv->value->base, nv->value->len);
462     } else {
463         rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
464     }
465 
466     if (rv == 0) {
467         assert(nv->name->len > 0);
468         if (nv->name->base[0] == ':') {
469             return NGHTTP2_ERR_HTTP_HEADER;
470         }
471         /* When ignoring regular headers, we set this flag so that we
472            still enforce header field ordering rule for pseudo header
473            fields. */
474         stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
475         return NGHTTP2_ERR_IGN_HTTP_HEADER;
476     }
477 
478     if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
479         return http_request_on_header(stream, nv, trailer);
480     }
481 
482     return http_response_on_header(stream, nv, trailer);
483 }
484 
nghttp2_http_on_request_headers(nghttp2_stream * stream,nghttp2_frame * frame)485 int nghttp2_http_on_request_headers(nghttp2_stream *stream,
486                                     nghttp2_frame *frame)
487 {
488     if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
489         if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
490             return -1;
491         }
492         stream->content_length = -1;
493     } else {
494         if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
495                 NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
496             (stream->http_flags &
497              (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
498             return -1;
499         }
500         if (!check_path(stream)) {
501             return -1;
502         }
503     }
504 
505     if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
506         /* we are going to reuse data fields for upcoming response.  Clear
507            them now, except for method flags. */
508         stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
509         stream->content_length = -1;
510     }
511 
512     return 0;
513 }
514 
nghttp2_http_on_response_headers(nghttp2_stream * stream)515 int nghttp2_http_on_response_headers(nghttp2_stream *stream)
516 {
517     if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
518         return -1;
519     }
520 
521     if (stream->status_code / 100 == 1) {
522         /* non-final response */
523         stream->http_flags =
524             (uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
525                        NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
526         stream->content_length = -1;
527         stream->status_code = -1;
528         return 0;
529     }
530 
531     stream->http_flags = (uint16_t)(stream->http_flags &
532                                     ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
533 
534     if (!expect_response_body(stream)) {
535         stream->content_length = 0;
536     } else if (stream->http_flags &
537                (NGHTTP2_HTTP_FLAG_METH_CONNECT |
538                 NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
539         stream->content_length = -1;
540     }
541 
542     return 0;
543 }
544 
nghttp2_http_on_trailer_headers(nghttp2_stream * stream,nghttp2_frame * frame)545 int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
546                                     nghttp2_frame *frame)
547 {
548     (void)stream;
549 
550     if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
551         return -1;
552     }
553 
554     return 0;
555 }
556 
nghttp2_http_on_remote_end_stream(nghttp2_stream * stream)557 int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream)
558 {
559     if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
560         return -1;
561     }
562 
563     if (stream->content_length != -1 &&
564         stream->content_length != stream->recv_content_length) {
565         return -1;
566     }
567 
568     return 0;
569 }
570 
nghttp2_http_on_data_chunk(nghttp2_stream * stream,size_t n)571 int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n)
572 {
573     stream->recv_content_length += (int64_t)n;
574 
575     if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
576         (stream->content_length != -1 &&
577          stream->recv_content_length > stream->content_length)) {
578         return -1;
579     }
580 
581     return 0;
582 }
583 
nghttp2_http_record_request_method(nghttp2_stream * stream,nghttp2_frame * frame)584 void nghttp2_http_record_request_method(nghttp2_stream *stream,
585                                         nghttp2_frame *frame)
586 {
587     const nghttp2_nv *nva;
588     size_t nvlen;
589     size_t i;
590 
591     switch (frame->hd.type) {
592     case NGHTTP2_HEADERS:
593         nva = frame->headers.nva;
594         nvlen = frame->headers.nvlen;
595         break;
596     case NGHTTP2_PUSH_PROMISE:
597         nva = frame->push_promise.nva;
598         nvlen = frame->push_promise.nvlen;
599         break;
600     default:
601         return;
602     }
603 
604     /* TODO we should do this strictly. */
605     for (i = 0; i < nvlen; ++i) {
606         const nghttp2_nv *nv = &nva[i];
607         if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
608               memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
609             continue;
610         }
611         if (lstreq("CONNECT", nv->value, nv->valuelen)) {
612             stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
613             return;
614         }
615         if (lstreq("HEAD", nv->value, nv->valuelen)) {
616             stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
617             return;
618         }
619         return;
620     }
621 }
622