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