tizen 2.4 release
[external/nghttp2.git] / src / h2load.cc
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2014 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 "h2load.h"
26
27 #include <getopt.h>
28 #include <signal.h>
29 #include <netinet/in.h>
30 #include <netinet/tcp.h>
31
32 #include <cstdio>
33 #include <cassert>
34 #include <cstdlib>
35 #include <iostream>
36 #include <iomanip>
37 #include <fstream>
38 #include <chrono>
39 #include <thread>
40 #include <future>
41
42 #ifdef HAVE_SPDYLAY
43 #include <spdylay/spdylay.h>
44 #endif // HAVE_SPDYLAY
45
46 #include <openssl/err.h>
47 #include <openssl/conf.h>
48
49 #include "http-parser/http_parser.h"
50
51 #include "h2load_http2_session.h"
52 #ifdef HAVE_SPDYLAY
53 #include "h2load_spdy_session.h"
54 #endif // HAVE_SPDYLAY
55 #include "ssl.h"
56 #include "http2.h"
57 #include "util.h"
58 #include "template.h"
59
60 using namespace nghttp2;
61
62 namespace h2load {
63
64 Config::Config()
65     : addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
66       max_concurrent_streams(-1), window_bits(16), connection_window_bits(16),
67       no_tls_proto(PROTO_HTTP2), port(0), default_port(0), verbose(false) {}
68
69 Config::~Config() { freeaddrinfo(addrs); }
70
71 Config config;
72
73 namespace {
74 void debug(const char *format, ...) {
75   if (config.verbose) {
76     fprintf(stderr, "[DEBUG] ");
77     va_list ap;
78     va_start(ap, format);
79     vfprintf(stderr, format, ap);
80     va_end(ap);
81   }
82 }
83 } // namespace
84
85 namespace {
86 void debug_nextproto_error() {
87 #ifdef HAVE_SPDYLAY
88   debug("no supported protocol was negotiated, expected: %s, "
89         "spdy/2, spdy/3, spdy/3.1\n",
90         NGHTTP2_PROTO_VERSION_ID);
91 #else  // !HAVE_SPDYLAY
92   debug("no supported protocol was negotiated, expected: %s\n",
93         NGHTTP2_PROTO_VERSION_ID);
94 #endif // !HAVE_SPDYLAY
95 }
96 } // namespace
97
98 RequestStat::RequestStat() : completed(false) {}
99
100 Stats::Stats(size_t req_todo)
101     : req_todo(0), req_started(0), req_done(0), req_success(0),
102       req_status_success(0), req_failed(0), req_error(0), bytes_total(0),
103       bytes_head(0), bytes_body(0), status(), req_stats(req_todo) {}
104
105 Stream::Stream() : status_success(-1) {}
106
107 namespace {
108 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
109   auto client = static_cast<Client *>(w->data);
110   if (client->do_read() != 0) {
111     client->fail();
112   }
113 }
114 } // namespace
115
116 namespace {
117 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
118   auto client = static_cast<Client *>(w->data);
119   auto rv = client->do_write();
120   if (rv == Client::ERR_CONNECT_FAIL) {
121     client->disconnect();
122     rv = client->connect();
123     if (rv != 0) {
124       client->fail();
125       return;
126     }
127     return;
128   }
129   if (rv != 0) {
130     client->fail();
131   }
132 }
133 } // namespace
134
135 Client::Client(Worker *worker, size_t req_todo)
136     : worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0),
137       state(CLIENT_IDLE), req_todo(req_todo), req_started(0), req_done(0),
138       fd(-1) {
139   ev_io_init(&wev, writecb, 0, EV_WRITE);
140   ev_io_init(&rev, readcb, 0, EV_READ);
141
142   wev.data = this;
143   rev.data = this;
144 }
145
146 Client::~Client() { disconnect(); }
147
148 int Client::do_read() { return readfn(*this); }
149 int Client::do_write() { return writefn(*this); }
150
151 int Client::connect() {
152   while (next_addr) {
153     auto addr = next_addr;
154     next_addr = next_addr->ai_next;
155     fd = util::create_nonblock_socket(addr->ai_family);
156     if (fd == -1) {
157       continue;
158     }
159     if (config.scheme == "https") {
160       ssl = SSL_new(worker->ssl_ctx);
161
162       auto config = worker->config;
163
164       if (!util::numeric_host(config->host.c_str())) {
165         SSL_set_tlsext_host_name(ssl, config->host.c_str());
166       }
167
168       SSL_set_fd(ssl, fd);
169       SSL_set_connect_state(ssl);
170     }
171
172     auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
173     if (rv != 0 && errno != EINPROGRESS) {
174       if (ssl) {
175         SSL_free(ssl);
176         ssl = nullptr;
177       }
178       close(fd);
179       fd = -1;
180       continue;
181     }
182     break;
183   }
184
185   if (fd == -1) {
186     return -1;
187   }
188
189   writefn = &Client::connected;
190
191   on_readfn = &Client::on_read;
192   on_writefn = &Client::on_write;
193
194   ev_io_set(&rev, fd, EV_READ);
195   ev_io_set(&wev, fd, EV_WRITE);
196
197   ev_io_start(worker->loop, &wev);
198
199   return 0;
200 }
201
202 void Client::fail() {
203   process_abandoned_streams();
204
205   disconnect();
206 }
207
208 void Client::disconnect() {
209   streams.clear();
210   session.reset();
211   state = CLIENT_IDLE;
212   ev_io_stop(worker->loop, &wev);
213   ev_io_stop(worker->loop, &rev);
214   if (ssl) {
215     SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
216     ERR_clear_error();
217     SSL_shutdown(ssl);
218     SSL_free(ssl);
219     ssl = nullptr;
220   }
221   if (fd != -1) {
222     shutdown(fd, SHUT_WR);
223     close(fd);
224     fd = -1;
225   }
226 }
227
228 void Client::submit_request() {
229   auto req_stat = &worker->stats.req_stats[worker->stats.req_started++];
230   session->submit_request(req_stat);
231   ++req_started;
232 }
233
234 void Client::process_abandoned_streams() {
235   auto req_abandoned = req_todo - req_done;
236
237   worker->stats.req_failed += req_abandoned;
238   worker->stats.req_error += req_abandoned;
239   worker->stats.req_done += req_abandoned;
240
241   req_done = req_todo;
242 }
243
244 void Client::report_progress() {
245   if (worker->id == 0 &&
246       worker->stats.req_done % worker->progress_interval == 0) {
247     std::cout << "progress: "
248               << worker->stats.req_done * 100 / worker->stats.req_todo
249               << "% done" << std::endl;
250   }
251 }
252
253 namespace {
254 const char *get_tls_protocol(SSL *ssl) {
255   auto session = SSL_get_session(ssl);
256
257   switch (session->ssl_version) {
258   case SSL2_VERSION:
259     return "SSLv2";
260   case SSL3_VERSION:
261     return "SSLv3";
262   case TLS1_2_VERSION:
263     return "TLSv1.2";
264   case TLS1_1_VERSION:
265     return "TLSv1.1";
266   case TLS1_VERSION:
267     return "TLSv1";
268   default:
269     return "unknown";
270   }
271 }
272 } // namespace
273
274 namespace {
275 void print_server_tmp_key(SSL *ssl) {
276 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
277   EVP_PKEY *key;
278
279   if (!SSL_get_server_tmp_key(ssl, &key)) {
280     return;
281   }
282
283   auto key_del = defer(EVP_PKEY_free, key);
284
285   std::cout << "Server Temp Key: ";
286
287   switch (EVP_PKEY_id(key)) {
288   case EVP_PKEY_RSA:
289     std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl;
290     break;
291   case EVP_PKEY_DH:
292     std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl;
293     break;
294   case EVP_PKEY_EC: {
295     auto ec = EVP_PKEY_get1_EC_KEY(key);
296     auto ec_del = defer(EC_KEY_free, ec);
297     auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
298     auto cname = EC_curve_nid2nist(nid);
299     if (!cname) {
300       cname = OBJ_nid2sn(nid);
301     }
302
303     std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits"
304               << std::endl;
305     break;
306   }
307   }
308 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
309 }
310 } // namespace
311
312 void Client::report_tls_info() {
313   if (worker->id == 0 && !worker->tls_info_report_done) {
314     worker->tls_info_report_done = true;
315     auto cipher = SSL_get_current_cipher(ssl);
316     std::cout << "Protocol: " << get_tls_protocol(ssl) << "\n"
317               << "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl;
318     print_server_tmp_key(ssl);
319   }
320 }
321
322 void Client::terminate_session() { session->terminate(); }
323
324 void Client::on_request(int32_t stream_id) { streams[stream_id] = Stream(); }
325
326 void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
327                        const uint8_t *value, size_t valuelen) {
328   auto itr = streams.find(stream_id);
329   if (itr == std::end(streams)) {
330     return;
331   }
332   auto &stream = (*itr).second;
333   if (stream.status_success == -1 && namelen == 7 &&
334       util::streq(":status", 7, name, namelen)) {
335     int status = 0;
336     for (size_t i = 0; i < valuelen; ++i) {
337       if ('0' <= value[i] && value[i] <= '9') {
338         status *= 10;
339         status += value[i] - '0';
340         if (status > 999) {
341           stream.status_success = 0;
342           return;
343         }
344       } else {
345         break;
346       }
347     }
348
349     if (status >= 200 && status < 300) {
350       ++worker->stats.status[2];
351       stream.status_success = 1;
352     } else if (status < 400) {
353       ++worker->stats.status[3];
354       stream.status_success = 1;
355     } else if (status < 600) {
356       ++worker->stats.status[status / 100];
357       stream.status_success = 0;
358     } else {
359       stream.status_success = 0;
360     }
361   }
362 }
363
364 void Client::on_stream_close(int32_t stream_id, bool success,
365                              RequestStat *req_stat) {
366   req_stat->stream_close_time = std::chrono::steady_clock::now();
367   if (success) {
368     req_stat->completed = true;
369     ++worker->stats.req_success;
370   }
371   ++worker->stats.req_done;
372   ++req_done;
373   if (success && streams[stream_id].status_success == 1) {
374     ++worker->stats.req_status_success;
375   } else {
376     ++worker->stats.req_failed;
377   }
378   report_progress();
379   streams.erase(stream_id);
380   if (req_done == req_todo) {
381     terminate_session();
382     return;
383   }
384
385   if (req_started < req_todo) {
386     submit_request();
387     return;
388   }
389 }
390
391 int Client::noop() { return 0; }
392
393 int Client::on_connect() {
394   if (ssl) {
395     report_tls_info();
396
397     const unsigned char *next_proto = nullptr;
398     unsigned int next_proto_len;
399     SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
400     for (int i = 0; i < 2; ++i) {
401       if (next_proto) {
402         if (util::check_h2_is_selected(next_proto, next_proto_len)) {
403           session = make_unique<Http2Session>(this);
404         } else {
405 #ifdef HAVE_SPDYLAY
406           auto spdy_version =
407               spdylay_npn_get_version(next_proto, next_proto_len);
408           if (spdy_version) {
409             session = make_unique<SpdySession>(this, spdy_version);
410           } else {
411             debug_nextproto_error();
412             fail();
413             return -1;
414           }
415 #else  // !HAVE_SPDYLAY
416           debug_nextproto_error();
417           fail();
418           return -1;
419 #endif // !HAVE_SPDYLAY
420         }
421       }
422
423 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
424       SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
425 #else  // OPENSSL_VERSION_NUMBER < 0x10002000L
426       break;
427 #endif // OPENSSL_VERSION_NUMBER < 0x10002000L
428     }
429
430     if (!next_proto) {
431       debug_nextproto_error();
432       fail();
433       return -1;
434     }
435   } else {
436     switch (config.no_tls_proto) {
437     case Config::PROTO_HTTP2:
438       session = make_unique<Http2Session>(this);
439       break;
440 #ifdef HAVE_SPDYLAY
441     case Config::PROTO_SPDY2:
442       session = make_unique<SpdySession>(this, SPDYLAY_PROTO_SPDY2);
443       break;
444     case Config::PROTO_SPDY3:
445       session = make_unique<SpdySession>(this, SPDYLAY_PROTO_SPDY3);
446       break;
447     case Config::PROTO_SPDY3_1:
448       session = make_unique<SpdySession>(this, SPDYLAY_PROTO_SPDY3_1);
449       break;
450 #endif // HAVE_SPDYLAY
451     default:
452       // unreachable
453       assert(0);
454     }
455   }
456
457   state = CLIENT_CONNECTED;
458
459   session->on_connect();
460
461   auto nreq =
462       std::min(req_todo - req_started, (size_t)config.max_concurrent_streams);
463
464   for (; nreq > 0; --nreq) {
465     submit_request();
466   }
467
468   signal_write();
469
470   return 0;
471 }
472
473 int Client::on_read(const uint8_t *data, size_t len) {
474   auto rv = session->on_read(data, len);
475   if (rv != 0) {
476     return -1;
477   }
478   worker->stats.bytes_total += len;
479   signal_write();
480   return 0;
481 }
482
483 int Client::on_write() {
484   if (session->on_write() != 0) {
485     return -1;
486   }
487   return 0;
488 }
489
490 int Client::read_clear() {
491   uint8_t buf[8192];
492
493   for (;;) {
494     ssize_t nread;
495     while ((nread = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR)
496       ;
497     if (nread == -1) {
498       if (errno == EAGAIN || errno == EWOULDBLOCK) {
499         return 0;
500       }
501       return -1;
502     }
503
504     if (nread == 0) {
505       return -1;
506     }
507
508     if (on_read(buf, nread) != 0) {
509       return -1;
510     }
511   }
512
513   return 0;
514 }
515
516 int Client::write_clear() {
517   for (;;) {
518     if (wb.rleft() > 0) {
519       ssize_t nwrite;
520       while ((nwrite = write(fd, wb.pos, wb.rleft())) == -1 && errno == EINTR)
521         ;
522       if (nwrite == -1) {
523         if (errno == EAGAIN || errno == EWOULDBLOCK) {
524           ev_io_start(worker->loop, &wev);
525           return 0;
526         }
527         return -1;
528       }
529       wb.drain(nwrite);
530       continue;
531     }
532
533     if (on_write() != 0) {
534       return -1;
535     }
536     if (wb.rleft() == 0) {
537       wb.reset();
538       break;
539     }
540   }
541
542   ev_io_stop(worker->loop, &wev);
543
544   return 0;
545 }
546
547 int Client::connected() {
548   if (!util::check_socket_connected(fd)) {
549     return ERR_CONNECT_FAIL;
550   }
551   ev_io_start(worker->loop, &rev);
552   ev_io_stop(worker->loop, &wev);
553
554   if (ssl) {
555     readfn = &Client::tls_handshake;
556     writefn = &Client::tls_handshake;
557
558     return do_write();
559   }
560
561   readfn = &Client::read_clear;
562   writefn = &Client::write_clear;
563
564   if (on_connect() != 0) {
565     return -1;
566   }
567
568   return 0;
569 }
570
571 int Client::tls_handshake() {
572   ERR_clear_error();
573
574   auto rv = SSL_do_handshake(ssl);
575
576   if (rv == 0) {
577     return -1;
578   }
579
580   if (rv < 0) {
581     auto err = SSL_get_error(ssl, rv);
582     switch (err) {
583     case SSL_ERROR_WANT_READ:
584       ev_io_stop(worker->loop, &wev);
585       return 0;
586     case SSL_ERROR_WANT_WRITE:
587       ev_io_start(worker->loop, &wev);
588       return 0;
589     default:
590       return -1;
591     }
592   }
593
594   ev_io_stop(worker->loop, &wev);
595
596   readfn = &Client::read_tls;
597   writefn = &Client::write_tls;
598
599   if (on_connect() != 0) {
600     return -1;
601   }
602
603   return 0;
604 }
605
606 int Client::read_tls() {
607   uint8_t buf[8192];
608
609   ERR_clear_error();
610
611   for (;;) {
612     auto rv = SSL_read(ssl, buf, sizeof(buf));
613
614     if (rv == 0) {
615       return -1;
616     }
617
618     if (rv < 0) {
619       auto err = SSL_get_error(ssl, rv);
620       switch (err) {
621       case SSL_ERROR_WANT_READ:
622         return 0;
623       case SSL_ERROR_WANT_WRITE:
624         // renegotiation started
625         return -1;
626       default:
627         return -1;
628       }
629     }
630
631     if (on_read(buf, rv) != 0) {
632       return -1;
633     }
634   }
635 }
636
637 int Client::write_tls() {
638   ERR_clear_error();
639
640   for (;;) {
641     if (wb.rleft() > 0) {
642       auto rv = SSL_write(ssl, wb.pos, wb.rleft());
643
644       if (rv == 0) {
645         return -1;
646       }
647
648       if (rv < 0) {
649         auto err = SSL_get_error(ssl, rv);
650         switch (err) {
651         case SSL_ERROR_WANT_READ:
652           // renegotiation started
653           return -1;
654         case SSL_ERROR_WANT_WRITE:
655           ev_io_start(worker->loop, &wev);
656           return 0;
657         default:
658           return -1;
659         }
660       }
661
662       wb.drain(rv);
663
664       continue;
665     }
666     if (on_write() != 0) {
667       return -1;
668     }
669     if (wb.rleft() == 0) {
670       break;
671     }
672   }
673
674   ev_io_stop(worker->loop, &wev);
675
676   return 0;
677 }
678
679 void Client::record_request_time(RequestStat *req_stat) {
680   req_stat->request_time = std::chrono::steady_clock::now();
681 }
682
683 void Client::signal_write() { ev_io_start(worker->loop, &wev); }
684
685 Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
686                Config *config)
687     : stats(req_todo), loop(ev_loop_new(0)), ssl_ctx(ssl_ctx), config(config),
688       id(id), tls_info_report_done(false) {
689   stats.req_todo = req_todo;
690   progress_interval = std::max((size_t)1, req_todo / 10);
691
692   auto nreqs_per_client = req_todo / nclients;
693   auto nreqs_rem = req_todo % nclients;
694
695   for (size_t i = 0; i < nclients; ++i) {
696     auto req_todo = nreqs_per_client;
697     if (nreqs_rem > 0) {
698       ++req_todo;
699       --nreqs_rem;
700     }
701     clients.push_back(make_unique<Client>(this, req_todo));
702   }
703 }
704
705 Worker::~Worker() {
706   // first clear clients so that io watchers are stopped before
707   // destructing ev_loop.
708   clients.clear();
709   ev_loop_destroy(loop);
710 }
711
712 void Worker::run() {
713   for (auto &client : clients) {
714     if (client->connect() != 0) {
715       std::cerr << "client could not connect to host" << std::endl;
716       client->fail();
717     }
718   }
719   ev_run(loop, 0);
720 }
721
722 namespace {
723 double within_sd(const std::vector<std::unique_ptr<Worker>> &workers,
724                  const std::chrono::microseconds &mean,
725                  const std::chrono::microseconds &sd, size_t n) {
726   auto upper = mean.count() + sd.count();
727   auto lower = mean.count() - sd.count();
728   size_t m = 0;
729   for (const auto &w : workers) {
730     for (const auto &req_stat : w->stats.req_stats) {
731       if (!req_stat.completed) {
732         continue;
733       }
734       auto t = std::chrono::duration_cast<std::chrono::microseconds>(
735           req_stat.stream_close_time - req_stat.request_time);
736       if (lower <= t.count() && t.count() <= upper) {
737         ++m;
738       }
739     }
740   }
741   return (m / static_cast<double>(n)) * 100;
742 }
743 } // namespace
744
745 namespace {
746 TimeStats
747 process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
748   auto ts = TimeStats();
749   int64_t sum = 0;
750   size_t n = 0;
751
752   ts.time_min = std::chrono::microseconds::max();
753   ts.time_max = std::chrono::microseconds::min();
754   ts.within_sd = 0.;
755
756   // standard deviation calculated using Rapid calculation method:
757   // http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
758   double a = 0, q = 0;
759   for (const auto &w : workers) {
760     for (const auto &req_stat : w->stats.req_stats) {
761       if (!req_stat.completed) {
762         continue;
763       }
764       ++n;
765       auto t = std::chrono::duration_cast<std::chrono::microseconds>(
766           req_stat.stream_close_time - req_stat.request_time);
767       ts.time_min = std::min(ts.time_min, t);
768       ts.time_max = std::max(ts.time_max, t);
769       sum += t.count();
770
771       auto na = a + (t.count() - a) / n;
772       q = q + (t.count() - a) * (t.count() - na);
773       a = na;
774     }
775   }
776   if (n == 0) {
777     ts.time_max = ts.time_min = std::chrono::microseconds::zero();
778     return ts;
779   }
780
781   ts.time_mean = std::chrono::microseconds(sum / n);
782   ts.time_sd = std::chrono::microseconds(
783       static_cast<std::chrono::microseconds::rep>(sqrt(q / n)));
784
785   ts.within_sd = within_sd(workers, ts.time_mean, ts.time_sd, n);
786   return ts;
787 }
788 } // namespace
789
790 namespace {
791 void resolve_host() {
792   int rv;
793   addrinfo hints, *res;
794
795   memset(&hints, 0, sizeof(hints));
796   hints.ai_family = AF_UNSPEC;
797   hints.ai_socktype = SOCK_STREAM;
798   hints.ai_protocol = 0;
799   hints.ai_flags = AI_ADDRCONFIG;
800
801   rv = getaddrinfo(config.host.c_str(), util::utos(config.port).c_str(), &hints,
802                    &res);
803   if (rv != 0) {
804     std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl;
805     exit(EXIT_FAILURE);
806   }
807   if (res == nullptr) {
808     std::cerr << "No address returned" << std::endl;
809     exit(EXIT_FAILURE);
810   }
811   config.addrs = res;
812 }
813 } // namespace
814
815 namespace {
816 std::string get_reqline(const char *uri, const http_parser_url &u) {
817   std::string reqline;
818
819   if (util::has_uri_field(u, UF_PATH)) {
820     reqline = util::get_uri_field(uri, u, UF_PATH);
821   } else {
822     reqline = "/";
823   }
824
825   if (util::has_uri_field(u, UF_QUERY)) {
826     reqline += "?";
827     reqline += util::get_uri_field(uri, u, UF_QUERY);
828   }
829
830   return reqline;
831 }
832 } // namespace
833
834 namespace {
835 int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
836                                 unsigned char *outlen, const unsigned char *in,
837                                 unsigned int inlen, void *arg) {
838   if (util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
839                       inlen)) {
840     return SSL_TLSEXT_ERR_OK;
841   }
842 #ifdef HAVE_SPDYLAY
843   if (spdylay_select_next_protocol(out, outlen, in, inlen) > 0) {
844     return SSL_TLSEXT_ERR_OK;
845   }
846 #endif
847   return SSL_TLSEXT_ERR_NOACK;
848 }
849 } // namespace
850
851 namespace {
852 template <typename Iterator>
853 std::vector<std::string> parse_uris(Iterator first, Iterator last) {
854   std::vector<std::string> reqlines;
855
856   // First URI is treated specially.  We use scheme, host and port of
857   // this URI and ignore those in the remaining URIs if present.
858   http_parser_url u;
859   memset(&u, 0, sizeof(u));
860
861   if (first == last) {
862     std::cerr << "no URI available" << std::endl;
863     exit(EXIT_FAILURE);
864   }
865
866   auto uri = (*first).c_str();
867   ++first;
868
869   if (http_parser_parse_url(uri, strlen(uri), 0, &u) != 0 ||
870       !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) {
871     std::cerr << "invalid URI: " << uri << std::endl;
872     exit(EXIT_FAILURE);
873   }
874
875   config.scheme = util::get_uri_field(uri, u, UF_SCHEMA);
876   config.host = util::get_uri_field(uri, u, UF_HOST);
877   config.default_port = util::get_default_port(uri, u);
878   if (util::has_uri_field(u, UF_PORT)) {
879     config.port = u.port;
880   } else {
881     config.port = config.default_port;
882   }
883
884   reqlines.push_back(get_reqline(uri, u));
885
886   for (; first != last; ++first) {
887     http_parser_url u;
888     memset(&u, 0, sizeof(u));
889
890     auto uri = (*first).c_str();
891
892     if (http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) {
893       std::cerr << "invalid URI: " << uri << std::endl;
894       exit(EXIT_FAILURE);
895     }
896
897     reqlines.push_back(get_reqline(uri, u));
898   }
899
900   return reqlines;
901 }
902 } // namespace
903
904 namespace {
905 std::vector<std::string> read_uri_from_file(std::istream &infile) {
906   std::vector<std::string> uris;
907   std::string line_uri;
908   while (std::getline(infile, line_uri)) {
909     uris.push_back(line_uri);
910   }
911
912   return uris;
913 }
914 } // namespace
915
916 namespace {
917 void print_version(std::ostream &out) {
918   out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl;
919 }
920 } // namespace
921
922 namespace {
923 void print_usage(std::ostream &out) {
924   out << R"(Usage: h2load [OPTIONS]... [URI]...
925 benchmarking tool for HTTP/2 and SPDY server)" << std::endl;
926 }
927 } // namespace
928
929 namespace {
930 void print_help(std::ostream &out) {
931   print_usage(out);
932
933   out << R"(
934   <URI>       Specify URI to access.   Multiple URIs can be specified.
935               URIs are used  in this order for each  client.  All URIs
936               are used, then  first URI is used and then  2nd URI, and
937               so  on.  The  scheme, host  and port  in the  subsequent
938               URIs, if present,  are ignored.  Those in  the first URI
939               are used solely.
940 Options:
941   -n, --requests=<N>
942               Number of requests.
943               Default: )" << config.nreqs << R"(
944   -c, --clients=<N>
945               Number of concurrent clients.
946               Default: )" << config.nclients << R"(
947   -t, --threads=<N>
948               Number of native threads.
949               Default: )" << config.nthreads << R"(
950   -i, --input-file=<FILE>
951               Path of a file with multiple URIs are seperated by EOLs.
952               This option will disable URIs getting from command-line.
953               If '-' is given as <FILE>, URIs will be read from stdin.
954               URIs are used  in this order for each  client.  All URIs
955               are used, then  first URI is used and then  2nd URI, and
956               so  on.  The  scheme, host  and port  in the  subsequent
957               URIs, if present,  are ignored.  Those in  the first URI
958               are used solely.
959   -m, --max-concurrent-streams=(auto|<N>)
960               Max concurrent streams to  issue per session.  If "auto"
961               is given, the number of given URIs is used.
962               Default: auto
963   -w, --window-bits=<N>
964               Sets the stream level initial window size to (2**<N>)-1.
965               For SPDY, 2**<N> is used instead.
966   -W, --connection-window-bits=<N>
967               Sets  the  connection  level   initial  window  size  to
968               (2**<N>)-1.  For SPDY, if <N>  is strictly less than 16,
969               this option  is ignored.   Otherwise 2**<N> is  used for
970               SPDY.
971   -H, --header=<HEADER>
972               Add/Override a header to the requests.
973   -p, --no-tls-proto=<PROTOID>
974               Specify ALPN identifier of the  protocol to be used when
975               accessing http URI without SSL/TLS.)";
976 #ifdef HAVE_SPDYLAY
977   out << R"(
978               Available protocols: spdy/2, spdy/3, spdy/3.1 and )";
979 #else  // !HAVE_SPDYLAY
980   out << R"(
981               Available protocol: )";
982 #endif // !HAVE_SPDYLAY
983   out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
984               Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
985   -v, --verbose
986               Output debug information.
987   --version   Display version information and exit.
988   -h, --help  Display this help and exit.)" << std::endl;
989 }
990 } // namespace
991
992 int main(int argc, char **argv) {
993   while (1) {
994     static int flag = 0;
995     static option long_options[] = {
996         {"requests", required_argument, nullptr, 'n'},
997         {"clients", required_argument, nullptr, 'c'},
998         {"threads", required_argument, nullptr, 't'},
999         {"max-concurrent-streams", required_argument, nullptr, 'm'},
1000         {"window-bits", required_argument, nullptr, 'w'},
1001         {"connection-window-bits", required_argument, nullptr, 'W'},
1002         {"input-file", required_argument, nullptr, 'i'},
1003         {"header", required_argument, nullptr, 'H'},
1004         {"no-tls-proto", required_argument, nullptr, 'p'},
1005         {"verbose", no_argument, nullptr, 'v'},
1006         {"help", no_argument, nullptr, 'h'},
1007         {"version", no_argument, &flag, 1},
1008         {nullptr, 0, nullptr, 0}};
1009     int option_index = 0;
1010     auto c = getopt_long(argc, argv, "hvW:c:m:n:p:t:w:H:i:", long_options,
1011                          &option_index);
1012     if (c == -1) {
1013       break;
1014     }
1015     switch (c) {
1016     case 'n':
1017       config.nreqs = strtoul(optarg, nullptr, 10);
1018       break;
1019     case 'c':
1020       config.nclients = strtoul(optarg, nullptr, 10);
1021       break;
1022     case 't':
1023 #ifdef NOTHREADS
1024       std::cerr << "-t: WARNING: Threading disabled at build time, "
1025                 << "no threads created." << std::endl;
1026 #else
1027       config.nthreads = strtoul(optarg, nullptr, 10);
1028 #endif // NOTHREADS
1029       break;
1030     case 'm':
1031       if (util::strieq("auto", optarg)) {
1032         config.max_concurrent_streams = -1;
1033       } else {
1034         config.max_concurrent_streams = strtoul(optarg, nullptr, 10);
1035       }
1036       break;
1037     case 'w':
1038     case 'W': {
1039       errno = 0;
1040       char *endptr = nullptr;
1041       auto n = strtoul(optarg, &endptr, 10);
1042       if (errno == 0 && *endptr == '\0' && n < 31) {
1043         if (c == 'w') {
1044           config.window_bits = n;
1045         } else {
1046           config.connection_window_bits = n;
1047         }
1048       } else {
1049         std::cerr << "-" << static_cast<char>(c)
1050                   << ": specify the integer in the range [0, 30], inclusive"
1051                   << std::endl;
1052         exit(EXIT_FAILURE);
1053       }
1054       break;
1055     }
1056     case 'H': {
1057       char *header = optarg;
1058       // Skip first possible ':' in the header name
1059       char *value = strchr(optarg + 1, ':');
1060       if (!value || (header[0] == ':' && header + 1 == value)) {
1061         std::cerr << "-H: invalid header: " << optarg << std::endl;
1062         exit(EXIT_FAILURE);
1063       }
1064       *value = 0;
1065       value++;
1066       while (isspace(*value)) {
1067         value++;
1068       }
1069       if (*value == 0) {
1070         // This could also be a valid case for suppressing a header
1071         // similar to curl
1072         std::cerr << "-H: invalid header - value missing: " << optarg
1073                   << std::endl;
1074         exit(EXIT_FAILURE);
1075       }
1076       // Note that there is no processing currently to handle multiple
1077       // message-header fields with the same field name
1078       config.custom_headers.emplace_back(header, value);
1079       util::inp_strlower(config.custom_headers.back().name);
1080       break;
1081     }
1082     case 'i': {
1083       config.ifile = std::string(optarg);
1084       break;
1085     }
1086     case 'p':
1087       if (util::strieq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, optarg)) {
1088         config.no_tls_proto = Config::PROTO_HTTP2;
1089 #ifdef HAVE_SPDYLAY
1090       } else if (util::strieq("spdy/2", optarg)) {
1091         config.no_tls_proto = Config::PROTO_SPDY2;
1092       } else if (util::strieq("spdy/3", optarg)) {
1093         config.no_tls_proto = Config::PROTO_SPDY3;
1094       } else if (util::strieq("spdy/3.1", optarg)) {
1095         config.no_tls_proto = Config::PROTO_SPDY3_1;
1096 #endif // HAVE_SPDYLAY
1097       } else {
1098         std::cerr << "-p: unsupported protocol " << optarg << std::endl;
1099         exit(EXIT_FAILURE);
1100       }
1101       break;
1102     case 'v':
1103       config.verbose = true;
1104       break;
1105     case 'h':
1106       print_help(std::cout);
1107       exit(EXIT_SUCCESS);
1108     case '?':
1109       util::show_candidates(argv[optind - 1], long_options);
1110       exit(EXIT_FAILURE);
1111     case 0:
1112       switch (flag) {
1113       case 1:
1114         // version option
1115         print_version(std::cout);
1116         exit(EXIT_SUCCESS);
1117       }
1118       break;
1119     default:
1120       break;
1121     }
1122   }
1123
1124   if (argc == optind) {
1125     if (config.ifile.empty()) {
1126       std::cerr << "no URI or input file given" << std::endl;
1127       exit(EXIT_FAILURE);
1128     }
1129   }
1130
1131   if (config.nreqs == 0) {
1132     std::cerr << "-n: the number of requests must be strictly greater than 0."
1133               << std::endl;
1134     exit(EXIT_FAILURE);
1135   }
1136
1137   if (config.max_concurrent_streams == 0) {
1138     std::cerr << "-m: the max concurrent streams must be strictly greater "
1139               << "than 0." << std::endl;
1140     exit(EXIT_FAILURE);
1141   }
1142
1143   if (config.nthreads == 0) {
1144     std::cerr << "-t: the number of threads must be strictly greater than 0."
1145               << std::endl;
1146     exit(EXIT_FAILURE);
1147   }
1148
1149   if (config.nreqs < config.nclients) {
1150     std::cerr << "-n, -c: the number of requests must be greater than or "
1151               << "equal to the concurrent clients." << std::endl;
1152     exit(EXIT_FAILURE);
1153   }
1154
1155   if (config.nthreads > std::thread::hardware_concurrency()) {
1156     std::cerr << "-t: warning: the number of threads is greater than hardware "
1157               << "cores." << std::endl;
1158   }
1159
1160   struct sigaction act;
1161   memset(&act, 0, sizeof(struct sigaction));
1162   act.sa_handler = SIG_IGN;
1163   sigaction(SIGPIPE, &act, nullptr);
1164   OPENSSL_config(nullptr);
1165   OpenSSL_add_all_algorithms();
1166   SSL_load_error_strings();
1167   SSL_library_init();
1168
1169 #ifndef NOTHREADS
1170   ssl::LibsslGlobalLock lock;
1171 #endif // NOTHREADS
1172
1173   auto ssl_ctx = SSL_CTX_new(SSLv23_client_method());
1174   if (!ssl_ctx) {
1175     std::cerr << "Failed to create SSL_CTX: "
1176               << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
1177     exit(EXIT_FAILURE);
1178   }
1179
1180   SSL_CTX_set_options(ssl_ctx,
1181                       SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
1182                           SSL_OP_NO_COMPRESSION |
1183                           SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
1184   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
1185   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
1186
1187   if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
1188     std::cerr << "SSL_CTX_set_cipher_list failed: "
1189               << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
1190     exit(EXIT_FAILURE);
1191   }
1192
1193   SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
1194                                    nullptr);
1195
1196 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1197   auto proto_list = util::get_default_alpn();
1198 #ifdef HAVE_SPDYLAY
1199   static const char spdy_proto_list[] = "\x8spdy/3.1\x6spdy/3\x6spdy/2";
1200   std::copy_n(spdy_proto_list, sizeof(spdy_proto_list) - 1,
1201               std::back_inserter(proto_list));
1202 #endif // HAVE_SPDYLAY
1203   SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
1204 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
1205
1206   std::vector<std::string> reqlines;
1207
1208   if (config.ifile.empty()) {
1209     std::vector<std::string> uris;
1210     std::copy(&argv[optind], &argv[argc], std::back_inserter(uris));
1211     reqlines = parse_uris(std::begin(uris), std::end(uris));
1212   } else {
1213     std::vector<std::string> uris;
1214     if (config.ifile == "-") {
1215       uris = read_uri_from_file(std::cin);
1216     } else {
1217       std::ifstream infile(config.ifile);
1218       if (!infile) {
1219         std::cerr << "cannot read input file: " << config.ifile << std::endl;
1220         exit(EXIT_FAILURE);
1221       }
1222
1223       uris = read_uri_from_file(infile);
1224     }
1225
1226     reqlines = parse_uris(std::begin(uris), std::end(uris));
1227   }
1228
1229   if (config.max_concurrent_streams == -1) {
1230     config.max_concurrent_streams = reqlines.size();
1231   }
1232
1233   Headers shared_nva;
1234   shared_nva.emplace_back(":scheme", config.scheme);
1235   if (config.port != config.default_port) {
1236     shared_nva.emplace_back(":authority",
1237                             config.host + ":" + util::utos(config.port));
1238   } else {
1239     shared_nva.emplace_back(":authority", config.host);
1240   }
1241   shared_nva.emplace_back(":method", "GET");
1242
1243   // list overridalbe headers
1244   auto override_hdrs =
1245       make_array<std::string>(":authority", ":host", ":method", ":scheme");
1246
1247   for (auto &kv : config.custom_headers) {
1248     if (std::find(std::begin(override_hdrs), std::end(override_hdrs),
1249                   kv.name) != std::end(override_hdrs)) {
1250       // override header
1251       for (auto &nv : shared_nva) {
1252         if ((nv.name == ":authority" && kv.name == ":host") ||
1253             (nv.name == kv.name)) {
1254           nv.value = kv.value;
1255         }
1256       }
1257     } else {
1258       // add additional headers
1259       shared_nva.push_back(kv);
1260     }
1261   }
1262
1263   for (auto &req : reqlines) {
1264     // For nghttp2
1265     std::vector<nghttp2_nv> nva;
1266
1267     nva.push_back(http2::make_nv_ls(":path", req));
1268
1269     for (auto &nv : shared_nva) {
1270       nva.push_back(http2::make_nv(nv.name, nv.value, false));
1271     }
1272
1273     config.nva.push_back(std::move(nva));
1274
1275     // For spdylay
1276     std::vector<const char *> cva;
1277
1278     cva.push_back(":path");
1279     cva.push_back(req.c_str());
1280
1281     for (auto &nv : shared_nva) {
1282       if (nv.name == ":authority") {
1283         cva.push_back(":host");
1284       } else {
1285         cva.push_back(nv.name.c_str());
1286       }
1287       cva.push_back(nv.value.c_str());
1288     }
1289     cva.push_back(":version");
1290     cva.push_back("HTTP/1.1");
1291     cva.push_back(nullptr);
1292
1293     config.nv.push_back(std::move(cva));
1294   }
1295
1296   resolve_host();
1297
1298   if (config.nclients == 1) {
1299     config.nthreads = 1;
1300   }
1301
1302   size_t nreqs_per_thread = config.nreqs / config.nthreads;
1303   ssize_t nreqs_rem = config.nreqs % config.nthreads;
1304
1305   size_t nclients_per_thread = config.nclients / config.nthreads;
1306   ssize_t nclients_rem = config.nclients % config.nthreads;
1307
1308   std::cout << "starting benchmark..." << std::endl;
1309
1310   auto start = std::chrono::steady_clock::now();
1311
1312   std::vector<std::unique_ptr<Worker>> workers;
1313   workers.reserve(config.nthreads - 1);
1314
1315 #ifndef NOTHREADS
1316   std::vector<std::future<void>> futures;
1317   for (size_t i = 0; i < config.nthreads - 1; ++i) {
1318     auto nreqs = nreqs_per_thread + (nreqs_rem-- > 0);
1319     auto nclients = nclients_per_thread + (nclients_rem-- > 0);
1320     std::cout << "spawning thread #" << i << ": " << nclients
1321               << " concurrent clients, " << nreqs << " total requests"
1322               << std::endl;
1323     workers.push_back(
1324         make_unique<Worker>(i, ssl_ctx, nreqs, nclients, &config));
1325     auto &worker = workers.back();
1326     futures.push_back(
1327         std::async(std::launch::async, [&worker]() { worker->run(); }));
1328   }
1329 #endif // NOTHREADS
1330
1331   auto nreqs_last = nreqs_per_thread + (nreqs_rem-- > 0);
1332   auto nclients_last = nclients_per_thread + (nclients_rem-- > 0);
1333   std::cout << "spawning thread #" << (config.nthreads - 1) << ": "
1334             << nclients_last << " concurrent clients, " << nreqs_last
1335             << " total requests" << std::endl;
1336   workers.push_back(make_unique<Worker>(config.nthreads - 1, ssl_ctx,
1337                                         nreqs_last, nclients_last, &config));
1338   workers.back()->run();
1339
1340 #ifndef NOTHREADS
1341   for (auto &fut : futures) {
1342     fut.get();
1343   }
1344 #endif // NOTHREADS
1345
1346   auto end = std::chrono::steady_clock::now();
1347   auto duration =
1348       std::chrono::duration_cast<std::chrono::microseconds>(end - start);
1349
1350   Stats stats(0);
1351   for (const auto &w : workers) {
1352     const auto &s = w->stats;
1353
1354     stats.req_todo += s.req_todo;
1355     stats.req_started += s.req_started;
1356     stats.req_done += s.req_done;
1357     stats.req_success += s.req_success;
1358     stats.req_status_success += s.req_status_success;
1359     stats.req_failed += s.req_failed;
1360     stats.req_error += s.req_error;
1361     stats.bytes_total += s.bytes_total;
1362     stats.bytes_head += s.bytes_head;
1363     stats.bytes_body += s.bytes_body;
1364
1365     for (size_t i = 0; i < stats.status.size(); ++i) {
1366       stats.status[i] += s.status[i];
1367     }
1368   }
1369
1370   auto time_stats = process_time_stats(workers);
1371
1372   // Requests which have not been issued due to connection errors, are
1373   // counted towards req_failed and req_error.
1374   auto req_not_issued =
1375       stats.req_todo - stats.req_status_success - stats.req_failed;
1376   stats.req_failed += req_not_issued;
1377   stats.req_error += req_not_issued;
1378
1379   // UI is heavily inspired by weighttp[1] and wrk[2]
1380   //
1381   // [1] https://github.com/lighttpd/weighttp
1382   // [2] https://github.com/wg/wrk
1383   size_t rps = 0;
1384   int64_t bps = 0;
1385   if (duration.count() > 0) {
1386     auto secd = static_cast<double>(duration.count()) / (1000 * 1000);
1387     rps = stats.req_success / secd;
1388     bps = stats.bytes_total / secd;
1389   }
1390
1391   std::cout << R"(
1392 finished in )" << util::format_duration(duration) << ", " << rps << " req/s, "
1393             << util::utos_with_funit(bps) << R"(B/s
1394 requests: )" << stats.req_todo << " total, " << stats.req_started
1395             << " started, " << stats.req_done << " done, "
1396             << stats.req_status_success << " succeeded, " << stats.req_failed
1397             << " failed, " << stats.req_error << R"( errored
1398 status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
1399             << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
1400 traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head
1401             << " bytes headers, " << stats.bytes_body << R"( bytes data
1402                      min         max         mean         sd        +/- sd
1403 time for request: )" << std::setw(10)
1404             << util::format_duration(time_stats.time_min) << "  "
1405             << std::setw(10) << util::format_duration(time_stats.time_max)
1406             << "  " << std::setw(10)
1407             << util::format_duration(time_stats.time_mean) << "  "
1408             << std::setw(10) << util::format_duration(time_stats.time_sd)
1409             << std::setw(9) << util::dtos(time_stats.within_sd) << "%"
1410             << std::endl;
1411
1412   SSL_CTX_free(ssl_ctx);
1413
1414   return 0;
1415 }
1416
1417 } // namespace h2load
1418
1419 int main(int argc, char **argv) { return h2load::main(argc, argv); }