Coverage Report

Created: 2026-10-17 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nghttp2/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
/*
37
 * memieq returns nonzero if the data pointed by |a| of length |n|
38
 * equals to |b| of the same length in case-insensitive manner.  The
39
 * data pointed by |a| must not include upper cased letters (A-Z).
40
 */
41
2.73k
static int memieq(const void *a, const void *b, size_t n) {
42
2.73k
  size_t i;
43
2.73k
  const uint8_t *aa = a, *bb = b;
44
45
12.7k
  for (i = 0; i < n; ++i) {
46
11.4k
    if (aa[i] != nghttp2_downcase_byte(bb[i])) {
47
1.46k
      return 0;
48
1.46k
    }
49
11.4k
  }
50
1.26k
  return 1;
51
2.73k
}
52
53
#define lstrieq(A, B, N)                                                       \
54
860
  (nghttp2_strlen_lit((A)) == (N) && memieq((A), (B), (N)))
55
56
0
static int32_t parse_status_code(const uint8_t *s, size_t len) {
57
0
  if (len != 3 || '1' > s[0] || s[0] > '9' || '0' > s[1] || s[1] > '9' ||
58
0
      '0' > s[2] || s[2] > '9') {
59
0
    return -1;
60
0
  }
61
62
0
  return (s[0] - '0') * 100 + (s[1] - '0') * 10 + (s[2] - '0');
63
0
}
64
65
749
static int64_t parse_uint(const uint8_t *s, size_t len) {
66
749
  uint64_t n = 0;
67
749
  uint32_t c;
68
749
  size_t i;
69
70
749
  if (len == 0) {
71
86
    return -1;
72
86
  }
73
74
7.81k
  for (i = 0; i < len; ++i) {
75
7.36k
    if ('0' > s[i] || s[i] > '9') {
76
79
      return -1;
77
79
    }
78
79
7.28k
    c = s[i] - '0';
80
81
7.28k
    if (n > ((uint64_t)INT64_MAX - c) / 10) {
82
137
      return -1;
83
137
    }
84
85
7.15k
    n = n * 10 + c;
86
7.15k
  }
87
88
447
  return (int64_t)n;
89
663
}
90
91
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
92
7.69k
                               uint32_t flag) {
93
7.69k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
94
857
    return 0;
95
857
  }
96
6.84k
  stream->http_flags = stream->http_flags | flag;
97
6.84k
  return 1;
98
7.69k
}
99
100
0
static int expect_response_body(nghttp2_stream *stream) {
101
0
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
102
0
         stream->status_code / 100 != 1 && stream->status_code != 304 &&
103
0
         stream->status_code != 204;
104
0
}
105
106
/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
107
   header field to represent system-wide OPTIONS request.  Otherwise,
108
   :path header field value must start with "/".  This function must
109
   be called after ":method" header field was received.  This function
110
   returns nonzero if path is valid.*/
111
813
static int check_path(nghttp2_stream *stream) {
112
813
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
113
801
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
114
10
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
115
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
116
813
}
117
118
3.33k
static int check_scheme(const uint8_t *value, size_t len) {
119
3.33k
  const uint8_t *last;
120
3.33k
  if (len == 0) {
121
349
    return 0;
122
349
  }
123
124
2.98k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
125
314
    return 0;
126
314
  }
127
128
2.67k
  last = value + len;
129
2.67k
  ++value;
130
131
41.2k
  for (; value != last; ++value) {
132
38.8k
    if (!(('A' <= *value && *value <= 'Z') ||
133
33.6k
          ('a' <= *value && *value <= 'z') ||
134
18.1k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
135
755
          *value == '.')) {
136
270
      return 0;
137
270
    }
138
38.8k
  }
139
2.40k
  return 1;
140
2.67k
}
141
142
0
static int lws(const uint8_t *s, size_t n) {
143
0
  size_t i;
144
0
  for (i = 0; i < n; ++i) {
145
0
    if (s[i] != ' ' && s[i] != '\t') {
146
0
      return 0;
147
0
    }
148
0
  }
149
0
  return 1;
150
0
}
151
152
/* Same as the array in nghttp2_helper.c, but '@' is not allowed */
153
static const uint8_t VALID_AUTHORITY_CHARS[256] = {
154
  ['!'] = 1, ['$'] = 1, ['%'] = 1, ['&'] = 1, ['\''] = 1, ['('] = 1, [')'] = 1,
155
  ['*'] = 1, ['+'] = 1, [','] = 1, ['-'] = 1, ['.'] = 1,  ['0'] = 1, ['1'] = 1,
156
  ['2'] = 1, ['3'] = 1, ['4'] = 1, ['5'] = 1, ['6'] = 1,  ['7'] = 1, ['8'] = 1,
157
  ['9'] = 1, [':'] = 1, [';'] = 1, ['='] = 1, ['A'] = 1,  ['B'] = 1, ['C'] = 1,
158
  ['D'] = 1, ['E'] = 1, ['F'] = 1, ['G'] = 1, ['H'] = 1,  ['I'] = 1, ['J'] = 1,
159
  ['K'] = 1, ['L'] = 1, ['M'] = 1, ['N'] = 1, ['O'] = 1,  ['P'] = 1, ['Q'] = 1,
160
  ['R'] = 1, ['S'] = 1, ['T'] = 1, ['U'] = 1, ['V'] = 1,  ['W'] = 1, ['X'] = 1,
161
  ['Y'] = 1, ['Z'] = 1, ['['] = 1, [']'] = 1, ['_'] = 1,  ['a'] = 1, ['b'] = 1,
162
  ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1, ['g'] = 1,  ['h'] = 1, ['i'] = 1,
163
  ['j'] = 1, ['k'] = 1, ['l'] = 1, ['m'] = 1, ['n'] = 1,  ['o'] = 1, ['p'] = 1,
164
  ['q'] = 1, ['r'] = 1, ['s'] = 1, ['t'] = 1, ['u'] = 1,  ['v'] = 1, ['w'] = 1,
165
  ['x'] = 1, ['y'] = 1, ['z'] = 1, ['~'] = 1,
166
};
167
168
1.99k
static int check_authority(const uint8_t *value, size_t len) {
169
1.99k
  const uint8_t *last;
170
11.6k
  for (last = value + len; value != last; ++value) {
171
10.1k
    if (!VALID_AUTHORITY_CHARS[*value]) {
172
503
      return 0;
173
503
    }
174
10.1k
  }
175
1.49k
  return 1;
176
1.99k
}
177
178
static int check_header_value(const nghttp2_stream *stream,
179
26.9k
                              const nghttp2_rcbuf *value) {
180
26.9k
  if (stream->flags &
181
26.9k
      NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
182
0
    return nghttp2_check_header_value(value->base, value->len);
183
0
  }
184
185
26.9k
  return nghttp2_check_header_value_rfc9113(value->base, value->len);
186
26.9k
}
187
188
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
189
38.8k
                                  int trailer, int connect_protocol) {
190
38.8k
  nghttp2_extpri extpri;
191
192
38.8k
  switch (nv->token) {
193
1.37k
  case NGHTTP2_TOKEN__AUTHORITY:
194
1.37k
    if (!check_authority(nv->value->base, nv->value->len) ||
195
1.25k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
196
285
      return NGHTTP2_ERR_HTTP_HEADER;
197
285
    }
198
1.09k
    break;
199
2.00k
  case NGHTTP2_TOKEN__METHOD:
200
2.00k
    if (!nghttp2_check_method(nv->value->base, nv->value->len) ||
201
1.83k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
202
194
      return NGHTTP2_ERR_HTTP_HEADER;
203
194
    }
204
1.80k
    switch (nv->value->len) {
205
995
    case 4:
206
995
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
207
66
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
208
66
      }
209
995
      break;
210
572
    case 7:
211
572
      switch (nv->value->base[6]) {
212
372
      case 'T':
213
372
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
214
302
          if (stream->stream_id % 2 == 0) {
215
            /* we won't allow CONNECT for push */
216
0
            return NGHTTP2_ERR_HTTP_HEADER;
217
0
          }
218
302
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
219
302
        }
220
372
        break;
221
372
      case 'S':
222
133
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
223
66
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
224
66
        }
225
133
        break;
226
572
      }
227
572
      break;
228
1.80k
    }
229
1.80k
    break;
230
1.85k
  case NGHTTP2_TOKEN__PATH:
231
1.85k
    if (!nghttp2_check_path(nv->value->base, nv->value->len) ||
232
1.73k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
233
498
      return NGHTTP2_ERR_HTTP_HEADER;
234
498
    }
235
1.35k
    if (nv->value->base[0] == '/') {
236
984
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
237
984
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
238
68
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
239
68
    }
240
1.35k
    break;
241
3.33k
  case NGHTTP2_TOKEN__SCHEME:
242
3.33k
    if (!check_scheme(nv->value->base, nv->value->len) ||
243
2.40k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
244
1.00k
      return NGHTTP2_ERR_HTTP_HEADER;
245
1.00k
    }
246
2.33k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
247
2.08k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
248
1.07k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
249
1.07k
    }
250
2.33k
    break;
251
306
  case NGHTTP2_TOKEN__PROTOCOL:
252
306
    if (!connect_protocol ||
253
234
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
254
148
      return NGHTTP2_ERR_HTTP_HEADER;
255
148
    }
256
158
    if (stream->flags &
257
158
        NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
258
      /* Check the value consists of just white spaces, which was done
259
         in check_pseudo_header before
260
         nghttp2_check_header_value_rfc9113 has been introduced. */
261
0
      if (lws(nv->value->base, nv->value->len) ||
262
0
          !nghttp2_check_header_value(nv->value->base, nv->value->len)) {
263
0
        return NGHTTP2_ERR_HTTP_HEADER;
264
0
      }
265
158
    } else if (!nghttp2_check_header_value_rfc9113(nv->value->base,
266
158
                                                   nv->value->len)) {
267
67
      return NGHTTP2_ERR_HTTP_HEADER;
268
67
    }
269
91
    break;
270
617
  case NGHTTP2_TOKEN_HOST:
271
617
    if (!check_authority(nv->value->base, nv->value->len)) {
272
383
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
273
383
    }
274
234
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
275
137
      return NGHTTP2_ERR_HTTP_HEADER;
276
137
    }
277
97
    break;
278
829
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
279
829
    if (stream->content_length != -1) {
280
80
      return NGHTTP2_ERR_HTTP_HEADER;
281
80
    }
282
749
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
283
749
    if (stream->content_length == -1) {
284
302
      return NGHTTP2_ERR_HTTP_HEADER;
285
302
    }
286
447
    break;
287
749
  }
288
  /* disallowed header fields */
289
447
  case NGHTTP2_TOKEN_CONNECTION:
290
147
  case NGHTTP2_TOKEN_KEEP_ALIVE:
291
164
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
292
321
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
293
396
  case NGHTTP2_TOKEN_UPGRADE:
294
396
    return NGHTTP2_ERR_HTTP_HEADER;
295
860
  case NGHTTP2_TOKEN_TE:
296
860
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
297
666
      return NGHTTP2_ERR_HTTP_HEADER;
298
666
    }
299
194
    break;
300
10.3k
  case NGHTTP2_TOKEN_PRIORITY:
301
10.3k
    if (!check_header_value(stream, nv->value)) {
302
216
      stream->http_flags &= ~NGHTTP2_HTTP_FLAG_PRIORITY;
303
216
      stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
304
305
216
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
306
216
    }
307
308
10.1k
    if (!trailer &&
309
        /* Do not parse the header field in PUSH_PROMISE. */
310
9.96k
        (stream->stream_id & 1) &&
311
9.96k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
312
9.76k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
313
9.76k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
314
9.76k
                                      nv->value->len) == 0) {
315
5.95k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
316
5.95k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
317
5.95k
      } else {
318
3.81k
        stream->http_flags &= ~NGHTTP2_HTTP_FLAG_PRIORITY;
319
3.81k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
320
3.81k
      }
321
9.76k
    }
322
10.1k
    break;
323
16.8k
  default:
324
16.8k
    if (nv->name->base[0] == ':') {
325
306
      return NGHTTP2_ERR_HTTP_HEADER;
326
306
    }
327
328
16.5k
    if (!check_header_value(stream, nv->value)) {
329
1.22k
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
330
1.22k
    }
331
38.8k
  }
332
333
32.9k
  return 0;
334
38.8k
}
335
336
0
static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv) {
337
0
  switch (nv->token) {
338
0
  case NGHTTP2_TOKEN__STATUS: {
339
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
340
0
      return NGHTTP2_ERR_HTTP_HEADER;
341
0
    }
342
0
    stream->status_code = parse_status_code(nv->value->base, nv->value->len);
343
0
    if (stream->status_code == -1 || stream->status_code == 101) {
344
0
      return NGHTTP2_ERR_HTTP_HEADER;
345
0
    }
346
0
    break;
347
0
  }
348
0
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
349
0
    if (stream->status_code == 204) {
350
      /* content-length header field in 204 response is prohibited by
351
         RFC 7230.  But some widely used servers send content-length:
352
         0.  Until they get fixed, we ignore it. */
353
0
      if (stream->content_length != -1) {
354
        /* Found multiple content-length field */
355
0
        return NGHTTP2_ERR_HTTP_HEADER;
356
0
      }
357
0
      if (!lstrieq("0", nv->value->base, nv->value->len)) {
358
0
        return NGHTTP2_ERR_HTTP_HEADER;
359
0
      }
360
0
      stream->content_length = 0;
361
0
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
362
0
    }
363
0
    if (stream->status_code / 100 == 1) {
364
0
      return NGHTTP2_ERR_HTTP_HEADER;
365
0
    }
366
    /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
367
0
    if (stream->status_code / 100 == 2 &&
368
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
369
0
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
370
0
    }
371
0
    if (stream->content_length != -1) {
372
0
      return NGHTTP2_ERR_HTTP_HEADER;
373
0
    }
374
0
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
375
0
    if (stream->content_length == -1) {
376
0
      return NGHTTP2_ERR_HTTP_HEADER;
377
0
    }
378
0
    break;
379
0
  }
380
  /* disallowed header fields */
381
0
  case NGHTTP2_TOKEN_CONNECTION:
382
0
  case NGHTTP2_TOKEN_KEEP_ALIVE:
383
0
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
384
0
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
385
0
  case NGHTTP2_TOKEN_UPGRADE:
386
0
    return NGHTTP2_ERR_HTTP_HEADER;
387
0
  case NGHTTP2_TOKEN_TE:
388
0
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
389
0
      return NGHTTP2_ERR_HTTP_HEADER;
390
0
    }
391
0
    break;
392
0
  default:
393
0
    if (nv->name->base[0] == ':') {
394
0
      return NGHTTP2_ERR_HTTP_HEADER;
395
0
    }
396
397
0
    if (!check_header_value(stream, nv->value)) {
398
0
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
399
0
    }
400
0
  }
401
402
0
  return 0;
403
0
}
404
405
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
406
                           const nghttp2_frame *frame, nghttp2_hd_nv *nv,
407
50.8k
                           int trailer) {
408
  /* We are strict for pseudo header field.  One bad character should
409
     lead to fail.  OTOH, we should be a bit forgiving for regular
410
     headers, since existing public internet has so much illegal
411
     headers floating around and if we kill the stream because of
412
     this, we may disrupt many web sites and/or libraries.  So we
413
     become conservative here, and just ignore those illegal regular
414
     headers. */
415
50.8k
  if (nv->name->len == 0) {
416
5.52k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
417
5.52k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
418
5.52k
  }
419
420
45.3k
  if (nv->name->base[0] == ':') {
421
    /* pseudo header must have a valid token. */
422
9.42k
    if (nv->token == -1 || trailer ||
423
9.30k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
424
247
      return NGHTTP2_ERR_HTTP_HEADER;
425
247
    }
426
35.9k
  } else {
427
35.9k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
428
429
35.9k
    switch (nghttp2_check_nonempty_header_name(nv->name->base, nv->name->len)) {
430
5.95k
    case 0:
431
5.95k
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
432
317
    case 2:
433
      /* header field name must be lower-cased without exception */
434
317
      return NGHTTP2_ERR_HTTP_HEADER;
435
35.9k
    }
436
35.9k
  }
437
438
45.3k
  assert(nv->name->len > 0);
439
440
38.8k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
441
38.8k
    return http_request_on_header(stream, nv, trailer,
442
38.8k
                                  session->server &&
443
38.8k
                                    session->pending_enable_connect_protocol);
444
38.8k
  }
445
446
0
  return http_response_on_header(stream, nv);
447
38.8k
}
448
449
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
450
8.82k
                                    nghttp2_frame *frame) {
451
8.82k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
452
8.74k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
453
301
    if ((stream->http_flags &
454
301
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
455
290
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
456
177
      return -1;
457
177
    }
458
124
    stream->content_length = -1;
459
8.52k
  } else {
460
8.52k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
461
8.52k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
462
847
        (stream->http_flags &
463
7.70k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
464
7.70k
      return -1;
465
7.70k
    }
466
823
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
467
10
        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
468
10
         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
469
10
      return -1;
470
10
    }
471
813
    if (!check_path(stream)) {
472
10
      return -1;
473
10
    }
474
813
  }
475
476
927
  if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
477
    /* we are going to reuse data fields for upcoming response.  Clear
478
       them now, except for method flags. */
479
0
    stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
480
0
    stream->content_length = -1;
481
0
  }
482
483
927
  return 0;
484
8.82k
}
485
486
0
int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
487
0
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
488
0
    return -1;
489
0
  }
490
491
0
  if (stream->status_code / 100 == 1) {
492
    /* non-final response */
493
0
    stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
494
0
                         NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
495
0
    stream->content_length = -1;
496
0
    stream->status_code = -1;
497
0
    return 0;
498
0
  }
499
500
0
  stream->http_flags =
501
0
    stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
502
503
0
  if (!expect_response_body(stream)) {
504
0
    stream->content_length = 0;
505
0
  } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
506
0
                                   NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
507
0
    stream->content_length = -1;
508
0
  }
509
510
0
  return 0;
511
0
}
512
513
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
514
36
                                    nghttp2_frame *frame) {
515
36
  (void)stream;
516
517
36
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
518
15
    return -1;
519
15
  }
520
521
21
  return 0;
522
36
}
523
524
293
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
525
293
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
526
0
    return -1;
527
0
  }
528
529
293
  if (stream->content_length != -1 &&
530
174
      stream->content_length != stream->recv_content_length) {
531
156
    return -1;
532
156
  }
533
534
137
  return 0;
535
293
}
536
537
2.83k
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
538
2.83k
  stream->recv_content_length += (int64_t)n;
539
540
2.83k
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
541
2.83k
      (stream->content_length != -1 &&
542
989
       stream->recv_content_length > stream->content_length)) {
543
23
    return -1;
544
23
  }
545
546
2.81k
  return 0;
547
2.83k
}
548
549
void nghttp2_http_record_request_method(nghttp2_stream *stream,
550
0
                                        nghttp2_frame *frame) {
551
0
  const nghttp2_nv *nva;
552
0
  size_t nvlen;
553
0
  size_t i;
554
555
0
  switch (frame->hd.type) {
556
0
  case NGHTTP2_HEADERS:
557
0
    nva = frame->headers.nva;
558
0
    nvlen = frame->headers.nvlen;
559
0
    break;
560
0
  case NGHTTP2_PUSH_PROMISE:
561
0
    nva = frame->push_promise.nva;
562
0
    nvlen = frame->push_promise.nvlen;
563
0
    break;
564
0
  default:
565
0
    return;
566
0
  }
567
568
  /* TODO we should do this strictly. */
569
0
  for (i = 0; i < nvlen; ++i) {
570
0
    const nghttp2_nv *nv = &nva[i];
571
0
    if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
572
0
          memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
573
0
      continue;
574
0
    }
575
0
    if (lstreq("CONNECT", nv->value, nv->valuelen)) {
576
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
577
0
      return;
578
0
    }
579
0
    if (lstreq("HEAD", nv->value, nv->valuelen)) {
580
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
581
0
      return;
582
0
    }
583
0
    return;
584
0
  }
585
0
}
586
587
int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
588
9.76k
                                size_t valuelen) {
589
9.76k
  nghttp2_extpri pri = *dest;
590
9.76k
  sfparse_parser sfp;
591
9.76k
  sfparse_vec key;
592
9.76k
  sfparse_value val;
593
9.76k
  int rv;
594
595
9.76k
  sfparse_parser_init(&sfp, value, valuelen);
596
597
18.4k
  for (;;) {
598
18.4k
    rv = sfparse_parser_dict(&sfp, &key, &val);
599
18.4k
    if (rv != 0) {
600
9.45k
      if (rv == SFPARSE_ERR_EOF) {
601
5.95k
        break;
602
5.95k
      }
603
604
3.50k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
605
9.45k
    }
606
607
9.02k
    if (key.len != 1) {
608
1.22k
      continue;
609
1.22k
    }
610
611
7.80k
    switch (key.base[0]) {
612
887
    case 'i':
613
887
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
614
68
        return NGHTTP2_ERR_INVALID_ARGUMENT;
615
68
      }
616
617
819
      pri.inc = val.boolean;
618
619
819
      break;
620
540
    case 'u':
621
540
      if (val.type != SFPARSE_TYPE_INTEGER ||
622
472
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
623
362
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
624
246
        return NGHTTP2_ERR_INVALID_ARGUMENT;
625
246
      }
626
627
294
      pri.urgency = (uint32_t)val.integer;
628
629
294
      break;
630
7.80k
    }
631
7.80k
  }
632
633
5.95k
  *dest = pri;
634
635
5.95k
  return 0;
636
9.76k
}