Coverage Report

Created: 2025-07-11 06:26

/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.3k
static uint8_t downcase(uint8_t c) {
37
37.3k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
37.3k
}
39
40
4.18k
static int memieq(const void *a, const void *b, size_t n) {
41
4.18k
  size_t i;
42
4.18k
  const uint8_t *aa = a, *bb = b;
43
44
20.3k
  for (i = 0; i < n; ++i) {
45
18.6k
    if (downcase(aa[i]) != downcase(bb[i])) {
46
2.56k
      return 0;
47
2.56k
    }
48
18.6k
  }
49
1.62k
  return 1;
50
4.18k
}
51
52
1.92k
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
53
54
927
static int64_t parse_uint(const uint8_t *s, size_t len) {
55
927
  int64_t n = 0;
56
927
  size_t i;
57
927
  if (len == 0) {
58
122
    return -1;
59
122
  }
60
8.28k
  for (i = 0; i < len; ++i) {
61
7.65k
    if ('0' <= s[i] && s[i] <= '9') {
62
7.55k
      if (n > INT64_MAX / 10) {
63
4
        return -1;
64
4
      }
65
7.54k
      n *= 10;
66
7.54k
      if (n > INT64_MAX - (s[i] - '0')) {
67
66
        return -1;
68
66
      }
69
7.48k
      n += s[i] - '0';
70
7.48k
      continue;
71
7.54k
    }
72
102
    return -1;
73
7.65k
  }
74
633
  return n;
75
805
}
76
77
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
78
7.34k
                               uint32_t flag) {
79
7.34k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
80
777
    return 0;
81
777
  }
82
6.56k
  stream->http_flags = stream->http_flags | flag;
83
6.56k
  return 1;
84
7.34k
}
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
718
static int check_path(nghttp2_stream *stream) {
98
718
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
99
718
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
100
712
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
101
21
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
102
718
}
103
104
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
105
58.8k
                                  int trailer, int connect_protocol) {
106
58.8k
  nghttp2_extpri extpri;
107
108
58.8k
  if (nv->name->base[0] == ':') {
109
7.30k
    if (trailer ||
110
7.30k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
111
131
      return NGHTTP2_ERR_HTTP_HEADER;
112
131
    }
113
7.30k
  }
114
115
58.7k
  switch (nv->token) {
116
1.05k
  case NGHTTP2_TOKEN__AUTHORITY:
117
1.05k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
118
159
      return NGHTTP2_ERR_HTTP_HEADER;
119
159
    }
120
894
    break;
121
1.83k
  case NGHTTP2_TOKEN__METHOD:
122
1.83k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
123
52
      return NGHTTP2_ERR_HTTP_HEADER;
124
52
    }
125
1.78k
    switch (nv->value->len) {
126
575
    case 4:
127
575
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
128
66
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
129
66
      }
130
575
      break;
131
374
    case 7:
132
374
      switch (nv->value->base[6]) {
133
158
      case 'T':
134
158
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
135
88
          if (stream->stream_id % 2 == 0) {
136
            /* we won't allow CONNECT for push */
137
0
            return NGHTTP2_ERR_HTTP_HEADER;
138
0
          }
139
88
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
140
88
        }
141
158
        break;
142
158
      case 'S':
143
149
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
144
80
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
145
80
        }
146
149
        break;
147
374
      }
148
374
      break;
149
1.78k
    }
150
1.78k
    break;
151
1.78k
  case NGHTTP2_TOKEN__PATH:
152
1.37k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
153
168
      return NGHTTP2_ERR_HTTP_HEADER;
154
168
    }
155
1.21k
    if (nv->value->base[0] == '/') {
156
922
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
157
922
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
158
75
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
159
75
    }
160
1.21k
    break;
161
2.62k
  case NGHTTP2_TOKEN__SCHEME:
162
2.62k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
163
82
      return NGHTTP2_ERR_HTTP_HEADER;
164
82
    }
165
2.54k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
166
2.54k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
167
1.02k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
168
1.02k
    }
169
2.54k
    break;
170
72
  case NGHTTP2_TOKEN__PROTOCOL:
171
72
    if (!connect_protocol) {
172
72
      return NGHTTP2_ERR_HTTP_HEADER;
173
72
    }
174
175
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
176
0
      return NGHTTP2_ERR_HTTP_HEADER;
177
0
    }
178
0
    break;
179
452
  case NGHTTP2_TOKEN_HOST:
180
452
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
181
316
      return NGHTTP2_ERR_HTTP_HEADER;
182
316
    }
183
136
    break;
184
1.02k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
185
1.02k
    if (stream->content_length != -1) {
186
98
      return NGHTTP2_ERR_HTTP_HEADER;
187
98
    }
188
927
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
189
927
    if (stream->content_length == -1) {
190
294
      return NGHTTP2_ERR_HTTP_HEADER;
191
294
    }
192
633
    break;
193
927
  }
194
  /* disallowed header fields */
195
633
  case NGHTTP2_TOKEN_CONNECTION:
196
163
  case NGHTTP2_TOKEN_KEEP_ALIVE:
197
235
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
198
314
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
199
386
  case NGHTTP2_TOKEN_UPGRADE:
200
386
    return NGHTTP2_ERR_HTTP_HEADER;
201
1.92k
  case NGHTTP2_TOKEN_TE:
202
1.92k
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
203
1.32k
      return NGHTTP2_ERR_HTTP_HEADER;
204
1.32k
    }
205
595
    break;
206
40.0k
  case NGHTTP2_TOKEN_PRIORITY:
207
40.0k
    if (!trailer &&
208
        /* Do not parse the header field in PUSH_PROMISE. */
209
40.0k
        (stream->stream_id & 1) &&
210
40.0k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
211
39.6k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
212
39.6k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
213
39.6k
                                      nv->value->len) == 0) {
214
26.9k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
215
26.9k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
216
26.9k
      } else {
217
12.6k
        stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
218
12.6k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
219
12.6k
      }
220
39.6k
    }
221
40.0k
    break;
222
7.94k
  default:
223
7.94k
    if (nv->name->base[0] == ':') {
224
211
      return NGHTTP2_ERR_HTTP_HEADER;
225
211
    }
226
58.7k
  }
227
228
55.5k
  if (nv->name->base[0] != ':') {
229
49.1k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
230
49.1k
  }
231
232
55.5k
  return 0;
233
58.7k
}
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.29k
static int check_scheme(const uint8_t *value, size_t len) {
316
3.29k
  const uint8_t *last;
317
3.29k
  if (len == 0) {
318
81
    return 0;
319
81
  }
320
321
3.21k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
322
343
    return 0;
323
343
  }
324
325
2.86k
  last = value + len;
326
2.86k
  ++value;
327
328
16.1k
  for (; value != last; ++value) {
329
13.4k
    if (!(('A' <= *value && *value <= 'Z') ||
330
13.4k
          ('a' <= *value && *value <= 'z') ||
331
13.4k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
332
13.4k
          *value == '.')) {
333
213
      return 0;
334
213
    }
335
13.4k
  }
336
2.65k
  return 1;
337
2.86k
}
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.60k
static int check_authority(const uint8_t *value, size_t len) {
418
1.60k
  const uint8_t *last;
419
12.5k
  for (last = value + len; value != last; ++value) {
420
10.9k
    if (!VALID_AUTHORITY_CHARS[*value]) {
421
86
      return 0;
422
86
    }
423
10.9k
  }
424
1.51k
  return 1;
425
1.60k
}
426
427
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
428
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
429
62.5k
                           int trailer) {
430
62.5k
  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
62.5k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
440
1.73k
    size_t i;
441
1.73k
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
442
90
      return NGHTTP2_ERR_HTTP_HEADER;
443
90
    }
444
    /* header field name must be lower-cased without exception */
445
10.9k
    for (i = 0; i < nv->name->len; ++i) {
446
9.81k
      uint8_t c = nv->name->base[i];
447
9.81k
      if ('A' <= c && c <= 'Z') {
448
490
        return NGHTTP2_ERR_HTTP_HEADER;
449
490
      }
450
9.81k
    }
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.15k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
455
1.15k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
456
1.64k
  }
457
458
60.8k
  switch (nv->token) {
459
2.48k
  case NGHTTP2_TOKEN__METHOD:
460
2.48k
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
461
2.48k
    break;
462
1.58k
  case NGHTTP2_TOKEN__PATH:
463
1.58k
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
464
1.58k
    break;
465
1.12k
  case NGHTTP2_TOKEN__AUTHORITY:
466
1.60k
  case NGHTTP2_TOKEN_HOST:
467
1.60k
    if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
468
1.60k
      rv = check_authority(nv->value->base, nv->value->len);
469
1.60k
    } 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.60k
    break;
477
3.29k
  case NGHTTP2_TOKEN__SCHEME:
478
3.29k
    rv = check_scheme(nv->value->base, nv->value->len);
479
3.29k
    break;
480
72
  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
72
    if ((stream->flags &
485
72
         NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
486
72
        lws(nv->value->base, nv->value->len)) {
487
0
      rv = 0;
488
0
      break;
489
0
    }
490
    /* fall through */
491
51.8k
  default:
492
51.8k
    if (stream->flags &
493
51.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
51.8k
    } else {
496
51.8k
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
497
51.8k
    }
498
60.8k
  }
499
500
60.8k
  if (rv == 0) {
501
1.96k
    assert(nv->name->len > 0);
502
1.96k
    if (nv->name->base[0] == ':') {
503
1.64k
      return NGHTTP2_ERR_HTTP_HEADER;
504
1.64k
    }
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
328
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
509
328
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
510
1.96k
  }
511
512
58.8k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
513
58.8k
    return http_request_on_header(stream, nv, trailer,
514
58.8k
                                  session->server &&
515
58.8k
                                    session->pending_enable_connect_protocol);
516
58.8k
  }
517
518
0
  return http_response_on_header(stream, nv, trailer);
519
58.8k
}
520
521
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
522
16.0k
                                    nghttp2_frame *frame) {
523
16.0k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
524
16.0k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
525
88
    if ((stream->http_flags &
526
88
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
527
88
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
528
78
      return -1;
529
78
    }
530
10
    stream->content_length = -1;
531
16.0k
  } else {
532
16.0k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
533
16.0k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
534
16.0k
        (stream->http_flags &
535
15.2k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
536
15.2k
      return -1;
537
15.2k
    }
538
718
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
539
718
        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
540
0
         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
541
0
      return -1;
542
0
    }
543
718
    if (!check_path(stream)) {
544
21
      return -1;
545
21
    }
546
718
  }
547
548
707
  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
707
  return 0;
556
16.0k
}
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
41
    return -1;
591
41
  }
592
593
14
  return 0;
594
55
}
595
596
287
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
597
287
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
598
0
    return -1;
599
0
  }
600
601
287
  if (stream->content_length != -1 &&
602
287
      stream->content_length != stream->recv_content_length) {
603
132
    return -1;
604
132
  }
605
606
155
  return 0;
607
287
}
608
609
213
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
610
213
  stream->recv_content_length += (int64_t)n;
611
612
213
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
613
213
      (stream->content_length != -1 &&
614
213
       stream->recv_content_length > stream->content_length)) {
615
15
    return -1;
616
15
  }
617
618
198
  return 0;
619
213
}
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
39.6k
                                size_t valuelen) {
661
39.6k
  nghttp2_extpri pri = *dest;
662
39.6k
  sfparse_parser sfp;
663
39.6k
  sfparse_vec key;
664
39.6k
  sfparse_value val;
665
39.6k
  int rv;
666
667
39.6k
  sfparse_parser_init(&sfp, value, valuelen);
668
669
76.6k
  for (;;) {
670
76.6k
    rv = sfparse_parser_dict(&sfp, &key, &val);
671
76.6k
    if (rv != 0) {
672
39.2k
      if (rv == SFPARSE_ERR_EOF) {
673
26.9k
        break;
674
26.9k
      }
675
676
12.3k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
677
39.2k
    }
678
679
37.3k
    if (key.len != 1) {
680
20.0k
      continue;
681
20.0k
    }
682
683
17.2k
    switch (key.base[0]) {
684
622
    case 'i':
685
622
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
686
72
        return NGHTTP2_ERR_INVALID_ARGUMENT;
687
72
      }
688
689
550
      pri.inc = val.boolean;
690
691
550
      break;
692
499
    case 'u':
693
499
      if (val.type != SFPARSE_TYPE_INTEGER ||
694
499
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
695
499
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
696
264
        return NGHTTP2_ERR_INVALID_ARGUMENT;
697
264
      }
698
699
235
      pri.urgency = (uint32_t)val.integer;
700
701
235
      break;
702
17.2k
    }
703
17.2k
  }
704
705
26.9k
  *dest = pri;
706
707
26.9k
  return 0;
708
39.6k
}