Coverage Report

Created: 2026-07-30 07:04

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
3.75k
static int memieq(const void *a, const void *b, size_t n) {
42
3.75k
  size_t i;
43
3.75k
  const uint8_t *aa = a, *bb = b;
44
45
19.4k
  for (i = 0; i < n; ++i) {
46
16.2k
    if (aa[i] != nghttp2_downcase_byte(bb[i])) {
47
567
      return 0;
48
567
    }
49
16.2k
  }
50
3.18k
  return 1;
51
3.75k
}
52
53
#define lstrieq(A, B, N)                                                       \
54
579
  (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.38k
static int64_t parse_uint(const uint8_t *s, size_t len) {
66
1.38k
  uint64_t n = 0;
67
1.38k
  uint32_t c;
68
1.38k
  size_t i;
69
70
1.38k
  if (len == 0) {
71
44
    return -1;
72
44
  }
73
74
8.91k
  for (i = 0; i < len; ++i) {
75
7.78k
    if ('0' > s[i] || s[i] > '9') {
76
115
      return -1;
77
115
    }
78
79
7.67k
    c = s[i] - '0';
80
81
7.67k
    if (n > ((uint64_t)INT64_MAX - c) / 10) {
82
98
      return -1;
83
98
    }
84
85
7.57k
    n = n * 10 + c;
86
7.57k
  }
87
88
1.13k
  return (int64_t)n;
89
1.34k
}
90
91
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
92
12.0k
                               uint32_t flag) {
93
12.0k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
94
504
    return 0;
95
504
  }
96
11.5k
  stream->http_flags = stream->http_flags | flag;
97
11.5k
  return 1;
98
12.0k
}
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
1.47k
static int check_path(nghttp2_stream *stream) {
112
1.47k
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
113
1.46k
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
114
19
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
115
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
116
1.47k
}
117
118
3.76k
static int check_scheme(const uint8_t *value, size_t len) {
119
3.76k
  const uint8_t *last;
120
3.76k
  if (len == 0) {
121
45
    return 0;
122
45
  }
123
124
3.71k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
125
120
    return 0;
126
120
  }
127
128
3.59k
  last = value + len;
129
3.59k
  ++value;
130
131
20.8k
  for (; value != last; ++value) {
132
17.4k
    if (!(('A' <= *value && *value <= 'Z') ||
133
16.8k
          ('a' <= *value && *value <= 'z') ||
134
6.09k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
135
181
          *value == '.')) {
136
137
      return 0;
137
137
    }
138
17.4k
  }
139
3.46k
  return 1;
140
3.59k
}
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
2.50k
static int check_authority(const uint8_t *value, size_t len) {
169
2.50k
  const uint8_t *last;
170
19.4k
  for (last = value + len; value != last; ++value) {
171
17.2k
    if (!VALID_AUTHORITY_CHARS[*value]) {
172
295
      return 0;
173
295
    }
174
17.2k
  }
175
2.21k
  return 1;
176
2.50k
}
177
178
static int check_header_value(const nghttp2_stream *stream,
179
25.2k
                              const nghttp2_rcbuf *value) {
180
25.2k
  if (stream->flags &
181
25.2k
      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
25.2k
  return nghttp2_check_header_value_rfc9113(value->base, value->len);
186
25.2k
}
187
188
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
189
40.4k
                                  int trailer, int connect_protocol) {
190
40.4k
  nghttp2_extpri extpri;
191
192
40.4k
  switch (nv->token) {
193
1.89k
  case NGHTTP2_TOKEN__AUTHORITY:
194
1.89k
    if (!check_authority(nv->value->base, nv->value->len) ||
195
1.83k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
196
205
      return NGHTTP2_ERR_HTTP_HEADER;
197
205
    }
198
1.68k
    break;
199
3.43k
  case NGHTTP2_TOKEN__METHOD:
200
3.43k
    if (!nghttp2_check_method(nv->value->base, nv->value->len) ||
201
3.28k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
202
202
      return NGHTTP2_ERR_HTTP_HEADER;
203
202
    }
204
3.23k
    switch (nv->value->len) {
205
1.87k
    case 4:
206
1.87k
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
207
18
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
208
18
      }
209
1.87k
      break;
210
127
    case 7:
211
127
      switch (nv->value->base[6]) {
212
72
      case 'T':
213
72
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
214
54
          if (stream->stream_id % 2 == 0) {
215
            /* we won't allow CONNECT for push */
216
0
            return NGHTTP2_ERR_HTTP_HEADER;
217
0
          }
218
54
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
219
54
        }
220
72
        break;
221
72
      case 'S':
222
37
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
223
18
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
224
18
        }
225
37
        break;
226
127
      }
227
127
      break;
228
3.23k
    }
229
3.23k
    break;
230
3.23k
  case NGHTTP2_TOKEN__PATH:
231
3.16k
    if (!nghttp2_check_path(nv->value->base, nv->value->len) ||
232
3.12k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
233
144
      return NGHTTP2_ERR_HTTP_HEADER;
234
144
    }
235
3.01k
    if (nv->value->base[0] == '/') {
236
2.71k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
237
2.71k
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
238
22
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
239
22
    }
240
3.01k
    break;
241
3.76k
  case NGHTTP2_TOKEN__SCHEME:
242
3.76k
    if (!check_scheme(nv->value->base, nv->value->len) ||
243
3.46k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
244
412
      return NGHTTP2_ERR_HTTP_HEADER;
245
412
    }
246
3.35k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
247
2.99k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
248
2.99k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
249
2.99k
    }
250
3.35k
    break;
251
16
  case NGHTTP2_TOKEN__PROTOCOL:
252
16
    if (!connect_protocol ||
253
16
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
254
16
      return NGHTTP2_ERR_HTTP_HEADER;
255
16
    }
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
612
  case NGHTTP2_TOKEN_HOST:
271
612
    if (!check_authority(nv->value->base, nv->value->len)) {
272
234
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
273
234
    }
274
378
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
275
88
      return NGHTTP2_ERR_HTTP_HEADER;
276
88
    }
277
290
    break;
278
1.48k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
279
1.48k
    if (stream->content_length != -1) {
280
96
      return NGHTTP2_ERR_HTTP_HEADER;
281
96
    }
282
1.38k
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
283
1.38k
    if (stream->content_length == -1) {
284
257
      return NGHTTP2_ERR_HTTP_HEADER;
285
257
    }
286
1.13k
    break;
287
1.38k
  }
288
  /* disallowed header fields */
289
1.13k
  case NGHTTP2_TOKEN_CONNECTION:
290
80
  case NGHTTP2_TOKEN_KEEP_ALIVE:
291
96
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
292
146
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
293
186
  case NGHTTP2_TOKEN_UPGRADE:
294
186
    return NGHTTP2_ERR_HTTP_HEADER;
295
579
  case NGHTTP2_TOKEN_TE:
296
579
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
297
385
      return NGHTTP2_ERR_HTTP_HEADER;
298
385
    }
299
194
    break;
300
8.34k
  case NGHTTP2_TOKEN_PRIORITY:
301
8.34k
    if (!check_header_value(stream, nv->value)) {
302
200
      stream->http_flags &= ~NGHTTP2_HTTP_FLAG_PRIORITY;
303
200
      stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
304
305
200
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
306
200
    }
307
308
8.14k
    if (!trailer &&
309
        /* Do not parse the header field in PUSH_PROMISE. */
310
7.95k
        (stream->stream_id & 1) &&
311
7.95k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
312
7.74k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
313
7.74k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
314
7.74k
                                      nv->value->len) == 0) {
315
5.58k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
316
5.58k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
317
5.58k
      } else {
318
2.16k
        stream->http_flags &= ~NGHTTP2_HTTP_FLAG_PRIORITY;
319
2.16k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
320
2.16k
      }
321
7.74k
    }
322
8.14k
    break;
323
16.9k
  default:
324
16.9k
    if (nv->name->base[0] == ':') {
325
88
      return NGHTTP2_ERR_HTTP_HEADER;
326
88
    }
327
328
16.8k
    if (!check_header_value(stream, nv->value)) {
329
1.35k
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
330
1.35k
    }
331
40.4k
  }
332
333
36.5k
  return 0;
334
40.4k
}
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
47.5k
                           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
47.5k
  if (nv->name->len == 0) {
416
3.77k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
417
3.77k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
418
3.77k
  }
419
420
43.7k
  if (nv->name->base[0] == ':') {
421
    /* pseudo header must have a valid token. */
422
12.6k
    if (nv->token == -1 || trailer ||
423
12.5k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
424
267
      return NGHTTP2_ERR_HTTP_HEADER;
425
267
    }
426
31.1k
  } else {
427
31.1k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
428
429
31.1k
    switch (nghttp2_check_nonempty_header_name(nv->name->base, nv->name->len)) {
430
2.78k
    case 0:
431
2.78k
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
432
283
    case 2:
433
      /* header field name must be lower-cased without exception */
434
283
      return NGHTTP2_ERR_HTTP_HEADER;
435
31.1k
    }
436
31.1k
  }
437
438
43.7k
  assert(nv->name->len > 0);
439
440
40.4k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
441
40.4k
    return http_request_on_header(stream, nv, trailer,
442
40.4k
                                  session->server &&
443
40.4k
                                    session->pending_enable_connect_protocol);
444
40.4k
  }
445
446
0
  return http_response_on_header(stream, nv);
447
40.4k
}
448
449
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
450
6.55k
                                    nghttp2_frame *frame) {
451
6.55k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
452
6.55k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
453
53
    if ((stream->http_flags &
454
53
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
455
43
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
456
43
      return -1;
457
43
    }
458
10
    stream->content_length = -1;
459
6.49k
  } else {
460
6.49k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
461
6.49k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
462
2.09k
        (stream->http_flags &
463
5.02k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
464
5.02k
      return -1;
465
5.02k
    }
466
1.47k
    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
1.47k
    if (!check_path(stream)) {
472
19
      return -1;
473
19
    }
474
1.47k
  }
475
476
1.46k
  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
1.46k
  return 0;
484
6.55k
}
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
40
                                    nghttp2_frame *frame) {
515
40
  (void)stream;
516
517
40
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
518
17
    return -1;
519
17
  }
520
521
23
  return 0;
522
40
}
523
524
709
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
525
709
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
526
0
    return -1;
527
0
  }
528
529
709
  if (stream->content_length != -1 &&
530
151
      stream->content_length != stream->recv_content_length) {
531
139
    return -1;
532
139
  }
533
534
570
  return 0;
535
709
}
536
537
285
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
538
285
  stream->recv_content_length += (int64_t)n;
539
540
285
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
541
285
      (stream->content_length != -1 &&
542
161
       stream->recv_content_length > stream->content_length)) {
543
19
    return -1;
544
19
  }
545
546
266
  return 0;
547
285
}
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.74k
                                size_t valuelen) {
589
7.74k
  nghttp2_extpri pri = *dest;
590
7.74k
  sfparse_parser sfp;
591
7.74k
  sfparse_vec key;
592
7.74k
  sfparse_value val;
593
7.74k
  int rv;
594
595
7.74k
  sfparse_parser_init(&sfp, value, valuelen);
596
597
16.8k
  for (;;) {
598
16.8k
    rv = sfparse_parser_dict(&sfp, &key, &val);
599
16.8k
    if (rv != 0) {
600
7.55k
      if (rv == SFPARSE_ERR_EOF) {
601
5.58k
        break;
602
5.58k
      }
603
604
1.97k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
605
7.55k
    }
606
607
9.29k
    if (key.len != 1) {
608
2.73k
      continue;
609
2.73k
    }
610
611
6.55k
    switch (key.base[0]) {
612
650
    case 'i':
613
650
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
614
35
        return NGHTTP2_ERR_INVALID_ARGUMENT;
615
35
      }
616
617
615
      pri.inc = val.boolean;
618
619
615
      break;
620
519
    case 'u':
621
519
      if (val.type != SFPARSE_TYPE_INTEGER ||
622
476
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
623
399
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
624
155
        return NGHTTP2_ERR_INVALID_ARGUMENT;
625
155
      }
626
627
364
      pri.urgency = (uint32_t)val.integer;
628
629
364
      break;
630
6.55k
    }
631
6.55k
  }
632
633
5.58k
  *dest = pri;
634
635
5.58k
  return 0;
636
7.74k
}