Coverage Report

Created: 2025-07-12 06:50

/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
38.0k
static uint8_t downcase(uint8_t c) {
37
38.0k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
38.0k
}
39
40
4.07k
static int memieq(const void *a, const void *b, size_t n) {
41
4.07k
  size_t i;
42
4.07k
  const uint8_t *aa = a, *bb = b;
43
44
20.5k
  for (i = 0; i < n; ++i) {
45
19.0k
    if (downcase(aa[i]) != downcase(bb[i])) {
46
2.53k
      return 0;
47
2.53k
    }
48
19.0k
  }
49
1.54k
  return 1;
50
4.07k
}
51
52
1.98k
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
53
54
742
static int64_t parse_uint(const uint8_t *s, size_t len) {
55
742
  int64_t n = 0;
56
742
  size_t i;
57
742
  if (len == 0) {
58
79
    return -1;
59
79
  }
60
8.96k
  for (i = 0; i < len; ++i) {
61
8.48k
    if ('0' <= s[i] && s[i] <= '9') {
62
8.36k
      if (n > INT64_MAX / 10) {
63
1
        return -1;
64
1
      }
65
8.36k
      n *= 10;
66
8.36k
      if (n > INT64_MAX - (s[i] - '0')) {
67
66
        return -1;
68
66
      }
69
8.30k
      n += s[i] - '0';
70
8.30k
      continue;
71
8.36k
    }
72
121
    return -1;
73
8.48k
  }
74
475
  return n;
75
663
}
76
77
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
78
6.82k
                               uint32_t flag) {
79
6.82k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
80
701
    return 0;
81
701
  }
82
6.12k
  stream->http_flags = stream->http_flags | flag;
83
6.12k
  return 1;
84
6.82k
}
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
608
static int check_path(nghttp2_stream *stream) {
98
608
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
99
608
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
100
578
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
101
10
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
102
608
}
103
104
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
105
56.9k
                                  int trailer, int connect_protocol) {
106
56.9k
  nghttp2_extpri extpri;
107
108
56.9k
  if (nv->name->base[0] == ':') {
109
7.79k
    if (trailer ||
110
7.79k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
111
988
      return NGHTTP2_ERR_HTTP_HEADER;
112
988
    }
113
7.79k
  }
114
115
55.9k
  switch (nv->token) {
116
1.23k
  case NGHTTP2_TOKEN__AUTHORITY:
117
1.23k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
118
275
      return NGHTTP2_ERR_HTTP_HEADER;
119
275
    }
120
957
    break;
121
1.57k
  case NGHTTP2_TOKEN__METHOD:
122
1.57k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
123
43
      return NGHTTP2_ERR_HTTP_HEADER;
124
43
    }
125
1.52k
    switch (nv->value->len) {
126
794
    case 4:
127
794
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
128
34
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
129
34
      }
130
794
      break;
131
523
    case 7:
132
523
      switch (nv->value->base[6]) {
133
303
      case 'T':
134
303
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
135
202
          if (stream->stream_id % 2 == 0) {
136
            /* we won't allow CONNECT for push */
137
0
            return NGHTTP2_ERR_HTTP_HEADER;
138
0
          }
139
202
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
140
202
        }
141
303
        break;
142
303
      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
523
      }
148
523
      break;
149
1.52k
    }
150
1.52k
    break;
151
1.52k
  case NGHTTP2_TOKEN__PATH:
152
1.19k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
153
145
      return NGHTTP2_ERR_HTTP_HEADER;
154
145
    }
155
1.04k
    if (nv->value->base[0] == '/') {
156
756
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
157
756
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
158
68
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
159
68
    }
160
1.04k
    break;
161
2.48k
  case NGHTTP2_TOKEN__SCHEME:
162
2.48k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
163
82
      return NGHTTP2_ERR_HTTP_HEADER;
164
82
    }
165
2.40k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
166
2.40k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
167
816
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
168
816
    }
169
2.40k
    break;
170
140
  case NGHTTP2_TOKEN__PROTOCOL:
171
140
    if (!connect_protocol) {
172
40
      return NGHTTP2_ERR_HTTP_HEADER;
173
40
    }
174
175
100
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
176
46
      return NGHTTP2_ERR_HTTP_HEADER;
177
46
    }
178
54
    break;
179
245
  case NGHTTP2_TOKEN_HOST:
180
245
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
181
110
      return NGHTTP2_ERR_HTTP_HEADER;
182
110
    }
183
135
    break;
184
811
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
185
811
    if (stream->content_length != -1) {
186
69
      return NGHTTP2_ERR_HTTP_HEADER;
187
69
    }
188
742
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
189
742
    if (stream->content_length == -1) {
190
267
      return NGHTTP2_ERR_HTTP_HEADER;
191
267
    }
192
475
    break;
193
742
  }
194
  /* disallowed header fields */
195
475
  case NGHTTP2_TOKEN_CONNECTION:
196
114
  case NGHTTP2_TOKEN_KEEP_ALIVE:
197
188
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
198
289
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
199
328
  case NGHTTP2_TOKEN_UPGRADE:
200
328
    return NGHTTP2_ERR_HTTP_HEADER;
201
1.98k
  case NGHTTP2_TOKEN_TE:
202
1.98k
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
203
1.25k
      return NGHTTP2_ERR_HTTP_HEADER;
204
1.25k
    }
205
728
    break;
206
40.5k
  case NGHTTP2_TOKEN_PRIORITY:
207
40.5k
    if (!trailer &&
208
        /* Do not parse the header field in PUSH_PROMISE. */
209
40.5k
        (stream->stream_id & 1) &&
210
40.5k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
211
39.7k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
212
39.7k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
213
39.7k
                                      nv->value->len) == 0) {
214
26.7k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
215
26.7k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
216
26.7k
      } 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
39.7k
    }
221
40.5k
    break;
222
5.44k
  default:
223
5.44k
    if (nv->name->base[0] == ':') {
224
190
      return NGHTTP2_ERR_HTTP_HEADER;
225
190
    }
226
55.9k
  }
227
228
53.1k
  if (nv->name->base[0] != ':') {
229
47.1k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
230
47.1k
  }
231
232
53.1k
  return 0;
233
55.9k
}
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.74k
static int check_scheme(const uint8_t *value, size_t len) {
316
3.74k
  const uint8_t *last;
317
3.74k
  if (len == 0) {
318
141
    return 0;
319
141
  }
320
321
3.60k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
322
260
    return 0;
323
260
  }
324
325
3.34k
  last = value + len;
326
3.34k
  ++value;
327
328
53.7k
  for (; value != last; ++value) {
329
50.8k
    if (!(('A' <= *value && *value <= 'Z') ||
330
50.8k
          ('a' <= *value && *value <= 'z') ||
331
50.8k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
332
50.8k
          *value == '.')) {
333
405
      return 0;
334
405
    }
335
50.8k
  }
336
2.93k
  return 1;
337
3.34k
}
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.99k
static int check_authority(const uint8_t *value, size_t len) {
418
1.99k
  const uint8_t *last;
419
41.8k
  for (last = value + len; value != last; ++value) {
420
39.9k
    if (!VALID_AUTHORITY_CHARS[*value]) {
421
114
      return 0;
422
114
    }
423
39.9k
  }
424
1.87k
  return 1;
425
1.99k
}
426
427
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
428
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
429
60.8k
                           int trailer) {
430
60.8k
  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
60.8k
  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
134
      return NGHTTP2_ERR_HTTP_HEADER;
443
134
    }
444
    /* header field name must be lower-cased without exception */
445
12.0k
    for (i = 0; i < nv->name->len; ++i) {
446
10.5k
      uint8_t c = nv->name->base[i];
447
10.5k
      if ('A' <= c && c <= 'Z') {
448
443
        return NGHTTP2_ERR_HTTP_HEADER;
449
443
      }
450
10.5k
    }
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.55k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
455
1.55k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
456
1.99k
  }
457
458
58.7k
  switch (nv->token) {
459
1.84k
  case NGHTTP2_TOKEN__METHOD:
460
1.84k
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
461
1.84k
    break;
462
1.31k
  case NGHTTP2_TOKEN__PATH:
463
1.31k
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
464
1.31k
    break;
465
1.73k
  case NGHTTP2_TOKEN__AUTHORITY:
466
1.99k
  case NGHTTP2_TOKEN_HOST:
467
1.99k
    if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
468
1.99k
      rv = check_authority(nv->value->base, nv->value->len);
469
1.99k
    } 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.99k
    break;
477
3.74k
  case NGHTTP2_TOKEN__SCHEME:
478
3.74k
    rv = check_scheme(nv->value->base, nv->value->len);
479
3.74k
    break;
480
141
  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
141
    if ((stream->flags &
485
141
         NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
486
141
        lws(nv->value->base, nv->value->len)) {
487
0
      rv = 0;
488
0
      break;
489
0
    }
490
    /* fall through */
491
49.8k
  default:
492
49.8k
    if (stream->flags &
493
49.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
49.8k
    } else {
496
49.8k
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
497
49.8k
    }
498
58.7k
  }
499
500
58.7k
  if (rv == 0) {
501
1.79k
    assert(nv->name->len > 0);
502
1.79k
    if (nv->name->base[0] == ':') {
503
1.33k
      return NGHTTP2_ERR_HTTP_HEADER;
504
1.33k
    }
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
458
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
509
458
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
510
1.79k
  }
511
512
56.9k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
513
56.9k
    return http_request_on_header(stream, nv, trailer,
514
56.9k
                                  session->server &&
515
56.9k
                                    session->pending_enable_connect_protocol);
516
56.9k
  }
517
518
0
  return http_response_on_header(stream, nv, trailer);
519
56.9k
}
520
521
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
522
15.4k
                                    nghttp2_frame *frame) {
523
15.4k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
524
15.4k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
525
194
    if ((stream->http_flags &
526
194
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
527
194
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
528
72
      return -1;
529
72
    }
530
122
    stream->content_length = -1;
531
15.2k
  } else {
532
15.2k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
533
15.2k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
534
15.2k
        (stream->http_flags &
535
14.6k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
536
14.6k
      return -1;
537
14.6k
    }
538
611
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
539
611
        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
540
3
         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
541
3
      return -1;
542
3
    }
543
608
    if (!check_path(stream)) {
544
10
      return -1;
545
10
    }
546
608
  }
547
548
720
  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
720
  return 0;
556
15.4k
}
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
56
                                    nghttp2_frame *frame) {
587
56
  (void)stream;
588
589
56
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
590
45
    return -1;
591
45
  }
592
593
11
  return 0;
594
56
}
595
596
214
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
597
214
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
598
0
    return -1;
599
0
  }
600
601
214
  if (stream->content_length != -1 &&
602
214
      stream->content_length != stream->recv_content_length) {
603
130
    return -1;
604
130
  }
605
606
84
  return 0;
607
214
}
608
609
2.07k
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
610
2.07k
  stream->recv_content_length += (int64_t)n;
611
612
2.07k
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
613
2.07k
      (stream->content_length != -1 &&
614
2.07k
       stream->recv_content_length > stream->content_length)) {
615
22
    return -1;
616
22
  }
617
618
2.04k
  return 0;
619
2.07k
}
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.7k
                                size_t valuelen) {
661
39.7k
  nghttp2_extpri pri = *dest;
662
39.7k
  sfparse_parser sfp;
663
39.7k
  sfparse_vec key;
664
39.7k
  sfparse_value val;
665
39.7k
  int rv;
666
667
39.7k
  sfparse_parser_init(&sfp, value, valuelen);
668
669
70.8k
  for (;;) {
670
70.8k
    rv = sfparse_parser_dict(&sfp, &key, &val);
671
70.8k
    if (rv != 0) {
672
39.3k
      if (rv == SFPARSE_ERR_EOF) {
673
26.7k
        break;
674
26.7k
      }
675
676
12.6k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
677
39.3k
    }
678
679
31.5k
    if (key.len != 1) {
680
5.88k
      continue;
681
5.88k
    }
682
683
25.6k
    switch (key.base[0]) {
684
793
    case 'i':
685
793
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
686
72
        return NGHTTP2_ERR_INVALID_ARGUMENT;
687
72
      }
688
689
721
      pri.inc = val.boolean;
690
691
721
      break;
692
613
    case 'u':
693
613
      if (val.type != SFPARSE_TYPE_INTEGER ||
694
613
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
695
613
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
696
283
        return NGHTTP2_ERR_INVALID_ARGUMENT;
697
283
      }
698
699
330
      pri.urgency = (uint32_t)val.integer;
700
701
330
      break;
702
25.6k
    }
703
25.6k
  }
704
705
26.7k
  *dest = pri;
706
707
26.7k
  return 0;
708
39.7k
}