Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmnghttp2/lib/nghttp2_http.c
Line
Count
Source
1
/*
2
 * nghttp2 - HTTP/2 C Library
3
 *
4
 * Copyright (c) 2015 Tatsuhiro Tsujikawa
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining
7
 * a copy of this software and associated documentation files (the
8
 * "Software"), to deal in the Software without restriction, including
9
 * without limitation the rights to use, copy, modify, merge, publish,
10
 * distribute, sublicense, and/or sell copies of the Software, and to
11
 * permit persons to whom the Software is furnished to do so, subject to
12
 * the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be
15
 * included in all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
 */
25
#include "nghttp2_http.h"
26
27
#include <string.h>
28
#include <assert.h>
29
#include <stdio.h>
30
31
#include "nghttp2_hd.h"
32
#include "nghttp2_helper.h"
33
#include "nghttp2_extpri.h"
34
#include "sfparse.h"
35
36
0
static uint8_t downcase(uint8_t c) {
37
0
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
0
}
39
40
0
static int memieq(const void *a, const void *b, size_t n) {
41
0
  size_t i;
42
0
  const uint8_t *aa = a, *bb = b;
43
44
0
  for (i = 0; i < n; ++i) {
45
0
    if (downcase(aa[i]) != downcase(bb[i])) {
46
0
      return 0;
47
0
    }
48
0
  }
49
0
  return 1;
50
0
}
51
52
0
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
53
54
0
static int64_t parse_uint(const uint8_t *s, size_t len) {
55
0
  int64_t n = 0;
56
0
  size_t i;
57
0
  if (len == 0) {
58
0
    return -1;
59
0
  }
60
0
  for (i = 0; i < len; ++i) {
61
0
    if ('0' <= s[i] && s[i] <= '9') {
62
0
      if (n > INT64_MAX / 10) {
63
0
        return -1;
64
0
      }
65
0
      n *= 10;
66
0
      if (n > INT64_MAX - (s[i] - '0')) {
67
0
        return -1;
68
0
      }
69
0
      n += s[i] - '0';
70
0
      continue;
71
0
    }
72
0
    return -1;
73
0
  }
74
0
  return n;
75
0
}
76
77
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
78
0
                               uint32_t flag) {
79
0
  if ((stream->http_flags & flag) || nv->value->len == 0) {
80
0
    return 0;
81
0
  }
82
0
  stream->http_flags = stream->http_flags | flag;
83
0
  return 1;
84
0
}
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
0
static int check_path(nghttp2_stream *stream) {
98
0
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
99
0
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
100
0
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
101
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
102
0
}
103
104
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
105
0
                                  int trailer, int connect_protocol) {
106
0
  nghttp2_extpri extpri;
107
108
0
  if (nv->name->base[0] == ':') {
109
0
    if (trailer ||
110
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
111
0
      return NGHTTP2_ERR_HTTP_HEADER;
112
0
    }
113
0
  }
114
115
0
  switch (nv->token) {
116
0
  case NGHTTP2_TOKEN__AUTHORITY:
117
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
118
0
      return NGHTTP2_ERR_HTTP_HEADER;
119
0
    }
120
0
    break;
121
0
  case NGHTTP2_TOKEN__METHOD:
122
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
123
0
      return NGHTTP2_ERR_HTTP_HEADER;
124
0
    }
125
0
    switch (nv->value->len) {
126
0
    case 4:
127
0
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
128
0
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
129
0
      }
130
0
      break;
131
0
    case 7:
132
0
      switch (nv->value->base[6]) {
133
0
      case 'T':
134
0
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
135
0
          if (stream->stream_id % 2 == 0) {
136
            /* we won't allow CONNECT for push */
137
0
            return NGHTTP2_ERR_HTTP_HEADER;
138
0
          }
139
0
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
140
0
        }
141
0
        break;
142
0
      case 'S':
143
0
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
144
0
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
145
0
        }
146
0
        break;
147
0
      }
148
0
      break;
149
0
    }
150
0
    break;
151
0
  case NGHTTP2_TOKEN__PATH:
152
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
153
0
      return NGHTTP2_ERR_HTTP_HEADER;
154
0
    }
155
0
    if (nv->value->base[0] == '/') {
156
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
157
0
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
158
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
159
0
    }
160
0
    break;
161
0
  case NGHTTP2_TOKEN__SCHEME:
162
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
163
0
      return NGHTTP2_ERR_HTTP_HEADER;
164
0
    }
165
0
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
166
0
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
167
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
168
0
    }
169
0
    break;
170
0
  case NGHTTP2_TOKEN__PROTOCOL:
171
0
    if (!connect_protocol) {
172
0
      return NGHTTP2_ERR_HTTP_HEADER;
173
0
    }
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
0
  case NGHTTP2_TOKEN_HOST:
180
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
181
0
      return NGHTTP2_ERR_HTTP_HEADER;
182
0
    }
183
0
    break;
184
0
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
185
0
    if (stream->content_length != -1) {
186
0
      return NGHTTP2_ERR_HTTP_HEADER;
187
0
    }
188
0
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
189
0
    if (stream->content_length == -1) {
190
0
      return NGHTTP2_ERR_HTTP_HEADER;
191
0
    }
192
0
    break;
193
0
  }
194
  /* disallowed header fields */
195
0
  case NGHTTP2_TOKEN_CONNECTION:
196
0
  case NGHTTP2_TOKEN_KEEP_ALIVE:
197
0
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
198
0
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
199
0
  case NGHTTP2_TOKEN_UPGRADE:
200
0
    return NGHTTP2_ERR_HTTP_HEADER;
201
0
  case NGHTTP2_TOKEN_TE:
202
0
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
203
0
      return NGHTTP2_ERR_HTTP_HEADER;
204
0
    }
205
0
    break;
206
0
  case NGHTTP2_TOKEN_PRIORITY:
207
0
    if (!trailer &&
208
        /* Do not parse the header field in PUSH_PROMISE. */
209
0
        (stream->stream_id & 1) &&
210
0
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
211
0
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
212
0
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
213
0
                                      nv->value->len) == 0) {
214
0
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
215
0
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
216
0
      } else {
217
0
        stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
218
0
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
219
0
      }
220
0
    }
221
0
    break;
222
0
  default:
223
0
    if (nv->name->base[0] == ':') {
224
0
      return NGHTTP2_ERR_HTTP_HEADER;
225
0
    }
226
0
  }
227
228
0
  if (nv->name->base[0] != ':') {
229
0
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
230
0
  }
231
232
0
  return 0;
233
0
}
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
0
static int check_scheme(const uint8_t *value, size_t len) {
316
0
  const uint8_t *last;
317
0
  if (len == 0) {
318
0
    return 0;
319
0
  }
320
321
0
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
322
0
    return 0;
323
0
  }
324
325
0
  last = value + len;
326
0
  ++value;
327
328
0
  for (; value != last; ++value) {
329
0
    if (!(('A' <= *value && *value <= 'Z') ||
330
0
          ('a' <= *value && *value <= 'z') ||
331
0
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
332
0
          *value == '.')) {
333
0
      return 0;
334
0
    }
335
0
  }
336
0
  return 1;
337
0
}
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
0
static int check_authority(const uint8_t *value, size_t len) {
418
0
  const uint8_t *last;
419
0
  for (last = value + len; value != last; ++value) {
420
0
    if (!VALID_AUTHORITY_CHARS[*value]) {
421
0
      return 0;
422
0
    }
423
0
  }
424
0
  return 1;
425
0
}
426
427
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
428
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
429
0
                           int trailer) {
430
0
  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
0
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
440
0
    size_t i;
441
0
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
442
0
      return NGHTTP2_ERR_HTTP_HEADER;
443
0
    }
444
    /* header field name must be lower-cased without exception */
445
0
    for (i = 0; i < nv->name->len; ++i) {
446
0
      uint8_t c = nv->name->base[i];
447
0
      if ('A' <= c && c <= 'Z') {
448
0
        return NGHTTP2_ERR_HTTP_HEADER;
449
0
      }
450
0
    }
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
0
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
455
0
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
456
0
  }
457
458
0
  switch (nv->token) {
459
0
  case NGHTTP2_TOKEN__METHOD:
460
0
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
461
0
    break;
462
0
  case NGHTTP2_TOKEN__PATH:
463
0
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
464
0
    break;
465
0
  case NGHTTP2_TOKEN__AUTHORITY:
466
0
  case NGHTTP2_TOKEN_HOST:
467
0
    if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
468
0
      rv = check_authority(nv->value->base, nv->value->len);
469
0
    } 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
0
    break;
477
0
  case NGHTTP2_TOKEN__SCHEME:
478
0
    rv = check_scheme(nv->value->base, nv->value->len);
479
0
    break;
480
0
  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
0
    if ((stream->flags &
485
0
         NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
486
0
        lws(nv->value->base, nv->value->len)) {
487
0
      rv = 0;
488
0
      break;
489
0
    }
490
    /* fall through */
491
0
  default:
492
0
    if (stream->flags &
493
0
        NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
494
0
      rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
495
0
    } else {
496
0
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
497
0
    }
498
0
  }
499
500
0
  if (rv == 0) {
501
0
    assert(nv->name->len > 0);
502
0
    if (nv->name->base[0] == ':') {
503
0
      return NGHTTP2_ERR_HTTP_HEADER;
504
0
    }
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
0
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
509
0
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
510
0
  }
511
512
0
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
513
0
    return http_request_on_header(stream, nv, trailer,
514
0
                                  session->server &&
515
0
                                    session->pending_enable_connect_protocol);
516
0
  }
517
518
0
  return http_response_on_header(stream, nv, trailer);
519
0
}
520
521
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
522
0
                                    nghttp2_frame *frame) {
523
0
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
524
0
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
525
0
    if ((stream->http_flags &
526
0
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
527
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
528
0
      return -1;
529
0
    }
530
0
    stream->content_length = -1;
531
0
  } else {
532
0
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
533
0
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
534
0
        (stream->http_flags &
535
0
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
536
0
      return -1;
537
0
    }
538
0
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
539
0
        ((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
0
    if (!check_path(stream)) {
544
0
      return -1;
545
0
    }
546
0
  }
547
548
0
  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
0
  return 0;
556
0
}
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
0
                                    nghttp2_frame *frame) {
587
0
  (void)stream;
588
589
0
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
590
0
    return -1;
591
0
  }
592
593
0
  return 0;
594
0
}
595
596
0
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
597
0
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
598
0
    return -1;
599
0
  }
600
601
0
  if (stream->content_length != -1 &&
602
0
      stream->content_length != stream->recv_content_length) {
603
0
    return -1;
604
0
  }
605
606
0
  return 0;
607
0
}
608
609
0
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
610
0
  stream->recv_content_length += (int64_t)n;
611
612
0
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
613
0
      (stream->content_length != -1 &&
614
0
       stream->recv_content_length > stream->content_length)) {
615
0
    return -1;
616
0
  }
617
618
0
  return 0;
619
0
}
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
0
                                size_t valuelen) {
661
0
  nghttp2_extpri pri = *dest;
662
0
  sfparse_parser sfp;
663
0
  sfparse_vec key;
664
0
  sfparse_value val;
665
0
  int rv;
666
667
0
  sfparse_parser_init(&sfp, value, valuelen);
668
669
0
  for (;;) {
670
0
    rv = sfparse_parser_dict(&sfp, &key, &val);
671
0
    if (rv != 0) {
672
0
      if (rv == SFPARSE_ERR_EOF) {
673
0
        break;
674
0
      }
675
676
0
      return NGHTTP2_ERR_INVALID_ARGUMENT;
677
0
    }
678
679
0
    if (key.len != 1) {
680
0
      continue;
681
0
    }
682
683
0
    switch (key.base[0]) {
684
0
    case 'i':
685
0
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
686
0
        return NGHTTP2_ERR_INVALID_ARGUMENT;
687
0
      }
688
689
0
      pri.inc = val.boolean;
690
691
0
      break;
692
0
    case 'u':
693
0
      if (val.type != SFPARSE_TYPE_INTEGER ||
694
0
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
695
0
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
696
0
        return NGHTTP2_ERR_INVALID_ARGUMENT;
697
0
      }
698
699
0
      pri.urgency = (uint32_t)val.integer;
700
701
0
      break;
702
0
    }
703
0
  }
704
705
0
  *dest = pri;
706
707
0
  return 0;
708
0
}