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