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() ? util::has_uri_field(u, UF_PORT) ? u.port
202                                         : scheme == "https"             ? 443
203                                                                         : 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 = util::has_uri_field(u, UF_PORT) ? u.port
1685                      : scheme == "https"             ? 443
1686                                                      : 80;
1687 
1688     if (port != link_port) {
1689       continue;
1690     }
1691 
1692     // No POST data for assets
1693     auto pri_spec = resolve_dep(res_type);
1694 
1695     if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) {
1696       submit_request(client, config.headers, client->reqvec.back().get());
1697     }
1698   }
1699   req->html_parser->clear_links();
1700 }
1701 } // namespace
1702 
1703 namespace {
get_client(void * user_data)1704 HttpClient *get_client(void *user_data) {
1705   return static_cast<HttpClient *>(user_data);
1706 }
1707 } // namespace
1708 
1709 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)1710 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
1711                                 int32_t stream_id, const uint8_t *data,
1712                                 size_t len, void *user_data) {
1713   auto client = get_client(user_data);
1714   auto req = static_cast<Request *>(
1715       nghttp2_session_get_stream_user_data(session, stream_id));
1716 
1717   if (!req) {
1718     return 0;
1719   }
1720 
1721   if (config.verbose >= 2) {
1722     verbose_on_data_chunk_recv_callback(session, flags, stream_id, data, len,
1723                                         user_data);
1724   }
1725 
1726   req->response_len += len;
1727 
1728   if (req->inflater) {
1729     while (len > 0) {
1730       const size_t MAX_OUTLEN = 4_k;
1731       std::array<uint8_t, MAX_OUTLEN> out;
1732       size_t outlen = MAX_OUTLEN;
1733       size_t tlen = len;
1734       int rv =
1735           nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen);
1736       if (rv != 0) {
1737         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
1738                                   NGHTTP2_INTERNAL_ERROR);
1739         break;
1740       }
1741 
1742       if (!config.null_out) {
1743         std::cout.write(reinterpret_cast<const char *>(out.data()), outlen);
1744       }
1745 
1746       update_html_parser(client, req, out.data(), outlen, 0);
1747       data += tlen;
1748       len -= tlen;
1749     }
1750 
1751     return 0;
1752   }
1753 
1754   if (!config.null_out) {
1755     std::cout.write(reinterpret_cast<const char *>(data), len);
1756   }
1757 
1758   update_html_parser(client, req, data, len, 0);
1759 
1760   return 0;
1761 }
1762 } // namespace
1763 
1764 namespace {
select_padding_callback(nghttp2_session * session,const nghttp2_frame * frame,size_t max_payload,void * user_data)1765 ssize_t select_padding_callback(nghttp2_session *session,
1766                                 const nghttp2_frame *frame, size_t max_payload,
1767                                 void *user_data) {
1768   return std::min(max_payload, frame->hd.length + config.padding);
1769 }
1770 } // namespace
1771 
1772 namespace {
check_response_header(nghttp2_session * session,Request * req)1773 void check_response_header(nghttp2_session *session, Request *req) {
1774   bool gzip = false;
1775 
1776   req->expect_final_response = false;
1777 
1778   auto status_hd = req->get_res_header(http2::HD__STATUS);
1779 
1780   if (!status_hd) {
1781     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
1782                               NGHTTP2_PROTOCOL_ERROR);
1783     return;
1784   }
1785 
1786   auto status = http2::parse_http_status_code(StringRef{status_hd->value});
1787   if (status == -1) {
1788     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
1789                               NGHTTP2_PROTOCOL_ERROR);
1790     return;
1791   }
1792 
1793   req->status = status;
1794 
1795   for (auto &nv : req->res_nva) {
1796     if ("content-encoding" == nv.name) {
1797       gzip = util::strieq_l("gzip", nv.value) ||
1798              util::strieq_l("deflate", nv.value);
1799       continue;
1800     }
1801   }
1802 
1803   if (req->status / 100 == 1) {
1804     if (req->continue_timer && (req->status == 100)) {
1805       // If the request is waiting for a 100 Continue, complete the handshake.
1806       req->continue_timer->dispatch_continue();
1807     }
1808 
1809     req->expect_final_response = true;
1810     req->status = 0;
1811     req->res_nva.clear();
1812     http2::init_hdidx(req->res_hdidx);
1813     return;
1814   } else if (req->continue_timer) {
1815     // A final response stops any pending Expect/Continue handshake.
1816     req->continue_timer->stop();
1817   }
1818 
1819   if (gzip) {
1820     if (!req->inflater) {
1821       req->init_inflater();
1822     }
1823   }
1824   if (config.get_assets && req->level == 0) {
1825     if (!req->html_parser) {
1826       req->init_html_parser();
1827     }
1828   }
1829 }
1830 } // namespace
1831 
1832 namespace {
on_begin_headers_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)1833 int on_begin_headers_callback(nghttp2_session *session,
1834                               const nghttp2_frame *frame, void *user_data) {
1835   auto client = get_client(user_data);
1836   switch (frame->hd.type) {
1837   case NGHTTP2_HEADERS: {
1838     auto req = static_cast<Request *>(
1839         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1840     if (!req) {
1841       break;
1842     }
1843 
1844     switch (frame->headers.cat) {
1845     case NGHTTP2_HCAT_RESPONSE:
1846     case NGHTTP2_HCAT_PUSH_RESPONSE:
1847       req->record_response_start_time();
1848       break;
1849     default:
1850       break;
1851     }
1852 
1853     break;
1854   }
1855   case NGHTTP2_PUSH_PROMISE: {
1856     auto stream_id = frame->push_promise.promised_stream_id;
1857     http_parser_url u{};
1858     // TODO Set pri and level
1859     nghttp2_priority_spec pri_spec;
1860 
1861     nghttp2_priority_spec_default_init(&pri_spec);
1862 
1863     auto req = std::make_unique<Request>("", u, nullptr, 0, pri_spec);
1864     req->stream_id = stream_id;
1865 
1866     nghttp2_session_set_stream_user_data(session, stream_id, req.get());
1867 
1868     client->request_done(req.get());
1869     req->record_request_start_time();
1870     client->reqvec.push_back(std::move(req));
1871 
1872     break;
1873   }
1874   }
1875   return 0;
1876 }
1877 } // namespace
1878 
1879 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)1880 int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
1881                        const uint8_t *name, size_t namelen,
1882                        const uint8_t *value, size_t valuelen, uint8_t flags,
1883                        void *user_data) {
1884   if (config.verbose) {
1885     verbose_on_header_callback(session, frame, name, namelen, value, valuelen,
1886                                flags, user_data);
1887   }
1888 
1889   switch (frame->hd.type) {
1890   case NGHTTP2_HEADERS: {
1891     auto req = static_cast<Request *>(
1892         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1893 
1894     if (!req) {
1895       break;
1896     }
1897 
1898     /* ignore trailer header */
1899     if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
1900         !req->expect_final_response) {
1901       break;
1902     }
1903 
1904     if (req->header_buffer_size + namelen + valuelen > 64_k) {
1905       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
1906                                 NGHTTP2_INTERNAL_ERROR);
1907       return 0;
1908     }
1909 
1910     req->header_buffer_size += namelen + valuelen;
1911 
1912     auto token = http2::lookup_token(name, namelen);
1913 
1914     http2::index_header(req->res_hdidx, token, req->res_nva.size());
1915     http2::add_header(req->res_nva, name, namelen, value, valuelen,
1916                       flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
1917     break;
1918   }
1919   case NGHTTP2_PUSH_PROMISE: {
1920     auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data(
1921         session, frame->push_promise.promised_stream_id));
1922 
1923     if (!req) {
1924       break;
1925     }
1926 
1927     if (req->header_buffer_size + namelen + valuelen > 64_k) {
1928       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1929                                 frame->push_promise.promised_stream_id,
1930                                 NGHTTP2_INTERNAL_ERROR);
1931       return 0;
1932     }
1933 
1934     req->header_buffer_size += namelen + valuelen;
1935 
1936     auto token = http2::lookup_token(name, namelen);
1937 
1938     http2::index_header(req->req_hdidx, token, req->req_nva.size());
1939     http2::add_header(req->req_nva, name, namelen, value, valuelen,
1940                       flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
1941     break;
1942   }
1943   }
1944   return 0;
1945 }
1946 } // namespace
1947 
1948 namespace {
on_frame_recv_callback2(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)1949 int on_frame_recv_callback2(nghttp2_session *session,
1950                             const nghttp2_frame *frame, void *user_data) {
1951   int rv = 0;
1952 
1953   if (config.verbose) {
1954     verbose_on_frame_recv_callback(session, frame, user_data);
1955   }
1956 
1957   auto client = get_client(user_data);
1958   switch (frame->hd.type) {
1959   case NGHTTP2_DATA: {
1960     auto req = static_cast<Request *>(
1961         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1962     if (!req) {
1963       return 0;
1964       ;
1965     }
1966 
1967     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1968       req->record_response_end_time();
1969       ++client->success;
1970     }
1971 
1972     break;
1973   }
1974   case NGHTTP2_HEADERS: {
1975     auto req = static_cast<Request *>(
1976         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1977     // If this is the HTTP Upgrade with OPTIONS method to avoid POST,
1978     // req is nullptr.
1979     if (!req) {
1980       return 0;
1981       ;
1982     }
1983 
1984     switch (frame->headers.cat) {
1985     case NGHTTP2_HCAT_RESPONSE:
1986     case NGHTTP2_HCAT_PUSH_RESPONSE:
1987       check_response_header(session, req);
1988       break;
1989     case NGHTTP2_HCAT_HEADERS:
1990       if (req->expect_final_response) {
1991         check_response_header(session, req);
1992         break;
1993       }
1994       if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
1995         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1996                                   frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
1997         return 0;
1998       }
1999       break;
2000     default:
2001       assert(0);
2002     }
2003 
2004     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2005       req->record_response_end_time();
2006       ++client->success;
2007     }
2008 
2009     break;
2010   }
2011   case NGHTTP2_SETTINGS:
2012     if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
2013       break;
2014     }
2015     ev_timer_stop(client->loop, &client->settings_timer);
2016     break;
2017   case NGHTTP2_PUSH_PROMISE: {
2018     auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data(
2019         session, frame->push_promise.promised_stream_id));
2020     if (!req) {
2021       break;
2022     }
2023 
2024     // Reset for response header field reception
2025     req->header_buffer_size = 0;
2026 
2027     auto scheme = req->get_req_header(http2::HD__SCHEME);
2028     auto authority = req->get_req_header(http2::HD__AUTHORITY);
2029     auto path = req->get_req_header(http2::HD__PATH);
2030 
2031     if (!authority) {
2032       authority = req->get_req_header(http2::HD_HOST);
2033     }
2034 
2035     // libnghttp2 guarantees :scheme, :method, :path and (:authority |
2036     // host) exist and non-empty.
2037     if (path->value[0] != '/') {
2038       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
2039                                 frame->push_promise.promised_stream_id,
2040                                 NGHTTP2_PROTOCOL_ERROR);
2041       break;
2042     }
2043     std::string uri = scheme->value;
2044     uri += "://";
2045     uri += authority->value;
2046     uri += path->value;
2047     http_parser_url u{};
2048     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
2049       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
2050                                 frame->push_promise.promised_stream_id,
2051                                 NGHTTP2_PROTOCOL_ERROR);
2052       break;
2053     }
2054     req->uri = uri;
2055     req->u = u;
2056 
2057     if (client->path_cache.count(uri)) {
2058       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
2059                                 frame->push_promise.promised_stream_id,
2060                                 NGHTTP2_CANCEL);
2061       break;
2062     }
2063 
2064     if (config.multiply == 1) {
2065       client->path_cache.insert(uri);
2066     }
2067 
2068     break;
2069   }
2070   }
2071   return rv;
2072 }
2073 } // namespace
2074 
2075 namespace {
before_frame_send_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)2076 int before_frame_send_callback(nghttp2_session *session,
2077                                const nghttp2_frame *frame, void *user_data) {
2078   if (frame->hd.type != NGHTTP2_HEADERS ||
2079       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
2080     return 0;
2081   }
2082   auto req = static_cast<Request *>(
2083       nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
2084   assert(req);
2085   req->record_request_start_time();
2086   return 0;
2087 }
2088 
2089 } // namespace
2090 
2091 namespace {
on_frame_send_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)2092 int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
2093                            void *user_data) {
2094   if (config.verbose) {
2095     verbose_on_frame_send_callback(session, frame, user_data);
2096   }
2097 
2098   if (frame->hd.type != NGHTTP2_HEADERS ||
2099       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
2100     return 0;
2101   }
2102 
2103   auto req = static_cast<Request *>(
2104       nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
2105   if (!req) {
2106     return 0;
2107   }
2108 
2109   // If this request is using Expect/Continue, start its ContinueTimer.
2110   if (req->continue_timer) {
2111     req->continue_timer->start();
2112   }
2113 
2114   return 0;
2115 }
2116 } // namespace
2117 
2118 namespace {
on_frame_not_send_callback(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * user_data)2119 int on_frame_not_send_callback(nghttp2_session *session,
2120                                const nghttp2_frame *frame, int lib_error_code,
2121                                void *user_data) {
2122   if (frame->hd.type != NGHTTP2_HEADERS ||
2123       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
2124     return 0;
2125   }
2126 
2127   auto req = static_cast<Request *>(
2128       nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
2129   if (!req) {
2130     return 0;
2131   }
2132 
2133   std::cerr << "[ERROR] request " << req->uri
2134             << " failed: " << nghttp2_strerror(lib_error_code) << std::endl;
2135 
2136   return 0;
2137 }
2138 } // namespace
2139 
2140 namespace {
on_stream_close_callback(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * user_data)2141 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
2142                              uint32_t error_code, void *user_data) {
2143   auto client = get_client(user_data);
2144   auto req = static_cast<Request *>(
2145       nghttp2_session_get_stream_user_data(session, stream_id));
2146 
2147   if (!req) {
2148     return 0;
2149   }
2150 
2151   // If this request is using Expect/Continue, stop its ContinueTimer.
2152   if (req->continue_timer) {
2153     req->continue_timer->stop();
2154   }
2155 
2156   update_html_parser(client, req, nullptr, 0, 1);
2157   ++client->complete;
2158 
2159   if (client->all_requests_processed()) {
2160     nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
2161   }
2162 
2163   return 0;
2164 }
2165 } // namespace
2166 
2167 struct RequestResult {
2168   std::chrono::microseconds time;
2169 };
2170 
2171 namespace {
print_stats(const HttpClient & client)2172 void print_stats(const HttpClient &client) {
2173   std::cout << "***** Statistics *****" << std::endl;
2174 
2175   std::vector<Request *> reqs;
2176   reqs.reserve(client.reqvec.size());
2177   for (const auto &req : client.reqvec) {
2178     if (req->timing.state == RequestState::ON_COMPLETE) {
2179       reqs.push_back(req.get());
2180     }
2181   }
2182 
2183   std::sort(std::begin(reqs), std::end(reqs),
2184             [](const Request *lhs, const Request *rhs) {
2185               const auto &ltiming = lhs->timing;
2186               const auto &rtiming = rhs->timing;
2187               return ltiming.response_end_time < rtiming.response_end_time ||
2188                      (ltiming.response_end_time == rtiming.response_end_time &&
2189                       ltiming.request_start_time < rtiming.request_start_time);
2190             });
2191 
2192   std::cout << R"(
2193 Request timing:
2194   responseEnd: the  time  when  last  byte of  response  was  received
2195                relative to connectEnd
2196  requestStart: the time  just before  first byte  of request  was sent
2197                relative  to connectEnd.   If  '*' is  shown, this  was
2198                pushed by server.
2199       process: responseEnd - requestStart
2200          code: HTTP status code
2201          size: number  of  bytes  received as  response  body  without
2202                inflation.
2203           URI: request URI
2204 
2205 see http://www.w3.org/TR/resource-timing/#processing-model
2206 
2207 sorted by 'complete'
2208 
2209 id  responseEnd requestStart  process code size request path)"
2210             << std::endl;
2211 
2212   const auto &base = client.timing.connect_end_time;
2213   for (const auto &req : reqs) {
2214     auto response_end = std::chrono::duration_cast<std::chrono::microseconds>(
2215         req->timing.response_end_time - base);
2216     auto request_start = std::chrono::duration_cast<std::chrono::microseconds>(
2217         req->timing.request_start_time - base);
2218     auto total = std::chrono::duration_cast<std::chrono::microseconds>(
2219         req->timing.response_end_time - req->timing.request_start_time);
2220     auto pushed = req->stream_id % 2 == 0;
2221 
2222     std::cout << std::setw(3) << req->stream_id << " " << std::setw(11)
2223               << ("+" + util::format_duration(response_end)) << " "
2224               << (pushed ? "*" : " ") << std::setw(11)
2225               << ("+" + util::format_duration(request_start)) << " "
2226               << std::setw(8) << util::format_duration(total) << " "
2227               << std::setw(4) << req->status << " " << std::setw(4)
2228               << util::utos_unit(req->response_len) << " "
2229               << req->make_reqpath() << std::endl;
2230   }
2231 }
2232 } // namespace
2233 
2234 #ifndef OPENSSL_NO_NEXTPROTONEG
2235 namespace {
client_select_next_proto_cb(SSL * ssl,unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)2236 int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
2237                                 unsigned char *outlen, const unsigned char *in,
2238                                 unsigned int inlen, void *arg) {
2239   if (config.verbose) {
2240     print_timer();
2241     std::cout << "[NPN] server offers:" << std::endl;
2242   }
2243   for (unsigned int i = 0; i < inlen; i += in[i] + 1) {
2244     if (config.verbose) {
2245       std::cout << "          * ";
2246       std::cout.write(reinterpret_cast<const char *>(&in[i + 1]), in[i]);
2247       std::cout << std::endl;
2248     }
2249   }
2250   if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
2251                        inlen)) {
2252     print_protocol_nego_error();
2253     return SSL_TLSEXT_ERR_NOACK;
2254   }
2255   return SSL_TLSEXT_ERR_OK;
2256 }
2257 } // namespace
2258 #endif // !OPENSSL_NO_NEXTPROTONEG
2259 
2260 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)2261 int communicate(
2262     const std::string &scheme, const std::string &host, uint16_t port,
2263     std::vector<
2264         std::tuple<std::string, nghttp2_data_provider *, int64_t, int32_t>>
2265         requests,
2266     const nghttp2_session_callbacks *callbacks) {
2267   int result = 0;
2268   auto loop = EV_DEFAULT;
2269   SSL_CTX *ssl_ctx = nullptr;
2270   if (scheme == "https") {
2271     ssl_ctx = SSL_CTX_new(SSLv23_client_method());
2272     if (!ssl_ctx) {
2273       std::cerr << "[ERROR] Failed to create SSL_CTX: "
2274                 << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2275       result = -1;
2276       goto fin;
2277     }
2278 
2279     auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
2280                     SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
2281                     SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
2282 
2283     SSL_CTX_set_options(ssl_ctx, ssl_opts);
2284     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
2285     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
2286 
2287     if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
2288       std::cerr << "[WARNING] Could not load system trusted CA certificates: "
2289                 << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2290     }
2291 
2292     if (nghttp2::tls::ssl_ctx_set_proto_versions(
2293             ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
2294             nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
2295       std::cerr << "[ERROR] Could not set TLS versions" << std::endl;
2296       result = -1;
2297       goto fin;
2298     }
2299 
2300     if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
2301       std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
2302                 << std::endl;
2303       result = -1;
2304       goto fin;
2305     }
2306     if (!config.keyfile.empty()) {
2307       if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(),
2308                                       SSL_FILETYPE_PEM) != 1) {
2309         std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
2310                   << std::endl;
2311         result = -1;
2312         goto fin;
2313       }
2314     }
2315     if (!config.certfile.empty()) {
2316       if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
2317                                              config.certfile.c_str()) != 1) {
2318         std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
2319                   << std::endl;
2320         result = -1;
2321         goto fin;
2322       }
2323     }
2324 #ifndef OPENSSL_NO_NEXTPROTONEG
2325     SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
2326                                      nullptr);
2327 #endif // !OPENSSL_NO_NEXTPROTONEG
2328 
2329 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
2330     auto proto_list = util::get_default_alpn();
2331 
2332     SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
2333 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
2334   }
2335   {
2336     HttpClient client{callbacks, loop, ssl_ctx};
2337 
2338     int32_t dep_stream_id = 0;
2339 
2340     if (!config.no_dep) {
2341       dep_stream_id = anchors[ANCHOR_FOLLOWERS].stream_id;
2342     }
2343 
2344     for (auto &req : requests) {
2345       nghttp2_priority_spec pri_spec;
2346 
2347       nghttp2_priority_spec_init(&pri_spec, dep_stream_id, std::get<3>(req), 0);
2348 
2349       for (int i = 0; i < config.multiply; ++i) {
2350         client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req),
2351                            pri_spec);
2352       }
2353     }
2354     client.update_hostport();
2355 
2356     client.record_start_time();
2357 
2358     if (client.resolve_host(host, port) != 0) {
2359       goto fin;
2360     }
2361 
2362     client.record_domain_lookup_end_time();
2363 
2364     if (client.initiate_connection() != 0) {
2365       std::cerr << "[ERROR] Could not connect to " << host << ", port " << port
2366                 << std::endl;
2367       goto fin;
2368     }
2369 
2370     ev_set_userdata(loop, &client);
2371     ev_run(loop, 0);
2372     ev_set_userdata(loop, nullptr);
2373 
2374 #ifdef HAVE_JANSSON
2375     if (!config.harfile.empty()) {
2376       FILE *outfile;
2377       if (config.harfile == "-") {
2378         outfile = stdout;
2379       } else {
2380         outfile = fopen(config.harfile.c_str(), "wb");
2381       }
2382 
2383       if (outfile) {
2384         client.output_har(outfile);
2385 
2386         if (outfile != stdout) {
2387           fclose(outfile);
2388         }
2389       } else {
2390         std::cerr << "Cannot open file " << config.harfile << ". "
2391                   << "har file could not be created." << std::endl;
2392       }
2393     }
2394 #endif // HAVE_JANSSON
2395 
2396     if (client.success != client.reqvec.size()) {
2397       std::cerr << "Some requests were not processed. total="
2398                 << client.reqvec.size() << ", processed=" << client.success
2399                 << std::endl;
2400     }
2401     if (config.stat) {
2402       print_stats(client);
2403     }
2404   }
2405 fin:
2406   if (ssl_ctx) {
2407     SSL_CTX_free(ssl_ctx);
2408   }
2409   return result;
2410 }
2411 } // namespace
2412 
2413 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)2414 ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
2415                            uint8_t *buf, size_t length, uint32_t *data_flags,
2416                            nghttp2_data_source *source, void *user_data) {
2417   int rv;
2418   auto req = static_cast<Request *>(
2419       nghttp2_session_get_stream_user_data(session, stream_id));
2420   assert(req);
2421   int fd = source->fd;
2422   ssize_t nread;
2423 
2424   while ((nread = pread(fd, buf, length, req->data_offset)) == -1 &&
2425          errno == EINTR)
2426     ;
2427 
2428   if (nread == -1) {
2429     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2430   }
2431 
2432   req->data_offset += nread;
2433 
2434   if (req->data_offset == req->data_length) {
2435     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
2436     if (!config.trailer.empty()) {
2437       std::vector<nghttp2_nv> nva;
2438       nva.reserve(config.trailer.size());
2439       for (auto &kv : config.trailer) {
2440         nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
2441       }
2442       rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
2443       if (rv != 0) {
2444         if (nghttp2_is_fatal(rv)) {
2445           return NGHTTP2_ERR_CALLBACK_FAILURE;
2446         }
2447       } else {
2448         *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
2449       }
2450     }
2451 
2452     return nread;
2453   }
2454 
2455   if (req->data_offset > req->data_length || nread == 0) {
2456     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2457   }
2458 
2459   return nread;
2460 }
2461 } // namespace
2462 
2463 namespace {
run(char ** uris,int n)2464 int run(char **uris, int n) {
2465   nghttp2_session_callbacks *callbacks;
2466 
2467   nghttp2_session_callbacks_new(&callbacks);
2468   auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks);
2469 
2470   nghttp2_session_callbacks_set_on_stream_close_callback(
2471       callbacks, on_stream_close_callback);
2472 
2473   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
2474                                                        on_frame_recv_callback2);
2475 
2476   if (config.verbose) {
2477     nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
2478         callbacks, verbose_on_invalid_frame_recv_callback);
2479 
2480     nghttp2_session_callbacks_set_error_callback2(callbacks,
2481                                                   verbose_error_callback);
2482   }
2483 
2484   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
2485       callbacks, on_data_chunk_recv_callback);
2486 
2487   nghttp2_session_callbacks_set_on_begin_headers_callback(
2488       callbacks, on_begin_headers_callback);
2489 
2490   nghttp2_session_callbacks_set_on_header_callback(callbacks,
2491                                                    on_header_callback);
2492 
2493   nghttp2_session_callbacks_set_before_frame_send_callback(
2494       callbacks, before_frame_send_callback);
2495 
2496   nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
2497                                                        on_frame_send_callback);
2498 
2499   nghttp2_session_callbacks_set_on_frame_not_send_callback(
2500       callbacks, on_frame_not_send_callback);
2501 
2502   if (config.padding) {
2503     nghttp2_session_callbacks_set_select_padding_callback(
2504         callbacks, select_padding_callback);
2505   }
2506 
2507   std::string prev_scheme;
2508   std::string prev_host;
2509   uint16_t prev_port = 0;
2510   int failures = 0;
2511   int data_fd = -1;
2512   nghttp2_data_provider data_prd;
2513   struct stat data_stat;
2514 
2515   if (!config.datafile.empty()) {
2516     if (config.datafile == "-") {
2517       if (fstat(0, &data_stat) == 0 &&
2518           (data_stat.st_mode & S_IFMT) == S_IFREG) {
2519         // use STDIN if it is a regular file
2520         data_fd = 0;
2521       } else {
2522         // copy the contents of STDIN to a temporary file
2523         char tempfn[] = "/tmp/nghttp.temp.XXXXXX";
2524         data_fd = mkstemp(tempfn);
2525         if (data_fd == -1) {
2526           std::cerr << "[ERROR] Could not create a temporary file in /tmp"
2527                     << std::endl;
2528           return 1;
2529         }
2530         if (unlink(tempfn) != 0) {
2531           std::cerr << "[WARNING] failed to unlink temporary file:" << tempfn
2532                     << std::endl;
2533         }
2534         while (1) {
2535           std::array<char, 1_k> buf;
2536           ssize_t rret, wret;
2537           while ((rret = read(0, buf.data(), buf.size())) == -1 &&
2538                  errno == EINTR)
2539             ;
2540           if (rret == 0)
2541             break;
2542           if (rret == -1) {
2543             std::cerr << "[ERROR] I/O error while reading from STDIN"
2544                       << std::endl;
2545             return 1;
2546           }
2547           while ((wret = write(data_fd, buf.data(), rret)) == -1 &&
2548                  errno == EINTR)
2549             ;
2550           if (wret != rret) {
2551             std::cerr << "[ERROR] I/O error while writing to temporary file"
2552                       << std::endl;
2553             return 1;
2554           }
2555         }
2556         if (fstat(data_fd, &data_stat) == -1) {
2557           close(data_fd);
2558           std::cerr << "[ERROR] Could not stat temporary file" << std::endl;
2559           return 1;
2560         }
2561       }
2562     } else {
2563       data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY);
2564       if (data_fd == -1) {
2565         std::cerr << "[ERROR] Could not open file " << config.datafile
2566                   << std::endl;
2567         return 1;
2568       }
2569       if (fstat(data_fd, &data_stat) == -1) {
2570         close(data_fd);
2571         std::cerr << "[ERROR] Could not stat file " << config.datafile
2572                   << std::endl;
2573         return 1;
2574       }
2575     }
2576     data_prd.source.fd = data_fd;
2577     data_prd.read_callback = file_read_callback;
2578   }
2579   std::vector<
2580       std::tuple<std::string, nghttp2_data_provider *, int64_t, int32_t>>
2581       requests;
2582 
2583   size_t next_weight_idx = 0;
2584 
2585   for (int i = 0; i < n; ++i) {
2586     http_parser_url u{};
2587     auto uri = strip_fragment(uris[i]);
2588     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
2589       ++next_weight_idx;
2590       std::cerr << "[ERROR] Could not parse URI " << uri << std::endl;
2591       continue;
2592     }
2593     if (!util::has_uri_field(u, UF_SCHEMA)) {
2594       ++next_weight_idx;
2595       std::cerr << "[ERROR] URI " << uri << " does not have scheme part"
2596                 << std::endl;
2597       continue;
2598     }
2599     auto port = util::has_uri_field(u, UF_PORT)
2600                     ? u.port
2601                     : util::get_default_port(uri.c_str(), u);
2602     auto host = decode_host(util::get_uri_field(uri.c_str(), u, UF_HOST));
2603     if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) ||
2604         host != prev_host || port != prev_port) {
2605       if (!requests.empty()) {
2606         if (communicate(prev_scheme, prev_host, prev_port, std::move(requests),
2607                         callbacks) != 0) {
2608           ++failures;
2609         }
2610         requests.clear();
2611       }
2612       prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA).str();
2613       prev_host = std::move(host);
2614       prev_port = port;
2615     }
2616     requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd,
2617                           data_stat.st_size, config.weight[next_weight_idx++]);
2618   }
2619   if (!requests.empty()) {
2620     if (communicate(prev_scheme, prev_host, prev_port, std::move(requests),
2621                     callbacks) != 0) {
2622       ++failures;
2623     }
2624   }
2625   return failures;
2626 }
2627 } // namespace
2628 
2629 namespace {
print_version(std::ostream & out)2630 void print_version(std::ostream &out) {
2631   out << "nghttp nghttp2/" NGHTTP2_VERSION << std::endl;
2632 }
2633 } // namespace
2634 
2635 namespace {
print_usage(std::ostream & out)2636 void print_usage(std::ostream &out) {
2637   out << R"(Usage: nghttp [OPTIONS]... <URI>...
2638 HTTP/2 client)"
2639       << std::endl;
2640 }
2641 } // namespace
2642 
2643 namespace {
print_help(std::ostream & out)2644 void print_help(std::ostream &out) {
2645   print_usage(out);
2646   out << R"(
2647   <URI>       Specify URI to access.
2648 Options:
2649   -v, --verbose
2650               Print   debug   information   such  as   reception   and
2651               transmission of frames and name/value pairs.  Specifying
2652               this option multiple times increases verbosity.
2653   -n, --null-out
2654               Discard downloaded data.
2655   -O, --remote-name
2656               Save  download  data  in  the  current  directory.   The
2657               filename is  derived from  URI.  If  URI ends  with '/',
2658               'index.html'  is used  as a  filename.  Not  implemented
2659               yet.
2660   -t, --timeout=<DURATION>
2661               Timeout each request after <DURATION>.  Set 0 to disable
2662               timeout.
2663   -w, --window-bits=<N>
2664               Sets the stream level initial window size to 2**<N>-1.
2665   -W, --connection-window-bits=<N>
2666               Sets  the  connection  level   initial  window  size  to
2667               2**<N>-1.
2668   -a, --get-assets
2669               Download assets  such as stylesheets, images  and script
2670               files linked  from the downloaded resource.   Only links
2671               whose  origins are  the same  with the  linking resource
2672               will be downloaded.   nghttp prioritizes resources using
2673               HTTP/2 dependency  based priority.  The  priority order,
2674               from highest to lowest,  is html itself, css, javascript
2675               and images.
2676   -s, --stat  Print statistics.
2677   -H, --header=<HEADER>
2678               Add a header to the requests.  Example: -H':method: PUT'
2679   --trailer=<HEADER>
2680               Add a trailer header to the requests.  <HEADER> must not
2681               include pseudo header field  (header field name starting
2682               with ':').  To  send trailer, one must use  -d option to
2683               send request body.  Example: --trailer 'foo: bar'.
2684   --cert=<CERT>
2685               Use  the specified  client certificate  file.  The  file
2686               must be in PEM format.
2687   --key=<KEY> Use the  client private key  file.  The file must  be in
2688               PEM format.
2689   -d, --data=<PATH>
2690               Post FILE to server. If '-'  is given, data will be read
2691               from stdin.
2692   -m, --multiply=<N>
2693               Request each URI <N> times.  By default, same URI is not
2694               requested twice.  This option disables it too.
2695   -u, --upgrade
2696               Perform HTTP Upgrade for HTTP/2.  This option is ignored
2697               if the request URI has https scheme.  If -d is used, the
2698               HTTP upgrade request is performed with OPTIONS method.
2699   -p, --weight=<WEIGHT>
2700               Sets  weight of  given  URI.  This  option  can be  used
2701               multiple times, and  N-th -p option sets  weight of N-th
2702               URI in the command line.  If  the number of -p option is
2703               less than the number of URI, the last -p option value is
2704               repeated.  If there is no -p option, default weight, 16,
2705               is assumed.  The valid value range is
2706               [)"
2707       << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT << R"(], inclusive.
2708   -M, --peer-max-concurrent-streams=<N>
2709               Use  <N>  as  SETTINGS_MAX_CONCURRENT_STREAMS  value  of
2710               remote endpoint as if it  is received in SETTINGS frame.
2711               Default: 100
2712   -c, --header-table-size=<SIZE>
2713               Specify decoder  header table  size.  If this  option is
2714               used  multiple times,  and the  minimum value  among the
2715               given values except  for last one is  strictly less than
2716               the last  value, that minimum  value is set  in SETTINGS
2717               frame  payload  before  the   last  value,  to  simulate
2718               multiple header table size change.
2719   --encoder-header-table-size=<SIZE>
2720               Specify encoder header table size.  The decoder (server)
2721               specifies  the maximum  dynamic table  size it  accepts.
2722               Then the negotiated dynamic table size is the minimum of
2723               this option value and the value which server specified.
2724   -b, --padding=<N>
2725               Add at  most <N>  bytes to a  frame payload  as padding.
2726               Specify 0 to disable padding.
2727   -r, --har=<PATH>
2728               Output HTTP  transactions <PATH> in HAR  format.  If '-'
2729               is given, data is written to stdout.
2730   --color     Force colored log output.
2731   --continuation
2732               Send large header to test CONTINUATION.
2733   --no-content-length
2734               Don't send content-length header field.
2735   --no-dep    Don't send dependency based priority hint to server.
2736   --hexdump   Display the  incoming traffic in  hexadecimal (Canonical
2737               hex+ASCII display).  If SSL/TLS  is used, decrypted data
2738               are used.
2739   --no-push   Disable server push.
2740   --max-concurrent-streams=<N>
2741               The  number of  concurrent  pushed  streams this  client
2742               accepts.
2743   --expect-continue
2744               Perform an Expect/Continue handshake:  wait to send DATA
2745               (up to  a short  timeout)  until the server sends  a 100
2746               Continue interim response. This option is ignored unless
2747               combined with the -d option.
2748   -y, --no-verify-peer
2749               Suppress  warning  on  server  certificate  verification
2750               failure.
2751   --version   Display version information and exit.
2752   -h, --help  Display this help and exit.
2753 
2754 --
2755 
2756   The <SIZE> argument is an integer and an optional unit (e.g., 10K is
2757   10 * 1024).  Units are K, M and G (powers of 1024).
2758 
2759   The <DURATION> argument is an integer and an optional unit (e.g., 1s
2760   is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
2761   (hours, minutes, seconds and milliseconds, respectively).  If a unit
2762   is omitted, a second is used as unit.)"
2763       << std::endl;
2764 }
2765 } // namespace
2766 
main(int argc,char ** argv)2767 int main(int argc, char **argv) {
2768   tls::libssl_init();
2769 
2770   bool color = false;
2771   while (1) {
2772     static int flag = 0;
2773     constexpr static option long_options[] = {
2774         {"verbose", no_argument, nullptr, 'v'},
2775         {"null-out", no_argument, nullptr, 'n'},
2776         {"remote-name", no_argument, nullptr, 'O'},
2777         {"timeout", required_argument, nullptr, 't'},
2778         {"window-bits", required_argument, nullptr, 'w'},
2779         {"connection-window-bits", required_argument, nullptr, 'W'},
2780         {"get-assets", no_argument, nullptr, 'a'},
2781         {"stat", no_argument, nullptr, 's'},
2782         {"help", no_argument, nullptr, 'h'},
2783         {"header", required_argument, nullptr, 'H'},
2784         {"data", required_argument, nullptr, 'd'},
2785         {"multiply", required_argument, nullptr, 'm'},
2786         {"upgrade", no_argument, nullptr, 'u'},
2787         {"weight", required_argument, nullptr, 'p'},
2788         {"peer-max-concurrent-streams", required_argument, nullptr, 'M'},
2789         {"header-table-size", required_argument, nullptr, 'c'},
2790         {"padding", required_argument, nullptr, 'b'},
2791         {"har", required_argument, nullptr, 'r'},
2792         {"no-verify-peer", no_argument, nullptr, 'y'},
2793         {"cert", required_argument, &flag, 1},
2794         {"key", required_argument, &flag, 2},
2795         {"color", no_argument, &flag, 3},
2796         {"continuation", no_argument, &flag, 4},
2797         {"version", no_argument, &flag, 5},
2798         {"no-content-length", no_argument, &flag, 6},
2799         {"no-dep", no_argument, &flag, 7},
2800         {"trailer", required_argument, &flag, 9},
2801         {"hexdump", no_argument, &flag, 10},
2802         {"no-push", no_argument, &flag, 11},
2803         {"max-concurrent-streams", required_argument, &flag, 12},
2804         {"expect-continue", no_argument, &flag, 13},
2805         {"encoder-header-table-size", required_argument, &flag, 14},
2806         {nullptr, 0, nullptr, 0}};
2807     int option_index = 0;
2808     int c =
2809         getopt_long(argc, argv, "M:Oab:c:d:m:np:r:hH:vst:uw:yW:", long_options,
2810                     &option_index);
2811     if (c == -1) {
2812       break;
2813     }
2814     switch (c) {
2815     case 'M':
2816       // peer-max-concurrent-streams option
2817       config.peer_max_concurrent_streams = strtoul(optarg, nullptr, 10);
2818       break;
2819     case 'O':
2820       config.remote_name = true;
2821       break;
2822     case 'h':
2823       print_help(std::cout);
2824       exit(EXIT_SUCCESS);
2825     case 'b':
2826       config.padding = strtol(optarg, nullptr, 10);
2827       break;
2828     case 'n':
2829       config.null_out = true;
2830       break;
2831     case 'p': {
2832       errno = 0;
2833       auto n = strtoul(optarg, nullptr, 10);
2834       if (errno == 0 && NGHTTP2_MIN_WEIGHT <= n && n <= NGHTTP2_MAX_WEIGHT) {
2835         config.weight.push_back(n);
2836       } else {
2837         std::cerr << "-p: specify the integer in the range ["
2838                   << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT
2839                   << "], inclusive" << std::endl;
2840         exit(EXIT_FAILURE);
2841       }
2842       break;
2843     }
2844     case 'r':
2845 #ifdef HAVE_JANSSON
2846       config.harfile = optarg;
2847 #else  // !HAVE_JANSSON
2848       std::cerr << "[WARNING]: -r, --har option is ignored because\n"
2849                 << "the binary was not compiled with libjansson." << std::endl;
2850 #endif // !HAVE_JANSSON
2851       break;
2852     case 'v':
2853       ++config.verbose;
2854       break;
2855     case 't':
2856       config.timeout = util::parse_duration_with_unit(optarg);
2857       if (config.timeout == std::numeric_limits<double>::infinity()) {
2858         std::cerr << "-t: bad timeout value: " << optarg << std::endl;
2859         exit(EXIT_FAILURE);
2860       }
2861       break;
2862     case 'u':
2863       config.upgrade = true;
2864       break;
2865     case 'w':
2866     case 'W': {
2867       errno = 0;
2868       char *endptr = nullptr;
2869       unsigned long int n = strtoul(optarg, &endptr, 10);
2870       if (errno == 0 && *endptr == '\0' && n < 31) {
2871         if (c == 'w') {
2872           config.window_bits = n;
2873         } else {
2874           config.connection_window_bits = n;
2875         }
2876       } else {
2877         std::cerr << "-" << static_cast<char>(c)
2878                   << ": specify the integer in the range [0, 30], inclusive"
2879                   << std::endl;
2880         exit(EXIT_FAILURE);
2881       }
2882       break;
2883     }
2884     case 'H': {
2885       char *header = optarg;
2886       // Skip first possible ':' in the header name
2887       char *value = strchr(optarg + 1, ':');
2888       if (!value || (header[0] == ':' && header + 1 == value)) {
2889         std::cerr << "-H: invalid header: " << optarg << std::endl;
2890         exit(EXIT_FAILURE);
2891       }
2892       *value = 0;
2893       value++;
2894       while (isspace(*value)) {
2895         value++;
2896       }
2897       if (*value == 0) {
2898         // This could also be a valid case for suppressing a header
2899         // similar to curl
2900         std::cerr << "-H: invalid header - value missing: " << optarg
2901                   << std::endl;
2902         exit(EXIT_FAILURE);
2903       }
2904       config.headers.emplace_back(header, value, false);
2905       util::inp_strlower(config.headers.back().name);
2906       break;
2907     }
2908     case 'a':
2909 #ifdef HAVE_LIBXML2
2910       config.get_assets = true;
2911 #else  // !HAVE_LIBXML2
2912       std::cerr << "[WARNING]: -a, --get-assets option is ignored because\n"
2913                 << "the binary was not compiled with libxml2." << std::endl;
2914 #endif // !HAVE_LIBXML2
2915       break;
2916     case 's':
2917       config.stat = true;
2918       break;
2919     case 'd':
2920       config.datafile = optarg;
2921       break;
2922     case 'm':
2923       config.multiply = strtoul(optarg, nullptr, 10);
2924       break;
2925     case 'c': {
2926       auto n = util::parse_uint_with_unit(optarg);
2927       if (n == -1) {
2928         std::cerr << "-c: Bad option value: " << optarg << std::endl;
2929         exit(EXIT_FAILURE);
2930       }
2931       if (n > std::numeric_limits<uint32_t>::max()) {
2932         std::cerr << "-c: Value too large.  It should be less than or equal to "
2933                   << std::numeric_limits<uint32_t>::max() << std::endl;
2934         exit(EXIT_FAILURE);
2935       }
2936       config.header_table_size = n;
2937       config.min_header_table_size = std::min(config.min_header_table_size, n);
2938       break;
2939     }
2940     case 'y':
2941       config.verify_peer = false;
2942       break;
2943     case '?':
2944       util::show_candidates(argv[optind - 1], long_options);
2945       exit(EXIT_FAILURE);
2946     case 0:
2947       switch (flag) {
2948       case 1:
2949         // cert option
2950         config.certfile = optarg;
2951         break;
2952       case 2:
2953         // key option
2954         config.keyfile = optarg;
2955         break;
2956       case 3:
2957         // color option
2958         color = true;
2959         break;
2960       case 4:
2961         // continuation option
2962         config.continuation = true;
2963         break;
2964       case 5:
2965         // version option
2966         print_version(std::cout);
2967         exit(EXIT_SUCCESS);
2968       case 6:
2969         // no-content-length option
2970         config.no_content_length = true;
2971         break;
2972       case 7:
2973         // no-dep option
2974         config.no_dep = true;
2975         break;
2976       case 9: {
2977         // trailer option
2978         auto header = optarg;
2979         auto value = strchr(optarg, ':');
2980         if (!value) {
2981           std::cerr << "--trailer: invalid header: " << optarg << std::endl;
2982           exit(EXIT_FAILURE);
2983         }
2984         *value = 0;
2985         value++;
2986         while (isspace(*value)) {
2987           value++;
2988         }
2989         if (*value == 0) {
2990           // This could also be a valid case for suppressing a header
2991           // similar to curl
2992           std::cerr << "--trailer: invalid header - value missing: " << optarg
2993                     << std::endl;
2994           exit(EXIT_FAILURE);
2995         }
2996         config.trailer.emplace_back(header, value, false);
2997         util::inp_strlower(config.trailer.back().name);
2998         break;
2999       }
3000       case 10:
3001         // hexdump option
3002         config.hexdump = true;
3003         break;
3004       case 11:
3005         // no-push option
3006         config.no_push = true;
3007         break;
3008       case 12:
3009         // max-concurrent-streams option
3010         config.max_concurrent_streams = strtoul(optarg, nullptr, 10);
3011         break;
3012       case 13:
3013         // expect-continue option
3014         config.expect_continue = true;
3015         break;
3016       case 14: {
3017         // encoder-header-table-size option
3018         auto n = util::parse_uint_with_unit(optarg);
3019         if (n == -1) {
3020           std::cerr << "--encoder-header-table-size: Bad option value: "
3021                     << optarg << std::endl;
3022           exit(EXIT_FAILURE);
3023         }
3024         if (n > std::numeric_limits<uint32_t>::max()) {
3025           std::cerr << "--encoder-header-table-size: Value too large.  It "
3026                        "should be less than or equal to "
3027                     << std::numeric_limits<uint32_t>::max() << std::endl;
3028           exit(EXIT_FAILURE);
3029         }
3030         config.encoder_header_table_size = n;
3031         break;
3032       }
3033       }
3034       break;
3035     default:
3036       break;
3037     }
3038   }
3039 
3040   int32_t weight_to_fill;
3041   if (config.weight.empty()) {
3042     weight_to_fill = NGHTTP2_DEFAULT_WEIGHT;
3043   } else {
3044     weight_to_fill = config.weight.back();
3045   }
3046   config.weight.insert(std::end(config.weight), argc - optind, weight_to_fill);
3047 
3048   // Find scheme overridden by extra header fields.
3049   auto scheme_it =
3050       std::find_if(std::begin(config.headers), std::end(config.headers),
3051                    [](const Header &nv) { return nv.name == ":scheme"; });
3052   if (scheme_it != std::end(config.headers)) {
3053     config.scheme_override = (*scheme_it).value;
3054   }
3055 
3056   // Find host and port overridden by extra header fields.
3057   auto authority_it =
3058       std::find_if(std::begin(config.headers), std::end(config.headers),
3059                    [](const Header &nv) { return nv.name == ":authority"; });
3060   if (authority_it == std::end(config.headers)) {
3061     authority_it =
3062         std::find_if(std::begin(config.headers), std::end(config.headers),
3063                      [](const Header &nv) { return nv.name == "host"; });
3064   }
3065 
3066   if (authority_it != std::end(config.headers)) {
3067     // authority_it may looks like "host:port".
3068     auto uri = "https://" + (*authority_it).value;
3069     http_parser_url u{};
3070     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
3071       std::cerr << "[ERROR] Could not parse authority in "
3072                 << (*authority_it).name << ": " << (*authority_it).value
3073                 << std::endl;
3074       exit(EXIT_FAILURE);
3075     }
3076 
3077     config.host_override = util::get_uri_field(uri.c_str(), u, UF_HOST).str();
3078     if (util::has_uri_field(u, UF_PORT)) {
3079       config.port_override = u.port;
3080     }
3081   }
3082 
3083   set_color_output(color || isatty(fileno(stdout)));
3084 
3085   nghttp2_option_set_peer_max_concurrent_streams(
3086       config.http2_option, config.peer_max_concurrent_streams);
3087 
3088   if (config.encoder_header_table_size != -1) {
3089     nghttp2_option_set_max_deflate_dynamic_table_size(
3090         config.http2_option, config.encoder_header_table_size);
3091   }
3092 
3093   struct sigaction act {};
3094   act.sa_handler = SIG_IGN;
3095   sigaction(SIGPIPE, &act, nullptr);
3096   reset_timer();
3097   return run(argv + optind, argc - optind);
3098 }
3099 
3100 } // namespace nghttp2
3101 
main(int argc,char ** argv)3102 int main(int argc, char **argv) {
3103   return nghttp2::run_app(nghttp2::main, argc, argv);
3104 }
3105