Imported Upstream version 1.0.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 char downcase(char c) {
35   return 'A' <= c && c <= 'Z' ? (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_nv *nv,
86                                int flag) {
87   if (stream->http_flags & flag) {
88     return 0;
89   }
90   if (lws(nv->value, nv->valuelen)) {
91     return 0;
92   }
93   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_nv *nv,
116                                   int token, int trailer) {
117   if (nv->name[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 (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->valuelen) {
135     case 4:
136       if (lstreq("HEAD", nv->value, nv->valuelen)) {
137         stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
138       }
139       break;
140     case 7:
141       switch (nv->value[6]) {
142       case 'T':
143         if (lstreq("CONNECT", nv->value, nv->valuelen)) {
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           if (stream->http_flags &
150               (NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
151             return NGHTTP2_ERR_HTTP_HEADER;
152           }
153         }
154         break;
155       case 'S':
156         if (lstreq("OPTIONS", nv->value, nv->valuelen)) {
157           stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
158         }
159         break;
160       }
161       break;
162     }
163     break;
164   case NGHTTP2_TOKEN__PATH:
165     if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
166       return NGHTTP2_ERR_HTTP_HEADER;
167     }
168     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
169       return NGHTTP2_ERR_HTTP_HEADER;
170     }
171     if (nv->value[0] == '/') {
172       stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
173     } else if (nv->valuelen == 1 && nv->value[0] == '*') {
174       stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
175     }
176     break;
177   case NGHTTP2_TOKEN__SCHEME:
178     if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
179       return NGHTTP2_ERR_HTTP_HEADER;
180     }
181     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
182       return NGHTTP2_ERR_HTTP_HEADER;
183     }
184     if ((nv->valuelen == 4 && memieq("http", nv->value, 4)) ||
185         (nv->valuelen == 5 && memieq("https", nv->value, 5))) {
186       stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
187     }
188     break;
189   case NGHTTP2_TOKEN_HOST:
190     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
191       return NGHTTP2_ERR_HTTP_HEADER;
192     }
193     break;
194   case NGHTTP2_TOKEN_CONTENT_LENGTH: {
195     if (stream->content_length != -1) {
196       return NGHTTP2_ERR_HTTP_HEADER;
197     }
198     stream->content_length = parse_uint(nv->value, nv->valuelen);
199     if (stream->content_length == -1) {
200       return NGHTTP2_ERR_HTTP_HEADER;
201     }
202     break;
203   }
204   /* disallowed header fields */
205   case NGHTTP2_TOKEN_CONNECTION:
206   case NGHTTP2_TOKEN_KEEP_ALIVE:
207   case NGHTTP2_TOKEN_PROXY_CONNECTION:
208   case NGHTTP2_TOKEN_TRANSFER_ENCODING:
209   case NGHTTP2_TOKEN_UPGRADE:
210     return NGHTTP2_ERR_HTTP_HEADER;
211   case NGHTTP2_TOKEN_TE:
212     if (!lstrieq("trailers", nv->value, nv->valuelen)) {
213       return NGHTTP2_ERR_HTTP_HEADER;
214     }
215     break;
216   default:
217     if (nv->name[0] == ':') {
218       return NGHTTP2_ERR_HTTP_HEADER;
219     }
220   }
221
222   if (nv->name[0] != ':') {
223     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
224   }
225
226   return 0;
227 }
228
229 static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
230                                    int token, int trailer) {
231   if (nv->name[0] == ':') {
232     if (trailer ||
233         (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
234       return NGHTTP2_ERR_HTTP_HEADER;
235     }
236   }
237
238   switch (token) {
239   case NGHTTP2_TOKEN__STATUS: {
240     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
241       return NGHTTP2_ERR_HTTP_HEADER;
242     }
243     if (nv->valuelen != 3) {
244       return NGHTTP2_ERR_HTTP_HEADER;
245     }
246     stream->status_code = parse_uint(nv->value, nv->valuelen);
247     if (stream->status_code == -1) {
248       return NGHTTP2_ERR_HTTP_HEADER;
249     }
250     break;
251   }
252   case NGHTTP2_TOKEN_CONTENT_LENGTH: {
253     if (stream->content_length != -1) {
254       return NGHTTP2_ERR_HTTP_HEADER;
255     }
256     stream->content_length = parse_uint(nv->value, nv->valuelen);
257     if (stream->content_length == -1) {
258       return NGHTTP2_ERR_HTTP_HEADER;
259     }
260     break;
261   }
262   /* disallowed header fields */
263   case NGHTTP2_TOKEN_CONNECTION:
264   case NGHTTP2_TOKEN_KEEP_ALIVE:
265   case NGHTTP2_TOKEN_PROXY_CONNECTION:
266   case NGHTTP2_TOKEN_TRANSFER_ENCODING:
267   case NGHTTP2_TOKEN_UPGRADE:
268     return NGHTTP2_ERR_HTTP_HEADER;
269   case NGHTTP2_TOKEN_TE:
270     if (!lstrieq("trailers", nv->value, nv->valuelen)) {
271       return NGHTTP2_ERR_HTTP_HEADER;
272     }
273     break;
274   default:
275     if (nv->name[0] == ':') {
276       return NGHTTP2_ERR_HTTP_HEADER;
277     }
278   }
279
280   if (nv->name[0] != ':') {
281     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
282   }
283
284   return 0;
285 }
286
287 int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
288                            nghttp2_frame *frame, nghttp2_nv *nv, int token,
289                            int trailer) {
290   /* We are strict for pseudo header field.  One bad character should
291      lead to fail.  OTOH, we should be a bit forgiving for regular
292      headers, since existing public internet has so much illegal
293      headers floating around and if we kill the stream because of
294      this, we may disrupt many web sites and/or libraries.  So we
295      become conservative here, and just ignore those illegal regular
296      headers. */
297   if (!nghttp2_check_header_name(nv->name, nv->namelen)) {
298     size_t i;
299     if (nv->namelen > 0 && nv->name[0] == ':') {
300       return NGHTTP2_ERR_HTTP_HEADER;
301     }
302     /* header field name must be lower-cased without exception */
303     for (i = 0; i < nv->namelen; ++i) {
304       char c = nv->name[i];
305       if ('A' <= c && c <= 'Z') {
306         return NGHTTP2_ERR_HTTP_HEADER;
307       }
308     }
309     /* When ignoring regular headers, we set this flag so that we
310        still enforce header field ordering rule for pseudo header
311        fields. */
312     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
313     return NGHTTP2_ERR_IGN_HTTP_HEADER;
314   }
315
316   if (!nghttp2_check_header_value(nv->value, nv->valuelen)) {
317     assert(nv->namelen > 0);
318     if (nv->name[0] == ':') {
319       return NGHTTP2_ERR_HTTP_HEADER;
320     }
321     /* When ignoring regular headers, we set this flag so that we
322        still enforce header field ordering rule for pseudo header
323        fields. */
324     stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
325     return NGHTTP2_ERR_IGN_HTTP_HEADER;
326   }
327
328   if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
329     return http_request_on_header(stream, nv, token, trailer);
330   }
331
332   return http_response_on_header(stream, nv, token, trailer);
333 }
334
335 int nghttp2_http_on_request_headers(nghttp2_stream *stream,
336                                     nghttp2_frame *frame) {
337   if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
338     if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
339       return -1;
340     }
341     stream->content_length = -1;
342   } else {
343     if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
344             NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
345         (stream->http_flags &
346          (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
347       return -1;
348     }
349     if (!check_path(stream)) {
350       return -1;
351     }
352   }
353
354   if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
355     /* we are going to reuse data fields for upcoming response.  Clear
356        them now, except for method flags. */
357     stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
358     stream->content_length = -1;
359   }
360
361   return 0;
362 }
363
364 int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
365   if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
366     return -1;
367   }
368
369   if (stream->status_code / 100 == 1) {
370     /* non-final response */
371     stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
372                          NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
373     stream->content_length = -1;
374     stream->status_code = -1;
375     return 0;
376   }
377
378   stream->http_flags &= ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
379
380   if (!expect_response_body(stream)) {
381     stream->content_length = 0;
382   } else if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
383     stream->content_length = -1;
384   }
385
386   return 0;
387 }
388
389 int nghttp2_http_on_trailer_headers(nghttp2_stream *stream _U_,
390                                     nghttp2_frame *frame) {
391   if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
392     return -1;
393   }
394
395   return 0;
396 }
397
398 int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
399   if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
400     return -1;
401   }
402
403   if (stream->content_length != -1 &&
404       stream->content_length != stream->recv_content_length) {
405     return -1;
406   }
407
408   return 0;
409 }
410
411 int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
412   stream->recv_content_length += n;
413
414   if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
415       (stream->content_length != -1 &&
416        stream->recv_content_length > stream->content_length)) {
417     return -1;
418   }
419
420   return 0;
421 }
422
423 void nghttp2_http_record_request_method(nghttp2_stream *stream,
424                                         nghttp2_frame *frame) {
425   const nghttp2_nv *nva;
426   size_t nvlen;
427   size_t i;
428
429   switch (frame->hd.type) {
430   case NGHTTP2_HEADERS:
431     nva = frame->headers.nva;
432     nvlen = frame->headers.nvlen;
433     break;
434   case NGHTTP2_PUSH_PROMISE:
435     nva = frame->push_promise.nva;
436     nvlen = frame->push_promise.nvlen;
437     break;
438   default:
439     return;
440   }
441
442   /* TODO we should do this strictly. */
443   for (i = 0; i < nvlen; ++i) {
444     const nghttp2_nv *nv = &nva[i];
445     if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
446           memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
447       continue;
448     }
449     if (lstreq("CONNECT", nv->value, nv->valuelen)) {
450       stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
451       return;
452     }
453     if (lstreq("HEAD", nv->value, nv->valuelen)) {
454       stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
455       return;
456     }
457     return;
458   }
459 }