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