Coverage Report

Created: 2024-09-08 06:32

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