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
100k
static uint8_t downcase(uint8_t c) {
37
100k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
100k
}
39
40
10.2k
static int memieq(const void *a, const void *b, size_t n) {
41
10.2k
  size_t i;
42
10.2k
  const uint8_t *aa = a, *bb = b;
43
44
59.9k
  for (i = 0; i < n; ++i) {
45
50.2k
    if (downcase(aa[i]) != downcase(bb[i])) {
46
591
      return 0;
47
591
    }
48
50.2k
  }
49
9.67k
  return 1;
50
10.2k
}
51
52
#define lstrieq(A, B, N)                                                       \
53
1.76k
  (nghttp2_strlen_lit((A)) == (N) && memieq((A), (B), (N)))
54
55
997
static int64_t parse_uint(const uint8_t *s, size_t len) {
56
997
  int64_t n = 0;
57
997
  size_t i;
58
997
  if (len == 0) {
59
8
    return -1;
60
8
  }
61
11.4k
  for (i = 0; i < len; ++i) {
62
10.4k
    if ('0' <= s[i] && s[i] <= '9') {
63
10.4k
      if (n > INT64_MAX / 10) {
64
8
        return -1;
65
8
      }
66
10.4k
      n *= 10;
67
10.4k
      if (n > INT64_MAX - (s[i] - '0')) {
68
2
        return -1;
69
2
      }
70
10.4k
      n += s[i] - '0';
71
10.4k
      continue;
72
10.4k
    }
73
43
    return -1;
74
10.4k
  }
75
936
  return n;
76
989
}
77
78
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
79
35.3k
                               uint32_t flag) {
80
35.3k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
81
133
    return 0;
82
133
  }
83
35.2k
  stream->http_flags = stream->http_flags | flag;
84
35.2k
  return 1;
85
35.3k
}
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
8.20k
static int check_path(nghttp2_stream *stream) {
99
8.20k
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
100
8.04k
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
101
3
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
102
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
103
8.20k
}
104
105
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
106
117k
                                  int trailer, int connect_protocol) {
107
117k
  nghttp2_extpri extpri;
108
109
117k
  if (nv->name->base[0] == ':') {
110
34.2k
    if (trailer ||
111
34.1k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
112
46
      return NGHTTP2_ERR_HTTP_HEADER;
113
46
    }
114
34.2k
  }
115
116
117k
  switch (nv->token) {
117
7.87k
  case NGHTTP2_TOKEN__AUTHORITY:
118
7.87k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
119
22
      return NGHTTP2_ERR_HTTP_HEADER;
120
22
    }
121
7.85k
    break;
122
9.15k
  case NGHTTP2_TOKEN__METHOD:
123
9.15k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
124
17
      return NGHTTP2_ERR_HTTP_HEADER;
125
17
    }
126
9.14k
    switch (nv->value->len) {
127
3.63k
    case 4:
128
3.63k
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
129
2
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
130
2
      }
131
3.63k
      break;
132
797
    case 7:
133
797
      switch (nv->value->base[6]) {
134
787
      case 'T':
135
787
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
136
785
          if (stream->stream_id % 2 == 0) {
137
            /* we won't allow CONNECT for push */
138
0
            return NGHTTP2_ERR_HTTP_HEADER;
139
0
          }
140
785
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
141
785
        }
142
787
        break;
143
787
      case 'S':
144
8
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
145
2
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
146
2
        }
147
8
        break;
148
797
      }
149
797
      break;
150
9.14k
    }
151
9.14k
    break;
152
9.14k
  case NGHTTP2_TOKEN__PATH:
153
8.37k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
154
24
      return NGHTTP2_ERR_HTTP_HEADER;
155
24
    }
156
8.34k
    if (nv->value->base[0] == '/') {
157
8.29k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
158
8.29k
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
159
2
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
160
2
    }
161
8.34k
    break;
162
8.70k
  case NGHTTP2_TOKEN__SCHEME:
163
8.70k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
164
51
      return NGHTTP2_ERR_HTTP_HEADER;
165
51
    }
166
8.65k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
167
8.15k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
168
8.15k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
169
8.15k
    }
170
8.65k
    break;
171
13
  case NGHTTP2_TOKEN__PROTOCOL:
172
13
    if (!connect_protocol) {
173
2
      return NGHTTP2_ERR_HTTP_HEADER;
174
2
    }
175
176
11
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
177
2
      return NGHTTP2_ERR_HTTP_HEADER;
178
2
    }
179
9
    break;
180
1.27k
  case NGHTTP2_TOKEN_HOST:
181
1.27k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
182
17
      return NGHTTP2_ERR_HTTP_HEADER;
183
17
    }
184
1.25k
    break;
185
1.25k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
186
1.09k
    if (stream->content_length != -1) {
187
93
      return NGHTTP2_ERR_HTTP_HEADER;
188
93
    }
189
997
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
190
997
    if (stream->content_length == -1) {
191
61
      return NGHTTP2_ERR_HTTP_HEADER;
192
61
    }
193
936
    break;
194
997
  }
195
  /* disallowed header fields */
196
936
  case NGHTTP2_TOKEN_CONNECTION:
197
7
  case NGHTTP2_TOKEN_KEEP_ALIVE:
198
12
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
199
29
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
200
32
  case NGHTTP2_TOKEN_UPGRADE:
201
32
    return NGHTTP2_ERR_HTTP_HEADER;
202
1.76k
  case NGHTTP2_TOKEN_TE:
203
1.76k
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
204
243
      return NGHTTP2_ERR_HTTP_HEADER;
205
243
    }
206
1.51k
    break;
207
50.2k
  case NGHTTP2_TOKEN_PRIORITY:
208
50.2k
    if (!trailer &&
209
        /* Do not parse the header field in PUSH_PROMISE. */
210
49.8k
        (stream->stream_id & 1) &&
211
49.8k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
212
49.1k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
213
49.1k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
214
49.1k
                                      nv->value->len) == 0) {
215
42.6k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
216
42.6k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
217
42.6k
      } else {
218
6.52k
        stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
219
6.52k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
220
6.52k
      }
221
49.1k
    }
222
50.2k
    break;
223
28.5k
  default:
224
28.5k
    if (nv->name->base[0] == ':') {
225
26
      return NGHTTP2_ERR_HTTP_HEADER;
226
26
    }
227
117k
  }
228
229
116k
  if (nv->name->base[0] != ':') {
230
82.4k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
231
82.4k
  }
232
233
116k
  return 0;
234
117k
}
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
8.80k
static int check_scheme(const uint8_t *value, size_t len) {
317
8.80k
  const uint8_t *last;
318
8.80k
  if (len == 0) {
319
7
    return 0;
320
7
  }
321
322
8.79k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
323
35
    return 0;
324
35
  }
325
326
8.75k
  last = value + len;
327
8.75k
  ++value;
328
329
74.4k
  for (; value != last; ++value) {
330
65.7k
    if (!(('A' <= *value && *value <= 'Z') ||
331
60.0k
          ('a' <= *value && *value <= 'z') ||
332
21.0k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
333
288
          *value == '.')) {
334
45
      return 0;
335
45
    }
336
65.7k
  }
337
8.71k
  return 1;
338
8.75k
}
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.53k
static int check_authority(const uint8_t *value, size_t len) {
419
9.53k
  const uint8_t *last;
420
85.3k
  for (last = value + len; value != last; ++value) {
421
76.1k
    if (!VALID_AUTHORITY_CHARS[*value]) {
422
384
      return 0;
423
384
    }
424
76.1k
  }
425
9.15k
  return 1;
426
9.53k
}
427
428
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
429
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
430
136k
                           int trailer) {
431
136k
  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
136k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
441
15.6k
    size_t i;
442
15.6k
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
443
38
      return NGHTTP2_ERR_HTTP_HEADER;
444
38
    }
445
    /* header field name must be lower-cased without exception */
446
104k
    for (i = 0; i < nv->name->len; ++i) {
447
89.3k
      uint8_t c = nv->name->base[i];
448
89.3k
      if ('A' <= c && c <= 'Z') {
449
389
        return NGHTTP2_ERR_HTTP_HEADER;
450
389
      }
451
89.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
15.2k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
456
15.2k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
457
15.6k
  }
458
459
120k
  switch (nv->token) {
460
9.18k
  case NGHTTP2_TOKEN__METHOD:
461
9.18k
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
462
9.18k
    break;
463
8.38k
  case NGHTTP2_TOKEN__PATH:
464
8.38k
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
465
8.38k
    break;
466
7.89k
  case NGHTTP2_TOKEN__AUTHORITY:
467
9.53k
  case NGHTTP2_TOKEN_HOST:
468
9.53k
    if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
469
9.53k
      rv = check_authority(nv->value->base, nv->value->len);
470
9.53k
    } 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.53k
    break;
478
8.80k
  case NGHTTP2_TOKEN__SCHEME:
479
8.80k
    rv = check_scheme(nv->value->base, nv->value->len);
480
8.80k
    break;
481
13
  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
13
    if ((stream->flags &
486
13
         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
84.5k
  default:
493
84.5k
    if (stream->flags &
494
84.5k
        NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
495
0
      rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
496
84.5k
    } else {
497
84.5k
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
498
84.5k
    }
499
120k
  }
500
501
120k
  if (rv == 0) {
502
3.34k
    assert(nv->name->len > 0);
503
3.34k
    if (nv->name->base[0] == ':') {
504
139
      return NGHTTP2_ERR_HTTP_HEADER;
505
139
    }
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.21k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
510
3.21k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
511
3.34k
  }
512
513
117k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
514
117k
    return http_request_on_header(stream, nv, trailer,
515
117k
                                  session->server &&
516
117k
                                    session->pending_enable_connect_protocol);
517
117k
  }
518
519
0
  return http_response_on_header(stream, nv, trailer);
520
117k
}
521
522
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
523
9.17k
                                    nghttp2_frame *frame) {
524
9.17k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
525
9.17k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
526
781
    if ((stream->http_flags &
527
781
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
528
779
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
529
4
      return -1;
530
4
    }
531
777
    stream->content_length = -1;
532
8.39k
  } else {
533
8.39k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
534
8.39k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
535
8.20k
        (stream->http_flags &
536
8.20k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
537
187
      return -1;
538
187
    }
539
8.20k
    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
8.20k
    if (!check_path(stream)) {
545
3
      return -1;
546
3
    }
547
8.20k
  }
548
549
8.98k
  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.98k
  return 0;
557
9.17k
}
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
61
                                    nghttp2_frame *frame) {
588
61
  (void)stream;
589
590
61
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
591
9
    return -1;
592
9
  }
593
594
52
  return 0;
595
61
}
596
597
4.82k
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
598
4.82k
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
599
0
    return -1;
600
0
  }
601
602
4.82k
  if (stream->content_length != -1 &&
603
243
      stream->content_length != stream->recv_content_length) {
604
203
    return -1;
605
203
  }
606
607
4.61k
  return 0;
608
4.82k
}
609
610
1.74k
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
611
1.74k
  stream->recv_content_length += (int64_t)n;
612
613
1.74k
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
614
1.74k
      (stream->content_length != -1 &&
615
389
       stream->recv_content_length > stream->content_length)) {
616
11
    return -1;
617
11
  }
618
619
1.73k
  return 0;
620
1.74k
}
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
49.1k
                                size_t valuelen) {
662
49.1k
  nghttp2_extpri pri = *dest;
663
49.1k
  sfparse_parser sfp;
664
49.1k
  sfparse_vec key;
665
49.1k
  sfparse_value val;
666
49.1k
  int rv;
667
668
49.1k
  sfparse_parser_init(&sfp, value, valuelen);
669
670
104k
  for (;;) {
671
104k
    rv = sfparse_parser_dict(&sfp, &key, &val);
672
104k
    if (rv != 0) {
673
48.8k
      if (rv == SFPARSE_ERR_EOF) {
674
42.6k
        break;
675
42.6k
      }
676
677
6.21k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
678
48.8k
    }
679
680
55.5k
    if (key.len != 1) {
681
15.1k
      continue;
682
15.1k
    }
683
684
40.3k
    switch (key.base[0]) {
685
1.13k
    case 'i':
686
1.13k
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
687
46
        return NGHTTP2_ERR_INVALID_ARGUMENT;
688
46
      }
689
690
1.08k
      pri.inc = val.boolean;
691
692
1.08k
      break;
693
789
    case 'u':
694
789
      if (val.type != SFPARSE_TYPE_INTEGER ||
695
733
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
696
607
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
697
266
        return NGHTTP2_ERR_INVALID_ARGUMENT;
698
266
      }
699
700
523
      pri.urgency = (uint32_t)val.integer;
701
702
523
      break;
703
40.3k
    }
704
40.3k
  }
705
706
42.6k
  *dest = pri;
707
708
42.6k
  return 0;
709
49.1k
}