Coverage Report

Created: 2026-02-26 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nghttp2/lib/nghttp2_http.c
Line
Count
Source
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
45.8k
static uint8_t downcase(uint8_t c) {
37
45.8k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
45.8k
}
39
40
5.02k
static int memieq(const void *a, const void *b, size_t n) {
41
5.02k
  size_t i;
42
5.02k
  const uint8_t *aa = a, *bb = b;
43
44
27.7k
  for (i = 0; i < n; ++i) {
45
22.9k
    if (downcase(aa[i]) != downcase(bb[i])) {
46
208
      return 0;
47
208
    }
48
22.9k
  }
49
4.81k
  return 1;
50
5.02k
}
51
52
#define lstrieq(A, B, N)                                                       \
53
466
  (nghttp2_strlen_lit((A)) == (N) && memieq((A), (B), (N)))
54
55
636
static int64_t parse_uint(const uint8_t *s, size_t len) {
56
636
  int64_t n = 0;
57
636
  size_t i;
58
636
  if (len == 0) {
59
6
    return -1;
60
6
  }
61
5.94k
  for (i = 0; i < len; ++i) {
62
5.33k
    if ('0' <= s[i] && s[i] <= '9') {
63
5.31k
      if (n > INT64_MAX / 10) {
64
5
        return -1;
65
5
      }
66
5.31k
      n *= 10;
67
5.31k
      if (n > INT64_MAX - (s[i] - '0')) {
68
1
        return -1;
69
1
      }
70
5.31k
      n += s[i] - '0';
71
5.31k
      continue;
72
5.31k
    }
73
22
    return -1;
74
5.33k
  }
75
602
  return n;
76
630
}
77
78
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
79
18.2k
                               uint32_t flag) {
80
18.2k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
81
75
    return 0;
82
75
  }
83
18.1k
  stream->http_flags = stream->http_flags | flag;
84
18.1k
  return 1;
85
18.2k
}
86
87
0
static int expect_response_body(nghttp2_stream *stream) {
88
0
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
89
0
         stream->status_code / 100 != 1 && stream->status_code != 304 &&
90
0
         stream->status_code != 204;
91
0
}
92
93
/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
94
   header field to represent system-wide OPTIONS request.  Otherwise,
95
   :path header field value must start with "/".  This function must
96
   be called after ":method" header field was received.  This function
97
   returns nonzero if path is valid.*/
98
4.40k
static int check_path(nghttp2_stream *stream) {
99
4.40k
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
100
4.38k
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
101
1
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
102
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
103
4.40k
}
104
105
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
106
55.7k
                                  int trailer, int connect_protocol) {
107
55.7k
  nghttp2_extpri extpri;
108
109
55.7k
  if (nv->name->base[0] == ':') {
110
17.0k
    if (trailer ||
111
17.0k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
112
24
      return NGHTTP2_ERR_HTTP_HEADER;
113
24
    }
114
17.0k
  }
115
116
55.7k
  switch (nv->token) {
117
3.29k
  case NGHTTP2_TOKEN__AUTHORITY:
118
3.29k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
119
10
      return NGHTTP2_ERR_HTTP_HEADER;
120
10
    }
121
3.28k
    break;
122
4.52k
  case NGHTTP2_TOKEN__METHOD:
123
4.52k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
124
7
      return NGHTTP2_ERR_HTTP_HEADER;
125
7
    }
126
4.51k
    switch (nv->value->len) {
127
1.53k
    case 4:
128
1.53k
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
129
1
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
130
1
      }
131
1.53k
      break;
132
17
    case 7:
133
17
      switch (nv->value->base[6]) {
134
13
      case 'T':
135
13
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
136
12
          if (stream->stream_id % 2 == 0) {
137
            /* we won't allow CONNECT for push */
138
0
            return NGHTTP2_ERR_HTTP_HEADER;
139
0
          }
140
12
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
141
12
        }
142
13
        break;
143
13
      case 'S':
144
3
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
145
1
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
146
1
        }
147
3
        break;
148
17
      }
149
17
      break;
150
4.51k
    }
151
4.51k
    break;
152
4.51k
  case NGHTTP2_TOKEN__PATH:
153
4.50k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
154
12
      return NGHTTP2_ERR_HTTP_HEADER;
155
12
    }
156
4.49k
    if (nv->value->base[0] == '/') {
157
4.47k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
158
4.47k
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
159
1
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
160
1
    }
161
4.49k
    break;
162
4.66k
  case NGHTTP2_TOKEN__SCHEME:
163
4.66k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
164
32
      return NGHTTP2_ERR_HTTP_HEADER;
165
32
    }
166
4.63k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
167
4.46k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
168
4.46k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
169
4.46k
    }
170
4.63k
    break;
171
1
  case NGHTTP2_TOKEN__PROTOCOL:
172
1
    if (!connect_protocol) {
173
1
      return NGHTTP2_ERR_HTTP_HEADER;
174
1
    }
175
176
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
177
0
      return NGHTTP2_ERR_HTTP_HEADER;
178
0
    }
179
0
    break;
180
1.21k
  case NGHTTP2_TOKEN_HOST:
181
1.21k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
182
14
      return NGHTTP2_ERR_HTTP_HEADER;
183
14
    }
184
1.20k
    break;
185
1.20k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
186
679
    if (stream->content_length != -1) {
187
43
      return NGHTTP2_ERR_HTTP_HEADER;
188
43
    }
189
636
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
190
636
    if (stream->content_length == -1) {
191
34
      return NGHTTP2_ERR_HTTP_HEADER;
192
34
    }
193
602
    break;
194
636
  }
195
  /* disallowed header fields */
196
602
  case NGHTTP2_TOKEN_CONNECTION:
197
5
  case NGHTTP2_TOKEN_KEEP_ALIVE:
198
9
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
199
19
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
200
21
  case NGHTTP2_TOKEN_UPGRADE:
201
21
    return NGHTTP2_ERR_HTTP_HEADER;
202
466
  case NGHTTP2_TOKEN_TE:
203
466
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
204
115
      return NGHTTP2_ERR_HTTP_HEADER;
205
115
    }
206
351
    break;
207
23.2k
  case NGHTTP2_TOKEN_PRIORITY:
208
23.2k
    if (!trailer &&
209
        /* Do not parse the header field in PUSH_PROMISE. */
210
23.0k
        (stream->stream_id & 1) &&
211
23.0k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
212
22.5k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
213
22.5k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
214
22.5k
                                      nv->value->len) == 0) {
215
19.8k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
216
19.8k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
217
19.8k
      } else {
218
2.67k
        stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
219
2.67k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
220
2.67k
      }
221
22.5k
    }
222
23.2k
    break;
223
13.1k
  default:
224
13.1k
    if (nv->name->base[0] == ':') {
225
12
      return NGHTTP2_ERR_HTTP_HEADER;
226
12
    }
227
55.7k
  }
228
229
55.4k
  if (nv->name->base[0] != ':') {
230
38.5k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
231
38.5k
  }
232
233
55.4k
  return 0;
234
55.7k
}
235
236
static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
237
0
                                   int trailer) {
238
0
  if (nv->name->base[0] == ':') {
239
0
    if (trailer ||
240
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
241
0
      return NGHTTP2_ERR_HTTP_HEADER;
242
0
    }
243
0
  }
244
245
0
  switch (nv->token) {
246
0
  case NGHTTP2_TOKEN__STATUS: {
247
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
248
0
      return NGHTTP2_ERR_HTTP_HEADER;
249
0
    }
250
0
    if (nv->value->len != 3) {
251
0
      return NGHTTP2_ERR_HTTP_HEADER;
252
0
    }
253
0
    stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
254
0
    if (stream->status_code == -1 || stream->status_code == 101) {
255
0
      return NGHTTP2_ERR_HTTP_HEADER;
256
0
    }
257
0
    break;
258
0
  }
259
0
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
260
0
    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
0
      if (stream->content_length != -1) {
265
        /* Found multiple content-length field */
266
0
        return NGHTTP2_ERR_HTTP_HEADER;
267
0
      }
268
0
      if (!lstrieq("0", nv->value->base, nv->value->len)) {
269
0
        return NGHTTP2_ERR_HTTP_HEADER;
270
0
      }
271
0
      stream->content_length = 0;
272
0
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
273
0
    }
274
0
    if (stream->status_code / 100 == 1) {
275
0
      return NGHTTP2_ERR_HTTP_HEADER;
276
0
    }
277
    /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
278
0
    if (stream->status_code / 100 == 2 &&
279
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
280
0
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
281
0
    }
282
0
    if (stream->content_length != -1) {
283
0
      return NGHTTP2_ERR_HTTP_HEADER;
284
0
    }
285
0
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
286
0
    if (stream->content_length == -1) {
287
0
      return NGHTTP2_ERR_HTTP_HEADER;
288
0
    }
289
0
    break;
290
0
  }
291
  /* disallowed header fields */
292
0
  case NGHTTP2_TOKEN_CONNECTION:
293
0
  case NGHTTP2_TOKEN_KEEP_ALIVE:
294
0
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
295
0
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
296
0
  case NGHTTP2_TOKEN_UPGRADE:
297
0
    return NGHTTP2_ERR_HTTP_HEADER;
298
0
  case NGHTTP2_TOKEN_TE:
299
0
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
300
0
      return NGHTTP2_ERR_HTTP_HEADER;
301
0
    }
302
0
    break;
303
0
  default:
304
0
    if (nv->name->base[0] == ':') {
305
0
      return NGHTTP2_ERR_HTTP_HEADER;
306
0
    }
307
0
  }
308
309
0
  if (nv->name->base[0] != ':') {
310
0
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
311
0
  }
312
313
0
  return 0;
314
0
}
315
316
4.70k
static int check_scheme(const uint8_t *value, size_t len) {
317
4.70k
  const uint8_t *last;
318
4.70k
  if (len == 0) {
319
4
    return 0;
320
4
  }
321
322
4.70k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
323
14
    return 0;
324
14
  }
325
326
4.69k
  last = value + len;
327
4.69k
  ++value;
328
329
27.6k
  for (; value != last; ++value) {
330
23.0k
    if (!(('A' <= *value && *value <= 'Z') ||
331
22.4k
          ('a' <= *value && *value <= 'z') ||
332
6.84k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
333
66
          *value == '.')) {
334
19
      return 0;
335
19
    }
336
23.0k
  }
337
4.67k
  return 1;
338
4.69k
}
339
340
0
static int lws(const uint8_t *s, size_t n) {
341
0
  size_t i;
342
0
  for (i = 0; i < n; ++i) {
343
0
    if (s[i] != ' ' && s[i] != '\t') {
344
0
      return 0;
345
0
    }
346
0
  }
347
0
  return 1;
348
0
}
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
418
4.68k
static int check_authority(const uint8_t *value, size_t len) {
419
4.68k
  const uint8_t *last;
420
38.2k
  for (last = value + len; value != last; ++value) {
421
33.7k
    if (!VALID_AUTHORITY_CHARS[*value]) {
422
169
      return 0;
423
169
    }
424
33.7k
  }
425
4.51k
  return 1;
426
4.68k
}
427
428
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
429
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
430
64.3k
                           int trailer) {
431
64.3k
  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
64.3k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
441
6.78k
    size_t i;
442
6.78k
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
443
13
      return NGHTTP2_ERR_HTTP_HEADER;
444
13
    }
445
    /* header field name must be lower-cased without exception */
446
50.9k
    for (i = 0; i < nv->name->len; ++i) {
447
44.3k
      uint8_t c = nv->name->base[i];
448
44.3k
      if ('A' <= c && c <= 'Z') {
449
193
        return NGHTTP2_ERR_HTTP_HEADER;
450
193
      }
451
44.3k
    }
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
6.57k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
456
6.57k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
457
6.77k
  }
458
459
57.6k
  switch (nv->token) {
460
4.53k
  case NGHTTP2_TOKEN__METHOD:
461
4.53k
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
462
4.53k
    break;
463
4.51k
  case NGHTTP2_TOKEN__PATH:
464
4.51k
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
465
4.51k
    break;
466
3.29k
  case NGHTTP2_TOKEN__AUTHORITY:
467
4.68k
  case NGHTTP2_TOKEN_HOST:
468
4.68k
    if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
469
4.68k
      rv = check_authority(nv->value->base, nv->value->len);
470
4.68k
    } else if (
471
0
      stream->flags &
472
0
      NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
473
0
      rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
474
0
    } else {
475
0
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
476
0
    }
477
4.68k
    break;
478
4.70k
  case NGHTTP2_TOKEN__SCHEME:
479
4.70k
    rv = check_scheme(nv->value->base, nv->value->len);
480
4.70k
    break;
481
1
  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
1
    if ((stream->flags &
486
1
         NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
487
0
        lws(nv->value->base, nv->value->len)) {
488
0
      rv = 0;
489
0
      break;
490
0
    }
491
    /* fall through */
492
39.1k
  default:
493
39.1k
    if (stream->flags &
494
39.1k
        NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
495
0
      rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
496
39.1k
    } else {
497
39.1k
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
498
39.1k
    }
499
57.6k
  }
500
501
57.6k
  if (rv == 0) {
502
1.84k
    assert(nv->name->len > 0);
503
1.84k
    if (nv->name->base[0] == ':') {
504
53
      return NGHTTP2_ERR_HTTP_HEADER;
505
53
    }
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
1.79k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
510
1.79k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
511
1.84k
  }
512
513
55.7k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
514
55.7k
    return http_request_on_header(stream, nv, trailer,
515
55.7k
                                  session->server &&
516
55.7k
                                    session->pending_enable_connect_protocol);
517
55.7k
  }
518
519
0
  return http_response_on_header(stream, nv, trailer);
520
55.7k
}
521
522
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
523
4.51k
                                    nghttp2_frame *frame) {
524
4.51k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
525
4.51k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
526
12
    if ((stream->http_flags &
527
12
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
528
11
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
529
2
      return -1;
530
2
    }
531
10
    stream->content_length = -1;
532
4.50k
  } else {
533
4.50k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
534
4.50k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
535
4.40k
        (stream->http_flags &
536
4.40k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
537
97
      return -1;
538
97
    }
539
4.40k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
540
0
        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
541
0
         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
542
0
      return -1;
543
0
    }
544
4.40k
    if (!check_path(stream)) {
545
1
      return -1;
546
1
    }
547
4.40k
  }
548
549
4.41k
  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
0
    stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
553
0
    stream->content_length = -1;
554
0
  }
555
556
4.41k
  return 0;
557
4.51k
}
558
559
0
int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
560
0
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
561
0
    return -1;
562
0
  }
563
564
0
  if (stream->status_code / 100 == 1) {
565
    /* non-final response */
566
0
    stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
567
0
                         NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
568
0
    stream->content_length = -1;
569
0
    stream->status_code = -1;
570
0
    return 0;
571
0
  }
572
573
0
  stream->http_flags =
574
0
    stream->http_flags & (uint32_t)~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
575
576
0
  if (!expect_response_body(stream)) {
577
0
    stream->content_length = 0;
578
0
  } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
579
0
                                   NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
580
0
    stream->content_length = -1;
581
0
  }
582
583
0
  return 0;
584
0
}
585
586
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
587
29
                                    nghttp2_frame *frame) {
588
29
  (void)stream;
589
590
29
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
591
6
    return -1;
592
6
  }
593
594
23
  return 0;
595
29
}
596
597
2.25k
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
598
2.25k
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
599
0
    return -1;
600
0
  }
601
602
2.25k
  if (stream->content_length != -1 &&
603
112
      stream->content_length != stream->recv_content_length) {
604
97
    return -1;
605
97
  }
606
607
2.15k
  return 0;
608
2.25k
}
609
610
267
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
611
267
  stream->recv_content_length += (int64_t)n;
612
613
267
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
614
267
      (stream->content_length != -1 &&
615
133
       stream->recv_content_length > stream->content_length)) {
616
6
    return -1;
617
6
  }
618
619
261
  return 0;
620
267
}
621
622
void nghttp2_http_record_request_method(nghttp2_stream *stream,
623
0
                                        nghttp2_frame *frame) {
624
0
  const nghttp2_nv *nva;
625
0
  size_t nvlen;
626
0
  size_t i;
627
628
0
  switch (frame->hd.type) {
629
0
  case NGHTTP2_HEADERS:
630
0
    nva = frame->headers.nva;
631
0
    nvlen = frame->headers.nvlen;
632
0
    break;
633
0
  case NGHTTP2_PUSH_PROMISE:
634
0
    nva = frame->push_promise.nva;
635
0
    nvlen = frame->push_promise.nvlen;
636
0
    break;
637
0
  default:
638
0
    return;
639
0
  }
640
641
  /* TODO we should do this strictly. */
642
0
  for (i = 0; i < nvlen; ++i) {
643
0
    const nghttp2_nv *nv = &nva[i];
644
0
    if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
645
0
          memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
646
0
      continue;
647
0
    }
648
0
    if (lstreq("CONNECT", nv->value, nv->valuelen)) {
649
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
650
0
      return;
651
0
    }
652
0
    if (lstreq("HEAD", nv->value, nv->valuelen)) {
653
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
654
0
      return;
655
0
    }
656
0
    return;
657
0
  }
658
0
}
659
660
int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
661
22.5k
                                size_t valuelen) {
662
22.5k
  nghttp2_extpri pri = *dest;
663
22.5k
  sfparse_parser sfp;
664
22.5k
  sfparse_vec key;
665
22.5k
  sfparse_value val;
666
22.5k
  int rv;
667
668
22.5k
  sfparse_parser_init(&sfp, value, valuelen);
669
670
49.6k
  for (;;) {
671
49.6k
    rv = sfparse_parser_dict(&sfp, &key, &val);
672
49.6k
    if (rv != 0) {
673
22.3k
      if (rv == SFPARSE_ERR_EOF) {
674
19.8k
        break;
675
19.8k
      }
676
677
2.51k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
678
22.3k
    }
679
680
27.2k
    if (key.len != 1) {
681
10.0k
      continue;
682
10.0k
    }
683
684
17.1k
    switch (key.base[0]) {
685
608
    case 'i':
686
608
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
687
23
        return NGHTTP2_ERR_INVALID_ARGUMENT;
688
23
      }
689
690
585
      pri.inc = val.boolean;
691
692
585
      break;
693
391
    case 'u':
694
391
      if (val.type != SFPARSE_TYPE_INTEGER ||
695
361
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
696
306
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
697
130
        return NGHTTP2_ERR_INVALID_ARGUMENT;
698
130
      }
699
700
261
      pri.urgency = (uint32_t)val.integer;
701
702
261
      break;
703
17.1k
    }
704
17.1k
  }
705
706
19.8k
  *dest = pri;
707
708
19.8k
  return 0;
709
22.5k
}