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.32k
static int memieq(const void *a, const void *b, size_t n) {
42
2.32k
  size_t i;
43
2.32k
  const uint8_t *aa = a, *bb = b;
44
45
11.7k
  for (i = 0; i < n; ++i) {
46
10.1k
    if (aa[i] != nghttp2_downcase_byte(bb[i])) {
47
723
      return 0;
48
723
    }
49
10.1k
  }
50
1.60k
  return 1;
51
2.32k
}
52
53
#define lstrieq(A, B, N)                                                       \
54
602
  (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
744
static int64_t parse_uint(const uint8_t *s, size_t len) {
66
744
  uint64_t n = 0;
67
744
  uint32_t c;
68
744
  size_t i;
69
70
744
  if (len == 0) {
71
54
    return -1;
72
54
  }
73
74
6.93k
  for (i = 0; i < len; ++i) {
75
6.38k
    if ('0' > s[i] || s[i] > '9') {
76
46
      return -1;
77
46
    }
78
79
6.34k
    c = s[i] - '0';
80
81
6.34k
    if (n > ((uint64_t)INT64_MAX - c) / 10) {
82
97
      return -1;
83
97
    }
84
85
6.24k
    n = n * 10 + c;
86
6.24k
  }
87
88
547
  return (int64_t)n;
89
690
}
90
91
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
92
7.13k
                               uint32_t flag) {
93
7.13k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
94
441
    return 0;
95
441
  }
96
6.69k
  stream->http_flags = stream->http_flags | flag;
97
6.69k
  return 1;
98
7.13k
}
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
988
static int check_path(nghttp2_stream *stream) {
112
988
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
113
978
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
114
18
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
115
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
116
988
}
117
118
2.38k
static int check_scheme(const uint8_t *value, size_t len) {
119
2.38k
  const uint8_t *last;
120
2.38k
  if (len == 0) {
121
46
    return 0;
122
46
  }
123
124
2.33k
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
125
131
    return 0;
126
131
  }
127
128
2.20k
  last = value + len;
129
2.20k
  ++value;
130
131
14.9k
  for (; value != last; ++value) {
132
12.8k
    if (!(('A' <= *value && *value <= 'Z') ||
133
12.0k
          ('a' <= *value && *value <= 'z') ||
134
5.87k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
135
191
          *value == '.')) {
136
125
      return 0;
137
125
    }
138
12.8k
  }
139
2.08k
  return 1;
140
2.20k
}
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.82k
static int check_authority(const uint8_t *value, size_t len) {
169
1.82k
  const uint8_t *last;
170
9.82k
  for (last = value + len; value != last; ++value) {
171
8.34k
    if (!VALID_AUTHORITY_CHARS[*value]) {
172
334
      return 0;
173
334
    }
174
8.34k
  }
175
1.48k
  return 1;
176
1.82k
}
177
178
static int check_header_value(const nghttp2_stream *stream,
179
22.3k
                              const nghttp2_rcbuf *value) {
180
22.3k
  if (stream->flags &
181
22.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
22.3k
  return nghttp2_check_header_value_rfc9113(value->base, value->len);
186
22.3k
}
187
188
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
189
32.0k
                                  int trailer, int connect_protocol) {
190
32.0k
  nghttp2_extpri extpri;
191
192
32.0k
  switch (nv->token) {
193
1.26k
  case NGHTTP2_TOKEN__AUTHORITY:
194
1.26k
    if (!check_authority(nv->value->base, nv->value->len) ||
195
1.17k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
196
212
      return NGHTTP2_ERR_HTTP_HEADER;
197
212
    }
198
1.05k
    break;
199
1.95k
  case NGHTTP2_TOKEN__METHOD:
200
1.95k
    if (!nghttp2_check_method(nv->value->base, nv->value->len) ||
201
1.77k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
202
224
      return NGHTTP2_ERR_HTTP_HEADER;
203
224
    }
204
1.72k
    switch (nv->value->len) {
205
744
    case 4:
206
744
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
207
34
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
208
34
      }
209
744
      break;
210
218
    case 7:
211
218
      switch (nv->value->base[6]) {
212
115
      case 'T':
213
115
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
214
81
          if (stream->stream_id % 2 == 0) {
215
            /* we won't allow CONNECT for push */
216
0
            return NGHTTP2_ERR_HTTP_HEADER;
217
0
          }
218
81
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
219
81
        }
220
115
        break;
221
115
      case 'S':
222
69
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
223
34
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
224
34
        }
225
69
        break;
226
218
      }
227
218
      break;
228
1.72k
    }
229
1.72k
    break;
230
1.83k
  case NGHTTP2_TOKEN__PATH:
231
1.83k
    if (!nghttp2_check_path(nv->value->base, nv->value->len) ||
232
1.79k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
233
127
      return NGHTTP2_ERR_HTTP_HEADER;
234
127
    }
235
1.70k
    if (nv->value->base[0] == '/') {
236
1.32k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
237
1.32k
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
238
238
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
239
238
    }
240
1.70k
    break;
241
2.38k
  case NGHTTP2_TOKEN__SCHEME:
242
2.38k
    if (!check_scheme(nv->value->base, nv->value->len) ||
243
2.08k
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
244
407
      return NGHTTP2_ERR_HTTP_HEADER;
245
407
    }
246
1.97k
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
247
1.41k
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
248
1.41k
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
249
1.41k
    }
250
1.97k
    break;
251
40
  case NGHTTP2_TOKEN__PROTOCOL:
252
40
    if (!connect_protocol ||
253
40
        !check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
254
40
      return NGHTTP2_ERR_HTTP_HEADER;
255
40
    }
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
551
  case NGHTTP2_TOKEN_HOST:
271
551
    if (!check_authority(nv->value->base, nv->value->len)) {
272
239
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
273
239
    }
274
312
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
275
87
      return NGHTTP2_ERR_HTTP_HEADER;
276
87
    }
277
225
    break;
278
840
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
279
840
    if (stream->content_length != -1) {
280
96
      return NGHTTP2_ERR_HTTP_HEADER;
281
96
    }
282
744
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
283
744
    if (stream->content_length == -1) {
284
197
      return NGHTTP2_ERR_HTTP_HEADER;
285
197
    }
286
547
    break;
287
744
  }
288
  /* disallowed header fields */
289
547
  case NGHTTP2_TOKEN_CONNECTION:
290
81
  case NGHTTP2_TOKEN_KEEP_ALIVE:
291
121
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
292
184
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
293
224
  case NGHTTP2_TOKEN_UPGRADE:
294
224
    return NGHTTP2_ERR_HTTP_HEADER;
295
602
  case NGHTTP2_TOKEN_TE:
296
602
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
297
408
      return NGHTTP2_ERR_HTTP_HEADER;
298
408
    }
299
194
    break;
300
7.86k
  case NGHTTP2_TOKEN_PRIORITY:
301
7.86k
    if (!check_header_value(stream, nv->value)) {
302
194
      stream->http_flags &= ~NGHTTP2_HTTP_FLAG_PRIORITY;
303
194
      stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
304
305
194
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
306
194
    }
307
308
7.67k
    if (!trailer &&
309
        /* Do not parse the header field in PUSH_PROMISE. */
310
7.47k
        (stream->stream_id & 1) &&
311
7.47k
        !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
312
7.25k
      nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
313
7.25k
      if (nghttp2_http_parse_priority(&extpri, nv->value->base,
314
7.25k
                                      nv->value->len) == 0) {
315
5.13k
        stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
316
5.13k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
317
5.13k
      } else {
318
2.11k
        stream->http_flags &= ~NGHTTP2_HTTP_FLAG_PRIORITY;
319
2.11k
        stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
320
2.11k
      }
321
7.25k
    }
322
7.67k
    break;
323
14.5k
  default:
324
14.5k
    if (nv->name->base[0] == ':') {
325
92
      return NGHTTP2_ERR_HTTP_HEADER;
326
92
    }
327
328
14.4k
    if (!check_header_value(stream, nv->value)) {
329
1.11k
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
330
1.11k
    }
331
32.0k
  }
332
333
28.4k
  return 0;
334
32.0k
}
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
39.4k
                           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
39.4k
  if (nv->name->len == 0) {
416
4.28k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
417
4.28k
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
418
4.28k
  }
419
420
35.1k
  if (nv->name->base[0] == ':') {
421
    /* pseudo header must have a valid token. */
422
7.76k
    if (nv->token == -1 || trailer ||
423
7.67k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
424
198
      return NGHTTP2_ERR_HTTP_HEADER;
425
198
    }
426
27.3k
  } else {
427
27.3k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
428
429
27.3k
    switch (nghttp2_check_nonempty_header_name(nv->name->base, nv->name->len)) {
430
2.56k
    case 0:
431
2.56k
      return NGHTTP2_ERR_IGN_HTTP_HEADER;
432
308
    case 2:
433
      /* header field name must be lower-cased without exception */
434
308
      return NGHTTP2_ERR_HTTP_HEADER;
435
27.3k
    }
436
27.3k
  }
437
438
35.1k
  assert(nv->name->len > 0);
439
440
32.0k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
441
32.0k
    return http_request_on_header(stream, nv, trailer,
442
32.0k
                                  session->server &&
443
32.0k
                                    session->pending_enable_connect_protocol);
444
32.0k
  }
445
446
0
  return http_response_on_header(stream, nv);
447
32.0k
}
448
449
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
450
6.65k
                                    nghttp2_frame *frame) {
451
6.65k
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
452
6.65k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
453
81
    if ((stream->http_flags &
454
81
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
455
71
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
456
71
      return -1;
457
71
    }
458
10
    stream->content_length = -1;
459
6.57k
  } else {
460
6.57k
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
461
6.57k
          NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
462
1.07k
        (stream->http_flags &
463
5.58k
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
464
5.58k
      return -1;
465
5.58k
    }
466
988
    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
988
    if (!check_path(stream)) {
472
18
      return -1;
473
18
    }
474
988
  }
475
476
980
  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
980
  return 0;
484
6.65k
}
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
78
                                    nghttp2_frame *frame) {
515
78
  (void)stream;
516
517
78
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
518
52
    return -1;
519
52
  }
520
521
26
  return 0;
522
78
}
523
524
422
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
525
422
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
526
0
    return -1;
527
0
  }
528
529
422
  if (stream->content_length != -1 &&
530
148
      stream->content_length != stream->recv_content_length) {
531
136
    return -1;
532
136
  }
533
534
286
  return 0;
535
422
}
536
537
295
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
538
295
  stream->recv_content_length += (int64_t)n;
539
540
295
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
541
295
      (stream->content_length != -1 &&
542
161
       stream->recv_content_length > stream->content_length)) {
543
20
    return -1;
544
20
  }
545
546
275
  return 0;
547
295
}
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.25k
                                size_t valuelen) {
589
7.25k
  nghttp2_extpri pri = *dest;
590
7.25k
  sfparse_parser sfp;
591
7.25k
  sfparse_vec key;
592
7.25k
  sfparse_value val;
593
7.25k
  int rv;
594
595
7.25k
  sfparse_parser_init(&sfp, value, valuelen);
596
597
15.7k
  for (;;) {
598
15.7k
    rv = sfparse_parser_dict(&sfp, &key, &val);
599
15.7k
    if (rv != 0) {
600
7.06k
      if (rv == SFPARSE_ERR_EOF) {
601
5.13k
        break;
602
5.13k
      }
603
604
1.92k
      return NGHTTP2_ERR_INVALID_ARGUMENT;
605
7.06k
    }
606
607
8.73k
    if (key.len != 1) {
608
2.22k
      continue;
609
2.22k
    }
610
611
6.50k
    switch (key.base[0]) {
612
693
    case 'i':
613
693
      if (val.type != SFPARSE_TYPE_BOOLEAN) {
614
36
        return NGHTTP2_ERR_INVALID_ARGUMENT;
615
36
      }
616
617
657
      pri.inc = val.boolean;
618
619
657
      break;
620
550
    case 'u':
621
550
      if (val.type != SFPARSE_TYPE_INTEGER ||
622
509
          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
623
429
          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
624
158
        return NGHTTP2_ERR_INVALID_ARGUMENT;
625
158
      }
626
627
392
      pri.urgency = (uint32_t)val.integer;
628
629
392
      break;
630
6.50k
    }
631
6.50k
  }
632
633
5.13k
  *dest = pri;
634
635
5.13k
  return 0;
636
7.25k
}