Coverage Report

Created: 2024-05-21 06:52

/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
122
static uint8_t downcase(uint8_t c) {
37
122
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
38
122
}
39
40
25
static int memieq(const void *a, const void *b, size_t n) {
41
25
  size_t i;
42
25
  const uint8_t *aa = a, *bb = b;
43
44
63
  for (i = 0; i < n; ++i) {
45
61
    if (downcase(aa[i]) != downcase(bb[i])) {
46
23
      return 0;
47
23
    }
48
61
  }
49
2
  return 1;
50
25
}
51
52
33
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
53
54
4.02k
static int64_t parse_uint(const uint8_t *s, size_t len) {
55
4.02k
  int64_t n = 0;
56
4.02k
  size_t i;
57
4.02k
  if (len == 0) {
58
43
    return -1;
59
43
  }
60
20.7k
  for (i = 0; i < len; ++i) {
61
16.9k
    if ('0' <= s[i] && s[i] <= '9') {
62
16.8k
      if (n > INT64_MAX / 10) {
63
11
        return -1;
64
11
      }
65
16.7k
      n *= 10;
66
16.7k
      if (n > INT64_MAX - (s[i] - '0')) {
67
2
        return -1;
68
2
      }
69
16.7k
      n += s[i] - '0';
70
16.7k
      continue;
71
16.7k
    }
72
128
    return -1;
73
16.9k
  }
74
3.84k
  return n;
75
3.98k
}
76
77
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
78
3.74k
                               uint32_t flag) {
79
3.74k
  if ((stream->http_flags & flag) || nv->value->len == 0) {
80
31
    return 0;
81
31
  }
82
3.71k
  stream->http_flags = stream->http_flags | flag;
83
3.71k
  return 1;
84
3.74k
}
85
86
887
static int expect_response_body(nghttp2_stream *stream) {
87
887
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
88
887
         stream->status_code / 100 != 1 && stream->status_code != 304 &&
89
887
         stream->status_code != 204;
90
887
}
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
133k
                                   int trailer) {
238
133k
  if (nv->name->base[0] == ':') {
239
4.01k
    if (trailer ||
240
4.01k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
241
133
      return NGHTTP2_ERR_HTTP_HEADER;
242
133
    }
243
4.01k
  }
244
245
133k
  switch (nv->token) {
246
3.74k
  case NGHTTP2_TOKEN__STATUS: {
247
3.74k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
248
31
      return NGHTTP2_ERR_HTTP_HEADER;
249
31
    }
250
3.71k
    if (nv->value->len != 3) {
251
8
      return NGHTTP2_ERR_HTTP_HEADER;
252
8
    }
253
3.70k
    stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
254
3.70k
    if (stream->status_code == -1 || stream->status_code == 101) {
255
11
      return NGHTTP2_ERR_HTTP_HEADER;
256
11
    }
257
3.69k
    break;
258
3.70k
  }
259
3.69k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
260
788
    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
18
      if (stream->content_length != -1) {
265
        /* Found multiple content-length field */
266
2
        return NGHTTP2_ERR_HTTP_HEADER;
267
2
      }
268
16
      if (!lstrieq("0", nv->value->base, nv->value->len)) {
269
14
        return NGHTTP2_ERR_HTTP_HEADER;
270
14
      }
271
2
      stream->content_length = 0;
272
2
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
273
16
    }
274
770
    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
769
    if (stream->status_code / 100 == 2 &&
279
769
        (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
280
348
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
281
348
    }
282
421
    if (stream->content_length != -1) {
283
103
      return NGHTTP2_ERR_HTTP_HEADER;
284
103
    }
285
318
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
286
318
    if (stream->content_length == -1) {
287
175
      return NGHTTP2_ERR_HTTP_HEADER;
288
175
    }
289
143
    break;
290
318
  }
291
  /* disallowed header fields */
292
143
  case NGHTTP2_TOKEN_CONNECTION:
293
7
  case NGHTTP2_TOKEN_KEEP_ALIVE:
294
8
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
295
24
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
296
28
  case NGHTTP2_TOKEN_UPGRADE:
297
28
    return NGHTTP2_ERR_HTTP_HEADER;
298
17
  case NGHTTP2_TOKEN_TE:
299
17
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
300
17
      return NGHTTP2_ERR_HTTP_HEADER;
301
17
    }
302
0
    break;
303
129k
  default:
304
129k
    if (nv->name->base[0] == ':') {
305
130
      return NGHTTP2_ERR_HTTP_HEADER;
306
130
    }
307
133k
  }
308
309
132k
  if (nv->name->base[0] != ':') {
310
129k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
311
129k
  }
312
313
132k
  return 0;
314
133k
}
315
316
149
static int check_scheme(const uint8_t *value, size_t len) {
317
149
  const uint8_t *last;
318
149
  if (len == 0) {
319
15
    return 0;
320
15
  }
321
322
134
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
323
19
    return 0;
324
19
  }
325
326
115
  last = value + len;
327
115
  ++value;
328
329
1.73k
  for (; value != last; ++value) {
330
1.66k
    if (!(('A' <= *value && *value <= 'Z') ||
331
1.66k
          ('a' <= *value && *value <= 'z') ||
332
1.66k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
333
1.66k
          *value == '.')) {
334
50
      return 0;
335
50
    }
336
1.66k
  }
337
65
  return 1;
338
115
}
339
340
20
static int lws(const uint8_t *s, size_t n) {
341
20
  size_t i;
342
436
  for (i = 0; i < n; ++i) {
343
431
    if (s[i] != ' ' && s[i] != '\t') {
344
15
      return 0;
345
15
    }
346
431
  }
347
5
  return 1;
348
20
}
349
350
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
351
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
352
134k
                           int trailer) {
353
134k
  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
134k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
363
399
    size_t i;
364
399
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
365
3
      return NGHTTP2_ERR_HTTP_HEADER;
366
3
    }
367
    /* header field name must be lower-cased without exception */
368
3.29k
    for (i = 0; i < nv->name->len; ++i) {
369
2.95k
      uint8_t c = nv->name->base[i];
370
2.95k
      if ('A' <= c && c <= 'Z') {
371
58
        return NGHTTP2_ERR_HTTP_HEADER;
372
58
      }
373
2.95k
    }
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
338
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
378
338
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
379
396
  }
380
381
134k
  switch (nv->token) {
382
81
  case NGHTTP2_TOKEN__METHOD:
383
81
    rv = nghttp2_check_method(nv->value->base, nv->value->len);
384
81
    break;
385
80
  case NGHTTP2_TOKEN__PATH:
386
80
    rv = nghttp2_check_path(nv->value->base, nv->value->len);
387
80
    break;
388
76
  case NGHTTP2_TOKEN__AUTHORITY:
389
3.22k
  case NGHTTP2_TOKEN_HOST:
390
3.22k
    if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
391
0
      rv = nghttp2_check_authority(nv->value->base, nv->value->len);
392
3.22k
    } else if (
393
3.22k
        stream->flags &
394
3.22k
        NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
395
3.22k
      rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
396
3.22k
    } else {
397
0
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
398
0
    }
399
3.22k
    break;
400
149
  case NGHTTP2_TOKEN__SCHEME:
401
149
    rv = check_scheme(nv->value->base, nv->value->len);
402
149
    break;
403
20
  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
20
    if ((stream->flags &
408
20
         NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
409
20
        lws(nv->value->base, nv->value->len)) {
410
5
      rv = 0;
411
5
      break;
412
5
    }
413
    /* fall through */
414
130k
  default:
415
130k
    if (stream->flags &
416
130k
        NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
417
130k
      rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
418
130k
    } else {
419
0
      rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
420
0
    }
421
134k
  }
422
423
134k
  if (rv == 0) {
424
312
    assert(nv->name->len > 0);
425
312
    if (nv->name->base[0] == ':') {
426
197
      return NGHTTP2_ERR_HTTP_HEADER;
427
197
    }
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
115
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
432
115
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
433
312
  }
434
435
133k
  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
133k
  return http_response_on_header(stream, nv, trailer);
442
133k
}
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
961
int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
482
961
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
483
40
    return -1;
484
40
  }
485
486
921
  if (stream->status_code / 100 == 1) {
487
    /* non-final response */
488
34
    stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
489
34
                         NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
490
34
    stream->content_length = -1;
491
34
    stream->status_code = -1;
492
34
    return 0;
493
34
  }
494
495
887
  stream->http_flags =
496
887
      stream->http_flags & (uint32_t)~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
497
498
887
  if (!expect_response_body(stream)) {
499
700
    stream->content_length = 0;
500
700
  } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
501
187
                                   NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
502
2
    stream->content_length = -1;
503
2
  }
504
505
887
  return 0;
506
921
}
507
508
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
509
17
                                    nghttp2_frame *frame) {
510
17
  (void)stream;
511
512
17
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
513
4
    return -1;
514
4
  }
515
516
13
  return 0;
517
17
}
518
519
733
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
520
733
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
521
2
    return -1;
522
2
  }
523
524
731
  if (stream->content_length != -1 &&
525
731
      stream->content_length != stream->recv_content_length) {
526
3
    return -1;
527
3
  }
528
529
728
  return 0;
530
731
}
531
532
207
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
533
207
  stream->recv_content_length += (int64_t)n;
534
535
207
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
536
207
      (stream->content_length != -1 &&
537
203
       stream->recv_content_length > stream->content_length)) {
538
18
    return -1;
539
18
  }
540
541
189
  return 0;
542
207
}
543
544
void nghttp2_http_record_request_method(nghttp2_stream *stream,
545
12.6k
                                        nghttp2_frame *frame) {
546
12.6k
  const nghttp2_nv *nva;
547
12.6k
  size_t nvlen;
548
12.6k
  size_t i;
549
550
12.6k
  switch (frame->hd.type) {
551
12.6k
  case NGHTTP2_HEADERS:
552
12.6k
    nva = frame->headers.nva;
553
12.6k
    nvlen = frame->headers.nvlen;
554
12.6k
    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
12.6k
  }
562
563
  /* TODO we should do this strictly. */
564
12.6k
  for (i = 0; i < nvlen; ++i) {
565
12.6k
    const nghttp2_nv *nv = &nva[i];
566
12.6k
    if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
567
12.6k
          memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
568
0
      continue;
569
0
    }
570
12.6k
    if (lstreq("CONNECT", nv->value, nv->valuelen)) {
571
27
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
572
27
      return;
573
27
    }
574
12.5k
    if (lstreq("HEAD", nv->value, nv->valuelen)) {
575
41
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
576
41
      return;
577
41
    }
578
12.5k
    return;
579
12.5k
  }
580
12.6k
}
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
}