• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifdef HAVE_NETINET_IN_H
30 #  include <netinet/in.h>
31 #endif // HAVE_NETINET_IN_H
32 #include <netinet/tcp.h>
33 #include <sys/stat.h>
34 #ifdef HAVE_FCNTL_H
35 #  include <fcntl.h>
36 #endif // HAVE_FCNTL_H
37 #include <sys/mman.h>
38 #include <netinet/udp.h>
39 
40 #include <cstdio>
41 #include <cassert>
42 #include <cstdlib>
43 #include <iostream>
44 #include <iomanip>
45 #include <fstream>
46 #include <chrono>
47 #include <thread>
48 #include <future>
49 #include <random>
50 
51 #include <openssl/err.h>
52 
53 #ifdef ENABLE_HTTP3
54 #  ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
55 #    include <ngtcp2/ngtcp2_crypto_quictls.h>
56 #  endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
57 #  ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
58 #    include <ngtcp2/ngtcp2_crypto_boringssl.h>
59 #  endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
60 #endif   // ENABLE_HTTP3
61 
62 #include "url-parser/url_parser.h"
63 
64 #include "h2load_http1_session.h"
65 #include "h2load_http2_session.h"
66 #ifdef ENABLE_HTTP3
67 #  include "h2load_http3_session.h"
68 #  include "h2load_quic.h"
69 #endif // ENABLE_HTTP3
70 #include "tls.h"
71 #include "http2.h"
72 #include "util.h"
73 #include "template.h"
74 
75 #ifndef O_BINARY
76 #  define O_BINARY (0)
77 #endif // O_BINARY
78 
79 using namespace nghttp2;
80 
81 namespace h2load {
82 
83 namespace {
recorded(const std::chrono::steady_clock::time_point & t)84 bool recorded(const std::chrono::steady_clock::time_point &t) {
85   return std::chrono::steady_clock::duration::zero() != t.time_since_epoch();
86 }
87 } // namespace
88 
89 #if OPENSSL_1_1_1_API
90 namespace {
91 std::ofstream keylog_file;
keylog_callback(const SSL * ssl,const char * line)92 void keylog_callback(const SSL *ssl, const char *line) {
93   keylog_file.write(line, strlen(line));
94   keylog_file.put('\n');
95   keylog_file.flush();
96 }
97 } // namespace
98 #endif // OPENSSL_1_1_1_API
99 
Config()100 Config::Config()
101     : ciphers(tls::DEFAULT_CIPHER_LIST),
102       tls13_ciphers("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_"
103                     "CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256"),
104       groups("X25519:P-256:P-384:P-521"),
105       data_length(-1),
106       data(nullptr),
107       addrs(nullptr),
108       nreqs(1),
109       nclients(1),
110       nthreads(1),
111       max_concurrent_streams(1),
112       window_bits(30),
113       connection_window_bits(30),
114       max_frame_size(16_k),
115       rate(0),
116       rate_period(1.0),
117       duration(0.0),
118       warm_up_time(0.0),
119       conn_active_timeout(0.),
120       conn_inactivity_timeout(0.),
121       no_tls_proto(PROTO_HTTP2),
122       header_table_size(4_k),
123       encoder_header_table_size(4_k),
124       data_fd(-1),
125       log_fd(-1),
126       qlog_file_base(),
127       port(0),
128       default_port(0),
129       connect_to_port(0),
130       verbose(false),
131       timing_script(false),
132       base_uri_unix(false),
133       unix_addr{},
134       rps(0.),
135       no_udp_gso(false),
136       max_udp_payload_size(0),
137       ktls(false) {}
138 
~Config()139 Config::~Config() {
140   if (addrs) {
141     if (base_uri_unix) {
142       delete addrs;
143     } else {
144       freeaddrinfo(addrs);
145     }
146   }
147 
148   if (data_fd != -1) {
149     close(data_fd);
150   }
151 }
152 
is_rate_mode() const153 bool Config::is_rate_mode() const { return (this->rate != 0); }
is_timing_based_mode() const154 bool Config::is_timing_based_mode() const { return (this->duration > 0); }
has_base_uri() const155 bool Config::has_base_uri() const { return (!this->base_uri.empty()); }
rps_enabled() const156 bool Config::rps_enabled() const { return this->rps > 0.0; }
is_quic() const157 bool Config::is_quic() const {
158 #ifdef ENABLE_HTTP3
159   return !npn_list.empty() &&
160          (npn_list[0] == NGHTTP3_ALPN_H3 || npn_list[0] == "\x5h3-29");
161 #else  // !ENABLE_HTTP3
162   return false;
163 #endif // !ENABLE_HTTP3
164 }
165 Config config;
166 
167 namespace {
168 constexpr size_t MAX_SAMPLES = 1000000;
169 } // namespace
170 
Stats(size_t req_todo,size_t nclients)171 Stats::Stats(size_t req_todo, size_t nclients)
172     : req_todo(req_todo),
173       req_started(0),
174       req_done(0),
175       req_success(0),
176       req_status_success(0),
177       req_failed(0),
178       req_error(0),
179       req_timedout(0),
180       bytes_total(0),
181       bytes_head(0),
182       bytes_head_decomp(0),
183       bytes_body(0),
184       status(),
185       udp_dgram_recv(0),
186       udp_dgram_sent(0) {}
187 
Stream()188 Stream::Stream() : req_stat{}, status_success(-1) {}
189 
190 namespace {
191 std::random_device rd;
192 } // namespace
193 
194 namespace {
195 std::mt19937 gen(rd());
196 } // namespace
197 
198 namespace {
sampling_init(Sampling & smp,size_t max_samples)199 void sampling_init(Sampling &smp, size_t max_samples) {
200   smp.n = 0;
201   smp.max_samples = max_samples;
202 }
203 } // namespace
204 
205 namespace {
writecb(struct ev_loop * loop,ev_io * w,int revents)206 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
207   auto client = static_cast<Client *>(w->data);
208   client->restart_timeout();
209   auto rv = client->do_write();
210   if (rv == Client::ERR_CONNECT_FAIL) {
211     client->disconnect();
212     // Try next address
213     client->current_addr = nullptr;
214     rv = client->connect();
215     if (rv != 0) {
216       client->fail();
217       client->worker->free_client(client);
218       delete client;
219       return;
220     }
221     return;
222   }
223   if (rv != 0) {
224     client->fail();
225     client->worker->free_client(client);
226     delete client;
227   }
228 }
229 } // namespace
230 
231 namespace {
readcb(struct ev_loop * loop,ev_io * w,int revents)232 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
233   auto client = static_cast<Client *>(w->data);
234   client->restart_timeout();
235   if (client->do_read() != 0) {
236     if (client->try_again_or_fail() == 0) {
237       return;
238     }
239     client->worker->free_client(client);
240     delete client;
241     return;
242   }
243   client->signal_write();
244 }
245 } // namespace
246 
247 namespace {
248 // Called every rate_period when rate mode is being used
rate_period_timeout_w_cb(struct ev_loop * loop,ev_timer * w,int revents)249 void rate_period_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
250   auto worker = static_cast<Worker *>(w->data);
251   auto nclients_per_second = worker->rate;
252   auto conns_remaining = worker->nclients - worker->nconns_made;
253   auto nclients = std::min(nclients_per_second, conns_remaining);
254 
255   for (size_t i = 0; i < nclients; ++i) {
256     auto req_todo = worker->nreqs_per_client;
257     if (worker->nreqs_rem > 0) {
258       ++req_todo;
259       --worker->nreqs_rem;
260     }
261     auto client =
262         std::make_unique<Client>(worker->next_client_id++, worker, req_todo);
263 
264     ++worker->nconns_made;
265 
266     if (client->connect() != 0) {
267       std::cerr << "client could not connect to host" << std::endl;
268       client->fail();
269     } else {
270       if (worker->config->is_timing_based_mode()) {
271         worker->clients.push_back(client.release());
272       } else {
273         client.release();
274       }
275     }
276     worker->report_rate_progress();
277   }
278   if (!worker->config->is_timing_based_mode()) {
279     if (worker->nconns_made >= worker->nclients) {
280       ev_timer_stop(worker->loop, w);
281     }
282   } else {
283     // To check whether all created clients are pushed correctly
284     assert(worker->nclients == worker->clients.size());
285   }
286 }
287 } // namespace
288 
289 namespace {
290 // Called when the duration for infinite number of requests are over
duration_timeout_cb(struct ev_loop * loop,ev_timer * w,int revents)291 void duration_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
292   auto worker = static_cast<Worker *>(w->data);
293 
294   worker->current_phase = Phase::DURATION_OVER;
295 
296   std::cout << "Main benchmark duration is over for thread #" << worker->id
297             << ". Stopping all clients." << std::endl;
298   worker->stop_all_clients();
299   std::cout << "Stopped all clients for thread #" << worker->id << std::endl;
300 }
301 } // namespace
302 
303 namespace {
304 // Called when the warmup duration for infinite number of requests are over
warmup_timeout_cb(struct ev_loop * loop,ev_timer * w,int revents)305 void warmup_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
306   auto worker = static_cast<Worker *>(w->data);
307 
308   std::cout << "Warm-up phase is over for thread #" << worker->id << "."
309             << std::endl;
310   std::cout << "Main benchmark duration is started for thread #" << worker->id
311             << "." << std::endl;
312   assert(worker->stats.req_started == 0);
313   assert(worker->stats.req_done == 0);
314 
315   for (auto client : worker->clients) {
316     if (client) {
317       assert(client->req_todo == 0);
318       assert(client->req_left == 1);
319       assert(client->req_inflight == 0);
320       assert(client->req_started == 0);
321       assert(client->req_done == 0);
322 
323       client->record_client_start_time();
324       client->clear_connect_times();
325       client->record_connect_start_time();
326     }
327   }
328 
329   worker->current_phase = Phase::MAIN_DURATION;
330 
331   ev_timer_start(worker->loop, &worker->duration_watcher);
332 }
333 } // namespace
334 
335 namespace {
rps_cb(struct ev_loop * loop,ev_timer * w,int revents)336 void rps_cb(struct ev_loop *loop, ev_timer *w, int revents) {
337   auto client = static_cast<Client *>(w->data);
338   auto &session = client->session;
339 
340   assert(!config.timing_script);
341 
342   if (client->req_left == 0) {
343     ev_timer_stop(loop, w);
344     return;
345   }
346 
347   auto now = std::chrono::steady_clock::now();
348   auto d = now - client->rps_duration_started;
349   auto n = static_cast<size_t>(
350       round(std::chrono::duration<double>(d).count() * config.rps));
351   client->rps_req_pending += n;
352   client->rps_duration_started +=
353       util::duration_from(static_cast<double>(n) / config.rps);
354 
355   if (client->rps_req_pending == 0) {
356     return;
357   }
358 
359   auto nreq = session->max_concurrent_streams() - client->rps_req_inflight;
360   if (nreq == 0) {
361     return;
362   }
363 
364   nreq = config.is_timing_based_mode() ? std::max(nreq, client->req_left)
365                                        : std::min(nreq, client->req_left);
366   nreq = std::min(nreq, client->rps_req_pending);
367 
368   client->rps_req_inflight += nreq;
369   client->rps_req_pending -= nreq;
370 
371   for (; nreq > 0; --nreq) {
372     if (client->submit_request() != 0) {
373       client->process_request_failure();
374       break;
375     }
376   }
377 
378   client->signal_write();
379 }
380 } // namespace
381 
382 namespace {
383 // Called when an a connection has been inactive for a set period of time
384 // or a fixed amount of time after all requests have been made on a
385 // connection
conn_timeout_cb(EV_P_ ev_timer * w,int revents)386 void conn_timeout_cb(EV_P_ ev_timer *w, int revents) {
387   auto client = static_cast<Client *>(w->data);
388 
389   ev_timer_stop(client->worker->loop, &client->conn_inactivity_watcher);
390   ev_timer_stop(client->worker->loop, &client->conn_active_watcher);
391 
392   if (util::check_socket_connected(client->fd)) {
393     client->timeout();
394   }
395 }
396 } // namespace
397 
398 namespace {
check_stop_client_request_timeout(Client * client,ev_timer * w)399 bool check_stop_client_request_timeout(Client *client, ev_timer *w) {
400   if (client->req_left == 0) {
401     // no more requests to make, stop timer
402     ev_timer_stop(client->worker->loop, w);
403     return true;
404   }
405 
406   return false;
407 }
408 } // namespace
409 
410 namespace {
client_request_timeout_cb(struct ev_loop * loop,ev_timer * w,int revents)411 void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
412   auto client = static_cast<Client *>(w->data);
413 
414   if (client->streams.size() >= (size_t)config.max_concurrent_streams) {
415     ev_timer_stop(client->worker->loop, w);
416     return;
417   }
418 
419   if (client->submit_request() != 0) {
420     ev_timer_stop(client->worker->loop, w);
421     client->process_request_failure();
422     return;
423   }
424   client->signal_write();
425 
426   if (check_stop_client_request_timeout(client, w)) {
427     return;
428   }
429 
430   auto duration =
431       config.timings[client->reqidx] - config.timings[client->reqidx - 1];
432 
433   while (duration < std::chrono::duration<double>(1e-9)) {
434     if (client->submit_request() != 0) {
435       ev_timer_stop(client->worker->loop, w);
436       client->process_request_failure();
437       return;
438     }
439     client->signal_write();
440     if (check_stop_client_request_timeout(client, w)) {
441       return;
442     }
443 
444     duration =
445         config.timings[client->reqidx] - config.timings[client->reqidx - 1];
446   }
447 
448   client->request_timeout_watcher.repeat = util::ev_tstamp_from(duration);
449   ev_timer_again(client->worker->loop, &client->request_timeout_watcher);
450 }
451 } // namespace
452 
Client(uint32_t id,Worker * worker,size_t req_todo)453 Client::Client(uint32_t id, Worker *worker, size_t req_todo)
454     : wb(&worker->mcpool),
455       cstat{},
456       worker(worker),
457       ssl(nullptr),
458 #ifdef ENABLE_HTTP3
459       quic{},
460 #endif // ENABLE_HTTP3
461       next_addr(config.addrs),
462       current_addr(nullptr),
463       reqidx(0),
464       state(CLIENT_IDLE),
465       req_todo(req_todo),
466       req_left(req_todo),
467       req_inflight(0),
468       req_started(0),
469       req_done(0),
470       id(id),
471       fd(-1),
472       local_addr{},
473       new_connection_requested(false),
474       final(false),
475       rps_req_pending(0),
476       rps_req_inflight(0) {
477   if (req_todo == 0) { // this means infinite number of requests are to be made
478     // This ensures that number of requests are unbounded
479     // Just a positive number is fine, we chose the first positive number
480     req_left = 1;
481   }
482   ev_io_init(&wev, writecb, 0, EV_WRITE);
483   ev_io_init(&rev, readcb, 0, EV_READ);
484 
485   wev.data = this;
486   rev.data = this;
487 
488   ev_timer_init(&conn_inactivity_watcher, conn_timeout_cb, 0.,
489                 worker->config->conn_inactivity_timeout);
490   conn_inactivity_watcher.data = this;
491 
492   ev_timer_init(&conn_active_watcher, conn_timeout_cb,
493                 worker->config->conn_active_timeout, 0.);
494   conn_active_watcher.data = this;
495 
496   ev_timer_init(&request_timeout_watcher, client_request_timeout_cb, 0., 0.);
497   request_timeout_watcher.data = this;
498 
499   ev_timer_init(&rps_watcher, rps_cb, 0., 0.);
500   rps_watcher.data = this;
501 
502 #ifdef ENABLE_HTTP3
503   ev_timer_init(&quic.pkt_timer, quic_pkt_timeout_cb, 0., 0.);
504   quic.pkt_timer.data = this;
505 
506   if (config.is_quic()) {
507     quic.tx.data = std::make_unique<uint8_t[]>(64_k);
508   }
509 
510   ngtcp2_ccerr_default(&quic.last_error);
511 #endif // ENABLE_HTTP3
512 }
513 
~Client()514 Client::~Client() {
515   disconnect();
516 
517 #ifdef ENABLE_HTTP3
518   if (config.is_quic()) {
519     quic_free();
520   }
521 #endif // ENABLE_HTTP3
522 
523   if (ssl) {
524     SSL_free(ssl);
525   }
526 
527   worker->sample_client_stat(&cstat);
528   ++worker->client_smp.n;
529 }
530 
do_read()531 int Client::do_read() { return readfn(*this); }
do_write()532 int Client::do_write() { return writefn(*this); }
533 
make_socket(addrinfo * addr)534 int Client::make_socket(addrinfo *addr) {
535   int rv;
536 
537   if (config.is_quic()) {
538 #ifdef ENABLE_HTTP3
539     fd = util::create_nonblock_udp_socket(addr->ai_family);
540     if (fd == -1) {
541       return -1;
542     }
543 
544 #  ifdef UDP_GRO
545     int val = 1;
546     if (setsockopt(fd, IPPROTO_UDP, UDP_GRO, &val, sizeof(val)) != 0) {
547       std::cerr << "setsockopt UDP_GRO failed" << std::endl;
548       return -1;
549     }
550 #  endif // UDP_GRO
551 
552     rv = util::bind_any_addr_udp(fd, addr->ai_family);
553     if (rv != 0) {
554       close(fd);
555       fd = -1;
556       return -1;
557     }
558 
559     socklen_t addrlen = sizeof(local_addr.su.storage);
560     rv = getsockname(fd, &local_addr.su.sa, &addrlen);
561     if (rv == -1) {
562       return -1;
563     }
564     local_addr.len = addrlen;
565 
566     if (quic_init(&local_addr.su.sa, local_addr.len, addr->ai_addr,
567                   addr->ai_addrlen) != 0) {
568       std::cerr << "quic_init failed" << std::endl;
569       return -1;
570     }
571 #endif // ENABLE_HTTP3
572   } else {
573     fd = util::create_nonblock_socket(addr->ai_family);
574     if (fd == -1) {
575       return -1;
576     }
577     if (config.scheme == "https") {
578       if (!ssl) {
579         ssl = SSL_new(worker->ssl_ctx);
580       }
581 
582       SSL_set_connect_state(ssl);
583     }
584   }
585 
586   if (ssl && !util::numeric_host(config.host.c_str())) {
587     SSL_set_tlsext_host_name(ssl, config.host.c_str());
588   }
589 
590   if (config.is_quic()) {
591     return 0;
592   }
593 
594   rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
595   if (rv != 0 && errno != EINPROGRESS) {
596     if (ssl) {
597       SSL_free(ssl);
598       ssl = nullptr;
599     }
600     close(fd);
601     fd = -1;
602     return -1;
603   }
604   return 0;
605 }
606 
connect()607 int Client::connect() {
608   int rv;
609 
610   if (!worker->config->is_timing_based_mode() ||
611       worker->current_phase == Phase::MAIN_DURATION) {
612     record_client_start_time();
613     clear_connect_times();
614     record_connect_start_time();
615   } else if (worker->current_phase == Phase::INITIAL_IDLE) {
616     worker->current_phase = Phase::WARM_UP;
617     std::cout << "Warm-up started for thread #" << worker->id << "."
618               << std::endl;
619     ev_timer_start(worker->loop, &worker->warmup_watcher);
620   }
621 
622   if (worker->config->conn_inactivity_timeout > 0.) {
623     ev_timer_again(worker->loop, &conn_inactivity_watcher);
624   }
625 
626   if (current_addr) {
627     rv = make_socket(current_addr);
628     if (rv == -1) {
629       return -1;
630     }
631   } else {
632     addrinfo *addr = nullptr;
633     while (next_addr) {
634       addr = next_addr;
635       next_addr = next_addr->ai_next;
636       rv = make_socket(addr);
637       if (rv == 0) {
638         break;
639       }
640     }
641 
642     if (fd == -1) {
643       return -1;
644     }
645 
646     assert(addr);
647 
648     current_addr = addr;
649   }
650 
651   ev_io_set(&rev, fd, EV_READ);
652   ev_io_set(&wev, fd, EV_WRITE);
653 
654   ev_io_start(worker->loop, &wev);
655 
656   if (config.is_quic()) {
657 #ifdef ENABLE_HTTP3
658     ev_io_start(worker->loop, &rev);
659 
660     readfn = &Client::read_quic;
661     writefn = &Client::write_quic;
662 #endif // ENABLE_HTTP3
663   } else {
664     writefn = &Client::connected;
665   }
666 
667   return 0;
668 }
669 
timeout()670 void Client::timeout() {
671   process_timedout_streams();
672 
673   disconnect();
674 }
675 
restart_timeout()676 void Client::restart_timeout() {
677   if (worker->config->conn_inactivity_timeout > 0.) {
678     ev_timer_again(worker->loop, &conn_inactivity_watcher);
679   }
680 }
681 
try_again_or_fail()682 int Client::try_again_or_fail() {
683   disconnect();
684 
685   if (new_connection_requested) {
686     new_connection_requested = false;
687 
688     if (req_left) {
689 
690       if (worker->current_phase == Phase::MAIN_DURATION) {
691         // At the moment, we don't have a facility to re-start request
692         // already in in-flight.  Make them fail.
693         worker->stats.req_failed += req_inflight;
694         worker->stats.req_error += req_inflight;
695 
696         req_inflight = 0;
697       }
698 
699       // Keep using current address
700       if (connect() == 0) {
701         return 0;
702       }
703       std::cerr << "client could not connect to host" << std::endl;
704     }
705   }
706 
707   process_abandoned_streams();
708 
709   return -1;
710 }
711 
fail()712 void Client::fail() {
713   disconnect();
714 
715   process_abandoned_streams();
716 }
717 
disconnect()718 void Client::disconnect() {
719   record_client_end_time();
720 
721 #ifdef ENABLE_HTTP3
722   if (config.is_quic()) {
723     quic_close_connection();
724   }
725 #endif // ENABLE_HTTP3
726 
727 #ifdef ENABLE_HTTP3
728   ev_timer_stop(worker->loop, &quic.pkt_timer);
729 #endif // ENABLE_HTTP3
730   ev_timer_stop(worker->loop, &conn_inactivity_watcher);
731   ev_timer_stop(worker->loop, &conn_active_watcher);
732   ev_timer_stop(worker->loop, &rps_watcher);
733   ev_timer_stop(worker->loop, &request_timeout_watcher);
734   streams.clear();
735   session.reset();
736   wb.reset();
737   state = CLIENT_IDLE;
738   ev_io_stop(worker->loop, &wev);
739   ev_io_stop(worker->loop, &rev);
740   if (ssl) {
741     if (config.is_quic()) {
742       SSL_free(ssl);
743       ssl = nullptr;
744     } else {
745       SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
746       ERR_clear_error();
747 
748       if (SSL_shutdown(ssl) != 1) {
749         SSL_free(ssl);
750         ssl = nullptr;
751       }
752     }
753   }
754   if (fd != -1) {
755     shutdown(fd, SHUT_WR);
756     close(fd);
757     fd = -1;
758   }
759 
760   final = false;
761 }
762 
submit_request()763 int Client::submit_request() {
764   if (session->submit_request() != 0) {
765     return -1;
766   }
767 
768   if (worker->current_phase != Phase::MAIN_DURATION) {
769     return 0;
770   }
771 
772   ++worker->stats.req_started;
773   ++req_started;
774   ++req_inflight;
775   if (!worker->config->is_timing_based_mode()) {
776     --req_left;
777   }
778   // if an active timeout is set and this is the last request to be submitted
779   // on this connection, start the active timeout.
780   if (worker->config->conn_active_timeout > 0. && req_left == 0) {
781     ev_timer_start(worker->loop, &conn_active_watcher);
782   }
783 
784   return 0;
785 }
786 
process_timedout_streams()787 void Client::process_timedout_streams() {
788   if (worker->current_phase != Phase::MAIN_DURATION) {
789     return;
790   }
791 
792   for (auto &p : streams) {
793     auto &req_stat = p.second.req_stat;
794     if (!req_stat.completed) {
795       req_stat.stream_close_time = std::chrono::steady_clock::now();
796     }
797   }
798 
799   worker->stats.req_timedout += req_inflight;
800 
801   process_abandoned_streams();
802 }
803 
process_abandoned_streams()804 void Client::process_abandoned_streams() {
805   if (worker->current_phase != Phase::MAIN_DURATION) {
806     return;
807   }
808 
809   auto req_abandoned = req_inflight + req_left;
810 
811   worker->stats.req_failed += req_abandoned;
812   worker->stats.req_error += req_abandoned;
813 
814   req_inflight = 0;
815   req_left = 0;
816 }
817 
process_request_failure()818 void Client::process_request_failure() {
819   if (worker->current_phase != Phase::MAIN_DURATION) {
820     return;
821   }
822 
823   worker->stats.req_failed += req_left;
824   worker->stats.req_error += req_left;
825 
826   req_left = 0;
827 
828   if (req_inflight == 0) {
829     terminate_session();
830   }
831   std::cout << "Process Request Failure:" << worker->stats.req_failed
832             << std::endl;
833 }
834 
835 namespace {
print_server_tmp_key(SSL * ssl)836 void print_server_tmp_key(SSL *ssl) {
837 // libressl does not have SSL_get_server_tmp_key
838 #if OPENSSL_VERSION_NUMBER >= 0x10002000L && defined(SSL_get_server_tmp_key)
839   EVP_PKEY *key;
840 
841   if (!SSL_get_server_tmp_key(ssl, &key)) {
842     return;
843   }
844 
845   auto key_del = defer(EVP_PKEY_free, key);
846 
847   std::cout << "Server Temp Key: ";
848 
849   auto pkey_id = EVP_PKEY_id(key);
850   switch (pkey_id) {
851   case EVP_PKEY_RSA:
852     std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl;
853     break;
854   case EVP_PKEY_DH:
855     std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl;
856     break;
857   case EVP_PKEY_EC: {
858 #  if OPENSSL_3_0_0_API
859     std::array<char, 64> curve_name;
860     const char *cname;
861     if (!EVP_PKEY_get_utf8_string_param(key, "group", curve_name.data(),
862                                         curve_name.size(), nullptr)) {
863       cname = "<unknown>";
864     } else {
865       cname = curve_name.data();
866     }
867 #  else  // !OPENSSL_3_0_0_API
868     auto ec = EVP_PKEY_get1_EC_KEY(key);
869     auto ec_del = defer(EC_KEY_free, ec);
870     auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
871     auto cname = EC_curve_nid2nist(nid);
872     if (!cname) {
873       cname = OBJ_nid2sn(nid);
874     }
875 #  endif // !OPENSSL_3_0_0_API
876 
877     std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits"
878               << std::endl;
879     break;
880   }
881   default:
882     std::cout << OBJ_nid2sn(pkey_id) << " " << EVP_PKEY_bits(key) << " bits"
883               << std::endl;
884     break;
885   }
886 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
887 }
888 } // namespace
889 
report_tls_info()890 void Client::report_tls_info() {
891   if (worker->id == 0 && !worker->tls_info_report_done) {
892     worker->tls_info_report_done = true;
893     auto cipher = SSL_get_current_cipher(ssl);
894     std::cout << "TLS Protocol: " << tls::get_tls_protocol(ssl) << "\n"
895               << "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl;
896     print_server_tmp_key(ssl);
897   }
898 }
899 
report_app_info()900 void Client::report_app_info() {
901   if (worker->id == 0 && !worker->app_info_report_done) {
902     worker->app_info_report_done = true;
903     std::cout << "Application protocol: " << selected_proto << std::endl;
904   }
905 }
906 
terminate_session()907 void Client::terminate_session() {
908 #ifdef ENABLE_HTTP3
909   if (config.is_quic()) {
910     quic.close_requested = true;
911   }
912 #endif // ENABLE_HTTP3
913   if (session) {
914     session->terminate();
915   }
916   // http1 session needs writecb to tear down session.
917   signal_write();
918 }
919 
on_request(int32_t stream_id)920 void Client::on_request(int32_t stream_id) { streams[stream_id] = Stream(); }
921 
on_header(int32_t stream_id,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen)922 void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
923                        const uint8_t *value, size_t valuelen) {
924   auto itr = streams.find(stream_id);
925   if (itr == std::end(streams)) {
926     return;
927   }
928   auto &stream = (*itr).second;
929 
930   if (worker->current_phase != Phase::MAIN_DURATION) {
931     // If the stream is for warm-up phase, then mark as a success
932     // But we do not update the count for 2xx, 3xx, etc status codes
933     // Same has been done in on_status_code function
934     stream.status_success = 1;
935     return;
936   }
937 
938   if (stream.status_success == -1 && namelen == 7 &&
939       util::streq_l(":status", name, namelen)) {
940     int status = 0;
941     for (size_t i = 0; i < valuelen; ++i) {
942       if ('0' <= value[i] && value[i] <= '9') {
943         status *= 10;
944         status += value[i] - '0';
945         if (status > 999) {
946           stream.status_success = 0;
947           return;
948         }
949       } else {
950         break;
951       }
952     }
953 
954     stream.req_stat.status = status;
955     if (status >= 200 && status < 300) {
956       ++worker->stats.status[2];
957       stream.status_success = 1;
958     } else if (status < 400) {
959       ++worker->stats.status[3];
960       stream.status_success = 1;
961     } else if (status < 600) {
962       ++worker->stats.status[status / 100];
963       stream.status_success = 0;
964     } else {
965       stream.status_success = 0;
966     }
967   }
968 }
969 
on_status_code(int32_t stream_id,uint16_t status)970 void Client::on_status_code(int32_t stream_id, uint16_t status) {
971   auto itr = streams.find(stream_id);
972   if (itr == std::end(streams)) {
973     return;
974   }
975   auto &stream = (*itr).second;
976 
977   if (worker->current_phase != Phase::MAIN_DURATION) {
978     stream.status_success = 1;
979     return;
980   }
981 
982   stream.req_stat.status = status;
983   if (status >= 200 && status < 300) {
984     ++worker->stats.status[2];
985     stream.status_success = 1;
986   } else if (status < 400) {
987     ++worker->stats.status[3];
988     stream.status_success = 1;
989   } else if (status < 600) {
990     ++worker->stats.status[status / 100];
991     stream.status_success = 0;
992   } else {
993     stream.status_success = 0;
994   }
995 }
996 
on_stream_close(int32_t stream_id,bool success,bool final)997 void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
998   if (worker->current_phase == Phase::MAIN_DURATION) {
999     if (req_inflight > 0) {
1000       --req_inflight;
1001     }
1002     auto req_stat = get_req_stat(stream_id);
1003     if (!req_stat) {
1004       return;
1005     }
1006 
1007     req_stat->stream_close_time = std::chrono::steady_clock::now();
1008     if (success) {
1009       req_stat->completed = true;
1010       ++worker->stats.req_success;
1011       ++cstat.req_success;
1012 
1013       if (streams[stream_id].status_success == 1) {
1014         ++worker->stats.req_status_success;
1015       } else {
1016         ++worker->stats.req_failed;
1017       }
1018 
1019       worker->sample_req_stat(req_stat);
1020 
1021       // Count up in successful cases only
1022       ++worker->request_times_smp.n;
1023     } else {
1024       ++worker->stats.req_failed;
1025       ++worker->stats.req_error;
1026     }
1027     ++worker->stats.req_done;
1028     ++req_done;
1029 
1030     if (worker->config->log_fd != -1) {
1031       auto start = std::chrono::duration_cast<std::chrono::microseconds>(
1032           req_stat->request_wall_time.time_since_epoch());
1033       auto delta = std::chrono::duration_cast<std::chrono::microseconds>(
1034           req_stat->stream_close_time - req_stat->request_time);
1035 
1036       std::array<uint8_t, 256> buf;
1037       auto p = std::begin(buf);
1038       p = util::utos(p, start.count());
1039       *p++ = '\t';
1040       if (success) {
1041         p = util::utos(p, req_stat->status);
1042       } else {
1043         *p++ = '-';
1044         *p++ = '1';
1045       }
1046       *p++ = '\t';
1047       p = util::utos(p, delta.count());
1048       *p++ = '\n';
1049 
1050       auto nwrite = static_cast<size_t>(std::distance(std::begin(buf), p));
1051       assert(nwrite <= buf.size());
1052       while (write(worker->config->log_fd, buf.data(), nwrite) == -1 &&
1053              errno == EINTR)
1054         ;
1055     }
1056   }
1057 
1058   worker->report_progress();
1059   streams.erase(stream_id);
1060   if (req_left == 0 && req_inflight == 0) {
1061     terminate_session();
1062     return;
1063   }
1064 
1065   if (!final && req_left > 0) {
1066     if (config.timing_script) {
1067       if (!ev_is_active(&request_timeout_watcher)) {
1068         ev_feed_event(worker->loop, &request_timeout_watcher, EV_TIMER);
1069       }
1070     } else if (!config.rps_enabled()) {
1071       if (submit_request() != 0) {
1072         process_request_failure();
1073       }
1074     } else if (rps_req_pending) {
1075       --rps_req_pending;
1076       if (submit_request() != 0) {
1077         process_request_failure();
1078       }
1079     } else {
1080       assert(rps_req_inflight);
1081       --rps_req_inflight;
1082     }
1083   }
1084 }
1085 
get_req_stat(int32_t stream_id)1086 RequestStat *Client::get_req_stat(int32_t stream_id) {
1087   auto it = streams.find(stream_id);
1088   if (it == std::end(streams)) {
1089     return nullptr;
1090   }
1091 
1092   return &(*it).second.req_stat;
1093 }
1094 
connection_made()1095 int Client::connection_made() {
1096   if (ssl) {
1097     report_tls_info();
1098 
1099     const unsigned char *next_proto = nullptr;
1100     unsigned int next_proto_len;
1101 
1102 #ifndef OPENSSL_NO_NEXTPROTONEG
1103     SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
1104 #endif // !OPENSSL_NO_NEXTPROTONEG
1105 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1106     if (next_proto == nullptr) {
1107       SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
1108     }
1109 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
1110 
1111     if (next_proto) {
1112       auto proto = StringRef{next_proto, next_proto_len};
1113       if (config.is_quic()) {
1114 #ifdef ENABLE_HTTP3
1115         assert(session);
1116         if (!util::streq(StringRef{&NGHTTP3_ALPN_H3[1]}, proto) &&
1117             !util::streq_l("h3-29", proto)) {
1118           return -1;
1119         }
1120 #endif // ENABLE_HTTP3
1121       } else if (util::check_h2_is_selected(proto)) {
1122         session = std::make_unique<Http2Session>(this);
1123       } else if (util::streq(NGHTTP2_H1_1, proto)) {
1124         session = std::make_unique<Http1Session>(this);
1125       }
1126 
1127       // Just assign next_proto to selected_proto anyway to show the
1128       // negotiation result.
1129       selected_proto = proto.str();
1130     } else if (config.is_quic()) {
1131       std::cerr << "QUIC requires ALPN negotiation" << std::endl;
1132       return -1;
1133     } else {
1134       std::cout << "No protocol negotiated. Fallback behaviour may be activated"
1135                 << std::endl;
1136 
1137       for (const auto &proto : config.npn_list) {
1138         if (util::streq(NGHTTP2_H1_1_ALPN, StringRef{proto})) {
1139           std::cout
1140               << "Server does not support NPN/ALPN. Falling back to HTTP/1.1."
1141               << std::endl;
1142           session = std::make_unique<Http1Session>(this);
1143           selected_proto = NGHTTP2_H1_1.str();
1144           break;
1145         }
1146       }
1147     }
1148 
1149     if (!selected_proto.empty()) {
1150       report_app_info();
1151     }
1152 
1153     if (!session) {
1154       std::cout
1155           << "No supported protocol was negotiated. Supported protocols were:"
1156           << std::endl;
1157       for (const auto &proto : config.npn_list) {
1158         std::cout << proto.substr(1) << std::endl;
1159       }
1160       disconnect();
1161       return -1;
1162     }
1163   } else {
1164     switch (config.no_tls_proto) {
1165     case Config::PROTO_HTTP2:
1166       session = std::make_unique<Http2Session>(this);
1167       selected_proto = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID;
1168       break;
1169     case Config::PROTO_HTTP1_1:
1170       session = std::make_unique<Http1Session>(this);
1171       selected_proto = NGHTTP2_H1_1.str();
1172       break;
1173     default:
1174       // unreachable
1175       assert(0);
1176     }
1177 
1178     report_app_info();
1179   }
1180 
1181   state = CLIENT_CONNECTED;
1182 
1183   session->on_connect();
1184 
1185   record_connect_time();
1186 
1187   if (config.rps_enabled()) {
1188     rps_watcher.repeat = std::max(0.01, 1. / config.rps);
1189     ev_timer_again(worker->loop, &rps_watcher);
1190     rps_duration_started = std::chrono::steady_clock::now();
1191   }
1192 
1193   if (config.rps_enabled()) {
1194     assert(req_left);
1195 
1196     ++rps_req_inflight;
1197 
1198     if (submit_request() != 0) {
1199       process_request_failure();
1200     }
1201   } else if (!config.timing_script) {
1202     auto nreq = config.is_timing_based_mode()
1203                     ? std::max(req_left, session->max_concurrent_streams())
1204                     : std::min(req_left, session->max_concurrent_streams());
1205 
1206     for (; nreq > 0; --nreq) {
1207       if (submit_request() != 0) {
1208         process_request_failure();
1209         break;
1210       }
1211     }
1212   } else {
1213 
1214     auto duration = config.timings[reqidx];
1215 
1216     while (duration < std::chrono::duration<double>(1e-9)) {
1217       if (submit_request() != 0) {
1218         process_request_failure();
1219         break;
1220       }
1221       duration = config.timings[reqidx];
1222       if (reqidx == 0) {
1223         // if reqidx wraps around back to 0, we uses up all lines and
1224         // should break
1225         break;
1226       }
1227     }
1228 
1229     if (duration >= std::chrono::duration<double>(1e-9)) {
1230       // double check since we may have break due to reqidx wraps
1231       // around back to 0
1232       request_timeout_watcher.repeat = util::ev_tstamp_from(duration);
1233       ev_timer_again(worker->loop, &request_timeout_watcher);
1234     }
1235   }
1236   signal_write();
1237 
1238   return 0;
1239 }
1240 
on_read(const uint8_t * data,size_t len)1241 int Client::on_read(const uint8_t *data, size_t len) {
1242   auto rv = session->on_read(data, len);
1243   if (rv != 0) {
1244     return -1;
1245   }
1246   if (worker->current_phase == Phase::MAIN_DURATION) {
1247     worker->stats.bytes_total += len;
1248   }
1249   signal_write();
1250   return 0;
1251 }
1252 
on_write()1253 int Client::on_write() {
1254   if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) {
1255     return 0;
1256   }
1257 
1258   if (session->on_write() != 0) {
1259     return -1;
1260   }
1261   return 0;
1262 }
1263 
read_clear()1264 int Client::read_clear() {
1265   uint8_t buf[8_k];
1266 
1267   for (;;) {
1268     ssize_t nread;
1269     while ((nread = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR)
1270       ;
1271     if (nread == -1) {
1272       if (errno == EAGAIN || errno == EWOULDBLOCK) {
1273         return 0;
1274       }
1275       return -1;
1276     }
1277 
1278     if (nread == 0) {
1279       return -1;
1280     }
1281 
1282     if (on_read(buf, nread) != 0) {
1283       return -1;
1284     }
1285   }
1286 
1287   return 0;
1288 }
1289 
write_clear()1290 int Client::write_clear() {
1291   std::array<struct iovec, 2> iov;
1292 
1293   for (;;) {
1294     if (on_write() != 0) {
1295       return -1;
1296     }
1297 
1298     auto iovcnt = wb.riovec(iov.data(), iov.size());
1299 
1300     if (iovcnt == 0) {
1301       break;
1302     }
1303 
1304     ssize_t nwrite;
1305     while ((nwrite = writev(fd, iov.data(), iovcnt)) == -1 && errno == EINTR)
1306       ;
1307 
1308     if (nwrite == -1) {
1309       if (errno == EAGAIN || errno == EWOULDBLOCK) {
1310         ev_io_start(worker->loop, &wev);
1311         return 0;
1312       }
1313       return -1;
1314     }
1315 
1316     wb.drain(nwrite);
1317   }
1318 
1319   ev_io_stop(worker->loop, &wev);
1320 
1321   return 0;
1322 }
1323 
connected()1324 int Client::connected() {
1325   if (!util::check_socket_connected(fd)) {
1326     return ERR_CONNECT_FAIL;
1327   }
1328   ev_io_start(worker->loop, &rev);
1329   ev_io_stop(worker->loop, &wev);
1330 
1331   if (ssl) {
1332     SSL_set_fd(ssl, fd);
1333 
1334     readfn = &Client::tls_handshake;
1335     writefn = &Client::tls_handshake;
1336 
1337     return do_write();
1338   }
1339 
1340   readfn = &Client::read_clear;
1341   writefn = &Client::write_clear;
1342 
1343   if (connection_made() != 0) {
1344     return -1;
1345   }
1346 
1347   return 0;
1348 }
1349 
tls_handshake()1350 int Client::tls_handshake() {
1351   ERR_clear_error();
1352 
1353   auto rv = SSL_do_handshake(ssl);
1354 
1355   if (rv <= 0) {
1356     auto err = SSL_get_error(ssl, rv);
1357     switch (err) {
1358     case SSL_ERROR_WANT_READ:
1359       ev_io_stop(worker->loop, &wev);
1360       return 0;
1361     case SSL_ERROR_WANT_WRITE:
1362       ev_io_start(worker->loop, &wev);
1363       return 0;
1364     default:
1365       return -1;
1366     }
1367   }
1368 
1369   ev_io_stop(worker->loop, &wev);
1370 
1371   readfn = &Client::read_tls;
1372   writefn = &Client::write_tls;
1373 
1374   if (connection_made() != 0) {
1375     return -1;
1376   }
1377 
1378   return 0;
1379 }
1380 
read_tls()1381 int Client::read_tls() {
1382   uint8_t buf[8_k];
1383 
1384   ERR_clear_error();
1385 
1386   for (;;) {
1387     auto rv = SSL_read(ssl, buf, sizeof(buf));
1388 
1389     if (rv <= 0) {
1390       auto err = SSL_get_error(ssl, rv);
1391       switch (err) {
1392       case SSL_ERROR_WANT_READ:
1393         return 0;
1394       case SSL_ERROR_WANT_WRITE:
1395         // renegotiation started
1396         return -1;
1397       default:
1398         return -1;
1399       }
1400     }
1401 
1402     if (on_read(buf, rv) != 0) {
1403       return -1;
1404     }
1405   }
1406 }
1407 
write_tls()1408 int Client::write_tls() {
1409   ERR_clear_error();
1410 
1411   struct iovec iov;
1412 
1413   for (;;) {
1414     if (on_write() != 0) {
1415       return -1;
1416     }
1417 
1418     auto iovcnt = wb.riovec(&iov, 1);
1419 
1420     if (iovcnt == 0) {
1421       break;
1422     }
1423 
1424     auto rv = SSL_write(ssl, iov.iov_base, iov.iov_len);
1425 
1426     if (rv <= 0) {
1427       auto err = SSL_get_error(ssl, rv);
1428       switch (err) {
1429       case SSL_ERROR_WANT_READ:
1430         // renegotiation started
1431         return -1;
1432       case SSL_ERROR_WANT_WRITE:
1433         ev_io_start(worker->loop, &wev);
1434         return 0;
1435       default:
1436         return -1;
1437       }
1438     }
1439 
1440     wb.drain(rv);
1441   }
1442 
1443   ev_io_stop(worker->loop, &wev);
1444 
1445   return 0;
1446 }
1447 
1448 #ifdef ENABLE_HTTP3
1449 // Returns 1 if sendmsg is blocked.
write_udp(const sockaddr * addr,socklen_t addrlen,const uint8_t * data,size_t datalen,size_t gso_size)1450 int Client::write_udp(const sockaddr *addr, socklen_t addrlen,
1451                       const uint8_t *data, size_t datalen, size_t gso_size) {
1452   iovec msg_iov;
1453   msg_iov.iov_base = const_cast<uint8_t *>(data);
1454   msg_iov.iov_len = datalen;
1455 
1456   msghdr msg{};
1457   msg.msg_name = const_cast<sockaddr *>(addr);
1458   msg.msg_namelen = addrlen;
1459   msg.msg_iov = &msg_iov;
1460   msg.msg_iovlen = 1;
1461 
1462 #  ifdef UDP_SEGMENT
1463   std::array<uint8_t, CMSG_SPACE(sizeof(uint16_t))> msg_ctrl{};
1464   if (gso_size && datalen > gso_size) {
1465     msg.msg_control = msg_ctrl.data();
1466     msg.msg_controllen = msg_ctrl.size();
1467 
1468     auto cm = CMSG_FIRSTHDR(&msg);
1469     cm->cmsg_level = SOL_UDP;
1470     cm->cmsg_type = UDP_SEGMENT;
1471     cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
1472     *(reinterpret_cast<uint16_t *>(CMSG_DATA(cm))) = gso_size;
1473   }
1474 #  endif // UDP_SEGMENT
1475 
1476   auto nwrite = sendmsg(fd, &msg, 0);
1477   if (nwrite < 0) {
1478     if (errno == EAGAIN || errno == EWOULDBLOCK) {
1479       return 1;
1480     }
1481 
1482     std::cerr << "sendmsg: errno=" << errno << std::endl;
1483   } else {
1484     ++worker->stats.udp_dgram_sent;
1485   }
1486 
1487   ev_io_stop(worker->loop, &wev);
1488 
1489   return 0;
1490 }
1491 #endif // ENABLE_HTTP3
1492 
record_request_time(RequestStat * req_stat)1493 void Client::record_request_time(RequestStat *req_stat) {
1494   req_stat->request_time = std::chrono::steady_clock::now();
1495   req_stat->request_wall_time = std::chrono::system_clock::now();
1496 }
1497 
record_connect_start_time()1498 void Client::record_connect_start_time() {
1499   cstat.connect_start_time = std::chrono::steady_clock::now();
1500 }
1501 
record_connect_time()1502 void Client::record_connect_time() {
1503   cstat.connect_time = std::chrono::steady_clock::now();
1504 }
1505 
record_ttfb()1506 void Client::record_ttfb() {
1507   if (recorded(cstat.ttfb)) {
1508     return;
1509   }
1510 
1511   cstat.ttfb = std::chrono::steady_clock::now();
1512 }
1513 
clear_connect_times()1514 void Client::clear_connect_times() {
1515   cstat.connect_start_time = std::chrono::steady_clock::time_point();
1516   cstat.connect_time = std::chrono::steady_clock::time_point();
1517   cstat.ttfb = std::chrono::steady_clock::time_point();
1518 }
1519 
record_client_start_time()1520 void Client::record_client_start_time() {
1521   // Record start time only once at the very first connection is going
1522   // to be made.
1523   if (recorded(cstat.client_start_time)) {
1524     return;
1525   }
1526 
1527   cstat.client_start_time = std::chrono::steady_clock::now();
1528 }
1529 
record_client_end_time()1530 void Client::record_client_end_time() {
1531   // Unlike client_start_time, we overwrite client_end_time.  This
1532   // handles multiple connect/disconnect for HTTP/1.1 benchmark.
1533   cstat.client_end_time = std::chrono::steady_clock::now();
1534 }
1535 
signal_write()1536 void Client::signal_write() { ev_io_start(worker->loop, &wev); }
1537 
try_new_connection()1538 void Client::try_new_connection() { new_connection_requested = true; }
1539 
1540 namespace {
get_ev_loop_flags()1541 int get_ev_loop_flags() {
1542   if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
1543     return ev_recommended_backends() | EVBACKEND_KQUEUE;
1544   }
1545 
1546   return 0;
1547 }
1548 } // namespace
1549 
Worker(uint32_t id,SSL_CTX * ssl_ctx,size_t req_todo,size_t nclients,size_t rate,size_t max_samples,Config * config)1550 Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
1551                size_t rate, size_t max_samples, Config *config)
1552     : randgen(util::make_mt19937()),
1553       stats(req_todo, nclients),
1554       loop(ev_loop_new(get_ev_loop_flags())),
1555       ssl_ctx(ssl_ctx),
1556       config(config),
1557       id(id),
1558       tls_info_report_done(false),
1559       app_info_report_done(false),
1560       nconns_made(0),
1561       nclients(nclients),
1562       nreqs_per_client(req_todo / nclients),
1563       nreqs_rem(req_todo % nclients),
1564       rate(rate),
1565       max_samples(max_samples),
1566       next_client_id(0) {
1567   if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
1568     progress_interval = std::max(static_cast<size_t>(1), req_todo / 10);
1569   } else {
1570     progress_interval = std::max(static_cast<size_t>(1), nclients / 10);
1571   }
1572 
1573   // Below timeout is not needed in case of timing-based benchmarking
1574   // create timer that will go off every rate_period
1575   ev_timer_init(&timeout_watcher, rate_period_timeout_w_cb, 0.,
1576                 config->rate_period);
1577   timeout_watcher.data = this;
1578 
1579   if (config->is_timing_based_mode()) {
1580     stats.req_stats.reserve(std::max(req_todo, max_samples));
1581     stats.client_stats.reserve(std::max(nclients, max_samples));
1582   } else {
1583     stats.req_stats.reserve(std::min(req_todo, max_samples));
1584     stats.client_stats.reserve(std::min(nclients, max_samples));
1585   }
1586 
1587   sampling_init(request_times_smp, max_samples);
1588   sampling_init(client_smp, max_samples);
1589 
1590   ev_timer_init(&duration_watcher, duration_timeout_cb, config->duration, 0.);
1591   duration_watcher.data = this;
1592 
1593   ev_timer_init(&warmup_watcher, warmup_timeout_cb, config->warm_up_time, 0.);
1594   warmup_watcher.data = this;
1595 
1596   if (config->is_timing_based_mode()) {
1597     current_phase = Phase::INITIAL_IDLE;
1598   } else {
1599     current_phase = Phase::MAIN_DURATION;
1600   }
1601 }
1602 
~Worker()1603 Worker::~Worker() {
1604   ev_timer_stop(loop, &timeout_watcher);
1605   ev_timer_stop(loop, &duration_watcher);
1606   ev_timer_stop(loop, &warmup_watcher);
1607   ev_loop_destroy(loop);
1608 }
1609 
stop_all_clients()1610 void Worker::stop_all_clients() {
1611   for (auto client : clients) {
1612     if (client) {
1613       client->terminate_session();
1614     }
1615   }
1616 }
1617 
free_client(Client * deleted_client)1618 void Worker::free_client(Client *deleted_client) {
1619   for (auto &client : clients) {
1620     if (client == deleted_client) {
1621       client->req_todo = client->req_done;
1622       stats.req_todo += client->req_todo;
1623       auto index = &client - &clients[0];
1624       clients[index] = nullptr;
1625       return;
1626     }
1627   }
1628 }
1629 
run()1630 void Worker::run() {
1631   if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
1632     for (size_t i = 0; i < nclients; ++i) {
1633       auto req_todo = nreqs_per_client;
1634       if (nreqs_rem > 0) {
1635         ++req_todo;
1636         --nreqs_rem;
1637       }
1638 
1639       auto client = std::make_unique<Client>(next_client_id++, this, req_todo);
1640       if (client->connect() != 0) {
1641         std::cerr << "client could not connect to host" << std::endl;
1642         client->fail();
1643       } else {
1644         client.release();
1645       }
1646     }
1647   } else if (config->is_rate_mode()) {
1648     ev_timer_again(loop, &timeout_watcher);
1649 
1650     // call callback so that we don't waste the first rate_period
1651     rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
1652   } else {
1653     // call the callback to start for one single time
1654     rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
1655   }
1656   ev_run(loop, 0);
1657 }
1658 
1659 namespace {
1660 template <typename Stats, typename Stat>
sample(Sampling & smp,Stats & stats,Stat * s)1661 void sample(Sampling &smp, Stats &stats, Stat *s) {
1662   ++smp.n;
1663   if (stats.size() < smp.max_samples) {
1664     stats.push_back(*s);
1665     return;
1666   }
1667   auto d = std::uniform_int_distribution<unsigned long>(0, smp.n - 1);
1668   auto i = d(gen);
1669   if (i < smp.max_samples) {
1670     stats[i] = *s;
1671   }
1672 }
1673 } // namespace
1674 
sample_req_stat(RequestStat * req_stat)1675 void Worker::sample_req_stat(RequestStat *req_stat) {
1676   sample(request_times_smp, stats.req_stats, req_stat);
1677 }
1678 
sample_client_stat(ClientStat * cstat)1679 void Worker::sample_client_stat(ClientStat *cstat) {
1680   sample(client_smp, stats.client_stats, cstat);
1681 }
1682 
report_progress()1683 void Worker::report_progress() {
1684   if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval ||
1685       config->is_timing_based_mode()) {
1686     return;
1687   }
1688 
1689   std::cout << "progress: " << stats.req_done * 100 / stats.req_todo << "% done"
1690             << std::endl;
1691 }
1692 
report_rate_progress()1693 void Worker::report_rate_progress() {
1694   if (id != 0 || nconns_made % progress_interval) {
1695     return;
1696   }
1697 
1698   std::cout << "progress: " << nconns_made * 100 / nclients
1699             << "% of clients started" << std::endl;
1700 }
1701 
1702 namespace {
1703 // Returns percentage of number of samples within mean +/- sd.
within_sd(const std::vector<double> & samples,double mean,double sd)1704 double within_sd(const std::vector<double> &samples, double mean, double sd) {
1705   if (samples.size() == 0) {
1706     return 0.0;
1707   }
1708   auto lower = mean - sd;
1709   auto upper = mean + sd;
1710   auto m = std::count_if(
1711       std::begin(samples), std::end(samples),
1712       [&lower, &upper](double t) { return lower <= t && t <= upper; });
1713   return (m / static_cast<double>(samples.size())) * 100;
1714 }
1715 } // namespace
1716 
1717 namespace {
1718 // Computes statistics using |samples|. The min, max, mean, sd, and
1719 // percentage of number of samples within mean +/- sd are computed.
1720 // If |sampling| is true, this computes sample variance.  Otherwise,
1721 // population variance.
compute_time_stat(const std::vector<double> & samples,bool sampling=false)1722 SDStat compute_time_stat(const std::vector<double> &samples,
1723                          bool sampling = false) {
1724   if (samples.empty()) {
1725     return {0.0, 0.0, 0.0, 0.0, 0.0};
1726   }
1727   // standard deviation calculated using Rapid calculation method:
1728   // https://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
1729   double a = 0, q = 0;
1730   size_t n = 0;
1731   double sum = 0;
1732   auto res = SDStat{std::numeric_limits<double>::max(),
1733                     std::numeric_limits<double>::min()};
1734   for (const auto &t : samples) {
1735     ++n;
1736     res.min = std::min(res.min, t);
1737     res.max = std::max(res.max, t);
1738     sum += t;
1739 
1740     auto na = a + (t - a) / n;
1741     q += (t - a) * (t - na);
1742     a = na;
1743   }
1744 
1745   assert(n > 0);
1746   res.mean = sum / n;
1747   res.sd = sqrt(q / (sampling && n > 1 ? n - 1 : n));
1748   res.within_sd = within_sd(samples, res.mean, res.sd);
1749 
1750   return res;
1751 }
1752 } // namespace
1753 
1754 namespace {
1755 SDStats
process_time_stats(const std::vector<std::unique_ptr<Worker>> & workers)1756 process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
1757   auto request_times_sampling = false;
1758   auto client_times_sampling = false;
1759   size_t nrequest_times = 0;
1760   size_t nclient_times = 0;
1761   for (const auto &w : workers) {
1762     nrequest_times += w->stats.req_stats.size();
1763     request_times_sampling = w->request_times_smp.n > w->stats.req_stats.size();
1764 
1765     nclient_times += w->stats.client_stats.size();
1766     client_times_sampling = w->client_smp.n > w->stats.client_stats.size();
1767   }
1768 
1769   std::vector<double> request_times;
1770   request_times.reserve(nrequest_times);
1771 
1772   std::vector<double> connect_times, ttfb_times, rps_values;
1773   connect_times.reserve(nclient_times);
1774   ttfb_times.reserve(nclient_times);
1775   rps_values.reserve(nclient_times);
1776 
1777   for (const auto &w : workers) {
1778     for (const auto &req_stat : w->stats.req_stats) {
1779       if (!req_stat.completed) {
1780         continue;
1781       }
1782       request_times.push_back(
1783           std::chrono::duration_cast<std::chrono::duration<double>>(
1784               req_stat.stream_close_time - req_stat.request_time)
1785               .count());
1786     }
1787 
1788     const auto &stat = w->stats;
1789 
1790     for (const auto &cstat : stat.client_stats) {
1791       if (recorded(cstat.client_start_time) &&
1792           recorded(cstat.client_end_time)) {
1793         auto t = std::chrono::duration_cast<std::chrono::duration<double>>(
1794                      cstat.client_end_time - cstat.client_start_time)
1795                      .count();
1796         if (t > 1e-9) {
1797           rps_values.push_back(cstat.req_success / t);
1798         }
1799       }
1800 
1801       // We will get connect event before FFTB.
1802       if (!recorded(cstat.connect_start_time) ||
1803           !recorded(cstat.connect_time)) {
1804         continue;
1805       }
1806 
1807       connect_times.push_back(
1808           std::chrono::duration_cast<std::chrono::duration<double>>(
1809               cstat.connect_time - cstat.connect_start_time)
1810               .count());
1811 
1812       if (!recorded(cstat.ttfb)) {
1813         continue;
1814       }
1815 
1816       ttfb_times.push_back(
1817           std::chrono::duration_cast<std::chrono::duration<double>>(
1818               cstat.ttfb - cstat.connect_start_time)
1819               .count());
1820     }
1821   }
1822 
1823   return {compute_time_stat(request_times, request_times_sampling),
1824           compute_time_stat(connect_times, client_times_sampling),
1825           compute_time_stat(ttfb_times, client_times_sampling),
1826           compute_time_stat(rps_values, client_times_sampling)};
1827 }
1828 } // namespace
1829 
1830 namespace {
resolve_host()1831 void resolve_host() {
1832   if (config.base_uri_unix) {
1833     auto res = std::make_unique<addrinfo>();
1834     res->ai_family = config.unix_addr.sun_family;
1835     res->ai_socktype = SOCK_STREAM;
1836     res->ai_addrlen = sizeof(config.unix_addr);
1837     res->ai_addr =
1838         static_cast<struct sockaddr *>(static_cast<void *>(&config.unix_addr));
1839 
1840     config.addrs = res.release();
1841     return;
1842   };
1843 
1844   int rv;
1845   addrinfo hints{}, *res;
1846 
1847   hints.ai_family = AF_UNSPEC;
1848   hints.ai_socktype = SOCK_STREAM;
1849   hints.ai_protocol = 0;
1850   hints.ai_flags = AI_ADDRCONFIG;
1851 
1852   const auto &resolve_host =
1853       config.connect_to_host.empty() ? config.host : config.connect_to_host;
1854   auto port =
1855       config.connect_to_port == 0 ? config.port : config.connect_to_port;
1856 
1857   rv =
1858       getaddrinfo(resolve_host.c_str(), util::utos(port).c_str(), &hints, &res);
1859   if (rv != 0) {
1860     std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl;
1861     exit(EXIT_FAILURE);
1862   }
1863   if (res == nullptr) {
1864     std::cerr << "No address returned" << std::endl;
1865     exit(EXIT_FAILURE);
1866   }
1867   config.addrs = res;
1868 }
1869 } // namespace
1870 
1871 namespace {
get_reqline(const char * uri,const http_parser_url & u)1872 std::string get_reqline(const char *uri, const http_parser_url &u) {
1873   std::string reqline;
1874 
1875   if (util::has_uri_field(u, UF_PATH)) {
1876     reqline = util::get_uri_field(uri, u, UF_PATH).str();
1877   } else {
1878     reqline = "/";
1879   }
1880 
1881   if (util::has_uri_field(u, UF_QUERY)) {
1882     reqline += '?';
1883     reqline += util::get_uri_field(uri, u, UF_QUERY);
1884   }
1885 
1886   return reqline;
1887 }
1888 } // namespace
1889 
1890 #ifndef OPENSSL_NO_NEXTPROTONEG
1891 namespace {
client_select_next_proto_cb(SSL * ssl,unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)1892 int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
1893                                 unsigned char *outlen, const unsigned char *in,
1894                                 unsigned int inlen, void *arg) {
1895   if (util::select_protocol(const_cast<const unsigned char **>(out), outlen, in,
1896                             inlen, config.npn_list)) {
1897     return SSL_TLSEXT_ERR_OK;
1898   }
1899 
1900   // OpenSSL will terminate handshake with fatal alert if we return
1901   // NOACK.  So there is no way to fallback.
1902   return SSL_TLSEXT_ERR_NOACK;
1903 }
1904 } // namespace
1905 #endif // !OPENSSL_NO_NEXTPROTONEG
1906 
1907 namespace {
1908 constexpr char UNIX_PATH_PREFIX[] = "unix:";
1909 } // namespace
1910 
1911 namespace {
parse_base_uri(const StringRef & base_uri)1912 bool parse_base_uri(const StringRef &base_uri) {
1913   http_parser_url u{};
1914   if (http_parser_parse_url(base_uri.c_str(), base_uri.size(), 0, &u) != 0 ||
1915       !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) {
1916     return false;
1917   }
1918 
1919   config.scheme = util::get_uri_field(base_uri.c_str(), u, UF_SCHEMA).str();
1920   config.host = util::get_uri_field(base_uri.c_str(), u, UF_HOST).str();
1921   config.default_port = util::get_default_port(base_uri.c_str(), u);
1922   if (util::has_uri_field(u, UF_PORT)) {
1923     config.port = u.port;
1924   } else {
1925     config.port = config.default_port;
1926   }
1927 
1928   return true;
1929 }
1930 } // namespace
1931 namespace {
1932 // Use std::vector<std::string>::iterator explicitly, without that,
1933 // http_parser_url u{} fails with clang-3.4.
parse_uris(std::vector<std::string>::iterator first,std::vector<std::string>::iterator last)1934 std::vector<std::string> parse_uris(std::vector<std::string>::iterator first,
1935                                     std::vector<std::string>::iterator last) {
1936   std::vector<std::string> reqlines;
1937 
1938   if (first == last) {
1939     std::cerr << "no URI available" << std::endl;
1940     exit(EXIT_FAILURE);
1941   }
1942 
1943   if (!config.has_base_uri()) {
1944 
1945     if (!parse_base_uri(StringRef{*first})) {
1946       std::cerr << "invalid URI: " << *first << std::endl;
1947       exit(EXIT_FAILURE);
1948     }
1949 
1950     config.base_uri = *first;
1951   }
1952 
1953   for (; first != last; ++first) {
1954     http_parser_url u{};
1955 
1956     auto uri = (*first).c_str();
1957 
1958     if (http_parser_parse_url(uri, (*first).size(), 0, &u) != 0) {
1959       std::cerr << "invalid URI: " << uri << std::endl;
1960       exit(EXIT_FAILURE);
1961     }
1962 
1963     reqlines.push_back(get_reqline(uri, u));
1964   }
1965 
1966   return reqlines;
1967 }
1968 } // namespace
1969 
1970 namespace {
read_uri_from_file(std::istream & infile)1971 std::vector<std::string> read_uri_from_file(std::istream &infile) {
1972   std::vector<std::string> uris;
1973   std::string line_uri;
1974   while (std::getline(infile, line_uri)) {
1975     uris.push_back(line_uri);
1976   }
1977 
1978   return uris;
1979 }
1980 } // namespace
1981 
1982 namespace {
read_script_from_file(std::istream & infile,std::vector<std::chrono::steady_clock::duration> & timings,std::vector<std::string> & uris)1983 void read_script_from_file(
1984     std::istream &infile,
1985     std::vector<std::chrono::steady_clock::duration> &timings,
1986     std::vector<std::string> &uris) {
1987   std::string script_line;
1988   int line_count = 0;
1989   while (std::getline(infile, script_line)) {
1990     line_count++;
1991     if (script_line.empty()) {
1992       std::cerr << "Empty line detected at line " << line_count
1993                 << ". Ignoring and continuing." << std::endl;
1994       continue;
1995     }
1996 
1997     std::size_t pos = script_line.find("\t");
1998     if (pos == std::string::npos) {
1999       std::cerr << "Invalid line format detected, no tab character at line "
2000                 << line_count << ". \n\t" << script_line << std::endl;
2001       exit(EXIT_FAILURE);
2002     }
2003 
2004     const char *start = script_line.c_str();
2005     char *end;
2006     auto v = std::strtod(start, &end);
2007 
2008     errno = 0;
2009     if (v < 0.0 || !std::isfinite(v) || end == start || errno != 0) {
2010       auto error = errno;
2011       std::cerr << "Time value error at line " << line_count << ". \n\t"
2012                 << "value = " << script_line.substr(0, pos) << std::endl;
2013       if (error != 0) {
2014         std::cerr << "\t" << strerror(error) << std::endl;
2015       }
2016       exit(EXIT_FAILURE);
2017     }
2018 
2019     timings.emplace_back(
2020         std::chrono::duration_cast<std::chrono::steady_clock::duration>(
2021             std::chrono::duration<double, std::milli>(v)));
2022     uris.push_back(script_line.substr(pos + 1, script_line.size()));
2023   }
2024 }
2025 } // namespace
2026 
2027 namespace {
create_worker(uint32_t id,SSL_CTX * ssl_ctx,size_t nreqs,size_t nclients,size_t rate,size_t max_samples)2028 std::unique_ptr<Worker> create_worker(uint32_t id, SSL_CTX *ssl_ctx,
2029                                       size_t nreqs, size_t nclients,
2030                                       size_t rate, size_t max_samples) {
2031   std::stringstream rate_report;
2032   if (config.is_rate_mode() && nclients > rate) {
2033     rate_report << "Up to " << rate << " client(s) will be created every "
2034                 << util::duration_str(config.rate_period) << " ";
2035   }
2036 
2037   if (config.is_timing_based_mode()) {
2038     std::cout << "spawning thread #" << id << ": " << nclients
2039               << " total client(s). Timing-based test with "
2040               << config.warm_up_time << "s of warm-up time and "
2041               << config.duration << "s of main duration for measurements."
2042               << std::endl;
2043   } else {
2044     std::cout << "spawning thread #" << id << ": " << nclients
2045               << " total client(s). " << rate_report.str() << nreqs
2046               << " total requests" << std::endl;
2047   }
2048 
2049   if (config.is_rate_mode()) {
2050     return std::make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate,
2051                                     max_samples, &config);
2052   } else {
2053     // Here rate is same as client because the rate_timeout callback
2054     // will be called only once
2055     return std::make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
2056                                     max_samples, &config);
2057   }
2058 }
2059 } // namespace
2060 
2061 namespace {
parse_header_table_size(uint32_t & dst,const char * opt,const char * optarg)2062 int parse_header_table_size(uint32_t &dst, const char *opt,
2063                             const char *optarg) {
2064   auto n = util::parse_uint_with_unit(optarg);
2065   if (n == -1) {
2066     std::cerr << "--" << opt << ": Bad option value: " << optarg << std::endl;
2067     return -1;
2068   }
2069   if (n > std::numeric_limits<uint32_t>::max()) {
2070     std::cerr << "--" << opt
2071               << ": Value too large.  It should be less than or equal to "
2072               << std::numeric_limits<uint32_t>::max() << std::endl;
2073     return -1;
2074   }
2075 
2076   dst = n;
2077 
2078   return 0;
2079 }
2080 } // namespace
2081 
2082 namespace {
print_version(std::ostream & out)2083 void print_version(std::ostream &out) {
2084   out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl;
2085 }
2086 } // namespace
2087 
2088 namespace {
print_usage(std::ostream & out)2089 void print_usage(std::ostream &out) {
2090   out << R"(Usage: h2load [OPTIONS]... [URI]...
2091 benchmarking tool for HTTP/2 server)"
2092       << std::endl;
2093 }
2094 } // namespace
2095 
2096 namespace {
2097 constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14,http/1.1";
2098 } // namespace
2099 
2100 namespace {
print_help(std::ostream & out)2101 void print_help(std::ostream &out) {
2102   print_usage(out);
2103 
2104   auto config = Config();
2105 
2106   out << R"(
2107   <URI>       Specify URI to access.   Multiple URIs can be specified.
2108               URIs are used  in this order for each  client.  All URIs
2109               are used, then  first URI is used and then  2nd URI, and
2110               so  on.  The  scheme, host  and port  in the  subsequent
2111               URIs, if present,  are ignored.  Those in  the first URI
2112               are used solely.  Definition of a base URI overrides all
2113               scheme, host or port values.
2114 Options:
2115   -n, --requests=<N>
2116               Number of  requests across all  clients.  If it  is used
2117               with --timing-script-file option,  this option specifies
2118               the number of requests  each client performs rather than
2119               the number of requests  across all clients.  This option
2120               is ignored if timing-based  benchmarking is enabled (see
2121               --duration option).
2122               Default: )"
2123       << config.nreqs << R"(
2124   -c, --clients=<N>
2125               Number  of concurrent  clients.   With  -r option,  this
2126               specifies the maximum number of connections to be made.
2127               Default: )"
2128       << config.nclients << R"(
2129   -t, --threads=<N>
2130               Number of native threads.
2131               Default: )"
2132       << config.nthreads << R"(
2133   -i, --input-file=<PATH>
2134               Path of a file with multiple URIs are separated by EOLs.
2135               This option will disable URIs getting from command-line.
2136               If '-' is given as <PATH>, URIs will be read from stdin.
2137               URIs are used  in this order for each  client.  All URIs
2138               are used, then  first URI is used and then  2nd URI, and
2139               so  on.  The  scheme, host  and port  in the  subsequent
2140               URIs, if present,  are ignored.  Those in  the first URI
2141               are used solely.  Definition of a base URI overrides all
2142               scheme, host or port values.
2143   -m, --max-concurrent-streams=<N>
2144               Max  concurrent  streams  to issue  per  session.   When
2145               http/1.1  is used,  this  specifies the  number of  HTTP
2146               pipelining requests in-flight.
2147               Default: 1
2148   -f, --max-frame-size=<SIZE>
2149               Maximum frame size that the local endpoint is willing to
2150               receive.
2151               Default: )"
2152       << util::utos_unit(config.max_frame_size) << R"(
2153   -w, --window-bits=<N>
2154               Sets the stream level initial window size to (2**<N>)-1.
2155               For QUIC, <N> is capped to 26 (roughly 64MiB).
2156               Default: )"
2157       << config.window_bits << R"(
2158   -W, --connection-window-bits=<N>
2159               Sets  the  connection  level   initial  window  size  to
2160               (2**<N>)-1.
2161               Default: )"
2162       << config.connection_window_bits << R"(
2163   -H, --header=<HEADER>
2164               Add/Override a header to the requests.
2165   --ciphers=<SUITE>
2166               Set  allowed cipher  list  for TLSv1.2  or earlier.   The
2167               format of the string is described in OpenSSL ciphers(1).
2168               Default: )"
2169       << config.ciphers << R"(
2170   --tls13-ciphers=<SUITE>
2171               Set allowed cipher list for  TLSv1.3.  The format of the
2172               string is described in OpenSSL ciphers(1).
2173               Default: )"
2174       << config.tls13_ciphers << R"(
2175   -p, --no-tls-proto=<PROTOID>
2176               Specify ALPN identifier of the  protocol to be used when
2177               accessing http URI without SSL/TLS.
2178               Available protocols: )"
2179       << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and )" << NGHTTP2_H1_1 << R"(
2180               Default: )"
2181       << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
2182   -d, --data=<PATH>
2183               Post FILE to  server.  The request method  is changed to
2184               POST.   For  http/1.1 connection,  if  -d  is used,  the
2185               maximum number of in-flight pipelined requests is set to
2186               1.
2187   -r, --rate=<N>
2188               Specifies  the  fixed  rate  at  which  connections  are
2189               created.   The   rate  must   be  a   positive  integer,
2190               representing the  number of  connections to be  made per
2191               rate period.   The maximum  number of connections  to be
2192               made  is  given  in  -c   option.   This  rate  will  be
2193               distributed among  threads as  evenly as  possible.  For
2194               example,  with   -t2  and   -r4,  each  thread   gets  2
2195               connections per period.  When the rate is 0, the program
2196               will run  as it  normally does, creating  connections at
2197               whatever variable rate it  wants.  The default value for
2198               this option is 0.  -r and -D are mutually exclusive.
2199   --rate-period=<DURATION>
2200               Specifies the time  period between creating connections.
2201               The period  must be a positive  number, representing the
2202               length of the period in time.  This option is ignored if
2203               the rate option is not used.  The default value for this
2204               option is 1s.
2205   -D, --duration=<DURATION>
2206               Specifies the main duration for the measurements in case
2207               of timing-based  benchmarking.  -D  and -r  are mutually
2208               exclusive.
2209   --warm-up-time=<DURATION>
2210               Specifies the  time  period  before  starting the actual
2211               measurements, in  case  of  timing-based benchmarking.
2212               Needs to provided along with -D option.
2213   -T, --connection-active-timeout=<DURATION>
2214               Specifies  the maximum  time that  h2load is  willing to
2215               keep a  connection open,  regardless of the  activity on
2216               said connection.  <DURATION> must be a positive integer,
2217               specifying the amount of time  to wait.  When no timeout
2218               value is  set (either  active or inactive),  h2load will
2219               keep  a  connection  open indefinitely,  waiting  for  a
2220               response.
2221   -N, --connection-inactivity-timeout=<DURATION>
2222               Specifies the amount  of time that h2load  is willing to
2223               wait to see activity  on a given connection.  <DURATION>
2224               must  be a  positive integer,  specifying the  amount of
2225               time  to wait.   When no  timeout value  is set  (either
2226               active or inactive), h2load  will keep a connection open
2227               indefinitely, waiting for a response.
2228   --timing-script-file=<PATH>
2229               Path of a file containing one or more lines separated by
2230               EOLs.  Each script line is composed of two tab-separated
2231               fields.  The first field represents the time offset from
2232               the start of execution, expressed as a positive value of
2233               milliseconds  with microsecond  resolution.  The  second
2234               field represents the URI.  This option will disable URIs
2235               getting from  command-line.  If '-' is  given as <PATH>,
2236               script lines will be read  from stdin.  Script lines are
2237               used in order for each client.   If -n is given, it must
2238               be less  than or  equal to the  number of  script lines,
2239               larger values are clamped to the number of script lines.
2240               If -n is not given,  the number of requests will default
2241               to the  number of  script lines.   The scheme,  host and
2242               port defined in  the first URI are  used solely.  Values
2243               contained  in  other  URIs,  if  present,  are  ignored.
2244               Definition of a  base URI overrides all  scheme, host or
2245               port   values.   --timing-script-file   and  --rps   are
2246               mutually exclusive.
2247   -B, --base-uri=(<URI>|unix:<PATH>)
2248               Specify URI from which the scheme, host and port will be
2249               used  for  all requests.   The  base  URI overrides  all
2250               values  defined either  at  the command  line or  inside
2251               input files.  If argument  starts with "unix:", then the
2252               rest  of the  argument will  be treated  as UNIX  domain
2253               socket path.   The connection is made  through that path
2254               instead of TCP.   In this case, scheme  is inferred from
2255               the first  URI appeared  in the  command line  or inside
2256               input files as usual.
2257   --npn-list=<LIST>
2258               Comma delimited list of  ALPN protocol identifier sorted
2259               in the  order of preference.  That  means most desirable
2260               protocol comes  first.  This  is used  in both  ALPN and
2261               NPN.  The parameter must be  delimited by a single comma
2262               only  and any  white spaces  are  treated as  a part  of
2263               protocol string.
2264               Default: )"
2265       << DEFAULT_NPN_LIST << R"(
2266   --h1        Short        hand         for        --npn-list=http/1.1
2267               --no-tls-proto=http/1.1,    which   effectively    force
2268               http/1.1 for both http and https URI.
2269   --header-table-size=<SIZE>
2270               Specify decoder header table size.
2271               Default: )"
2272       << util::utos_unit(config.header_table_size) << R"(
2273   --encoder-header-table-size=<SIZE>
2274               Specify encoder header table size.  The decoder (server)
2275               specifies  the maximum  dynamic table  size it  accepts.
2276               Then the negotiated dynamic table size is the minimum of
2277               this option value and the value which server specified.
2278               Default: )"
2279       << util::utos_unit(config.encoder_header_table_size) << R"(
2280   --log-file=<PATH>
2281               Write per-request information to a file as tab-separated
2282               columns: start  time as  microseconds since  epoch; HTTP
2283               status code;  microseconds until end of  response.  More
2284               columns may be added later.  Rows are ordered by end-of-
2285               response  time when  using  one worker  thread, but  may
2286               appear slightly  out of order with  multiple threads due
2287               to buffering.  Status code is -1 for failed streams.
2288   --qlog-file-base=<PATH>
2289               Enable qlog output and specify base file name for qlogs.
2290               Qlog is emitted  for each connection.  For  a given base
2291               name   "base",    each   output   file    name   becomes
2292               "base.M.N.sqlog" where M is worker ID and N is client ID
2293               (e.g. "base.0.3.sqlog").  Only effective in QUIC runs.
2294   --connect-to=<HOST>[:<PORT>]
2295               Host and port to connect  instead of using the authority
2296               in <URI>.
2297   --rps=<N>   Specify request  per second for each  client.  --rps and
2298               --timing-script-file are mutually exclusive.
2299   --groups=<GROUPS>
2300               Specify the supported groups.
2301               Default: )"
2302       << config.groups << R"(
2303   --no-udp-gso
2304               Disable UDP GSO.
2305   --max-udp-payload-size=<SIZE>
2306               Specify the maximum outgoing UDP datagram payload size.
2307   --ktls      Enable ktls.
2308   -v, --verbose
2309               Output debug information.
2310   --version   Display version information and exit.
2311   -h, --help  Display this help and exit.
2312 
2313 --
2314 
2315   The <SIZE> argument is an integer and an optional unit (e.g., 10K is
2316   10 * 1024).  Units are K, M and G (powers of 1024).
2317 
2318   The <DURATION> argument is an integer and an optional unit (e.g., 1s
2319   is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
2320   (hours, minutes, seconds and milliseconds, respectively).  If a unit
2321   is omitted, a second is used as unit.)"
2322       << std::endl;
2323 }
2324 } // namespace
2325 
main(int argc,char ** argv)2326 int main(int argc, char **argv) {
2327   tls::libssl_init();
2328 
2329 #ifndef NOTHREADS
2330   tls::LibsslGlobalLock lock;
2331 #endif // NOTHREADS
2332 
2333   std::string datafile;
2334   std::string logfile;
2335   std::string qlog_base;
2336   bool nreqs_set_manually = false;
2337   while (1) {
2338     static int flag = 0;
2339     constexpr static option long_options[] = {
2340         {"requests", required_argument, nullptr, 'n'},
2341         {"clients", required_argument, nullptr, 'c'},
2342         {"data", required_argument, nullptr, 'd'},
2343         {"threads", required_argument, nullptr, 't'},
2344         {"max-concurrent-streams", required_argument, nullptr, 'm'},
2345         {"window-bits", required_argument, nullptr, 'w'},
2346         {"max-frame-size", required_argument, nullptr, 'f'},
2347         {"connection-window-bits", required_argument, nullptr, 'W'},
2348         {"input-file", required_argument, nullptr, 'i'},
2349         {"header", required_argument, nullptr, 'H'},
2350         {"no-tls-proto", required_argument, nullptr, 'p'},
2351         {"verbose", no_argument, nullptr, 'v'},
2352         {"help", no_argument, nullptr, 'h'},
2353         {"version", no_argument, &flag, 1},
2354         {"ciphers", required_argument, &flag, 2},
2355         {"rate", required_argument, nullptr, 'r'},
2356         {"connection-active-timeout", required_argument, nullptr, 'T'},
2357         {"connection-inactivity-timeout", required_argument, nullptr, 'N'},
2358         {"duration", required_argument, nullptr, 'D'},
2359         {"timing-script-file", required_argument, &flag, 3},
2360         {"base-uri", required_argument, nullptr, 'B'},
2361         {"npn-list", required_argument, &flag, 4},
2362         {"rate-period", required_argument, &flag, 5},
2363         {"h1", no_argument, &flag, 6},
2364         {"header-table-size", required_argument, &flag, 7},
2365         {"encoder-header-table-size", required_argument, &flag, 8},
2366         {"warm-up-time", required_argument, &flag, 9},
2367         {"log-file", required_argument, &flag, 10},
2368         {"connect-to", required_argument, &flag, 11},
2369         {"rps", required_argument, &flag, 12},
2370         {"groups", required_argument, &flag, 13},
2371         {"tls13-ciphers", required_argument, &flag, 14},
2372         {"no-udp-gso", no_argument, &flag, 15},
2373         {"qlog-file-base", required_argument, &flag, 16},
2374         {"max-udp-payload-size", required_argument, &flag, 17},
2375         {"ktls", no_argument, &flag, 18},
2376         {nullptr, 0, nullptr, 0}};
2377     int option_index = 0;
2378     auto c = getopt_long(argc, argv,
2379                          "hvW:c:d:m:n:p:t:w:f:H:i:r:T:N:D:B:", long_options,
2380                          &option_index);
2381     if (c == -1) {
2382       break;
2383     }
2384     switch (c) {
2385     case 'n': {
2386       auto n = util::parse_uint(optarg);
2387       if (n == -1) {
2388         std::cerr << "-n: bad option value: " << optarg << std::endl;
2389         exit(EXIT_FAILURE);
2390       }
2391       config.nreqs = n;
2392       nreqs_set_manually = true;
2393       break;
2394     }
2395     case 'c': {
2396       auto n = util::parse_uint(optarg);
2397       if (n == -1) {
2398         std::cerr << "-c: bad option value: " << optarg << std::endl;
2399         exit(EXIT_FAILURE);
2400       }
2401       config.nclients = n;
2402       break;
2403     }
2404     case 'd':
2405       datafile = optarg;
2406       break;
2407     case 't': {
2408 #ifdef NOTHREADS
2409       std::cerr << "-t: WARNING: Threading disabled at build time, "
2410                 << "no threads created." << std::endl;
2411 #else
2412       auto n = util::parse_uint(optarg);
2413       if (n == -1) {
2414         std::cerr << "-t: bad option value: " << optarg << std::endl;
2415         exit(EXIT_FAILURE);
2416       }
2417       config.nthreads = n;
2418 #endif // NOTHREADS
2419       break;
2420     }
2421     case 'm': {
2422       auto n = util::parse_uint(optarg);
2423       if (n == -1) {
2424         std::cerr << "-m: bad option value: " << optarg << std::endl;
2425         exit(EXIT_FAILURE);
2426       }
2427       config.max_concurrent_streams = n;
2428       break;
2429     }
2430     case 'w':
2431     case 'W': {
2432       auto n = util::parse_uint(optarg);
2433       if (n == -1 || n > 30) {
2434         std::cerr << "-" << static_cast<char>(c)
2435                   << ": specify the integer in the range [0, 30], inclusive"
2436                   << std::endl;
2437         exit(EXIT_FAILURE);
2438       }
2439       if (c == 'w') {
2440         config.window_bits = n;
2441       } else {
2442         config.connection_window_bits = n;
2443       }
2444       break;
2445     }
2446     case 'f': {
2447       auto n = util::parse_uint_with_unit(optarg);
2448       if (n == -1) {
2449         std::cerr << "--max-frame-size: bad option value: " << optarg
2450                   << std::endl;
2451         exit(EXIT_FAILURE);
2452       }
2453       if (static_cast<uint64_t>(n) < 16_k) {
2454         std::cerr << "--max-frame-size: minimum 16384" << std::endl;
2455         exit(EXIT_FAILURE);
2456       }
2457       if (static_cast<uint64_t>(n) > 16_m - 1) {
2458         std::cerr << "--max-frame-size: maximum 16777215" << std::endl;
2459         exit(EXIT_FAILURE);
2460       }
2461       config.max_frame_size = n;
2462       break;
2463     }
2464     case 'H': {
2465       char *header = optarg;
2466       // Skip first possible ':' in the header name
2467       char *value = strchr(optarg + 1, ':');
2468       if (!value || (header[0] == ':' && header + 1 == value)) {
2469         std::cerr << "-H: invalid header: " << optarg << std::endl;
2470         exit(EXIT_FAILURE);
2471       }
2472       *value = 0;
2473       value++;
2474       while (isspace(*value)) {
2475         value++;
2476       }
2477       if (*value == 0) {
2478         // This could also be a valid case for suppressing a header
2479         // similar to curl
2480         std::cerr << "-H: invalid header - value missing: " << optarg
2481                   << std::endl;
2482         exit(EXIT_FAILURE);
2483       }
2484       // Note that there is no processing currently to handle multiple
2485       // message-header fields with the same field name
2486       config.custom_headers.emplace_back(header, value);
2487       util::inp_strlower(config.custom_headers.back().name);
2488       break;
2489     }
2490     case 'i':
2491       config.ifile = optarg;
2492       break;
2493     case 'p': {
2494       auto proto = StringRef{optarg};
2495       if (util::strieq(StringRef::from_lit(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID),
2496                        proto)) {
2497         config.no_tls_proto = Config::PROTO_HTTP2;
2498       } else if (util::strieq(NGHTTP2_H1_1, proto)) {
2499         config.no_tls_proto = Config::PROTO_HTTP1_1;
2500       } else {
2501         std::cerr << "-p: unsupported protocol " << proto << std::endl;
2502         exit(EXIT_FAILURE);
2503       }
2504       break;
2505     }
2506     case 'r': {
2507       auto n = util::parse_uint(optarg);
2508       if (n == -1) {
2509         std::cerr << "-r: bad option value: " << optarg << std::endl;
2510         exit(EXIT_FAILURE);
2511       }
2512       if (n == 0) {
2513         std::cerr << "-r: the rate at which connections are made "
2514                   << "must be positive." << std::endl;
2515         exit(EXIT_FAILURE);
2516       }
2517       config.rate = n;
2518       break;
2519     }
2520     case 'T':
2521       config.conn_active_timeout = util::parse_duration_with_unit(optarg);
2522       if (!std::isfinite(config.conn_active_timeout)) {
2523         std::cerr << "-T: bad value for the conn_active_timeout wait time: "
2524                   << optarg << std::endl;
2525         exit(EXIT_FAILURE);
2526       }
2527       break;
2528     case 'N':
2529       config.conn_inactivity_timeout = util::parse_duration_with_unit(optarg);
2530       if (!std::isfinite(config.conn_inactivity_timeout)) {
2531         std::cerr << "-N: bad value for the conn_inactivity_timeout wait time: "
2532                   << optarg << std::endl;
2533         exit(EXIT_FAILURE);
2534       }
2535       break;
2536     case 'B': {
2537       auto arg = StringRef{optarg};
2538       config.base_uri = "";
2539       config.base_uri_unix = false;
2540 
2541       if (util::istarts_with_l(arg, UNIX_PATH_PREFIX)) {
2542         // UNIX domain socket path
2543         sockaddr_un un;
2544 
2545         auto path = StringRef{std::begin(arg) + str_size(UNIX_PATH_PREFIX),
2546                               std::end(arg)};
2547 
2548         if (path.size() == 0 || path.size() + 1 > sizeof(un.sun_path)) {
2549           std::cerr << "--base-uri: invalid UNIX domain socket path: " << arg
2550                     << std::endl;
2551           exit(EXIT_FAILURE);
2552         }
2553 
2554         config.base_uri_unix = true;
2555 
2556         auto &unix_addr = config.unix_addr;
2557         std::copy(std::begin(path), std::end(path), unix_addr.sun_path);
2558         unix_addr.sun_path[path.size()] = '\0';
2559         unix_addr.sun_family = AF_UNIX;
2560 
2561         break;
2562       }
2563 
2564       if (!parse_base_uri(arg)) {
2565         std::cerr << "--base-uri: invalid base URI: " << arg << std::endl;
2566         exit(EXIT_FAILURE);
2567       }
2568 
2569       config.base_uri = arg.str();
2570       break;
2571     }
2572     case 'D':
2573       config.duration = util::parse_duration_with_unit(optarg);
2574       if (!std::isfinite(config.duration)) {
2575         std::cerr << "-D: value error " << optarg << std::endl;
2576         exit(EXIT_FAILURE);
2577       }
2578       break;
2579     case 'v':
2580       config.verbose = true;
2581       break;
2582     case 'h':
2583       print_help(std::cout);
2584       exit(EXIT_SUCCESS);
2585     case '?':
2586       util::show_candidates(argv[optind - 1], long_options);
2587       exit(EXIT_FAILURE);
2588     case 0:
2589       switch (flag) {
2590       case 1:
2591         // version option
2592         print_version(std::cout);
2593         exit(EXIT_SUCCESS);
2594       case 2:
2595         // ciphers option
2596         config.ciphers = optarg;
2597         break;
2598       case 3:
2599         // timing-script option
2600         config.ifile = optarg;
2601         config.timing_script = true;
2602         break;
2603       case 4:
2604         // npn-list option
2605         config.npn_list = util::parse_config_str_list(StringRef{optarg});
2606         break;
2607       case 5:
2608         // rate-period
2609         config.rate_period = util::parse_duration_with_unit(optarg);
2610         if (!std::isfinite(config.rate_period)) {
2611           std::cerr << "--rate-period: value error " << optarg << std::endl;
2612           exit(EXIT_FAILURE);
2613         }
2614         break;
2615       case 6:
2616         // --h1
2617         config.npn_list =
2618             util::parse_config_str_list(StringRef::from_lit("http/1.1"));
2619         config.no_tls_proto = Config::PROTO_HTTP1_1;
2620         break;
2621       case 7:
2622         // --header-table-size
2623         if (parse_header_table_size(config.header_table_size,
2624                                     "header-table-size", optarg) != 0) {
2625           exit(EXIT_FAILURE);
2626         }
2627         break;
2628       case 8:
2629         // --encoder-header-table-size
2630         if (parse_header_table_size(config.encoder_header_table_size,
2631                                     "encoder-header-table-size", optarg) != 0) {
2632           exit(EXIT_FAILURE);
2633         }
2634         break;
2635       case 9:
2636         // --warm-up-time
2637         config.warm_up_time = util::parse_duration_with_unit(optarg);
2638         if (!std::isfinite(config.warm_up_time)) {
2639           std::cerr << "--warm-up-time: value error " << optarg << std::endl;
2640           exit(EXIT_FAILURE);
2641         }
2642         break;
2643       case 10:
2644         // --log-file
2645         logfile = optarg;
2646         break;
2647       case 11: {
2648         // --connect-to
2649         auto p = util::split_hostport(StringRef{optarg});
2650         int64_t port = 0;
2651         if (p.first.empty() ||
2652             (!p.second.empty() && (port = util::parse_uint(p.second)) == -1)) {
2653           std::cerr << "--connect-to: Invalid value " << optarg << std::endl;
2654           exit(EXIT_FAILURE);
2655         }
2656         config.connect_to_host = p.first.str();
2657         config.connect_to_port = port;
2658         break;
2659       }
2660       case 12: {
2661         char *end;
2662         auto v = std::strtod(optarg, &end);
2663         if (end == optarg || *end != '\0' || !std::isfinite(v) ||
2664             1. / v < 1e-6) {
2665           std::cerr << "--rps: Invalid value " << optarg << std::endl;
2666           exit(EXIT_FAILURE);
2667         }
2668         config.rps = v;
2669         break;
2670       }
2671       case 13:
2672         // --groups
2673         config.groups = optarg;
2674         break;
2675       case 14:
2676         // --tls13-ciphers
2677         config.tls13_ciphers = optarg;
2678         break;
2679       case 15:
2680         // --no-udp-gso
2681         config.no_udp_gso = true;
2682         break;
2683       case 16:
2684         // --qlog-file-base
2685         qlog_base = optarg;
2686         break;
2687       case 17: {
2688         // --max-udp-payload-size
2689         auto n = util::parse_uint_with_unit(optarg);
2690         if (n == -1) {
2691           std::cerr << "--max-udp-payload-size: bad option value: " << optarg
2692                     << std::endl;
2693           exit(EXIT_FAILURE);
2694         }
2695         if (static_cast<uint64_t>(n) > 64_k) {
2696           std::cerr << "--max-udp-payload-size: must not exceed 65536"
2697                     << std::endl;
2698           exit(EXIT_FAILURE);
2699         }
2700         config.max_udp_payload_size = n;
2701         break;
2702       }
2703       case 18:
2704         // --ktls
2705         config.ktls = true;
2706         break;
2707       }
2708       break;
2709     default:
2710       break;
2711     }
2712   }
2713 
2714   if (argc == optind) {
2715     if (config.ifile.empty()) {
2716       std::cerr << "no URI or input file given" << std::endl;
2717       exit(EXIT_FAILURE);
2718     }
2719   }
2720 
2721   if (config.nclients == 0) {
2722     std::cerr << "-c: the number of clients must be strictly greater than 0."
2723               << std::endl;
2724     exit(EXIT_FAILURE);
2725   }
2726 
2727   if (config.npn_list.empty()) {
2728     config.npn_list =
2729         util::parse_config_str_list(StringRef::from_lit(DEFAULT_NPN_LIST));
2730   }
2731 
2732   // serialize the APLN tokens
2733   for (auto &proto : config.npn_list) {
2734     proto.insert(proto.begin(), static_cast<unsigned char>(proto.size()));
2735   }
2736 
2737   std::vector<std::string> reqlines;
2738 
2739   if (config.ifile.empty()) {
2740     std::vector<std::string> uris;
2741     std::copy(&argv[optind], &argv[argc], std::back_inserter(uris));
2742     reqlines = parse_uris(std::begin(uris), std::end(uris));
2743   } else {
2744     std::vector<std::string> uris;
2745     if (!config.timing_script) {
2746       if (config.ifile == "-") {
2747         uris = read_uri_from_file(std::cin);
2748       } else {
2749         std::ifstream infile(config.ifile);
2750         if (!infile) {
2751           std::cerr << "cannot read input file: " << config.ifile << std::endl;
2752           exit(EXIT_FAILURE);
2753         }
2754 
2755         uris = read_uri_from_file(infile);
2756       }
2757     } else {
2758       if (config.ifile == "-") {
2759         read_script_from_file(std::cin, config.timings, uris);
2760       } else {
2761         std::ifstream infile(config.ifile);
2762         if (!infile) {
2763           std::cerr << "cannot read input file: " << config.ifile << std::endl;
2764           exit(EXIT_FAILURE);
2765         }
2766 
2767         read_script_from_file(infile, config.timings, uris);
2768       }
2769 
2770       if (nreqs_set_manually) {
2771         if (config.nreqs > uris.size()) {
2772           std::cerr << "-n: the number of requests must be less than or equal "
2773                        "to the number of timing script entries. Setting number "
2774                        "of requests to "
2775                     << uris.size() << std::endl;
2776 
2777           config.nreqs = uris.size();
2778         }
2779       } else {
2780         config.nreqs = uris.size();
2781       }
2782     }
2783 
2784     reqlines = parse_uris(std::begin(uris), std::end(uris));
2785   }
2786 
2787   if (reqlines.empty()) {
2788     std::cerr << "No URI given" << std::endl;
2789     exit(EXIT_FAILURE);
2790   }
2791 
2792   if (config.is_timing_based_mode() && config.is_rate_mode()) {
2793     std::cerr << "-r, -D: they are mutually exclusive." << std::endl;
2794     exit(EXIT_FAILURE);
2795   }
2796 
2797   if (config.timing_script && config.rps_enabled()) {
2798     std::cerr << "--timing-script-file, --rps: they are mutually exclusive."
2799               << std::endl;
2800     exit(EXIT_FAILURE);
2801   }
2802 
2803   if (config.nreqs == 0 && !config.is_timing_based_mode()) {
2804     std::cerr << "-n: the number of requests must be strictly greater than 0 "
2805                  "if timing-based test is not being run."
2806               << std::endl;
2807     exit(EXIT_FAILURE);
2808   }
2809 
2810   if (config.max_concurrent_streams == 0) {
2811     std::cerr << "-m: the max concurrent streams must be strictly greater "
2812               << "than 0." << std::endl;
2813     exit(EXIT_FAILURE);
2814   }
2815 
2816   if (config.nthreads == 0) {
2817     std::cerr << "-t: the number of threads must be strictly greater than 0."
2818               << std::endl;
2819     exit(EXIT_FAILURE);
2820   }
2821 
2822   if (config.nthreads > std::thread::hardware_concurrency()) {
2823     std::cerr << "-t: warning: the number of threads is greater than hardware "
2824               << "cores." << std::endl;
2825   }
2826 
2827   // With timing script, we don't distribute config.nreqs to each
2828   // client or thread.
2829   if (!config.timing_script && config.nreqs < config.nclients &&
2830       !config.is_timing_based_mode()) {
2831     std::cerr << "-n, -c: the number of requests must be greater than or "
2832               << "equal to the clients." << std::endl;
2833     exit(EXIT_FAILURE);
2834   }
2835 
2836   if (config.nclients < config.nthreads) {
2837     std::cerr << "-c, -t: the number of clients must be greater than or equal "
2838               << "to the number of threads." << std::endl;
2839     exit(EXIT_FAILURE);
2840   }
2841 
2842   if (config.is_timing_based_mode()) {
2843     config.nreqs = 0;
2844   }
2845 
2846   if (config.is_rate_mode()) {
2847     if (config.rate < config.nthreads) {
2848       std::cerr << "-r, -t: the connection rate must be greater than or equal "
2849                 << "to the number of threads." << std::endl;
2850       exit(EXIT_FAILURE);
2851     }
2852 
2853     if (config.rate > config.nclients) {
2854       std::cerr << "-r, -c: the connection rate must be smaller than or equal "
2855                    "to the number of clients."
2856                 << std::endl;
2857       exit(EXIT_FAILURE);
2858     }
2859   }
2860 
2861   if (!datafile.empty()) {
2862     config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY);
2863     if (config.data_fd == -1) {
2864       std::cerr << "-d: Could not open file " << datafile << std::endl;
2865       exit(EXIT_FAILURE);
2866     }
2867     struct stat data_stat;
2868     if (fstat(config.data_fd, &data_stat) == -1) {
2869       std::cerr << "-d: Could not stat file " << datafile << std::endl;
2870       exit(EXIT_FAILURE);
2871     }
2872     config.data_length = data_stat.st_size;
2873     auto addr = mmap(nullptr, config.data_length, PROT_READ, MAP_SHARED,
2874                      config.data_fd, 0);
2875     if (addr == MAP_FAILED) {
2876       std::cerr << "-d: Could not mmap file " << datafile << std::endl;
2877       exit(EXIT_FAILURE);
2878     }
2879     config.data = static_cast<uint8_t *>(addr);
2880   }
2881 
2882   if (!logfile.empty()) {
2883     config.log_fd = open(logfile.c_str(), O_WRONLY | O_CREAT | O_APPEND,
2884                          S_IRUSR | S_IWUSR | S_IRGRP);
2885     if (config.log_fd == -1) {
2886       std::cerr << "--log-file: Could not open file " << logfile << std::endl;
2887       exit(EXIT_FAILURE);
2888     }
2889   }
2890 
2891   if (!qlog_base.empty()) {
2892     if (!config.is_quic()) {
2893       std::cerr
2894           << "Warning: --qlog-file-base: only effective in quic, ignoring."
2895           << std::endl;
2896     } else {
2897 #ifdef ENABLE_HTTP3
2898       config.qlog_file_base = qlog_base;
2899 #endif // ENABLE_HTTP3
2900     }
2901   }
2902 
2903   struct sigaction act {};
2904   act.sa_handler = SIG_IGN;
2905   sigaction(SIGPIPE, &act, nullptr);
2906 
2907   auto ssl_ctx = SSL_CTX_new(TLS_client_method());
2908   if (!ssl_ctx) {
2909     std::cerr << "Failed to create SSL_CTX: "
2910               << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2911     exit(EXIT_FAILURE);
2912   }
2913 
2914   auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
2915                   SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
2916                   SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
2917 
2918 #ifdef SSL_OP_ENABLE_KTLS
2919   if (config.ktls) {
2920     ssl_opts |= SSL_OP_ENABLE_KTLS;
2921   }
2922 #endif // SSL_OP_ENABLE_KTLS
2923 
2924   SSL_CTX_set_options(ssl_ctx, ssl_opts);
2925   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
2926   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
2927 
2928   if (config.is_quic()) {
2929 #ifdef ENABLE_HTTP3
2930 #  ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
2931     if (ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) {
2932       std::cerr << "ngtcp2_crypto_quictls_configure_client_context failed"
2933                 << std::endl;
2934       exit(EXIT_FAILURE);
2935     }
2936 #  endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
2937 #  ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
2938     if (ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
2939       std::cerr << "ngtcp2_crypto_boringssl_configure_client_context failed"
2940                 << std::endl;
2941       exit(EXIT_FAILURE);
2942     }
2943 #  endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
2944 #endif   // ENABLE_HTTP3
2945   } else if (nghttp2::tls::ssl_ctx_set_proto_versions(
2946                  ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
2947                  nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
2948     std::cerr << "Could not set TLS versions" << std::endl;
2949     exit(EXIT_FAILURE);
2950   }
2951 
2952   if (SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers.c_str()) == 0) {
2953     std::cerr << "SSL_CTX_set_cipher_list with " << config.ciphers
2954               << " failed: " << ERR_error_string(ERR_get_error(), nullptr)
2955               << std::endl;
2956     exit(EXIT_FAILURE);
2957   }
2958 
2959 #if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
2960   if (SSL_CTX_set_ciphersuites(ssl_ctx, config.tls13_ciphers.c_str()) == 0) {
2961     std::cerr << "SSL_CTX_set_ciphersuites with " << config.tls13_ciphers
2962               << " failed: " << ERR_error_string(ERR_get_error(), nullptr)
2963               << std::endl;
2964     exit(EXIT_FAILURE);
2965   }
2966 #endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
2967 
2968 #if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
2969   if (SSL_CTX_set1_groups_list(ssl_ctx, config.groups.c_str()) != 1) {
2970     std::cerr << "SSL_CTX_set1_groups_list failed" << std::endl;
2971     exit(EXIT_FAILURE);
2972   }
2973 #else  // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
2974   if (SSL_CTX_set1_curves_list(ssl_ctx, config.groups.c_str()) != 1) {
2975     std::cerr << "SSL_CTX_set1_curves_list failed" << std::endl;
2976     exit(EXIT_FAILURE);
2977   }
2978 #endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
2979 
2980 #ifndef OPENSSL_NO_NEXTPROTONEG
2981   SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
2982                                    nullptr);
2983 #endif // !OPENSSL_NO_NEXTPROTONEG
2984 
2985 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
2986   std::vector<unsigned char> proto_list;
2987   for (const auto &proto : config.npn_list) {
2988     std::copy_n(proto.c_str(), proto.size(), std::back_inserter(proto_list));
2989   }
2990 
2991   SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
2992 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
2993 
2994 #if OPENSSL_1_1_1_API
2995   auto keylog_filename = getenv("SSLKEYLOGFILE");
2996   if (keylog_filename) {
2997     keylog_file.open(keylog_filename, std::ios_base::app);
2998     if (keylog_file) {
2999       SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
3000     }
3001   }
3002 #endif // OPENSSL_1_1_1_API
3003 
3004   std::string user_agent = "h2load nghttp2/" NGHTTP2_VERSION;
3005   Headers shared_nva;
3006   shared_nva.emplace_back(":scheme", config.scheme);
3007   if (config.port != config.default_port) {
3008     shared_nva.emplace_back(":authority",
3009                             config.host + ":" + util::utos(config.port));
3010   } else {
3011     shared_nva.emplace_back(":authority", config.host);
3012   }
3013   shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST");
3014   shared_nva.emplace_back("user-agent", user_agent);
3015 
3016   // list header fields that can be overridden.
3017   auto override_hdrs = make_array<std::string>(":authority", ":host", ":method",
3018                                                ":scheme", "user-agent");
3019 
3020   for (auto &kv : config.custom_headers) {
3021     if (std::find(std::begin(override_hdrs), std::end(override_hdrs),
3022                   kv.name) != std::end(override_hdrs)) {
3023       // override header
3024       for (auto &nv : shared_nva) {
3025         if ((nv.name == ":authority" && kv.name == ":host") ||
3026             (nv.name == kv.name)) {
3027           nv.value = kv.value;
3028         }
3029       }
3030     } else {
3031       // add additional headers
3032       shared_nva.push_back(kv);
3033     }
3034   }
3035 
3036   std::string content_length_str;
3037   if (config.data_fd != -1) {
3038     content_length_str = util::utos(config.data_length);
3039   }
3040 
3041   auto method_it =
3042       std::find_if(std::begin(shared_nva), std::end(shared_nva),
3043                    [](const Header &nv) { return nv.name == ":method"; });
3044   assert(method_it != std::end(shared_nva));
3045 
3046   config.h1reqs.reserve(reqlines.size());
3047   config.nva.reserve(reqlines.size());
3048 
3049   for (auto &req : reqlines) {
3050     // For HTTP/1.1
3051     auto h1req = (*method_it).value;
3052     h1req += ' ';
3053     h1req += req;
3054     h1req += " HTTP/1.1\r\n";
3055     for (auto &nv : shared_nva) {
3056       if (nv.name == ":authority") {
3057         h1req += "Host: ";
3058         h1req += nv.value;
3059         h1req += "\r\n";
3060         continue;
3061       }
3062       if (nv.name[0] == ':') {
3063         continue;
3064       }
3065       h1req += nv.name;
3066       h1req += ": ";
3067       h1req += nv.value;
3068       h1req += "\r\n";
3069     }
3070 
3071     if (!content_length_str.empty()) {
3072       h1req += "Content-Length: ";
3073       h1req += content_length_str;
3074       h1req += "\r\n";
3075     }
3076     h1req += "\r\n";
3077 
3078     config.h1reqs.push_back(std::move(h1req));
3079 
3080     // For nghttp2
3081     std::vector<nghttp2_nv> nva;
3082     // 2 for :path, and possible content-length
3083     nva.reserve(2 + shared_nva.size());
3084 
3085     nva.push_back(http2::make_nv_ls(":path", req));
3086 
3087     for (auto &nv : shared_nva) {
3088       nva.push_back(http2::make_nv(nv.name, nv.value, false));
3089     }
3090 
3091     if (!content_length_str.empty()) {
3092       nva.push_back(http2::make_nv(StringRef::from_lit("content-length"),
3093                                    StringRef{content_length_str}));
3094     }
3095 
3096     config.nva.push_back(std::move(nva));
3097   }
3098 
3099   // Don't DOS our server!
3100   if (config.host == "nghttp2.org") {
3101     std::cerr << "Using h2load against public server " << config.host
3102               << " should be prohibited." << std::endl;
3103     exit(EXIT_FAILURE);
3104   }
3105 
3106   resolve_host();
3107 
3108   std::cout << "starting benchmark..." << std::endl;
3109 
3110   std::vector<std::unique_ptr<Worker>> workers;
3111   workers.reserve(config.nthreads);
3112 
3113 #ifndef NOTHREADS
3114   size_t nreqs_per_thread = 0;
3115   ssize_t nreqs_rem = 0;
3116 
3117   if (!config.timing_script) {
3118     nreqs_per_thread = config.nreqs / config.nthreads;
3119     nreqs_rem = config.nreqs % config.nthreads;
3120   }
3121 
3122   size_t nclients_per_thread = config.nclients / config.nthreads;
3123   ssize_t nclients_rem = config.nclients % config.nthreads;
3124 
3125   size_t rate_per_thread = config.rate / config.nthreads;
3126   ssize_t rate_per_thread_rem = config.rate % config.nthreads;
3127 
3128   size_t max_samples_per_thread =
3129       std::max(static_cast<size_t>(256), MAX_SAMPLES / config.nthreads);
3130 
3131   std::mutex mu;
3132   std::condition_variable cv;
3133   auto ready = false;
3134 
3135   std::vector<std::future<void>> futures;
3136   for (size_t i = 0; i < config.nthreads; ++i) {
3137     auto rate = rate_per_thread;
3138     if (rate_per_thread_rem > 0) {
3139       --rate_per_thread_rem;
3140       ++rate;
3141     }
3142     auto nclients = nclients_per_thread;
3143     if (nclients_rem > 0) {
3144       --nclients_rem;
3145       ++nclients;
3146     }
3147 
3148     size_t nreqs;
3149     if (config.timing_script) {
3150       // With timing script, each client issues config.nreqs requests.
3151       // We divide nreqs by number of clients in Worker ctor to
3152       // distribute requests to those clients evenly, so multiply
3153       // config.nreqs here by config.nclients.
3154       nreqs = config.nreqs * nclients;
3155     } else {
3156       nreqs = nreqs_per_thread;
3157       if (nreqs_rem > 0) {
3158         --nreqs_rem;
3159         ++nreqs;
3160       }
3161     }
3162 
3163     workers.push_back(create_worker(i, ssl_ctx, nreqs, nclients, rate,
3164                                     max_samples_per_thread));
3165     auto &worker = workers.back();
3166     futures.push_back(
3167         std::async(std::launch::async, [&worker, &mu, &cv, &ready]() {
3168           {
3169             std::unique_lock<std::mutex> ulk(mu);
3170             cv.wait(ulk, [&ready] { return ready; });
3171           }
3172           worker->run();
3173         }));
3174   }
3175 
3176   {
3177     std::lock_guard<std::mutex> lg(mu);
3178     ready = true;
3179     cv.notify_all();
3180   }
3181 
3182   auto start = std::chrono::steady_clock::now();
3183 
3184   for (auto &fut : futures) {
3185     fut.get();
3186   }
3187 
3188 #else  // NOTHREADS
3189   auto rate = config.rate;
3190   auto nclients = config.nclients;
3191   auto nreqs =
3192       config.timing_script ? config.nreqs * config.nclients : config.nreqs;
3193 
3194   workers.push_back(
3195       create_worker(0, ssl_ctx, nreqs, nclients, rate, MAX_SAMPLES));
3196 
3197   auto start = std::chrono::steady_clock::now();
3198 
3199   workers.back()->run();
3200 #endif // NOTHREADS
3201 
3202   auto end = std::chrono::steady_clock::now();
3203   auto duration =
3204       std::chrono::duration_cast<std::chrono::microseconds>(end - start);
3205 
3206   Stats stats(0, 0);
3207   for (const auto &w : workers) {
3208     const auto &s = w->stats;
3209 
3210     stats.req_todo += s.req_todo;
3211     stats.req_started += s.req_started;
3212     stats.req_done += s.req_done;
3213     stats.req_timedout += s.req_timedout;
3214     stats.req_success += s.req_success;
3215     stats.req_status_success += s.req_status_success;
3216     stats.req_failed += s.req_failed;
3217     stats.req_error += s.req_error;
3218     stats.bytes_total += s.bytes_total;
3219     stats.bytes_head += s.bytes_head;
3220     stats.bytes_head_decomp += s.bytes_head_decomp;
3221     stats.bytes_body += s.bytes_body;
3222     stats.udp_dgram_recv += s.udp_dgram_recv;
3223     stats.udp_dgram_sent += s.udp_dgram_sent;
3224 
3225     for (size_t i = 0; i < stats.status.size(); ++i) {
3226       stats.status[i] += s.status[i];
3227     }
3228   }
3229 
3230   auto ts = process_time_stats(workers);
3231 
3232   // Requests which have not been issued due to connection errors, are
3233   // counted towards req_failed and req_error.
3234   auto req_not_issued =
3235       (stats.req_todo - stats.req_status_success - stats.req_failed);
3236   stats.req_failed += req_not_issued;
3237   stats.req_error += req_not_issued;
3238 
3239   // UI is heavily inspired by weighttp[1] and wrk[2]
3240   //
3241   // [1] https://github.com/lighttpd/weighttp
3242   // [2] https://github.com/wg/wrk
3243   double rps = 0;
3244   int64_t bps = 0;
3245   if (duration.count() > 0) {
3246     if (config.is_timing_based_mode()) {
3247       // we only want to consider the main duration if warm-up is given
3248       rps = stats.req_success / config.duration;
3249       bps = stats.bytes_total / config.duration;
3250     } else {
3251       auto secd = std::chrono::duration_cast<
3252           std::chrono::duration<double, std::chrono::seconds::period>>(
3253           duration);
3254       rps = stats.req_success / secd.count();
3255       bps = stats.bytes_total / secd.count();
3256     }
3257   }
3258 
3259   double header_space_savings = 0.;
3260   if (stats.bytes_head_decomp > 0) {
3261     header_space_savings =
3262         1. - static_cast<double>(stats.bytes_head) / stats.bytes_head_decomp;
3263   }
3264 
3265   std::cout << std::fixed << std::setprecision(2) << R"(
3266 finished in )"
3267             << util::format_duration(duration) << ", " << rps << " req/s, "
3268             << util::utos_funit(bps) << R"(B/s
3269 requests: )" << stats.req_todo
3270             << " total, " << stats.req_started << " started, " << stats.req_done
3271             << " done, " << stats.req_status_success << " succeeded, "
3272             << stats.req_failed << " failed, " << stats.req_error
3273             << " errored, " << stats.req_timedout << R"( timeout
3274 status codes: )"
3275             << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
3276             << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
3277 traffic: )" << util::utos_funit(stats.bytes_total)
3278             << "B (" << stats.bytes_total << ") total, "
3279             << util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
3280             << ") headers (space savings " << header_space_savings * 100
3281             << "%), " << util::utos_funit(stats.bytes_body) << "B ("
3282             << stats.bytes_body << R"() data)" << std::endl;
3283 #ifdef ENABLE_HTTP3
3284   if (config.is_quic()) {
3285     std::cout << "UDP datagram: " << stats.udp_dgram_sent << " sent, "
3286               << stats.udp_dgram_recv << " received" << std::endl;
3287   }
3288 #endif // ENABLE_HTTP3
3289   std::cout
3290       << R"(                     min         max         mean         sd        +/- sd
3291 time for request: )"
3292       << std::setw(10) << util::format_duration(ts.request.min) << "  "
3293       << std::setw(10) << util::format_duration(ts.request.max) << "  "
3294       << std::setw(10) << util::format_duration(ts.request.mean) << "  "
3295       << std::setw(10) << util::format_duration(ts.request.sd) << std::setw(9)
3296       << util::dtos(ts.request.within_sd) << "%"
3297       << "\ntime for connect: " << std::setw(10)
3298       << util::format_duration(ts.connect.min) << "  " << std::setw(10)
3299       << util::format_duration(ts.connect.max) << "  " << std::setw(10)
3300       << util::format_duration(ts.connect.mean) << "  " << std::setw(10)
3301       << util::format_duration(ts.connect.sd) << std::setw(9)
3302       << util::dtos(ts.connect.within_sd) << "%"
3303       << "\ntime to 1st byte: " << std::setw(10)
3304       << util::format_duration(ts.ttfb.min) << "  " << std::setw(10)
3305       << util::format_duration(ts.ttfb.max) << "  " << std::setw(10)
3306       << util::format_duration(ts.ttfb.mean) << "  " << std::setw(10)
3307       << util::format_duration(ts.ttfb.sd) << std::setw(9)
3308       << util::dtos(ts.ttfb.within_sd) << "%"
3309       << "\nreq/s           : " << std::setw(10) << ts.rps.min << "  "
3310       << std::setw(10) << ts.rps.max << "  " << std::setw(10) << ts.rps.mean
3311       << "  " << std::setw(10) << ts.rps.sd << std::setw(9)
3312       << util::dtos(ts.rps.within_sd) << "%" << std::endl;
3313 
3314   SSL_CTX_free(ssl_ctx);
3315 
3316   if (config.log_fd != -1) {
3317     close(config.log_fd);
3318   }
3319 
3320   return 0;
3321 }
3322 
3323 } // namespace h2load
3324 
main(int argc,char ** argv)3325 int main(int argc, char **argv) { return h2load::main(argc, argv); }
3326