Coverage Report

Created: 2022-10-16 06:45

/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
34
1.32k
static uint8_t downcase(uint8_t c) {
35
1.32k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
36
1.32k
}
37
38
102
static int memieq(const void *a, const void *b, size_t n) {
39
102
  size_t i;
40
102
  const uint8_t *aa = a, *bb = b;
41
42
741
  for (i = 0; i < n; ++i) {
43
663
    if (downcase(aa[i]) != downcase(bb[i])) {
44
24
      return 0;
45
24
    }
46
663
  }
47
78
  return 1;
48
102
}
49
50
106
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
51
52
2.41k
static int64_t parse_uint(const uint8_t *s, size_t len) {
53
2.41k
  int64_t n = 0;
54
2.41k
  size_t i;
55
2.41k
  if (len == 0) {
56
20
    return -1;
57
20
  }
58
13.6k
  for (i = 0; i < len; ++i) {
59
11.3k
    if ('0' <= s[i] && s[i] <= '9') {
60
11.2k
      if (n > INT64_MAX / 10) {
61
2
        return -1;
62
2
      }
63
11.2k
      n *= 10;
64
11.2k
      if (n > INT64_MAX - (s[i] - '0')) {
65
1
        return -1;
66
1
      }
67
11.2k
      n += s[i] - '0';
68
11.2k
      continue;
69
11.2k
    }
70
37
    return -1;
71
11.3k
  }
72
2.35k
  return n;
73
2.39k
}
74
75
2.09k
static int lws(const uint8_t *s, size_t n) {
76
2.09k
  size_t i;
77
2.16k
  for (i = 0; i < n; ++i) {
78
2.13k
    if (s[i] != ' ' && s[i] != '\t') {
79
2.06k
      return 0;
80
2.06k
    }
81
2.13k
  }
82
32
  return 1;
83
2.09k
}
84
85
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
86
2.09k
                               int flag) {
87
2.09k
  if (stream->http_flags & flag) {
88
3
    return 0;
89
3
  }
90
2.09k
  if (lws(nv->value->base, nv->value->len)) {
91
32
    return 0;
92
32
  }
93
2.06k
  stream->http_flags = (uint16_t)(stream->http_flags | flag);
94
2.06k
  return 1;
95
2.09k
}
96
97
1.79k
static int expect_response_body(nghttp2_stream *stream) {
98
1.79k
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
99
1.79k
         stream->status_code / 100 != 1 && stream->status_code != 304 &&
100
1.79k
         stream->status_code != 204;
101
1.79k
}
102
103
/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
104
   header field to represent system-wide OPTIONS request.  Otherwise,
105
   :path header field value must start with "/".  This function must
106
   be called after ":method" header field was received.  This function
107
   returns nonzero if path is valid.*/
108
0
static int check_path(nghttp2_stream *stream) {
109
0
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
110
0
         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
111
0
          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
112
0
           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
113
0
}
114
115
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
116
0
                                  int trailer, int connect_protocol) {
117
0
  if (nv->name->base[0] == ':') {
118
0
    if (trailer ||
119
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
120
0
      return NGHTTP2_ERR_HTTP_HEADER;
121
0
    }
122
0
  }
123
124
0
  switch (nv->token) {
125
0
  case NGHTTP2_TOKEN__AUTHORITY:
126
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
127
0
      return NGHTTP2_ERR_HTTP_HEADER;
128
0
    }
129
0
    break;
130
0
  case NGHTTP2_TOKEN__METHOD:
131
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
132
0
      return NGHTTP2_ERR_HTTP_HEADER;
133
0
    }
134
0
    switch (nv->value->len) {
135
0
    case 4:
136
0
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
137
0
        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
138
0
      }
139
0
      break;
140
0
    case 7:
141
0
      switch (nv->value->base[6]) {
142
0
      case 'T':
143
0
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
144
0
          if (stream->stream_id % 2 == 0) {
145
            /* we won't allow CONNECT for push */
146
0
            return NGHTTP2_ERR_HTTP_HEADER;
147
0
          }
148
0
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
149
0
        }
150
0
        break;
151
0
      case 'S':
152
0
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
153
0
          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
154
0
        }
155
0
        break;
156
0
      }
157
0
      break;
158
0
    }
159
0
    break;
160
0
  case NGHTTP2_TOKEN__PATH:
161
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
162
0
      return NGHTTP2_ERR_HTTP_HEADER;
163
0
    }
164
0
    if (nv->value->base[0] == '/') {
165
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
166
0
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
167
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
168
0
    }
169
0
    break;
170
0
  case NGHTTP2_TOKEN__SCHEME:
171
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
172
0
      return NGHTTP2_ERR_HTTP_HEADER;
173
0
    }
174
0
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
175
0
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
176
0
      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
177
0
    }
178
0
    break;
179
0
  case NGHTTP2_TOKEN__PROTOCOL:
180
0
    if (!connect_protocol) {
181
0
      return NGHTTP2_ERR_HTTP_HEADER;
182
0
    }
183
184
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
185
0
      return NGHTTP2_ERR_HTTP_HEADER;
186
0
    }
187
0
    break;
188
0
  case NGHTTP2_TOKEN_HOST:
189
0
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
190
0
      return NGHTTP2_ERR_HTTP_HEADER;
191
0
    }
192
0
    break;
193
0
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
194
0
    if (stream->content_length != -1) {
195
0
      return NGHTTP2_ERR_HTTP_HEADER;
196
0
    }
197
0
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
198
0
    if (stream->content_length == -1) {
199
0
      return NGHTTP2_ERR_HTTP_HEADER;
200
0
    }
201
0
    break;
202
0
  }
203
  /* disallowed header fields */
204
0
  case NGHTTP2_TOKEN_CONNECTION:
205
0
  case NGHTTP2_TOKEN_KEEP_ALIVE:
206
0
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
207
0
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
208
0
  case NGHTTP2_TOKEN_UPGRADE:
209
0
    return NGHTTP2_ERR_HTTP_HEADER;
210
0
  case NGHTTP2_TOKEN_TE:
211
0
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
212
0
      return NGHTTP2_ERR_HTTP_HEADER;
213
0
    }
214
0
    break;
215
0
  default:
216
0
    if (nv->name->base[0] == ':') {
217
0
      return NGHTTP2_ERR_HTTP_HEADER;
218
0
    }
219
0
  }
220
221
0
  if (nv->name->base[0] != ':') {
222
0
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
223
0
  }
224
225
0
  return 0;
226
0
}
227
228
static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
229
49.0k
                                   int trailer) {
230
49.0k
  if (nv->name->base[0] == ':') {
231
2.25k
    if (trailer ||
232
2.25k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
233
45
      return NGHTTP2_ERR_HTTP_HEADER;
234
45
    }
235
2.25k
  }
236
237
49.0k
  switch (nv->token) {
238
2.09k
  case NGHTTP2_TOKEN__STATUS: {
239
2.09k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
240
35
      return NGHTTP2_ERR_HTTP_HEADER;
241
35
    }
242
2.06k
    if (nv->value->len != 3) {
243
16
      return NGHTTP2_ERR_HTTP_HEADER;
244
16
    }
245
2.04k
    stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
246
2.04k
    if (stream->status_code == -1 || stream->status_code == 101) {
247
19
      return NGHTTP2_ERR_HTTP_HEADER;
248
19
    }
249
2.02k
    break;
250
2.04k
  }
251
2.02k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
252
502
    if (stream->status_code == 204) {
253
      /* content-length header field in 204 response is prohibited by
254
         RFC 7230.  But some widely used servers send content-length:
255
         0.  Until they get fixed, we ignore it. */
256
16
      if (stream->content_length != -1) {
257
        /* Found multiple content-length field */
258
1
        return NGHTTP2_ERR_HTTP_HEADER;
259
1
      }
260
15
      if (!lstrieq("0", nv->value->base, nv->value->len)) {
261
13
        return NGHTTP2_ERR_HTTP_HEADER;
262
13
      }
263
2
      stream->content_length = 0;
264
2
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
265
15
    }
266
486
    if (stream->status_code / 100 == 1) {
267
4
      return NGHTTP2_ERR_HTTP_HEADER;
268
4
    }
269
    /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
270
482
    if (stream->status_code / 100 == 2 &&
271
482
        (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
272
75
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
273
75
    }
274
407
    if (stream->content_length != -1) {
275
35
      return NGHTTP2_ERR_HTTP_HEADER;
276
35
    }
277
372
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
278
372
    if (stream->content_length == -1) {
279
42
      return NGHTTP2_ERR_HTTP_HEADER;
280
42
    }
281
330
    break;
282
372
  }
283
  /* disallowed header fields */
284
330
  case NGHTTP2_TOKEN_CONNECTION:
285
7
  case NGHTTP2_TOKEN_KEEP_ALIVE:
286
11
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
287
20
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
288
23
  case NGHTTP2_TOKEN_UPGRADE:
289
23
    return NGHTTP2_ERR_HTTP_HEADER;
290
91
  case NGHTTP2_TOKEN_TE:
291
91
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
292
15
      return NGHTTP2_ERR_HTTP_HEADER;
293
15
    }
294
76
    break;
295
46.3k
  default:
296
46.3k
    if (nv->name->base[0] == ':') {
297
112
      return NGHTTP2_ERR_HTTP_HEADER;
298
112
    }
299
49.0k
  }
300
301
48.6k
  if (nv->name->base[0] != ':') {
302
46.6k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
303
46.6k
  }
304
305
48.6k
  return 0;
306
49.0k
}
307
308
85
static int check_scheme(const uint8_t *value, size_t len) {
309
85
  const uint8_t *last;
310
85
  if (len == 0) {
311
10
    return 0;
312
10
  }
313
314
75
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
315
12
    return 0;
316
12
  }
317
318
63
  last = value + len;
319
63
  ++value;
320
321
890
  for (; value != last; ++value) {
322
847
    if (!(('A' <= *value && *value <= 'Z') ||
323
847
          ('a' <= *value && *value <= 'z') ||
324
847
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
325
847
          *value == '.')) {
326
20
      return 0;
327
20
    }
328
847
  }
329
43
  return 1;
330
63
}
331
332
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
333
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
334
49.3k
                           int trailer) {
335
49.3k
  int rv;
336
337
  /* We are strict for pseudo header field.  One bad character should
338
     lead to fail.  OTOH, we should be a bit forgiving for regular
339
     headers, since existing public internet has so much illegal
340
     headers floating around and if we kill the stream because of
341
     this, we may disrupt many web sites and/or libraries.  So we
342
     become conservative here, and just ignore those illegal regular
343
     headers. */
344
49.3k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
345
171
    size_t i;
346
171
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
347
4
      return NGHTTP2_ERR_HTTP_HEADER;
348
4
    }
349
    /* header field name must be lower-cased without exception */
350
1.33k
    for (i = 0; i < nv->name->len; ++i) {
351
1.19k
      uint8_t c = nv->name->base[i];
352
1.19k
      if ('A' <= c && c <= 'Z') {
353
24
        return NGHTTP2_ERR_HTTP_HEADER;
354
24
      }
355
1.19k
    }
356
    /* When ignoring regular headers, we set this flag so that we
357
       still enforce header field ordering rule for pseudo header
358
       fields. */
359
143
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
360
143
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
361
167
  }
362
363
49.1k
  if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
364
49.1k
      nv->token == NGHTTP2_TOKEN_HOST) {
365
857
    rv = nghttp2_check_authority(nv->value->base, nv->value->len);
366
48.3k
  } else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
367
85
    rv = check_scheme(nv->value->base, nv->value->len);
368
48.2k
  } else {
369
48.2k
    rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
370
48.2k
  }
371
372
49.1k
  if (rv == 0) {
373
95
    assert(nv->name->len > 0);
374
95
    if (nv->name->base[0] == ':') {
375
69
      return NGHTTP2_ERR_HTTP_HEADER;
376
69
    }
377
    /* When ignoring regular headers, we set this flag so that we
378
       still enforce header field ordering rule for pseudo header
379
       fields. */
380
26
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
381
26
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
382
95
  }
383
384
49.0k
  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
385
0
    return http_request_on_header(stream, nv, trailer,
386
0
                                  session->server &&
387
0
                                      session->pending_enable_connect_protocol);
388
0
  }
389
390
49.0k
  return http_response_on_header(stream, nv, trailer);
391
49.0k
}
392
393
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
394
0
                                    nghttp2_frame *frame) {
395
0
  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
396
0
      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
397
0
    if ((stream->http_flags &
398
0
         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
399
0
        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
400
0
      return -1;
401
0
    }
402
0
    stream->content_length = -1;
403
0
  } else {
404
0
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
405
0
            NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
406
0
        (stream->http_flags &
407
0
         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
408
0
      return -1;
409
0
    }
410
0
    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
411
0
        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
412
0
         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
413
0
      return -1;
414
0
    }
415
0
    if (!check_path(stream)) {
416
0
      return -1;
417
0
    }
418
0
  }
419
420
0
  if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
421
    /* we are going to reuse data fields for upcoming response.  Clear
422
       them now, except for method flags. */
423
0
    stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
424
0
    stream->content_length = -1;
425
0
  }
426
427
0
  return 0;
428
0
}
429
430
1.95k
int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
431
1.95k
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
432
49
    return -1;
433
49
  }
434
435
1.90k
  if (stream->status_code / 100 == 1) {
436
    /* non-final response */
437
105
    stream->http_flags =
438
105
        (uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
439
105
                   NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
440
105
    stream->content_length = -1;
441
105
    stream->status_code = -1;
442
105
    return 0;
443
105
  }
444
445
1.79k
  stream->http_flags =
446
1.79k
      (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
447
448
1.79k
  if (!expect_response_body(stream)) {
449
668
    stream->content_length = 0;
450
1.12k
  } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
451
1.12k
                                   NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
452
1
    stream->content_length = -1;
453
1
  }
454
455
1.79k
  return 0;
456
1.90k
}
457
458
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
459
9
                                    nghttp2_frame *frame) {
460
9
  (void)stream;
461
462
9
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
463
3
    return -1;
464
3
  }
465
466
6
  return 0;
467
9
}
468
469
1.13k
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
470
1.13k
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
471
4
    return -1;
472
4
  }
473
474
1.13k
  if (stream->content_length != -1 &&
475
1.13k
      stream->content_length != stream->recv_content_length) {
476
133
    return -1;
477
133
  }
478
479
997
  return 0;
480
1.13k
}
481
482
370
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
483
370
  stream->recv_content_length += (int64_t)n;
484
485
370
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
486
370
      (stream->content_length != -1 &&
487
363
       stream->recv_content_length > stream->content_length)) {
488
33
    return -1;
489
33
  }
490
491
337
  return 0;
492
370
}
493
494
void nghttp2_http_record_request_method(nghttp2_stream *stream,
495
8.08k
                                        nghttp2_frame *frame) {
496
8.08k
  const nghttp2_nv *nva;
497
8.08k
  size_t nvlen;
498
8.08k
  size_t i;
499
500
8.08k
  switch (frame->hd.type) {
501
8.08k
  case NGHTTP2_HEADERS:
502
8.08k
    nva = frame->headers.nva;
503
8.08k
    nvlen = frame->headers.nvlen;
504
8.08k
    break;
505
0
  case NGHTTP2_PUSH_PROMISE:
506
0
    nva = frame->push_promise.nva;
507
0
    nvlen = frame->push_promise.nvlen;
508
0
    break;
509
0
  default:
510
0
    return;
511
8.08k
  }
512
513
  /* TODO we should do this strictly. */
514
8.08k
  for (i = 0; i < nvlen; ++i) {
515
8.08k
    const nghttp2_nv *nv = &nva[i];
516
8.08k
    if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
517
8.08k
          memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
518
0
      continue;
519
0
    }
520
8.08k
    if (lstreq("CONNECT", nv->value, nv->valuelen)) {
521
14
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
522
14
      return;
523
14
    }
524
8.07k
    if (lstreq("HEAD", nv->value, nv->valuelen)) {
525
55
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
526
55
      return;
527
55
    }
528
8.01k
    return;
529
8.07k
  }
530
8.08k
}