Coverage Report

Created: 2026-06-15 06:53

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
9.48k
static int memieq(const void *a, const void *b, size_t n) {
42
9.48k
  size_t i;
43
9.48k
  const uint8_t *aa = a, *bb = b;
44
45
51.4k
  for (i = 0; i < n; ++i) {
46
42.5k
    if (aa[i] != nghttp2_downcase_byte(bb[i])) {
47
617
      return 0;
48
617
    }
49
42.5k
  }
50
8.87k
  return 1;
51
9.48k
}
52
53
#define lstrieq(A, B, N)                                                       \
54
1.24k
  (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
4.20k
static int64_t parse_uint(const uint8_t *s, size_t len) {
66
4.20k
  uint64_t n = 0;
67
4.20k
  uint32_t c;
68
4.20k
  size_t i;
69
70
4.20k
  if (len == 0) {
71
52
    return -1;
72
52
  }
73
74
15.7k
  for (i = 0; i < len; ++i) {
75
12.0k
    if ('0' > s[i] || s[i] > '9') {
76
447
      return -1;
77
447
    }
78
79
11.5k
    c = s[i] - '0';
80
81
11.5k
    if (n > ((uint64_t)INT64_MAX - c) / 10) {
82
7
      return -1;
83
7
    }
84
85
11.5k
    n = n * 10 + c;
86
11.5k
  }
87
88
3.70k
  return (int64_t)n;
89
4.15k
}
90
91
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
92
30.3k
                               uint32_t flag) {
93
30.3k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
94
509
    return 0;
95
509
  }
96
29.8k
  stream->http_flags = stream->http_flags | flag;
97
29.8k
  return 1;
98
30.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
3.43k
static int check_path(nghttp2_stream *stream) {
112
3.43k
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
113
3.42k
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
114
16
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
115
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
116
3.43k
}
117
118
8.63k
static int check_scheme(const uint8_t *value, size_t len) {
119
8.63k
  const uint8_t *last;
120
8.63k
  if (len == 0) {
121
27
    return 0;
122
27
  }
123
124
8.60k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
125
90
    return 0;
126
90
  }
127
128
8.51k
  last = value + len;
129
8.51k
  ++value;
130
131
41.0k
  for (; value != last; ++value) {
132
32.6k
    if (!(('A' <= *value && *value <= 'Z') ||
133
32.1k
          ('a' <= *value && *value <= 'z') ||
134
5.26k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
135
141
          *value == '.')) {
136
50
      return 0;
137
50
    }
138
32.6k
  }
139
8.46k
  return 1;
140
8.51k
}
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
4.22k
static int check_authority(const uint8_t *value, size_t len) {
169
4.22k
  const uint8_t *last;
170
24.3k
  for (last = value + len; value != last; ++value) {
171
20.2k
    if (!VALID_AUTHORITY_CHARS[*value]) {
172
154
      return 0;
173
154
    }
174
20.2k
  }
175
4.07k
  return 1;
176
4.22k
}
177
178
static int check_header_value(const nghttp2_stream *stream,
179
24.3k
                              const nghttp2_rcbuf *value) {
180
24.3k
  if (stream->flags &
181
24.3k
      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.3k
  return nghttp2_check_header_value_rfc9113(value->base, value->len);
186
24.3k
}
187
188
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
189
89.6k
                                  int trailer, int connect_protocol) {
190
89.6k
  nghttp2_extpri extpri;
191
192
89.6k
  switch (nv->token) {
193
2.85k
  case NGHTTP2_TOKEN__AUTHORITY:
194
2.85k
    if (!check_authority(nv->value->base, nv->value->len) ||
195
2.80k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
196
167
      return NGHTTP2_ERR_HTTP_HEADER;
197
167
    }
198
2.69k
    break;
199
9.01k
  case NGHTTP2_TOKEN__METHOD:
200
9.01k
    if (!nghttp2_check_method(nv->value->base, nv->value->len) ||
201
8.93k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
202
141
      return NGHTTP2_ERR_HTTP_HEADER;
203
141
    }
204
8.87k
    switch (nv->value->len) {
205
6.14k
    case 4:
206
6.14k
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
207
1
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
208
1
      }
209
6.14k
      break;
210
49
    case 7:
211
49
      switch (nv->value->base[6]) {
212
31
      case 'T':
213
31
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
214
20
          if (stream->stream_id % 2 == 0) {
215
            /* we won't allow CONNECT for push */
216
0
            return NGHTTP2_ERR_HTTP_HEADER;
217
0
          }
218
20
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
219
20
        }
220
31
        break;
221
31
      case 'S':
222
8
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
223
1
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
224
1
        }
225
8
        break;
226
49
      }
227
49
      break;
228
8.87k
    }
229
8.87k
    break;
230
8.90k
  case NGHTTP2_TOKEN__PATH:
231
8.90k
    if (!nghttp2_check_path(nv->value->base, nv->value->len) ||
232
8.87k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
233
172
      return NGHTTP2_ERR_HTTP_HEADER;
234
172
    }
235
8.73k
    if (nv->value->base[0] == '/') {
236
8.34k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
237
8.34k
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
238
13
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
239
13
    }
240
8.73k
    break;
241
8.63k
  case NGHTTP2_TOKEN__SCHEME:
242
8.63k
    if (!check_scheme(nv->value->base, nv->value->len) ||
243
8.46k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
244
286
      return NGHTTP2_ERR_HTTP_HEADER;
245
286
    }
246
8.34k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
247
8.08k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
248
8.08k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
249
8.08k
    }
250
8.34k
    break;
251
6
  case NGHTTP2_TOKEN__PROTOCOL:
252
6
    if (!connect_protocol ||
253
6
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
254
6
      return NGHTTP2_ERR_HTTP_HEADER;
255
6
    }
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
1.36k
  case NGHTTP2_TOKEN_HOST:
271
1.36k
    if (!check_authority(nv->value->base, nv->value->len)) {
272
96
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
273
96
    }
274
1.27k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
275
91
      return NGHTTP2_ERR_HTTP_HEADER;
276
91
    }
277
1.17k
    break;
278
4.30k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
279
4.30k
    if (stream->content_length != -1) {
280
94
      return NGHTTP2_ERR_HTTP_HEADER;
281
94
    }
282
4.20k
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
283
4.20k
    if (stream->content_length == -1) {
284
506
      return NGHTTP2_ERR_HTTP_HEADER;
285
506
    }
286
3.70k
    break;
287
4.20k
  }
288
  /* disallowed header fields */
289
3.70k
  case NGHTTP2_TOKEN_CONNECTION:
290
56
  case NGHTTP2_TOKEN_KEEP_ALIVE:
291
61
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
292
149
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
293
154
  case NGHTTP2_TOKEN_UPGRADE:
294
154
    return NGHTTP2_ERR_HTTP_HEADER;
295
1.24k
  case NGHTTP2_TOKEN_TE:
296
1.24k
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
297
451
      return NGHTTP2_ERR_HTTP_HEADER;
298
451
    }
299
789
    break;
300
28.7k
  case NGHTTP2_TOKEN_PRIORITY:
301
28.7k
    if (!trailer &&
302
        /* Do not parse the header field in PUSH_PROMISE. */
303
28.5k
        (stream->stream_id & 1) &&
304
28.5k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
305
28.2k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
306
28.2k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
307
28.2k
                                      nv->value->len) == 0) {
308
21.2k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
309
21.2k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
310
21.2k
      } else {
311
7.01k
        stream->http_flags &= ~NGHTTP2_HTTP_FLAG_PRIORITY;
312
7.01k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
313
7.01k
      }
314
28.2k
    }
315
28.7k
    break;
316
24.4k
  default:
317
24.4k
    if (nv->name->base[0] == ':') {
318
107
      return NGHTTP2_ERR_HTTP_HEADER;
319
107
    }
320
321
24.3k
    if (!check_header_value(stream, nv->value)) {
322
692
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
323
692
    }
324
89.6k
  }
325
326
86.6k
  return 0;
327
89.6k
}
328
329
0
static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv) {
330
0
  switch (nv->token) {
331
0
  case NGHTTP2_TOKEN__STATUS: {
332
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
333
0
      return NGHTTP2_ERR_HTTP_HEADER;
334
0
    }
335
0
    stream->status_code = parse_status_code(nv->value->base, nv->value->len);
336
0
    if (stream->status_code == -1 || stream->status_code == 101) {
337
0
      return NGHTTP2_ERR_HTTP_HEADER;
338
0
    }
339
0
    break;
340
0
  }
341
0
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
342
0
    if (stream->status_code == 204) {
343
      /* content-length header field in 204 response is prohibited by
344
         RFC 7230.  But some widely used servers send content-length:
345
         0.  Until they get fixed, we ignore it. */
346
0
      if (stream->content_length != -1) {
347
        /* Found multiple content-length field */
348
0
        return NGHTTP2_ERR_HTTP_HEADER;
349
0
      }
350
0
      if (!lstrieq("0", nv->value->base, nv->value->len)) {
351
0
        return NGHTTP2_ERR_HTTP_HEADER;
352
0
      }
353
0
      stream->content_length = 0;
354
0
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
355
0
    }
356
0
    if (stream->status_code / 100 == 1) {
357
0
      return NGHTTP2_ERR_HTTP_HEADER;
358
0
    }
359
    /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
360
0
    if (stream->status_code / 100 == 2 &&
361
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
362
0
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
363
0
    }
364
0
    if (stream->content_length != -1) {
365
0
      return NGHTTP2_ERR_HTTP_HEADER;
366
0
    }
367
0
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
368
0
    if (stream->content_length == -1) {
369
0
      return NGHTTP2_ERR_HTTP_HEADER;
370
0
    }
371
0
    break;
372
0
  }
373
  /* disallowed header fields */
374
0
  case NGHTTP2_TOKEN_CONNECTION:
375
0
  case NGHTTP2_TOKEN_KEEP_ALIVE:
376
0
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
377
0
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
378
0
  case NGHTTP2_TOKEN_UPGRADE:
379
0
    return NGHTTP2_ERR_HTTP_HEADER;
380
0
  case NGHTTP2_TOKEN_TE:
381
0
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
382
0
      return NGHTTP2_ERR_HTTP_HEADER;
383
0
    }
384
0
    break;
385
0
  default:
386
0
    if (nv->name->base[0] == ':') {
387
0
      return NGHTTP2_ERR_HTTP_HEADER;
388
0
    }
389
390
0
    if (!check_header_value(stream, nv->value)) {
391
0
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
392
0
    }
393
0
  }
394
395
0
  return 0;
396
0
}
397
398
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
399
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
400
98.7k
                           int trailer) {
401
  /* We are strict for pseudo header field.  One bad character should
402
     lead to fail.  OTOH, we should be a bit forgiving for regular
403
     headers, since existing public internet has so much illegal
404
     headers floating around and if we kill the stream because of
405
     this, we may disrupt many web sites and/or libraries.  So we
406
     become conservative here, and just ignore those illegal regular
407
     headers. */
408
98.7k
  if (nv->name->len == 0) {
409
3.55k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
410
3.55k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
411
3.55k
  }
412
413
95.2k
  if (nv->name->base[0] == ':') {
414
    /* pseudo header must have a valid token. */
415
31.2k
    if (nv->token == -1 || trailer ||
416
31.1k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
417
1.75k
      return NGHTTP2_ERR_HTTP_HEADER;
418
1.75k
    }
419
63.9k
  } else {
420
63.9k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
421
422
63.9k
    switch (nghttp2_check_nonempty_header_name(nv->name->base, nv->name->len)) {
423
3.59k
    case 0:
424
3.59k
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
425
195
    case 2:
426
      /* header field name must be lower-cased without exception */
427
195
      return NGHTTP2_ERR_HTTP_HEADER;
428
63.9k
    }
429
63.9k
  }
430
431
95.2k
  assert(nv->name->len > 0);
432
433
89.6k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
434
89.6k
    return http_request_on_header(stream, nv, trailer,
435
89.6k
                                  session->server &&
436
89.6k
                                    session->pending_enable_connect_protocol);
437
89.6k
  }
438
439
0
  return http_response_on_header(stream, nv);
440
89.6k
}
441
442
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
443
6.70k
                                    nghttp2_frame *frame) {
444
6.70k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
445
6.70k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
446
19
    if ((stream->http_flags &
447
19
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
448
18
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
449
8
      return -1;
450
8
    }
451
11
    stream->content_length = -1;
452
6.68k
  } else {
453
6.68k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
454
6.68k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
455
5.53k
        (stream->http_flags &
456
5.53k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
457
3.24k
      return -1;
458
3.24k
    }
459
3.43k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
460
0
        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
461
0
         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
462
0
      return -1;
463
0
    }
464
3.43k
    if (!check_path(stream)) {
465
16
      return -1;
466
16
    }
467
3.43k
  }
468
469
3.43k
  if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
470
    /* we are going to reuse data fields for upcoming response.  Clear
471
       them now, except for method flags. */
472
0
    stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
473
0
    stream->content_length = -1;
474
0
  }
475
476
3.43k
  return 0;
477
6.70k
}
478
479
0
int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
480
0
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
481
0
    return -1;
482
0
  }
483
484
0
  if (stream->status_code / 100 == 1) {
485
    /* non-final response */
486
0
    stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
487
0
                         NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
488
0
    stream->content_length = -1;
489
0
    stream->status_code = -1;
490
0
    return 0;
491
0
  }
492
493
0
  stream->http_flags =
494
0
    stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
495
496
0
  if (!expect_response_body(stream)) {
497
0
    stream->content_length = 0;
498
0
  } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
499
0
                                   NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
500
0
    stream->content_length = -1;
501
0
  }
502
503
0
  return 0;
504
0
}
505
506
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
507
26
                                    nghttp2_frame *frame) {
508
26
  (void)stream;
509
510
26
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
511
6
    return -1;
512
6
  }
513
514
20
  return 0;
515
26
}
516
517
1.18k
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
518
1.18k
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
519
0
    return -1;
520
0
  }
521
522
1.18k
  if (stream->content_length != -1 &&
523
135
      stream->content_length != stream->recv_content_length) {
524
108
    return -1;
525
108
  }
526
527
1.07k
  return 0;
528
1.18k
}
529
530
268
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
531
268
  stream->recv_content_length += (int64_t)n;
532
533
268
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
534
268
      (stream->content_length != -1 &&
535
140
       stream->recv_content_length > stream->content_length)) {
536
13
    return -1;
537
13
  }
538
539
255
  return 0;
540
268
}
541
542
void nghttp2_http_record_request_method(nghttp2_stream *stream,
543
0
                                        nghttp2_frame *frame) {
544
0
  const nghttp2_nv *nva;
545
0
  size_t nvlen;
546
0
  size_t i;
547
548
0
  switch (frame->hd.type) {
549
0
  case NGHTTP2_HEADERS:
550
0
    nva = frame->headers.nva;
551
0
    nvlen = frame->headers.nvlen;
552
0
    break;
553
0
  case NGHTTP2_PUSH_PROMISE:
554
0
    nva = frame->push_promise.nva;
555
0
    nvlen = frame->push_promise.nvlen;
556
0
    break;
557
0
  default:
558
0
    return;
559
0
  }
560
561
  /* TODO we should do this strictly. */
562
0
  for (i = 0; i < nvlen; ++i) {
563
0
    const nghttp2_nv *nv = &nva[i];
564
0
    if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
565
0
          memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
566
0
      continue;
567
0
    }
568
0
    if (lstreq("CONNECT", nv->value, nv->valuelen)) {
569
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
570
0
      return;
571
0
    }
572
0
    if (lstreq("HEAD", nv->value, nv->valuelen)) {
573
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
574
0
      return;
575
0
    }
576
0
    return;
577
0
  }
578
0
}
579
580
int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
581
28.2k
                                size_t valuelen) {
582
28.2k
  nghttp2_extpri pri = *dest;
583
28.2k
  sfparse_parser sfp;
584
28.2k
  sfparse_vec key;
585
28.2k
  sfparse_value val;
586
28.2k
  int rv;
587
588
28.2k
  sfparse_parser_init(&sfp, value, valuelen);
589
590
61.1k
  for (;;) {
591
61.1k
    rv = sfparse_parser_dict(&sfp, &key, &val);
592
61.1k
    if (rv != 0) {
593
28.0k
      if (rv == SFPARSE_ERR_EOF) {
594
21.2k
        break;
595
21.2k
      }
596
597
6.76k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
598
28.0k
    }
599
600
33.1k
    if (key.len != 1) {
601
9.77k
      continue;
602
9.77k
    }
603
604
23.3k
    switch (key.base[0]) {
605
856
    case 'i':
606
856
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
607
96
        return NGHTTP2_ERR_INVALID_ARGUMENT;
608
96
      }
609
610
760
      pri.inc = val.boolean;
611
612
760
      break;
613
407
    case 'u':
614
407
      if (val.type != SFPARSE_TYPE_INTEGER ||
615
354
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
616
292
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
617
156
        return NGHTTP2_ERR_INVALID_ARGUMENT;
618
156
      }
619
620
251
      pri.urgency = (uint32_t)val.integer;
621
622
251
      break;
623
23.3k
    }
624
23.3k
  }
625
626
21.2k
  *dest = pri;
627
628
21.2k
  return 0;
629
28.2k
}