Coverage Report

Created: 2025-08-03 06:38

/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.4k
static uint8_t downcase(uint8_t c) {
37
37.4k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
37.4k
}
39
40
4.13k
static int memieq(const void *a, const void *b, size_t n) {
41
4.13k
  size_t i;
42
4.13k
  const uint8_t *aa = a, *bb = b;
43
44
20.2k
  for (i = 0; i < n; ++i) {
45
18.7k
    if (downcase(aa[i]) != downcase(bb[i])) {
46
2.64k
      return 0;
47
2.64k
    }
48
18.7k
  }
49
1.49k
  return 1;
50
4.13k
}
51
52
1.93k
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
53
54
727
static int64_t parse_uint(const uint8_t *s, size_t len) {
55
727
  int64_t n = 0;
56
727
  size_t i;
57
727
  if (len == 0) {
58
78
    return -1;
59
78
  }
60
8.01k
  for (i = 0; i < len; ++i) {
61
7.53k
    if ('0' <= s[i] && s[i] <= '9') {
62
7.42k
      if (n > INT64_MAX / 10) {
63
1
        return -1;
64
1
      }
65
7.42k
      n *= 10;
66
7.42k
      if (n > INT64_MAX - (s[i] - '0')) {
67
66
        return -1;
68
66
      }
69
7.36k
      n += s[i] - '0';
70
7.36k
      continue;
71
7.42k
    }
72
105
    return -1;
73
7.53k
  }
74
477
  return n;
75
649
}
76
77
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
78
7.12k
                               uint32_t flag) {
79
7.12k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
80
665
    return 0;
81
665
  }
82
6.45k
  stream->http_flags = stream->http_flags | flag;
83
6.45k
  return 1;
84
7.12k
}
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
627
static int check_path(nghttp2_stream *stream) {
98
627
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
99
627
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
100
613
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
101
10
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
102
627
}
103
104
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
105
54.0k
                                  int trailer, int connect_protocol) {
106
54.0k
  nghttp2_extpri extpri;
107
108
54.0k
  if (nv->name->base[0] == ':') {
109
7.43k
    if (trailer ||
110
7.43k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
111
374
      return NGHTTP2_ERR_HTTP_HEADER;
112
374
    }
113
7.43k
  }
114
115
53.6k
  switch (nv->token) {
116
1.23k
  case NGHTTP2_TOKEN__AUTHORITY:
117
1.23k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
118
241
      return NGHTTP2_ERR_HTTP_HEADER;
119
241
    }
120
998
    break;
121
1.63k
  case NGHTTP2_TOKEN__METHOD:
122
1.63k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
123
38
      return NGHTTP2_ERR_HTTP_HEADER;
124
38
    }
125
1.59k
    switch (nv->value->len) {
126
835
    case 4:
127
835
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
128
66
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
129
66
      }
130
835
      break;
131
562
    case 7:
132
562
      switch (nv->value->base[6]) {
133
336
      case 'T':
134
336
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
135
229
          if (stream->stream_id % 2 == 0) {
136
            /* we won't allow CONNECT for push */
137
0
            return NGHTTP2_ERR_HTTP_HEADER;
138
0
          }
139
229
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
140
229
        }
141
336
        break;
142
336
      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
562
      }
148
562
      break;
149
1.59k
    }
150
1.59k
    break;
151
1.59k
  case NGHTTP2_TOKEN__PATH:
152
1.30k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
153
161
      return NGHTTP2_ERR_HTTP_HEADER;
154
161
    }
155
1.14k
    if (nv->value->base[0] == '/') {
156
803
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
157
803
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
158
66
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
159
66
    }
160
1.14k
    break;
161
2.57k
  case NGHTTP2_TOKEN__SCHEME:
162
2.57k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
163
71
      return NGHTTP2_ERR_HTTP_HEADER;
164
71
    }
165
2.50k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
166
2.50k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
167
880
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
168
880
    }
169
2.50k
    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
268
  case NGHTTP2_TOKEN_HOST:
180
268
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
181
109
      return NGHTTP2_ERR_HTTP_HEADER;
182
109
    }
183
159
    break;
184
797
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
185
797
    if (stream->content_length != -1) {
186
70
      return NGHTTP2_ERR_HTTP_HEADER;
187
70
    }
188
727
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
189
727
    if (stream->content_length == -1) {
190
250
      return NGHTTP2_ERR_HTTP_HEADER;
191
250
    }
192
477
    break;
193
727
  }
194
  /* disallowed header fields */
195
477
  case NGHTTP2_TOKEN_CONNECTION:
196
114
  case NGHTTP2_TOKEN_KEEP_ALIVE:
197
187
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
198
294
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
199
371
  case NGHTTP2_TOKEN_UPGRADE:
200
371
    return NGHTTP2_ERR_HTTP_HEADER;
201
1.93k
  case NGHTTP2_TOKEN_TE:
202
1.93k
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
203
1.31k
      return NGHTTP2_ERR_HTTP_HEADER;
204
1.31k
    }
205
614
    break;
206
37.1k
  case NGHTTP2_TOKEN_PRIORITY:
207
37.1k
    if (!trailer &&
208
        /* Do not parse the header field in PUSH_PROMISE. */
209
37.1k
        (stream->stream_id & 1) &&
210
37.1k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
211
36.7k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
212
36.7k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
213
36.7k
                                      nv->value->len) == 0) {
214
23.8k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
215
23.8k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
216
23.8k
      } else {
217
12.8k
        stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
218
12.8k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
219
12.8k
      }
220
36.7k
    }
221
37.1k
    break;
222
6.27k
  default:
223
6.27k
    if (nv->name->base[0] == ':') {
224
166
      return NGHTTP2_ERR_HTTP_HEADER;
225
166
    }
226
53.6k
  }
227
228
50.7k
  if (nv->name->base[0] != ':') {
229
44.4k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
230
44.4k
  }
231
232
50.7k
  return 0;
233
53.6k
}
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
132
    return 0;
319
132
  }
320
321
3.40k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
322
399
    return 0;
323
399
  }
324
325
3.00k
  last = value + len;
326
3.00k
  ++value;
327
328
44.3k
  for (; value != last; ++value) {
329
41.7k
    if (!(('A' <= *value && *value <= 'Z') ||
330
41.7k
          ('a' <= *value && *value <= 'z') ||
331
41.7k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
332
41.7k
          *value == '.')) {
333
393
      return 0;
334
393
    }
335
41.7k
  }
336
2.61k
  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.84k
static int check_authority(const uint8_t *value, size_t len) {
418
1.84k
  const uint8_t *last;
419
36.1k
  for (last = value + len; value != last; ++value) {
420
34.4k
    if (!VALID_AUTHORITY_CHARS[*value]) {
421
106
      return 0;
422
106
    }
423
34.4k
  }
424
1.74k
  return 1;
425
1.84k
}
426
427
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
428
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
429
58.0k
                           int trailer) {
430
58.0k
  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.0k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
440
2.13k
    size_t i;
441
2.13k
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
442
96
      return NGHTTP2_ERR_HTTP_HEADER;
443
96
    }
444
    /* header field name must be lower-cased without exception */
445
11.5k
    for (i = 0; i < nv->name->len; ++i) {
446
10.0k
      uint8_t c = nv->name->base[i];
447
10.0k
      if ('A' <= c && c <= 'Z') {
448
505
        return NGHTTP2_ERR_HTTP_HEADER;
449
505
      }
450
10.0k
    }
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.53k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
455
1.53k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
456
2.03k
  }
457
458
55.9k
  switch (nv->token) {
459
1.89k
  case NGHTTP2_TOKEN__METHOD:
460
1.89k
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
461
1.89k
    break;
462
1.42k
  case NGHTTP2_TOKEN__PATH:
463
1.42k
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
464
1.42k
    break;
465
1.51k
  case NGHTTP2_TOKEN__AUTHORITY:
466
1.84k
  case NGHTTP2_TOKEN_HOST:
467
1.84k
    if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
468
1.84k
      rv = check_authority(nv->value->base, nv->value->len);
469
1.84k
    } 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.84k
    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.2k
  default:
492
47.2k
    if (stream->flags &
493
47.2k
        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.2k
    } else {
496
47.2k
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
497
47.2k
    }
498
55.9k
  }
499
500
55.9k
  if (rv == 0) {
501
1.91k
    assert(nv->name->len > 0);
502
1.91k
    if (nv->name->base[0] == ':') {
503
1.44k
      return NGHTTP2_ERR_HTTP_HEADER;
504
1.44k
    }
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
475
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
509
475
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
510
1.91k
  }
511
512
54.0k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
513
54.0k
    return http_request_on_header(stream, nv, trailer,
514
54.0k
                                  session->server &&
515
54.0k
                                    session->pending_enable_connect_protocol);
516
54.0k
  }
517
518
0
  return http_response_on_header(stream, nv, trailer);
519
54.0k
}
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
220
    if ((stream->http_flags &
526
220
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
527
220
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
528
80
      return -1;
529
80
    }
530
140
    stream->content_length = -1;
531
15.9k
  } else {
532
15.9k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
533
15.9k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
534
15.9k
        (stream->http_flags &
535
15.3k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
536
15.3k
      return -1;
537
15.3k
    }
538
637
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
539
637
        ((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
627
    if (!check_path(stream)) {
544
10
      return -1;
545
10
    }
546
627
  }
547
548
757
  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
757
  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
55
                                    nghttp2_frame *frame) {
587
55
  (void)stream;
588
589
55
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
590
48
    return -1;
591
48
  }
592
593
7
  return 0;
594
55
}
595
596
226
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
597
226
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
598
0
    return -1;
599
0
  }
600
601
226
  if (stream->content_length != -1 &&
602
226
      stream->content_length != stream->recv_content_length) {
603
129
    return -1;
604
129
  }
605
606
97
  return 0;
607
226
}
608
609
1.98k
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
610
1.98k
  stream->recv_content_length += (int64_t)n;
611
612
1.98k
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
613
1.98k
      (stream->content_length != -1 &&
614
1.98k
       stream->recv_content_length > stream->content_length)) {
615
4
    return -1;
616
4
  }
617
618
1.98k
  return 0;
619
1.98k
}
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.7k
                                size_t valuelen) {
661
36.7k
  nghttp2_extpri pri = *dest;
662
36.7k
  sfparse_parser sfp;
663
36.7k
  sfparse_vec key;
664
36.7k
  sfparse_value val;
665
36.7k
  int rv;
666
667
36.7k
  sfparse_parser_init(&sfp, value, valuelen);
668
669
65.6k
  for (;;) {
670
65.6k
    rv = sfparse_parser_dict(&sfp, &key, &val);
671
65.6k
    if (rv != 0) {
672
36.3k
      if (rv == SFPARSE_ERR_EOF) {
673
23.8k
        break;
674
23.8k
      }
675
676
12.5k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
677
36.3k
    }
678
679
29.2k
    if (key.len != 1) {
680
5.53k
      continue;
681
5.53k
    }
682
683
23.7k
    switch (key.base[0]) {
684
522
    case 'i':
685
522
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
686
73
        return NGHTTP2_ERR_INVALID_ARGUMENT;
687
73
      }
688
689
449
      pri.inc = val.boolean;
690
691
449
      break;
692
798
    case 'u':
693
798
      if (val.type != SFPARSE_TYPE_INTEGER ||
694
798
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
695
798
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
696
262
        return NGHTTP2_ERR_INVALID_ARGUMENT;
697
262
      }
698
699
536
      pri.urgency = (uint32_t)val.integer;
700
701
536
      break;
702
23.7k
    }
703
23.7k
  }
704
705
23.8k
  *dest = pri;
706
707
23.8k
  return 0;
708
36.7k
}