Coverage Report

Created: 2026-07-16 06:32

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
5.24k
static int memieq(const void *a, const void *b, size_t n) {
42
5.24k
  size_t i;
43
5.24k
  const uint8_t *aa = a, *bb = b;
44
45
27.1k
  for (i = 0; i < n; ++i) {
46
22.6k
    if (aa[i] != nghttp2_downcase_byte(bb[i])) {
47
718
      return 0;
48
718
    }
49
22.6k
  }
50
4.52k
  return 1;
51
5.24k
}
52
53
#define lstrieq(A, B, N)                                                       \
54
595
  (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
1.71k
static int64_t parse_uint(const uint8_t *s, size_t len) {
66
1.71k
  uint64_t n = 0;
67
1.71k
  uint32_t c;
68
1.71k
  size_t i;
69
70
1.71k
  if (len == 0) {
71
46
    return -1;
72
46
  }
73
74
9.73k
  for (i = 0; i < len; ++i) {
75
8.51k
    if ('0' > s[i] || s[i] > '9') {
76
341
      return -1;
77
341
    }
78
79
8.17k
    c = s[i] - '0';
80
81
8.17k
    if (n > ((uint64_t)INT64_MAX - c) / 10) {
82
103
      return -1;
83
103
    }
84
85
8.06k
    n = n * 10 + c;
86
8.06k
  }
87
88
1.22k
  return (int64_t)n;
89
1.66k
}
90
91
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
92
17.3k
                               uint32_t flag) {
93
17.3k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
94
542
    return 0;
95
542
  }
96
16.7k
  stream->http_flags = stream->http_flags | flag;
97
16.7k
  return 1;
98
17.3k
}
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
2.45k
static int check_path(nghttp2_stream *stream) {
112
2.45k
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
113
2.44k
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
114
23
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
115
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
116
2.45k
}
117
118
5.27k
static int check_scheme(const uint8_t *value, size_t len) {
119
5.27k
  const uint8_t *last;
120
5.27k
  if (len == 0) {
121
48
    return 0;
122
48
  }
123
124
5.23k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
125
153
    return 0;
126
153
  }
127
128
5.07k
  last = value + len;
129
5.07k
  ++value;
130
131
26.3k
  for (; value != last; ++value) {
132
21.4k
    if (!(('A' <= *value && *value <= 'Z') ||
133
20.7k
          ('a' <= *value && *value <= 'z') ||
134
5.01k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
135
207
          *value == '.')) {
136
133
      return 0;
137
133
    }
138
21.4k
  }
139
4.94k
  return 1;
140
5.07k
}
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
3.49k
static int check_authority(const uint8_t *value, size_t len) {
169
3.49k
  const uint8_t *last;
170
21.2k
  for (last = value + len; value != last; ++value) {
171
18.1k
    if (!VALID_AUTHORITY_CHARS[*value]) {
172
360
      return 0;
173
360
    }
174
18.1k
  }
175
3.13k
  return 1;
176
3.49k
}
177
178
static int check_header_value(const nghttp2_stream *stream,
179
24.6k
                              const nghttp2_rcbuf *value) {
180
24.6k
  if (stream->flags &
181
24.6k
      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
24.6k
  return nghttp2_check_header_value_rfc9113(value->base, value->len);
186
24.6k
}
187
188
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
189
45.6k
                                  int trailer, int connect_protocol) {
190
45.6k
  nghttp2_extpri extpri;
191
192
45.6k
  switch (nv->token) {
193
2.55k
  case NGHTTP2_TOKEN__AUTHORITY:
194
2.55k
    if (!check_authority(nv->value->base, nv->value->len) ||
195
2.43k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
196
263
      return NGHTTP2_ERR_HTTP_HEADER;
197
263
    }
198
2.29k
    break;
199
4.80k
  case NGHTTP2_TOKEN__METHOD:
200
4.80k
    if (!nghttp2_check_method(nv->value->base, nv->value->len) ||
201
4.65k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
202
217
      return NGHTTP2_ERR_HTTP_HEADER;
203
217
    }
204
4.58k
    switch (nv->value->len) {
205
2.44k
    case 4:
206
2.44k
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
207
18
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
208
18
      }
209
2.44k
      break;
210
80
    case 7:
211
80
      switch (nv->value->base[6]) {
212
49
      case 'T':
213
49
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
214
37
          if (stream->stream_id % 2 == 0) {
215
            /* we won't allow CONNECT for push */
216
0
            return NGHTTP2_ERR_HTTP_HEADER;
217
0
          }
218
37
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
219
37
        }
220
49
        break;
221
49
      case 'S':
222
21
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
223
10
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
224
10
        }
225
21
        break;
226
80
      }
227
80
      break;
228
4.58k
    }
229
4.58k
    break;
230
4.64k
  case NGHTTP2_TOKEN__PATH:
231
4.64k
    if (!nghttp2_check_path(nv->value->base, nv->value->len) ||
232
4.60k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
233
175
      return NGHTTP2_ERR_HTTP_HEADER;
234
175
    }
235
4.46k
    if (nv->value->base[0] == '/') {
236
4.17k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
237
4.17k
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
238
21
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
239
21
    }
240
4.46k
    break;
241
5.27k
  case NGHTTP2_TOKEN__SCHEME:
242
5.27k
    if (!check_scheme(nv->value->base, nv->value->len) ||
243
4.94k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
244
460
      return NGHTTP2_ERR_HTTP_HEADER;
245
460
    }
246
4.81k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
247
4.32k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
248
4.32k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
249
4.32k
    }
250
4.81k
    break;
251
9
  case NGHTTP2_TOKEN__PROTOCOL:
252
9
    if (!connect_protocol ||
253
9
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
254
9
      return NGHTTP2_ERR_HTTP_HEADER;
255
9
    }
256
0
    if (stream->flags &
257
0
        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
0
    } else if (!nghttp2_check_header_value_rfc9113(nv->value->base,
266
0
                                                   nv->value->len)) {
267
0
      return NGHTTP2_ERR_HTTP_HEADER;
268
0
    }
269
0
    break;
270
936
  case NGHTTP2_TOKEN_HOST:
271
936
    if (!check_authority(nv->value->base, nv->value->len)) {
272
243
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
273
243
    }
274
693
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
275
71
      return NGHTTP2_ERR_HTTP_HEADER;
276
71
    }
277
622
    break;
278
1.80k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
279
1.80k
    if (stream->content_length != -1) {
280
96
      return NGHTTP2_ERR_HTTP_HEADER;
281
96
    }
282
1.71k
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
283
1.71k
    if (stream->content_length == -1) {
284
490
      return NGHTTP2_ERR_HTTP_HEADER;
285
490
    }
286
1.22k
    break;
287
1.71k
  }
288
  /* disallowed header fields */
289
1.22k
  case NGHTTP2_TOKEN_CONNECTION:
290
80
  case NGHTTP2_TOKEN_KEEP_ALIVE:
291
97
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
292
143
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
293
167
  case NGHTTP2_TOKEN_UPGRADE:
294
167
    return NGHTTP2_ERR_HTTP_HEADER;
295
595
  case NGHTTP2_TOKEN_TE:
296
595
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
297
401
      return NGHTTP2_ERR_HTTP_HEADER;
298
401
    }
299
194
    break;
300
8.52k
  case NGHTTP2_TOKEN_PRIORITY:
301
8.52k
    if (!check_header_value(stream, nv->value)) {
302
328
      stream->http_flags &= ~NGHTTP2_HTTP_FLAG_PRIORITY;
303
328
      stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
304
305
328
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
306
328
    }
307
308
8.19k
    if (!trailer &&
309
        /* Do not parse the header field in PUSH_PROMISE. */
310
8.00k
        (stream->stream_id & 1) &&
311
8.00k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
312
7.79k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
313
7.79k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
314
7.79k
                                      nv->value->len) == 0) {
315
5.54k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
316
5.54k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
317
5.54k
      } else {
318
2.25k
        stream->http_flags &= ~NGHTTP2_HTTP_FLAG_PRIORITY;
319
2.25k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
320
2.25k
      }
321
7.79k
    }
322
8.19k
    break;
323
16.3k
  default:
324
16.3k
    if (nv->name->base[0] == ':') {
325
153
      return NGHTTP2_ERR_HTTP_HEADER;
326
153
    }
327
328
16.1k
    if (!check_header_value(stream, nv->value)) {
329
911
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
330
911
    }
331
45.6k
  }
332
333
41.6k
  return 0;
334
45.6k
}
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
52.9k
                           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
52.9k
  if (nv->name->len == 0) {
416
3.59k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
417
3.59k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
418
3.59k
  }
419
420
49.4k
  if (nv->name->base[0] == ':') {
421
    /* pseudo header must have a valid token. */
422
17.9k
    if (nv->token == -1 || trailer ||
423
17.7k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
424
460
      return NGHTTP2_ERR_HTTP_HEADER;
425
460
    }
426
31.4k
  } else {
427
31.4k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
428
429
31.4k
    switch (nghttp2_check_nonempty_header_name(nv->name->base, nv->name->len)) {
430
3.03k
    case 0:
431
3.03k
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
432
261
    case 2:
433
      /* header field name must be lower-cased without exception */
434
261
      return NGHTTP2_ERR_HTTP_HEADER;
435
31.4k
    }
436
31.4k
  }
437
438
49.4k
  assert(nv->name->len > 0);
439
440
45.6k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
441
45.6k
    return http_request_on_header(stream, nv, trailer,
442
45.6k
                                  session->server &&
443
45.6k
                                    session->pending_enable_connect_protocol);
444
45.6k
  }
445
446
0
  return http_response_on_header(stream, nv);
447
45.6k
}
448
449
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
450
4.99k
                                    nghttp2_frame *frame) {
451
4.99k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
452
4.99k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
453
34
    if ((stream->http_flags &
454
34
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
455
23
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
456
23
      return -1;
457
23
    }
458
11
    stream->content_length = -1;
459
4.96k
  } else {
460
4.96k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
461
4.96k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
462
2.91k
        (stream->http_flags &
463
2.91k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
464
2.51k
      return -1;
465
2.51k
    }
466
2.45k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
467
0
        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
468
0
         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
469
0
      return -1;
470
0
    }
471
2.45k
    if (!check_path(stream)) {
472
23
      return -1;
473
23
    }
474
2.45k
  }
475
476
2.43k
  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
2.43k
  return 0;
484
4.99k
}
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
28
                                    nghttp2_frame *frame) {
515
28
  (void)stream;
516
517
28
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
518
9
    return -1;
519
9
  }
520
521
19
  return 0;
522
28
}
523
524
963
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
525
963
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
526
0
    return -1;
527
0
  }
528
529
963
  if (stream->content_length != -1 &&
530
139
      stream->content_length != stream->recv_content_length) {
531
129
    return -1;
532
129
  }
533
534
834
  return 0;
535
963
}
536
537
301
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
538
301
  stream->recv_content_length += (int64_t)n;
539
540
301
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
541
301
      (stream->content_length != -1 &&
542
159
       stream->recv_content_length > stream->content_length)) {
543
19
    return -1;
544
19
  }
545
546
282
  return 0;
547
301
}
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
7.79k
                                size_t valuelen) {
589
7.79k
  nghttp2_extpri pri = *dest;
590
7.79k
  sfparse_parser sfp;
591
7.79k
  sfparse_vec key;
592
7.79k
  sfparse_value val;
593
7.79k
  int rv;
594
595
7.79k
  sfparse_parser_init(&sfp, value, valuelen);
596
597
17.1k
  for (;;) {
598
17.1k
    rv = sfparse_parser_dict(&sfp, &key, &val);
599
17.1k
    if (rv != 0) {
600
7.60k
      if (rv == SFPARSE_ERR_EOF) {
601
5.54k
        break;
602
5.54k
      }
603
604
2.05k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
605
7.60k
    }
606
607
9.50k
    if (key.len != 1) {
608
3.04k
      continue;
609
3.04k
    }
610
611
6.46k
    switch (key.base[0]) {
612
677
    case 'i':
613
677
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
614
34
        return NGHTTP2_ERR_INVALID_ARGUMENT;
615
34
      }
616
617
643
      pri.inc = val.boolean;
618
619
643
      break;
620
500
    case 'u':
621
500
      if (val.type != SFPARSE_TYPE_INTEGER ||
622
453
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
623
377
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
624
161
        return NGHTTP2_ERR_INVALID_ARGUMENT;
625
161
      }
626
627
339
      pri.urgency = (uint32_t)val.integer;
628
629
339
      break;
630
6.46k
    }
631
6.46k
  }
632
633
5.54k
  *dest = pri;
634
635
5.54k
  return 0;
636
7.79k
}