Coverage Report

Created: 2022-11-30 06:20

/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.29k
static uint8_t downcase(uint8_t c) {
35
1.29k
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
36
1.29k
}
37
38
99
static int memieq(const void *a, const void *b, size_t n) {
39
99
  size_t i;
40
99
  const uint8_t *aa = a, *bb = b;
41
42
720
  for (i = 0; i < n; ++i) {
43
645
    if (downcase(aa[i]) != downcase(bb[i])) {
44
24
      return 0;
45
24
    }
46
645
  }
47
75
  return 1;
48
99
}
49
50
102
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
51
52
2.51k
static int64_t parse_uint(const uint8_t *s, size_t len) {
53
2.51k
  int64_t n = 0;
54
2.51k
  size_t i;
55
2.51k
  if (len == 0) {
56
25
    return -1;
57
25
  }
58
14.3k
  for (i = 0; i < len; ++i) {
59
11.9k
    if ('0' <= s[i] && s[i] <= '9') {
60
11.8k
      if (n > INT64_MAX / 10) {
61
3
        return -1;
62
3
      }
63
11.8k
      n *= 10;
64
11.8k
      if (n > INT64_MAX - (s[i] - '0')) {
65
1
        return -1;
66
1
      }
67
11.8k
      n += s[i] - '0';
68
11.8k
      continue;
69
11.8k
    }
70
32
    return -1;
71
11.9k
  }
72
2.45k
  return n;
73
2.49k
}
74
75
2.21k
static int lws(const uint8_t *s, size_t n) {
76
2.21k
  size_t i;
77
2.28k
  for (i = 0; i < n; ++i) {
78
2.25k
    if (s[i] != ' ' && s[i] != '\t') {
79
2.18k
      return 0;
80
2.18k
    }
81
2.25k
  }
82
31
  return 1;
83
2.21k
}
84
85
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
86
2.22k
                               int flag) {
87
2.22k
  if (stream->http_flags & flag) {
88
6
    return 0;
89
6
  }
90
2.21k
  if (lws(nv->value->base, nv->value->len)) {
91
31
    return 0;
92
31
  }
93
2.18k
  stream->http_flags = (uint16_t)(stream->http_flags | flag);
94
2.18k
  return 1;
95
2.21k
}
96
97
1.94k
static int expect_response_body(nghttp2_stream *stream) {
98
1.94k
  return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
99
1.94k
         stream->status_code / 100 != 1 && stream->status_code != 304 &&
100
1.94k
         stream->status_code != 204;
101
1.94k
}
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
53.0k
                                   int trailer) {
230
53.0k
  if (nv->name->base[0] == ':') {
231
2.36k
    if (trailer ||
232
2.36k
        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
233
39
      return NGHTTP2_ERR_HTTP_HEADER;
234
39
    }
235
2.36k
  }
236
237
52.9k
  switch (nv->token) {
238
2.22k
  case NGHTTP2_TOKEN__STATUS: {
239
2.22k
    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
240
37
      return NGHTTP2_ERR_HTTP_HEADER;
241
37
    }
242
2.18k
    if (nv->value->len != 3) {
243
20
      return NGHTTP2_ERR_HTTP_HEADER;
244
20
    }
245
2.16k
    stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
246
2.16k
    if (stream->status_code == -1 || stream->status_code == 101) {
247
16
      return NGHTTP2_ERR_HTTP_HEADER;
248
16
    }
249
2.14k
    break;
250
2.16k
  }
251
2.14k
  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
252
533
    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
15
      if (stream->content_length != -1) {
257
        /* Found multiple content-length field */
258
1
        return NGHTTP2_ERR_HTTP_HEADER;
259
1
      }
260
14
      if (!lstrieq("0", nv->value->base, nv->value->len)) {
261
12
        return NGHTTP2_ERR_HTTP_HEADER;
262
12
      }
263
2
      stream->content_length = 0;
264
2
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
265
14
    }
266
518
    if (stream->status_code / 100 == 1) {
267
6
      return NGHTTP2_ERR_HTTP_HEADER;
268
6
    }
269
    /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
270
512
    if (stream->status_code / 100 == 2 &&
271
512
        (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
272
110
      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
273
110
    }
274
402
    if (stream->content_length != -1) {
275
47
      return NGHTTP2_ERR_HTTP_HEADER;
276
47
    }
277
355
    stream->content_length = parse_uint(nv->value->base, nv->value->len);
278
355
    if (stream->content_length == -1) {
279
46
      return NGHTTP2_ERR_HTTP_HEADER;
280
46
    }
281
309
    break;
282
355
  }
283
  /* disallowed header fields */
284
309
  case NGHTTP2_TOKEN_CONNECTION:
285
5
  case NGHTTP2_TOKEN_KEEP_ALIVE:
286
7
  case NGHTTP2_TOKEN_PROXY_CONNECTION:
287
16
  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
288
17
  case NGHTTP2_TOKEN_UPGRADE:
289
17
    return NGHTTP2_ERR_HTTP_HEADER;
290
88
  case NGHTTP2_TOKEN_TE:
291
88
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
292
15
      return NGHTTP2_ERR_HTTP_HEADER;
293
15
    }
294
73
    break;
295
50.1k
  default:
296
50.1k
    if (nv->name->base[0] == ':') {
297
104
      return NGHTTP2_ERR_HTTP_HEADER;
298
104
    }
299
52.9k
  }
300
301
52.5k
  if (nv->name->base[0] != ':') {
302
50.3k
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
303
50.3k
  }
304
305
52.5k
  return 0;
306
52.9k
}
307
308
84
static int check_scheme(const uint8_t *value, size_t len) {
309
84
  const uint8_t *last;
310
84
  if (len == 0) {
311
13
    return 0;
312
13
  }
313
314
71
  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
315
10
    return 0;
316
10
  }
317
318
61
  last = value + len;
319
61
  ++value;
320
321
1.05k
  for (; value != last; ++value) {
322
1.00k
    if (!(('A' <= *value && *value <= 'Z') ||
323
1.00k
          ('a' <= *value && *value <= 'z') ||
324
1.00k
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
325
1.00k
          *value == '.')) {
326
20
      return 0;
327
20
    }
328
1.00k
  }
329
41
  return 1;
330
61
}
331
332
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
333
                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
334
53.2k
                           int trailer) {
335
53.2k
  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
53.2k
  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
345
169
    size_t i;
346
169
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
347
1
      return NGHTTP2_ERR_HTTP_HEADER;
348
1
    }
349
    /* header field name must be lower-cased without exception */
350
1.08k
    for (i = 0; i < nv->name->len; ++i) {
351
946
      uint8_t c = nv->name->base[i];
352
946
      if ('A' <= c && c <= 'Z') {
353
26
        return NGHTTP2_ERR_HTTP_HEADER;
354
26
      }
355
946
    }
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
142
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
360
142
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
361
168
  }
362
363
53.1k
  if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
364
53.1k
      nv->token == NGHTTP2_TOKEN_HOST) {
365
723
    rv = nghttp2_check_authority(nv->value->base, nv->value->len);
366
52.3k
  } else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
367
84
    rv = check_scheme(nv->value->base, nv->value->len);
368
52.2k
  } else {
369
52.2k
    rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
370
52.2k
  }
371
372
53.1k
  if (rv == 0) {
373
86
    assert(nv->name->len > 0);
374
86
    if (nv->name->base[0] == ':') {
375
61
      return NGHTTP2_ERR_HTTP_HEADER;
376
61
    }
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
25
    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
381
25
    return NGHTTP2_ERR_IGN_HTTP_HEADER;
382
86
  }
383
384
53.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
53.0k
  return http_response_on_header(stream, nv, trailer);
391
53.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
2.08k
int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
431
2.08k
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
432
57
    return -1;
433
57
  }
434
435
2.02k
  if (stream->status_code / 100 == 1) {
436
    /* non-final response */
437
79
    stream->http_flags =
438
79
        (uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
439
79
                   NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
440
79
    stream->content_length = -1;
441
79
    stream->status_code = -1;
442
79
    return 0;
443
79
  }
444
445
1.94k
  stream->http_flags =
446
1.94k
      (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
447
448
1.94k
  if (!expect_response_body(stream)) {
449
727
    stream->content_length = 0;
450
1.22k
  } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
451
1.22k
                                   NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
452
1
    stream->content_length = -1;
453
1
  }
454
455
1.94k
  return 0;
456
2.02k
}
457
458
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
459
13
                                    nghttp2_frame *frame) {
460
13
  (void)stream;
461
462
13
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
463
4
    return -1;
464
4
  }
465
466
9
  return 0;
467
13
}
468
469
1.31k
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
470
1.31k
  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
471
3
    return -1;
472
3
  }
473
474
1.31k
  if (stream->content_length != -1 &&
475
1.31k
      stream->content_length != stream->recv_content_length) {
476
129
    return -1;
477
129
  }
478
479
1.18k
  return 0;
480
1.31k
}
481
482
290
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
483
290
  stream->recv_content_length += (int64_t)n;
484
485
290
  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
486
290
      (stream->content_length != -1 &&
487
285
       stream->recv_content_length > stream->content_length)) {
488
21
    return -1;
489
21
  }
490
491
269
  return 0;
492
290
}
493
494
void nghttp2_http_record_request_method(nghttp2_stream *stream,
495
8.17k
                                        nghttp2_frame *frame) {
496
8.17k
  const nghttp2_nv *nva;
497
8.17k
  size_t nvlen;
498
8.17k
  size_t i;
499
500
8.17k
  switch (frame->hd.type) {
501
8.17k
  case NGHTTP2_HEADERS:
502
8.17k
    nva = frame->headers.nva;
503
8.17k
    nvlen = frame->headers.nvlen;
504
8.17k
    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.17k
  }
512
513
  /* TODO we should do this strictly. */
514
8.17k
  for (i = 0; i < nvlen; ++i) {
515
8.17k
    const nghttp2_nv *nv = &nva[i];
516
8.17k
    if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
517
8.17k
          memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
518
0
      continue;
519
0
    }
520
8.17k
    if (lstreq("CONNECT", nv->value, nv->valuelen)) {
521
14
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
522
14
      return;
523
14
    }
524
8.15k
    if (lstreq("HEAD", nv->value, nv->valuelen)) {
525
48
      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
526
48
      return;
527
48
    }
528
8.11k
    return;
529
8.15k
  }
530
8.17k
}