Coverage Report

Created: 2025-07-18 06:25

/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
36.6k
static uint8_t downcase(uint8_t c) {
37
36.6k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
36.6k
}
39
40
4.09k
static int memieq(const void *a, const void *b, size_t n) {
41
4.09k
  size_t i;
42
4.09k
  const uint8_t *aa = a, *bb = b;
43
44
19.7k
  for (i = 0; i < n; ++i) {
45
18.3k
    if (downcase(aa[i]) != downcase(bb[i])) {
46
2.63k
      return 0;
47
2.63k
    }
48
18.3k
  }
49
1.45k
  return 1;
50
4.09k
}
51
52
1.88k
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
53
54
736
static int64_t parse_uint(const uint8_t *s, size_t len) {
55
736
  int64_t n = 0;
56
736
  size_t i;
57
736
  if (len == 0) {
58
79
    return -1;
59
79
  }
60
8.10k
  for (i = 0; i < len; ++i) {
61
7.62k
    if ('0' <= s[i] && s[i] <= '9') {
62
7.51k
      if (n > INT64_MAX / 10) {
63
0
        return -1;
64
0
      }
65
7.51k
      n *= 10;
66
7.51k
      if (n > INT64_MAX - (s[i] - '0')) {
67
66
        return -1;
68
66
      }
69
7.45k
      n += s[i] - '0';
70
7.45k
      continue;
71
7.51k
    }
72
107
    return -1;
73
7.62k
  }
74
484
  return n;
75
657
}
76
77
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
78
7.09k
                               uint32_t flag) {
79
7.09k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
80
722
    return 0;
81
722
  }
82
6.36k
  stream->http_flags = stream->http_flags | flag;
83
6.36k
  return 1;
84
7.09k
}
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
625
static int check_path(nghttp2_stream *stream) {
98
625
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
99
625
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
100
602
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
101
10
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
102
625
}
103
104
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
105
54.6k
                                  int trailer, int connect_protocol) {
106
54.6k
  nghttp2_extpri extpri;
107
108
54.6k
  if (nv->name->base[0] == ':') {
109
7.52k
    if (trailer ||
110
7.52k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
111
457
      return NGHTTP2_ERR_HTTP_HEADER;
112
457
    }
113
7.52k
  }
114
115
54.1k
  switch (nv->token) {
116
1.23k
  case NGHTTP2_TOKEN__AUTHORITY:
117
1.23k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
118
276
      return NGHTTP2_ERR_HTTP_HEADER;
119
276
    }
120
963
    break;
121
1.65k
  case NGHTTP2_TOKEN__METHOD:
122
1.65k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
123
46
      return NGHTTP2_ERR_HTTP_HEADER;
124
46
    }
125
1.60k
    switch (nv->value->len) {
126
850
    case 4:
127
850
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
128
66
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
129
66
      }
130
850
      break;
131
557
    case 7:
132
557
      switch (nv->value->base[6]) {
133
317
      case 'T':
134
317
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
135
210
          if (stream->stream_id % 2 == 0) {
136
            /* we won't allow CONNECT for push */
137
0
            return NGHTTP2_ERR_HTTP_HEADER;
138
0
          }
139
210
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
140
210
        }
141
317
        break;
142
317
      case 'S':
143
148
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
144
66
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
145
66
        }
146
148
        break;
147
557
      }
148
557
      break;
149
1.60k
    }
150
1.60k
    break;
151
1.60k
  case NGHTTP2_TOKEN__PATH:
152
1.29k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
153
161
      return NGHTTP2_ERR_HTTP_HEADER;
154
161
    }
155
1.13k
    if (nv->value->base[0] == '/') {
156
769
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
157
769
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
158
76
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
159
76
    }
160
1.13k
    break;
161
2.55k
  case NGHTTP2_TOKEN__SCHEME:
162
2.55k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
163
82
      return NGHTTP2_ERR_HTTP_HEADER;
164
82
    }
165
2.47k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
166
2.47k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
167
853
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
168
853
    }
169
2.47k
    break;
170
142
  case NGHTTP2_TOKEN__PROTOCOL:
171
142
    if (!connect_protocol) {
172
40
      return NGHTTP2_ERR_HTTP_HEADER;
173
40
    }
174
175
102
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
176
45
      return NGHTTP2_ERR_HTTP_HEADER;
177
45
    }
178
57
    break;
179
250
  case NGHTTP2_TOKEN_HOST:
180
250
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
181
112
      return NGHTTP2_ERR_HTTP_HEADER;
182
112
    }
183
138
    break;
184
806
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
185
806
    if (stream->content_length != -1) {
186
70
      return NGHTTP2_ERR_HTTP_HEADER;
187
70
    }
188
736
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
189
736
    if (stream->content_length == -1) {
190
252
      return NGHTTP2_ERR_HTTP_HEADER;
191
252
    }
192
484
    break;
193
736
  }
194
  /* disallowed header fields */
195
484
  case NGHTTP2_TOKEN_CONNECTION:
196
114
  case NGHTTP2_TOKEN_KEEP_ALIVE:
197
187
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
198
289
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
199
360
  case NGHTTP2_TOKEN_UPGRADE:
200
360
    return NGHTTP2_ERR_HTTP_HEADER;
201
1.88k
  case NGHTTP2_TOKEN_TE:
202
1.88k
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
203
1.28k
      return NGHTTP2_ERR_HTTP_HEADER;
204
1.28k
    }
205
603
    break;
206
38.2k
  case NGHTTP2_TOKEN_PRIORITY:
207
38.2k
    if (!trailer &&
208
        /* Do not parse the header field in PUSH_PROMISE. */
209
38.2k
        (stream->stream_id & 1) &&
210
38.2k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
211
37.4k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
212
37.4k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
213
37.4k
                                      nv->value->len) == 0) {
214
24.5k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
215
24.5k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
216
24.5k
      } else {
217
12.9k
        stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
218
12.9k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
219
12.9k
      }
220
37.4k
    }
221
38.2k
    break;
222
5.78k
  default:
223
5.78k
    if (nv->name->base[0] == ':') {
224
185
      return NGHTTP2_ERR_HTTP_HEADER;
225
185
    }
226
54.1k
  }
227
228
51.2k
  if (nv->name->base[0] != ':') {
229
45.0k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
230
45.0k
  }
231
232
51.2k
  return 0;
233
54.1k
}
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.48k
static int check_scheme(const uint8_t *value, size_t len) {
316
3.48k
  const uint8_t *last;
317
3.48k
  if (len == 0) {
318
127
    return 0;
319
127
  }
320
321
3.35k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
322
274
    return 0;
323
274
  }
324
325
3.08k
  last = value + len;
326
3.08k
  ++value;
327
328
49.2k
  for (; value != last; ++value) {
329
46.6k
    if (!(('A' <= *value && *value <= 'Z') ||
330
46.6k
          ('a' <= *value && *value <= 'z') ||
331
46.6k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
332
46.6k
          *value == '.')) {
333
438
      return 0;
334
438
    }
335
46.6k
  }
336
2.64k
  return 1;
337
3.08k
}
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
34.9k
  for (last = value + len; value != last; ++value) {
420
33.1k
    if (!VALID_AUTHORITY_CHARS[*value]) {
421
103
      return 0;
422
103
    }
423
33.1k
  }
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.6k
                           int trailer) {
430
58.6k
  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.6k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
440
2.16k
    size_t i;
441
2.16k
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
442
111
      return NGHTTP2_ERR_HTTP_HEADER;
443
111
    }
444
    /* header field name must be lower-cased without exception */
445
12.2k
    for (i = 0; i < nv->name->len; ++i) {
446
10.6k
      uint8_t c = nv->name->base[i];
447
10.6k
      if ('A' <= c && c <= 'Z') {
448
471
        return NGHTTP2_ERR_HTTP_HEADER;
449
471
      }
450
10.6k
    }
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.58k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
455
1.58k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
456
2.05k
  }
457
458
56.4k
  switch (nv->token) {
459
1.91k
  case NGHTTP2_TOKEN__METHOD:
460
1.91k
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
461
1.91k
    break;
462
1.41k
  case NGHTTP2_TOKEN__PATH:
463
1.41k
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
464
1.41k
    break;
465
1.59k
  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.48k
  case NGHTTP2_TOKEN__SCHEME:
478
3.48k
    rv = check_scheme(nv->value->base, nv->value->len);
479
3.48k
    break;
480
143
  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
143
    if ((stream->flags &
485
143
         NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
486
143
        lws(nv->value->base, nv->value->len)) {
487
0
      rv = 0;
488
0
      break;
489
0
    }
490
    /* fall through */
491
47.8k
  default:
492
47.8k
    if (stream->flags &
493
47.8k
        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.8k
    } else {
496
47.8k
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
497
47.8k
    }
498
56.4k
  }
499
500
56.4k
  if (rv == 0) {
501
1.83k
    assert(nv->name->len > 0);
502
1.83k
    if (nv->name->base[0] == ':') {
503
1.40k
      return NGHTTP2_ERR_HTTP_HEADER;
504
1.40k
    }
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
425
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
509
425
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
510
1.83k
  }
511
512
54.6k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
513
54.6k
    return http_request_on_header(stream, nv, trailer,
514
54.6k
                                  session->server &&
515
54.6k
                                    session->pending_enable_connect_protocol);
516
54.6k
  }
517
518
0
  return http_response_on_header(stream, nv, trailer);
519
54.6k
}
520
521
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
522
16.1k
                                    nghttp2_frame *frame) {
523
16.1k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
524
16.1k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
525
203
    if ((stream->http_flags &
526
203
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
527
203
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
528
79
      return -1;
529
79
    }
530
124
    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.2k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
536
15.2k
      return -1;
537
15.2k
    }
538
631
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
539
631
        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
540
6
         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
541
6
      return -1;
542
6
    }
543
625
    if (!check_path(stream)) {
544
10
      return -1;
545
10
    }
546
625
  }
547
548
739
  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
739
  return 0;
556
16.1k
}
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
66
                                    nghttp2_frame *frame) {
587
66
  (void)stream;
588
589
66
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
590
55
    return -1;
591
55
  }
592
593
11
  return 0;
594
66
}
595
596
206
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
597
206
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
598
0
    return -1;
599
0
  }
600
601
206
  if (stream->content_length != -1 &&
602
206
      stream->content_length != stream->recv_content_length) {
603
118
    return -1;
604
118
  }
605
606
88
  return 0;
607
206
}
608
609
1.67k
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
610
1.67k
  stream->recv_content_length += (int64_t)n;
611
612
1.67k
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
613
1.67k
      (stream->content_length != -1 &&
614
1.67k
       stream->recv_content_length > stream->content_length)) {
615
18
    return -1;
616
18
  }
617
618
1.66k
  return 0;
619
1.67k
}
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
37.4k
                                size_t valuelen) {
661
37.4k
  nghttp2_extpri pri = *dest;
662
37.4k
  sfparse_parser sfp;
663
37.4k
  sfparse_vec key;
664
37.4k
  sfparse_value val;
665
37.4k
  int rv;
666
667
37.4k
  sfparse_parser_init(&sfp, value, valuelen);
668
669
66.8k
  for (;;) {
670
66.8k
    rv = sfparse_parser_dict(&sfp, &key, &val);
671
66.8k
    if (rv != 0) {
672
37.1k
      if (rv == SFPARSE_ERR_EOF) {
673
24.5k
        break;
674
24.5k
      }
675
676
12.5k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
677
37.1k
    }
678
679
29.7k
    if (key.len != 1) {
680
5.72k
      continue;
681
5.72k
    }
682
683
24.0k
    switch (key.base[0]) {
684
599
    case 'i':
685
599
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
686
95
        return NGHTTP2_ERR_INVALID_ARGUMENT;
687
95
      }
688
689
504
      pri.inc = val.boolean;
690
691
504
      break;
692
808
    case 'u':
693
808
      if (val.type != SFPARSE_TYPE_INTEGER ||
694
808
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
695
808
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
696
258
        return NGHTTP2_ERR_INVALID_ARGUMENT;
697
258
      }
698
699
550
      pri.urgency = (uint32_t)val.integer;
700
701
550
      break;
702
24.0k
    }
703
24.0k
  }
704
705
24.5k
  *dest = pri;
706
707
24.5k
  return 0;
708
37.4k
}