tizen 2.4 release
[external/nghttp2.git] / src / nghttp.cc
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 #include <unistd.h>
29 #include <fcntl.h>
30 #include <netinet/in.h>
31 #include <netinet/tcp.h>
32 #include <getopt.h>
33
34 #include <cassert>
35 #include <cstdio>
36 #include <cerrno>
37 #include <cstdlib>
38 #include <cstring>
39 #include <iostream>
40 #include <iomanip>
41 #include <sstream>
42 #include <tuple>
43
44 #include <openssl/err.h>
45 #include <openssl/conf.h>
46
47 #ifdef HAVE_JANSSON
48 #include <jansson.h>
49 #endif // HAVE_JANSSON
50
51 #include "app_helper.h"
52 #include "HtmlParser.h"
53 #include "util.h"
54 #include "base64.h"
55 #include "ssl.h"
56 #include "template.h"
57
58 #ifndef O_BINARY
59 #define O_BINARY (0)
60 #endif // O_BINARY
61
62 namespace nghttp2 {
63
64 // stream ID of anchor stream node when --dep-idle is enabled.  These
65 // * portion of ANCHOR_ID_* matches RequestPriority in HtmlParser.h.
66 // The stream ID = 1 is excluded since it is used as first stream in
67 // upgrade case.
68 enum {
69   ANCHOR_ID_HIGH = 3,
70   ANCHOR_ID_MEDIUM = 5,
71   ANCHOR_ID_LOW = 7,
72   ANCHOR_ID_LOWEST = 9,
73 };
74
75 Config::Config()
76     : output_upper_thres(1024 * 1024), padding(0),
77       peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS),
78       header_table_size(-1), weight(NGHTTP2_DEFAULT_WEIGHT), multiply(1),
79       timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0),
80       null_out(false), remote_name(false), get_assets(false), stat(false),
81       upgrade(false), continuation(false), no_content_length(false),
82       no_dep(false), dep_idle(false) {
83   nghttp2_option_new(&http2_option);
84   nghttp2_option_set_peer_max_concurrent_streams(http2_option,
85                                                  peer_max_concurrent_streams);
86 }
87
88 Config::~Config() { nghttp2_option_del(http2_option); }
89
90 namespace {
91 Config config;
92 } // namespace
93
94 namespace {
95 void print_protocol_nego_error() {
96   std::cerr << "[ERROR] HTTP/2 protocol was not selected."
97             << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")"
98             << std::endl;
99 }
100 } // namespace
101
102 namespace {
103 std::string strip_fragment(const char *raw_uri) {
104   const char *end;
105   for (end = raw_uri; *end && *end != '#'; ++end)
106     ;
107   size_t len = end - raw_uri;
108   return std::string(raw_uri, len);
109 }
110 } // namespace
111
112 namespace {
113 // Returns numeric address string of |addr|.  If getnameinfo() is
114 // failed, "unknown" is returned.
115 std::string numeric_name(addrinfo *addr) {
116   std::array<char, NI_MAXHOST> host;
117   auto rv = getnameinfo(addr->ai_addr, addr->ai_addrlen, host.data(),
118                         host.size(), nullptr, 0, NI_NUMERICHOST);
119   if (rv != 0) {
120     return "unknown";
121   }
122   return host.data();
123 }
124 } // namespace
125
126 Request::Request(const std::string &uri, const http_parser_url &u,
127                  const nghttp2_data_provider *data_prd, int64_t data_length,
128                  const nghttp2_priority_spec &pri_spec,
129                  std::shared_ptr<Dependency> dep, int pri, int level)
130     : uri(uri), u(u), dep(std::move(dep)), pri_spec(pri_spec),
131       data_length(data_length), data_offset(0), response_len(0),
132       inflater(nullptr), html_parser(nullptr), data_prd(data_prd),
133       stream_id(-1), status(0), level(level), pri(pri),
134       expect_final_response(false) {
135   http2::init_hdidx(res_hdidx);
136   http2::init_hdidx(req_hdidx);
137 }
138
139 Request::~Request() {
140   nghttp2_gzip_inflate_del(inflater);
141   delete html_parser;
142 }
143
144 void Request::init_inflater() {
145   int rv;
146   rv = nghttp2_gzip_inflate_new(&inflater);
147   assert(rv == 0);
148 }
149
150 void Request::init_html_parser() { html_parser = new HtmlParser(uri); }
151
152 int Request::update_html_parser(const uint8_t *data, size_t len, int fin) {
153   if (!html_parser) {
154     return 0;
155   }
156   return html_parser->parse_chunk(reinterpret_cast<const char *>(data), len,
157                                   fin);
158 }
159
160 std::string Request::make_reqpath() const {
161   std::string path = util::has_uri_field(u, UF_PATH)
162                          ? util::get_uri_field(uri.c_str(), u, UF_PATH)
163                          : "/";
164   if (util::has_uri_field(u, UF_QUERY)) {
165     path += "?";
166     path.append(uri.c_str() + u.field_data[UF_QUERY].off,
167                 u.field_data[UF_QUERY].len);
168   }
169   return path;
170 }
171
172 int32_t Request::find_dep_stream_id(int start) {
173   for (auto i = start; i >= 0; --i) {
174     for (auto req : dep->deps[i]) {
175       return req->stream_id;
176     }
177   }
178   return -1;
179 }
180
181 nghttp2_priority_spec Request::resolve_dep(int32_t pri) {
182   nghttp2_priority_spec pri_spec;
183   int exclusive = 0;
184   int32_t stream_id = -1;
185
186   nghttp2_priority_spec_default_init(&pri_spec);
187
188   if (config.no_dep) {
189     return pri_spec;
190   }
191
192   if (config.dep_idle) {
193     int32_t anchor_id = 0;
194     switch (pri) {
195     case REQ_PRI_HIGH:
196       anchor_id = ANCHOR_ID_HIGH;
197       break;
198     case REQ_PRI_MEDIUM:
199       anchor_id = ANCHOR_ID_MEDIUM;
200       break;
201     case REQ_PRI_LOW:
202       anchor_id = ANCHOR_ID_LOW;
203       break;
204     case REQ_PRI_LOWEST:
205       anchor_id = ANCHOR_ID_LOWEST;
206       break;
207     }
208     nghttp2_priority_spec_init(&pri_spec, anchor_id, NGHTTP2_DEFAULT_WEIGHT, 0);
209     return pri_spec;
210   }
211
212   if (pri == 0) {
213     return pri_spec;
214   }
215
216   auto start = std::min(pri, (int)dep->deps.size() - 1);
217
218   for (auto i = start; i >= 0; --i) {
219     if (dep->deps[i][0]->pri < pri) {
220       stream_id = find_dep_stream_id(i);
221
222       if (i != (int)dep->deps.size() - 1) {
223         exclusive = 1;
224       }
225
226       break;
227     } else if (dep->deps[i][0]->pri == pri) {
228       stream_id = find_dep_stream_id(i - 1);
229
230       break;
231     }
232   }
233
234   if (stream_id == -1) {
235     return pri_spec;
236   }
237
238   nghttp2_priority_spec_init(&pri_spec, stream_id, NGHTTP2_DEFAULT_WEIGHT,
239                              exclusive);
240
241   return pri_spec;
242 }
243
244 bool Request::is_ipv6_literal_addr() const {
245   if (util::has_uri_field(u, UF_HOST)) {
246     return memchr(uri.c_str() + u.field_data[UF_HOST].off, ':',
247                   u.field_data[UF_HOST].len);
248   } else {
249     return false;
250   }
251 }
252
253 bool Request::response_pseudo_header_allowed(int16_t token) const {
254   if (!res_nva.empty() && res_nva.back().name.c_str()[0] != ':') {
255     return false;
256   }
257   switch (token) {
258   case http2::HD__STATUS:
259     return res_hdidx[token] == -1;
260   default:
261     return false;
262   }
263 }
264
265 bool Request::push_request_pseudo_header_allowed(int16_t token) const {
266   if (!req_nva.empty() && req_nva.back().name.c_str()[0] != ':') {
267     return false;
268   }
269   switch (token) {
270   case http2::HD__AUTHORITY:
271   case http2::HD__METHOD:
272   case http2::HD__PATH:
273   case http2::HD__SCHEME:
274     return req_hdidx[token] == -1;
275   default:
276     return false;
277   }
278 }
279
280 Headers::value_type *Request::get_res_header(int16_t token) {
281   auto idx = res_hdidx[token];
282   if (idx == -1) {
283     return nullptr;
284   }
285   return &res_nva[idx];
286 }
287
288 Headers::value_type *Request::get_req_header(int16_t token) {
289   auto idx = req_hdidx[token];
290   if (idx == -1) {
291     return nullptr;
292   }
293   return &req_nva[idx];
294 }
295
296 void Request::record_request_time() {
297   timing.state = RequestState::ON_REQUEST;
298   timing.on_request_time = get_time();
299 }
300
301 void Request::record_response_time() {
302   timing.state = RequestState::ON_RESPONSE;
303   timing.on_response_time = get_time();
304 }
305
306 void Request::record_complete_time() {
307   timing.state = RequestState::ON_COMPLETE;
308   timing.on_complete_time = get_time();
309 }
310
311 namespace {
312 int htp_msg_begincb(http_parser *htp) {
313   if (config.verbose) {
314     print_timer();
315     std::cout << " HTTP Upgrade response" << std::endl;
316   }
317   return 0;
318 }
319 } // namespace
320
321 namespace {
322 int htp_statuscb(http_parser *htp, const char *at, size_t length) {
323   auto client = static_cast<HttpClient *>(htp->data);
324   client->upgrade_response_status_code = htp->status_code;
325   return 0;
326 }
327 } // namespace
328
329 namespace {
330 int htp_msg_completecb(http_parser *htp) {
331   auto client = static_cast<HttpClient *>(htp->data);
332   client->upgrade_response_complete = true;
333   return 0;
334 }
335 } // namespace
336
337 namespace {
338 http_parser_settings htp_hooks = {
339     htp_msg_begincb,   // http_cb      on_message_begin;
340     nullptr,           // http_data_cb on_url;
341     htp_statuscb,      // http_data_cb on_status;
342     nullptr,           // http_data_cb on_header_field;
343     nullptr,           // http_data_cb on_header_value;
344     nullptr,           // http_cb      on_headers_complete;
345     nullptr,           // http_data_cb on_body;
346     htp_msg_completecb // http_cb      on_message_complete;
347 };
348 } // namespace
349
350 namespace {
351 int submit_request(HttpClient *client, const Headers &headers, Request *req) {
352   auto path = req->make_reqpath();
353   auto scheme = util::get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA);
354   auto build_headers = Headers{{":method", req->data_prd ? "POST" : "GET"},
355                                {":path", path},
356                                {":scheme", scheme},
357                                {":authority", client->hostport},
358                                {"accept", "*/*"},
359                                {"accept-encoding", "gzip, deflate"},
360                                {"user-agent", "nghttp2/" NGHTTP2_VERSION}};
361   if (config.continuation) {
362     for (size_t i = 0; i < 6; ++i) {
363       build_headers.emplace_back("continuation-test-" + util::utos(i + 1),
364                                  std::string(4096, '-'));
365     }
366   }
367   auto num_initial_headers = build_headers.size();
368   if (!config.no_content_length && req->data_prd) {
369     build_headers.emplace_back("content-length", util::utos(req->data_length));
370   }
371   for (auto &kv : headers) {
372     size_t i;
373     for (i = 0; i < num_initial_headers; ++i) {
374       if (kv.name == build_headers[i].name) {
375         build_headers[i].value = kv.value;
376         break;
377       }
378     }
379     if (i < num_initial_headers) {
380       continue;
381     }
382
383     build_headers.emplace_back(kv.name, kv.value, kv.no_index);
384   }
385
386   auto nva = std::vector<nghttp2_nv>();
387   nva.reserve(build_headers.size());
388
389   for (auto &kv : build_headers) {
390     nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
391   }
392
393   auto stream_id =
394       nghttp2_submit_request(client->session, &req->pri_spec, nva.data(),
395                              nva.size(), req->data_prd, req);
396   if (stream_id < 0) {
397     std::cerr << "[ERROR] nghttp2_submit_request() returned error: "
398               << nghttp2_strerror(stream_id) << std::endl;
399     return -1;
400   }
401
402   req->stream_id = stream_id;
403   client->on_request(req);
404
405   req->req_nva = std::move(build_headers);
406
407   return 0;
408 }
409 } // namespace
410
411 namespace {
412 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
413   auto client = static_cast<HttpClient *>(w->data);
414   if (client->do_read() != 0) {
415     client->disconnect();
416   }
417 }
418 } // namespace
419
420 namespace {
421 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
422   auto client = static_cast<HttpClient *>(w->data);
423   auto rv = client->do_write();
424   if (rv == HttpClient::ERR_CONNECT_FAIL) {
425     client->on_connect_fail();
426     return;
427   }
428   if (rv != 0) {
429     client->disconnect();
430   }
431 }
432 } // namespace
433
434 namespace {
435 void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
436   auto client = static_cast<HttpClient *>(w->data);
437   std::cerr << "[ERROR] Timeout" << std::endl;
438   client->disconnect();
439 }
440 } // namespace
441
442 namespace {
443 void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
444   auto client = static_cast<HttpClient *>(w->data);
445   ev_timer_stop(loop, w);
446
447   nghttp2_session_terminate_session(client->session, NGHTTP2_SETTINGS_TIMEOUT);
448
449   client->signal_write();
450 }
451 } // namespace
452
453 HttpClient::HttpClient(const nghttp2_session_callbacks *callbacks,
454                        struct ev_loop *loop, SSL_CTX *ssl_ctx)
455     : session(nullptr), callbacks(callbacks), loop(loop), ssl_ctx(ssl_ctx),
456       ssl(nullptr), addrs(nullptr), next_addr(nullptr), cur_addr(nullptr),
457       complete(0), settings_payloadlen(0), state(ClientState::IDLE),
458       upgrade_response_status_code(0), fd(-1),
459       upgrade_response_complete(false) {
460   ev_io_init(&wev, writecb, 0, EV_WRITE);
461   ev_io_init(&rev, readcb, 0, EV_READ);
462
463   wev.data = this;
464   rev.data = this;
465
466   ev_timer_init(&wt, timeoutcb, 0., config.timeout);
467   ev_timer_init(&rt, timeoutcb, 0., config.timeout);
468
469   wt.data = this;
470   rt.data = this;
471
472   ev_timer_init(&settings_timer, settings_timeout_cb, 0., 10.);
473
474   settings_timer.data = this;
475 }
476
477 HttpClient::~HttpClient() {
478   disconnect();
479
480   if (addrs) {
481     freeaddrinfo(addrs);
482     addrs = nullptr;
483     next_addr = nullptr;
484   }
485 }
486
487 bool HttpClient::need_upgrade() const {
488   return config.upgrade && scheme == "http";
489 }
490
491 int HttpClient::resolve_host(const std::string &host, uint16_t port) {
492   int rv;
493   addrinfo hints;
494   this->host = host;
495   memset(&hints, 0, sizeof(hints));
496   hints.ai_family = AF_UNSPEC;
497   hints.ai_socktype = SOCK_STREAM;
498   hints.ai_protocol = 0;
499   hints.ai_flags = AI_ADDRCONFIG;
500   rv = getaddrinfo(host.c_str(), util::utos(port).c_str(), &hints, &addrs);
501   if (rv != 0) {
502     std::cerr << "[ERROR] getaddrinfo() failed: " << gai_strerror(rv)
503               << std::endl;
504     return -1;
505   }
506   if (addrs == nullptr) {
507     std::cerr << "[ERROR] No address returned" << std::endl;
508     return -1;
509   }
510   next_addr = addrs;
511   return 0;
512 }
513
514 int HttpClient::initiate_connection() {
515   int rv;
516
517   cur_addr = nullptr;
518   while (next_addr) {
519     cur_addr = next_addr;
520     next_addr = next_addr->ai_next;
521     fd = util::create_nonblock_socket(cur_addr->ai_family);
522     if (fd == -1) {
523       continue;
524     }
525
526     if (ssl_ctx) {
527       // We are establishing TLS connection.
528       ssl = SSL_new(ssl_ctx);
529       if (!ssl) {
530         std::cerr << "[ERROR] SSL_new() failed: "
531                   << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
532         return -1;
533       }
534
535       SSL_set_fd(ssl, fd);
536       SSL_set_connect_state(ssl);
537
538       // If the user overrode the host header, use that value for
539       // the SNI extension
540       const char *host_string = nullptr;
541       auto i =
542           std::find_if(std::begin(config.headers), std::end(config.headers),
543                        [](const Header &nv) { return "host" == nv.name; });
544       if (i != std::end(config.headers)) {
545         host_string = (*i).value.c_str();
546       } else {
547         host_string = host.c_str();
548       }
549
550       if (!util::numeric_host(host_string)) {
551         SSL_set_tlsext_host_name(ssl, host_string);
552       }
553     }
554
555     rv = connect(fd, cur_addr->ai_addr, cur_addr->ai_addrlen);
556
557     if (rv != 0 && errno != EINPROGRESS) {
558       if (ssl) {
559         SSL_free(ssl);
560         ssl = nullptr;
561       }
562       close(fd);
563       fd = -1;
564       continue;
565     }
566     break;
567   }
568
569   if (fd == -1) {
570     return -1;
571   }
572
573   writefn = &HttpClient::connected;
574
575   if (need_upgrade()) {
576     on_readfn = &HttpClient::on_upgrade_read;
577     on_writefn = &HttpClient::on_upgrade_connect;
578   } else {
579     on_readfn = &HttpClient::on_read;
580     on_writefn = &HttpClient::on_write;
581   }
582
583   ev_io_set(&rev, fd, EV_READ);
584   ev_io_set(&wev, fd, EV_WRITE);
585
586   ev_io_start(loop, &wev);
587
588   ev_timer_again(loop, &wt);
589
590   return 0;
591 }
592
593 void HttpClient::disconnect() {
594   state = ClientState::IDLE;
595
596   ev_timer_stop(loop, &settings_timer);
597
598   ev_timer_stop(loop, &rt);
599   ev_timer_stop(loop, &wt);
600
601   ev_io_stop(loop, &rev);
602   ev_io_stop(loop, &wev);
603
604   nghttp2_session_del(session);
605   session = nullptr;
606
607   if (ssl) {
608     SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
609     ERR_clear_error();
610     SSL_shutdown(ssl);
611     SSL_free(ssl);
612     ssl = nullptr;
613   }
614
615   if (fd != -1) {
616     shutdown(fd, SHUT_WR);
617     close(fd);
618     fd = -1;
619   }
620 }
621
622 int HttpClient::read_clear() {
623   ev_timer_again(loop, &rt);
624
625   std::array<uint8_t, 8192> buf;
626
627   for (;;) {
628     ssize_t nread;
629     while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR)
630       ;
631     if (nread == -1) {
632       if (errno == EAGAIN || errno == EWOULDBLOCK) {
633         return 0;
634       }
635       return -1;
636     }
637
638     if (nread == 0) {
639       return -1;
640     }
641
642     if (on_readfn(*this, buf.data(), nread) != 0) {
643       return -1;
644     }
645   }
646
647   return 0;
648 }
649
650 int HttpClient::write_clear() {
651   ev_timer_again(loop, &rt);
652
653   for (;;) {
654     if (wb.rleft() > 0) {
655       ssize_t nwrite;
656       while ((nwrite = write(fd, wb.pos, wb.rleft())) == -1 && errno == EINTR)
657         ;
658       if (nwrite == -1) {
659         if (errno == EAGAIN || errno == EWOULDBLOCK) {
660           ev_io_start(loop, &wev);
661           ev_timer_again(loop, &wt);
662           return 0;
663         }
664         return -1;
665       }
666       wb.drain(nwrite);
667       continue;
668     }
669
670     if (on_writefn(*this) != 0) {
671       return -1;
672     }
673     if (wb.rleft() == 0) {
674       wb.reset();
675       break;
676     }
677   }
678
679   ev_io_stop(loop, &wev);
680   ev_timer_stop(loop, &wt);
681
682   return 0;
683 }
684
685 int HttpClient::noop() { return 0; }
686
687 void HttpClient::on_connect_fail() {
688   if (state == ClientState::IDLE) {
689     std::cerr << "[ERROR] Could not connect to the address "
690               << numeric_name(cur_addr) << std::endl;
691   }
692   auto cur_state = state;
693   disconnect();
694   if (cur_state == ClientState::IDLE) {
695     if (initiate_connection() == 0) {
696       std::cerr << "Trying next address " << numeric_name(cur_addr)
697                 << std::endl;
698     }
699   }
700 }
701
702 int HttpClient::connected() {
703   if (!util::check_socket_connected(fd)) {
704     return ERR_CONNECT_FAIL;
705   }
706
707   if (config.verbose) {
708     print_timer();
709     std::cout << " Connected" << std::endl;
710   }
711
712   record_connect_time();
713   state = ClientState::CONNECTED;
714
715   ev_io_start(loop, &rev);
716   ev_io_stop(loop, &wev);
717
718   ev_timer_again(loop, &rt);
719   ev_timer_stop(loop, &wt);
720
721   if (ssl) {
722     readfn = &HttpClient::tls_handshake;
723     writefn = &HttpClient::tls_handshake;
724
725     return do_write();
726   }
727
728   readfn = &HttpClient::read_clear;
729   writefn = &HttpClient::write_clear;
730
731   if (need_upgrade()) {
732     htp = make_unique<http_parser>();
733     http_parser_init(htp.get(), HTTP_RESPONSE);
734     htp->data = this;
735
736     return do_write();
737   }
738
739   if (on_connect() != 0) {
740     return -1;
741   }
742
743   return 0;
744 }
745
746 namespace {
747 size_t populate_settings(nghttp2_settings_entry *iv) {
748   size_t niv = 2;
749
750   iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
751   iv[0].value = 100;
752
753   iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
754   if (config.window_bits != -1) {
755     iv[1].value = (1 << config.window_bits) - 1;
756   } else {
757     iv[1].value = NGHTTP2_INITIAL_WINDOW_SIZE;
758   }
759
760   if (config.header_table_size >= 0) {
761     iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
762     iv[niv].value = config.header_table_size;
763     ++niv;
764   }
765   return niv;
766 }
767 } // namespace
768
769 int HttpClient::on_upgrade_connect() {
770   ssize_t rv;
771   record_handshake_time();
772   assert(!reqvec.empty());
773   std::array<nghttp2_settings_entry, 32> iv;
774   size_t niv = populate_settings(iv.data());
775   assert(settings_payload.size() >= 8 * niv);
776   rv = nghttp2_pack_settings_payload(settings_payload.data(),
777                                      settings_payload.size(), iv.data(), niv);
778   if (rv < 0) {
779     return -1;
780   }
781   settings_payloadlen = rv;
782   auto token68 =
783       base64::encode(std::begin(settings_payload),
784                      std::begin(settings_payload) + settings_payloadlen);
785   util::to_token68(token68);
786   std::string req;
787   if (reqvec[0]->data_prd) {
788     // If the request contains upload data, use OPTIONS * to upgrade
789     req = "OPTIONS *";
790   } else {
791     req = "GET ";
792     req += reqvec[0]->make_reqpath();
793   }
794   req += " HTTP/1.1\r\n"
795          "Host: ";
796   req += hostport;
797   req += "\r\n"
798          "Connection: Upgrade, HTTP2-Settings\r\n"
799          "Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n"
800          "HTTP2-Settings: ";
801   req += token68;
802   req += "\r\n"
803          "Accept: */*\r\n"
804          "User-Agent: nghttp2/" NGHTTP2_VERSION "\r\n"
805          "\r\n";
806
807   wb.write(req.c_str(), req.size());
808
809   if (config.verbose) {
810     print_timer();
811     std::cout << " HTTP Upgrade request\n" << req << std::endl;
812   }
813
814   // record request time if this is GET request
815   if (!reqvec[0]->data_prd) {
816     reqvec[0]->record_request_time();
817   }
818
819   on_writefn = &HttpClient::noop;
820
821   signal_write();
822
823   return 0;
824 }
825
826 int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) {
827   int rv;
828
829   auto nread = http_parser_execute(htp.get(), &htp_hooks,
830                                    reinterpret_cast<const char *>(data), len);
831
832   if (config.verbose) {
833     std::cout.write(reinterpret_cast<const char *>(data), nread);
834   }
835
836   auto htperr = HTTP_PARSER_ERRNO(htp.get());
837
838   if (htperr != HPE_OK) {
839     std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: "
840               << "(" << http_errno_name(htperr) << ") "
841               << http_errno_description(htperr) << std::endl;
842     return -1;
843   }
844
845   if (!upgrade_response_complete) {
846     return 0;
847   }
848
849   if (config.verbose) {
850     std::cout << std::endl;
851   }
852
853   if (upgrade_response_status_code != 101) {
854     std::cerr << "[ERROR] HTTP Upgrade failed" << std::endl;
855
856     return -1;
857   }
858
859   if (config.verbose) {
860     print_timer();
861     std::cout << " HTTP Upgrade success" << std::endl;
862   }
863
864   on_readfn = &HttpClient::on_read;
865   on_writefn = &HttpClient::on_write;
866
867   rv = on_connect();
868   if (rv != 0) {
869     return rv;
870   }
871
872   // Read remaining data in the buffer because it is not notified
873   // callback anymore.
874   rv = on_readfn(*this, data + nread, len - nread);
875   if (rv != 0) {
876     return rv;
877   }
878
879   return 0;
880 }
881
882 int HttpClient::do_read() { return readfn(*this); }
883 int HttpClient::do_write() { return writefn(*this); }
884
885 int HttpClient::on_connect() {
886   int rv;
887
888   if (ssl) {
889     // Check NPN or ALPN result
890     const unsigned char *next_proto = nullptr;
891     unsigned int next_proto_len;
892     SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
893     for (int i = 0; i < 2; ++i) {
894       if (next_proto) {
895         if (config.verbose) {
896           std::cout << "The negotiated protocol: ";
897           std::cout.write(reinterpret_cast<const char *>(next_proto),
898                           next_proto_len);
899           std::cout << std::endl;
900         }
901         if (!util::check_h2_is_selected(next_proto, next_proto_len)) {
902           next_proto = nullptr;
903         }
904         break;
905       }
906 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
907       SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
908 #else  // OPENSSL_VERSION_NUMBER < 0x10002000L
909       break;
910 #endif // OPENSSL_VERSION_NUMBER < 0x10002000L
911     }
912     if (!next_proto) {
913       print_protocol_nego_error();
914       return -1;
915     }
916   }
917
918   if (!need_upgrade()) {
919     record_handshake_time();
920   }
921
922   rv = nghttp2_session_client_new2(&session, callbacks, this,
923                                    config.http2_option);
924
925   if (rv != 0) {
926     return -1;
927   }
928   if (need_upgrade()) {
929     // Adjust stream user-data depending on the existence of upload
930     // data
931     Request *stream_user_data = nullptr;
932     if (!reqvec[0]->data_prd) {
933       stream_user_data = reqvec[0].get();
934     }
935     rv = nghttp2_session_upgrade(session, settings_payload.data(),
936                                  settings_payloadlen, stream_user_data);
937     if (rv != 0) {
938       std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: "
939                 << nghttp2_strerror(rv) << std::endl;
940       return -1;
941     }
942     if (stream_user_data) {
943       stream_user_data->stream_id = 1;
944       on_request(stream_user_data);
945     }
946   }
947   // Send connection header here
948   wb.write(NGHTTP2_CLIENT_CONNECTION_PREFACE,
949            NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
950   // If upgrade succeeds, the SETTINGS value sent with
951   // HTTP2-Settings header field has already been submitted to
952   // session object.
953   if (!need_upgrade()) {
954     std::array<nghttp2_settings_entry, 16> iv;
955     auto niv = populate_settings(iv.data());
956     rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv.data(), niv);
957     if (rv != 0) {
958       return -1;
959     }
960   }
961   if (!config.no_dep && config.dep_idle) {
962     // Create anchor stream nodes
963     nghttp2_priority_spec pri_spec;
964     int32_t dep_stream_id = 0;
965
966     for (auto stream_id :
967          {ANCHOR_ID_HIGH, ANCHOR_ID_MEDIUM, ANCHOR_ID_LOW, ANCHOR_ID_LOWEST}) {
968
969       nghttp2_priority_spec_init(&pri_spec, dep_stream_id,
970                                  NGHTTP2_DEFAULT_WEIGHT, 0);
971       rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, stream_id,
972                                    &pri_spec);
973       if (rv != 0) {
974         return -1;
975       }
976
977       dep_stream_id = stream_id;
978     }
979
980     rv = nghttp2_session_set_next_stream_id(session, ANCHOR_ID_LOWEST + 2);
981     if (rv != 0) {
982       return -1;
983     }
984
985     if (need_upgrade()) {
986       // Amend the priority because we cannot send priority in
987       // HTTP/1.1 Upgrade.
988       nghttp2_priority_spec_init(&pri_spec, ANCHOR_ID_HIGH, config.weight, 0);
989
990       rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec);
991       if (rv != 0) {
992         return -1;
993       }
994     }
995   } else if (need_upgrade() && config.weight != NGHTTP2_DEFAULT_WEIGHT) {
996     // Amend the priority because we cannot send priority in
997     // HTTP/1.1 Upgrade.
998     nghttp2_priority_spec pri_spec;
999
1000     nghttp2_priority_spec_init(&pri_spec, 0, config.weight, 0);
1001
1002     rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec);
1003     if (rv != 0) {
1004       return -1;
1005     }
1006   }
1007
1008   ev_timer_again(loop, &settings_timer);
1009
1010   if (config.connection_window_bits != -1) {
1011     int32_t wininc = (1 << config.connection_window_bits) - 1 -
1012                      NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
1013     rv = nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, wininc);
1014     if (rv != 0) {
1015       return -1;
1016     }
1017   }
1018   // Adjust first request depending on the existence of the upload
1019   // data
1020   for (auto i = std::begin(reqvec) + (need_upgrade() && !reqvec[0]->data_prd);
1021        i != std::end(reqvec); ++i) {
1022     if (submit_request(this, config.headers, (*i).get()) != 0) {
1023       return -1;
1024     }
1025   }
1026
1027   signal_write();
1028
1029   return 0;
1030 }
1031
1032 int HttpClient::on_read(const uint8_t *data, size_t len) {
1033   auto rv = nghttp2_session_mem_recv(session, data, len);
1034   if (rv < 0) {
1035     std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: "
1036               << nghttp2_strerror(rv) << std::endl;
1037     return -1;
1038   }
1039
1040   assert(static_cast<size_t>(rv) == len);
1041
1042   if (nghttp2_session_want_read(session) == 0 &&
1043       nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) {
1044     return -1;
1045   }
1046
1047   signal_write();
1048
1049   return 0;
1050 }
1051
1052 int HttpClient::on_write() {
1053   auto rv = nghttp2_session_send(session);
1054   if (rv != 0) {
1055     std::cerr << "[ERROR] nghttp2_session_send() returned error: "
1056               << nghttp2_strerror(rv) << std::endl;
1057     return -1;
1058   }
1059
1060   if (nghttp2_session_want_read(session) == 0 &&
1061       nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) {
1062     return -1;
1063   }
1064
1065   return 0;
1066 }
1067
1068 int HttpClient::tls_handshake() {
1069   ev_timer_again(loop, &rt);
1070
1071   ERR_clear_error();
1072
1073   auto rv = SSL_do_handshake(ssl);
1074
1075   if (rv == 0) {
1076     return -1;
1077   }
1078
1079   if (rv < 0) {
1080     auto err = SSL_get_error(ssl, rv);
1081     switch (err) {
1082     case SSL_ERROR_WANT_READ:
1083       ev_io_stop(loop, &wev);
1084       ev_timer_stop(loop, &wt);
1085       return 0;
1086     case SSL_ERROR_WANT_WRITE:
1087       ev_io_start(loop, &wev);
1088       ev_timer_again(loop, &wt);
1089       return 0;
1090     default:
1091       return -1;
1092     }
1093   }
1094
1095   ev_io_stop(loop, &wev);
1096   ev_timer_stop(loop, &wt);
1097
1098   readfn = &HttpClient::read_tls;
1099   writefn = &HttpClient::write_tls;
1100
1101   if (on_connect() != 0) {
1102     return -1;
1103   }
1104
1105   return 0;
1106 }
1107
1108 int HttpClient::read_tls() {
1109   ev_timer_again(loop, &rt);
1110
1111   ERR_clear_error();
1112
1113   std::array<uint8_t, 8192> buf;
1114   for (;;) {
1115     auto rv = SSL_read(ssl, buf.data(), buf.size());
1116
1117     if (rv == 0) {
1118       return -1;
1119     }
1120
1121     if (rv < 0) {
1122       auto err = SSL_get_error(ssl, rv);
1123       switch (err) {
1124       case SSL_ERROR_WANT_READ:
1125         return 0;
1126       case SSL_ERROR_WANT_WRITE:
1127         // renegotiation started
1128         return -1;
1129       default:
1130         return -1;
1131       }
1132     }
1133
1134     if (on_readfn(*this, buf.data(), rv) != 0) {
1135       return -1;
1136     }
1137   }
1138 }
1139
1140 int HttpClient::write_tls() {
1141   ev_timer_again(loop, &rt);
1142
1143   ERR_clear_error();
1144
1145   for (;;) {
1146     if (wb.rleft() > 0) {
1147       auto rv = SSL_write(ssl, wb.pos, wb.rleft());
1148
1149       if (rv == 0) {
1150         return -1;
1151       }
1152
1153       if (rv < 0) {
1154         auto err = SSL_get_error(ssl, rv);
1155         switch (err) {
1156         case SSL_ERROR_WANT_READ:
1157           // renegotiation started
1158           return -1;
1159         case SSL_ERROR_WANT_WRITE:
1160           ev_io_start(loop, &wev);
1161           ev_timer_again(loop, &wt);
1162           return 0;
1163         default:
1164           return -1;
1165         }
1166       }
1167
1168       wb.drain(rv);
1169
1170       continue;
1171     }
1172     if (on_writefn(*this) != 0) {
1173       return -1;
1174     }
1175     if (wb.rleft() == 0) {
1176       break;
1177     }
1178   }
1179
1180   ev_io_stop(loop, &wev);
1181   ev_timer_stop(loop, &wt);
1182
1183   return 0;
1184 }
1185
1186 void HttpClient::signal_write() { ev_io_start(loop, &wev); }
1187
1188 bool HttpClient::all_requests_processed() const {
1189   return complete == reqvec.size();
1190 }
1191
1192 void HttpClient::update_hostport() {
1193   if (reqvec.empty()) {
1194     return;
1195   }
1196   scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_SCHEMA);
1197   std::stringstream ss;
1198   if (reqvec[0]->is_ipv6_literal_addr()) {
1199     ss << "[";
1200     util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);
1201     ss << "]";
1202   } else {
1203     util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);
1204   }
1205   if (util::has_uri_field(reqvec[0]->u, UF_PORT) &&
1206       reqvec[0]->u.port !=
1207           util::get_default_port(reqvec[0]->uri.c_str(), reqvec[0]->u)) {
1208     ss << ":" << reqvec[0]->u.port;
1209   }
1210   hostport = ss.str();
1211 }
1212
1213 bool HttpClient::add_request(const std::string &uri,
1214                              const nghttp2_data_provider *data_prd,
1215                              int64_t data_length,
1216                              const nghttp2_priority_spec &pri_spec,
1217                              std::shared_ptr<Dependency> dep, int pri,
1218                              int level) {
1219   http_parser_url u;
1220   memset(&u, 0, sizeof(u));
1221   if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
1222     return false;
1223   }
1224   if (path_cache.count(uri)) {
1225     return false;
1226   }
1227
1228   if (config.multiply == 1) {
1229     path_cache.insert(uri);
1230   }
1231
1232   reqvec.push_back(make_unique<Request>(uri, u, data_prd, data_length, pri_spec,
1233                                         std::move(dep), pri, level));
1234   return true;
1235 }
1236
1237 void HttpClient::record_handshake_time() {
1238   timing.on_handshake_time = get_time();
1239 }
1240
1241 void HttpClient::record_started_time() {
1242   timing.started_system_time = std::chrono::system_clock::now();
1243   timing.on_started_time = get_time();
1244 }
1245
1246 void HttpClient::record_dns_complete_time() {
1247   timing.on_dns_complete_time = get_time();
1248 }
1249
1250 void HttpClient::record_connect_time() { timing.on_connect_time = get_time(); }
1251
1252 void HttpClient::on_request(Request *req) {
1253   if (req->pri == 0 && req->dep) {
1254     assert(req->dep->deps.empty());
1255
1256     req->dep->deps.push_back(std::vector<Request *>{req});
1257
1258     return;
1259   }
1260
1261   if (req->stream_id % 2 == 0) {
1262     return;
1263   }
1264
1265   auto itr = std::begin(req->dep->deps);
1266   for (; itr != std::end(req->dep->deps); ++itr) {
1267     if ((*itr)[0]->pri == req->pri) {
1268       (*itr).push_back(req);
1269
1270       break;
1271     }
1272
1273     if ((*itr)[0]->pri > req->pri) {
1274       auto v = std::vector<Request *>{req};
1275       req->dep->deps.insert(itr, std::move(v));
1276
1277       break;
1278     }
1279   }
1280
1281   if (itr == std::end(req->dep->deps)) {
1282     req->dep->deps.push_back(std::vector<Request *>{req});
1283   }
1284 }
1285
1286 #ifdef HAVE_JANSSON
1287 void HttpClient::output_har(FILE *outfile) {
1288   static auto PAGE_ID = "page_0";
1289
1290   auto root = json_object();
1291   auto log = json_object();
1292   json_object_set_new(root, "log", log);
1293   json_object_set_new(log, "version", json_string("1.2"));
1294
1295   auto creator = json_object();
1296   json_object_set_new(log, "creator", creator);
1297
1298   json_object_set_new(creator, "name", json_string("nghttp"));
1299   json_object_set_new(creator, "version", json_string(NGHTTP2_VERSION));
1300
1301   auto pages = json_array();
1302   json_object_set_new(log, "pages", pages);
1303
1304   auto page = json_object();
1305   json_array_append_new(pages, page);
1306
1307   json_object_set_new(
1308       page, "startedDateTime",
1309       json_string(util::format_iso8601(timing.started_system_time).c_str()));
1310   json_object_set_new(page, "id", json_string(PAGE_ID));
1311   json_object_set_new(page, "title", json_string(""));
1312
1313   json_object_set_new(page, "pageTimings", json_object());
1314
1315   auto entries = json_array();
1316   json_object_set_new(log, "entries", entries);
1317
1318   auto dns_delta =
1319       std::chrono::duration_cast<std::chrono::microseconds>(
1320           timing.on_dns_complete_time - timing.on_started_time).count() /
1321       1000.0;
1322   auto connect_delta =
1323       std::chrono::duration_cast<std::chrono::microseconds>(
1324           timing.on_connect_time - timing.on_dns_complete_time).count() /
1325       1000.0;
1326
1327   for (size_t i = 0; i < reqvec.size(); ++i) {
1328     auto &req = reqvec[i];
1329
1330     if (req->timing.state != RequestState::ON_COMPLETE) {
1331       continue;
1332     }
1333
1334     auto entry = json_object();
1335     json_array_append_new(entries, entry);
1336
1337     auto &req_timing = req->timing;
1338     auto request_time =
1339         (i == 0) ? timing.started_system_time
1340                  : timing.started_system_time +
1341                        std::chrono::duration_cast<
1342                            std::chrono::system_clock::duration>(
1343                            req_timing.on_request_time - timing.on_started_time);
1344
1345     auto wait_delta =
1346         std::chrono::duration_cast<std::chrono::microseconds>(
1347             req_timing.on_response_time - req_timing.on_request_time).count() /
1348         1000.0;
1349     auto receive_delta =
1350         std::chrono::duration_cast<std::chrono::microseconds>(
1351             req_timing.on_complete_time - req_timing.on_response_time).count() /
1352         1000.0;
1353
1354     auto time_sum =
1355         std::chrono::duration_cast<std::chrono::microseconds>(
1356             (i == 0) ? (req_timing.on_complete_time - timing.on_started_time)
1357                      : (req_timing.on_complete_time -
1358                         req_timing.on_request_time)).count() /
1359         1000.0;
1360
1361     json_object_set_new(
1362         entry, "startedDateTime",
1363         json_string(util::format_iso8601(request_time).c_str()));
1364     json_object_set_new(entry, "time", json_real(time_sum));
1365
1366     auto request = json_object();
1367     json_object_set_new(entry, "request", request);
1368
1369     auto method_ptr = http2::get_header(req->req_nva, ":method");
1370
1371     const char *method = "GET";
1372     if (method_ptr) {
1373       method = (*method_ptr).value.c_str();
1374     }
1375
1376     auto req_headers = json_array();
1377     json_object_set_new(request, "headers", req_headers);
1378
1379     for (auto &nv : req->req_nva) {
1380       auto hd = json_object();
1381       json_array_append_new(req_headers, hd);
1382
1383       json_object_set_new(hd, "name", json_string(nv.name.c_str()));
1384       json_object_set_new(hd, "value", json_string(nv.value.c_str()));
1385     }
1386
1387     json_object_set_new(request, "method", json_string(method));
1388     json_object_set_new(request, "url", json_string(req->uri.c_str()));
1389     json_object_set_new(request, "httpVersion", json_string("HTTP/2.0"));
1390     json_object_set_new(request, "cookies", json_array());
1391     json_object_set_new(request, "queryString", json_array());
1392     json_object_set_new(request, "headersSize", json_integer(-1));
1393     json_object_set_new(request, "bodySize", json_integer(-1));
1394
1395     auto response = json_object();
1396     json_object_set_new(entry, "response", response);
1397
1398     auto res_headers = json_array();
1399     json_object_set_new(response, "headers", res_headers);
1400
1401     for (auto &nv : req->res_nva) {
1402       auto hd = json_object();
1403       json_array_append_new(res_headers, hd);
1404
1405       json_object_set_new(hd, "name", json_string(nv.name.c_str()));
1406       json_object_set_new(hd, "value", json_string(nv.value.c_str()));
1407     }
1408
1409     json_object_set_new(response, "status", json_integer(req->status));
1410     json_object_set_new(response, "statusText", json_string(""));
1411     json_object_set_new(response, "httpVersion", json_string("HTTP/2.0"));
1412     json_object_set_new(response, "cookies", json_array());
1413
1414     auto content = json_object();
1415     json_object_set_new(response, "content", content);
1416
1417     json_object_set_new(content, "size", json_integer(req->response_len));
1418
1419     auto content_type_ptr = http2::get_header(req->res_nva, "content-type");
1420
1421     const char *content_type = "";
1422     if (content_type_ptr) {
1423       content_type = content_type_ptr->value.c_str();
1424     }
1425
1426     json_object_set_new(content, "mimeType", json_string(content_type));
1427
1428     json_object_set_new(response, "redirectURL", json_string(""));
1429     json_object_set_new(response, "headersSize", json_integer(-1));
1430     json_object_set_new(response, "bodySize", json_integer(-1));
1431
1432     json_object_set_new(entry, "cache", json_object());
1433
1434     auto timings = json_object();
1435     json_object_set_new(entry, "timings", timings);
1436
1437     auto dns_timing = (i == 0) ? dns_delta : 0;
1438     auto connect_timing = (i == 0) ? connect_delta : 0;
1439
1440     json_object_set_new(timings, "dns", json_real(dns_timing));
1441     json_object_set_new(timings, "connect", json_real(connect_timing));
1442
1443     json_object_set_new(timings, "blocked", json_real(0.0));
1444     json_object_set_new(timings, "send", json_real(0.0));
1445     json_object_set_new(timings, "wait", json_real(wait_delta));
1446     json_object_set_new(timings, "receive", json_real(receive_delta));
1447
1448     json_object_set_new(entry, "pageref", json_string(PAGE_ID));
1449   }
1450
1451   json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2));
1452   json_decref(root);
1453 }
1454 #endif // HAVE_JANSSON
1455
1456 namespace {
1457 void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
1458                         size_t len, int fin) {
1459   if (!req->html_parser) {
1460     return;
1461   }
1462   req->update_html_parser(data, len, fin);
1463
1464   for (auto &p : req->html_parser->get_links()) {
1465     auto uri = strip_fragment(p.first.c_str());
1466     auto pri = p.second;
1467
1468     http_parser_url u;
1469     memset(&u, 0, sizeof(u));
1470     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 &&
1471         util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_SCHEMA) &&
1472         util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) &&
1473         util::porteq(uri.c_str(), u, req->uri.c_str(), req->u)) {
1474       // No POST data for assets
1475       auto pri_spec = req->resolve_dep(pri);
1476
1477       if (client->add_request(uri, nullptr, 0, pri_spec, req->dep, pri,
1478                               req->level + 1)) {
1479
1480         submit_request(client, config.headers, client->reqvec.back().get());
1481       }
1482     }
1483   }
1484   req->html_parser->clear_links();
1485 }
1486 } // namespace
1487
1488 namespace {
1489 HttpClient *get_client(void *user_data) {
1490   return static_cast<HttpClient *>(user_data);
1491 }
1492 } // namespace
1493
1494 namespace {
1495 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
1496                                 int32_t stream_id, const uint8_t *data,
1497                                 size_t len, void *user_data) {
1498   auto client = get_client(user_data);
1499   auto req = static_cast<Request *>(
1500       nghttp2_session_get_stream_user_data(session, stream_id));
1501
1502   if (!req) {
1503     return 0;
1504   }
1505
1506   if (config.verbose >= 2) {
1507     verbose_on_data_chunk_recv_callback(session, flags, stream_id, data, len,
1508                                         user_data);
1509   }
1510
1511   if (req->status == 0) {
1512     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
1513                               NGHTTP2_PROTOCOL_ERROR);
1514     return 0;
1515   }
1516
1517   if (req->inflater) {
1518     while (len > 0) {
1519       const size_t MAX_OUTLEN = 4096;
1520       std::array<uint8_t, MAX_OUTLEN> out;
1521       size_t outlen = MAX_OUTLEN;
1522       size_t tlen = len;
1523       int rv =
1524           nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen);
1525       if (rv != 0) {
1526         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
1527                                   NGHTTP2_INTERNAL_ERROR);
1528         break;
1529       }
1530
1531       req->response_len += outlen;
1532
1533       if (!config.null_out) {
1534         std::cout.write(reinterpret_cast<const char *>(out.data()), outlen);
1535       }
1536
1537       update_html_parser(client, req, out.data(), outlen, 0);
1538       data += tlen;
1539       len -= tlen;
1540     }
1541
1542     return 0;
1543   }
1544
1545   req->response_len += len;
1546
1547   if (!config.null_out) {
1548     std::cout.write(reinterpret_cast<const char *>(data), len);
1549   }
1550
1551   update_html_parser(client, req, data, len, 0);
1552
1553   return 0;
1554 }
1555 } // namespace
1556
1557 namespace {
1558 ssize_t select_padding_callback(nghttp2_session *session,
1559                                 const nghttp2_frame *frame, size_t max_payload,
1560                                 void *user_data) {
1561   return std::min(max_payload, frame->hd.length + config.padding);
1562 }
1563 } // namespace
1564
1565 namespace {
1566 void check_response_header(nghttp2_session *session, Request *req) {
1567   bool gzip = false;
1568
1569   req->expect_final_response = false;
1570
1571   auto status_hd = req->get_res_header(http2::HD__STATUS);
1572
1573   if (!status_hd) {
1574     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
1575                               NGHTTP2_PROTOCOL_ERROR);
1576     return;
1577   }
1578
1579   auto status = http2::parse_http_status_code(status_hd->value);
1580   if (status == -1) {
1581     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
1582                               NGHTTP2_PROTOCOL_ERROR);
1583     return;
1584   }
1585
1586   req->status = status;
1587
1588   for (auto &nv : req->res_nva) {
1589     if ("content-encoding" == nv.name) {
1590       gzip =
1591           util::strieq("gzip", nv.value) || util::strieq("deflate", nv.value);
1592       continue;
1593     }
1594   }
1595
1596   if (req->status / 100 == 1) {
1597     req->expect_final_response = true;
1598     req->status = 0;
1599     req->res_nva.clear();
1600     http2::init_hdidx(req->res_hdidx);
1601     return;
1602   }
1603
1604   if (gzip) {
1605     if (!req->inflater) {
1606       req->init_inflater();
1607     }
1608   }
1609   if (config.get_assets && req->level == 0) {
1610     if (!req->html_parser) {
1611       req->init_html_parser();
1612     }
1613   }
1614 }
1615 } // namespace
1616
1617 namespace {
1618 int on_begin_headers_callback(nghttp2_session *session,
1619                               const nghttp2_frame *frame, void *user_data) {
1620   auto client = get_client(user_data);
1621   switch (frame->hd.type) {
1622   case NGHTTP2_PUSH_PROMISE: {
1623     auto stream_id = frame->push_promise.promised_stream_id;
1624     http_parser_url u;
1625     memset(&u, 0, sizeof(u));
1626     // TODO Set pri and level
1627     nghttp2_priority_spec pri_spec;
1628
1629     nghttp2_priority_spec_default_init(&pri_spec);
1630
1631     auto req = make_unique<Request>("", u, nullptr, 0, pri_spec, nullptr);
1632     req->stream_id = stream_id;
1633
1634     nghttp2_session_set_stream_user_data(session, stream_id, req.get());
1635
1636     client->on_request(req.get());
1637     req->record_request_time();
1638     client->reqvec.push_back(std::move(req));
1639
1640     break;
1641   }
1642   }
1643   return 0;
1644 }
1645 } // namespace
1646
1647 namespace {
1648 int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
1649                        const uint8_t *name, size_t namelen,
1650                        const uint8_t *value, size_t valuelen, uint8_t flags,
1651                        void *user_data) {
1652   if (config.verbose) {
1653     verbose_on_header_callback(session, frame, name, namelen, value, valuelen,
1654                                flags, user_data);
1655   }
1656
1657   if (!http2::check_nv(name, namelen, value, valuelen)) {
1658     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
1659                               NGHTTP2_PROTOCOL_ERROR);
1660     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1661   }
1662
1663   switch (frame->hd.type) {
1664   case NGHTTP2_HEADERS: {
1665     auto req = static_cast<Request *>(
1666         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1667
1668     if (!req) {
1669       break;
1670     }
1671
1672     if (frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
1673         frame->headers.cat != NGHTTP2_HCAT_PUSH_RESPONSE &&
1674         (frame->headers.cat != NGHTTP2_HCAT_HEADERS ||
1675          !req->expect_final_response)) {
1676       break;
1677     }
1678
1679     auto token = http2::lookup_token(name, namelen);
1680
1681     if (name[0] == ':') {
1682       if (!req->response_pseudo_header_allowed(token)) {
1683         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1684                                   frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
1685         return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1686       }
1687     }
1688
1689     http2::index_header(req->res_hdidx, token, req->res_nva.size());
1690     http2::add_header(req->res_nva, name, namelen, value, valuelen,
1691                       flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
1692     break;
1693   }
1694   case NGHTTP2_PUSH_PROMISE: {
1695     auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data(
1696         session, frame->push_promise.promised_stream_id));
1697
1698     if (!req) {
1699       break;
1700     }
1701
1702     auto token = http2::lookup_token(name, namelen);
1703
1704     if (name[0] == ':') {
1705       if (!req->push_request_pseudo_header_allowed(token)) {
1706         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1707                                   frame->push_promise.promised_stream_id,
1708                                   NGHTTP2_PROTOCOL_ERROR);
1709         break;
1710       }
1711     }
1712
1713     http2::index_header(req->req_hdidx, token, req->req_nva.size());
1714     http2::add_header(req->req_nva, name, namelen, value, valuelen,
1715                       flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
1716     break;
1717   }
1718   }
1719   return 0;
1720 }
1721 } // namespace
1722
1723 namespace {
1724 int on_frame_recv_callback2(nghttp2_session *session,
1725                             const nghttp2_frame *frame, void *user_data) {
1726   int rv = 0;
1727
1728   if (config.verbose) {
1729     verbose_on_frame_recv_callback(session, frame, user_data);
1730   }
1731
1732   auto client = get_client(user_data);
1733   switch (frame->hd.type) {
1734   case NGHTTP2_HEADERS: {
1735     auto req = static_cast<Request *>(
1736         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1737     // If this is the HTTP Upgrade with OPTIONS method to avoid POST,
1738     // req is nullptr.
1739     if (!req) {
1740       break;
1741     }
1742
1743     if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE ||
1744         frame->headers.cat == NGHTTP2_HCAT_PUSH_RESPONSE) {
1745       req->record_response_time();
1746       check_response_header(session, req);
1747
1748       break;
1749     }
1750
1751     if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
1752       if (req->expect_final_response) {
1753         check_response_header(session, req);
1754       } else {
1755         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1756                                   frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
1757         break;
1758       }
1759     }
1760
1761     if (req->status == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
1762       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
1763                                 NGHTTP2_PROTOCOL_ERROR);
1764       break;
1765     }
1766
1767     break;
1768   }
1769   case NGHTTP2_SETTINGS:
1770     if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
1771       break;
1772     }
1773     ev_timer_stop(client->loop, &client->settings_timer);
1774     break;
1775   case NGHTTP2_PUSH_PROMISE: {
1776     auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data(
1777         session, frame->push_promise.promised_stream_id));
1778     if (!req) {
1779       break;
1780     }
1781     auto scheme = req->get_req_header(http2::HD__SCHEME);
1782     auto authority = req->get_req_header(http2::HD__AUTHORITY);
1783     auto method = req->get_req_header(http2::HD__METHOD);
1784     auto path = req->get_req_header(http2::HD__PATH);
1785
1786     if (!authority) {
1787       authority = req->get_req_header(http2::HD_HOST);
1788     }
1789
1790     if (!scheme || !authority || !method || !path || scheme->value.empty() ||
1791         authority->value.empty() || method->value.empty() ||
1792         path->value.empty() || path->value[0] != '/') {
1793       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1794                                 frame->push_promise.promised_stream_id,
1795                                 NGHTTP2_PROTOCOL_ERROR);
1796       break;
1797     }
1798     std::string uri = scheme->value;
1799     uri += "://";
1800     uri += authority->value;
1801     uri += path->value;
1802     http_parser_url u;
1803     memset(&u, 0, sizeof(u));
1804     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
1805       nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1806                                 frame->push_promise.promised_stream_id,
1807                                 NGHTTP2_PROTOCOL_ERROR);
1808       break;
1809     }
1810     req->uri = uri;
1811     req->u = u;
1812     break;
1813   }
1814   }
1815   return rv;
1816 }
1817 } // namespace
1818
1819 namespace {
1820 int before_frame_send_callback(nghttp2_session *session,
1821                                const nghttp2_frame *frame, void *user_data) {
1822   if (frame->hd.type != NGHTTP2_HEADERS ||
1823       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
1824     return 0;
1825   }
1826   auto req = static_cast<Request *>(
1827       nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1828   req->record_request_time();
1829   return 0;
1830 }
1831
1832 } // namespace
1833
1834 namespace {
1835 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
1836                              uint32_t error_code, void *user_data) {
1837   auto client = get_client(user_data);
1838   auto req = static_cast<Request *>(
1839       nghttp2_session_get_stream_user_data(session, stream_id));
1840
1841   if (!req) {
1842     return 0;
1843   }
1844
1845   update_html_parser(client, req, nullptr, 0, 1);
1846   req->record_complete_time();
1847   ++client->complete;
1848
1849   if (client->all_requests_processed()) {
1850     nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
1851   }
1852
1853   return 0;
1854 }
1855 } // namespace
1856
1857 struct RequestResult {
1858   std::chrono::microseconds time;
1859 };
1860
1861 namespace {
1862 void print_stats(const HttpClient &client) {
1863   std::cout << "***** Statistics *****" << std::endl;
1864
1865   std::vector<Request *> reqs;
1866   reqs.reserve(client.reqvec.size());
1867   for (const auto &req : client.reqvec) {
1868     if (req->timing.state == RequestState::ON_COMPLETE) {
1869       reqs.push_back(req.get());
1870     }
1871   }
1872
1873   std::sort(std::begin(reqs), std::end(reqs),
1874             [](const Request *lhs, const Request *rhs) {
1875     const auto &ltiming = lhs->timing;
1876     const auto &rtiming = rhs->timing;
1877     return ltiming.on_complete_time < rtiming.on_complete_time ||
1878            (ltiming.on_complete_time == rtiming.on_complete_time &&
1879             ltiming.on_request_time < rtiming.on_request_time);
1880   });
1881
1882   std::cout << R"(
1883 Request timing:
1884   complete: relative time from protocol handshake to stream close
1885    request: relative   time  from   protocol   handshake  to   request
1886             transmission
1887    process: time for request and response
1888       code: HTTP status code
1889        URI: request URI
1890
1891 sorted by 'complete'
1892
1893 complete  request   process  code request path)" << std::endl;
1894
1895   const auto &base = client.timing.on_handshake_time;
1896   for (const auto &req : reqs) {
1897     auto completed_delta =
1898         std::chrono::duration_cast<std::chrono::microseconds>(
1899             req->timing.on_complete_time - base);
1900     auto request_delta = std::chrono::duration_cast<std::chrono::microseconds>(
1901         req->timing.on_request_time - base);
1902     auto total = std::chrono::duration_cast<std::chrono::microseconds>(
1903         req->timing.on_complete_time - req->timing.on_request_time);
1904
1905     std::cout << std::setw(9) << ("+" + util::format_duration(completed_delta))
1906               << " " << std::setw(9)
1907               << ("+" + util::format_duration(request_delta)) << " "
1908               << std::setw(8) << util::format_duration(total) << " "
1909               << std::setw(4) << req->status << " " << req->make_reqpath()
1910               << std::endl;
1911   }
1912 }
1913 } // namespace
1914
1915 namespace {
1916 int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
1917                                 unsigned char *outlen, const unsigned char *in,
1918                                 unsigned int inlen, void *arg) {
1919   if (config.verbose) {
1920     print_timer();
1921     std::cout << "[NPN] server offers:" << std::endl;
1922   }
1923   for (unsigned int i = 0; i < inlen; i += in [i] + 1) {
1924     if (config.verbose) {
1925       std::cout << "          * ";
1926       std::cout.write(reinterpret_cast<const char *>(&in[i + 1]), in[i]);
1927       std::cout << std::endl;
1928     }
1929   }
1930   if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
1931                        inlen)) {
1932     print_protocol_nego_error();
1933     return SSL_TLSEXT_ERR_NOACK;
1934   }
1935   return SSL_TLSEXT_ERR_OK;
1936 }
1937 } // namespace
1938
1939 namespace {
1940 // Recommended general purpose "Intermediate compatibility" cipher by
1941 // mozilla.
1942 //
1943 // https://wiki.mozilla.org/Security/Server_Side_TLS
1944 const char *const CIPHER_LIST =
1945     "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-"
1946     "AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:"
1947     "DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-"
1948     "AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-"
1949     "AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-"
1950     "AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:"
1951     "DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-"
1952     "SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-"
1953     "SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!"
1954     "aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA";
1955 } // namespace
1956
1957 namespace {
1958 int communicate(
1959     const std::string &scheme, const std::string &host, uint16_t port,
1960     std::vector<std::tuple<std::string, nghttp2_data_provider *, int64_t>>
1961         requests, const nghttp2_session_callbacks *callbacks) {
1962   int result = 0;
1963   auto loop = EV_DEFAULT;
1964   SSL_CTX *ssl_ctx = nullptr;
1965   if (scheme == "https") {
1966     ssl_ctx = SSL_CTX_new(SSLv23_client_method());
1967     if (!ssl_ctx) {
1968       std::cerr << "[ERROR] Failed to create SSL_CTX: "
1969                 << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
1970       result = -1;
1971       goto fin;
1972     }
1973     SSL_CTX_set_options(ssl_ctx,
1974                         SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
1975                             SSL_OP_NO_COMPRESSION |
1976                             SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
1977     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
1978     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
1979     if (SSL_CTX_set_cipher_list(ssl_ctx, CIPHER_LIST) == 0) {
1980       std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
1981                 << std::endl;
1982       result = -1;
1983       goto fin;
1984     }
1985     if (!config.keyfile.empty()) {
1986       if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(),
1987                                       SSL_FILETYPE_PEM) != 1) {
1988         std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
1989                   << std::endl;
1990         result = -1;
1991         goto fin;
1992       }
1993     }
1994     if (!config.certfile.empty()) {
1995       if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
1996                                              config.certfile.c_str()) != 1) {
1997         std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
1998                   << std::endl;
1999         result = -1;
2000         goto fin;
2001       }
2002     }
2003     SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
2004                                      nullptr);
2005
2006 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
2007     auto proto_list = util::get_default_alpn();
2008
2009     SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
2010 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
2011   }
2012   {
2013     HttpClient client{callbacks, loop, ssl_ctx};
2014
2015     nghttp2_priority_spec pri_spec;
2016     int32_t dep_stream_id = 0;
2017
2018     if (!config.no_dep && config.dep_idle) {
2019       dep_stream_id = ANCHOR_ID_HIGH;
2020     }
2021
2022     nghttp2_priority_spec_init(&pri_spec, dep_stream_id, config.weight, 0);
2023
2024     for (auto req : requests) {
2025       for (int i = 0; i < config.multiply; ++i) {
2026         auto dep = std::make_shared<Dependency>();
2027         client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req),
2028                            pri_spec, std::move(dep));
2029       }
2030     }
2031     client.update_hostport();
2032
2033     client.record_started_time();
2034
2035     if (client.resolve_host(host, port) != 0) {
2036       goto fin;
2037     }
2038
2039     client.record_dns_complete_time();
2040
2041     if (client.initiate_connection() != 0) {
2042       goto fin;
2043     }
2044     ev_run(loop, 0);
2045
2046 #ifdef HAVE_JANSSON
2047     if (!config.harfile.empty()) {
2048       FILE *outfile;
2049       if (config.harfile == "-") {
2050         outfile = stdout;
2051       } else {
2052         outfile = fopen(config.harfile.c_str(), "wb");
2053       }
2054
2055       if (outfile) {
2056         client.output_har(outfile);
2057
2058         if (outfile != stdout) {
2059           fclose(outfile);
2060         }
2061       } else {
2062         std::cerr << "Cannot open file " << config.harfile << ". "
2063                   << "har file could not be created." << std::endl;
2064       }
2065     }
2066 #endif // HAVE_JANSSON
2067
2068     if (!client.all_requests_processed()) {
2069       std::cerr << "Some requests were not processed. total="
2070                 << client.reqvec.size() << ", processed=" << client.complete
2071                 << std::endl;
2072     }
2073     if (config.stat) {
2074       print_stats(client);
2075     }
2076   }
2077 fin:
2078   if (ssl_ctx) {
2079     SSL_CTX_free(ssl_ctx);
2080   }
2081   return result;
2082 }
2083 } // namespace
2084
2085 namespace {
2086 ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
2087                            uint8_t *buf, size_t length, uint32_t *data_flags,
2088                            nghttp2_data_source *source, void *user_data) {
2089   auto req = static_cast<Request *>(
2090       nghttp2_session_get_stream_user_data(session, stream_id));
2091   assert(req);
2092   int fd = source->fd;
2093   ssize_t nread;
2094
2095   while ((nread = pread(fd, buf, length, req->data_offset)) == -1 &&
2096          errno == EINTR)
2097     ;
2098
2099   if (nread == -1) {
2100     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2101   }
2102
2103   if (nread == 0) {
2104     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
2105   } else {
2106     req->data_offset += nread;
2107   }
2108
2109   return nread;
2110 }
2111 } // namespace
2112
2113 namespace {
2114 ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
2115                       size_t length, int flags, void *user_data) {
2116   auto client = static_cast<HttpClient *>(user_data);
2117   auto &wb = client->wb;
2118
2119   if (wb.wleft() == 0) {
2120     return NGHTTP2_ERR_WOULDBLOCK;
2121   }
2122
2123   return wb.write(data, length);
2124 }
2125 } // namespace
2126
2127 namespace {
2128 int run(char **uris, int n) {
2129   nghttp2_session_callbacks *callbacks;
2130
2131   nghttp2_session_callbacks_new(&callbacks);
2132   auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks);
2133
2134   nghttp2_session_callbacks_set_on_stream_close_callback(
2135       callbacks, on_stream_close_callback);
2136
2137   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
2138                                                        on_frame_recv_callback2);
2139
2140   if (config.verbose) {
2141     nghttp2_session_callbacks_set_on_frame_send_callback(
2142         callbacks, verbose_on_frame_send_callback);
2143
2144     nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
2145         callbacks, verbose_on_invalid_frame_recv_callback);
2146   }
2147
2148   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
2149       callbacks, on_data_chunk_recv_callback);
2150
2151   nghttp2_session_callbacks_set_on_begin_headers_callback(
2152       callbacks, on_begin_headers_callback);
2153
2154   nghttp2_session_callbacks_set_on_header_callback(callbacks,
2155                                                    on_header_callback);
2156
2157   nghttp2_session_callbacks_set_before_frame_send_callback(
2158       callbacks, before_frame_send_callback);
2159
2160   nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
2161
2162   if (config.padding) {
2163     nghttp2_session_callbacks_set_select_padding_callback(
2164         callbacks, select_padding_callback);
2165   }
2166
2167   std::string prev_scheme;
2168   std::string prev_host;
2169   uint16_t prev_port = 0;
2170   int failures = 0;
2171   int data_fd = -1;
2172   nghttp2_data_provider data_prd;
2173   struct stat data_stat;
2174
2175   if (!config.datafile.empty()) {
2176     if (config.datafile == "-") {
2177       if (fstat(0, &data_stat) == 0 &&
2178           (data_stat.st_mode & S_IFMT) == S_IFREG) {
2179         // use STDIN if it is a regular file
2180         data_fd = 0;
2181       } else {
2182         // copy the contents of STDIN to a temporary file
2183         char tempfn[] = "/tmp/nghttp.temp.XXXXXX";
2184         data_fd = mkstemp(tempfn);
2185         if (data_fd == -1) {
2186           std::cerr << "[ERROR] Could not create a temporary file in /tmp"
2187                     << std::endl;
2188           return 1;
2189         }
2190         if (unlink(tempfn) != 0) {
2191           std::cerr << "[WARNING] failed to unlink temporary file:" << tempfn
2192                     << std::endl;
2193         }
2194         while (1) {
2195           std::array<char, 1024> buf;
2196           ssize_t rret, wret;
2197           while ((rret = read(0, buf.data(), buf.size())) == -1 &&
2198                  errno == EINTR)
2199             ;
2200           if (rret == 0)
2201             break;
2202           if (rret == -1) {
2203             std::cerr << "[ERROR] I/O error while reading from STDIN"
2204                       << std::endl;
2205             return 1;
2206           }
2207           while ((wret = write(data_fd, buf.data(), rret)) == -1 &&
2208                  errno == EINTR)
2209             ;
2210           if (wret != rret) {
2211             std::cerr << "[ERROR] I/O error while writing to temporary file"
2212                       << std::endl;
2213             return 1;
2214           }
2215         }
2216         if (fstat(data_fd, &data_stat) == -1) {
2217           close(data_fd);
2218           std::cerr << "[ERROR] Could not stat temporary file" << std::endl;
2219           return 1;
2220         }
2221       }
2222     } else {
2223       data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY);
2224       if (data_fd == -1) {
2225         std::cerr << "[ERROR] Could not open file " << config.datafile
2226                   << std::endl;
2227         return 1;
2228       }
2229       if (fstat(data_fd, &data_stat) == -1) {
2230         close(data_fd);
2231         std::cerr << "[ERROR] Could not stat file " << config.datafile
2232                   << std::endl;
2233         return 1;
2234       }
2235     }
2236     data_prd.source.fd = data_fd;
2237     data_prd.read_callback = file_read_callback;
2238   }
2239   std::vector<std::tuple<std::string, nghttp2_data_provider *, int64_t>>
2240       requests;
2241   for (int i = 0; i < n; ++i) {
2242     http_parser_url u;
2243     memset(&u, 0, sizeof(u));
2244     auto uri = strip_fragment(uris[i]);
2245     if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 &&
2246         util::has_uri_field(u, UF_SCHEMA)) {
2247       uint16_t port = util::has_uri_field(u, UF_PORT)
2248                           ? u.port
2249                           : util::get_default_port(uri.c_str(), u);
2250       if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) ||
2251           !util::fieldeq(uri.c_str(), u, UF_HOST, prev_host.c_str()) ||
2252           port != prev_port) {
2253         if (!requests.empty()) {
2254           if (communicate(prev_scheme, prev_host, prev_port,
2255                           std::move(requests), callbacks) != 0) {
2256             ++failures;
2257           }
2258           requests.clear();
2259         }
2260         prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA);
2261         prev_host = util::get_uri_field(uri.c_str(), u, UF_HOST);
2262         prev_port = port;
2263       }
2264       requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd,
2265                             data_stat.st_size);
2266     }
2267   }
2268   if (!requests.empty()) {
2269     if (communicate(prev_scheme, prev_host, prev_port, std::move(requests),
2270                     callbacks) != 0) {
2271       ++failures;
2272     }
2273   }
2274   return failures;
2275 }
2276 } // namespace
2277
2278 namespace {
2279 void print_version(std::ostream &out) {
2280   out << "nghttp nghttp2/" NGHTTP2_VERSION << std::endl;
2281 }
2282 } // namespace
2283
2284 namespace {
2285 void print_usage(std::ostream &out) {
2286   out << R"(Usage: nghttp [OPTIONS]... <URI>...
2287 HTTP/2 experimental client)" << std::endl;
2288 }
2289 } // namespace
2290
2291 namespace {
2292 void print_help(std::ostream &out) {
2293   print_usage(out);
2294   out << R"(
2295   <URI>       Specify URI to access.
2296 Options:
2297   -v, --verbose
2298               Print   debug   information   such  as   reception   and
2299               transmission of frames and name/value pairs.  Specifying
2300               this option multiple times increases verbosity.
2301   -n, --null-out
2302               Discard downloaded data.
2303   -O, --remote-name
2304               Save  download  data  in  the  current  directory.   The
2305               filename is  dereived from URI.   If URI ends  with '/',
2306               'index.html'  is used  as a  filename.  Not  implemented
2307               yet.
2308   -t, --timeout=<SEC>
2309               Timeout each request after <SEC> seconds.
2310   -w, --window-bits=<N>
2311               Sets the stream level initial window size to 2**<N>-1.
2312   -W, --connection-window-bits=<N>
2313               Sets  the  connection  level   initial  window  size  to
2314               2**<N>-1.
2315   -a, --get-assets
2316               Download assets  such as stylesheets, images  and script
2317               files linked  from the downloaded resource.   Only links
2318               whose  origins are  the same  with the  linking resource
2319               will be downloaded.   nghttp prioritizes resources using
2320               HTTP/2 dependency  based priority.  The  priority order,
2321               from highest to lowest,  is html itself, css, javascript
2322               and images.
2323   -s, --stat  Print statistics.
2324   -H, --header=<HEADER>
2325               Add a header to the requests.  Example: -H':method: PUT'
2326   --cert=<CERT>
2327               Use  the specified  client certificate  file.  The  file
2328               must be in PEM format.
2329   --key=<KEY> Use the  client private key  file.  The file must  be in
2330               PEM format.
2331   -d, --data=<FILE>
2332               Post FILE to server. If '-'  is given, data will be read
2333               from stdin.
2334   -m, --multiply=<N>
2335               Request each URI <N> times.  By default, same URI is not
2336               requested twice.  This option disables it too.
2337   -u, --upgrade
2338               Perform HTTP Upgrade for HTTP/2.  This option is ignored
2339               if the request URI has https scheme.  If -d is used, the
2340               HTTP upgrade request is performed with OPTIONS method.
2341   -p, --weight=<WEIGHT>
2342               Sets priority group weight.  The valid value range is
2343               [)" << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT
2344       << R"(], inclusive.
2345               Default: )" << NGHTTP2_DEFAULT_WEIGHT << R"(
2346   -M, --peer-max-concurrent-streams=<N>
2347               Use  <N>  as  SETTINGS_MAX_CONCURRENT_STREAMS  value  of
2348               remote endpoint as if it  is received in SETTINGS frame.
2349               The default is large enough as it is seen as unlimited.
2350   -c, --header-table-size=<SIZE>
2351               Specify decoder header table size.
2352   -b, --padding=<N>
2353               Add at  most <N>  bytes to a  frame payload  as padding.
2354               Specify 0 to disable padding.
2355   -r, --har=<FILE>
2356               Output HTTP  transactions <FILE> in HAR  format.  If '-'
2357               is given, data is written to stdout.
2358   --color     Force colored log output.
2359   --continuation
2360               Send large header to test CONTINUATION.
2361   --no-content-length
2362               Don't send content-length header field.
2363   --no-dep    Don't send dependency based priority hint to server.
2364   --dep-idle  Use idle streams as anchor nodes to express priority.
2365   --version   Display version information and exit.
2366   -h, --help  Display this help and exit.
2367
2368   The <SIZE> argument is an integer and an optional unit (e.g., 10K is
2369   10 * 1024).  Units are K, M and G (powers of 1024).)" << std::endl;
2370 }
2371 } // namespace
2372
2373 int main(int argc, char **argv) {
2374   bool color = false;
2375   while (1) {
2376     static int flag = 0;
2377     static option long_options[] = {
2378         {"verbose", no_argument, nullptr, 'v'},
2379         {"null-out", no_argument, nullptr, 'n'},
2380         {"remote-name", no_argument, nullptr, 'O'},
2381         {"timeout", required_argument, nullptr, 't'},
2382         {"window-bits", required_argument, nullptr, 'w'},
2383         {"connection-window-bits", required_argument, nullptr, 'W'},
2384         {"get-assets", no_argument, nullptr, 'a'},
2385         {"stat", no_argument, nullptr, 's'},
2386         {"help", no_argument, nullptr, 'h'},
2387         {"header", required_argument, nullptr, 'H'},
2388         {"data", required_argument, nullptr, 'd'},
2389         {"multiply", required_argument, nullptr, 'm'},
2390         {"upgrade", no_argument, nullptr, 'u'},
2391         {"weight", required_argument, nullptr, 'p'},
2392         {"peer-max-concurrent-streams", required_argument, nullptr, 'M'},
2393         {"header-table-size", required_argument, nullptr, 'c'},
2394         {"padding", required_argument, nullptr, 'b'},
2395         {"har", required_argument, nullptr, 'r'},
2396         {"cert", required_argument, &flag, 1},
2397         {"key", required_argument, &flag, 2},
2398         {"color", no_argument, &flag, 3},
2399         {"continuation", no_argument, &flag, 4},
2400         {"version", no_argument, &flag, 5},
2401         {"no-content-length", no_argument, &flag, 6},
2402         {"no-dep", no_argument, &flag, 7},
2403         {"dep-idle", no_argument, &flag, 8},
2404         {nullptr, 0, nullptr, 0}};
2405     int option_index = 0;
2406     int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:",
2407                         long_options, &option_index);
2408     if (c == -1) {
2409       break;
2410     }
2411     switch (c) {
2412     case 'M':
2413       // peer-max-concurrent-streams option
2414       config.peer_max_concurrent_streams = strtoul(optarg, nullptr, 10);
2415       break;
2416     case 'O':
2417       config.remote_name = true;
2418       break;
2419     case 'h':
2420       print_help(std::cout);
2421       exit(EXIT_SUCCESS);
2422     case 'b':
2423       config.padding = strtol(optarg, nullptr, 10);
2424       break;
2425     case 'n':
2426       config.null_out = true;
2427       break;
2428     case 'p': {
2429       errno = 0;
2430       auto n = strtoul(optarg, nullptr, 10);
2431       if (errno == 0 && NGHTTP2_MIN_WEIGHT <= n && n <= NGHTTP2_MAX_WEIGHT) {
2432         config.weight = n;
2433       } else {
2434         std::cerr << "-p: specify the integer in the range ["
2435                   << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT
2436                   << "], inclusive" << std::endl;
2437         exit(EXIT_FAILURE);
2438       }
2439       break;
2440     }
2441     case 'r':
2442 #ifdef HAVE_JANSSON
2443       config.harfile = optarg;
2444 #else  // !HAVE_JANSSON
2445       std::cerr << "[WARNING]: -r, --har option is ignored because\n"
2446                 << "the binary was not compiled with libjansson." << std::endl;
2447 #endif // !HAVE_JANSSON
2448       break;
2449     case 'v':
2450       ++config.verbose;
2451       break;
2452     case 't':
2453       config.timeout = atoi(optarg);
2454       break;
2455     case 'u':
2456       config.upgrade = true;
2457       break;
2458     case 'w':
2459     case 'W': {
2460       errno = 0;
2461       char *endptr = nullptr;
2462       unsigned long int n = strtoul(optarg, &endptr, 10);
2463       if (errno == 0 && *endptr == '\0' && n < 31) {
2464         if (c == 'w') {
2465           config.window_bits = n;
2466         } else {
2467           config.connection_window_bits = n;
2468         }
2469       } else {
2470         std::cerr << "-" << static_cast<char>(c)
2471                   << ": specify the integer in the range [0, 30], inclusive"
2472                   << std::endl;
2473         exit(EXIT_FAILURE);
2474       }
2475       break;
2476     }
2477     case 'H': {
2478       char *header = optarg;
2479       // Skip first possible ':' in the header name
2480       char *value = strchr(optarg + 1, ':');
2481       if (!value || (header[0] == ':' && header + 1 == value)) {
2482         std::cerr << "-H: invalid header: " << optarg << std::endl;
2483         exit(EXIT_FAILURE);
2484       }
2485       *value = 0;
2486       value++;
2487       while (isspace(*value)) {
2488         value++;
2489       }
2490       if (*value == 0) {
2491         // This could also be a valid case for suppressing a header
2492         // similar to curl
2493         std::cerr << "-H: invalid header - value missing: " << optarg
2494                   << std::endl;
2495         exit(EXIT_FAILURE);
2496       }
2497       // To test "never index" repr, don't index authorization header
2498       // field unconditionally.
2499       auto no_index = util::strieq("authorization", header);
2500       config.headers.emplace_back(header, value, no_index);
2501       util::inp_strlower(config.headers.back().name);
2502       break;
2503     }
2504     case 'a':
2505 #ifdef HAVE_LIBXML2
2506       config.get_assets = true;
2507 #else  // !HAVE_LIBXML2
2508       std::cerr << "[WARNING]: -a, --get-assets option is ignored because\n"
2509                 << "the binary was not compiled with libxml2." << std::endl;
2510 #endif // !HAVE_LIBXML2
2511       break;
2512     case 's':
2513       config.stat = true;
2514       break;
2515     case 'd':
2516       config.datafile = optarg;
2517       break;
2518     case 'm':
2519       config.multiply = strtoul(optarg, nullptr, 10);
2520       break;
2521     case 'c':
2522       errno = 0;
2523       config.header_table_size = util::parse_uint_with_unit(optarg);
2524       if (config.header_table_size == -1) {
2525         std::cerr << "-c: Bad option value: " << optarg << std::endl;
2526         exit(EXIT_FAILURE);
2527       }
2528       break;
2529     case '?':
2530       util::show_candidates(argv[optind - 1], long_options);
2531       exit(EXIT_FAILURE);
2532     case 0:
2533       switch (flag) {
2534       case 1:
2535         // cert option
2536         config.certfile = optarg;
2537         break;
2538       case 2:
2539         // key option
2540         config.keyfile = optarg;
2541         break;
2542       case 3:
2543         // color option
2544         color = true;
2545         break;
2546       case 4:
2547         // continuation option
2548         config.continuation = true;
2549         break;
2550       case 5:
2551         // version option
2552         print_version(std::cout);
2553         exit(EXIT_SUCCESS);
2554       case 6:
2555         // no-content-length option
2556         config.no_content_length = true;
2557         break;
2558       case 7:
2559         // no-dep option
2560         config.no_dep = true;
2561         break;
2562       case 8:
2563         // dep-idle option
2564         config.dep_idle = true;
2565         break;
2566       }
2567       break;
2568     default:
2569       break;
2570     }
2571   }
2572
2573   set_color_output(color || isatty(fileno(stdout)));
2574
2575   nghttp2_option_set_peer_max_concurrent_streams(
2576       config.http2_option, config.peer_max_concurrent_streams);
2577
2578   struct sigaction act;
2579   memset(&act, 0, sizeof(struct sigaction));
2580   act.sa_handler = SIG_IGN;
2581   sigaction(SIGPIPE, &act, nullptr);
2582   OPENSSL_config(nullptr);
2583   OpenSSL_add_all_algorithms();
2584   SSL_load_error_strings();
2585   SSL_library_init();
2586   reset_timer();
2587   return run(argv + optind, argc - optind);
2588 }
2589
2590 } // namespace nghttp2
2591
2592 int main(int argc, char **argv) { return nghttp2::main(argc, argv); }