Coverage Report

Created: 2026-04-29 06:03

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
133k
static uint8_t downcase(uint8_t c) {
37
133k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
133k
}
39
40
14.4k
static int memieq(const void *a, const void *b, size_t n) {
41
14.4k
  size_t i;
42
14.4k
  const uint8_t *aa = a, *bb = b;
43
44
80.3k
  for (i = 0; i < n; ++i) {
45
66.9k
    if (downcase(aa[i]) != downcase(bb[i])) {
46
1.00k
      return 0;
47
1.00k
    }
48
66.9k
  }
49
13.4k
  return 1;
50
14.4k
}
51
52
#define lstrieq(A, B, N)                                                       \
53
2.01k
  (nghttp2_strlen_lit((A)) == (N) && memieq((A), (B), (N)))
54
55
4.41k
static int64_t parse_uint(const uint8_t *s, size_t len) {
56
4.41k
  int64_t n = 0;
57
4.41k
  size_t i;
58
4.41k
  if (len == 0) {
59
57
    return -1;
60
57
  }
61
21.3k
  for (i = 0; i < len; ++i) {
62
17.2k
    if ('0' <= s[i] && s[i] <= '9') {
63
16.9k
      if (n > INT64_MAX / 10) {
64
12
        return -1;
65
12
      }
66
16.9k
      n *= 10;
67
16.9k
      if (n > INT64_MAX - (s[i] - '0')) {
68
2
        return -1;
69
2
      }
70
16.9k
      n += s[i] - '0';
71
16.9k
      continue;
72
16.9k
    }
73
270
    return -1;
74
17.2k
  }
75
4.06k
  return n;
76
4.35k
}
77
78
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
79
49.2k
                               uint32_t flag) {
80
49.2k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
81
571
    return 0;
82
571
  }
83
48.6k
  stream->http_flags = stream->http_flags | flag;
84
48.6k
  return 1;
85
49.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
7.49k
static int check_path(nghttp2_stream *stream) {
99
7.49k
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
100
7.35k
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
101
18
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
102
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
103
7.49k
}
104
105
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
106
152k
                                  int trailer, int connect_protocol) {
107
152k
  nghttp2_extpri extpri;
108
109
152k
  if (nv->name->base[0] == ':') {
110
49.0k
    if (trailer ||
111
49.0k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
112
1.05k
      return NGHTTP2_ERR_HTTP_HEADER;
113
1.05k
    }
114
49.0k
  }
115
116
151k
  switch (nv->token) {
117
7.99k
  case NGHTTP2_TOKEN__AUTHORITY:
118
7.99k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
119
122
      return NGHTTP2_ERR_HTTP_HEADER;
120
122
    }
121
7.87k
    break;
122
14.1k
  case NGHTTP2_TOKEN__METHOD:
123
14.1k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
124
65
      return NGHTTP2_ERR_HTTP_HEADER;
125
65
    }
126
14.1k
    switch (nv->value->len) {
127
8.48k
    case 4:
128
8.48k
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
129
2
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
130
2
      }
131
8.48k
      break;
132
1.20k
    case 7:
133
1.20k
      switch (nv->value->base[6]) {
134
1.17k
      case 'T':
135
1.17k
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
136
1.16k
          if (stream->stream_id % 2 == 0) {
137
            /* we won't allow CONNECT for push */
138
0
            return NGHTTP2_ERR_HTTP_HEADER;
139
0
          }
140
1.16k
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
141
1.16k
        }
142
1.17k
        break;
143
1.17k
      case 'S':
144
12
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
145
2
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
146
2
        }
147
12
        break;
148
1.20k
      }
149
1.20k
      break;
150
14.1k
    }
151
14.1k
    break;
152
14.1k
  case NGHTTP2_TOKEN__PATH:
153
12.9k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
154
149
      return NGHTTP2_ERR_HTTP_HEADER;
155
149
    }
156
12.8k
    if (nv->value->base[0] == '/') {
157
12.4k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
158
12.4k
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
159
14
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
160
14
    }
161
12.8k
    break;
162
12.7k
  case NGHTTP2_TOKEN__SCHEME:
163
12.7k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
164
138
      return NGHTTP2_ERR_HTTP_HEADER;
165
138
    }
166
12.6k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
167
12.0k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
168
12.0k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
169
12.0k
    }
170
12.6k
    break;
171
17
  case NGHTTP2_TOKEN__PROTOCOL:
172
17
    if (!connect_protocol) {
173
7
      return NGHTTP2_ERR_HTTP_HEADER;
174
7
    }
175
176
10
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
177
2
      return NGHTTP2_ERR_HTTP_HEADER;
178
2
    }
179
8
    break;
180
1.30k
  case NGHTTP2_TOKEN_HOST:
181
1.30k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
182
95
      return NGHTTP2_ERR_HTTP_HEADER;
183
95
    }
184
1.21k
    break;
185
4.56k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
186
4.56k
    if (stream->content_length != -1) {
187
153
      return NGHTTP2_ERR_HTTP_HEADER;
188
153
    }
189
4.41k
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
190
4.41k
    if (stream->content_length == -1) {
191
341
      return NGHTTP2_ERR_HTTP_HEADER;
192
341
    }
193
4.06k
    break;
194
4.41k
  }
195
  /* disallowed header fields */
196
4.06k
  case NGHTTP2_TOKEN_CONNECTION:
197
59
  case NGHTTP2_TOKEN_KEEP_ALIVE:
198
65
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
199
139
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
200
145
  case NGHTTP2_TOKEN_UPGRADE:
201
145
    return NGHTTP2_ERR_HTTP_HEADER;
202
2.01k
  case NGHTTP2_TOKEN_TE:
203
2.01k
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
204
578
      return NGHTTP2_ERR_HTTP_HEADER;
205
578
    }
206
1.43k
    break;
207
56.1k
  case NGHTTP2_TOKEN_PRIORITY:
208
56.1k
    if (!trailer &&
209
        /* Do not parse the header field in PUSH_PROMISE. */
210
55.7k
        (stream->stream_id & 1) &&
211
55.7k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
212
55.2k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
213
55.2k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
214
55.2k
                                      nv->value->len) == 0) {
215
44.3k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
216
44.3k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
217
44.3k
      } else {
218
10.9k
        stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
219
10.9k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
220
10.9k
      }
221
55.2k
    }
222
56.1k
    break;
223
39.6k
  default:
224
39.6k
    if (nv->name->base[0] == ':') {
225
118
      return NGHTTP2_ERR_HTTP_HEADER;
226
118
    }
227
151k
  }
228
229
149k
  if (nv->name->base[0] != ':') {
230
102k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
231
102k
  }
232
233
149k
  return 0;
234
151k
}
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
13.7k
static int check_scheme(const uint8_t *value, size_t len) {
317
13.7k
  const uint8_t *last;
318
13.7k
  if (len == 0) {
319
54
    return 0;
320
54
  }
321
322
13.7k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
323
150
    return 0;
324
150
  }
325
326
13.5k
  last = value + len;
327
13.5k
  ++value;
328
329
109k
  for (; value != last; ++value) {
330
96.4k
    if (!(('A' <= *value && *value <= 'Z') ||
331
87.6k
          ('a' <= *value && *value <= 'z') ||
332
29.1k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
333
731
          *value == '.')) {
334
471
      return 0;
335
471
    }
336
96.4k
  }
337
13.1k
  return 1;
338
13.5k
}
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
9.73k
static int check_authority(const uint8_t *value, size_t len) {
419
9.73k
  const uint8_t *last;
420
80.6k
  for (last = value + len; value != last; ++value) {
421
71.3k
    if (!VALID_AUTHORITY_CHARS[*value]) {
422
385
      return 0;
423
385
    }
424
71.3k
  }
425
9.34k
  return 1;
426
9.73k
}
427
428
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
429
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
430
174k
                           int trailer) {
431
174k
  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
174k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
441
17.2k
    size_t i;
442
17.2k
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
443
76
      return NGHTTP2_ERR_HTTP_HEADER;
444
76
    }
445
    /* header field name must be lower-cased without exception */
446
108k
    for (i = 0; i < nv->name->len; ++i) {
447
91.7k
      uint8_t c = nv->name->base[i];
448
91.7k
      if ('A' <= c && c <= 'Z') {
449
479
        return NGHTTP2_ERR_HTTP_HEADER;
450
479
      }
451
91.7k
    }
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
16.6k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
456
16.6k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
457
17.1k
  }
458
459
156k
  switch (nv->token) {
460
14.5k
  case NGHTTP2_TOKEN__METHOD:
461
14.5k
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
462
14.5k
    break;
463
13.1k
  case NGHTTP2_TOKEN__PATH:
464
13.1k
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
465
13.1k
    break;
466
8.11k
  case NGHTTP2_TOKEN__AUTHORITY:
467
9.73k
  case NGHTTP2_TOKEN_HOST:
468
9.73k
    if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
469
9.73k
      rv = check_authority(nv->value->base, nv->value->len);
470
9.73k
    } 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
9.73k
    break;
478
13.7k
  case NGHTTP2_TOKEN__SCHEME:
479
13.7k
    rv = check_scheme(nv->value->base, nv->value->len);
480
13.7k
    break;
481
17
  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
17
    if ((stream->flags &
486
17
         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
105k
  default:
493
105k
    if (stream->flags &
494
105k
        NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
495
0
      rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
496
105k
    } else {
497
105k
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
498
105k
    }
499
156k
  }
500
501
156k
  if (rv == 0) {
502
4.17k
    assert(nv->name->len > 0);
503
4.17k
    if (nv->name->base[0] == ':') {
504
1.12k
      return NGHTTP2_ERR_HTTP_HEADER;
505
1.12k
    }
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
3.04k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
510
3.04k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
511
4.17k
  }
512
513
152k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
514
152k
    return http_request_on_header(stream, nv, trailer,
515
152k
                                  session->server &&
516
152k
                                    session->pending_enable_connect_protocol);
517
152k
  }
518
519
0
  return http_response_on_header(stream, nv, trailer);
520
152k
}
521
522
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
523
12.2k
                                    nghttp2_frame *frame) {
524
12.2k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
525
12.2k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
526
1.16k
    if ((stream->http_flags &
527
1.16k
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
528
1.15k
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
529
11
      return -1;
530
11
    }
531
1.15k
    stream->content_length = -1;
532
11.0k
  } else {
533
11.0k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
534
11.0k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
535
9.75k
        (stream->http_flags &
536
9.75k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
537
3.55k
      return -1;
538
3.55k
    }
539
7.49k
    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
7.49k
    if (!check_path(stream)) {
545
18
      return -1;
546
18
    }
547
7.49k
  }
548
549
8.62k
  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
8.62k
  return 0;
557
12.2k
}
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
56
                                    nghttp2_frame *frame) {
588
56
  (void)stream;
589
590
56
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
591
13
    return -1;
592
13
  }
593
594
43
  return 0;
595
56
}
596
597
3.80k
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
598
3.80k
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
599
0
    return -1;
600
0
  }
601
602
3.80k
  if (stream->content_length != -1 &&
603
281
      stream->content_length != stream->recv_content_length) {
604
229
    return -1;
605
229
  }
606
607
3.57k
  return 0;
608
3.80k
}
609
610
2.10k
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
611
2.10k
  stream->recv_content_length += (int64_t)n;
612
613
2.10k
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
614
2.10k
      (stream->content_length != -1 &&
615
406
       stream->recv_content_length > stream->content_length)) {
616
24
    return -1;
617
24
  }
618
619
2.07k
  return 0;
620
2.10k
}
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
55.2k
                                size_t valuelen) {
662
55.2k
  nghttp2_extpri pri = *dest;
663
55.2k
  sfparse_parser sfp;
664
55.2k
  sfparse_vec key;
665
55.2k
  sfparse_value val;
666
55.2k
  int rv;
667
668
55.2k
  sfparse_parser_init(&sfp, value, valuelen);
669
670
115k
  for (;;) {
671
115k
    rv = sfparse_parser_dict(&sfp, &key, &val);
672
115k
    if (rv != 0) {
673
54.8k
      if (rv == SFPARSE_ERR_EOF) {
674
44.3k
        break;
675
44.3k
      }
676
677
10.5k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
678
54.8k
    }
679
680
61.0k
    if (key.len != 1) {
681
14.2k
      continue;
682
14.2k
    }
683
684
46.7k
    switch (key.base[0]) {
685
1.35k
    case 'i':
686
1.35k
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
687
118
        return NGHTTP2_ERR_INVALID_ARGUMENT;
688
118
      }
689
690
1.23k
      pri.inc = val.boolean;
691
692
1.23k
      break;
693
899
    case 'u':
694
899
      if (val.type != SFPARSE_TYPE_INTEGER ||
695
824
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
696
680
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
697
305
        return NGHTTP2_ERR_INVALID_ARGUMENT;
698
305
      }
699
700
594
      pri.urgency = (uint32_t)val.integer;
701
702
594
      break;
703
46.7k
    }
704
46.7k
  }
705
706
44.3k
  *dest = pri;
707
708
44.3k
  return 0;
709
55.2k
}