Coverage Report

Created: 2025-08-09 06:11

/src/nghttp2/lib/nghttp2_http.c
Line
Count
Source (jump to first uncovered line)
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
36
37.0k
static uint8_t downcase(uint8_t c) {
37
37.0k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
37.0k
}
39
40
4.15k
static int memieq(const void *a, const void *b, size_t n) {
41
4.15k
  size_t i;
42
4.15k
  const uint8_t *aa = a, *bb = b;
43
44
19.9k
  for (i = 0; i < n; ++i) {
45
18.5k
    if (downcase(aa[i]) != downcase(bb[i])) {
46
2.71k
      return 0;
47
2.71k
    }
48
18.5k
  }
49
1.43k
  return 1;
50
4.15k
}
51
52
1.92k
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
53
54
718
static int64_t parse_uint(const uint8_t *s, size_t len) {
55
718
  int64_t n = 0;
56
718
  size_t i;
57
718
  if (len == 0) {
58
76
    return -1;
59
76
  }
60
7.92k
  for (i = 0; i < len; ++i) {
61
7.45k
    if ('0' <= s[i] && s[i] <= '9') {
62
7.35k
      if (n > INT64_MAX / 10) {
63
1
        return -1;
64
1
      }
65
7.35k
      n *= 10;
66
7.35k
      if (n > INT64_MAX - (s[i] - '0')) {
67
66
        return -1;
68
66
      }
69
7.28k
      n += s[i] - '0';
70
7.28k
      continue;
71
7.35k
    }
72
105
    return -1;
73
7.45k
  }
74
470
  return n;
75
642
}
76
77
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
78
7.08k
                               uint32_t flag) {
79
7.08k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
80
663
    return 0;
81
663
  }
82
6.42k
  stream->http_flags = stream->http_flags | flag;
83
6.42k
  return 1;
84
7.08k
}
85
86
0
static int expect_response_body(nghttp2_stream *stream) {
87
0
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
88
0
         stream->status_code / 100 != 1 && stream->status_code != 304 &&
89
0
         stream->status_code != 204;
90
0
}
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.*/
97
621
static int check_path(nghttp2_stream *stream) {
98
621
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
99
621
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
100
606
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
101
10
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
102
621
}
103
104
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
105
54.1k
                                  int trailer, int connect_protocol) {
106
54.1k
  nghttp2_extpri extpri;
107
108
54.1k
  if (nv->name->base[0] == ':') {
109
7.47k
    if (trailer ||
110
7.47k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
111
383
      return NGHTTP2_ERR_HTTP_HEADER;
112
383
    }
113
7.47k
  }
114
115
53.7k
  switch (nv->token) {
116
1.21k
  case NGHTTP2_TOKEN__AUTHORITY:
117
1.21k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
118
240
      return NGHTTP2_ERR_HTTP_HEADER;
119
240
    }
120
978
    break;
121
1.63k
  case NGHTTP2_TOKEN__METHOD:
122
1.63k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
123
37
      return NGHTTP2_ERR_HTTP_HEADER;
124
37
    }
125
1.60k
    switch (nv->value->len) {
126
841
    case 4:
127
841
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
128
66
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
129
66
      }
130
841
      break;
131
566
    case 7:
132
566
      switch (nv->value->base[6]) {
133
340
      case 'T':
134
340
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
135
233
          if (stream->stream_id % 2 == 0) {
136
            /* we won't allow CONNECT for push */
137
0
            return NGHTTP2_ERR_HTTP_HEADER;
138
0
          }
139
233
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
140
233
        }
141
340
        break;
142
340
      case 'S':
143
133
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
144
66
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
145
66
        }
146
133
        break;
147
566
      }
148
566
      break;
149
1.60k
    }
150
1.60k
    break;
151
1.60k
  case NGHTTP2_TOKEN__PATH:
152
1.27k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
153
165
      return NGHTTP2_ERR_HTTP_HEADER;
154
165
    }
155
1.10k
    if (nv->value->base[0] == '/') {
156
798
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
157
798
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
158
66
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
159
66
    }
160
1.10k
    break;
161
2.58k
  case NGHTTP2_TOKEN__SCHEME:
162
2.58k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
163
68
      return NGHTTP2_ERR_HTTP_HEADER;
164
68
    }
165
2.51k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
166
2.51k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
167
855
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
168
855
    }
169
2.51k
    break;
170
146
  case NGHTTP2_TOKEN__PROTOCOL:
171
146
    if (!connect_protocol) {
172
40
      return NGHTTP2_ERR_HTTP_HEADER;
173
40
    }
174
175
106
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
176
45
      return NGHTTP2_ERR_HTTP_HEADER;
177
45
    }
178
61
    break;
179
267
  case NGHTTP2_TOKEN_HOST:
180
267
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
181
108
      return NGHTTP2_ERR_HTTP_HEADER;
182
108
    }
183
159
    break;
184
787
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
185
787
    if (stream->content_length != -1) {
186
69
      return NGHTTP2_ERR_HTTP_HEADER;
187
69
    }
188
718
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
189
718
    if (stream->content_length == -1) {
190
248
      return NGHTTP2_ERR_HTTP_HEADER;
191
248
    }
192
470
    break;
193
718
  }
194
  /* disallowed header fields */
195
470
  case NGHTTP2_TOKEN_CONNECTION:
196
114
  case NGHTTP2_TOKEN_KEEP_ALIVE:
197
189
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
198
277
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
199
349
  case NGHTTP2_TOKEN_UPGRADE:
200
349
    return NGHTTP2_ERR_HTTP_HEADER;
201
1.92k
  case NGHTTP2_TOKEN_TE:
202
1.92k
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
203
1.34k
      return NGHTTP2_ERR_HTTP_HEADER;
204
1.34k
    }
205
580
    break;
206
37.0k
  case NGHTTP2_TOKEN_PRIORITY:
207
37.0k
    if (!trailer &&
208
        /* Do not parse the header field in PUSH_PROMISE. */
209
37.0k
        (stream->stream_id & 1) &&
210
37.0k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
211
36.6k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
212
36.6k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
213
36.6k
                                      nv->value->len) == 0) {
214
23.6k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
215
23.6k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
216
23.6k
      } else {
217
12.9k
        stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
218
12.9k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
219
12.9k
      }
220
36.6k
    }
221
37.0k
    break;
222
6.54k
  default:
223
6.54k
    if (nv->name->base[0] == ':') {
224
229
      return NGHTTP2_ERR_HTTP_HEADER;
225
229
    }
226
53.7k
  }
227
228
50.8k
  if (nv->name->base[0] != ':') {
229
44.5k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
230
44.5k
  }
231
232
50.8k
  return 0;
233
53.7k
}
234
235
static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
236
0
                                   int trailer) {
237
0
  if (nv->name->base[0] == ':') {
238
0
    if (trailer ||
239
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
240
0
      return NGHTTP2_ERR_HTTP_HEADER;
241
0
    }
242
0
  }
243
244
0
  switch (nv->token) {
245
0
  case NGHTTP2_TOKEN__STATUS: {
246
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
247
0
      return NGHTTP2_ERR_HTTP_HEADER;
248
0
    }
249
0
    if (nv->value->len != 3) {
250
0
      return NGHTTP2_ERR_HTTP_HEADER;
251
0
    }
252
0
    stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
253
0
    if (stream->status_code == -1 || stream->status_code == 101) {
254
0
      return NGHTTP2_ERR_HTTP_HEADER;
255
0
    }
256
0
    break;
257
0
  }
258
0
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
259
0
    if (stream->status_code == 204) {
260
      /* content-length header field in 204 response is prohibited by
261
         RFC 7230.  But some widely used servers send content-length:
262
         0.  Until they get fixed, we ignore it. */
263
0
      if (stream->content_length != -1) {
264
        /* Found multiple content-length field */
265
0
        return NGHTTP2_ERR_HTTP_HEADER;
266
0
      }
267
0
      if (!lstrieq("0", nv->value->base, nv->value->len)) {
268
0
        return NGHTTP2_ERR_HTTP_HEADER;
269
0
      }
270
0
      stream->content_length = 0;
271
0
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
272
0
    }
273
0
    if (stream->status_code / 100 == 1) {
274
0
      return NGHTTP2_ERR_HTTP_HEADER;
275
0
    }
276
    /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
277
0
    if (stream->status_code / 100 == 2 &&
278
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
279
0
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
280
0
    }
281
0
    if (stream->content_length != -1) {
282
0
      return NGHTTP2_ERR_HTTP_HEADER;
283
0
    }
284
0
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
285
0
    if (stream->content_length == -1) {
286
0
      return NGHTTP2_ERR_HTTP_HEADER;
287
0
    }
288
0
    break;
289
0
  }
290
  /* disallowed header fields */
291
0
  case NGHTTP2_TOKEN_CONNECTION:
292
0
  case NGHTTP2_TOKEN_KEEP_ALIVE:
293
0
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
294
0
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
295
0
  case NGHTTP2_TOKEN_UPGRADE:
296
0
    return NGHTTP2_ERR_HTTP_HEADER;
297
0
  case NGHTTP2_TOKEN_TE:
298
0
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
299
0
      return NGHTTP2_ERR_HTTP_HEADER;
300
0
    }
301
0
    break;
302
0
  default:
303
0
    if (nv->name->base[0] == ':') {
304
0
      return NGHTTP2_ERR_HTTP_HEADER;
305
0
    }
306
0
  }
307
308
0
  if (nv->name->base[0] != ':') {
309
0
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
310
0
  }
311
312
0
  return 0;
313
0
}
314
315
3.53k
static int check_scheme(const uint8_t *value, size_t len) {
316
3.53k
  const uint8_t *last;
317
3.53k
  if (len == 0) {
318
133
    return 0;
319
133
  }
320
321
3.40k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
322
392
    return 0;
323
392
  }
324
325
3.00k
  last = value + len;
326
3.00k
  ++value;
327
328
44.2k
  for (; value != last; ++value) {
329
41.6k
    if (!(('A' <= *value && *value <= 'Z') ||
330
41.6k
          ('a' <= *value && *value <= 'z') ||
331
41.6k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
332
41.6k
          *value == '.')) {
333
367
      return 0;
334
367
    }
335
41.6k
  }
336
2.64k
  return 1;
337
3.00k
}
338
339
0
static int lws(const uint8_t *s, size_t n) {
340
0
  size_t i;
341
0
  for (i = 0; i < n; ++i) {
342
0
    if (s[i] != ' ' && s[i] != '\t') {
343
0
      return 0;
344
0
    }
345
0
  }
346
0
  return 1;
347
0
}
348
349
/* Generated by genauthoritychartbl.py, but '@' is not allowed */
350
static char VALID_AUTHORITY_CHARS[] = {
351
  0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
352
  0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
353
  0 /* BS   */, 0 /* HT   */, 0 /* LF   */, 0 /* VT   */,
354
  0 /* FF   */, 0 /* CR   */, 0 /* SO   */, 0 /* SI   */,
355
  0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
356
  0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */,
357
  0 /* CAN  */, 0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */,
358
  0 /* FS   */, 0 /* GS   */, 0 /* RS   */, 0 /* US   */,
359
  0 /* SPC  */, 1 /* !    */, 0 /* "    */, 0 /* #    */,
360
  1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
361
  1 /* (    */, 1 /* )    */, 1 /* *    */, 1 /* +    */,
362
  1 /* ,    */, 1 /* -    */, 1 /* .    */, 0 /* /    */,
363
  1 /* 0    */, 1 /* 1    */, 1 /* 2    */, 1 /* 3    */,
364
  1 /* 4    */, 1 /* 5    */, 1 /* 6    */, 1 /* 7    */,
365
  1 /* 8    */, 1 /* 9    */, 1 /* :    */, 1 /* ;    */,
366
  0 /* <    */, 1 /* =    */, 0 /* >    */, 0 /* ?    */,
367
  0 /* @    */, 1 /* A    */, 1 /* B    */, 1 /* C    */,
368
  1 /* D    */, 1 /* E    */, 1 /* F    */, 1 /* G    */,
369
  1 /* H    */, 1 /* I    */, 1 /* J    */, 1 /* K    */,
370
  1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,
371
  1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */,
372
  1 /* T    */, 1 /* U    */, 1 /* V    */, 1 /* W    */,
373
  1 /* X    */, 1 /* Y    */, 1 /* Z    */, 1 /* [    */,
374
  0 /* \    */, 1 /* ]    */, 0 /* ^    */, 1 /* _    */,
375
  0 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
376
  1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */,
377
  1 /* h    */, 1 /* i    */, 1 /* j    */, 1 /* k    */,
378
  1 /* l    */, 1 /* m    */, 1 /* n    */, 1 /* o    */,
379
  1 /* p    */, 1 /* q    */, 1 /* r    */, 1 /* s    */,
380
  1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
381
  1 /* x    */, 1 /* y    */, 1 /* z    */, 0 /* {    */,
382
  0 /* |    */, 0 /* }    */, 1 /* ~    */, 0 /* DEL  */,
383
  0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
384
  0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
385
  0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
386
  0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
387
  0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
388
  0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
389
  0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
390
  0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
391
  0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
392
  0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
393
  0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
394
  0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
395
  0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
396
  0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
397
  0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
398
  0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
399
  0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
400
  0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
401
  0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
402
  0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
403
  0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
404
  0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
405
  0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
406
  0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
407
  0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
408
  0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
409
  0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
410
  0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
411
  0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
412
  0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
413
  0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
414
  0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
415
};
416
417
1.81k
static int check_authority(const uint8_t *value, size_t len) {
418
1.81k
  const uint8_t *last;
419
35.9k
  for (last = value + len; value != last; ++value) {
420
34.2k
    if (!VALID_AUTHORITY_CHARS[*value]) {
421
96
      return 0;
422
96
    }
423
34.2k
  }
424
1.72k
  return 1;
425
1.81k
}
426
427
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
428
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
429
58.1k
                           int trailer) {
430
58.1k
  int rv;
431
432
  /* We are strict for pseudo header field.  One bad character should
433
     lead to fail.  OTOH, we should be a bit forgiving for regular
434
     headers, since existing public internet has so much illegal
435
     headers floating around and if we kill the stream because of
436
     this, we may disrupt many web sites and/or libraries.  So we
437
     become conservative here, and just ignore those illegal regular
438
     headers. */
439
58.1k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
440
2.08k
    size_t i;
441
2.08k
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
442
94
      return NGHTTP2_ERR_HTTP_HEADER;
443
94
    }
444
    /* header field name must be lower-cased without exception */
445
11.2k
    for (i = 0; i < nv->name->len; ++i) {
446
9.73k
      uint8_t c = nv->name->base[i];
447
9.73k
      if ('A' <= c && c <= 'Z') {
448
501
        return NGHTTP2_ERR_HTTP_HEADER;
449
501
      }
450
9.73k
    }
451
    /* When ignoring regular headers, we set this flag so that we
452
       still enforce header field ordering rule for pseudo header
453
       fields. */
454
1.48k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
455
1.48k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
456
1.98k
  }
457
458
56.0k
  switch (nv->token) {
459
1.88k
  case NGHTTP2_TOKEN__METHOD:
460
1.88k
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
461
1.88k
    break;
462
1.39k
  case NGHTTP2_TOKEN__PATH:
463
1.39k
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
464
1.39k
    break;
465
1.48k
  case NGHTTP2_TOKEN__AUTHORITY:
466
1.81k
  case NGHTTP2_TOKEN_HOST:
467
1.81k
    if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
468
1.81k
      rv = check_authority(nv->value->base, nv->value->len);
469
1.81k
    } else if (
470
0
      stream->flags &
471
0
      NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
472
0
      rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
473
0
    } else {
474
0
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
475
0
    }
476
1.81k
    break;
477
3.53k
  case NGHTTP2_TOKEN__SCHEME:
478
3.53k
    rv = check_scheme(nv->value->base, nv->value->len);
479
3.53k
    break;
480
147
  case NGHTTP2_TOKEN__PROTOCOL:
481
    /* Check the value consists of just white spaces, which was done
482
       in check_pseudo_header before
483
       nghttp2_check_header_value_rfc9113 has been introduced. */
484
147
    if ((stream->flags &
485
147
         NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
486
147
        lws(nv->value->base, nv->value->len)) {
487
0
      rv = 0;
488
0
      break;
489
0
    }
490
    /* fall through */
491
47.4k
  default:
492
47.4k
    if (stream->flags &
493
47.4k
        NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
494
0
      rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
495
47.4k
    } else {
496
47.4k
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
497
47.4k
    }
498
56.0k
  }
499
500
56.0k
  if (rv == 0) {
501
1.87k
    assert(nv->name->len > 0);
502
1.87k
    if (nv->name->base[0] == ':') {
503
1.39k
      return NGHTTP2_ERR_HTTP_HEADER;
504
1.39k
    }
505
    /* When ignoring regular headers, we set this flag so that we
506
       still enforce header field ordering rule for pseudo header
507
       fields. */
508
473
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
509
473
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
510
1.87k
  }
511
512
54.1k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
513
54.1k
    return http_request_on_header(stream, nv, trailer,
514
54.1k
                                  session->server &&
515
54.1k
                                    session->pending_enable_connect_protocol);
516
54.1k
  }
517
518
0
  return http_response_on_header(stream, nv, trailer);
519
54.1k
}
520
521
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
522
16.2k
                                    nghttp2_frame *frame) {
523
16.2k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
524
16.2k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
525
225
    if ((stream->http_flags &
526
225
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
527
225
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
528
80
      return -1;
529
80
    }
530
145
    stream->content_length = -1;
531
16.0k
  } else {
532
16.0k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
533
16.0k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
534
16.0k
        (stream->http_flags &
535
15.4k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
536
15.4k
      return -1;
537
15.4k
    }
538
631
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
539
631
        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
540
10
         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
541
10
      return -1;
542
10
    }
543
621
    if (!check_path(stream)) {
544
10
      return -1;
545
10
    }
546
621
  }
547
548
756
  if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
549
    /* we are going to reuse data fields for upcoming response.  Clear
550
       them now, except for method flags. */
551
0
    stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
552
0
    stream->content_length = -1;
553
0
  }
554
555
756
  return 0;
556
16.2k
}
557
558
0
int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
559
0
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
560
0
    return -1;
561
0
  }
562
563
0
  if (stream->status_code / 100 == 1) {
564
    /* non-final response */
565
0
    stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
566
0
                         NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
567
0
    stream->content_length = -1;
568
0
    stream->status_code = -1;
569
0
    return 0;
570
0
  }
571
572
0
  stream->http_flags =
573
0
    stream->http_flags & (uint32_t)~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
574
575
0
  if (!expect_response_body(stream)) {
576
0
    stream->content_length = 0;
577
0
  } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
578
0
                                   NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
579
0
    stream->content_length = -1;
580
0
  }
581
582
0
  return 0;
583
0
}
584
585
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
586
53
                                    nghttp2_frame *frame) {
587
53
  (void)stream;
588
589
53
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
590
47
    return -1;
591
47
  }
592
593
6
  return 0;
594
53
}
595
596
232
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
597
232
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
598
0
    return -1;
599
0
  }
600
601
232
  if (stream->content_length != -1 &&
602
232
      stream->content_length != stream->recv_content_length) {
603
132
    return -1;
604
132
  }
605
606
100
  return 0;
607
232
}
608
609
1.94k
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
610
1.94k
  stream->recv_content_length += (int64_t)n;
611
612
1.94k
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
613
1.94k
      (stream->content_length != -1 &&
614
1.94k
       stream->recv_content_length > stream->content_length)) {
615
3
    return -1;
616
3
  }
617
618
1.94k
  return 0;
619
1.94k
}
620
621
void nghttp2_http_record_request_method(nghttp2_stream *stream,
622
0
                                        nghttp2_frame *frame) {
623
0
  const nghttp2_nv *nva;
624
0
  size_t nvlen;
625
0
  size_t i;
626
627
0
  switch (frame->hd.type) {
628
0
  case NGHTTP2_HEADERS:
629
0
    nva = frame->headers.nva;
630
0
    nvlen = frame->headers.nvlen;
631
0
    break;
632
0
  case NGHTTP2_PUSH_PROMISE:
633
0
    nva = frame->push_promise.nva;
634
0
    nvlen = frame->push_promise.nvlen;
635
0
    break;
636
0
  default:
637
0
    return;
638
0
  }
639
640
  /* TODO we should do this strictly. */
641
0
  for (i = 0; i < nvlen; ++i) {
642
0
    const nghttp2_nv *nv = &nva[i];
643
0
    if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
644
0
          memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
645
0
      continue;
646
0
    }
647
0
    if (lstreq("CONNECT", nv->value, nv->valuelen)) {
648
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
649
0
      return;
650
0
    }
651
0
    if (lstreq("HEAD", nv->value, nv->valuelen)) {
652
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
653
0
      return;
654
0
    }
655
0
    return;
656
0
  }
657
0
}
658
659
int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
660
36.6k
                                size_t valuelen) {
661
36.6k
  nghttp2_extpri pri = *dest;
662
36.6k
  sfparse_parser sfp;
663
36.6k
  sfparse_vec key;
664
36.6k
  sfparse_value val;
665
36.6k
  int rv;
666
667
36.6k
  sfparse_parser_init(&sfp, value, valuelen);
668
669
65.4k
  for (;;) {
670
65.4k
    rv = sfparse_parser_dict(&sfp, &key, &val);
671
65.4k
    if (rv != 0) {
672
36.2k
      if (rv == SFPARSE_ERR_EOF) {
673
23.6k
        break;
674
23.6k
      }
675
676
12.6k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
677
36.2k
    }
678
679
29.1k
    if (key.len != 1) {
680
5.61k
      continue;
681
5.61k
    }
682
683
23.5k
    switch (key.base[0]) {
684
495
    case 'i':
685
495
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
686
71
        return NGHTTP2_ERR_INVALID_ARGUMENT;
687
71
      }
688
689
424
      pri.inc = val.boolean;
690
691
424
      break;
692
773
    case 'u':
693
773
      if (val.type != SFPARSE_TYPE_INTEGER ||
694
773
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
695
773
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
696
260
        return NGHTTP2_ERR_INVALID_ARGUMENT;
697
260
      }
698
699
513
      pri.urgency = (uint32_t)val.integer;
700
701
513
      break;
702
23.5k
    }
703
23.5k
  }
704
705
23.6k
  *dest = pri;
706
707
23.6k
  return 0;
708
36.6k
}