• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2012 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 "http2.h"
26 
27 #include "llhttp.h"
28 
29 #include "util.h"
30 
31 namespace nghttp2 {
32 
33 namespace http2 {
34 
get_reason_phrase(unsigned int status_code)35 StringRef get_reason_phrase(unsigned int status_code) {
36   switch (status_code) {
37   case 100:
38     return "Continue"_sr;
39   case 101:
40     return "Switching Protocols"_sr;
41   case 103:
42     return "Early Hints"_sr;
43   case 200:
44     return "OK"_sr;
45   case 201:
46     return "Created"_sr;
47   case 202:
48     return "Accepted"_sr;
49   case 203:
50     return "Non-Authoritative Information"_sr;
51   case 204:
52     return "No Content"_sr;
53   case 205:
54     return "Reset Content"_sr;
55   case 206:
56     return "Partial Content"_sr;
57   case 300:
58     return "Multiple Choices"_sr;
59   case 301:
60     return "Moved Permanently"_sr;
61   case 302:
62     return "Found"_sr;
63   case 303:
64     return "See Other"_sr;
65   case 304:
66     return "Not Modified"_sr;
67   case 305:
68     return "Use Proxy"_sr;
69   // case 306: return "(Unused)"_sr;
70   case 307:
71     return "Temporary Redirect"_sr;
72   case 308:
73     return "Permanent Redirect"_sr;
74   case 400:
75     return "Bad Request"_sr;
76   case 401:
77     return "Unauthorized"_sr;
78   case 402:
79     return "Payment Required"_sr;
80   case 403:
81     return "Forbidden"_sr;
82   case 404:
83     return "Not Found"_sr;
84   case 405:
85     return "Method Not Allowed"_sr;
86   case 406:
87     return "Not Acceptable"_sr;
88   case 407:
89     return "Proxy Authentication Required"_sr;
90   case 408:
91     return "Request Timeout"_sr;
92   case 409:
93     return "Conflict"_sr;
94   case 410:
95     return "Gone"_sr;
96   case 411:
97     return "Length Required"_sr;
98   case 412:
99     return "Precondition Failed"_sr;
100   case 413:
101     return "Payload Too Large"_sr;
102   case 414:
103     return "URI Too Long"_sr;
104   case 415:
105     return "Unsupported Media Type"_sr;
106   case 416:
107     return "Requested Range Not Satisfiable"_sr;
108   case 417:
109     return "Expectation Failed"_sr;
110   case 421:
111     return "Misdirected Request"_sr;
112   case 425:
113     // https://tools.ietf.org/html/rfc8470
114     return "Too Early"_sr;
115   case 426:
116     return "Upgrade Required"_sr;
117   case 428:
118     return "Precondition Required"_sr;
119   case 429:
120     return "Too Many Requests"_sr;
121   case 431:
122     return "Request Header Fields Too Large"_sr;
123   case 451:
124     return "Unavailable For Legal Reasons"_sr;
125   case 500:
126     return "Internal Server Error"_sr;
127   case 501:
128     return "Not Implemented"_sr;
129   case 502:
130     return "Bad Gateway"_sr;
131   case 503:
132     return "Service Unavailable"_sr;
133   case 504:
134     return "Gateway Timeout"_sr;
135   case 505:
136     return "HTTP Version Not Supported"_sr;
137   case 511:
138     return "Network Authentication Required"_sr;
139   default:
140     return StringRef{};
141   }
142 }
143 
stringify_status(BlockAllocator & balloc,unsigned int status_code)144 StringRef stringify_status(BlockAllocator &balloc, unsigned int status_code) {
145   switch (status_code) {
146   case 100:
147     return "100"_sr;
148   case 101:
149     return "101"_sr;
150   case 103:
151     return "103"_sr;
152   case 200:
153     return "200"_sr;
154   case 201:
155     return "201"_sr;
156   case 202:
157     return "202"_sr;
158   case 203:
159     return "203"_sr;
160   case 204:
161     return "204"_sr;
162   case 205:
163     return "205"_sr;
164   case 206:
165     return "206"_sr;
166   case 300:
167     return "300"_sr;
168   case 301:
169     return "301"_sr;
170   case 302:
171     return "302"_sr;
172   case 303:
173     return "303"_sr;
174   case 304:
175     return "304"_sr;
176   case 305:
177     return "305"_sr;
178   // case 306: return "306"_sr;
179   case 307:
180     return "307"_sr;
181   case 308:
182     return "308"_sr;
183   case 400:
184     return "400"_sr;
185   case 401:
186     return "401"_sr;
187   case 402:
188     return "402"_sr;
189   case 403:
190     return "403"_sr;
191   case 404:
192     return "404"_sr;
193   case 405:
194     return "405"_sr;
195   case 406:
196     return "406"_sr;
197   case 407:
198     return "407"_sr;
199   case 408:
200     return "408"_sr;
201   case 409:
202     return "409"_sr;
203   case 410:
204     return "410"_sr;
205   case 411:
206     return "411"_sr;
207   case 412:
208     return "412"_sr;
209   case 413:
210     return "413"_sr;
211   case 414:
212     return "414"_sr;
213   case 415:
214     return "415"_sr;
215   case 416:
216     return "416"_sr;
217   case 417:
218     return "417"_sr;
219   case 421:
220     return "421"_sr;
221   case 426:
222     return "426"_sr;
223   case 428:
224     return "428"_sr;
225   case 429:
226     return "429"_sr;
227   case 431:
228     return "431"_sr;
229   case 451:
230     return "451"_sr;
231   case 500:
232     return "500"_sr;
233   case 501:
234     return "501"_sr;
235   case 502:
236     return "502"_sr;
237   case 503:
238     return "503"_sr;
239   case 504:
240     return "504"_sr;
241   case 505:
242     return "505"_sr;
243   case 511:
244     return "511"_sr;
245   default:
246     return util::make_string_ref_uint(balloc, status_code);
247   }
248 }
249 
capitalize(DefaultMemchunks * buf,const StringRef & s)250 void capitalize(DefaultMemchunks *buf, const StringRef &s) {
251   buf->append(util::upcase(s[0]));
252   for (size_t i = 1; i < s.size(); ++i) {
253     if (s[i - 1] == '-') {
254       buf->append(util::upcase(s[i]));
255     } else {
256       buf->append(s[i]);
257     }
258   }
259 }
260 
lws(const char * value)261 bool lws(const char *value) {
262   for (; *value; ++value) {
263     switch (*value) {
264     case '\t':
265     case ' ':
266       continue;
267     default:
268       return false;
269     }
270   }
271   return true;
272 }
273 
copy_url_component(std::string & dest,const http_parser_url * u,int field,const char * url)274 void copy_url_component(std::string &dest, const http_parser_url *u, int field,
275                         const char *url) {
276   if (u->field_set & (1 << field)) {
277     dest.assign(url + u->field_data[field].off, u->field_data[field].len);
278   }
279 }
280 
to_header(const StringRef & name,const StringRef & value,bool no_index,int32_t token)281 Headers::value_type to_header(const StringRef &name, const StringRef &value,
282                               bool no_index, int32_t token) {
283   return Header(std::string{std::begin(name), std::end(name)},
284                 std::string{std::begin(value), std::end(value)}, no_index,
285                 token);
286 }
287 
add_header(Headers & nva,const StringRef & name,const StringRef & value,bool no_index,int32_t token)288 void add_header(Headers &nva, const StringRef &name, const StringRef &value,
289                 bool no_index, int32_t token) {
290   nva.push_back(to_header(name, value, no_index, token));
291 }
292 
get_header(const Headers & nva,const char * name)293 const Headers::value_type *get_header(const Headers &nva, const char *name) {
294   const Headers::value_type *res = nullptr;
295   for (auto &nv : nva) {
296     if (nv.name == name) {
297       res = &nv;
298     }
299   }
300   return res;
301 }
302 
non_empty_value(const HeaderRefs::value_type * nv)303 bool non_empty_value(const HeaderRefs::value_type *nv) {
304   return nv && !nv->value.empty();
305 }
306 
307 namespace {
copy_headers_to_nva_internal(std::vector<nghttp2_nv> & nva,const HeaderRefs & headers,uint8_t nv_flags,uint32_t flags)308 void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
309                                   const HeaderRefs &headers, uint8_t nv_flags,
310                                   uint32_t flags) {
311   auto it_forwarded = std::end(headers);
312   auto it_xff = std::end(headers);
313   auto it_xfp = std::end(headers);
314   auto it_via = std::end(headers);
315 
316   for (auto it = std::begin(headers); it != std::end(headers); ++it) {
317     auto kv = &(*it);
318     if (kv->name.empty() || kv->name[0] == ':') {
319       continue;
320     }
321     switch (kv->token) {
322     case HD_COOKIE:
323     case HD_CONNECTION:
324     case HD_HOST:
325     case HD_HTTP2_SETTINGS:
326     case HD_KEEP_ALIVE:
327     case HD_PROXY_CONNECTION:
328     case HD_SERVER:
329     case HD_TE:
330     case HD_TRANSFER_ENCODING:
331     case HD_UPGRADE:
332       continue;
333     case HD_EARLY_DATA:
334       if (flags & HDOP_STRIP_EARLY_DATA) {
335         continue;
336       }
337       break;
338     case HD_SEC_WEBSOCKET_ACCEPT:
339       if (flags & HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) {
340         continue;
341       }
342       break;
343     case HD_SEC_WEBSOCKET_KEY:
344       if (flags & HDOP_STRIP_SEC_WEBSOCKET_KEY) {
345         continue;
346       }
347       break;
348     case HD_FORWARDED:
349       if (flags & HDOP_STRIP_FORWARDED) {
350         continue;
351       }
352 
353       if (it_forwarded == std::end(headers)) {
354         it_forwarded = it;
355         continue;
356       }
357 
358       kv = &(*it_forwarded);
359       it_forwarded = it;
360       break;
361     case HD_X_FORWARDED_FOR:
362       if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
363         continue;
364       }
365 
366       if (it_xff == std::end(headers)) {
367         it_xff = it;
368         continue;
369       }
370 
371       kv = &(*it_xff);
372       it_xff = it;
373       break;
374     case HD_X_FORWARDED_PROTO:
375       if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
376         continue;
377       }
378 
379       if (it_xfp == std::end(headers)) {
380         it_xfp = it;
381         continue;
382       }
383 
384       kv = &(*it_xfp);
385       it_xfp = it;
386       break;
387     case HD_VIA:
388       if (flags & HDOP_STRIP_VIA) {
389         continue;
390       }
391 
392       if (it_via == std::end(headers)) {
393         it_via = it;
394         continue;
395       }
396 
397       kv = &(*it_via);
398       it_via = it;
399       break;
400     }
401     nva.push_back(make_field_flags(kv->name, kv->value,
402                                    nv_flags | no_index(kv->no_index)));
403   }
404 }
405 } // namespace
406 
copy_headers_to_nva(std::vector<nghttp2_nv> & nva,const HeaderRefs & headers,uint32_t flags)407 void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
408                          const HeaderRefs &headers, uint32_t flags) {
409   copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE, flags);
410 }
411 
copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> & nva,const HeaderRefs & headers,uint32_t flags)412 void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
413                                 const HeaderRefs &headers, uint32_t flags) {
414   copy_headers_to_nva_internal(
415       nva, headers,
416       NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE, flags);
417 }
418 
build_http1_headers_from_headers(DefaultMemchunks * buf,const HeaderRefs & headers,uint32_t flags)419 void build_http1_headers_from_headers(DefaultMemchunks *buf,
420                                       const HeaderRefs &headers,
421                                       uint32_t flags) {
422   auto it_forwarded = std::end(headers);
423   auto it_xff = std::end(headers);
424   auto it_xfp = std::end(headers);
425   auto it_via = std::end(headers);
426 
427   for (auto it = std::begin(headers); it != std::end(headers); ++it) {
428     auto kv = &(*it);
429     if (kv->name.empty() || kv->name[0] == ':') {
430       continue;
431     }
432     switch (kv->token) {
433     case HD_CONNECTION:
434     case HD_COOKIE:
435     case HD_HOST:
436     case HD_HTTP2_SETTINGS:
437     case HD_KEEP_ALIVE:
438     case HD_PROXY_CONNECTION:
439     case HD_SERVER:
440     case HD_UPGRADE:
441       continue;
442     case HD_EARLY_DATA:
443       if (flags & HDOP_STRIP_EARLY_DATA) {
444         continue;
445       }
446       break;
447     case HD_TRANSFER_ENCODING:
448       if (flags & HDOP_STRIP_TRANSFER_ENCODING) {
449         continue;
450       }
451       break;
452     case HD_FORWARDED:
453       if (flags & HDOP_STRIP_FORWARDED) {
454         continue;
455       }
456 
457       if (it_forwarded == std::end(headers)) {
458         it_forwarded = it;
459         continue;
460       }
461 
462       kv = &(*it_forwarded);
463       it_forwarded = it;
464       break;
465     case HD_X_FORWARDED_FOR:
466       if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
467         continue;
468       }
469 
470       if (it_xff == std::end(headers)) {
471         it_xff = it;
472         continue;
473       }
474 
475       kv = &(*it_xff);
476       it_xff = it;
477       break;
478     case HD_X_FORWARDED_PROTO:
479       if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
480         continue;
481       }
482 
483       if (it_xfp == std::end(headers)) {
484         it_xfp = it;
485         continue;
486       }
487 
488       kv = &(*it_xfp);
489       it_xfp = it;
490       break;
491     case HD_VIA:
492       if (flags & HDOP_STRIP_VIA) {
493         continue;
494       }
495 
496       if (it_via == std::end(headers)) {
497         it_via = it;
498         continue;
499       }
500 
501       kv = &(*it_via);
502       it_via = it;
503       break;
504     }
505     capitalize(buf, kv->name);
506     buf->append(": ");
507     buf->append(kv->value);
508     buf->append("\r\n");
509   }
510 }
511 
determine_window_update_transmission(nghttp2_session * session,int32_t stream_id)512 int32_t determine_window_update_transmission(nghttp2_session *session,
513                                              int32_t stream_id) {
514   int32_t recv_length, window_size;
515   if (stream_id == 0) {
516     recv_length = nghttp2_session_get_effective_recv_data_length(session);
517     window_size = nghttp2_session_get_effective_local_window_size(session);
518   } else {
519     recv_length = nghttp2_session_get_stream_effective_recv_data_length(
520         session, stream_id);
521     window_size = nghttp2_session_get_stream_effective_local_window_size(
522         session, stream_id);
523   }
524   if (recv_length != -1 && window_size != -1) {
525     if (recv_length >= window_size / 2) {
526       return recv_length;
527     }
528   }
529   return -1;
530 }
531 
dump_nv(FILE * out,const char ** nv)532 void dump_nv(FILE *out, const char **nv) {
533   for (size_t i = 0; nv[i]; i += 2) {
534     fprintf(out, "%s: %s\n", nv[i], nv[i + 1]);
535   }
536   fputc('\n', out);
537   fflush(out);
538 }
539 
dump_nv(FILE * out,const nghttp2_nv * nva,size_t nvlen)540 void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen) {
541   auto end = nva + nvlen;
542   for (; nva != end; ++nva) {
543     fprintf(out, "%s: %s\n", nva->name, nva->value);
544   }
545   fputc('\n', out);
546   fflush(out);
547 }
548 
dump_nv(FILE * out,const Headers & nva)549 void dump_nv(FILE *out, const Headers &nva) {
550   for (auto &nv : nva) {
551     fprintf(out, "%s: %s\n", nv.name.c_str(), nv.value.c_str());
552   }
553   fputc('\n', out);
554   fflush(out);
555 }
556 
dump_nv(FILE * out,const HeaderRefs & nva)557 void dump_nv(FILE *out, const HeaderRefs &nva) {
558   for (auto &nv : nva) {
559     fprintf(out, "%s: %s\n", nv.name.data(), nv.value.data());
560   }
561   fputc('\n', out);
562   fflush(out);
563 }
564 
erase_header(HeaderRef * hd)565 void erase_header(HeaderRef *hd) {
566   hd->name = StringRef{};
567   hd->token = -1;
568 }
569 
rewrite_location_uri(BlockAllocator & balloc,const StringRef & uri,const http_parser_url & u,const StringRef & match_host,const StringRef & request_authority,const StringRef & upstream_scheme)570 StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
571                                const http_parser_url &u,
572                                const StringRef &match_host,
573                                const StringRef &request_authority,
574                                const StringRef &upstream_scheme) {
575   // We just rewrite scheme and authority.
576   if ((u.field_set & (1 << UF_HOST)) == 0) {
577     return StringRef{};
578   }
579   auto field = &u.field_data[UF_HOST];
580   if (!util::starts_with(std::begin(match_host), std::end(match_host),
581                          &uri[field->off], &uri[field->off] + field->len) ||
582       (match_host.size() != field->len && match_host[field->len] != ':')) {
583     return StringRef{};
584   }
585 
586   auto len = 0;
587   if (!request_authority.empty()) {
588     len += upstream_scheme.size() + str_size("://") + request_authority.size();
589   }
590 
591   if (u.field_set & (1 << UF_PATH)) {
592     field = &u.field_data[UF_PATH];
593     len += field->len;
594   }
595 
596   if (u.field_set & (1 << UF_QUERY)) {
597     field = &u.field_data[UF_QUERY];
598     len += 1 + field->len;
599   }
600 
601   if (u.field_set & (1 << UF_FRAGMENT)) {
602     field = &u.field_data[UF_FRAGMENT];
603     len += 1 + field->len;
604   }
605 
606   auto iov = make_byte_ref(balloc, len + 1);
607   auto p = std::begin(iov);
608 
609   if (!request_authority.empty()) {
610     p = std::copy(std::begin(upstream_scheme), std::end(upstream_scheme), p);
611     p = util::copy_lit(p, "://");
612     p = std::copy(std::begin(request_authority), std::end(request_authority),
613                   p);
614   }
615   if (u.field_set & (1 << UF_PATH)) {
616     field = &u.field_data[UF_PATH];
617     p = std::copy_n(&uri[field->off], field->len, p);
618   }
619   if (u.field_set & (1 << UF_QUERY)) {
620     field = &u.field_data[UF_QUERY];
621     *p++ = '?';
622     p = std::copy_n(&uri[field->off], field->len, p);
623   }
624   if (u.field_set & (1 << UF_FRAGMENT)) {
625     field = &u.field_data[UF_FRAGMENT];
626     *p++ = '#';
627     p = std::copy_n(&uri[field->off], field->len, p);
628   }
629 
630   *p = '\0';
631 
632   return StringRef{std::span{std::begin(iov), p}};
633 }
634 
parse_http_status_code(const StringRef & src)635 int parse_http_status_code(const StringRef &src) {
636   if (src.size() != 3) {
637     return -1;
638   }
639 
640   int status = 0;
641   for (auto c : src) {
642     if (!isdigit(c)) {
643       return -1;
644     }
645     status *= 10;
646     status += c - '0';
647   }
648 
649   if (status < 100) {
650     return -1;
651   }
652 
653   return status;
654 }
655 
656 // This function was generated by genheaderfunc.py.  Inspired by h2o
657 // header lookup.  https://github.com/h2o/h2o
lookup_token(const StringRef & name)658 int lookup_token(const StringRef &name) {
659   switch (name.size()) {
660   case 2:
661     switch (name[1]) {
662     case 'e':
663       if (util::streq("t"_sr, name, 1)) {
664         return HD_TE;
665       }
666       break;
667     }
668     break;
669   case 3:
670     switch (name[2]) {
671     case 'a':
672       if (util::streq("vi"_sr, name, 2)) {
673         return HD_VIA;
674       }
675       break;
676     }
677     break;
678   case 4:
679     switch (name[3]) {
680     case 'e':
681       if (util::streq("dat"_sr, name, 3)) {
682         return HD_DATE;
683       }
684       break;
685     case 'k':
686       if (util::streq("lin"_sr, name, 3)) {
687         return HD_LINK;
688       }
689       break;
690     case 't':
691       if (util::streq("hos"_sr, name, 3)) {
692         return HD_HOST;
693       }
694       break;
695     }
696     break;
697   case 5:
698     switch (name[4]) {
699     case 'h':
700       if (util::streq(":pat"_sr, name, 4)) {
701         return HD__PATH;
702       }
703       break;
704     case 't':
705       if (util::streq(":hos"_sr, name, 4)) {
706         return HD__HOST;
707       }
708       break;
709     }
710     break;
711   case 6:
712     switch (name[5]) {
713     case 'e':
714       if (util::streq("cooki"_sr, name, 5)) {
715         return HD_COOKIE;
716       }
717       break;
718     case 'r':
719       if (util::streq("serve"_sr, name, 5)) {
720         return HD_SERVER;
721       }
722       break;
723     case 't':
724       if (util::streq("expec"_sr, name, 5)) {
725         return HD_EXPECT;
726       }
727       break;
728     }
729     break;
730   case 7:
731     switch (name[6]) {
732     case 'c':
733       if (util::streq("alt-sv"_sr, name, 6)) {
734         return HD_ALT_SVC;
735       }
736       break;
737     case 'd':
738       if (util::streq(":metho"_sr, name, 6)) {
739         return HD__METHOD;
740       }
741       break;
742     case 'e':
743       if (util::streq(":schem"_sr, name, 6)) {
744         return HD__SCHEME;
745       }
746       if (util::streq("upgrad"_sr, name, 6)) {
747         return HD_UPGRADE;
748       }
749       break;
750     case 'r':
751       if (util::streq("traile"_sr, name, 6)) {
752         return HD_TRAILER;
753       }
754       break;
755     case 's':
756       if (util::streq(":statu"_sr, name, 6)) {
757         return HD__STATUS;
758       }
759       break;
760     }
761     break;
762   case 8:
763     switch (name[7]) {
764     case 'n':
765       if (util::streq("locatio"_sr, name, 7)) {
766         return HD_LOCATION;
767       }
768       break;
769     case 'y':
770       if (util::streq("priorit"_sr, name, 7)) {
771         return HD_PRIORITY;
772       }
773       break;
774     }
775     break;
776   case 9:
777     switch (name[8]) {
778     case 'd':
779       if (util::streq("forwarde"_sr, name, 8)) {
780         return HD_FORWARDED;
781       }
782       break;
783     case 'l':
784       if (util::streq(":protoco"_sr, name, 8)) {
785         return HD__PROTOCOL;
786       }
787       break;
788     }
789     break;
790   case 10:
791     switch (name[9]) {
792     case 'a':
793       if (util::streq("early-dat"_sr, name, 9)) {
794         return HD_EARLY_DATA;
795       }
796       break;
797     case 'e':
798       if (util::streq("keep-aliv"_sr, name, 9)) {
799         return HD_KEEP_ALIVE;
800       }
801       break;
802     case 'n':
803       if (util::streq("connectio"_sr, name, 9)) {
804         return HD_CONNECTION;
805       }
806       break;
807     case 't':
808       if (util::streq("user-agen"_sr, name, 9)) {
809         return HD_USER_AGENT;
810       }
811       break;
812     case 'y':
813       if (util::streq(":authorit"_sr, name, 9)) {
814         return HD__AUTHORITY;
815       }
816       break;
817     }
818     break;
819   case 12:
820     switch (name[11]) {
821     case 'e':
822       if (util::streq("content-typ"_sr, name, 11)) {
823         return HD_CONTENT_TYPE;
824       }
825       break;
826     }
827     break;
828   case 13:
829     switch (name[12]) {
830     case 'l':
831       if (util::streq("cache-contro"_sr, name, 12)) {
832         return HD_CACHE_CONTROL;
833       }
834       break;
835     }
836     break;
837   case 14:
838     switch (name[13]) {
839     case 'h':
840       if (util::streq("content-lengt"_sr, name, 13)) {
841         return HD_CONTENT_LENGTH;
842       }
843       break;
844     case 's':
845       if (util::streq("http2-setting"_sr, name, 13)) {
846         return HD_HTTP2_SETTINGS;
847       }
848       break;
849     }
850     break;
851   case 15:
852     switch (name[14]) {
853     case 'e':
854       if (util::streq("accept-languag"_sr, name, 14)) {
855         return HD_ACCEPT_LANGUAGE;
856       }
857       break;
858     case 'g':
859       if (util::streq("accept-encodin"_sr, name, 14)) {
860         return HD_ACCEPT_ENCODING;
861       }
862       break;
863     case 'r':
864       if (util::streq("x-forwarded-fo"_sr, name, 14)) {
865         return HD_X_FORWARDED_FOR;
866       }
867       break;
868     }
869     break;
870   case 16:
871     switch (name[15]) {
872     case 'n':
873       if (util::streq("proxy-connectio"_sr, name, 15)) {
874         return HD_PROXY_CONNECTION;
875       }
876       break;
877     }
878     break;
879   case 17:
880     switch (name[16]) {
881     case 'e':
882       if (util::streq("if-modified-sinc"_sr, name, 16)) {
883         return HD_IF_MODIFIED_SINCE;
884       }
885       break;
886     case 'g':
887       if (util::streq("transfer-encodin"_sr, name, 16)) {
888         return HD_TRANSFER_ENCODING;
889       }
890       break;
891     case 'o':
892       if (util::streq("x-forwarded-prot"_sr, name, 16)) {
893         return HD_X_FORWARDED_PROTO;
894       }
895       break;
896     case 'y':
897       if (util::streq("sec-websocket-ke"_sr, name, 16)) {
898         return HD_SEC_WEBSOCKET_KEY;
899       }
900       break;
901     }
902     break;
903   case 20:
904     switch (name[19]) {
905     case 't':
906       if (util::streq("sec-websocket-accep"_sr, name, 19)) {
907         return HD_SEC_WEBSOCKET_ACCEPT;
908       }
909       break;
910     }
911     break;
912   }
913   return -1;
914 }
915 
init_hdidx(HeaderIndex & hdidx)916 void init_hdidx(HeaderIndex &hdidx) {
917   std::fill(std::begin(hdidx), std::end(hdidx), -1);
918 }
919 
index_header(HeaderIndex & hdidx,int32_t token,size_t idx)920 void index_header(HeaderIndex &hdidx, int32_t token, size_t idx) {
921   if (token == -1) {
922     return;
923   }
924   assert(token < HD_MAXIDX);
925   hdidx[token] = idx;
926 }
927 
get_header(const HeaderIndex & hdidx,int32_t token,const Headers & nva)928 const Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
929                                       const Headers &nva) {
930   auto i = hdidx[token];
931   if (i == -1) {
932     return nullptr;
933   }
934   return &nva[i];
935 }
936 
get_header(const HeaderIndex & hdidx,int32_t token,Headers & nva)937 Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
938                                 Headers &nva) {
939   auto i = hdidx[token];
940   if (i == -1) {
941     return nullptr;
942   }
943   return &nva[i];
944 }
945 
946 namespace {
skip_lws(InputIt first,InputIt last)947 template <typename InputIt> InputIt skip_lws(InputIt first, InputIt last) {
948   for (; first != last; ++first) {
949     switch (*first) {
950     case ' ':
951     case '\t':
952       continue;
953     default:
954       return first;
955     }
956   }
957   return first;
958 }
959 } // namespace
960 
961 namespace {
962 template <typename InputIt>
skip_to_next_field(InputIt first,InputIt last)963 InputIt skip_to_next_field(InputIt first, InputIt last) {
964   for (; first != last; ++first) {
965     switch (*first) {
966     case ' ':
967     case '\t':
968     case ',':
969       continue;
970     default:
971       return first;
972     }
973   }
974   return first;
975 }
976 } // namespace
977 
978 namespace {
979 // Skip to the right dquote ('"'), handling backslash escapes.
980 // Returns |last| if input is not terminated with '"'.
981 template <typename InputIt>
skip_to_right_dquote(InputIt first,InputIt last)982 InputIt skip_to_right_dquote(InputIt first, InputIt last) {
983   for (; first != last;) {
984     switch (*first) {
985     case '"':
986       return first;
987       // quoted-pair
988     case '\\':
989       ++first;
990       if (first == last) {
991         return first;
992       }
993 
994       switch (*first) {
995       case '\t':
996       case ' ':
997         break;
998       default:
999         if ((0x21 <= *first && *first <= 0x7e) /* VCHAR */ ||
1000             (0x80 <= *first && *first <= 0xff) /* obs-text */) {
1001           break;
1002         }
1003 
1004         return last;
1005       }
1006 
1007       break;
1008       // qdtext
1009     case '\t':
1010     case ' ':
1011     case '!':
1012       break;
1013     default:
1014       if ((0x23 <= *first && *first <= 0x5b) ||
1015           (0x5d <= *first && *first <= 0x7e)) {
1016         break;
1017       }
1018 
1019       return last;
1020     }
1021     ++first;
1022   }
1023   return first;
1024 }
1025 } // namespace
1026 
1027 namespace {
1028 // Returns true if link-param does not match pattern |pat| of length
1029 // |patlen| or it has empty value ("").  |pat| should be parmname
1030 // followed by "=".
check_link_param_empty(const char * first,const char * last,const char * pat,size_t patlen)1031 bool check_link_param_empty(const char *first, const char *last,
1032                             const char *pat, size_t patlen) {
1033   if (first + patlen <= last) {
1034     if (std::equal(pat, pat + patlen, first, util::CaseCmp())) {
1035       // we only accept URI if pat is followed by "" (e.g.,
1036       // loadpolicy="") here.
1037       if (first + patlen + 2 <= last) {
1038         if (*(first + patlen) != '"' || *(first + patlen + 1) != '"') {
1039           return false;
1040         }
1041       } else {
1042         // here we got invalid production (anchor=") or anchor=?
1043         return false;
1044       }
1045     }
1046   }
1047   return true;
1048 }
1049 } // namespace
1050 
1051 namespace {
1052 // Returns true if link-param consists of only parmname, and it
1053 // matches string [pat, pat + patlen).
check_link_param_without_value(const char * first,const char * last,const char * pat,size_t patlen)1054 bool check_link_param_without_value(const char *first, const char *last,
1055                                     const char *pat, size_t patlen) {
1056   if (first + patlen > last) {
1057     return false;
1058   }
1059 
1060   if (first + patlen == last) {
1061     return std::equal(pat, pat + patlen, first, util::CaseCmp());
1062   }
1063 
1064   switch (*(first + patlen)) {
1065   case ';':
1066   case ',':
1067     return std::equal(pat, pat + patlen, first, util::CaseCmp());
1068   }
1069 
1070   return false;
1071 }
1072 } // namespace
1073 
1074 namespace {
1075 std::pair<LinkHeader, const char *>
parse_next_link_header_once(const char * first,const char * last)1076 parse_next_link_header_once(const char *first, const char *last) {
1077   first = skip_to_next_field(first, last);
1078   if (first == last || *first != '<') {
1079     return {{StringRef{}}, last};
1080   }
1081   auto url_first = ++first;
1082   first = std::find(first, last, '>');
1083   if (first == last) {
1084     return {{StringRef{}}, first};
1085   }
1086   auto url_last = first++;
1087   if (first == last) {
1088     return {{StringRef{}}, first};
1089   }
1090   // we expect ';' or ',' here
1091   switch (*first) {
1092   case ',':
1093     return {{StringRef{}}, ++first};
1094   case ';':
1095     ++first;
1096     break;
1097   default:
1098     return {{StringRef{}}, last};
1099   }
1100 
1101   auto ok = false;
1102   auto ign = false;
1103   for (;;) {
1104     first = skip_lws(first, last);
1105     if (first == last) {
1106       return {{StringRef{}}, first};
1107     }
1108     // we expect link-param
1109 
1110     if (!ign) {
1111       if (!ok) {
1112         // rel can take several relations using quoted form.
1113         static constexpr char PLP[] = "rel=\"";
1114         static constexpr size_t PLPLEN = str_size(PLP);
1115 
1116         static constexpr char PLT[] = "preload";
1117         static constexpr size_t PLTLEN = str_size(PLT);
1118         if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' &&
1119             std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) {
1120           // we have to search preload in whitespace separated list:
1121           // rel="preload something http://example.org/foo"
1122           first += PLPLEN;
1123           auto start = first;
1124           for (; first != last;) {
1125             if (*first != ' ' && *first != '"') {
1126               ++first;
1127               continue;
1128             }
1129 
1130             if (start == first) {
1131               return {{StringRef{}}, last};
1132             }
1133 
1134             if (!ok && start + PLTLEN == first &&
1135                 std::equal(PLT, PLT + PLTLEN, start, util::CaseCmp())) {
1136               ok = true;
1137             }
1138 
1139             if (*first == '"') {
1140               break;
1141             }
1142             first = skip_lws(first, last);
1143             start = first;
1144           }
1145           if (first == last) {
1146             return {{StringRef{}}, last};
1147           }
1148           assert(*first == '"');
1149           ++first;
1150           if (first == last || *first == ',') {
1151             goto almost_done;
1152           }
1153           if (*first == ';') {
1154             ++first;
1155             // parse next link-param
1156             continue;
1157           }
1158           return {{StringRef{}}, last};
1159         }
1160       }
1161       // we are only interested in rel=preload parameter.  Others are
1162       // simply skipped.
1163       static constexpr char PL[] = "rel=preload";
1164       static constexpr size_t PLLEN = str_size(PL);
1165       if (first + PLLEN == last) {
1166         if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
1167           // ok = true;
1168           // this is the end of sequence
1169           return {{{url_first, url_last}}, last};
1170         }
1171       } else if (first + PLLEN + 1 <= last) {
1172         switch (*(first + PLLEN)) {
1173         case ',':
1174           if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
1175             break;
1176           }
1177           // ok = true;
1178           // skip including ','
1179           first += PLLEN + 1;
1180           return {{{url_first, url_last}}, first};
1181         case ';':
1182           if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
1183             break;
1184           }
1185           ok = true;
1186           // skip including ';'
1187           first += PLLEN + 1;
1188           // continue parse next link-param
1189           continue;
1190         }
1191       }
1192       // we have to reject URI if we have nonempty anchor parameter.
1193       static constexpr char ANCHOR[] = "anchor=";
1194       static constexpr size_t ANCHORLEN = str_size(ANCHOR);
1195       if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) {
1196         ign = true;
1197       }
1198 
1199       // reject URI if we have non-empty loadpolicy.  This could be
1200       // tightened up to just pick up "next" or "insert".
1201       static constexpr char LOADPOLICY[] = "loadpolicy=";
1202       static constexpr size_t LOADPOLICYLEN = str_size(LOADPOLICY);
1203       if (!ign &&
1204           !check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) {
1205         ign = true;
1206       }
1207 
1208       // reject URI if we have nopush attribute.
1209       static constexpr char NOPUSH[] = "nopush";
1210       static constexpr size_t NOPUSHLEN = str_size(NOPUSH);
1211       if (!ign &&
1212           check_link_param_without_value(first, last, NOPUSH, NOPUSHLEN)) {
1213         ign = true;
1214       }
1215     }
1216 
1217     auto param_first = first;
1218     for (; first != last;) {
1219       if (util::in_attr_char(*first)) {
1220         ++first;
1221         continue;
1222       }
1223       // '*' is only allowed at the end of parameter name and must be
1224       // followed by '='
1225       if (last - first >= 2 && first != param_first) {
1226         if (*first == '*' && *(first + 1) == '=') {
1227           ++first;
1228           break;
1229         }
1230       }
1231       if (*first == '=' || *first == ';' || *first == ',') {
1232         break;
1233       }
1234       return {{StringRef{}}, last};
1235     }
1236     if (param_first == first) {
1237       // empty parmname
1238       return {{StringRef{}}, last};
1239     }
1240     // link-param without value is acceptable (see link-extension) if
1241     // it is not followed by '='
1242     if (first == last || *first == ',') {
1243       goto almost_done;
1244     }
1245     if (*first == ';') {
1246       ++first;
1247       // parse next link-param
1248       continue;
1249     }
1250     // now parsing link-param value
1251     assert(*first == '=');
1252     ++first;
1253     if (first == last) {
1254       // empty value is not acceptable
1255       return {{StringRef{}}, first};
1256     }
1257     if (*first == '"') {
1258       // quoted-string
1259       first = skip_to_right_dquote(first + 1, last);
1260       if (first == last) {
1261         return {{StringRef{}}, first};
1262       }
1263       ++first;
1264       if (first == last || *first == ',') {
1265         goto almost_done;
1266       }
1267       if (*first == ';') {
1268         ++first;
1269         // parse next link-param
1270         continue;
1271       }
1272       return {{StringRef{}}, last};
1273     }
1274     // not quoted-string, skip to next ',' or ';'
1275     if (*first == ',' || *first == ';') {
1276       // empty value
1277       return {{StringRef{}}, last};
1278     }
1279     for (; first != last; ++first) {
1280       if (*first == ',' || *first == ';') {
1281         break;
1282       }
1283     }
1284     if (first == last || *first == ',') {
1285       goto almost_done;
1286     }
1287     assert(*first == ';');
1288     ++first;
1289     // parse next link-param
1290   }
1291 
1292 almost_done:
1293   assert(first == last || *first == ',');
1294 
1295   if (first != last) {
1296     ++first;
1297   }
1298   if (ok && !ign) {
1299     return {{{url_first, url_last}}, first};
1300   }
1301   return {{StringRef{}}, first};
1302 }
1303 } // namespace
1304 
parse_link_header(const StringRef & src)1305 std::vector<LinkHeader> parse_link_header(const StringRef &src) {
1306   std::vector<LinkHeader> res;
1307   for (auto first = std::begin(src); first != std::end(src);) {
1308     auto rv = parse_next_link_header_once(first, std::end(src));
1309     first = rv.second;
1310     auto &link = rv.first;
1311     if (!link.uri.empty()) {
1312       res.push_back(link);
1313     }
1314   }
1315   return res;
1316 }
1317 
path_join(const StringRef & base_path,const StringRef & base_query,const StringRef & rel_path,const StringRef & rel_query)1318 std::string path_join(const StringRef &base_path, const StringRef &base_query,
1319                       const StringRef &rel_path, const StringRef &rel_query) {
1320   BlockAllocator balloc(1024, 1024);
1321 
1322   return std::string{
1323       path_join(balloc, base_path, base_query, rel_path, rel_query)};
1324 }
1325 
expect_response_body(int status_code)1326 bool expect_response_body(int status_code) {
1327   return status_code == 101 ||
1328          (status_code / 100 != 1 && status_code != 304 && status_code != 204);
1329 }
1330 
expect_response_body(const std::string & method,int status_code)1331 bool expect_response_body(const std::string &method, int status_code) {
1332   return method != "HEAD" && expect_response_body(status_code);
1333 }
1334 
expect_response_body(int method_token,int status_code)1335 bool expect_response_body(int method_token, int status_code) {
1336   return method_token != HTTP_HEAD && expect_response_body(status_code);
1337 }
1338 
1339 // This function was generated by genmethodfunc.py.
lookup_method_token(const StringRef & name)1340 int lookup_method_token(const StringRef &name) {
1341   switch (name.size()) {
1342   case 3:
1343     switch (name[2]) {
1344     case 'L':
1345       if (util::streq("AC"_sr, name, 2)) {
1346         return HTTP_ACL;
1347       }
1348       break;
1349     case 'T':
1350       if (util::streq("GE"_sr, name, 2)) {
1351         return HTTP_GET;
1352       }
1353       if (util::streq("PU"_sr, name, 2)) {
1354         return HTTP_PUT;
1355       }
1356       break;
1357     }
1358     break;
1359   case 4:
1360     switch (name[3]) {
1361     case 'D':
1362       if (util::streq("BIN"_sr, name, 3)) {
1363         return HTTP_BIND;
1364       }
1365       if (util::streq("HEA"_sr, name, 3)) {
1366         return HTTP_HEAD;
1367       }
1368       break;
1369     case 'E':
1370       if (util::streq("MOV"_sr, name, 3)) {
1371         return HTTP_MOVE;
1372       }
1373       break;
1374     case 'K':
1375       if (util::streq("LIN"_sr, name, 3)) {
1376         return HTTP_LINK;
1377       }
1378       if (util::streq("LOC"_sr, name, 3)) {
1379         return HTTP_LOCK;
1380       }
1381       break;
1382     case 'T':
1383       if (util::streq("POS"_sr, name, 3)) {
1384         return HTTP_POST;
1385       }
1386       break;
1387     case 'Y':
1388       if (util::streq("COP"_sr, name, 3)) {
1389         return HTTP_COPY;
1390       }
1391       break;
1392     }
1393     break;
1394   case 5:
1395     switch (name[4]) {
1396     case 'E':
1397       if (util::streq("MERG"_sr, name, 4)) {
1398         return HTTP_MERGE;
1399       }
1400       if (util::streq("PURG"_sr, name, 4)) {
1401         return HTTP_PURGE;
1402       }
1403       if (util::streq("TRAC"_sr, name, 4)) {
1404         return HTTP_TRACE;
1405       }
1406       break;
1407     case 'H':
1408       if (util::streq("PATC"_sr, name, 4)) {
1409         return HTTP_PATCH;
1410       }
1411       break;
1412     case 'L':
1413       if (util::streq("MKCO"_sr, name, 4)) {
1414         return HTTP_MKCOL;
1415       }
1416       break;
1417     }
1418     break;
1419   case 6:
1420     switch (name[5]) {
1421     case 'D':
1422       if (util::streq("REBIN"_sr, name, 5)) {
1423         return HTTP_REBIND;
1424       }
1425       if (util::streq("UNBIN"_sr, name, 5)) {
1426         return HTTP_UNBIND;
1427       }
1428       break;
1429     case 'E':
1430       if (util::streq("DELET"_sr, name, 5)) {
1431         return HTTP_DELETE;
1432       }
1433       if (util::streq("SOURC"_sr, name, 5)) {
1434         return HTTP_SOURCE;
1435       }
1436       break;
1437     case 'H':
1438       if (util::streq("SEARC"_sr, name, 5)) {
1439         return HTTP_SEARCH;
1440       }
1441       break;
1442     case 'K':
1443       if (util::streq("UNLIN"_sr, name, 5)) {
1444         return HTTP_UNLINK;
1445       }
1446       if (util::streq("UNLOC"_sr, name, 5)) {
1447         return HTTP_UNLOCK;
1448       }
1449       break;
1450     case 'T':
1451       if (util::streq("REPOR"_sr, name, 5)) {
1452         return HTTP_REPORT;
1453       }
1454       break;
1455     case 'Y':
1456       if (util::streq("NOTIF"_sr, name, 5)) {
1457         return HTTP_NOTIFY;
1458       }
1459       break;
1460     }
1461     break;
1462   case 7:
1463     switch (name[6]) {
1464     case 'H':
1465       if (util::streq("MSEARC"_sr, name, 6)) {
1466         return HTTP_MSEARCH;
1467       }
1468       break;
1469     case 'S':
1470       if (util::streq("OPTION"_sr, name, 6)) {
1471         return HTTP_OPTIONS;
1472       }
1473       break;
1474     case 'T':
1475       if (util::streq("CONNEC"_sr, name, 6)) {
1476         return HTTP_CONNECT;
1477       }
1478       break;
1479     }
1480     break;
1481   case 8:
1482     switch (name[7]) {
1483     case 'D':
1484       if (util::streq("PROPFIN"_sr, name, 7)) {
1485         return HTTP_PROPFIND;
1486       }
1487       break;
1488     case 'T':
1489       if (util::streq("CHECKOU"_sr, name, 7)) {
1490         return HTTP_CHECKOUT;
1491       }
1492       break;
1493     }
1494     break;
1495   case 9:
1496     switch (name[8]) {
1497     case 'E':
1498       if (util::streq("SUBSCRIB"_sr, name, 8)) {
1499         return HTTP_SUBSCRIBE;
1500       }
1501       break;
1502     case 'H':
1503       if (util::streq("PROPPATC"_sr, name, 8)) {
1504         return HTTP_PROPPATCH;
1505       }
1506       break;
1507     }
1508     break;
1509   case 10:
1510     switch (name[9]) {
1511     case 'R':
1512       if (util::streq("MKCALENDA"_sr, name, 9)) {
1513         return HTTP_MKCALENDAR;
1514       }
1515       break;
1516     case 'Y':
1517       if (util::streq("MKACTIVIT"_sr, name, 9)) {
1518         return HTTP_MKACTIVITY;
1519       }
1520       break;
1521     }
1522     break;
1523   case 11:
1524     switch (name[10]) {
1525     case 'E':
1526       if (util::streq("UNSUBSCRIB"_sr, name, 10)) {
1527         return HTTP_UNSUBSCRIBE;
1528       }
1529       break;
1530     }
1531     break;
1532   }
1533   return -1;
1534 }
1535 
to_method_string(int method_token)1536 StringRef to_method_string(int method_token) {
1537   // we happened to use same value for method with llhttp.
1538   return StringRef{
1539       llhttp_method_name(static_cast<llhttp_method>(method_token))};
1540 }
1541 
get_pure_path_component(const StringRef & uri)1542 StringRef get_pure_path_component(const StringRef &uri) {
1543   int rv;
1544 
1545   http_parser_url u{};
1546   rv = http_parser_parse_url(uri.data(), uri.size(), 0, &u);
1547   if (rv != 0) {
1548     return StringRef{};
1549   }
1550 
1551   if (u.field_set & (1 << UF_PATH)) {
1552     auto &f = u.field_data[UF_PATH];
1553     return StringRef{uri.data() + f.off, f.len};
1554   }
1555 
1556   return "/"_sr;
1557 }
1558 
construct_push_component(BlockAllocator & balloc,StringRef & scheme,StringRef & authority,StringRef & path,const StringRef & base,const StringRef & uri)1559 int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
1560                              StringRef &authority, StringRef &path,
1561                              const StringRef &base, const StringRef &uri) {
1562   int rv;
1563   StringRef rel, relq;
1564 
1565   if (uri.size() == 0) {
1566     return -1;
1567   }
1568 
1569   http_parser_url u{};
1570 
1571   rv = http_parser_parse_url(uri.data(), uri.size(), 0, &u);
1572 
1573   if (rv != 0) {
1574     if (uri[0] == '/') {
1575       return -1;
1576     }
1577 
1578     // treat link_url as relative URI.
1579     auto end = std::find(std::begin(uri), std::end(uri), '#');
1580     auto q = std::find(std::begin(uri), end, '?');
1581 
1582     rel = StringRef{std::begin(uri), q};
1583     if (q != end) {
1584       relq = StringRef{q + 1, std::end(uri)};
1585     }
1586   } else {
1587     if (u.field_set & (1 << UF_SCHEMA)) {
1588       scheme = util::get_uri_field(uri.data(), u, UF_SCHEMA);
1589     }
1590 
1591     if (u.field_set & (1 << UF_HOST)) {
1592       auto auth = util::get_uri_field(uri.data(), u, UF_HOST);
1593       auto len = auth.size();
1594       auto port_exists = u.field_set & (1 << UF_PORT);
1595       if (port_exists) {
1596         len += 1 + str_size("65535");
1597       }
1598       auto iov = make_byte_ref(balloc, len + 1);
1599       auto p = std::begin(iov);
1600       p = std::copy(std::begin(auth), std::end(auth), p);
1601       if (port_exists) {
1602         *p++ = ':';
1603         p = util::utos(p, u.port);
1604       }
1605       *p = '\0';
1606 
1607       authority = StringRef{std::span{std::begin(iov), p}};
1608     }
1609 
1610     if (u.field_set & (1 << UF_PATH)) {
1611       auto &f = u.field_data[UF_PATH];
1612       rel = StringRef{uri.data() + f.off, f.len};
1613     } else {
1614       rel = "/"_sr;
1615     }
1616 
1617     if (u.field_set & (1 << UF_QUERY)) {
1618       auto &f = u.field_data[UF_QUERY];
1619       relq = StringRef{uri.data() + f.off, f.len};
1620     }
1621   }
1622 
1623   path = path_join(balloc, base, StringRef{}, rel, relq);
1624 
1625   return 0;
1626 }
1627 
1628 namespace {
eat_file(InputIt first,InputIt last)1629 template <typename InputIt> InputIt eat_file(InputIt first, InputIt last) {
1630   if (first == last) {
1631     *first++ = '/';
1632     return first;
1633   }
1634 
1635   if (*(last - 1) == '/') {
1636     return last;
1637   }
1638 
1639   auto p = last;
1640   for (; p != first && *(p - 1) != '/'; --p)
1641     ;
1642   if (p == first) {
1643     // this should not happened in normal case, where we expect path
1644     // starts with '/'
1645     *first++ = '/';
1646     return first;
1647   }
1648 
1649   return p;
1650 }
1651 } // namespace
1652 
1653 namespace {
eat_dir(InputIt first,InputIt last)1654 template <typename InputIt> InputIt eat_dir(InputIt first, InputIt last) {
1655   auto p = eat_file(first, last);
1656 
1657   --p;
1658 
1659   assert(*p == '/');
1660 
1661   return eat_file(first, p);
1662 }
1663 } // namespace
1664 
path_join(BlockAllocator & balloc,const StringRef & base_path,const StringRef & base_query,const StringRef & rel_path,const StringRef & rel_query)1665 StringRef path_join(BlockAllocator &balloc, const StringRef &base_path,
1666                     const StringRef &base_query, const StringRef &rel_path,
1667                     const StringRef &rel_query) {
1668   auto res = make_byte_ref(
1669       balloc, std::max(static_cast<size_t>(1), base_path.size()) +
1670                   rel_path.size() + 1 +
1671                   std::max(base_query.size(), rel_query.size()) + 1);
1672   auto p = std::begin(res);
1673 
1674   if (rel_path.empty()) {
1675     if (base_path.empty()) {
1676       *p++ = '/';
1677     } else {
1678       p = std::copy(std::begin(base_path), std::end(base_path), p);
1679     }
1680     if (rel_query.empty()) {
1681       if (!base_query.empty()) {
1682         *p++ = '?';
1683         p = std::copy(std::begin(base_query), std::end(base_query), p);
1684       }
1685       *p = '\0';
1686       return StringRef{std::span{std::begin(res), p}};
1687     }
1688     *p++ = '?';
1689     p = std::copy(std::begin(rel_query), std::end(rel_query), p);
1690     *p = '\0';
1691     return StringRef{std::span{std::begin(res), p}};
1692   }
1693 
1694   auto first = std::begin(rel_path);
1695   auto last = std::end(rel_path);
1696 
1697   if (rel_path[0] == '/') {
1698     *p++ = '/';
1699     ++first;
1700     for (; first != last && *first == '/'; ++first)
1701       ;
1702   } else if (base_path.empty()) {
1703     *p++ = '/';
1704   } else {
1705     p = std::copy(std::begin(base_path), std::end(base_path), p);
1706   }
1707 
1708   for (; first != last;) {
1709     if (*first == '.') {
1710       if (first + 1 == last) {
1711         if (*(p - 1) != '/') {
1712           p = eat_file(std::begin(res), p);
1713         }
1714         break;
1715       }
1716       if (*(first + 1) == '/') {
1717         if (*(p - 1) != '/') {
1718           p = eat_file(std::begin(res), p);
1719         }
1720         first += 2;
1721         continue;
1722       }
1723       if (*(first + 1) == '.') {
1724         if (first + 2 == last) {
1725           p = eat_dir(std::begin(res), p);
1726           break;
1727         }
1728         if (*(first + 2) == '/') {
1729           p = eat_dir(std::begin(res), p);
1730           first += 3;
1731           continue;
1732         }
1733       }
1734     }
1735     if (*(p - 1) != '/') {
1736       p = eat_file(std::begin(res), p);
1737     }
1738     auto slash = std::find(first, last, '/');
1739     if (slash == last) {
1740       p = std::copy(first, last, p);
1741       break;
1742     }
1743     p = std::copy(first, slash + 1, p);
1744     first = slash + 1;
1745     for (; first != last && *first == '/'; ++first)
1746       ;
1747   }
1748   if (!rel_query.empty()) {
1749     *p++ = '?';
1750     p = std::copy(std::begin(rel_query), std::end(rel_query), p);
1751   }
1752   *p = '\0';
1753   return StringRef{std::span{std::begin(res), p}};
1754 }
1755 
normalize_path(BlockAllocator & balloc,const StringRef & path,const StringRef & query)1756 StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
1757                          const StringRef &query) {
1758   // First, decode %XX for unreserved characters, then do
1759   // http2::path_join
1760 
1761   // We won't find %XX if length is less than 3.
1762   if (path.size() < 3 ||
1763       std::find(std::begin(path), std::end(path), '%') == std::end(path)) {
1764     return path_join(balloc, StringRef{}, StringRef{}, path, query);
1765   }
1766 
1767   // includes last terminal NULL.
1768   auto result = make_byte_ref(balloc, path.size() + 1);
1769   auto p = std::begin(result);
1770 
1771   auto it = std::begin(path);
1772   for (; it + 2 < std::end(path);) {
1773     if (*it == '%') {
1774       if (util::is_hex_digit(*(it + 1)) && util::is_hex_digit(*(it + 2))) {
1775         auto c =
1776             (util::hex_to_uint(*(it + 1)) << 4) + util::hex_to_uint(*(it + 2));
1777         if (util::in_rfc3986_unreserved_chars(c)) {
1778           *p++ = c;
1779 
1780           it += 3;
1781 
1782           continue;
1783         }
1784         *p++ = '%';
1785         *p++ = util::upcase(*(it + 1));
1786         *p++ = util::upcase(*(it + 2));
1787 
1788         it += 3;
1789 
1790         continue;
1791       }
1792     }
1793     *p++ = *it++;
1794   }
1795 
1796   p = std::copy(it, std::end(path), p);
1797   *p = '\0';
1798 
1799   return path_join(balloc, StringRef{}, StringRef{},
1800                    StringRef{std::span{std::begin(result), p}}, query);
1801 }
1802 
normalize_path_colon(BlockAllocator & balloc,const StringRef & path,const StringRef & query)1803 StringRef normalize_path_colon(BlockAllocator &balloc, const StringRef &path,
1804                                const StringRef &query) {
1805   // First, decode %XX for unreserved characters and ':', then do
1806   // http2::path_join
1807 
1808   // We won't find %XX if length is less than 3.
1809   if (path.size() < 3 ||
1810       std::find(std::begin(path), std::end(path), '%') == std::end(path)) {
1811     return path_join(balloc, StringRef{}, StringRef{}, path, query);
1812   }
1813 
1814   // includes last terminal NULL.
1815   auto result = make_byte_ref(balloc, path.size() + 1);
1816   auto p = std::begin(result);
1817 
1818   auto it = std::begin(path);
1819   for (; it + 2 < std::end(path);) {
1820     if (*it == '%') {
1821       if (util::is_hex_digit(*(it + 1)) && util::is_hex_digit(*(it + 2))) {
1822         auto c =
1823             (util::hex_to_uint(*(it + 1)) << 4) + util::hex_to_uint(*(it + 2));
1824         if (util::in_rfc3986_unreserved_chars(c) || c == ':') {
1825           *p++ = c;
1826 
1827           it += 3;
1828 
1829           continue;
1830         }
1831         *p++ = '%';
1832         *p++ = util::upcase(*(it + 1));
1833         *p++ = util::upcase(*(it + 2));
1834 
1835         it += 3;
1836 
1837         continue;
1838       }
1839     }
1840     *p++ = *it++;
1841   }
1842 
1843   p = std::copy(it, std::end(path), p);
1844   *p = '\0';
1845 
1846   return path_join(balloc, StringRef{}, StringRef{},
1847                    StringRef{std::span{std::begin(result), p}}, query);
1848 }
1849 
normalize_path(const StringRef & path,const StringRef & query)1850 std::string normalize_path(const StringRef &path, const StringRef &query) {
1851   BlockAllocator balloc(1024, 1024);
1852 
1853   return std::string{normalize_path(balloc, path, query)};
1854 }
1855 
rewrite_clean_path(BlockAllocator & balloc,const StringRef & src)1856 StringRef rewrite_clean_path(BlockAllocator &balloc, const StringRef &src) {
1857   if (src.empty() || src[0] != '/') {
1858     return src;
1859   }
1860   // probably, not necessary most of the case, but just in case.
1861   auto fragment = std::find(std::begin(src), std::end(src), '#');
1862   auto raw_query = std::find(std::begin(src), fragment, '?');
1863   auto query = raw_query;
1864   if (query != fragment) {
1865     ++query;
1866   }
1867   return normalize_path(balloc, StringRef{std::begin(src), raw_query},
1868                         StringRef{query, fragment});
1869 }
1870 
copy_lower(BlockAllocator & balloc,const StringRef & src)1871 StringRef copy_lower(BlockAllocator &balloc, const StringRef &src) {
1872   auto iov = make_byte_ref(balloc, src.size() + 1);
1873   auto p = std::begin(iov);
1874   p = std::copy(std::begin(src), std::end(src), p);
1875   *p = '\0';
1876   util::inp_strlower(std::begin(iov), p);
1877   return StringRef{std::span{std::begin(iov), p}};
1878 }
1879 
contains_trailers(const StringRef & s)1880 bool contains_trailers(const StringRef &s) {
1881   constexpr auto trailers = "trailers"_sr;
1882 
1883   for (auto p = std::begin(s), end = std::end(s);; ++p) {
1884     p = std::find_if(p, end, [](char c) { return c != ' ' && c != '\t'; });
1885     if (p == end || static_cast<size_t>(end - p) < trailers.size()) {
1886       return false;
1887     }
1888     if (util::strieq(trailers, StringRef{p, p + trailers.size()})) {
1889       // Make sure that there is no character other than white spaces
1890       // before next "," or end of string.
1891       p = std::find_if(p + trailers.size(), end,
1892                        [](char c) { return c != ' ' && c != '\t'; });
1893       if (p == end || *p == ',') {
1894         return true;
1895       }
1896     }
1897     // Skip to next ",".
1898     p = std::find_if(p, end, [](char c) { return c == ','; });
1899     if (p == end) {
1900       return false;
1901     }
1902   }
1903 }
1904 
make_websocket_accept_token(uint8_t * dest,const StringRef & key)1905 StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key) {
1906   static constexpr char magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1907   std::array<char, base64::encode_length(16) + str_size(magic)> s;
1908   auto p = std::copy(std::begin(key), std::end(key), std::begin(s));
1909   std::copy_n(magic, str_size(magic), p);
1910 
1911   std::array<uint8_t, 20> h;
1912   if (util::sha1(h.data(), StringRef{s}) != 0) {
1913     return StringRef{};
1914   }
1915 
1916   auto end = base64::encode(std::begin(h), std::end(h), dest);
1917   return StringRef{std::span{dest, end}};
1918 }
1919 
legacy_http1(int major,int minor)1920 bool legacy_http1(int major, int minor) {
1921   return major <= 0 || (major == 1 && minor == 0);
1922 }
1923 
check_transfer_encoding(const StringRef & s)1924 bool check_transfer_encoding(const StringRef &s) {
1925   if (s.empty()) {
1926     return false;
1927   }
1928 
1929   auto it = std::begin(s);
1930 
1931   for (;;) {
1932     // token
1933     if (!util::in_token(*it)) {
1934       return false;
1935     }
1936 
1937     ++it;
1938 
1939     for (; it != std::end(s) && util::in_token(*it); ++it)
1940       ;
1941 
1942     if (it == std::end(s)) {
1943       return true;
1944     }
1945 
1946     for (;;) {
1947       // OWS
1948       it = skip_lws(it, std::end(s));
1949       if (it == std::end(s)) {
1950         return false;
1951       }
1952 
1953       if (*it == ',') {
1954         ++it;
1955 
1956         it = skip_lws(it, std::end(s));
1957         if (it == std::end(s)) {
1958           return false;
1959         }
1960 
1961         break;
1962       }
1963 
1964       if (*it != ';') {
1965         return false;
1966       }
1967 
1968       ++it;
1969 
1970       // transfer-parameter follows
1971 
1972       // OWS
1973       it = skip_lws(it, std::end(s));
1974       if (it == std::end(s)) {
1975         return false;
1976       }
1977 
1978       // token
1979       if (!util::in_token(*it)) {
1980         return false;
1981       }
1982 
1983       ++it;
1984 
1985       for (; it != std::end(s) && util::in_token(*it); ++it)
1986         ;
1987 
1988       if (it == std::end(s)) {
1989         return false;
1990       }
1991 
1992       // No BWS allowed
1993       if (*it != '=') {
1994         return false;
1995       }
1996 
1997       ++it;
1998 
1999       if (util::in_token(*it)) {
2000         // token
2001         ++it;
2002 
2003         for (; it != std::end(s) && util::in_token(*it); ++it)
2004           ;
2005       } else if (*it == '"') {
2006         // quoted-string
2007         ++it;
2008 
2009         it = skip_to_right_dquote(it, std::end(s));
2010         if (it == std::end(s)) {
2011           return false;
2012         }
2013 
2014         ++it;
2015       } else {
2016         return false;
2017       }
2018 
2019       if (it == std::end(s)) {
2020         return true;
2021       }
2022     }
2023   }
2024 }
2025 
2026 } // namespace http2
2027 
2028 } // namespace nghttp2
2029