• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2013 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 "nghttp.h"
26 
27 #include <sys/stat.h>
28 #ifdef HAVE_UNISTD_H
29 #  include <unistd.h>
30 #endif // HAVE_UNISTD_H
31 #ifdef HAVE_FCNTL_H
32 #  include <fcntl.h>
33 #endif // HAVE_FCNTL_H
34 #ifdef HAVE_NETINET_IN_H
35 #  include <netinet/in.h>
36 #endif // HAVE_NETINET_IN_H
37 #include <netinet/tcp.h>
38 #include <getopt.h>
39 
40 #include <cassert>
41 #include <cstdio>
42 #include <cerrno>
43 #include <cstdlib>
44 #include <cstring>
45 #include <iostream>
46 #include <iomanip>
47 #include <sstream>
48 #include <tuple>
49 
50 #include <openssl/err.h>
51 
52 #ifdef HAVE_JANSSON
53 #  include <jansson.h>
54 #endif // HAVE_JANSSON
55 
56 #include "app_helper.h"
57 #include "HtmlParser.h"
58 #include "util.h"
59 #include "base64.h"
60 #include "tls.h"
61 #include "template.h"
62 #include "ssl_compat.h"
63 
64 #ifndef O_BINARY
65 #  define O_BINARY (0)
66 #endif // O_BINARY
67 
68 namespace nghttp2 {
69 
70 // The anchor stream nodes when --no-dep is not used.  The stream ID =
71 // 1 is excluded since it is used as first stream in upgrade case.  We
72 // follows the same dependency anchor nodes as Firefox does.
73 struct Anchor {
74   int32_t stream_id;
75   // stream ID this anchor depends on
76   int32_t dep_stream_id;
77   // .. with this weight.
78   int32_t weight;
79 };
80 
81 // This is index into anchors.  Firefox uses ANCHOR_FOLLOWERS for html
82 // file.
83 enum {
84   ANCHOR_LEADERS,
85   ANCHOR_UNBLOCKED,
86   ANCHOR_BACKGROUND,
87   ANCHOR_SPECULATIVE,
88   ANCHOR_FOLLOWERS,
89 };
90 
91 namespace {
92 constexpr auto anchors = std::array<Anchor, 5>{{
93     {3, 0, 201},
94     {5, 0, 101},
95     {7, 0, 1},
96     {9, 7, 1},
97     {11, 3, 1},
98 }};
99 } // namespace
100 
Config()101 Config::Config()
102     : header_table_size(-1),
103       min_header_table_size(std::numeric_limits<uint32_t>::max()),
104       encoder_header_table_size(-1),
105       padding(0),
106       max_concurrent_streams(100),
107       peer_max_concurrent_streams(100),
108       multiply(1),
109       timeout(0.),
110       window_bits(-1),
111       connection_window_bits(-1),
112       verbose(0),
113       port_override(0),
114       null_out(false),
115       remote_name(false),
116       get_assets(false),
117       stat(false),
118       upgrade(false),
119       continuation(false),
120       no_content_length(false),
121       no_dep(false),
122       hexdump(false),
123       no_push(false),
124       expect_continue(false),
125       verify_peer(true),
126       ktls(false),
127       no_rfc7540_pri(false) {
128   nghttp2_option_new(&http2_option);
129   nghttp2_option_set_peer_max_concurrent_streams(http2_option,
130                                                  peer_max_concurrent_streams);
131   nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ALTSVC);
132   nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ORIGIN);
133 }
134 
~Config()135 Config::~Config() { nghttp2_option_del(http2_option); }
136 
137 namespace {
138 Config config;
139 } // namespace
140 
141 namespace {
print_protocol_nego_error()142 void print_protocol_nego_error() {
143   std::cerr << "[ERROR] HTTP/2 protocol was not selected."
144             << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")"
145             << std::endl;
146 }
147 } // namespace
148 
149 namespace {
strip_fragment(const char * raw_uri)150 std::string strip_fragment(const char *raw_uri) {
151   const char *end;
152   for (end = raw_uri; *end && *end != '#'; ++end)
153     ;
154   size_t len = end - raw_uri;
155   return std::string(raw_uri, len);
156 }
157 } // namespace
158 
Request(const std::string & uri,const http_parser_url & u,const nghttp2_data_provider * data_prd,int64_t data_length,const nghttp2_priority_spec & pri_spec,int level)159 Request::Request(const std::string &uri, const http_parser_url &u,
160                  const nghttp2_data_provider *data_prd, int64_t data_length,
161                  const nghttp2_priority_spec &pri_spec, int level)
162     : uri(uri),
163       u(u),
164       pri_spec(pri_spec),
165       data_length(data_length),
166       data_offset(0),
167       response_len(0),
168       inflater(nullptr),
169       data_prd(data_prd),
170       header_buffer_size(0),
171       stream_id(-1),
172       status(0),
173       level(level),
174       expect_final_response(false) {
175   http2::init_hdidx(res_hdidx);
176   http2::init_hdidx(req_hdidx);
177 }
178 
~Request()179 Request::~Request() { nghttp2_gzip_inflate_del(inflater); }
180 
init_inflater()181 void Request::init_inflater() {
182   int rv;
183   // This is required with --disable-assert.
184   (void)rv;
185   rv = nghttp2_gzip_inflate_new(&inflater);
186   assert(rv == 0);
187 }
188 
get_real_scheme() const189 StringRef Request::get_real_scheme() const {
190   return config.scheme_override.empty()
191              ? util::get_uri_field(uri.c_str(), u, UF_SCHEMA)
192              : StringRef{config.scheme_override};
193 }
194 
get_real_host() const195 StringRef Request::get_real_host() const {
196   return config.host_override.empty()
197              ? util::get_uri_field(uri.c_str(), u, UF_HOST)
198              : StringRef{config.host_override};
199 }
200 
get_real_port() const201 uint16_t Request::get_real_port() const {
202   auto scheme = get_real_scheme();
203   return config.host_override.empty() ? util::has_uri_field(u, UF_PORT) ? u.port
204                                         : scheme == "https"             ? 443
205                                                                         : 80
206          : config.port_override == 0  ? scheme == "https" ? 443 : 80
207                                       : config.port_override;
208 }
209 
init_html_parser()210 void Request::init_html_parser() {
211   // We crawl HTML using overridden scheme, host, and port.
212   auto scheme = get_real_scheme();
213   auto host = get_real_host();
214   auto port = get_real_port();
215   auto ipv6_lit =
216       std::find(std::begin(host), std::end(host), ':') != std::end(host);
217 
218   auto base_uri = scheme.str();
219   base_uri += "://";
220   if (ipv6_lit) {
221     base_uri += '[';
222   }
223   base_uri += host;
224   if (ipv6_lit) {
225     base_uri += ']';
226   }
227   if (!((scheme == "https" && port == 443) ||
228         (scheme == "http" && port == 80))) {
229     base_uri += ':';
230     base_uri += util::utos(port);
231   }
232   base_uri += util::get_uri_field(uri.c_str(), u, UF_PATH);
233   if (util::has_uri_field(u, UF_QUERY)) {
234     base_uri += '?';
235     base_uri += util::get_uri_field(uri.c_str(), u, UF_QUERY);
236   }
237 
238   html_parser = std::make_unique<HtmlParser>(base_uri);
239 }
240 
update_html_parser(const uint8_t * data,size_t len,int fin)241 int Request::update_html_parser(const uint8_t *data, size_t len, int fin) {
242   if (!html_parser) {
243     return 0;
244   }
245   return html_parser->parse_chunk(reinterpret_cast<const char *>(data), len,
246                                   fin);
247 }
248 
make_reqpath() const249 std::string Request::make_reqpath() const {
250   std::string path = util::has_uri_field(u, UF_PATH)
251                          ? util::get_uri_field(uri.c_str(), u, UF_PATH).str()
252                          : "/";
253   if (util::has_uri_field(u, UF_QUERY)) {
254     path += '?';
255     path.append(uri.c_str() + u.field_data[UF_QUERY].off,
256                 u.field_data[UF_QUERY].len);
257   }
258   return path;
259 }
260 
261 namespace {
262 // Perform special handling |host| if it is IPv6 literal and includes
263 // zone ID per RFC 6874.
decode_host(const StringRef & host)264 std::string decode_host(const StringRef &host) {
265   auto zone_start = std::find(std::begin(host), std::end(host), '%');
266   if (zone_start == std::end(host) ||
267       !util::ipv6_numeric_addr(
268           std::string(std::begin(host), zone_start).c_str())) {
269     return host.str();
270   }
271   // case: ::1%
272   if (zone_start + 1 == std::end(host)) {
273     return StringRef{host.c_str(), host.size() - 1}.str();
274   }
275   // case: ::1%12 or ::1%1
276   if (zone_start + 3 >= std::end(host)) {
277     return host.str();
278   }
279   // If we see "%25", followed by more characters, then decode %25 as
280   // '%'.
281   auto zone_id_src = (*(zone_start + 1) == '2' && *(zone_start + 2) == '5')
282                          ? zone_start + 3
283                          : zone_start + 1;
284   auto zone_id = util::percent_decode(zone_id_src, std::end(host));
285   auto res = std::string(std::begin(host), zone_start + 1);
286   res += zone_id;
287   return res;
288 }
289 } // namespace
290 
291 namespace {
resolve_dep(int res_type)292 nghttp2_priority_spec resolve_dep(int res_type) {
293   nghttp2_priority_spec pri_spec;
294 
295   if (config.no_dep) {
296     nghttp2_priority_spec_default_init(&pri_spec);
297 
298     return pri_spec;
299   }
300 
301   int32_t anchor_id;
302   int32_t weight;
303   switch (res_type) {
304   case REQ_CSS:
305   case REQ_JS:
306     anchor_id = anchors[ANCHOR_LEADERS].stream_id;
307     weight = 32;
308     break;
309   case REQ_UNBLOCK_JS:
310     anchor_id = anchors[ANCHOR_UNBLOCKED].stream_id;
311     weight = 32;
312     break;
313   case REQ_IMG:
314     anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id;
315     weight = 12;
316     break;
317   default:
318     anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id;
319     weight = 32;
320   }
321 
322   nghttp2_priority_spec_init(&pri_spec, anchor_id, weight, 0);
323   return pri_spec;
324 }
325 } // namespace
326 
is_ipv6_literal_addr() const327 bool Request::is_ipv6_literal_addr() const {
328   if (util::has_uri_field(u, UF_HOST)) {
329     return memchr(uri.c_str() + u.field_data[UF_HOST].off, ':',
330                   u.field_data[UF_HOST].len);
331   } else {
332     return false;
333   }
334 }
335 
get_res_header(int32_t token)336 Headers::value_type *Request::get_res_header(int32_t token) {
337   auto idx = res_hdidx[token];
338   if (idx == -1) {
339     return nullptr;
340   }
341   return &res_nva[idx];
342 }
343 
get_req_header(int32_t token)344 Headers::value_type *Request::get_req_header(int32_t token) {
345   auto idx = req_hdidx[token];
346   if (idx == -1) {
347     return nullptr;
348   }
349   return &req_nva[idx];
350 }
351 
record_request_start_time()352 void Request::record_request_start_time() {
353   timing.state = RequestState::ON_REQUEST;
354   timing.request_start_time = get_time();
355 }
356 
record_response_start_time()357 void Request::record_response_start_time() {
358   timing.state = RequestState::ON_RESPONSE;
359   timing.response_start_time = get_time();
360 }
361 
record_response_end_time()362 void Request::record_response_end_time() {
363   timing.state = RequestState::ON_COMPLETE;
364   timing.response_end_time = get_time();
365 }
366 
367 namespace {
continue_timeout_cb(struct ev_loop * loop,ev_timer * w,int revents)368 void continue_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
369   auto client = static_cast<HttpClient *>(ev_userdata(loop));
370   auto req = static_cast<Request *>(w->data);
371   int error;
372 
373   error = nghttp2_submit_data(client->session, NGHTTP2_FLAG_END_STREAM,
374                               req->stream_id, req->data_prd);
375 
376   if (error) {
377     std::cerr << "[ERROR] nghttp2_submit_data() returned error: "
378               << nghttp2_strerror(error) << std::endl;
379     nghttp2_submit_rst_stream(client->session, NGHTTP2_FLAG_NONE,
380                               req->stream_id, NGHTTP2_INTERNAL_ERROR);
381   }
382 
383   client->signal_write();
384 }
385 } // namespace
386 
ContinueTimer(struct ev_loop * loop,Request * req)387 ContinueTimer::ContinueTimer(struct ev_loop *loop, Request *req) : loop(loop) {
388   ev_timer_init(&timer, continue_timeout_cb, 1., 0.);
389   timer.data = req;
390 }
391 
~ContinueTimer()392 ContinueTimer::~ContinueTimer() { stop(); }
393 
start()394 void ContinueTimer::start() { ev_timer_start(loop, &timer); }
395 
stop()396 void ContinueTimer::stop() { ev_timer_stop(loop, &timer); }
397 
dispatch_continue()398 void ContinueTimer::dispatch_continue() {
399   // Only dispatch the timeout callback if it hasn't already been called.
400   if (ev_is_active(&timer)) {
401     ev_feed_event(loop, &timer, 0);
402   }
403 }
404 
405 namespace {
htp_msg_begincb(llhttp_t * htp)406 int htp_msg_begincb(llhttp_t *htp) {
407   if (config.verbose) {
408     print_timer();
409     std::cout << " HTTP Upgrade response" << std::endl;
410   }
411   return 0;
412 }
413 } // namespace
414 
415 namespace {
htp_msg_completecb(llhttp_t * htp)416 int htp_msg_completecb(llhttp_t *htp) {
417   auto client = static_cast<HttpClient *>(htp->data);
418   client->upgrade_response_status_code = htp->status_code;
419   client->upgrade_response_complete = true;
420   return 0;
421 }
422 } // namespace
423 
424 namespace {
425 constexpr llhttp_settings_t htp_hooks = {
426     htp_msg_begincb,    // llhttp_cb      on_message_begin;
427     nullptr,            // llhttp_data_cb on_url;
428     nullptr,            // llhttp_data_cb on_status;
429     nullptr,            // llhttp_data_cb on_method;
430     nullptr,            // llhttp_data_cb on_version;
431     nullptr,            // llhttp_data_cb on_header_field;
432     nullptr,            // llhttp_data_cb on_header_value;
433     nullptr,            // llhttp_data_cb on_chunk_extension_name;
434     nullptr,            // llhttp_data_cb on_chunk_extension_value;
435     nullptr,            // llhttp_cb      on_headers_complete;
436     nullptr,            // llhttp_data_cb on_body;
437     htp_msg_completecb, // llhttp_cb      on_message_complete;
438     nullptr,            // llhttp_cb      on_url_complete;
439     nullptr,            // llhttp_cb      on_status_complete;
440     nullptr,            // llhttp_cb      on_method_complete;
441     nullptr,            // llhttp_cb      on_version_complete;
442     nullptr,            // llhttp_cb      on_header_field_complete;
443     nullptr,            // llhttp_cb      on_header_value_complete;
444     nullptr,            // llhttp_cb      on_chunk_extension_name_complete;
445     nullptr,            // llhttp_cb      on_chunk_extension_value_complete;
446     nullptr,            // llhttp_cb      on_chunk_header;
447     nullptr,            // llhttp_cb      on_chunk_complete;
448     nullptr,            // llhttp_cb      on_reset;
449 };
450 } // namespace
451 
452 namespace {
submit_request(HttpClient * client,const Headers & headers,Request * req)453 int submit_request(HttpClient *client, const Headers &headers, Request *req) {
454   auto scheme = util::get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA);
455   auto build_headers = Headers{{":method", req->data_prd ? "POST" : "GET"},
456                                {":path", req->make_reqpath()},
457                                {":scheme", scheme.str()},
458                                {":authority", client->hostport},
459                                {"accept", "*/*"},
460                                {"accept-encoding", "gzip, deflate"},
461                                {"user-agent", "nghttp2/" NGHTTP2_VERSION}};
462   bool expect_continue = false;
463 
464   if (config.continuation) {
465     for (size_t i = 0; i < 6; ++i) {
466       build_headers.emplace_back("continuation-test-" + util::utos(i + 1),
467                                  std::string(4_k, '-'));
468     }
469   }
470 
471   auto num_initial_headers = build_headers.size();
472 
473   if (req->data_prd) {
474     if (!config.no_content_length) {
475       build_headers.emplace_back("content-length",
476                                  util::utos(req->data_length));
477     }
478     if (config.expect_continue) {
479       expect_continue = true;
480       build_headers.emplace_back("expect", "100-continue");
481     }
482   }
483 
484   for (auto &kv : headers) {
485     size_t i;
486     for (i = 0; i < num_initial_headers; ++i) {
487       if (kv.name == build_headers[i].name) {
488         build_headers[i].value = kv.value;
489         break;
490       }
491     }
492     if (i < num_initial_headers) {
493       continue;
494     }
495 
496     build_headers.emplace_back(kv.name, kv.value, kv.no_index);
497   }
498 
499   auto nva = std::vector<nghttp2_nv>();
500   nva.reserve(build_headers.size());
501 
502   for (auto &kv : build_headers) {
503     nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
504   }
505 
506   auto method = http2::get_header(build_headers, ":method");
507   assert(method);
508 
509   req->method = method->value;
510 
511   std::string trailer_names;
512   if (!config.trailer.empty()) {
513     trailer_names = config.trailer[0].name;
514     for (size_t i = 1; i < config.trailer.size(); ++i) {
515       trailer_names += ", ";
516       trailer_names += config.trailer[i].name;
517     }
518     nva.push_back(http2::make_nv_ls("trailer", trailer_names));
519   }
520 
521   int32_t stream_id;
522 
523   if (expect_continue) {
524     stream_id = nghttp2_submit_headers(client->session, 0, -1, &req->pri_spec,
525                                        nva.data(), nva.size(), req);
526   } else {
527     stream_id =
528         nghttp2_submit_request(client->session, &req->pri_spec, nva.data(),
529                                nva.size(), req->data_prd, req);
530   }
531 
532   if (stream_id < 0) {
533     std::cerr << "[ERROR] nghttp2_submit_"
534               << (expect_continue ? "headers" : "request")
535               << "() returned error: " << nghttp2_strerror(stream_id)
536               << std::endl;
537     return -1;
538   }
539 
540   req->stream_id = stream_id;
541   client->request_done(req);
542 
543   req->req_nva = std::move(build_headers);
544 
545   if (expect_continue) {
546     auto timer = std::make_unique<ContinueTimer>(client->loop, req);
547     req->continue_timer = std::move(timer);
548   }
549 
550   return 0;
551 }
552 } // namespace
553 
554 namespace {
readcb(struct ev_loop * loop,ev_io * w,int revents)555 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
556   auto client = static_cast<HttpClient *>(w->data);
557   if (client->do_read() != 0) {
558     client->disconnect();
559   }
560 }
561 } // namespace
562 
563 namespace {
writecb(struct ev_loop * loop,ev_io * w,int revents)564 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
565   auto client = static_cast<HttpClient *>(w->data);
566   auto rv = client->do_write();
567   if (rv == HttpClient::ERR_CONNECT_FAIL) {
568     client->connect_fail();
569     return;
570   }
571   if (rv != 0) {
572     client->disconnect();
573   }
574 }
575 } // namespace
576 
577 namespace {
timeoutcb(struct ev_loop * loop,ev_timer * w,int revents)578 void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
579   auto client = static_cast<HttpClient *>(w->data);
580   std::cerr << "[ERROR] Timeout" << std::endl;
581   client->disconnect();
582 }
583 } // namespace
584 
585 namespace {
settings_timeout_cb(struct ev_loop * loop,ev_timer * w,int revents)586 void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
587   auto client = static_cast<HttpClient *>(w->data);
588   ev_timer_stop(loop, w);
589 
590   nghttp2_session_terminate_session(client->session, NGHTTP2_SETTINGS_TIMEOUT);
591 
592   client->signal_write();
593 }
594 } // namespace
595 
HttpClient(const nghttp2_session_callbacks * callbacks,struct ev_loop * loop,SSL_CTX * ssl_ctx)596 HttpClient::HttpClient(const nghttp2_session_callbacks *callbacks,
597                        struct ev_loop *loop, SSL_CTX *ssl_ctx)
598     : wb(&mcpool),
599       session(nullptr),
600       callbacks(callbacks),
601       loop(loop),
602       ssl_ctx(ssl_ctx),
603       ssl(nullptr),
604       addrs(nullptr),
605       next_addr(nullptr),
606       cur_addr(nullptr),
607       complete(0),
608       success(0),
609       settings_payloadlen(0),
610       state(ClientState::IDLE),
611       upgrade_response_status_code(0),
612       fd(-1),
613       upgrade_response_complete(false) {
614   ev_io_init(&wev, writecb, 0, EV_WRITE);
615   ev_io_init(&rev, readcb, 0, EV_READ);
616 
617   wev.data = this;
618   rev.data = this;
619 
620   ev_timer_init(&wt, timeoutcb, 0., config.timeout);
621   ev_timer_init(&rt, timeoutcb, 0., config.timeout);
622 
623   wt.data = this;
624   rt.data = this;
625 
626   ev_timer_init(&settings_timer, settings_timeout_cb, 0., 10.);
627 
628   settings_timer.data = this;
629 }
630 
~HttpClient()631 HttpClient::~HttpClient() {
632   disconnect();
633 
634   if (addrs) {
635     freeaddrinfo(addrs);
636     addrs = nullptr;
637     next_addr = nullptr;
638   }
639 }
640 
need_upgrade() const641 bool HttpClient::need_upgrade() const {
642   return config.upgrade && scheme == "http";
643 }
644 
resolve_host(const std::string & host,uint16_t port)645 int HttpClient::resolve_host(const std::string &host, uint16_t port) {
646   int rv;
647   this->host = host;
648   addrinfo hints{};
649   hints.ai_family = AF_UNSPEC;
650   hints.ai_socktype = SOCK_STREAM;
651   hints.ai_protocol = 0;
652   hints.ai_flags = AI_ADDRCONFIG;
653   rv = getaddrinfo(host.c_str(), util::utos(port).c_str(), &hints, &addrs);
654   if (rv != 0) {
655     std::cerr << "[ERROR] getaddrinfo() failed: " << gai_strerror(rv)
656               << std::endl;
657     return -1;
658   }
659   if (addrs == nullptr) {
660     std::cerr << "[ERROR] No address returned" << std::endl;
661     return -1;
662   }
663   next_addr = addrs;
664   return 0;
665 }
666 
667 namespace {
668 // Just returns 1 to continue handshake.
verify_cb(int preverify_ok,X509_STORE_CTX * ctx)669 int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
670 } // namespace
671 
initiate_connection()672 int HttpClient::initiate_connection() {
673   int rv;
674 
675   cur_addr = nullptr;
676   while (next_addr) {
677     cur_addr = next_addr;
678     next_addr = next_addr->ai_next;
679     fd = util::create_nonblock_socket(cur_addr->ai_family);
680     if (fd == -1) {
681       continue;
682     }
683 
684     if (ssl_ctx) {
685       // We are establishing TLS connection.
686       ssl = SSL_new(ssl_ctx);
687       if (!ssl) {
688         std::cerr << "[ERROR] SSL_new() failed: "
689                   << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
690         return -1;
691       }
692 
693       SSL_set_connect_state(ssl);
694 
695       // If the user overrode the :authority or host header, use that
696       // value for the SNI extension
697       const auto &host_string =
698           config.host_override.empty() ? host : config.host_override;
699 
700 #if LIBRESSL_2_7_API ||                                                        \
701     (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L) ||             \
702     defined(OPENSSL_IS_BORINGSSL)
703       auto param = SSL_get0_param(ssl);
704       X509_VERIFY_PARAM_set_hostflags(param, 0);
705       X509_VERIFY_PARAM_set1_host(param, host_string.c_str(),
706                                   host_string.size());
707 #endif // LIBRESSL_2_7_API || (!LIBRESSL_IN_USE &&
708        // OPENSSL_VERSION_NUMBER >= 0x10002000L) ||
709        // defined(OPENSSL_IS_BORINGSSL)
710       SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb);
711 
712       if (!util::numeric_host(host_string.c_str())) {
713         SSL_set_tlsext_host_name(ssl, host_string.c_str());
714       }
715     }
716 
717     rv = connect(fd, cur_addr->ai_addr, cur_addr->ai_addrlen);
718 
719     if (rv != 0 && errno != EINPROGRESS) {
720       if (ssl) {
721         SSL_free(ssl);
722         ssl = nullptr;
723       }
724       close(fd);
725       fd = -1;
726       continue;
727     }
728     break;
729   }
730 
731   if (fd == -1) {
732     return -1;
733   }
734 
735   writefn = &HttpClient::connected;
736 
737   if (need_upgrade()) {
738     on_readfn = &HttpClient::on_upgrade_read;
739     on_writefn = &HttpClient::on_upgrade_connect;
740   } else {
741     on_readfn = &HttpClient::on_read;
742     on_writefn = &HttpClient::on_write;
743   }
744 
745   ev_io_set(&rev, fd, EV_READ);
746   ev_io_set(&wev, fd, EV_WRITE);
747 
748   ev_io_start(loop, &wev);
749 
750   ev_timer_again(loop, &wt);
751 
752   return 0;
753 }
754 
disconnect()755 void HttpClient::disconnect() {
756   state = ClientState::IDLE;
757 
758   for (auto req = std::begin(reqvec); req != std::end(reqvec); ++req) {
759     if ((*req)->continue_timer) {
760       (*req)->continue_timer->stop();
761     }
762   }
763 
764   ev_timer_stop(loop, &settings_timer);
765 
766   ev_timer_stop(loop, &rt);
767   ev_timer_stop(loop, &wt);
768 
769   ev_io_stop(loop, &rev);
770   ev_io_stop(loop, &wev);
771 
772   nghttp2_session_del(session);
773   session = nullptr;
774 
775   if (ssl) {
776     SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
777     ERR_clear_error();
778     SSL_shutdown(ssl);
779     SSL_free(ssl);
780     ssl = nullptr;
781   }
782 
783   if (fd != -1) {
784     shutdown(fd, SHUT_WR);
785     close(fd);
786     fd = -1;
787   }
788 }
789 
read_clear()790 int HttpClient::read_clear() {
791   ev_timer_again(loop, &rt);
792 
793   std::array<uint8_t, 8_k> buf;
794 
795   for (;;) {
796     ssize_t nread;
797     while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR)
798       ;
799     if (nread == -1) {
800       if (errno == EAGAIN || errno == EWOULDBLOCK) {
801         return 0;
802       }
803       return -1;
804     }
805 
806     if (nread == 0) {
807       return -1;
808     }
809 
810     if (on_readfn(*this, buf.data(), nread) != 0) {
811       return -1;
812     }
813   }
814 
815   return 0;
816 }
817 
write_clear()818 int HttpClient::write_clear() {
819   ev_timer_again(loop, &rt);
820 
821   std::array<struct iovec, 2> iov;
822 
823   for (;;) {
824     if (on_writefn(*this) != 0) {
825       return -1;
826     }
827 
828     auto iovcnt = wb.riovec(iov.data(), iov.size());
829 
830     if (iovcnt == 0) {
831       break;
832     }
833 
834     ssize_t nwrite;
835     while ((nwrite = writev(fd, iov.data(), iovcnt)) == -1 && errno == EINTR)
836       ;
837     if (nwrite == -1) {
838       if (errno == EAGAIN || errno == EWOULDBLOCK) {
839         ev_io_start(loop, &wev);
840         ev_timer_again(loop, &wt);
841         return 0;
842       }
843       return -1;
844     }
845 
846     wb.drain(nwrite);
847   }
848 
849   ev_io_stop(loop, &wev);
850   ev_timer_stop(loop, &wt);
851 
852   return 0;
853 }
854 
noop()855 int HttpClient::noop() { return 0; }
856 
connect_fail()857 void HttpClient::connect_fail() {
858   if (state == ClientState::IDLE) {
859     std::cerr << "[ERROR] Could not connect to the address "
860               << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen)
861               << std::endl;
862   }
863   auto cur_state = state;
864   disconnect();
865   if (cur_state == ClientState::IDLE) {
866     if (initiate_connection() == 0) {
867       std::cerr << "Trying next address "
868                 << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen)
869                 << std::endl;
870     }
871   }
872 }
873 
connected()874 int HttpClient::connected() {
875   if (!util::check_socket_connected(fd)) {
876     return ERR_CONNECT_FAIL;
877   }
878 
879   if (config.verbose) {
880     print_timer();
881     std::cout << " Connected" << std::endl;
882   }
883 
884   state = ClientState::CONNECTED;
885 
886   ev_io_start(loop, &rev);
887   ev_io_stop(loop, &wev);
888 
889   ev_timer_again(loop, &rt);
890   ev_timer_stop(loop, &wt);
891 
892   if (ssl) {
893     SSL_set_fd(ssl, fd);
894 
895     readfn = &HttpClient::tls_handshake;
896     writefn = &HttpClient::tls_handshake;
897 
898     return do_write();
899   }
900 
901   readfn = &HttpClient::read_clear;
902   writefn = &HttpClient::write_clear;
903 
904   if (need_upgrade()) {
905     htp = std::make_unique<llhttp_t>();
906     llhttp_init(htp.get(), HTTP_RESPONSE, &htp_hooks);
907     htp->data = this;
908 
909     return do_write();
910   }
911 
912   if (connection_made() != 0) {
913     return -1;
914   }
915 
916   return 0;
917 }
918 
919 namespace {
populate_settings(nghttp2_settings_entry * iv)920 size_t populate_settings(nghttp2_settings_entry *iv) {
921   size_t niv = 2;
922 
923   iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
924   iv[0].value = config.max_concurrent_streams;
925 
926   iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
927   if (config.window_bits != -1) {
928     iv[1].value = (1 << config.window_bits) - 1;
929   } else {
930     iv[1].value = NGHTTP2_INITIAL_WINDOW_SIZE;
931   }
932 
933   if (config.header_table_size >= 0) {
934     if (config.min_header_table_size < config.header_table_size) {
935       iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
936       iv[niv].value = config.min_header_table_size;
937       ++niv;
938     }
939 
940     iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
941     iv[niv].value = config.header_table_size;
942     ++niv;
943   }
944 
945   if (config.no_push) {
946     iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
947     iv[niv].value = 0;
948     ++niv;
949   }
950 
951   if (config.no_rfc7540_pri) {
952     iv[niv].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
953     iv[niv].value = 1;
954     ++niv;
955   }
956 
957   return niv;
958 }
959 } // namespace
960 
on_upgrade_connect()961 int HttpClient::on_upgrade_connect() {
962   ssize_t rv;
963   record_connect_end_time();
964   assert(!reqvec.empty());
965   std::array<nghttp2_settings_entry, 16> iv;
966   size_t niv = populate_settings(iv.data());
967   assert(settings_payload.size() >= 8 * niv);
968   rv = nghttp2_pack_settings_payload(settings_payload.data(),
969                                      settings_payload.size(), iv.data(), niv);
970   if (rv < 0) {
971     return -1;
972   }
973   settings_payloadlen = rv;
974   auto token68 =
975       base64::encode(std::begin(settings_payload),
976                      std::begin(settings_payload) + settings_payloadlen);
977   util::to_token68(token68);
978 
979   std::string req;
980   if (reqvec[0]->data_prd) {
981     // If the request contains upload data, use OPTIONS * to upgrade
982     req = "OPTIONS *";
983   } else {
984     auto meth = std::find_if(
985         std::begin(config.headers), std::end(config.headers),
986         [](const Header &kv) { return util::streq_l(":method", kv.name); });
987 
988     if (meth == std::end(config.headers)) {
989       req = "GET ";
990       reqvec[0]->method = "GET";
991     } else {
992       req = (*meth).value;
993       req += ' ';
994       reqvec[0]->method = (*meth).value;
995     }
996     req += reqvec[0]->make_reqpath();
997   }
998 
999   auto headers = Headers{{"host", hostport},
1000                          {"connection", "Upgrade, HTTP2-Settings"},
1001                          {"upgrade", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID},
1002                          {"http2-settings", token68},
1003                          {"accept", "*/*"},
1004                          {"user-agent", "nghttp2/" NGHTTP2_VERSION}};
1005   auto initial_headerslen = headers.size();
1006 
1007   for (auto &kv : config.headers) {
1008     size_t i;
1009     if (kv.name.empty() || kv.name[0] == ':') {
1010       continue;
1011     }
1012     for (i = 0; i < initial_headerslen; ++i) {
1013       if (kv.name == headers[i].name) {
1014         headers[i].value = kv.value;
1015         break;
1016       }
1017     }
1018     if (i < initial_headerslen) {
1019       continue;
1020     }
1021     headers.emplace_back(kv.name, kv.value, kv.no_index);
1022   }
1023 
1024   req += " HTTP/1.1\r\n";
1025 
1026   for (auto &kv : headers) {
1027     req += kv.name;
1028     req += ": ";
1029     req += kv.value;
1030     req += "\r\n";
1031   }
1032   req += "\r\n";
1033 
1034   wb.append(req);
1035 
1036   if (config.verbose) {
1037     print_timer();
1038     std::cout << " HTTP Upgrade request\n" << req << std::endl;
1039   }
1040 
1041   if (!reqvec[0]->data_prd) {
1042     // record request time if this is a part of real request.
1043     reqvec[0]->record_request_start_time();
1044     reqvec[0]->req_nva = std::move(headers);
1045   }
1046 
1047   on_writefn = &HttpClient::noop;
1048 
1049   signal_write();
1050 
1051   return 0;
1052 }
1053 
on_upgrade_read(const uint8_t * data,size_t len)1054 int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) {
1055   int rv;
1056 
1057   auto htperr =
1058       llhttp_execute(htp.get(), reinterpret_cast<const char *>(data), len);
1059   auto nread = htperr == HPE_OK
1060                    ? len
1061                    : static_cast<size_t>(reinterpret_cast<const uint8_t *>(
1062                                              llhttp_get_error_pos(htp.get())) -
1063                                          data);
1064 
1065   if (config.verbose) {
1066     std::cout.write(reinterpret_cast<const char *>(data), nread);
1067   }
1068 
1069   if (htperr != HPE_OK && htperr != HPE_PAUSED_UPGRADE) {
1070     std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: "
1071               << "(" << llhttp_errno_name(htperr) << ") "
1072               << llhttp_get_error_reason(htp.get()) << std::endl;
1073     return -1;
1074   }
1075 
1076   if (!upgrade_response_complete) {
1077     return 0;
1078   }
1079 
1080   if (config.verbose) {
1081     std::cout << std::endl;
1082   }
1083 
1084   if (upgrade_response_status_code != 101) {
1085     std::cerr << "[ERROR] HTTP Upgrade failed" << std::endl;
1086 
1087     return -1;
1088   }
1089 
1090   if (config.verbose) {
1091     print_timer();
1092     std::cout << " HTTP Upgrade success" << std::endl;
1093   }
1094 
1095   on_readfn = &HttpClient::on_read;
1096   on_writefn = &HttpClient::on_write;
1097 
1098   rv = connection_made();
1099   if (rv != 0) {
1100     return rv;
1101   }
1102 
1103   // Read remaining data in the buffer because it is not notified
1104   // callback anymore.
1105   rv = on_readfn(*this, data + nread, len - nread);
1106   if (rv != 0) {
1107     return rv;
1108   }
1109 
1110   return 0;
1111 }
1112 
do_read()1113 int HttpClient::do_read() { return readfn(*this); }
do_write()1114 int HttpClient::do_write() { return writefn(*this); }
1115 
connection_made()1116 int HttpClient::connection_made() {
1117   int rv;
1118 
1119   if (!need_upgrade()) {
1120     record_connect_end_time();
1121   }
1122 
1123   if (ssl) {
1124     // Check NPN or ALPN result
1125     const unsigned char *next_proto = nullptr;
1126     unsigned int next_proto_len;
1127 #ifndef OPENSSL_NO_NEXTPROTONEG
1128     SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
1129 #endif // !OPENSSL_NO_NEXTPROTONEG
1130     for (int i = 0; i < 2; ++i) {
1131       if (next_proto) {
1132         auto proto = StringRef{next_proto, next_proto_len};
1133         if (config.verbose) {
1134           std::cout << "The negotiated protocol: " << proto << std::endl;
1135         }
1136         if (!util::check_h2_is_selected(proto)) {
1137           next_proto = nullptr;
1138         }
1139         break;
1140       }
1141 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1142       SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
1143 #else  // OPENSSL_VERSION_NUMBER < 0x10002000L
1144       break;
1145 #endif // OPENSSL_VERSION_NUMBER < 0x10002000L
1146     }
1147     if (!next_proto) {
1148       print_protocol_nego_error();
1149       return -1;
1150     }
1151   }
1152 
1153   rv = nghttp2_session_client_new2(&session, callbacks, this,
1154                                    config.http2_option);
1155 
1156   if (rv != 0) {
1157     return -1;
1158   }
1159   if (need_upgrade()) {
1160     // Adjust stream user-data depending on the existence of upload
1161     // data
1162     Request *stream_user_data = nullptr;
1163     if (!reqvec[0]->data_prd) {
1164       stream_user_data = reqvec[0].get();
1165     }
1166     // If HEAD is used, that is only when user specified it with -H
1167     // option.
1168     auto head_request = stream_user_data && stream_user_data->method == "HEAD";
1169     rv = nghttp2_session_upgrade2(session, settings_payload.data(),
1170                                   settings_payloadlen, head_request,
1171                                   stream_user_data);
1172     if (rv != 0) {
1173       std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: "
1174                 << nghttp2_strerror(rv) << std::endl;
1175       return -1;
1176     }
1177     if (stream_user_data) {
1178       stream_user_data->stream_id = 1;
1179       request_done(stream_user_data);
1180     }
1181   }
1182   // If upgrade succeeds, the SETTINGS value sent with
1183   // HTTP2-Settings header field has already been submitted to
1184   // session object.
1185   if (!need_upgrade()) {
1186     std::array<nghttp2_settings_entry, 16> iv;
1187     auto niv = populate_settings(iv.data());
1188     rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv.data(), niv);
1189     if (rv != 0) {
1190       return -1;
1191     }
1192   }
1193   if (!config.no_dep) {
1194     // Create anchor stream nodes
1195     nghttp2_priority_spec pri_spec;
1196 
1197     for (auto &anchor : anchors) {
1198       nghttp2_priority_spec_init(&pri_spec, anchor.dep_stream_id, anchor.weight,
1199                                  0);
1200       rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, anchor.stream_id,
1201                                    &pri_spec);
1202       if (rv != 0) {
1203         return -1;
1204       }
1205     }
1206 
1207     rv = nghttp2_session_set_next_stream_id(
1208         session, anchors[ANCHOR_FOLLOWERS].stream_id + 2);
1209     if (rv != 0) {
1210       return -1;
1211     }
1212 
1213     if (need_upgrade() && !reqvec[0]->data_prd) {
1214       // Amend the priority because we cannot send priority in
1215       // HTTP/1.1 Upgrade.
1216       auto &anchor = anchors[ANCHOR_FOLLOWERS];
1217       nghttp2_priority_spec_init(&pri_spec, anchor.stream_id,
1218                                  reqvec[0]->pri_spec.weight, 0);
1219 
1220       rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec);
1221       if (rv != 0) {
1222         return -1;
1223       }
1224     }
1225   } else if (need_upgrade() && !reqvec[0]->data_prd &&
1226              reqvec[0]->pri_spec.weight != NGHTTP2_DEFAULT_WEIGHT) {
1227     // Amend the priority because we cannot send priority in HTTP/1.1
1228     // Upgrade.
1229     nghttp2_priority_spec pri_spec;
1230 
1231     nghttp2_priority_spec_init(&pri_spec, 0, reqvec[0]->pri_spec.weight, 0);
1232 
1233     rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec);
1234     if (rv != 0) {
1235       return -1;
1236     }
1237   }
1238 
1239   ev_timer_again(loop, &settings_timer);
1240 
1241   if (config.connection_window_bits != -1) {
1242     int32_t window_size = (1 << config.connection_window_bits) - 1;
1243     rv = nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0,
1244                                                window_size);
1245     if (rv != 0) {
1246       return -1;
1247     }
1248   }
1249   // Adjust first request depending on the existence of the upload
1250   // data
1251   for (auto i = std::begin(reqvec) + (need_upgrade() && !reqvec[0]->data_prd);
1252        i != std::end(reqvec); ++i) {
1253     if (submit_request(this, config.headers, (*i).get()) != 0) {
1254       return -1;
1255     }
1256   }
1257 
1258   signal_write();
1259 
1260   return 0;
1261 }
1262 
on_read(const uint8_t * data,size_t len)1263 int HttpClient::on_read(const uint8_t *data, size_t len) {
1264   if (config.hexdump) {
1265     util::hexdump(stdout, data, len);
1266   }
1267 
1268   auto rv = nghttp2_session_mem_recv(session, data, len);
1269   if (rv < 0) {
1270     std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: "
1271               << nghttp2_strerror(rv) << std::endl;
1272     return -1;
1273   }
1274 
1275   assert(static_cast<size_t>(rv) == len);
1276 
1277   if (nghttp2_session_want_read(session) == 0 &&
1278       nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) {
1279     return -1;
1280   }
1281 
1282   signal_write();
1283 
1284   return 0;
1285 }
1286 
on_write()1287 int HttpClient::on_write() {
1288   for (;;) {
1289     if (wb.rleft() >= 16384) {
1290       return 0;
1291     }
1292 
1293     const uint8_t *data;
1294     auto len = nghttp2_session_mem_send(session, &data);
1295     if (len < 0) {
1296       std::cerr << "[ERROR] nghttp2_session_send() returned error: "
1297                 << nghttp2_strerror(len) << std::endl;
1298       return -1;
1299     }
1300 
1301     if (len == 0) {
1302       break;
1303     }
1304 
1305     wb.append(data, len);
1306   }
1307 
1308   if (nghttp2_session_want_read(session) == 0 &&
1309       nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) {
1310     return -1;
1311   }
1312 
1313   return 0;
1314 }
1315 
tls_handshake()1316 int HttpClient::tls_handshake() {
1317   ev_timer_again(loop, &rt);
1318 
1319   ERR_clear_error();
1320 
1321   auto rv = SSL_do_handshake(ssl);
1322 
1323   if (rv <= 0) {
1324     auto err = SSL_get_error(ssl, rv);
1325     switch (err) {
1326     case SSL_ERROR_WANT_READ:
1327       ev_io_stop(loop, &wev);
1328       ev_timer_stop(loop, &wt);
1329       return 0;
1330     case SSL_ERROR_WANT_WRITE:
1331       ev_io_start(loop, &wev);
1332       ev_timer_again(loop, &wt);
1333       return 0;
1334     default:
1335       return -1;
1336     }
1337   }
1338 
1339   ev_io_stop(loop, &wev);
1340   ev_timer_stop(loop, &wt);
1341 
1342   readfn = &HttpClient::read_tls;
1343   writefn = &HttpClient::write_tls;
1344 
1345   if (config.verify_peer) {
1346     auto verify_res = SSL_get_verify_result(ssl);
1347     if (verify_res != X509_V_OK) {
1348       std::cerr << "[WARNING] Certificate verification failed: "
1349                 << X509_verify_cert_error_string(verify_res) << std::endl;
1350     }
1351   }
1352 
1353   if (connection_made() != 0) {
1354     return -1;
1355   }
1356 
1357   return 0;
1358 }
1359 
read_tls()1360 int HttpClient::read_tls() {
1361   ev_timer_again(loop, &rt);
1362 
1363   ERR_clear_error();
1364 
1365   std::array<uint8_t, 8_k> buf;
1366   for (;;) {
1367     auto rv = SSL_read(ssl, buf.data(), buf.size());
1368 
1369     if (rv <= 0) {
1370       auto err = SSL_get_error(ssl, rv);
1371       switch (err) {
1372       case SSL_ERROR_WANT_READ:
1373         return 0;
1374       case SSL_ERROR_WANT_WRITE:
1375         // renegotiation started
1376         return -1;
1377       default:
1378         return -1;
1379       }
1380     }
1381 
1382     if (on_readfn(*this, buf.data(), rv) != 0) {
1383       return -1;
1384     }
1385   }
1386 }
1387 
write_tls()1388 int HttpClient::write_tls() {
1389   ev_timer_again(loop, &rt);
1390 
1391   ERR_clear_error();
1392 
1393   struct iovec iov;
1394 
1395   for (;;) {
1396     if (on_writefn(*this) != 0) {
1397       return -1;
1398     }
1399 
1400     auto iovcnt = wb.riovec(&iov, 1);
1401 
1402     if (iovcnt == 0) {
1403       break;
1404     }
1405 
1406     auto rv = SSL_write(ssl, iov.iov_base, iov.iov_len);
1407 
1408     if (rv <= 0) {
1409       auto err = SSL_get_error(ssl, rv);
1410       switch (err) {
1411       case SSL_ERROR_WANT_READ:
1412         // renegotiation started
1413         return -1;
1414       case SSL_ERROR_WANT_WRITE:
1415         ev_io_start(loop, &wev);
1416         ev_timer_again(loop, &wt);
1417         return 0;
1418       default:
1419         return -1;
1420       }
1421     }
1422 
1423     wb.drain(rv);
1424   }
1425 
1426   ev_io_stop(loop, &wev);
1427   ev_timer_stop(loop, &wt);
1428 
1429   return 0;
1430 }
1431 
signal_write()1432 void HttpClient::signal_write() { ev_io_start(loop, &wev); }
1433 
all_requests_processed() const1434 bool HttpClient::all_requests_processed() const {
1435   return complete == reqvec.size();
1436 }
1437 
update_hostport()1438 void HttpClient::update_hostport() {
1439   if (reqvec.empty()) {
1440     return;
1441   }
1442   scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_SCHEMA)
1443                .str();
1444   std::stringstream ss;
1445   if (reqvec[0]->is_ipv6_literal_addr()) {
1446     // we may have zone ID, which must start with "%25", or "%".  RFC
1447     // 6874 defines "%25" only, and just "%" is allowed for just
1448     // convenience to end-user input.
1449     auto host =
1450         util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);
1451     auto end = std::find(std::begin(host), std::end(host), '%');
1452     ss << "[";
1453     ss.write(host.c_str(), end - std::begin(host));
1454     ss << "]";
1455   } else {
1456     util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);
1457   }
1458   if (util::has_uri_field(reqvec[0]->u, UF_PORT) &&
1459       reqvec[0]->u.port !=
1460           util::get_default_port(reqvec[0]->uri.c_str(), reqvec[0]->u)) {
1461     ss << ":" << reqvec[0]->u.port;
1462   }
1463   hostport = ss.str();
1464 }
1465 
add_request(const std::string & uri,const nghttp2_data_provider * data_prd,int64_t data_length,const nghttp2_priority_spec & pri_spec,int level)1466 bool HttpClient::add_request(const std::string &uri,
1467                              const nghttp2_data_provider *data_prd,
1468                              int64_t data_length,
1469                              const nghttp2_priority_spec &pri_spec, int level) {
1470   http_parser_url u{};
1471   if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
1472     return false;
1473   }
1474   if (path_cache.count(uri)) {
1475     return false;
1476   }
1477 
1478   if (config.multiply == 1) {
1479     path_cache.insert(uri);
1480   }
1481 
1482   reqvec.push_back(std::make_unique<Request>(uri, u, data_prd, data_length,
1483                                              pri_spec, level));
1484   return true;
1485 }
1486 
record_start_time()1487 void HttpClient::record_start_time() {
1488   timing.system_start_time = std::chrono::system_clock::now();
1489   timing.start_time = get_time();
1490 }
1491 
record_domain_lookup_end_time()1492 void HttpClient::record_domain_lookup_end_time() {
1493   timing.domain_lookup_end_time = get_time();
1494 }
1495 
record_connect_end_time()1496 void HttpClient::record_connect_end_time() {
1497   timing.connect_end_time = get_time();
1498 }
1499 
request_done(Request * req)1500 void HttpClient::request_done(Request *req) {
1501   if (req->stream_id % 2 == 0) {
1502     return;
1503   }
1504 }
1505 
1506 #ifdef HAVE_JANSSON
output_har(FILE * outfile)1507 void HttpClient::output_har(FILE *outfile) {
1508   static auto PAGE_ID = "page_0";
1509 
1510   auto root = json_object();
1511   auto log = json_object();
1512   json_object_set_new(root, "log", log);
1513   json_object_set_new(log, "version", json_string("1.2"));
1514 
1515   auto creator = json_object();
1516   json_object_set_new(log, "creator", creator);
1517 
1518   json_object_set_new(creator, "name", json_string("nghttp"));
1519   json_object_set_new(creator, "version", json_string(NGHTTP2_VERSION));
1520 
1521   auto pages = json_array();
1522   json_object_set_new(log, "pages", pages);
1523 
1524   auto page = json_object();
1525   json_array_append_new(pages, page);
1526 
1527   json_object_set_new(
1528       page, "startedDateTime",
1529       json_string(util::format_iso8601(timing.system_start_time).c_str()));
1530   json_object_set_new(page, "id", json_string(PAGE_ID));
1531   json_object_set_new(page, "title", json_string(""));
1532 
1533   json_object_set_new(page, "pageTimings", json_object());
1534 
1535   auto entries = json_array();
1536   json_object_set_new(log, "entries", entries);
1537 
1538   auto dns_delta = std::chrono::duration_cast<std::chrono::microseconds>(
1539                        timing.domain_lookup_end_time - timing.start_time)
1540                        .count() /
1541                    1000.0;
1542   auto connect_delta =
1543       std::chrono::duration_cast<std::chrono::microseconds>(
1544           timing.connect_end_time - timing.domain_lookup_end_time)
1545           .count() /
1546       1000.0;
1547 
1548   for (size_t i = 0; i < reqvec.size(); ++i) {
1549     auto &req = reqvec[i];
1550 
1551     if (req->timing.state != RequestState::ON_COMPLETE) {
1552       continue;
1553     }
1554 
1555     auto entry = json_object();
1556     json_array_append_new(entries, entry);
1557 
1558     auto &req_timing = req->timing;
1559     auto request_time =
1560         (i == 0) ? timing.system_start_time
1561                  : timing.system_start_time +
1562                        std::chrono::duration_cast<
1563                            std::chrono::system_clock::duration>(
1564                            req_timing.request_start_time - timing.start_time);
1565 
1566     auto wait_delta =
1567         std::chrono::duration_cast<std::chrono::microseconds>(
1568             req_timing.response_start_time - req_timing.request_start_time)
1569             .count() /
1570         1000.0;
1571     auto receive_delta =
1572         std::chrono::duration_cast<std::chrono::microseconds>(
1573             req_timing.response_end_time - req_timing.response_start_time)
1574             .count() /
1575         1000.0;
1576 
1577     auto time_sum =
1578         std::chrono::duration_cast<std::chrono::microseconds>(
1579             (i == 0) ? (req_timing.response_end_time - timing.start_time)
1580                      : (req_timing.response_end_time -
1581                         req_timing.request_start_time))
1582             .count() /
1583         1000.0;
1584 
1585     json_object_set_new(
1586         entry, "startedDateTime",
1587         json_string(util::format_iso8601(request_time).c_str()));
1588     json_object_set_new(entry, "time", json_real(time_sum));
1589 
1590     auto pushed = req->stream_id % 2 == 0;
1591 
1592     json_object_set_new(entry, "comment",
1593                         json_string(pushed ? "Pushed Object" : ""));
1594 
1595     auto request = json_object();
1596     json_object_set_new(entry, "request", request);
1597 
1598     auto req_headers = json_array();
1599     json_object_set_new(request, "headers", req_headers);
1600 
1601     for (auto &nv : req->req_nva) {
1602       auto hd = json_object();
1603       json_array_append_new(req_headers, hd);
1604 
1605       json_object_set_new(hd, "name", json_string(nv.name.c_str()));
1606       json_object_set_new(hd, "value", json_string(nv.value.c_str()));
1607     }
1608 
1609     json_object_set_new(request, "method", json_string(req->method.c_str()));
1610     json_object_set_new(request, "url", json_string(req->uri.c_str()));
1611     json_object_set_new(request, "httpVersion", json_string("HTTP/2.0"));
1612     json_object_set_new(request, "cookies", json_array());
1613     json_object_set_new(request, "queryString", json_array());
1614     json_object_set_new(request, "headersSize", json_integer(-1));
1615     json_object_set_new(request, "bodySize", json_integer(-1));
1616 
1617     auto response = json_object();
1618     json_object_set_new(entry, "response", response);
1619 
1620     auto res_headers = json_array();
1621     json_object_set_new(response, "headers", res_headers);
1622 
1623     for (auto &nv : req->res_nva) {
1624       auto hd = json_object();
1625       json_array_append_new(res_headers, hd);
1626 
1627       json_object_set_new(hd, "name", json_string(nv.name.c_str()));
1628       json_object_set_new(hd, "value", json_string(nv.value.c_str()));
1629     }
1630 
1631     json_object_set_new(response, "status", json_integer(req->status));
1632     json_object_set_new(response, "statusText", json_string(""));
1633     json_object_set_new(response, "httpVersion", json_string("HTTP/2.0"));
1634     json_object_set_new(response, "cookies", json_array());
1635 
1636     auto content = json_object();
1637     json_object_set_new(response, "content", content);
1638 
1639     json_object_set_new(content, "size", json_integer(req->response_len));
1640 
1641     auto content_type_ptr = http2::get_header(req->res_nva, "content-type");
1642 
1643     const char *content_type = "";
1644     if (content_type_ptr) {
1645       content_type = content_type_ptr->value.c_str();
1646     }
1647 
1648     json_object_set_new(content, "mimeType", json_string(content_type));
1649 
1650     json_object_set_new(response, "redirectURL", json_string(""));
1651     json_object_set_new(response, "headersSize", json_integer(-1));
1652     json_object_set_new(response, "bodySize", json_integer(-1));
1653     json_object_set_new(entry, "cache", json_object());
1654 
1655     auto timings = json_object();
1656     json_object_set_new(entry, "timings", timings);
1657 
1658     auto dns_timing = (i == 0) ? dns_delta : 0;
1659     auto connect_timing = (i == 0) ? connect_delta : 0;
1660 
1661     json_object_set_new(timings, "dns", json_real(dns_timing));
1662     json_object_set_new(timings, "connect", json_real(connect_timing));
1663 
1664     json_object_set_new(timings, "blocked", json_real(0.0));
1665     json_object_set_new(timings, "send", json_real(0.0));
1666     json_object_set_new(timings, "wait", json_real(wait_delta));
1667     json_object_set_new(timings, "receive", json_real(receive_delta));
1668 
1669     json_object_set_new(entry, "pageref", json_string(PAGE_ID));
1670     json_object_set_new(entry, "connection",
1671                         json_string(util::utos(req->stream_id).c_str()));
1672   }
1673 
1674   json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2));
1675   json_decref(root);
1676 }
1677 #endif // HAVE_JANSSON
1678 
1679 namespace {
update_html_parser(HttpClient * client,Request * req,const uint8_t * data,size_t len,int fin)1680 void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
1681                         size_t len, int fin) {
1682   if (!req->html_parser) {
1683     return;
1684   }
1685   req->update_html_parser(data, len, fin);
1686 
1687   auto scheme = req->get_real_scheme();
1688   auto host = req->get_real_host();
1689   auto port = req->get_real_port();
1690 
1691   for (auto &p : req->html_parser->get_links()) {
1692     auto uri = strip_fragment(p.first.c_str());
1693     auto res_type = p.second;
1694 
1695     http_parser_url u{};
1696     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
1697       continue;
1698     }
1699 
1700     if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, scheme) ||
1701         !util::fieldeq(uri.c_str(), u, UF_HOST, host)) {
1702       continue;
1703     }
1704 
1705     auto link_port = util::has_uri_field(u, UF_PORT) ? u.port
1706                      : scheme == "https"             ? 443
1707                                                      : 80;
1708 
1709     if (port != link_port) {
1710       continue;
1711     }
1712 
1713     // No POST data for assets
1714     auto pri_spec = resolve_dep(res_type);
1715 
1716     if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) {
1717       submit_request(client, config.headers, client->reqvec.back().get());
1718     }
1719   }
1720   req->html_parser->clear_links();
1721 }
1722 } // namespace
1723 
1724 namespace {
get_client(void * user_data)1725 HttpClient *get_client(void *user_data) {
1726   return static_cast<HttpClient *>(user_data);
1727 }
1728 } // namespace
1729 
1730 namespace {
on_data_chunk_recv_callback(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)1731 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
1732                                 int32_t stream_id, const uint8_t *data,
1733                                 size_t len, void *user_data) {
1734   auto client = get_client(user_data);
1735   auto req = static_cast<Request *>(
1736       nghttp2_session_get_stream_user_data(session, stream_id));
1737 
1738   if (!req) {
1739     return 0;
1740   }
1741 
1742   if (config.verbose >= 2) {
1743     verbose_on_data_chunk_recv_callback(session, flags, stream_id, data, len,
1744                                         user_data);
1745   }
1746 
1747   req->response_len += len;
1748 
1749   if (req->inflater) {
1750     while (len > 0) {
1751       const size_t MAX_OUTLEN = 4_k;
1752       std::array<uint8_t, MAX_OUTLEN> out;
1753       size_t outlen = MAX_OUTLEN;
1754       size_t tlen = len;
1755       int rv =
1756           nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen);
1757       if (rv != 0) {
1758         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
1759                                   NGHTTP2_INTERNAL_ERROR);
1760         break;
1761       }
1762 
1763       if (!config.null_out) {
1764         std::cout.write(reinterpret_cast<const char *>(out.data()), outlen);
1765       }
1766 
1767       update_html_parser(client, req, out.data(), outlen, 0);
1768       data += tlen;
1769       len -= tlen;
1770     }
1771 
1772     return 0;
1773   }
1774 
1775   if (!config.null_out) {
1776     std::cout.write(reinterpret_cast<const char *>(data), len);
1777   }
1778 
1779   update_html_parser(client, req, data, len, 0);
1780 
1781   return 0;
1782 }
1783 } // namespace
1784 
1785 namespace {
select_padding_callback(nghttp2_session * session,const nghttp2_frame * frame,size_t max_payload,void * user_data)1786 ssize_t select_padding_callback(nghttp2_session *session,
1787                                 const nghttp2_frame *frame, size_t max_payload,
1788                                 void *user_data) {
1789   return std::min(max_payload, frame->hd.length + config.padding);
1790 }
1791 } // namespace
1792 
1793 namespace {
check_response_header(nghttp2_session * session,Request * req)1794 void check_response_header(nghttp2_session *session, Request *req) {
1795   bool gzip = false;
1796 
1797   req->expect_final_response = false;
1798 
1799   auto status_hd = req->get_res_header(http2::HD__STATUS);
1800 
1801   if (!status_hd) {
1802     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
1803                               NGHTTP2_PROTOCOL_ERROR);
1804     return;
1805   }
1806 
1807   auto status = http2::parse_http_status_code(StringRef{status_hd->value});
1808   if (status == -1) {
1809     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
1810                               NGHTTP2_PROTOCOL_ERROR);
1811     return;
1812   }
1813 
1814   req->status = status;
1815 
1816   for (auto &nv : req->res_nva) {
1817     if ("content-encoding" == nv.name) {
1818       gzip = util::strieq_l("gzip", nv.value) ||
1819              util::strieq_l("deflate", nv.value);
1820       continue;
1821     }
1822   }
1823 
1824   if (req->status / 100 == 1) {
1825     if (req->continue_timer && (req->status == 100)) {
1826       // If the request is waiting for a 100 Continue, complete the handshake.
1827       req->continue_timer->dispatch_continue();
1828     }
1829 
1830     req->expect_final_response = true;
1831     req->status = 0;
1832     req->res_nva.clear();
1833     http2::init_hdidx(req->res_hdidx);
1834     return;
1835   } else if (req->continue_timer) {
1836     // A final response stops any pending Expect/Continue handshake.
1837     req->continue_timer->stop();
1838   }
1839 
1840   if (gzip) {
1841     if (!req->inflater) {
1842       req->init_inflater();
1843     }
1844   }
1845   if (config.get_assets && req->level == 0) {
1846     if (!req->html_parser) {
1847       req->init_html_parser();
1848     }
1849   }
1850 }
1851 } // namespace
1852 
1853 namespace {
on_begin_headers_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)1854 int on_begin_headers_callback(nghttp2_session *session,
1855                               const nghttp2_frame *frame, void *user_data) {
1856   auto client = get_client(user_data);
1857   switch (frame->hd.type) {
1858   case NGHTTP2_HEADERS: {
1859     auto req = static_cast<Request *>(
1860         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1861     if (!req) {
1862       break;
1863     }
1864 
1865     switch (frame->headers.cat) {
1866     case NGHTTP2_HCAT_RESPONSE:
1867     case NGHTTP2_HCAT_PUSH_RESPONSE:
1868       req->record_response_start_time();
1869       break;
1870     default:
1871       break;
1872     }
1873 
1874     break;
1875   }
1876   case NGHTTP2_PUSH_PROMISE: {
1877     auto stream_id = frame->push_promise.promised_stream_id;
1878     http_parser_url u{};
1879     // TODO Set pri and level
1880     nghttp2_priority_spec pri_spec;
1881 
1882     nghttp2_priority_spec_default_init(&pri_spec);
1883 
1884     auto req = std::make_unique<Request>("", u, nullptr, 0, pri_spec);
1885     req->stream_id = stream_id;
1886 
1887     nghttp2_session_set_stream_user_data(session, stream_id, req.get());
1888 
1889     client->request_done(req.get());
1890     req->record_request_start_time();
1891     client->reqvec.push_back(std::move(req));
1892 
1893     break;
1894   }
1895   }
1896   return 0;
1897 }
1898 } // namespace
1899 
1900 namespace {
on_header_callback(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * user_data)1901 int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
1902                        const uint8_t *name, size_t namelen,
1903                        const uint8_t *value, size_t valuelen, uint8_t flags,
1904                        void *user_data) {
1905   if (config.verbose) {
1906     verbose_on_header_callback(session, frame, name, namelen, value, valuelen,
1907                                flags, user_data);
1908   }
1909 
1910   switch (frame->hd.type) {
1911   case NGHTTP2_HEADERS: {
1912     auto req = static_cast<Request *>(
1913         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1914 
1915     if (!req) {
1916       break;
1917     }
1918 
1919     /* ignore trailer header */
1920     if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
1921         !req->expect_final_response) {
1922       break;
1923     }
1924 
1925     if (req->header_buffer_size + namelen + valuelen > 64_k) {
1926       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
1927                                 NGHTTP2_INTERNAL_ERROR);
1928       return 0;
1929     }
1930 
1931     req->header_buffer_size += namelen + valuelen;
1932 
1933     auto token = http2::lookup_token(name, namelen);
1934 
1935     http2::index_header(req->res_hdidx, token, req->res_nva.size());
1936     http2::add_header(req->res_nva, name, namelen, value, valuelen,
1937                       flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
1938     break;
1939   }
1940   case NGHTTP2_PUSH_PROMISE: {
1941     auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data(
1942         session, frame->push_promise.promised_stream_id));
1943 
1944     if (!req) {
1945       break;
1946     }
1947 
1948     if (req->header_buffer_size + namelen + valuelen > 64_k) {
1949       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1950                                 frame->push_promise.promised_stream_id,
1951                                 NGHTTP2_INTERNAL_ERROR);
1952       return 0;
1953     }
1954 
1955     req->header_buffer_size += namelen + valuelen;
1956 
1957     auto token = http2::lookup_token(name, namelen);
1958 
1959     http2::index_header(req->req_hdidx, token, req->req_nva.size());
1960     http2::add_header(req->req_nva, name, namelen, value, valuelen,
1961                       flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
1962     break;
1963   }
1964   }
1965   return 0;
1966 }
1967 } // namespace
1968 
1969 namespace {
on_frame_recv_callback2(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)1970 int on_frame_recv_callback2(nghttp2_session *session,
1971                             const nghttp2_frame *frame, void *user_data) {
1972   int rv = 0;
1973 
1974   if (config.verbose) {
1975     verbose_on_frame_recv_callback(session, frame, user_data);
1976   }
1977 
1978   auto client = get_client(user_data);
1979   switch (frame->hd.type) {
1980   case NGHTTP2_DATA: {
1981     auto req = static_cast<Request *>(
1982         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1983     if (!req) {
1984       return 0;
1985       ;
1986     }
1987 
1988     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1989       req->record_response_end_time();
1990       ++client->success;
1991     }
1992 
1993     break;
1994   }
1995   case NGHTTP2_HEADERS: {
1996     auto req = static_cast<Request *>(
1997         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1998     // If this is the HTTP Upgrade with OPTIONS method to avoid POST,
1999     // req is nullptr.
2000     if (!req) {
2001       return 0;
2002       ;
2003     }
2004 
2005     switch (frame->headers.cat) {
2006     case NGHTTP2_HCAT_RESPONSE:
2007     case NGHTTP2_HCAT_PUSH_RESPONSE:
2008       check_response_header(session, req);
2009       break;
2010     case NGHTTP2_HCAT_HEADERS:
2011       if (req->expect_final_response) {
2012         check_response_header(session, req);
2013         break;
2014       }
2015       if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
2016         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
2017                                   frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
2018         return 0;
2019       }
2020       break;
2021     default:
2022       assert(0);
2023     }
2024 
2025     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2026       req->record_response_end_time();
2027       ++client->success;
2028     }
2029 
2030     break;
2031   }
2032   case NGHTTP2_SETTINGS:
2033     if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
2034       break;
2035     }
2036     ev_timer_stop(client->loop, &client->settings_timer);
2037     break;
2038   case NGHTTP2_PUSH_PROMISE: {
2039     auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data(
2040         session, frame->push_promise.promised_stream_id));
2041     if (!req) {
2042       break;
2043     }
2044 
2045     // Reset for response header field reception
2046     req->header_buffer_size = 0;
2047 
2048     auto scheme = req->get_req_header(http2::HD__SCHEME);
2049     auto authority = req->get_req_header(http2::HD__AUTHORITY);
2050     auto path = req->get_req_header(http2::HD__PATH);
2051 
2052     if (!authority) {
2053       authority = req->get_req_header(http2::HD_HOST);
2054     }
2055 
2056     // libnghttp2 guarantees :scheme, :method, :path and (:authority |
2057     // host) exist and non-empty.
2058     if (path->value[0] != '/') {
2059       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
2060                                 frame->push_promise.promised_stream_id,
2061                                 NGHTTP2_PROTOCOL_ERROR);
2062       break;
2063     }
2064     std::string uri = scheme->value;
2065     uri += "://";
2066     uri += authority->value;
2067     uri += path->value;
2068     http_parser_url u{};
2069     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
2070       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
2071                                 frame->push_promise.promised_stream_id,
2072                                 NGHTTP2_PROTOCOL_ERROR);
2073       break;
2074     }
2075     req->uri = uri;
2076     req->u = u;
2077 
2078     if (client->path_cache.count(uri)) {
2079       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
2080                                 frame->push_promise.promised_stream_id,
2081                                 NGHTTP2_CANCEL);
2082       break;
2083     }
2084 
2085     if (config.multiply == 1) {
2086       client->path_cache.insert(uri);
2087     }
2088 
2089     break;
2090   }
2091   }
2092   return rv;
2093 }
2094 } // namespace
2095 
2096 namespace {
before_frame_send_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)2097 int before_frame_send_callback(nghttp2_session *session,
2098                                const nghttp2_frame *frame, void *user_data) {
2099   if (frame->hd.type != NGHTTP2_HEADERS ||
2100       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
2101     return 0;
2102   }
2103   auto req = static_cast<Request *>(
2104       nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
2105   assert(req);
2106   req->record_request_start_time();
2107   return 0;
2108 }
2109 
2110 } // namespace
2111 
2112 namespace {
on_frame_send_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)2113 int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
2114                            void *user_data) {
2115   if (config.verbose) {
2116     verbose_on_frame_send_callback(session, frame, user_data);
2117   }
2118 
2119   if (frame->hd.type != NGHTTP2_HEADERS ||
2120       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
2121     return 0;
2122   }
2123 
2124   auto req = static_cast<Request *>(
2125       nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
2126   if (!req) {
2127     return 0;
2128   }
2129 
2130   // If this request is using Expect/Continue, start its ContinueTimer.
2131   if (req->continue_timer) {
2132     req->continue_timer->start();
2133   }
2134 
2135   return 0;
2136 }
2137 } // namespace
2138 
2139 namespace {
on_frame_not_send_callback(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * user_data)2140 int on_frame_not_send_callback(nghttp2_session *session,
2141                                const nghttp2_frame *frame, int lib_error_code,
2142                                void *user_data) {
2143   if (frame->hd.type != NGHTTP2_HEADERS ||
2144       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
2145     return 0;
2146   }
2147 
2148   auto req = static_cast<Request *>(
2149       nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
2150   if (!req) {
2151     return 0;
2152   }
2153 
2154   std::cerr << "[ERROR] request " << req->uri
2155             << " failed: " << nghttp2_strerror(lib_error_code) << std::endl;
2156 
2157   return 0;
2158 }
2159 } // namespace
2160 
2161 namespace {
on_stream_close_callback(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * user_data)2162 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
2163                              uint32_t error_code, void *user_data) {
2164   auto client = get_client(user_data);
2165   auto req = static_cast<Request *>(
2166       nghttp2_session_get_stream_user_data(session, stream_id));
2167 
2168   if (!req) {
2169     return 0;
2170   }
2171 
2172   // If this request is using Expect/Continue, stop its ContinueTimer.
2173   if (req->continue_timer) {
2174     req->continue_timer->stop();
2175   }
2176 
2177   update_html_parser(client, req, nullptr, 0, 1);
2178   ++client->complete;
2179 
2180   if (client->all_requests_processed()) {
2181     nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
2182   }
2183 
2184   return 0;
2185 }
2186 } // namespace
2187 
2188 struct RequestResult {
2189   std::chrono::microseconds time;
2190 };
2191 
2192 namespace {
print_stats(const HttpClient & client)2193 void print_stats(const HttpClient &client) {
2194   std::cout << "***** Statistics *****" << std::endl;
2195 
2196   std::vector<Request *> reqs;
2197   reqs.reserve(client.reqvec.size());
2198   for (const auto &req : client.reqvec) {
2199     if (req->timing.state == RequestState::ON_COMPLETE) {
2200       reqs.push_back(req.get());
2201     }
2202   }
2203 
2204   std::sort(std::begin(reqs), std::end(reqs),
2205             [](const Request *lhs, const Request *rhs) {
2206               const auto &ltiming = lhs->timing;
2207               const auto &rtiming = rhs->timing;
2208               return ltiming.response_end_time < rtiming.response_end_time ||
2209                      (ltiming.response_end_time == rtiming.response_end_time &&
2210                       ltiming.request_start_time < rtiming.request_start_time);
2211             });
2212 
2213   std::cout << R"(
2214 Request timing:
2215   responseEnd: the  time  when  last  byte of  response  was  received
2216                relative to connectEnd
2217  requestStart: the time  just before  first byte  of request  was sent
2218                relative  to connectEnd.   If  '*' is  shown, this  was
2219                pushed by server.
2220       process: responseEnd - requestStart
2221          code: HTTP status code
2222          size: number  of  bytes  received as  response  body  without
2223                inflation.
2224           URI: request URI
2225 
2226 see http://www.w3.org/TR/resource-timing/#processing-model
2227 
2228 sorted by 'complete'
2229 
2230 id  responseEnd requestStart  process code size request path)"
2231             << std::endl;
2232 
2233   const auto &base = client.timing.connect_end_time;
2234   for (const auto &req : reqs) {
2235     auto response_end = std::chrono::duration_cast<std::chrono::microseconds>(
2236         req->timing.response_end_time - base);
2237     auto request_start = std::chrono::duration_cast<std::chrono::microseconds>(
2238         req->timing.request_start_time - base);
2239     auto total = std::chrono::duration_cast<std::chrono::microseconds>(
2240         req->timing.response_end_time - req->timing.request_start_time);
2241     auto pushed = req->stream_id % 2 == 0;
2242 
2243     std::cout << std::setw(3) << req->stream_id << " " << std::setw(11)
2244               << ("+" + util::format_duration(response_end)) << " "
2245               << (pushed ? "*" : " ") << std::setw(11)
2246               << ("+" + util::format_duration(request_start)) << " "
2247               << std::setw(8) << util::format_duration(total) << " "
2248               << std::setw(4) << req->status << " " << std::setw(4)
2249               << util::utos_unit(req->response_len) << " "
2250               << req->make_reqpath() << std::endl;
2251   }
2252 }
2253 } // namespace
2254 
2255 #ifndef OPENSSL_NO_NEXTPROTONEG
2256 namespace {
client_select_next_proto_cb(SSL * ssl,unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)2257 int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
2258                                 unsigned char *outlen, const unsigned char *in,
2259                                 unsigned int inlen, void *arg) {
2260   if (config.verbose) {
2261     print_timer();
2262     std::cout << "[NPN] server offers:" << std::endl;
2263   }
2264   for (unsigned int i = 0; i < inlen; i += in[i] + 1) {
2265     if (config.verbose) {
2266       std::cout << "          * ";
2267       std::cout.write(reinterpret_cast<const char *>(&in[i + 1]), in[i]);
2268       std::cout << std::endl;
2269     }
2270   }
2271   if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
2272                        inlen)) {
2273     print_protocol_nego_error();
2274     return SSL_TLSEXT_ERR_NOACK;
2275   }
2276   return SSL_TLSEXT_ERR_OK;
2277 }
2278 } // namespace
2279 #endif // !OPENSSL_NO_NEXTPROTONEG
2280 
2281 namespace {
communicate(const std::string & scheme,const std::string & host,uint16_t port,std::vector<std::tuple<std::string,nghttp2_data_provider *,int64_t,int32_t>> requests,const nghttp2_session_callbacks * callbacks)2282 int communicate(
2283     const std::string &scheme, const std::string &host, uint16_t port,
2284     std::vector<
2285         std::tuple<std::string, nghttp2_data_provider *, int64_t, int32_t>>
2286         requests,
2287     const nghttp2_session_callbacks *callbacks) {
2288   int result = 0;
2289   auto loop = EV_DEFAULT;
2290   SSL_CTX *ssl_ctx = nullptr;
2291   if (scheme == "https") {
2292     ssl_ctx = SSL_CTX_new(TLS_client_method());
2293     if (!ssl_ctx) {
2294       std::cerr << "[ERROR] Failed to create SSL_CTX: "
2295                 << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2296       result = -1;
2297       goto fin;
2298     }
2299 
2300     auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
2301                     SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
2302                     SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
2303 
2304 #ifdef SSL_OP_ENABLE_KTLS
2305     if (config.ktls) {
2306       ssl_opts |= SSL_OP_ENABLE_KTLS;
2307     }
2308 #endif // SSL_OP_ENABLE_KTLS
2309 
2310     SSL_CTX_set_options(ssl_ctx, ssl_opts);
2311     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
2312     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
2313 
2314     if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
2315       std::cerr << "[WARNING] Could not load system trusted CA certificates: "
2316                 << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2317     }
2318 
2319     if (nghttp2::tls::ssl_ctx_set_proto_versions(
2320             ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
2321             nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
2322       std::cerr << "[ERROR] Could not set TLS versions" << std::endl;
2323       result = -1;
2324       goto fin;
2325     }
2326 
2327     if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
2328       std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
2329                 << std::endl;
2330       result = -1;
2331       goto fin;
2332     }
2333     if (!config.keyfile.empty()) {
2334       if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(),
2335                                       SSL_FILETYPE_PEM) != 1) {
2336         std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
2337                   << std::endl;
2338         result = -1;
2339         goto fin;
2340       }
2341     }
2342     if (!config.certfile.empty()) {
2343       if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
2344                                              config.certfile.c_str()) != 1) {
2345         std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
2346                   << std::endl;
2347         result = -1;
2348         goto fin;
2349       }
2350     }
2351 #ifndef OPENSSL_NO_NEXTPROTONEG
2352     SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
2353                                      nullptr);
2354 #endif // !OPENSSL_NO_NEXTPROTONEG
2355 
2356 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
2357     auto proto_list = util::get_default_alpn();
2358 
2359     SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
2360 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
2361   }
2362   {
2363     HttpClient client{callbacks, loop, ssl_ctx};
2364 
2365     int32_t dep_stream_id = 0;
2366 
2367     if (!config.no_dep) {
2368       dep_stream_id = anchors[ANCHOR_FOLLOWERS].stream_id;
2369     }
2370 
2371     for (auto &req : requests) {
2372       nghttp2_priority_spec pri_spec;
2373 
2374       nghttp2_priority_spec_init(&pri_spec, dep_stream_id, std::get<3>(req), 0);
2375 
2376       for (int i = 0; i < config.multiply; ++i) {
2377         client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req),
2378                            pri_spec);
2379       }
2380     }
2381     client.update_hostport();
2382 
2383     client.record_start_time();
2384 
2385     if (client.resolve_host(host, port) != 0) {
2386       goto fin;
2387     }
2388 
2389     client.record_domain_lookup_end_time();
2390 
2391     if (client.initiate_connection() != 0) {
2392       std::cerr << "[ERROR] Could not connect to " << host << ", port " << port
2393                 << std::endl;
2394       goto fin;
2395     }
2396 
2397     ev_set_userdata(loop, &client);
2398     ev_run(loop, 0);
2399     ev_set_userdata(loop, nullptr);
2400 
2401 #ifdef HAVE_JANSSON
2402     if (!config.harfile.empty()) {
2403       FILE *outfile;
2404       if (config.harfile == "-") {
2405         outfile = stdout;
2406       } else {
2407         outfile = fopen(config.harfile.c_str(), "wb");
2408       }
2409 
2410       if (outfile) {
2411         client.output_har(outfile);
2412 
2413         if (outfile != stdout) {
2414           fclose(outfile);
2415         }
2416       } else {
2417         std::cerr << "Cannot open file " << config.harfile << ". "
2418                   << "har file could not be created." << std::endl;
2419       }
2420     }
2421 #endif // HAVE_JANSSON
2422 
2423     if (client.success != client.reqvec.size()) {
2424       std::cerr << "Some requests were not processed. total="
2425                 << client.reqvec.size() << ", processed=" << client.success
2426                 << std::endl;
2427     }
2428     if (config.stat) {
2429       print_stats(client);
2430     }
2431   }
2432 fin:
2433   if (ssl_ctx) {
2434     SSL_CTX_free(ssl_ctx);
2435   }
2436   return result;
2437 }
2438 } // namespace
2439 
2440 namespace {
file_read_callback(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * user_data)2441 ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
2442                            uint8_t *buf, size_t length, uint32_t *data_flags,
2443                            nghttp2_data_source *source, void *user_data) {
2444   int rv;
2445   auto req = static_cast<Request *>(
2446       nghttp2_session_get_stream_user_data(session, stream_id));
2447   assert(req);
2448   int fd = source->fd;
2449   ssize_t nread;
2450 
2451   while ((nread = pread(fd, buf, length, req->data_offset)) == -1 &&
2452          errno == EINTR)
2453     ;
2454 
2455   if (nread == -1) {
2456     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2457   }
2458 
2459   req->data_offset += nread;
2460 
2461   if (req->data_offset == req->data_length) {
2462     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
2463     if (!config.trailer.empty()) {
2464       std::vector<nghttp2_nv> nva;
2465       nva.reserve(config.trailer.size());
2466       for (auto &kv : config.trailer) {
2467         nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
2468       }
2469       rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
2470       if (rv != 0) {
2471         if (nghttp2_is_fatal(rv)) {
2472           return NGHTTP2_ERR_CALLBACK_FAILURE;
2473         }
2474       } else {
2475         *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
2476       }
2477     }
2478 
2479     return nread;
2480   }
2481 
2482   if (req->data_offset > req->data_length || nread == 0) {
2483     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2484   }
2485 
2486   return nread;
2487 }
2488 } // namespace
2489 
2490 namespace {
run(char ** uris,int n)2491 int run(char **uris, int n) {
2492   nghttp2_session_callbacks *callbacks;
2493 
2494   nghttp2_session_callbacks_new(&callbacks);
2495   auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks);
2496 
2497   nghttp2_session_callbacks_set_on_stream_close_callback(
2498       callbacks, on_stream_close_callback);
2499 
2500   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
2501                                                        on_frame_recv_callback2);
2502 
2503   if (config.verbose) {
2504     nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
2505         callbacks, verbose_on_invalid_frame_recv_callback);
2506 
2507     nghttp2_session_callbacks_set_error_callback2(callbacks,
2508                                                   verbose_error_callback);
2509   }
2510 
2511   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
2512       callbacks, on_data_chunk_recv_callback);
2513 
2514   nghttp2_session_callbacks_set_on_begin_headers_callback(
2515       callbacks, on_begin_headers_callback);
2516 
2517   nghttp2_session_callbacks_set_on_header_callback(callbacks,
2518                                                    on_header_callback);
2519 
2520   nghttp2_session_callbacks_set_before_frame_send_callback(
2521       callbacks, before_frame_send_callback);
2522 
2523   nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
2524                                                        on_frame_send_callback);
2525 
2526   nghttp2_session_callbacks_set_on_frame_not_send_callback(
2527       callbacks, on_frame_not_send_callback);
2528 
2529   if (config.padding) {
2530     nghttp2_session_callbacks_set_select_padding_callback(
2531         callbacks, select_padding_callback);
2532   }
2533 
2534   std::string prev_scheme;
2535   std::string prev_host;
2536   uint16_t prev_port = 0;
2537   int failures = 0;
2538   int data_fd = -1;
2539   nghttp2_data_provider data_prd;
2540   struct stat data_stat;
2541 
2542   if (!config.datafile.empty()) {
2543     if (config.datafile == "-") {
2544       if (fstat(0, &data_stat) == 0 &&
2545           (data_stat.st_mode & S_IFMT) == S_IFREG) {
2546         // use STDIN if it is a regular file
2547         data_fd = 0;
2548       } else {
2549         // copy the contents of STDIN to a temporary file
2550         char tempfn[] = "/tmp/nghttp.temp.XXXXXX";
2551         data_fd = mkstemp(tempfn);
2552         if (data_fd == -1) {
2553           std::cerr << "[ERROR] Could not create a temporary file in /tmp"
2554                     << std::endl;
2555           return 1;
2556         }
2557         if (unlink(tempfn) != 0) {
2558           std::cerr << "[WARNING] failed to unlink temporary file:" << tempfn
2559                     << std::endl;
2560         }
2561         while (1) {
2562           std::array<char, 1_k> buf;
2563           ssize_t rret, wret;
2564           while ((rret = read(0, buf.data(), buf.size())) == -1 &&
2565                  errno == EINTR)
2566             ;
2567           if (rret == 0)
2568             break;
2569           if (rret == -1) {
2570             std::cerr << "[ERROR] I/O error while reading from STDIN"
2571                       << std::endl;
2572             return 1;
2573           }
2574           while ((wret = write(data_fd, buf.data(), rret)) == -1 &&
2575                  errno == EINTR)
2576             ;
2577           if (wret != rret) {
2578             std::cerr << "[ERROR] I/O error while writing to temporary file"
2579                       << std::endl;
2580             return 1;
2581           }
2582         }
2583         if (fstat(data_fd, &data_stat) == -1) {
2584           close(data_fd);
2585           std::cerr << "[ERROR] Could not stat temporary file" << std::endl;
2586           return 1;
2587         }
2588       }
2589     } else {
2590       data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY);
2591       if (data_fd == -1) {
2592         std::cerr << "[ERROR] Could not open file " << config.datafile
2593                   << std::endl;
2594         return 1;
2595       }
2596       if (fstat(data_fd, &data_stat) == -1) {
2597         close(data_fd);
2598         std::cerr << "[ERROR] Could not stat file " << config.datafile
2599                   << std::endl;
2600         return 1;
2601       }
2602     }
2603     data_prd.source.fd = data_fd;
2604     data_prd.read_callback = file_read_callback;
2605   }
2606   std::vector<
2607       std::tuple<std::string, nghttp2_data_provider *, int64_t, int32_t>>
2608       requests;
2609 
2610   size_t next_weight_idx = 0;
2611 
2612   for (int i = 0; i < n; ++i) {
2613     http_parser_url u{};
2614     auto uri = strip_fragment(uris[i]);
2615     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
2616       ++next_weight_idx;
2617       std::cerr << "[ERROR] Could not parse URI " << uri << std::endl;
2618       continue;
2619     }
2620     if (!util::has_uri_field(u, UF_SCHEMA)) {
2621       ++next_weight_idx;
2622       std::cerr << "[ERROR] URI " << uri << " does not have scheme part"
2623                 << std::endl;
2624       continue;
2625     }
2626     auto port = util::has_uri_field(u, UF_PORT)
2627                     ? u.port
2628                     : util::get_default_port(uri.c_str(), u);
2629     auto host = decode_host(util::get_uri_field(uri.c_str(), u, UF_HOST));
2630     if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) ||
2631         host != prev_host || port != prev_port) {
2632       if (!requests.empty()) {
2633         if (communicate(prev_scheme, prev_host, prev_port, std::move(requests),
2634                         callbacks) != 0) {
2635           ++failures;
2636         }
2637         requests.clear();
2638       }
2639       prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA).str();
2640       prev_host = std::move(host);
2641       prev_port = port;
2642     }
2643     requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd,
2644                           data_stat.st_size, config.weight[next_weight_idx++]);
2645   }
2646   if (!requests.empty()) {
2647     if (communicate(prev_scheme, prev_host, prev_port, std::move(requests),
2648                     callbacks) != 0) {
2649       ++failures;
2650     }
2651   }
2652   return failures;
2653 }
2654 } // namespace
2655 
2656 namespace {
print_version(std::ostream & out)2657 void print_version(std::ostream &out) {
2658   out << "nghttp nghttp2/" NGHTTP2_VERSION << std::endl;
2659 }
2660 } // namespace
2661 
2662 namespace {
print_usage(std::ostream & out)2663 void print_usage(std::ostream &out) {
2664   out << R"(Usage: nghttp [OPTIONS]... <URI>...
2665 HTTP/2 client)"
2666       << std::endl;
2667 }
2668 } // namespace
2669 
2670 namespace {
print_help(std::ostream & out)2671 void print_help(std::ostream &out) {
2672   print_usage(out);
2673   out << R"(
2674   <URI>       Specify URI to access.
2675 Options:
2676   -v, --verbose
2677               Print   debug   information   such  as   reception   and
2678               transmission of frames and name/value pairs.  Specifying
2679               this option multiple times increases verbosity.
2680   -n, --null-out
2681               Discard downloaded data.
2682   -O, --remote-name
2683               Save  download  data  in  the  current  directory.   The
2684               filename is  derived from  URI.  If  URI ends  with '/',
2685               'index.html'  is used  as a  filename.  Not  implemented
2686               yet.
2687   -t, --timeout=<DURATION>
2688               Timeout each request after <DURATION>.  Set 0 to disable
2689               timeout.
2690   -w, --window-bits=<N>
2691               Sets the stream level initial window size to 2**<N>-1.
2692   -W, --connection-window-bits=<N>
2693               Sets  the  connection  level   initial  window  size  to
2694               2**<N>-1.
2695   -a, --get-assets
2696               Download assets  such as stylesheets, images  and script
2697               files linked  from the downloaded resource.   Only links
2698               whose  origins are  the same  with the  linking resource
2699               will be downloaded.   nghttp prioritizes resources using
2700               HTTP/2 dependency  based priority.  The  priority order,
2701               from highest to lowest,  is html itself, css, javascript
2702               and images.
2703   -s, --stat  Print statistics.
2704   -H, --header=<HEADER>
2705               Add a header to the requests.  Example: -H':method: PUT'
2706   --trailer=<HEADER>
2707               Add a trailer header to the requests.  <HEADER> must not
2708               include pseudo header field  (header field name starting
2709               with ':').  To  send trailer, one must use  -d option to
2710               send request body.  Example: --trailer 'foo: bar'.
2711   --cert=<CERT>
2712               Use  the specified  client certificate  file.  The  file
2713               must be in PEM format.
2714   --key=<KEY> Use the  client private key  file.  The file must  be in
2715               PEM format.
2716   -d, --data=<PATH>
2717               Post FILE to server. If '-'  is given, data will be read
2718               from stdin.
2719   -m, --multiply=<N>
2720               Request each URI <N> times.  By default, same URI is not
2721               requested twice.  This option disables it too.
2722   -u, --upgrade
2723               Perform HTTP Upgrade for HTTP/2.  This option is ignored
2724               if the request URI has https scheme.  If -d is used, the
2725               HTTP upgrade request is performed with OPTIONS method.
2726   -p, --weight=<WEIGHT>
2727               Sets  weight of  given  URI.  This  option  can be  used
2728               multiple times, and  N-th -p option sets  weight of N-th
2729               URI in the command line.  If  the number of -p option is
2730               less than the number of URI, the last -p option value is
2731               repeated.  If there is no -p option, default weight, 16,
2732               is assumed.  The valid value range is
2733               [)"
2734       << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT << R"(], inclusive.
2735   -M, --peer-max-concurrent-streams=<N>
2736               Use  <N>  as  SETTINGS_MAX_CONCURRENT_STREAMS  value  of
2737               remote endpoint as if it  is received in SETTINGS frame.
2738               Default: 100
2739   -c, --header-table-size=<SIZE>
2740               Specify decoder  header table  size.  If this  option is
2741               used  multiple times,  and the  minimum value  among the
2742               given values except  for last one is  strictly less than
2743               the last  value, that minimum  value is set  in SETTINGS
2744               frame  payload  before  the   last  value,  to  simulate
2745               multiple header table size change.
2746   --encoder-header-table-size=<SIZE>
2747               Specify encoder header table size.  The decoder (server)
2748               specifies  the maximum  dynamic table  size it  accepts.
2749               Then the negotiated dynamic table size is the minimum of
2750               this option value and the value which server specified.
2751   -b, --padding=<N>
2752               Add at  most <N>  bytes to a  frame payload  as padding.
2753               Specify 0 to disable padding.
2754   -r, --har=<PATH>
2755               Output HTTP  transactions <PATH> in HAR  format.  If '-'
2756               is given, data is written to stdout.
2757   --color     Force colored log output.
2758   --continuation
2759               Send large header to test CONTINUATION.
2760   --no-content-length
2761               Don't send content-length header field.
2762   --no-dep    Don't send dependency based priority hint to server.
2763   --hexdump   Display the  incoming traffic in  hexadecimal (Canonical
2764               hex+ASCII display).  If SSL/TLS  is used, decrypted data
2765               are used.
2766   --no-push   Disable server push.
2767   --max-concurrent-streams=<N>
2768               The  number of  concurrent  pushed  streams this  client
2769               accepts.
2770   --expect-continue
2771               Perform an Expect/Continue handshake:  wait to send DATA
2772               (up to  a short  timeout)  until the server sends  a 100
2773               Continue interim response. This option is ignored unless
2774               combined with the -d option.
2775   -y, --no-verify-peer
2776               Suppress  warning  on  server  certificate  verification
2777               failure.
2778   --ktls      Enable ktls.
2779   --no-rfc7540-pri
2780               Disable RFC7540 priorities.
2781   --version   Display version information and exit.
2782   -h, --help  Display this help and exit.
2783 
2784 --
2785 
2786   The <SIZE> argument is an integer and an optional unit (e.g., 10K is
2787   10 * 1024).  Units are K, M and G (powers of 1024).
2788 
2789   The <DURATION> argument is an integer and an optional unit (e.g., 1s
2790   is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
2791   (hours, minutes, seconds and milliseconds, respectively).  If a unit
2792   is omitted, a second is used as unit.)"
2793       << std::endl;
2794 }
2795 } // namespace
2796 
main(int argc,char ** argv)2797 int main(int argc, char **argv) {
2798   tls::libssl_init();
2799 
2800   bool color = false;
2801   while (1) {
2802     static int flag = 0;
2803     constexpr static option long_options[] = {
2804         {"verbose", no_argument, nullptr, 'v'},
2805         {"null-out", no_argument, nullptr, 'n'},
2806         {"remote-name", no_argument, nullptr, 'O'},
2807         {"timeout", required_argument, nullptr, 't'},
2808         {"window-bits", required_argument, nullptr, 'w'},
2809         {"connection-window-bits", required_argument, nullptr, 'W'},
2810         {"get-assets", no_argument, nullptr, 'a'},
2811         {"stat", no_argument, nullptr, 's'},
2812         {"help", no_argument, nullptr, 'h'},
2813         {"header", required_argument, nullptr, 'H'},
2814         {"data", required_argument, nullptr, 'd'},
2815         {"multiply", required_argument, nullptr, 'm'},
2816         {"upgrade", no_argument, nullptr, 'u'},
2817         {"weight", required_argument, nullptr, 'p'},
2818         {"peer-max-concurrent-streams", required_argument, nullptr, 'M'},
2819         {"header-table-size", required_argument, nullptr, 'c'},
2820         {"padding", required_argument, nullptr, 'b'},
2821         {"har", required_argument, nullptr, 'r'},
2822         {"no-verify-peer", no_argument, nullptr, 'y'},
2823         {"cert", required_argument, &flag, 1},
2824         {"key", required_argument, &flag, 2},
2825         {"color", no_argument, &flag, 3},
2826         {"continuation", no_argument, &flag, 4},
2827         {"version", no_argument, &flag, 5},
2828         {"no-content-length", no_argument, &flag, 6},
2829         {"no-dep", no_argument, &flag, 7},
2830         {"trailer", required_argument, &flag, 9},
2831         {"hexdump", no_argument, &flag, 10},
2832         {"no-push", no_argument, &flag, 11},
2833         {"max-concurrent-streams", required_argument, &flag, 12},
2834         {"expect-continue", no_argument, &flag, 13},
2835         {"encoder-header-table-size", required_argument, &flag, 14},
2836         {"ktls", no_argument, &flag, 15},
2837         {"no-rfc7540-pri", no_argument, &flag, 16},
2838         {nullptr, 0, nullptr, 0}};
2839     int option_index = 0;
2840     int c =
2841         getopt_long(argc, argv, "M:Oab:c:d:m:np:r:hH:vst:uw:yW:", long_options,
2842                     &option_index);
2843     if (c == -1) {
2844       break;
2845     }
2846     switch (c) {
2847     case 'M': {
2848       // peer-max-concurrent-streams option
2849       auto n = util::parse_uint(optarg);
2850       if (n == -1) {
2851         std::cerr << "-M: Bad option value: " << optarg << std::endl;
2852         exit(EXIT_FAILURE);
2853       }
2854       config.peer_max_concurrent_streams = n;
2855       break;
2856     }
2857     case 'O':
2858       config.remote_name = true;
2859       break;
2860     case 'h':
2861       print_help(std::cout);
2862       exit(EXIT_SUCCESS);
2863     case 'b': {
2864       auto n = util::parse_uint(optarg);
2865       if (n == -1) {
2866         std::cerr << "-b: Bad option value: " << optarg << std::endl;
2867         exit(EXIT_FAILURE);
2868       }
2869       config.padding = n;
2870       break;
2871     }
2872     case 'n':
2873       config.null_out = true;
2874       break;
2875     case 'p': {
2876       auto n = util::parse_uint(optarg);
2877       if (n == -1 || NGHTTP2_MIN_WEIGHT > n || n > NGHTTP2_MAX_WEIGHT) {
2878         std::cerr << "-p: specify the integer in the range ["
2879                   << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT
2880                   << "], inclusive" << std::endl;
2881         exit(EXIT_FAILURE);
2882       }
2883       config.weight.push_back(n);
2884       break;
2885     }
2886     case 'r':
2887 #ifdef HAVE_JANSSON
2888       config.harfile = optarg;
2889 #else  // !HAVE_JANSSON
2890       std::cerr << "[WARNING]: -r, --har option is ignored because\n"
2891                 << "the binary was not compiled with libjansson." << std::endl;
2892 #endif // !HAVE_JANSSON
2893       break;
2894     case 'v':
2895       ++config.verbose;
2896       break;
2897     case 't':
2898       config.timeout = util::parse_duration_with_unit(optarg);
2899       if (config.timeout == std::numeric_limits<double>::infinity()) {
2900         std::cerr << "-t: bad timeout value: " << optarg << std::endl;
2901         exit(EXIT_FAILURE);
2902       }
2903       break;
2904     case 'u':
2905       config.upgrade = true;
2906       break;
2907     case 'w':
2908     case 'W': {
2909       auto n = util::parse_uint(optarg);
2910       if (n == -1 || n > 30) {
2911         std::cerr << "-" << static_cast<char>(c)
2912                   << ": specify the integer in the range [0, 30], inclusive"
2913                   << std::endl;
2914         exit(EXIT_FAILURE);
2915       }
2916       if (c == 'w') {
2917         config.window_bits = n;
2918       } else {
2919         config.connection_window_bits = n;
2920       }
2921       break;
2922     }
2923     case 'H': {
2924       char *header = optarg;
2925       // Skip first possible ':' in the header name
2926       char *value = strchr(optarg + 1, ':');
2927       if (!value || (header[0] == ':' && header + 1 == value)) {
2928         std::cerr << "-H: invalid header: " << optarg << std::endl;
2929         exit(EXIT_FAILURE);
2930       }
2931       *value = 0;
2932       value++;
2933       while (isspace(*value)) {
2934         value++;
2935       }
2936       if (*value == 0) {
2937         // This could also be a valid case for suppressing a header
2938         // similar to curl
2939         std::cerr << "-H: invalid header - value missing: " << optarg
2940                   << std::endl;
2941         exit(EXIT_FAILURE);
2942       }
2943       config.headers.emplace_back(header, value, false);
2944       util::inp_strlower(config.headers.back().name);
2945       break;
2946     }
2947     case 'a':
2948 #ifdef HAVE_LIBXML2
2949       config.get_assets = true;
2950 #else  // !HAVE_LIBXML2
2951       std::cerr << "[WARNING]: -a, --get-assets option is ignored because\n"
2952                 << "the binary was not compiled with libxml2." << std::endl;
2953 #endif // !HAVE_LIBXML2
2954       break;
2955     case 's':
2956       config.stat = true;
2957       break;
2958     case 'd':
2959       config.datafile = optarg;
2960       break;
2961     case 'm': {
2962       auto n = util::parse_uint(optarg);
2963       if (n == -1) {
2964         std::cerr << "-m: Bad option value: " << optarg << std::endl;
2965         exit(EXIT_FAILURE);
2966       }
2967       config.multiply = n;
2968       break;
2969     }
2970     case 'c': {
2971       auto n = util::parse_uint_with_unit(optarg);
2972       if (n == -1) {
2973         std::cerr << "-c: Bad option value: " << optarg << std::endl;
2974         exit(EXIT_FAILURE);
2975       }
2976       if (n > std::numeric_limits<uint32_t>::max()) {
2977         std::cerr << "-c: Value too large.  It should be less than or equal to "
2978                   << std::numeric_limits<uint32_t>::max() << std::endl;
2979         exit(EXIT_FAILURE);
2980       }
2981       config.header_table_size = n;
2982       config.min_header_table_size = std::min(config.min_header_table_size, n);
2983       break;
2984     }
2985     case 'y':
2986       config.verify_peer = false;
2987       break;
2988     case '?':
2989       util::show_candidates(argv[optind - 1], long_options);
2990       exit(EXIT_FAILURE);
2991     case 0:
2992       switch (flag) {
2993       case 1:
2994         // cert option
2995         config.certfile = optarg;
2996         break;
2997       case 2:
2998         // key option
2999         config.keyfile = optarg;
3000         break;
3001       case 3:
3002         // color option
3003         color = true;
3004         break;
3005       case 4:
3006         // continuation option
3007         config.continuation = true;
3008         break;
3009       case 5:
3010         // version option
3011         print_version(std::cout);
3012         exit(EXIT_SUCCESS);
3013       case 6:
3014         // no-content-length option
3015         config.no_content_length = true;
3016         break;
3017       case 7:
3018         // no-dep option
3019         config.no_dep = true;
3020         break;
3021       case 9: {
3022         // trailer option
3023         auto header = optarg;
3024         auto value = strchr(optarg, ':');
3025         if (!value) {
3026           std::cerr << "--trailer: invalid header: " << optarg << std::endl;
3027           exit(EXIT_FAILURE);
3028         }
3029         *value = 0;
3030         value++;
3031         while (isspace(*value)) {
3032           value++;
3033         }
3034         if (*value == 0) {
3035           // This could also be a valid case for suppressing a header
3036           // similar to curl
3037           std::cerr << "--trailer: invalid header - value missing: " << optarg
3038                     << std::endl;
3039           exit(EXIT_FAILURE);
3040         }
3041         config.trailer.emplace_back(header, value, false);
3042         util::inp_strlower(config.trailer.back().name);
3043         break;
3044       }
3045       case 10:
3046         // hexdump option
3047         config.hexdump = true;
3048         break;
3049       case 11:
3050         // no-push option
3051         config.no_push = true;
3052         break;
3053       case 12: {
3054         // max-concurrent-streams option
3055         auto n = util::parse_uint(optarg);
3056         if (n == -1) {
3057           std::cerr << "--max-concurrent-streams: Bad option value: " << optarg
3058                     << std::endl;
3059           exit(EXIT_FAILURE);
3060         }
3061         config.max_concurrent_streams = n;
3062         break;
3063       }
3064       case 13:
3065         // expect-continue option
3066         config.expect_continue = true;
3067         break;
3068       case 14: {
3069         // encoder-header-table-size option
3070         auto n = util::parse_uint_with_unit(optarg);
3071         if (n == -1) {
3072           std::cerr << "--encoder-header-table-size: Bad option value: "
3073                     << optarg << std::endl;
3074           exit(EXIT_FAILURE);
3075         }
3076         if (n > std::numeric_limits<uint32_t>::max()) {
3077           std::cerr << "--encoder-header-table-size: Value too large.  It "
3078                        "should be less than or equal to "
3079                     << std::numeric_limits<uint32_t>::max() << std::endl;
3080           exit(EXIT_FAILURE);
3081         }
3082         config.encoder_header_table_size = n;
3083         break;
3084       }
3085       case 15:
3086         // ktls option
3087         config.ktls = true;
3088         break;
3089       case 16:
3090         // no-rfc7540-pri option
3091         config.no_rfc7540_pri = true;
3092         break;
3093       }
3094       break;
3095     default:
3096       break;
3097     }
3098   }
3099 
3100   int32_t weight_to_fill;
3101   if (config.weight.empty()) {
3102     weight_to_fill = NGHTTP2_DEFAULT_WEIGHT;
3103   } else {
3104     weight_to_fill = config.weight.back();
3105   }
3106   config.weight.insert(std::end(config.weight), argc - optind, weight_to_fill);
3107 
3108   // Find scheme overridden by extra header fields.
3109   auto scheme_it =
3110       std::find_if(std::begin(config.headers), std::end(config.headers),
3111                    [](const Header &nv) { return nv.name == ":scheme"; });
3112   if (scheme_it != std::end(config.headers)) {
3113     config.scheme_override = (*scheme_it).value;
3114   }
3115 
3116   // Find host and port overridden by extra header fields.
3117   auto authority_it =
3118       std::find_if(std::begin(config.headers), std::end(config.headers),
3119                    [](const Header &nv) { return nv.name == ":authority"; });
3120   if (authority_it == std::end(config.headers)) {
3121     authority_it =
3122         std::find_if(std::begin(config.headers), std::end(config.headers),
3123                      [](const Header &nv) { return nv.name == "host"; });
3124   }
3125 
3126   if (authority_it != std::end(config.headers)) {
3127     // authority_it may looks like "host:port".
3128     auto uri = "https://" + (*authority_it).value;
3129     http_parser_url u{};
3130     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
3131       std::cerr << "[ERROR] Could not parse authority in "
3132                 << (*authority_it).name << ": " << (*authority_it).value
3133                 << std::endl;
3134       exit(EXIT_FAILURE);
3135     }
3136 
3137     config.host_override = util::get_uri_field(uri.c_str(), u, UF_HOST).str();
3138     if (util::has_uri_field(u, UF_PORT)) {
3139       config.port_override = u.port;
3140     }
3141   }
3142 
3143   set_color_output(color || isatty(fileno(stdout)));
3144 
3145   nghttp2_option_set_peer_max_concurrent_streams(
3146       config.http2_option, config.peer_max_concurrent_streams);
3147 
3148   if (config.encoder_header_table_size != -1) {
3149     nghttp2_option_set_max_deflate_dynamic_table_size(
3150         config.http2_option, config.encoder_header_table_size);
3151   }
3152 
3153   struct sigaction act {};
3154   act.sa_handler = SIG_IGN;
3155   sigaction(SIGPIPE, &act, nullptr);
3156   reset_timer();
3157   return run(argv + optind, argc - optind);
3158 }
3159 
3160 } // namespace nghttp2
3161 
main(int argc,char ** argv)3162 int main(int argc, char **argv) {
3163   return nghttp2::run_app(nghttp2::main, argc, argv);
3164 }
3165