Upgrade to 1.46.0
[platform/upstream/nghttp2.git] / lib / nghttp2_http.c
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
34 static uint8_t downcase(uint8_t c) {
35   return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
36 }
37
38 static int memieq(const void *a, const void *b, size_t n) {
39   size_t i;
40   const uint8_t *aa = a, *bb = b;
41
42   for (i = 0; i < n; ++i) {
43     if (downcase(aa[i]) != downcase(bb[i])) {
44       return 0;
45     }
46   }
47   return 1;
48 }
49
50 #define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
51
52 static int64_t parse_uint(const uint8_t *s, size_t len) {
53   int64_t n = 0;
54   size_t i;
55   if (len == 0) {
56     return -1;
57   }
58   for (i = 0; i < len; ++i) {
59     if ('0' <= s[i] && s[i] <= '9') {
60       if (n > INT64_MAX / 10) {
61         return -1;
62       }
63       n *= 10;
64       if (n > INT64_MAX - (s[i] - '0')) {
65         return -1;
66       }
67       n += s[i] - '0';
68       continue;
69     }
70     return -1;
71   }
72   return n;
73 }
74
75 static int lws(const uint8_t *s, size_t n) {
76   size_t i;
77   for (i = 0; i < n; ++i) {
78     if (s[i] != ' ' && s[i] != '\t') {
79       return 0;
80     }
81   }
82   return 1;
83 }
84
85 static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
86                                int flag) {
87   if (stream->http_flags & flag) {
88     return 0;
89   }
90   if (lws(nv->value->base, nv->value->len)) {
91     return 0;
92   }
93   stream->http_flags = (uint16_t)(stream->http_flags | flag);
94   return 1;
95 }
96
97 static int expect_response_body(nghttp2_stream *stream) {
98   return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
99          stream->status_code / 100 != 1 && stream->status_code != 304 &&
100          stream->status_code != 204;
101 }
102
103 /* For "http" or "https" URIs, OPTIONS request may have "*" in :path
104    header field to represent system-wide OPTIONS request.  Otherwise,
105    :path header field value must start with "/".  This function must
106    be called after ":method" header field was received.  This function
107    returns nonzero if path is valid.*/
108 static int check_path(nghttp2_stream *stream) {
109   return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
110          ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
111           ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
112            (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
113 }
114
115 static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
116                                   int trailer, int connect_protocol) {
117   if (nv->name->base[0] == ':') {
118     if (trailer ||
119         (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
120       return NGHTTP2_ERR_HTTP_HEADER;
121     }
122   }
123
124   switch (nv->token) {
125   case NGHTTP2_TOKEN__AUTHORITY:
126     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
127       return NGHTTP2_ERR_HTTP_HEADER;
128     }
129     break;
130   case NGHTTP2_TOKEN__METHOD:
131     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
132       return NGHTTP2_ERR_HTTP_HEADER;
133     }
134     switch (nv->value->len) {
135     case 4:
136       if (lstreq("HEAD", nv->value->base, nv->value->len)) {
137         stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
138       }
139       break;
140     case 7:
141       switch (nv->value->base[6]) {
142       case 'T':
143         if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
144           if (stream->stream_id % 2 == 0) {
145             /* we won't allow CONNECT for push */
146             return NGHTTP2_ERR_HTTP_HEADER;
147           }
148           stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
149         }
150         break;
151       case 'S':
152         if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
153           stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
154         }
155         break;
156       }
157       break;
158     }
159     break;
160   case NGHTTP2_TOKEN__PATH:
161     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
162       return NGHTTP2_ERR_HTTP_HEADER;
163     }
164     if (nv->value->base[0] == '/') {
165       stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
166     } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
167       stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
168     }
169     break;
170   case NGHTTP2_TOKEN__SCHEME:
171     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
172       return NGHTTP2_ERR_HTTP_HEADER;
173     }
174     if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
175         (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
176       stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
177     }
178     break;
179   case NGHTTP2_TOKEN__PROTOCOL:
180     if (!connect_protocol) {
181       return NGHTTP2_ERR_HTTP_HEADER;
182     }
183
184     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
185       return NGHTTP2_ERR_HTTP_HEADER;
186     }
187     break;
188   case NGHTTP2_TOKEN_HOST:
189     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
190       return NGHTTP2_ERR_HTTP_HEADER;
191     }
192     break;
193   case NGHTTP2_TOKEN_CONTENT_LENGTH: {
194     if (stream->content_length != -1) {
195       return NGHTTP2_ERR_HTTP_HEADER;
196     }
197     stream->content_length = parse_uint(nv->value->base, nv->value->len);
198     if (stream->content_length == -1) {
199       return NGHTTP2_ERR_HTTP_HEADER;
200     }
201     break;
202   }
203   /* disallowed header fields */
204   case NGHTTP2_TOKEN_CONNECTION:
205   case NGHTTP2_TOKEN_KEEP_ALIVE:
206   case NGHTTP2_TOKEN_PROXY_CONNECTION:
207   case NGHTTP2_TOKEN_TRANSFER_ENCODING:
208   case NGHTTP2_TOKEN_UPGRADE:
209     return NGHTTP2_ERR_HTTP_HEADER;
210   case NGHTTP2_TOKEN_TE:
211     if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
212       return NGHTTP2_ERR_HTTP_HEADER;
213     }
214     break;
215   default:
216     if (nv->name->base[0] == ':') {
217       return NGHTTP2_ERR_HTTP_HEADER;
218     }
219   }
220
221   if (nv->name->base[0] != ':') {
222     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
223   }
224
225   return 0;
226 }
227
228 static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
229                                    int trailer) {
230   if (nv->name->base[0] == ':') {
231     if (trailer ||
232         (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
233       return NGHTTP2_ERR_HTTP_HEADER;
234     }
235   }
236
237   switch (nv->token) {
238   case NGHTTP2_TOKEN__STATUS: {
239     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
240       return NGHTTP2_ERR_HTTP_HEADER;
241     }
242     if (nv->value->len != 3) {
243       return NGHTTP2_ERR_HTTP_HEADER;
244     }
245     stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
246     if (stream->status_code == -1 || stream->status_code == 101) {
247       return NGHTTP2_ERR_HTTP_HEADER;
248     }
249     break;
250   }
251   case NGHTTP2_TOKEN_CONTENT_LENGTH: {
252     if (stream->status_code == 204) {
253       /* content-length header field in 204 response is prohibited by
254          RFC 7230.  But some widely used servers send content-length:
255          0.  Until they get fixed, we ignore it. */
256       if (stream->content_length != -1) {
257         /* Found multiple content-length field */
258         return NGHTTP2_ERR_HTTP_HEADER;
259       }
260       if (!lstrieq("0", nv->value->base, nv->value->len)) {
261         return NGHTTP2_ERR_HTTP_HEADER;
262       }
263       stream->content_length = 0;
264       return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
265     }
266     if (stream->status_code / 100 == 1) {
267       return NGHTTP2_ERR_HTTP_HEADER;
268     }
269     /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
270     if (stream->status_code / 100 == 2 &&
271         (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
272       return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
273     }
274     if (stream->content_length != -1) {
275       return NGHTTP2_ERR_HTTP_HEADER;
276     }
277     stream->content_length = parse_uint(nv->value->base, nv->value->len);
278     if (stream->content_length == -1) {
279       return NGHTTP2_ERR_HTTP_HEADER;
280     }
281     break;
282   }
283   /* disallowed header fields */
284   case NGHTTP2_TOKEN_CONNECTION:
285   case NGHTTP2_TOKEN_KEEP_ALIVE:
286   case NGHTTP2_TOKEN_PROXY_CONNECTION:
287   case NGHTTP2_TOKEN_TRANSFER_ENCODING:
288   case NGHTTP2_TOKEN_UPGRADE:
289     return NGHTTP2_ERR_HTTP_HEADER;
290   case NGHTTP2_TOKEN_TE:
291     if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
292       return NGHTTP2_ERR_HTTP_HEADER;
293     }
294     break;
295   default:
296     if (nv->name->base[0] == ':') {
297       return NGHTTP2_ERR_HTTP_HEADER;
298     }
299   }
300
301   if (nv->name->base[0] != ':') {
302     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
303   }
304
305   return 0;
306 }
307
308 static int check_scheme(const uint8_t *value, size_t len) {
309   const uint8_t *last;
310   if (len == 0) {
311     return 0;
312   }
313
314   if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
315     return 0;
316   }
317
318   last = value + len;
319   ++value;
320
321   for (; value != last; ++value) {
322     if (!(('A' <= *value && *value <= 'Z') ||
323           ('a' <= *value && *value <= 'z') ||
324           ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
325           *value == '.')) {
326       return 0;
327     }
328   }
329   return 1;
330 }
331
332 int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
333                            nghttp2_frame *frame, nghttp2_hd_nv *nv,
334                            int trailer) {
335   int rv;
336
337   /* We are strict for pseudo header field.  One bad character should
338      lead to fail.  OTOH, we should be a bit forgiving for regular
339      headers, since existing public internet has so much illegal
340      headers floating around and if we kill the stream because of
341      this, we may disrupt many web sites and/or libraries.  So we
342      become conservative here, and just ignore those illegal regular
343      headers. */
344   if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
345     size_t i;
346     if (nv->name->len > 0 && nv->name->base[0] == ':') {
347       return NGHTTP2_ERR_HTTP_HEADER;
348     }
349     /* header field name must be lower-cased without exception */
350     for (i = 0; i < nv->name->len; ++i) {
351       uint8_t c = nv->name->base[i];
352       if ('A' <= c && c <= 'Z') {
353         return NGHTTP2_ERR_HTTP_HEADER;
354       }
355     }
356     /* When ignoring regular headers, we set this flag so that we
357        still enforce header field ordering rule for pseudo header
358        fields. */
359     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
360     return NGHTTP2_ERR_IGN_HTTP_HEADER;
361   }
362
363   switch (nv->token) {
364   case NGHTTP2_TOKEN__METHOD:
365     rv = nghttp2_check_method(nv->value->base, nv->value->len);
366     break;
367   case NGHTTP2_TOKEN__PATH:
368     rv = nghttp2_check_path(nv->value->base, nv->value->len);
369     break;
370   case NGHTTP2_TOKEN__AUTHORITY:
371   case NGHTTP2_TOKEN_HOST:
372     rv = nghttp2_check_authority(nv->value->base, nv->value->len);
373     break;
374   case NGHTTP2_TOKEN__SCHEME:
375     rv = check_scheme(nv->value->base, nv->value->len);
376     break;
377   default:
378     rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
379   }
380
381   if (rv == 0) {
382     assert(nv->name->len > 0);
383     if (nv->name->base[0] == ':') {
384       return NGHTTP2_ERR_HTTP_HEADER;
385     }
386     /* When ignoring regular headers, we set this flag so that we
387        still enforce header field ordering rule for pseudo header
388        fields. */
389     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
390     return NGHTTP2_ERR_IGN_HTTP_HEADER;
391   }
392
393   if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
394     return http_request_on_header(stream, nv, trailer,
395                                   session->server &&
396                                       session->pending_enable_connect_protocol);
397   }
398
399   return http_response_on_header(stream, nv, trailer);
400 }
401
402 int nghttp2_http_on_request_headers(nghttp2_stream *stream,
403                                     nghttp2_frame *frame) {
404   if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
405       (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
406     if ((stream->http_flags &
407          (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
408         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
409       return -1;
410     }
411     stream->content_length = -1;
412   } else {
413     if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
414             NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
415         (stream->http_flags &
416          (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
417       return -1;
418     }
419     if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
420         ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
421          (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
422       return -1;
423     }
424     if (!check_path(stream)) {
425       return -1;
426     }
427   }
428
429   if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
430     /* we are going to reuse data fields for upcoming response.  Clear
431        them now, except for method flags. */
432     stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
433     stream->content_length = -1;
434   }
435
436   return 0;
437 }
438
439 int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
440   if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
441     return -1;
442   }
443
444   if (stream->status_code / 100 == 1) {
445     /* non-final response */
446     stream->http_flags =
447         (uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
448                    NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
449     stream->content_length = -1;
450     stream->status_code = -1;
451     return 0;
452   }
453
454   stream->http_flags =
455       (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
456
457   if (!expect_response_body(stream)) {
458     stream->content_length = 0;
459   } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
460                                    NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
461     stream->content_length = -1;
462   }
463
464   return 0;
465 }
466
467 int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
468                                     nghttp2_frame *frame) {
469   (void)stream;
470
471   if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
472     return -1;
473   }
474
475   return 0;
476 }
477
478 int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
479   if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
480     return -1;
481   }
482
483   if (stream->content_length != -1 &&
484       stream->content_length != stream->recv_content_length) {
485     return -1;
486   }
487
488   return 0;
489 }
490
491 int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
492   stream->recv_content_length += (int64_t)n;
493
494   if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
495       (stream->content_length != -1 &&
496        stream->recv_content_length > stream->content_length)) {
497     return -1;
498   }
499
500   return 0;
501 }
502
503 void nghttp2_http_record_request_method(nghttp2_stream *stream,
504                                         nghttp2_frame *frame) {
505   const nghttp2_nv *nva;
506   size_t nvlen;
507   size_t i;
508
509   switch (frame->hd.type) {
510   case NGHTTP2_HEADERS:
511     nva = frame->headers.nva;
512     nvlen = frame->headers.nvlen;
513     break;
514   case NGHTTP2_PUSH_PROMISE:
515     nva = frame->push_promise.nva;
516     nvlen = frame->push_promise.nvlen;
517     break;
518   default:
519     return;
520   }
521
522   /* TODO we should do this strictly. */
523   for (i = 0; i < nvlen; ++i) {
524     const nghttp2_nv *nv = &nva[i];
525     if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
526           memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
527       continue;
528     }
529     if (lstreq("CONNECT", nv->value, nv->valuelen)) {
530       stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
531       return;
532     }
533     if (lstreq("HEAD", nv->value, nv->valuelen)) {
534       stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
535       return;
536     }
537     return;
538   }
539 }