• 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     uint16_t n = gso_size;
1473     memcpy(CMSG_DATA(cm), &n, sizeof(n));
1474   }
1475 #  endif // UDP_SEGMENT
1476 
1477   auto nwrite = sendmsg(fd, &msg, 0);
1478   if (nwrite < 0) {
1479     if (errno == EAGAIN || errno == EWOULDBLOCK) {
1480       return 1;
1481     }
1482 
1483     std::cerr << "sendmsg: errno=" << errno << std::endl;
1484   } else {
1485     ++worker->stats.udp_dgram_sent;
1486   }
1487 
1488   ev_io_stop(worker->loop, &wev);
1489 
1490   return 0;
1491 }
1492 #endif // ENABLE_HTTP3
1493 
record_request_time(RequestStat * req_stat)1494 void Client::record_request_time(RequestStat *req_stat) {
1495   req_stat->request_time = std::chrono::steady_clock::now();
1496   req_stat->request_wall_time = std::chrono::system_clock::now();
1497 }
1498 
record_connect_start_time()1499 void Client::record_connect_start_time() {
1500   cstat.connect_start_time = std::chrono::steady_clock::now();
1501 }
1502 
record_connect_time()1503 void Client::record_connect_time() {
1504   cstat.connect_time = std::chrono::steady_clock::now();
1505 }
1506 
record_ttfb()1507 void Client::record_ttfb() {
1508   if (recorded(cstat.ttfb)) {
1509     return;
1510   }
1511 
1512   cstat.ttfb = std::chrono::steady_clock::now();
1513 }
1514 
clear_connect_times()1515 void Client::clear_connect_times() {
1516   cstat.connect_start_time = std::chrono::steady_clock::time_point();
1517   cstat.connect_time = std::chrono::steady_clock::time_point();
1518   cstat.ttfb = std::chrono::steady_clock::time_point();
1519 }
1520 
record_client_start_time()1521 void Client::record_client_start_time() {
1522   // Record start time only once at the very first connection is going
1523   // to be made.
1524   if (recorded(cstat.client_start_time)) {
1525     return;
1526   }
1527 
1528   cstat.client_start_time = std::chrono::steady_clock::now();
1529 }
1530 
record_client_end_time()1531 void Client::record_client_end_time() {
1532   // Unlike client_start_time, we overwrite client_end_time.  This
1533   // handles multiple connect/disconnect for HTTP/1.1 benchmark.
1534   cstat.client_end_time = std::chrono::steady_clock::now();
1535 }
1536 
signal_write()1537 void Client::signal_write() { ev_io_start(worker->loop, &wev); }
1538 
try_new_connection()1539 void Client::try_new_connection() { new_connection_requested = true; }
1540 
1541 namespace {
get_ev_loop_flags()1542 int get_ev_loop_flags() {
1543   if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
1544     return ev_recommended_backends() | EVBACKEND_KQUEUE;
1545   }
1546 
1547   return 0;
1548 }
1549 } // namespace
1550 
Worker(uint32_t id,SSL_CTX * ssl_ctx,size_t req_todo,size_t nclients,size_t rate,size_t max_samples,Config * config)1551 Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
1552                size_t rate, size_t max_samples, Config *config)
1553     : randgen(util::make_mt19937()),
1554       stats(req_todo, nclients),
1555       loop(ev_loop_new(get_ev_loop_flags())),
1556       ssl_ctx(ssl_ctx),
1557       config(config),
1558       id(id),
1559       tls_info_report_done(false),
1560       app_info_report_done(false),
1561       nconns_made(0),
1562       nclients(nclients),
1563       nreqs_per_client(req_todo / nclients),
1564       nreqs_rem(req_todo % nclients),
1565       rate(rate),
1566       max_samples(max_samples),
1567       next_client_id(0) {
1568   if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
1569     progress_interval = std::max(static_cast<size_t>(1), req_todo / 10);
1570   } else {
1571     progress_interval = std::max(static_cast<size_t>(1), nclients / 10);
1572   }
1573 
1574   // Below timeout is not needed in case of timing-based benchmarking
1575   // create timer that will go off every rate_period
1576   ev_timer_init(&timeout_watcher, rate_period_timeout_w_cb, 0.,
1577                 config->rate_period);
1578   timeout_watcher.data = this;
1579 
1580   if (config->is_timing_based_mode()) {
1581     stats.req_stats.reserve(std::max(req_todo, max_samples));
1582     stats.client_stats.reserve(std::max(nclients, max_samples));
1583   } else {
1584     stats.req_stats.reserve(std::min(req_todo, max_samples));
1585     stats.client_stats.reserve(std::min(nclients, max_samples));
1586   }
1587 
1588   sampling_init(request_times_smp, max_samples);
1589   sampling_init(client_smp, max_samples);
1590 
1591   ev_timer_init(&duration_watcher, duration_timeout_cb, config->duration, 0.);
1592   duration_watcher.data = this;
1593 
1594   ev_timer_init(&warmup_watcher, warmup_timeout_cb, config->warm_up_time, 0.);
1595   warmup_watcher.data = this;
1596 
1597   if (config->is_timing_based_mode()) {
1598     current_phase = Phase::INITIAL_IDLE;
1599   } else {
1600     current_phase = Phase::MAIN_DURATION;
1601   }
1602 }
1603 
~Worker()1604 Worker::~Worker() {
1605   ev_timer_stop(loop, &timeout_watcher);
1606   ev_timer_stop(loop, &duration_watcher);
1607   ev_timer_stop(loop, &warmup_watcher);
1608   ev_loop_destroy(loop);
1609 }
1610 
stop_all_clients()1611 void Worker::stop_all_clients() {
1612   for (auto client : clients) {
1613     if (client) {
1614       client->terminate_session();
1615     }
1616   }
1617 }
1618 
free_client(Client * deleted_client)1619 void Worker::free_client(Client *deleted_client) {
1620   for (auto &client : clients) {
1621     if (client == deleted_client) {
1622       client->req_todo = client->req_done;
1623       stats.req_todo += client->req_todo;
1624       auto index = &client - &clients[0];
1625       clients[index] = nullptr;
1626       return;
1627     }
1628   }
1629 }
1630 
run()1631 void Worker::run() {
1632   if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
1633     for (size_t i = 0; i < nclients; ++i) {
1634       auto req_todo = nreqs_per_client;
1635       if (nreqs_rem > 0) {
1636         ++req_todo;
1637         --nreqs_rem;
1638       }
1639 
1640       auto client = std::make_unique<Client>(next_client_id++, this, req_todo);
1641       if (client->connect() != 0) {
1642         std::cerr << "client could not connect to host" << std::endl;
1643         client->fail();
1644       } else {
1645         client.release();
1646       }
1647     }
1648   } else if (config->is_rate_mode()) {
1649     ev_timer_again(loop, &timeout_watcher);
1650 
1651     // call callback so that we don't waste the first rate_period
1652     rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
1653   } else {
1654     // call the callback to start for one single time
1655     rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
1656   }
1657   ev_run(loop, 0);
1658 }
1659 
1660 namespace {
1661 template <typename Stats, typename Stat>
sample(Sampling & smp,Stats & stats,Stat * s)1662 void sample(Sampling &smp, Stats &stats, Stat *s) {
1663   ++smp.n;
1664   if (stats.size() < smp.max_samples) {
1665     stats.push_back(*s);
1666     return;
1667   }
1668   auto d = std::uniform_int_distribution<unsigned long>(0, smp.n - 1);
1669   auto i = d(gen);
1670   if (i < smp.max_samples) {
1671     stats[i] = *s;
1672   }
1673 }
1674 } // namespace
1675 
sample_req_stat(RequestStat * req_stat)1676 void Worker::sample_req_stat(RequestStat *req_stat) {
1677   sample(request_times_smp, stats.req_stats, req_stat);
1678 }
1679 
sample_client_stat(ClientStat * cstat)1680 void Worker::sample_client_stat(ClientStat *cstat) {
1681   sample(client_smp, stats.client_stats, cstat);
1682 }
1683 
report_progress()1684 void Worker::report_progress() {
1685   if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval ||
1686       config->is_timing_based_mode()) {
1687     return;
1688   }
1689 
1690   std::cout << "progress: " << stats.req_done * 100 / stats.req_todo << "% done"
1691             << std::endl;
1692 }
1693 
report_rate_progress()1694 void Worker::report_rate_progress() {
1695   if (id != 0 || nconns_made % progress_interval) {
1696     return;
1697   }
1698 
1699   std::cout << "progress: " << nconns_made * 100 / nclients
1700             << "% of clients started" << std::endl;
1701 }
1702 
1703 namespace {
1704 // Returns percentage of number of samples within mean +/- sd.
within_sd(const std::vector<double> & samples,double mean,double sd)1705 double within_sd(const std::vector<double> &samples, double mean, double sd) {
1706   if (samples.size() == 0) {
1707     return 0.0;
1708   }
1709   auto lower = mean - sd;
1710   auto upper = mean + sd;
1711   auto m = std::count_if(
1712       std::begin(samples), std::end(samples),
1713       [&lower, &upper](double t) { return lower <= t && t <= upper; });
1714   return (m / static_cast<double>(samples.size())) * 100;
1715 }
1716 } // namespace
1717 
1718 namespace {
1719 // Computes statistics using |samples|. The min, max, mean, sd, and
1720 // percentage of number of samples within mean +/- sd are computed.
1721 // If |sampling| is true, this computes sample variance.  Otherwise,
1722 // population variance.
compute_time_stat(const std::vector<double> & samples,bool sampling=false)1723 SDStat compute_time_stat(const std::vector<double> &samples,
1724                          bool sampling = false) {
1725   if (samples.empty()) {
1726     return {0.0, 0.0, 0.0, 0.0, 0.0};
1727   }
1728   // standard deviation calculated using Rapid calculation method:
1729   // https://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
1730   double a = 0, q = 0;
1731   size_t n = 0;
1732   double sum = 0;
1733   auto res = SDStat{std::numeric_limits<double>::max(),
1734                     std::numeric_limits<double>::min()};
1735   for (const auto &t : samples) {
1736     ++n;
1737     res.min = std::min(res.min, t);
1738     res.max = std::max(res.max, t);
1739     sum += t;
1740 
1741     auto na = a + (t - a) / n;
1742     q += (t - a) * (t - na);
1743     a = na;
1744   }
1745 
1746   assert(n > 0);
1747   res.mean = sum / n;
1748   res.sd = sqrt(q / (sampling && n > 1 ? n - 1 : n));
1749   res.within_sd = within_sd(samples, res.mean, res.sd);
1750 
1751   return res;
1752 }
1753 } // namespace
1754 
1755 namespace {
1756 SDStats
process_time_stats(const std::vector<std::unique_ptr<Worker>> & workers)1757 process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
1758   auto request_times_sampling = false;
1759   auto client_times_sampling = false;
1760   size_t nrequest_times = 0;
1761   size_t nclient_times = 0;
1762   for (const auto &w : workers) {
1763     nrequest_times += w->stats.req_stats.size();
1764     request_times_sampling = w->request_times_smp.n > w->stats.req_stats.size();
1765 
1766     nclient_times += w->stats.client_stats.size();
1767     client_times_sampling = w->client_smp.n > w->stats.client_stats.size();
1768   }
1769 
1770   std::vector<double> request_times;
1771   request_times.reserve(nrequest_times);
1772 
1773   std::vector<double> connect_times, ttfb_times, rps_values;
1774   connect_times.reserve(nclient_times);
1775   ttfb_times.reserve(nclient_times);
1776   rps_values.reserve(nclient_times);
1777 
1778   for (const auto &w : workers) {
1779     for (const auto &req_stat : w->stats.req_stats) {
1780       if (!req_stat.completed) {
1781         continue;
1782       }
1783       request_times.push_back(
1784           std::chrono::duration_cast<std::chrono::duration<double>>(
1785               req_stat.stream_close_time - req_stat.request_time)
1786               .count());
1787     }
1788 
1789     const auto &stat = w->stats;
1790 
1791     for (const auto &cstat : stat.client_stats) {
1792       if (recorded(cstat.client_start_time) &&
1793           recorded(cstat.client_end_time)) {
1794         auto t = std::chrono::duration_cast<std::chrono::duration<double>>(
1795                      cstat.client_end_time - cstat.client_start_time)
1796                      .count();
1797         if (t > 1e-9) {
1798           rps_values.push_back(cstat.req_success / t);
1799         }
1800       }
1801 
1802       // We will get connect event before FFTB.
1803       if (!recorded(cstat.connect_start_time) ||
1804           !recorded(cstat.connect_time)) {
1805         continue;
1806       }
1807 
1808       connect_times.push_back(
1809           std::chrono::duration_cast<std::chrono::duration<double>>(
1810               cstat.connect_time - cstat.connect_start_time)
1811               .count());
1812 
1813       if (!recorded(cstat.ttfb)) {
1814         continue;
1815       }
1816 
1817       ttfb_times.push_back(
1818           std::chrono::duration_cast<std::chrono::duration<double>>(
1819               cstat.ttfb - cstat.connect_start_time)
1820               .count());
1821     }
1822   }
1823 
1824   return {compute_time_stat(request_times, request_times_sampling),
1825           compute_time_stat(connect_times, client_times_sampling),
1826           compute_time_stat(ttfb_times, client_times_sampling),
1827           compute_time_stat(rps_values, client_times_sampling)};
1828 }
1829 } // namespace
1830 
1831 namespace {
resolve_host()1832 void resolve_host() {
1833   if (config.base_uri_unix) {
1834     auto res = std::make_unique<addrinfo>();
1835     res->ai_family = config.unix_addr.sun_family;
1836     res->ai_socktype = SOCK_STREAM;
1837     res->ai_addrlen = sizeof(config.unix_addr);
1838     res->ai_addr =
1839         static_cast<struct sockaddr *>(static_cast<void *>(&config.unix_addr));
1840 
1841     config.addrs = res.release();
1842     return;
1843   };
1844 
1845   int rv;
1846   addrinfo hints{}, *res;
1847 
1848   hints.ai_family = AF_UNSPEC;
1849   hints.ai_socktype = SOCK_STREAM;
1850   hints.ai_protocol = 0;
1851   hints.ai_flags = AI_ADDRCONFIG;
1852 
1853   const auto &resolve_host =
1854       config.connect_to_host.empty() ? config.host : config.connect_to_host;
1855   auto port =
1856       config.connect_to_port == 0 ? config.port : config.connect_to_port;
1857 
1858   rv =
1859       getaddrinfo(resolve_host.c_str(), util::utos(port).c_str(), &hints, &res);
1860   if (rv != 0) {
1861     std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl;
1862     exit(EXIT_FAILURE);
1863   }
1864   if (res == nullptr) {
1865     std::cerr << "No address returned" << std::endl;
1866     exit(EXIT_FAILURE);
1867   }
1868   config.addrs = res;
1869 }
1870 } // namespace
1871 
1872 namespace {
get_reqline(const char * uri,const http_parser_url & u)1873 std::string get_reqline(const char *uri, const http_parser_url &u) {
1874   std::string reqline;
1875 
1876   if (util::has_uri_field(u, UF_PATH)) {
1877     reqline = util::get_uri_field(uri, u, UF_PATH).str();
1878   } else {
1879     reqline = "/";
1880   }
1881 
1882   if (util::has_uri_field(u, UF_QUERY)) {
1883     reqline += '?';
1884     reqline += util::get_uri_field(uri, u, UF_QUERY);
1885   }
1886 
1887   return reqline;
1888 }
1889 } // namespace
1890 
1891 #ifndef OPENSSL_NO_NEXTPROTONEG
1892 namespace {
client_select_next_proto_cb(SSL * ssl,unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)1893 int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
1894                                 unsigned char *outlen, const unsigned char *in,
1895                                 unsigned int inlen, void *arg) {
1896   if (util::select_protocol(const_cast<const unsigned char **>(out), outlen, in,
1897                             inlen, config.npn_list)) {
1898     return SSL_TLSEXT_ERR_OK;
1899   }
1900 
1901   // OpenSSL will terminate handshake with fatal alert if we return
1902   // NOACK.  So there is no way to fallback.
1903   return SSL_TLSEXT_ERR_NOACK;
1904 }
1905 } // namespace
1906 #endif // !OPENSSL_NO_NEXTPROTONEG
1907 
1908 namespace {
1909 constexpr char UNIX_PATH_PREFIX[] = "unix:";
1910 } // namespace
1911 
1912 namespace {
parse_base_uri(const StringRef & base_uri)1913 bool parse_base_uri(const StringRef &base_uri) {
1914   http_parser_url u{};
1915   if (http_parser_parse_url(base_uri.c_str(), base_uri.size(), 0, &u) != 0 ||
1916       !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) {
1917     return false;
1918   }
1919 
1920   config.scheme = util::get_uri_field(base_uri.c_str(), u, UF_SCHEMA).str();
1921   config.host = util::get_uri_field(base_uri.c_str(), u, UF_HOST).str();
1922   config.default_port = util::get_default_port(base_uri.c_str(), u);
1923   if (util::has_uri_field(u, UF_PORT)) {
1924     config.port = u.port;
1925   } else {
1926     config.port = config.default_port;
1927   }
1928 
1929   return true;
1930 }
1931 } // namespace
1932 namespace {
1933 // Use std::vector<std::string>::iterator explicitly, without that,
1934 // http_parser_url u{} fails with clang-3.4.
parse_uris(std::vector<std::string>::iterator first,std::vector<std::string>::iterator last)1935 std::vector<std::string> parse_uris(std::vector<std::string>::iterator first,
1936                                     std::vector<std::string>::iterator last) {
1937   std::vector<std::string> reqlines;
1938 
1939   if (first == last) {
1940     std::cerr << "no URI available" << std::endl;
1941     exit(EXIT_FAILURE);
1942   }
1943 
1944   if (!config.has_base_uri()) {
1945 
1946     if (!parse_base_uri(StringRef{*first})) {
1947       std::cerr << "invalid URI: " << *first << std::endl;
1948       exit(EXIT_FAILURE);
1949     }
1950 
1951     config.base_uri = *first;
1952   }
1953 
1954   for (; first != last; ++first) {
1955     http_parser_url u{};
1956 
1957     auto uri = (*first).c_str();
1958 
1959     if (http_parser_parse_url(uri, (*first).size(), 0, &u) != 0) {
1960       std::cerr << "invalid URI: " << uri << std::endl;
1961       exit(EXIT_FAILURE);
1962     }
1963 
1964     reqlines.push_back(get_reqline(uri, u));
1965   }
1966 
1967   return reqlines;
1968 }
1969 } // namespace
1970 
1971 namespace {
read_uri_from_file(std::istream & infile)1972 std::vector<std::string> read_uri_from_file(std::istream &infile) {
1973   std::vector<std::string> uris;
1974   std::string line_uri;
1975   while (std::getline(infile, line_uri)) {
1976     uris.push_back(line_uri);
1977   }
1978 
1979   return uris;
1980 }
1981 } // namespace
1982 
1983 namespace {
read_script_from_file(std::istream & infile,std::vector<std::chrono::steady_clock::duration> & timings,std::vector<std::string> & uris)1984 void read_script_from_file(
1985     std::istream &infile,
1986     std::vector<std::chrono::steady_clock::duration> &timings,
1987     std::vector<std::string> &uris) {
1988   std::string script_line;
1989   int line_count = 0;
1990   while (std::getline(infile, script_line)) {
1991     line_count++;
1992     if (script_line.empty()) {
1993       std::cerr << "Empty line detected at line " << line_count
1994                 << ". Ignoring and continuing." << std::endl;
1995       continue;
1996     }
1997 
1998     std::size_t pos = script_line.find("\t");
1999     if (pos == std::string::npos) {
2000       std::cerr << "Invalid line format detected, no tab character at line "
2001                 << line_count << ". \n\t" << script_line << std::endl;
2002       exit(EXIT_FAILURE);
2003     }
2004 
2005     const char *start = script_line.c_str();
2006     char *end;
2007     auto v = std::strtod(start, &end);
2008 
2009     errno = 0;
2010     if (v < 0.0 || !std::isfinite(v) || end == start || errno != 0) {
2011       auto error = errno;
2012       std::cerr << "Time value error at line " << line_count << ". \n\t"
2013                 << "value = " << script_line.substr(0, pos) << std::endl;
2014       if (error != 0) {
2015         std::cerr << "\t" << strerror(error) << std::endl;
2016       }
2017       exit(EXIT_FAILURE);
2018     }
2019 
2020     timings.emplace_back(
2021         std::chrono::duration_cast<std::chrono::steady_clock::duration>(
2022             std::chrono::duration<double, std::milli>(v)));
2023     uris.push_back(script_line.substr(pos + 1, script_line.size()));
2024   }
2025 }
2026 } // namespace
2027 
2028 namespace {
create_worker(uint32_t id,SSL_CTX * ssl_ctx,size_t nreqs,size_t nclients,size_t rate,size_t max_samples)2029 std::unique_ptr<Worker> create_worker(uint32_t id, SSL_CTX *ssl_ctx,
2030                                       size_t nreqs, size_t nclients,
2031                                       size_t rate, size_t max_samples) {
2032   std::stringstream rate_report;
2033   if (config.is_rate_mode() && nclients > rate) {
2034     rate_report << "Up to " << rate << " client(s) will be created every "
2035                 << util::duration_str(config.rate_period) << " ";
2036   }
2037 
2038   if (config.is_timing_based_mode()) {
2039     std::cout << "spawning thread #" << id << ": " << nclients
2040               << " total client(s). Timing-based test with "
2041               << config.warm_up_time << "s of warm-up time and "
2042               << config.duration << "s of main duration for measurements."
2043               << std::endl;
2044   } else {
2045     std::cout << "spawning thread #" << id << ": " << nclients
2046               << " total client(s). " << rate_report.str() << nreqs
2047               << " total requests" << std::endl;
2048   }
2049 
2050   if (config.is_rate_mode()) {
2051     return std::make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate,
2052                                     max_samples, &config);
2053   } else {
2054     // Here rate is same as client because the rate_timeout callback
2055     // will be called only once
2056     return std::make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
2057                                     max_samples, &config);
2058   }
2059 }
2060 } // namespace
2061 
2062 namespace {
parse_header_table_size(uint32_t & dst,const char * opt,const char * optarg)2063 int parse_header_table_size(uint32_t &dst, const char *opt,
2064                             const char *optarg) {
2065   auto n = util::parse_uint_with_unit(optarg);
2066   if (n == -1) {
2067     std::cerr << "--" << opt << ": Bad option value: " << optarg << std::endl;
2068     return -1;
2069   }
2070   if (n > std::numeric_limits<uint32_t>::max()) {
2071     std::cerr << "--" << opt
2072               << ": Value too large.  It should be less than or equal to "
2073               << std::numeric_limits<uint32_t>::max() << std::endl;
2074     return -1;
2075   }
2076 
2077   dst = n;
2078 
2079   return 0;
2080 }
2081 } // namespace
2082 
2083 namespace {
print_version(std::ostream & out)2084 void print_version(std::ostream &out) {
2085   out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl;
2086 }
2087 } // namespace
2088 
2089 namespace {
print_usage(std::ostream & out)2090 void print_usage(std::ostream &out) {
2091   out << R"(Usage: h2load [OPTIONS]... [URI]...
2092 benchmarking tool for HTTP/2 server)"
2093       << std::endl;
2094 }
2095 } // namespace
2096 
2097 namespace {
2098 constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14,http/1.1";
2099 } // namespace
2100 
2101 namespace {
print_help(std::ostream & out)2102 void print_help(std::ostream &out) {
2103   print_usage(out);
2104 
2105   auto config = Config();
2106 
2107   out << R"(
2108   <URI>       Specify URI to access.   Multiple URIs can be specified.
2109               URIs are used  in this order for each  client.  All URIs
2110               are used, then  first URI is used and then  2nd URI, and
2111               so  on.  The  scheme, host  and port  in the  subsequent
2112               URIs, if present,  are ignored.  Those in  the first URI
2113               are used solely.  Definition of a base URI overrides all
2114               scheme, host or port values.
2115 Options:
2116   -n, --requests=<N>
2117               Number of  requests across all  clients.  If it  is used
2118               with --timing-script-file option,  this option specifies
2119               the number of requests  each client performs rather than
2120               the number of requests  across all clients.  This option
2121               is ignored if timing-based  benchmarking is enabled (see
2122               --duration option).
2123               Default: )"
2124       << config.nreqs << R"(
2125   -c, --clients=<N>
2126               Number  of concurrent  clients.   With  -r option,  this
2127               specifies the maximum number of connections to be made.
2128               Default: )"
2129       << config.nclients << R"(
2130   -t, --threads=<N>
2131               Number of native threads.
2132               Default: )"
2133       << config.nthreads << R"(
2134   -i, --input-file=<PATH>
2135               Path of a file with multiple URIs are separated by EOLs.
2136               This option will disable URIs getting from command-line.
2137               If '-' is given as <PATH>, URIs will be read from stdin.
2138               URIs are used  in this order for each  client.  All URIs
2139               are used, then  first URI is used and then  2nd URI, and
2140               so  on.  The  scheme, host  and port  in the  subsequent
2141               URIs, if present,  are ignored.  Those in  the first URI
2142               are used solely.  Definition of a base URI overrides all
2143               scheme, host or port values.
2144   -m, --max-concurrent-streams=<N>
2145               Max  concurrent  streams  to issue  per  session.   When
2146               http/1.1  is used,  this  specifies the  number of  HTTP
2147               pipelining requests in-flight.
2148               Default: 1
2149   -f, --max-frame-size=<SIZE>
2150               Maximum frame size that the local endpoint is willing to
2151               receive.
2152               Default: )"
2153       << util::utos_unit(config.max_frame_size) << R"(
2154   -w, --window-bits=<N>
2155               Sets the stream level initial window size to (2**<N>)-1.
2156               For QUIC, <N> is capped to 26 (roughly 64MiB).
2157               Default: )"
2158       << config.window_bits << R"(
2159   -W, --connection-window-bits=<N>
2160               Sets  the  connection  level   initial  window  size  to
2161               (2**<N>)-1.
2162               Default: )"
2163       << config.connection_window_bits << R"(
2164   -H, --header=<HEADER>
2165               Add/Override a header to the requests.
2166   --ciphers=<SUITE>
2167               Set  allowed cipher  list  for TLSv1.2  or earlier.   The
2168               format of the string is described in OpenSSL ciphers(1).
2169               Default: )"
2170       << config.ciphers << R"(
2171   --tls13-ciphers=<SUITE>
2172               Set allowed cipher list for  TLSv1.3.  The format of the
2173               string is described in OpenSSL ciphers(1).
2174               Default: )"
2175       << config.tls13_ciphers << R"(
2176   -p, --no-tls-proto=<PROTOID>
2177               Specify ALPN identifier of the  protocol to be used when
2178               accessing http URI without SSL/TLS.
2179               Available protocols: )"
2180       << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and )" << NGHTTP2_H1_1 << R"(
2181               Default: )"
2182       << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
2183   -d, --data=<PATH>
2184               Post FILE to  server.  The request method  is changed to
2185               POST.   For  http/1.1 connection,  if  -d  is used,  the
2186               maximum number of in-flight pipelined requests is set to
2187               1.
2188   -r, --rate=<N>
2189               Specifies  the  fixed  rate  at  which  connections  are
2190               created.   The   rate  must   be  a   positive  integer,
2191               representing the  number of  connections to be  made per
2192               rate period.   The maximum  number of connections  to be
2193               made  is  given  in  -c   option.   This  rate  will  be
2194               distributed among  threads as  evenly as  possible.  For
2195               example,  with   -t2  and   -r4,  each  thread   gets  2
2196               connections per period.  When the rate is 0, the program
2197               will run  as it  normally does, creating  connections at
2198               whatever variable rate it  wants.  The default value for
2199               this option is 0.  -r and -D are mutually exclusive.
2200   --rate-period=<DURATION>
2201               Specifies the time  period between creating connections.
2202               The period  must be a positive  number, representing the
2203               length of the period in time.  This option is ignored if
2204               the rate option is not used.  The default value for this
2205               option is 1s.
2206   -D, --duration=<DURATION>
2207               Specifies the main duration for the measurements in case
2208               of timing-based  benchmarking.  -D  and -r  are mutually
2209               exclusive.
2210   --warm-up-time=<DURATION>
2211               Specifies the  time  period  before  starting the actual
2212               measurements, in  case  of  timing-based benchmarking.
2213               Needs to provided along with -D option.
2214   -T, --connection-active-timeout=<DURATION>
2215               Specifies  the maximum  time that  h2load is  willing to
2216               keep a  connection open,  regardless of the  activity on
2217               said connection.  <DURATION> must be a positive integer,
2218               specifying the amount of time  to wait.  When no timeout
2219               value is  set (either  active or inactive),  h2load will
2220               keep  a  connection  open indefinitely,  waiting  for  a
2221               response.
2222   -N, --connection-inactivity-timeout=<DURATION>
2223               Specifies the amount  of time that h2load  is willing to
2224               wait to see activity  on a given connection.  <DURATION>
2225               must  be a  positive integer,  specifying the  amount of
2226               time  to wait.   When no  timeout value  is set  (either
2227               active or inactive), h2load  will keep a connection open
2228               indefinitely, waiting for a response.
2229   --timing-script-file=<PATH>
2230               Path of a file containing one or more lines separated by
2231               EOLs.  Each script line is composed of two tab-separated
2232               fields.  The first field represents the time offset from
2233               the start of execution, expressed as a positive value of
2234               milliseconds  with microsecond  resolution.  The  second
2235               field represents the URI.  This option will disable URIs
2236               getting from  command-line.  If '-' is  given as <PATH>,
2237               script lines will be read  from stdin.  Script lines are
2238               used in order for each client.   If -n is given, it must
2239               be less  than or  equal to the  number of  script lines,
2240               larger values are clamped to the number of script lines.
2241               If -n is not given,  the number of requests will default
2242               to the  number of  script lines.   The scheme,  host and
2243               port defined in  the first URI are  used solely.  Values
2244               contained  in  other  URIs,  if  present,  are  ignored.
2245               Definition of a  base URI overrides all  scheme, host or
2246               port   values.   --timing-script-file   and  --rps   are
2247               mutually exclusive.
2248   -B, --base-uri=(<URI>|unix:<PATH>)
2249               Specify URI from which the scheme, host and port will be
2250               used  for  all requests.   The  base  URI overrides  all
2251               values  defined either  at  the command  line or  inside
2252               input files.  If argument  starts with "unix:", then the
2253               rest  of the  argument will  be treated  as UNIX  domain
2254               socket path.   The connection is made  through that path
2255               instead of TCP.   In this case, scheme  is inferred from
2256               the first  URI appeared  in the  command line  or inside
2257               input files as usual.
2258   --npn-list=<LIST>
2259               Comma delimited list of  ALPN protocol identifier sorted
2260               in the  order of preference.  That  means most desirable
2261               protocol comes  first.  This  is used  in both  ALPN and
2262               NPN.  The parameter must be  delimited by a single comma
2263               only  and any  white spaces  are  treated as  a part  of
2264               protocol string.
2265               Default: )"
2266       << DEFAULT_NPN_LIST << R"(
2267   --h1        Short        hand         for        --npn-list=http/1.1
2268               --no-tls-proto=http/1.1,    which   effectively    force
2269               http/1.1 for both http and https URI.
2270   --header-table-size=<SIZE>
2271               Specify decoder header table size.
2272               Default: )"
2273       << util::utos_unit(config.header_table_size) << R"(
2274   --encoder-header-table-size=<SIZE>
2275               Specify encoder header table size.  The decoder (server)
2276               specifies  the maximum  dynamic table  size it  accepts.
2277               Then the negotiated dynamic table size is the minimum of
2278               this option value and the value which server specified.
2279               Default: )"
2280       << util::utos_unit(config.encoder_header_table_size) << R"(
2281   --log-file=<PATH>
2282               Write per-request information to a file as tab-separated
2283               columns: start  time as  microseconds since  epoch; HTTP
2284               status code;  microseconds until end of  response.  More
2285               columns may be added later.  Rows are ordered by end-of-
2286               response  time when  using  one worker  thread, but  may
2287               appear slightly  out of order with  multiple threads due
2288               to buffering.  Status code is -1 for failed streams.
2289   --qlog-file-base=<PATH>
2290               Enable qlog output and specify base file name for qlogs.
2291               Qlog is emitted  for each connection.  For  a given base
2292               name   "base",    each   output   file    name   becomes
2293               "base.M.N.sqlog" where M is worker ID and N is client ID
2294               (e.g. "base.0.3.sqlog").  Only effective in QUIC runs.
2295   --connect-to=<HOST>[:<PORT>]
2296               Host and port to connect  instead of using the authority
2297               in <URI>.
2298   --rps=<N>   Specify request  per second for each  client.  --rps and
2299               --timing-script-file are mutually exclusive.
2300   --groups=<GROUPS>
2301               Specify the supported groups.
2302               Default: )"
2303       << config.groups << R"(
2304   --no-udp-gso
2305               Disable UDP GSO.
2306   --max-udp-payload-size=<SIZE>
2307               Specify the maximum outgoing UDP datagram payload size.
2308   --ktls      Enable ktls.
2309   -v, --verbose
2310               Output debug information.
2311   --version   Display version information and exit.
2312   -h, --help  Display this help and exit.
2313 
2314 --
2315 
2316   The <SIZE> argument is an integer and an optional unit (e.g., 10K is
2317   10 * 1024).  Units are K, M and G (powers of 1024).
2318 
2319   The <DURATION> argument is an integer and an optional unit (e.g., 1s
2320   is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
2321   (hours, minutes, seconds and milliseconds, respectively).  If a unit
2322   is omitted, a second is used as unit.)"
2323       << std::endl;
2324 }
2325 } // namespace
2326 
main(int argc,char ** argv)2327 int main(int argc, char **argv) {
2328   tls::libssl_init();
2329 
2330 #ifndef NOTHREADS
2331   tls::LibsslGlobalLock lock;
2332 #endif // NOTHREADS
2333 
2334   std::string datafile;
2335   std::string logfile;
2336   std::string qlog_base;
2337   bool nreqs_set_manually = false;
2338   while (1) {
2339     static int flag = 0;
2340     constexpr static option long_options[] = {
2341         {"requests", required_argument, nullptr, 'n'},
2342         {"clients", required_argument, nullptr, 'c'},
2343         {"data", required_argument, nullptr, 'd'},
2344         {"threads", required_argument, nullptr, 't'},
2345         {"max-concurrent-streams", required_argument, nullptr, 'm'},
2346         {"window-bits", required_argument, nullptr, 'w'},
2347         {"max-frame-size", required_argument, nullptr, 'f'},
2348         {"connection-window-bits", required_argument, nullptr, 'W'},
2349         {"input-file", required_argument, nullptr, 'i'},
2350         {"header", required_argument, nullptr, 'H'},
2351         {"no-tls-proto", required_argument, nullptr, 'p'},
2352         {"verbose", no_argument, nullptr, 'v'},
2353         {"help", no_argument, nullptr, 'h'},
2354         {"version", no_argument, &flag, 1},
2355         {"ciphers", required_argument, &flag, 2},
2356         {"rate", required_argument, nullptr, 'r'},
2357         {"connection-active-timeout", required_argument, nullptr, 'T'},
2358         {"connection-inactivity-timeout", required_argument, nullptr, 'N'},
2359         {"duration", required_argument, nullptr, 'D'},
2360         {"timing-script-file", required_argument, &flag, 3},
2361         {"base-uri", required_argument, nullptr, 'B'},
2362         {"npn-list", required_argument, &flag, 4},
2363         {"rate-period", required_argument, &flag, 5},
2364         {"h1", no_argument, &flag, 6},
2365         {"header-table-size", required_argument, &flag, 7},
2366         {"encoder-header-table-size", required_argument, &flag, 8},
2367         {"warm-up-time", required_argument, &flag, 9},
2368         {"log-file", required_argument, &flag, 10},
2369         {"connect-to", required_argument, &flag, 11},
2370         {"rps", required_argument, &flag, 12},
2371         {"groups", required_argument, &flag, 13},
2372         {"tls13-ciphers", required_argument, &flag, 14},
2373         {"no-udp-gso", no_argument, &flag, 15},
2374         {"qlog-file-base", required_argument, &flag, 16},
2375         {"max-udp-payload-size", required_argument, &flag, 17},
2376         {"ktls", no_argument, &flag, 18},
2377         {nullptr, 0, nullptr, 0}};
2378     int option_index = 0;
2379     auto c = getopt_long(argc, argv,
2380                          "hvW:c:d:m:n:p:t:w:f:H:i:r:T:N:D:B:", long_options,
2381                          &option_index);
2382     if (c == -1) {
2383       break;
2384     }
2385     switch (c) {
2386     case 'n': {
2387       auto n = util::parse_uint(optarg);
2388       if (n == -1) {
2389         std::cerr << "-n: bad option value: " << optarg << std::endl;
2390         exit(EXIT_FAILURE);
2391       }
2392       config.nreqs = n;
2393       nreqs_set_manually = true;
2394       break;
2395     }
2396     case 'c': {
2397       auto n = util::parse_uint(optarg);
2398       if (n == -1) {
2399         std::cerr << "-c: bad option value: " << optarg << std::endl;
2400         exit(EXIT_FAILURE);
2401       }
2402       config.nclients = n;
2403       break;
2404     }
2405     case 'd':
2406       datafile = optarg;
2407       break;
2408     case 't': {
2409 #ifdef NOTHREADS
2410       std::cerr << "-t: WARNING: Threading disabled at build time, "
2411                 << "no threads created." << std::endl;
2412 #else
2413       auto n = util::parse_uint(optarg);
2414       if (n == -1) {
2415         std::cerr << "-t: bad option value: " << optarg << std::endl;
2416         exit(EXIT_FAILURE);
2417       }
2418       config.nthreads = n;
2419 #endif // NOTHREADS
2420       break;
2421     }
2422     case 'm': {
2423       auto n = util::parse_uint(optarg);
2424       if (n == -1) {
2425         std::cerr << "-m: bad option value: " << optarg << std::endl;
2426         exit(EXIT_FAILURE);
2427       }
2428       config.max_concurrent_streams = n;
2429       break;
2430     }
2431     case 'w':
2432     case 'W': {
2433       auto n = util::parse_uint(optarg);
2434       if (n == -1 || n > 30) {
2435         std::cerr << "-" << static_cast<char>(c)
2436                   << ": specify the integer in the range [0, 30], inclusive"
2437                   << std::endl;
2438         exit(EXIT_FAILURE);
2439       }
2440       if (c == 'w') {
2441         config.window_bits = n;
2442       } else {
2443         config.connection_window_bits = n;
2444       }
2445       break;
2446     }
2447     case 'f': {
2448       auto n = util::parse_uint_with_unit(optarg);
2449       if (n == -1) {
2450         std::cerr << "--max-frame-size: bad option value: " << optarg
2451                   << std::endl;
2452         exit(EXIT_FAILURE);
2453       }
2454       if (static_cast<uint64_t>(n) < 16_k) {
2455         std::cerr << "--max-frame-size: minimum 16384" << std::endl;
2456         exit(EXIT_FAILURE);
2457       }
2458       if (static_cast<uint64_t>(n) > 16_m - 1) {
2459         std::cerr << "--max-frame-size: maximum 16777215" << std::endl;
2460         exit(EXIT_FAILURE);
2461       }
2462       config.max_frame_size = n;
2463       break;
2464     }
2465     case 'H': {
2466       char *header = optarg;
2467       // Skip first possible ':' in the header name
2468       char *value = strchr(optarg + 1, ':');
2469       if (!value || (header[0] == ':' && header + 1 == value)) {
2470         std::cerr << "-H: invalid header: " << optarg << std::endl;
2471         exit(EXIT_FAILURE);
2472       }
2473       *value = 0;
2474       value++;
2475       while (isspace(*value)) {
2476         value++;
2477       }
2478       if (*value == 0) {
2479         // This could also be a valid case for suppressing a header
2480         // similar to curl
2481         std::cerr << "-H: invalid header - value missing: " << optarg
2482                   << std::endl;
2483         exit(EXIT_FAILURE);
2484       }
2485       // Note that there is no processing currently to handle multiple
2486       // message-header fields with the same field name
2487       config.custom_headers.emplace_back(header, value);
2488       util::inp_strlower(config.custom_headers.back().name);
2489       break;
2490     }
2491     case 'i':
2492       config.ifile = optarg;
2493       break;
2494     case 'p': {
2495       auto proto = StringRef{optarg};
2496       if (util::strieq(StringRef::from_lit(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID),
2497                        proto)) {
2498         config.no_tls_proto = Config::PROTO_HTTP2;
2499       } else if (util::strieq(NGHTTP2_H1_1, proto)) {
2500         config.no_tls_proto = Config::PROTO_HTTP1_1;
2501       } else {
2502         std::cerr << "-p: unsupported protocol " << proto << std::endl;
2503         exit(EXIT_FAILURE);
2504       }
2505       break;
2506     }
2507     case 'r': {
2508       auto n = util::parse_uint(optarg);
2509       if (n == -1) {
2510         std::cerr << "-r: bad option value: " << optarg << std::endl;
2511         exit(EXIT_FAILURE);
2512       }
2513       if (n == 0) {
2514         std::cerr << "-r: the rate at which connections are made "
2515                   << "must be positive." << std::endl;
2516         exit(EXIT_FAILURE);
2517       }
2518       config.rate = n;
2519       break;
2520     }
2521     case 'T':
2522       config.conn_active_timeout = util::parse_duration_with_unit(optarg);
2523       if (!std::isfinite(config.conn_active_timeout)) {
2524         std::cerr << "-T: bad value for the conn_active_timeout wait time: "
2525                   << optarg << std::endl;
2526         exit(EXIT_FAILURE);
2527       }
2528       break;
2529     case 'N':
2530       config.conn_inactivity_timeout = util::parse_duration_with_unit(optarg);
2531       if (!std::isfinite(config.conn_inactivity_timeout)) {
2532         std::cerr << "-N: bad value for the conn_inactivity_timeout wait time: "
2533                   << optarg << std::endl;
2534         exit(EXIT_FAILURE);
2535       }
2536       break;
2537     case 'B': {
2538       auto arg = StringRef{optarg};
2539       config.base_uri = "";
2540       config.base_uri_unix = false;
2541 
2542       if (util::istarts_with_l(arg, UNIX_PATH_PREFIX)) {
2543         // UNIX domain socket path
2544         sockaddr_un un;
2545 
2546         auto path = StringRef{std::begin(arg) + str_size(UNIX_PATH_PREFIX),
2547                               std::end(arg)};
2548 
2549         if (path.size() == 0 || path.size() + 1 > sizeof(un.sun_path)) {
2550           std::cerr << "--base-uri: invalid UNIX domain socket path: " << arg
2551                     << std::endl;
2552           exit(EXIT_FAILURE);
2553         }
2554 
2555         config.base_uri_unix = true;
2556 
2557         auto &unix_addr = config.unix_addr;
2558         std::copy(std::begin(path), std::end(path), unix_addr.sun_path);
2559         unix_addr.sun_path[path.size()] = '\0';
2560         unix_addr.sun_family = AF_UNIX;
2561 
2562         break;
2563       }
2564 
2565       if (!parse_base_uri(arg)) {
2566         std::cerr << "--base-uri: invalid base URI: " << arg << std::endl;
2567         exit(EXIT_FAILURE);
2568       }
2569 
2570       config.base_uri = arg.str();
2571       break;
2572     }
2573     case 'D':
2574       config.duration = util::parse_duration_with_unit(optarg);
2575       if (!std::isfinite(config.duration)) {
2576         std::cerr << "-D: value error " << optarg << std::endl;
2577         exit(EXIT_FAILURE);
2578       }
2579       break;
2580     case 'v':
2581       config.verbose = true;
2582       break;
2583     case 'h':
2584       print_help(std::cout);
2585       exit(EXIT_SUCCESS);
2586     case '?':
2587       util::show_candidates(argv[optind - 1], long_options);
2588       exit(EXIT_FAILURE);
2589     case 0:
2590       switch (flag) {
2591       case 1:
2592         // version option
2593         print_version(std::cout);
2594         exit(EXIT_SUCCESS);
2595       case 2:
2596         // ciphers option
2597         config.ciphers = optarg;
2598         break;
2599       case 3:
2600         // timing-script option
2601         config.ifile = optarg;
2602         config.timing_script = true;
2603         break;
2604       case 4:
2605         // npn-list option
2606         config.npn_list = util::parse_config_str_list(StringRef{optarg});
2607         break;
2608       case 5:
2609         // rate-period
2610         config.rate_period = util::parse_duration_with_unit(optarg);
2611         if (!std::isfinite(config.rate_period)) {
2612           std::cerr << "--rate-period: value error " << optarg << std::endl;
2613           exit(EXIT_FAILURE);
2614         }
2615         break;
2616       case 6:
2617         // --h1
2618         config.npn_list =
2619             util::parse_config_str_list(StringRef::from_lit("http/1.1"));
2620         config.no_tls_proto = Config::PROTO_HTTP1_1;
2621         break;
2622       case 7:
2623         // --header-table-size
2624         if (parse_header_table_size(config.header_table_size,
2625                                     "header-table-size", optarg) != 0) {
2626           exit(EXIT_FAILURE);
2627         }
2628         break;
2629       case 8:
2630         // --encoder-header-table-size
2631         if (parse_header_table_size(config.encoder_header_table_size,
2632                                     "encoder-header-table-size", optarg) != 0) {
2633           exit(EXIT_FAILURE);
2634         }
2635         break;
2636       case 9:
2637         // --warm-up-time
2638         config.warm_up_time = util::parse_duration_with_unit(optarg);
2639         if (!std::isfinite(config.warm_up_time)) {
2640           std::cerr << "--warm-up-time: value error " << optarg << std::endl;
2641           exit(EXIT_FAILURE);
2642         }
2643         break;
2644       case 10:
2645         // --log-file
2646         logfile = optarg;
2647         break;
2648       case 11: {
2649         // --connect-to
2650         auto p = util::split_hostport(StringRef{optarg});
2651         int64_t port = 0;
2652         if (p.first.empty() ||
2653             (!p.second.empty() && (port = util::parse_uint(p.second)) == -1)) {
2654           std::cerr << "--connect-to: Invalid value " << optarg << std::endl;
2655           exit(EXIT_FAILURE);
2656         }
2657         config.connect_to_host = p.first.str();
2658         config.connect_to_port = port;
2659         break;
2660       }
2661       case 12: {
2662         char *end;
2663         auto v = std::strtod(optarg, &end);
2664         if (end == optarg || *end != '\0' || !std::isfinite(v) ||
2665             1. / v < 1e-6) {
2666           std::cerr << "--rps: Invalid value " << optarg << std::endl;
2667           exit(EXIT_FAILURE);
2668         }
2669         config.rps = v;
2670         break;
2671       }
2672       case 13:
2673         // --groups
2674         config.groups = optarg;
2675         break;
2676       case 14:
2677         // --tls13-ciphers
2678         config.tls13_ciphers = optarg;
2679         break;
2680       case 15:
2681         // --no-udp-gso
2682         config.no_udp_gso = true;
2683         break;
2684       case 16:
2685         // --qlog-file-base
2686         qlog_base = optarg;
2687         break;
2688       case 17: {
2689         // --max-udp-payload-size
2690         auto n = util::parse_uint_with_unit(optarg);
2691         if (n == -1) {
2692           std::cerr << "--max-udp-payload-size: bad option value: " << optarg
2693                     << std::endl;
2694           exit(EXIT_FAILURE);
2695         }
2696         if (static_cast<uint64_t>(n) > 64_k) {
2697           std::cerr << "--max-udp-payload-size: must not exceed 65536"
2698                     << std::endl;
2699           exit(EXIT_FAILURE);
2700         }
2701         config.max_udp_payload_size = n;
2702         break;
2703       }
2704       case 18:
2705         // --ktls
2706         config.ktls = true;
2707         break;
2708       }
2709       break;
2710     default:
2711       break;
2712     }
2713   }
2714 
2715   if (argc == optind) {
2716     if (config.ifile.empty()) {
2717       std::cerr << "no URI or input file given" << std::endl;
2718       exit(EXIT_FAILURE);
2719     }
2720   }
2721 
2722   if (config.nclients == 0) {
2723     std::cerr << "-c: the number of clients must be strictly greater than 0."
2724               << std::endl;
2725     exit(EXIT_FAILURE);
2726   }
2727 
2728   if (config.npn_list.empty()) {
2729     config.npn_list =
2730         util::parse_config_str_list(StringRef::from_lit(DEFAULT_NPN_LIST));
2731   }
2732 
2733   // serialize the APLN tokens
2734   for (auto &proto : config.npn_list) {
2735     proto.insert(proto.begin(), static_cast<unsigned char>(proto.size()));
2736   }
2737 
2738   std::vector<std::string> reqlines;
2739 
2740   if (config.ifile.empty()) {
2741     std::vector<std::string> uris;
2742     std::copy(&argv[optind], &argv[argc], std::back_inserter(uris));
2743     reqlines = parse_uris(std::begin(uris), std::end(uris));
2744   } else {
2745     std::vector<std::string> uris;
2746     if (!config.timing_script) {
2747       if (config.ifile == "-") {
2748         uris = read_uri_from_file(std::cin);
2749       } else {
2750         std::ifstream infile(config.ifile);
2751         if (!infile) {
2752           std::cerr << "cannot read input file: " << config.ifile << std::endl;
2753           exit(EXIT_FAILURE);
2754         }
2755 
2756         uris = read_uri_from_file(infile);
2757       }
2758     } else {
2759       if (config.ifile == "-") {
2760         read_script_from_file(std::cin, config.timings, uris);
2761       } else {
2762         std::ifstream infile(config.ifile);
2763         if (!infile) {
2764           std::cerr << "cannot read input file: " << config.ifile << std::endl;
2765           exit(EXIT_FAILURE);
2766         }
2767 
2768         read_script_from_file(infile, config.timings, uris);
2769       }
2770 
2771       if (nreqs_set_manually) {
2772         if (config.nreqs > uris.size()) {
2773           std::cerr << "-n: the number of requests must be less than or equal "
2774                        "to the number of timing script entries. Setting number "
2775                        "of requests to "
2776                     << uris.size() << std::endl;
2777 
2778           config.nreqs = uris.size();
2779         }
2780       } else {
2781         config.nreqs = uris.size();
2782       }
2783     }
2784 
2785     reqlines = parse_uris(std::begin(uris), std::end(uris));
2786   }
2787 
2788   if (reqlines.empty()) {
2789     std::cerr << "No URI given" << std::endl;
2790     exit(EXIT_FAILURE);
2791   }
2792 
2793   if (config.is_timing_based_mode() && config.is_rate_mode()) {
2794     std::cerr << "-r, -D: they are mutually exclusive." << std::endl;
2795     exit(EXIT_FAILURE);
2796   }
2797 
2798   if (config.timing_script && config.rps_enabled()) {
2799     std::cerr << "--timing-script-file, --rps: they are mutually exclusive."
2800               << std::endl;
2801     exit(EXIT_FAILURE);
2802   }
2803 
2804   if (config.nreqs == 0 && !config.is_timing_based_mode()) {
2805     std::cerr << "-n: the number of requests must be strictly greater than 0 "
2806                  "if timing-based test is not being run."
2807               << std::endl;
2808     exit(EXIT_FAILURE);
2809   }
2810 
2811   if (config.max_concurrent_streams == 0) {
2812     std::cerr << "-m: the max concurrent streams must be strictly greater "
2813               << "than 0." << std::endl;
2814     exit(EXIT_FAILURE);
2815   }
2816 
2817   if (config.nthreads == 0) {
2818     std::cerr << "-t: the number of threads must be strictly greater than 0."
2819               << std::endl;
2820     exit(EXIT_FAILURE);
2821   }
2822 
2823   if (config.nthreads > std::thread::hardware_concurrency()) {
2824     std::cerr << "-t: warning: the number of threads is greater than hardware "
2825               << "cores." << std::endl;
2826   }
2827 
2828   // With timing script, we don't distribute config.nreqs to each
2829   // client or thread.
2830   if (!config.timing_script && config.nreqs < config.nclients &&
2831       !config.is_timing_based_mode()) {
2832     std::cerr << "-n, -c: the number of requests must be greater than or "
2833               << "equal to the clients." << std::endl;
2834     exit(EXIT_FAILURE);
2835   }
2836 
2837   if (config.nclients < config.nthreads) {
2838     std::cerr << "-c, -t: the number of clients must be greater than or equal "
2839               << "to the number of threads." << std::endl;
2840     exit(EXIT_FAILURE);
2841   }
2842 
2843   if (config.is_timing_based_mode()) {
2844     config.nreqs = 0;
2845   }
2846 
2847   if (config.is_rate_mode()) {
2848     if (config.rate < config.nthreads) {
2849       std::cerr << "-r, -t: the connection rate must be greater than or equal "
2850                 << "to the number of threads." << std::endl;
2851       exit(EXIT_FAILURE);
2852     }
2853 
2854     if (config.rate > config.nclients) {
2855       std::cerr << "-r, -c: the connection rate must be smaller than or equal "
2856                    "to the number of clients."
2857                 << std::endl;
2858       exit(EXIT_FAILURE);
2859     }
2860   }
2861 
2862   if (!datafile.empty()) {
2863     config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY);
2864     if (config.data_fd == -1) {
2865       std::cerr << "-d: Could not open file " << datafile << std::endl;
2866       exit(EXIT_FAILURE);
2867     }
2868     struct stat data_stat;
2869     if (fstat(config.data_fd, &data_stat) == -1) {
2870       std::cerr << "-d: Could not stat file " << datafile << std::endl;
2871       exit(EXIT_FAILURE);
2872     }
2873     config.data_length = data_stat.st_size;
2874     auto addr = mmap(nullptr, config.data_length, PROT_READ, MAP_SHARED,
2875                      config.data_fd, 0);
2876     if (addr == MAP_FAILED) {
2877       std::cerr << "-d: Could not mmap file " << datafile << std::endl;
2878       exit(EXIT_FAILURE);
2879     }
2880     config.data = static_cast<uint8_t *>(addr);
2881   }
2882 
2883   if (!logfile.empty()) {
2884     config.log_fd = open(logfile.c_str(), O_WRONLY | O_CREAT | O_APPEND,
2885                          S_IRUSR | S_IWUSR | S_IRGRP);
2886     if (config.log_fd == -1) {
2887       std::cerr << "--log-file: Could not open file " << logfile << std::endl;
2888       exit(EXIT_FAILURE);
2889     }
2890   }
2891 
2892   if (!qlog_base.empty()) {
2893     if (!config.is_quic()) {
2894       std::cerr
2895           << "Warning: --qlog-file-base: only effective in quic, ignoring."
2896           << std::endl;
2897     } else {
2898 #ifdef ENABLE_HTTP3
2899       config.qlog_file_base = qlog_base;
2900 #endif // ENABLE_HTTP3
2901     }
2902   }
2903 
2904   struct sigaction act {};
2905   act.sa_handler = SIG_IGN;
2906   sigaction(SIGPIPE, &act, nullptr);
2907 
2908   auto ssl_ctx = SSL_CTX_new(TLS_client_method());
2909   if (!ssl_ctx) {
2910     std::cerr << "Failed to create SSL_CTX: "
2911               << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2912     exit(EXIT_FAILURE);
2913   }
2914 
2915   auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
2916                   SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
2917                   SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
2918 
2919 #ifdef SSL_OP_ENABLE_KTLS
2920   if (config.ktls) {
2921     ssl_opts |= SSL_OP_ENABLE_KTLS;
2922   }
2923 #endif // SSL_OP_ENABLE_KTLS
2924 
2925   SSL_CTX_set_options(ssl_ctx, ssl_opts);
2926   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
2927   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
2928 
2929   if (config.is_quic()) {
2930 #ifdef ENABLE_HTTP3
2931 #  ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
2932     if (ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) {
2933       std::cerr << "ngtcp2_crypto_quictls_configure_client_context failed"
2934                 << std::endl;
2935       exit(EXIT_FAILURE);
2936     }
2937 #  endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
2938 #  ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
2939     if (ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
2940       std::cerr << "ngtcp2_crypto_boringssl_configure_client_context failed"
2941                 << std::endl;
2942       exit(EXIT_FAILURE);
2943     }
2944 #  endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
2945 #endif   // ENABLE_HTTP3
2946   } else if (nghttp2::tls::ssl_ctx_set_proto_versions(
2947                  ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
2948                  nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
2949     std::cerr << "Could not set TLS versions" << std::endl;
2950     exit(EXIT_FAILURE);
2951   }
2952 
2953   if (SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers.c_str()) == 0) {
2954     std::cerr << "SSL_CTX_set_cipher_list with " << config.ciphers
2955               << " failed: " << ERR_error_string(ERR_get_error(), nullptr)
2956               << std::endl;
2957     exit(EXIT_FAILURE);
2958   }
2959 
2960 #if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
2961   if (SSL_CTX_set_ciphersuites(ssl_ctx, config.tls13_ciphers.c_str()) == 0) {
2962     std::cerr << "SSL_CTX_set_ciphersuites with " << config.tls13_ciphers
2963               << " failed: " << ERR_error_string(ERR_get_error(), nullptr)
2964               << std::endl;
2965     exit(EXIT_FAILURE);
2966   }
2967 #endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
2968 
2969 #if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
2970   if (SSL_CTX_set1_groups_list(ssl_ctx, config.groups.c_str()) != 1) {
2971     std::cerr << "SSL_CTX_set1_groups_list failed" << std::endl;
2972     exit(EXIT_FAILURE);
2973   }
2974 #else  // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
2975   if (SSL_CTX_set1_curves_list(ssl_ctx, config.groups.c_str()) != 1) {
2976     std::cerr << "SSL_CTX_set1_curves_list failed" << std::endl;
2977     exit(EXIT_FAILURE);
2978   }
2979 #endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
2980 
2981 #ifndef OPENSSL_NO_NEXTPROTONEG
2982   SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
2983                                    nullptr);
2984 #endif // !OPENSSL_NO_NEXTPROTONEG
2985 
2986 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
2987   std::vector<unsigned char> proto_list;
2988   for (const auto &proto : config.npn_list) {
2989     std::copy_n(proto.c_str(), proto.size(), std::back_inserter(proto_list));
2990   }
2991 
2992   SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
2993 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
2994 
2995 #if OPENSSL_1_1_1_API
2996   auto keylog_filename = getenv("SSLKEYLOGFILE");
2997   if (keylog_filename) {
2998     keylog_file.open(keylog_filename, std::ios_base::app);
2999     if (keylog_file) {
3000       SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
3001     }
3002   }
3003 #endif // OPENSSL_1_1_1_API
3004 
3005   std::string user_agent = "h2load nghttp2/" NGHTTP2_VERSION;
3006   Headers shared_nva;
3007   shared_nva.emplace_back(":scheme", config.scheme);
3008   if (config.port != config.default_port) {
3009     shared_nva.emplace_back(":authority",
3010                             config.host + ":" + util::utos(config.port));
3011   } else {
3012     shared_nva.emplace_back(":authority", config.host);
3013   }
3014   shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST");
3015   shared_nva.emplace_back("user-agent", user_agent);
3016 
3017   // list header fields that can be overridden.
3018   auto override_hdrs = make_array<std::string>(":authority", ":host", ":method",
3019                                                ":scheme", "user-agent");
3020 
3021   for (auto &kv : config.custom_headers) {
3022     if (std::find(std::begin(override_hdrs), std::end(override_hdrs),
3023                   kv.name) != std::end(override_hdrs)) {
3024       // override header
3025       for (auto &nv : shared_nva) {
3026         if ((nv.name == ":authority" && kv.name == ":host") ||
3027             (nv.name == kv.name)) {
3028           nv.value = kv.value;
3029         }
3030       }
3031     } else {
3032       // add additional headers
3033       shared_nva.push_back(kv);
3034     }
3035   }
3036 
3037   std::string content_length_str;
3038   if (config.data_fd != -1) {
3039     content_length_str = util::utos(config.data_length);
3040   }
3041 
3042   auto method_it =
3043       std::find_if(std::begin(shared_nva), std::end(shared_nva),
3044                    [](const Header &nv) { return nv.name == ":method"; });
3045   assert(method_it != std::end(shared_nva));
3046 
3047   config.h1reqs.reserve(reqlines.size());
3048   config.nva.reserve(reqlines.size());
3049 
3050   for (auto &req : reqlines) {
3051     // For HTTP/1.1
3052     auto h1req = (*method_it).value;
3053     h1req += ' ';
3054     h1req += req;
3055     h1req += " HTTP/1.1\r\n";
3056     for (auto &nv : shared_nva) {
3057       if (nv.name == ":authority") {
3058         h1req += "Host: ";
3059         h1req += nv.value;
3060         h1req += "\r\n";
3061         continue;
3062       }
3063       if (nv.name[0] == ':') {
3064         continue;
3065       }
3066       h1req += nv.name;
3067       h1req += ": ";
3068       h1req += nv.value;
3069       h1req += "\r\n";
3070     }
3071 
3072     if (!content_length_str.empty()) {
3073       h1req += "Content-Length: ";
3074       h1req += content_length_str;
3075       h1req += "\r\n";
3076     }
3077     h1req += "\r\n";
3078 
3079     config.h1reqs.push_back(std::move(h1req));
3080 
3081     // For nghttp2
3082     std::vector<nghttp2_nv> nva;
3083     // 2 for :path, and possible content-length
3084     nva.reserve(2 + shared_nva.size());
3085 
3086     nva.push_back(http2::make_nv_ls(":path", req));
3087 
3088     for (auto &nv : shared_nva) {
3089       nva.push_back(http2::make_nv(nv.name, nv.value, false));
3090     }
3091 
3092     if (!content_length_str.empty()) {
3093       nva.push_back(http2::make_nv(StringRef::from_lit("content-length"),
3094                                    StringRef{content_length_str}));
3095     }
3096 
3097     config.nva.push_back(std::move(nva));
3098   }
3099 
3100   // Don't DOS our server!
3101   if (config.host == "nghttp2.org") {
3102     std::cerr << "Using h2load against public server " << config.host
3103               << " should be prohibited." << std::endl;
3104     exit(EXIT_FAILURE);
3105   }
3106 
3107   resolve_host();
3108 
3109   std::cout << "starting benchmark..." << std::endl;
3110 
3111   std::vector<std::unique_ptr<Worker>> workers;
3112   workers.reserve(config.nthreads);
3113 
3114 #ifndef NOTHREADS
3115   size_t nreqs_per_thread = 0;
3116   ssize_t nreqs_rem = 0;
3117 
3118   if (!config.timing_script) {
3119     nreqs_per_thread = config.nreqs / config.nthreads;
3120     nreqs_rem = config.nreqs % config.nthreads;
3121   }
3122 
3123   size_t nclients_per_thread = config.nclients / config.nthreads;
3124   ssize_t nclients_rem = config.nclients % config.nthreads;
3125 
3126   size_t rate_per_thread = config.rate / config.nthreads;
3127   ssize_t rate_per_thread_rem = config.rate % config.nthreads;
3128 
3129   size_t max_samples_per_thread =
3130       std::max(static_cast<size_t>(256), MAX_SAMPLES / config.nthreads);
3131 
3132   std::mutex mu;
3133   std::condition_variable cv;
3134   auto ready = false;
3135 
3136   std::vector<std::future<void>> futures;
3137   for (size_t i = 0; i < config.nthreads; ++i) {
3138     auto rate = rate_per_thread;
3139     if (rate_per_thread_rem > 0) {
3140       --rate_per_thread_rem;
3141       ++rate;
3142     }
3143     auto nclients = nclients_per_thread;
3144     if (nclients_rem > 0) {
3145       --nclients_rem;
3146       ++nclients;
3147     }
3148 
3149     size_t nreqs;
3150     if (config.timing_script) {
3151       // With timing script, each client issues config.nreqs requests.
3152       // We divide nreqs by number of clients in Worker ctor to
3153       // distribute requests to those clients evenly, so multiply
3154       // config.nreqs here by config.nclients.
3155       nreqs = config.nreqs * nclients;
3156     } else {
3157       nreqs = nreqs_per_thread;
3158       if (nreqs_rem > 0) {
3159         --nreqs_rem;
3160         ++nreqs;
3161       }
3162     }
3163 
3164     workers.push_back(create_worker(i, ssl_ctx, nreqs, nclients, rate,
3165                                     max_samples_per_thread));
3166     auto &worker = workers.back();
3167     futures.push_back(
3168         std::async(std::launch::async, [&worker, &mu, &cv, &ready]() {
3169           {
3170             std::unique_lock<std::mutex> ulk(mu);
3171             cv.wait(ulk, [&ready] { return ready; });
3172           }
3173           worker->run();
3174         }));
3175   }
3176 
3177   {
3178     std::lock_guard<std::mutex> lg(mu);
3179     ready = true;
3180     cv.notify_all();
3181   }
3182 
3183   auto start = std::chrono::steady_clock::now();
3184 
3185   for (auto &fut : futures) {
3186     fut.get();
3187   }
3188 
3189 #else  // NOTHREADS
3190   auto rate = config.rate;
3191   auto nclients = config.nclients;
3192   auto nreqs =
3193       config.timing_script ? config.nreqs * config.nclients : config.nreqs;
3194 
3195   workers.push_back(
3196       create_worker(0, ssl_ctx, nreqs, nclients, rate, MAX_SAMPLES));
3197 
3198   auto start = std::chrono::steady_clock::now();
3199 
3200   workers.back()->run();
3201 #endif // NOTHREADS
3202 
3203   auto end = std::chrono::steady_clock::now();
3204   auto duration =
3205       std::chrono::duration_cast<std::chrono::microseconds>(end - start);
3206 
3207   Stats stats(0, 0);
3208   for (const auto &w : workers) {
3209     const auto &s = w->stats;
3210 
3211     stats.req_todo += s.req_todo;
3212     stats.req_started += s.req_started;
3213     stats.req_done += s.req_done;
3214     stats.req_timedout += s.req_timedout;
3215     stats.req_success += s.req_success;
3216     stats.req_status_success += s.req_status_success;
3217     stats.req_failed += s.req_failed;
3218     stats.req_error += s.req_error;
3219     stats.bytes_total += s.bytes_total;
3220     stats.bytes_head += s.bytes_head;
3221     stats.bytes_head_decomp += s.bytes_head_decomp;
3222     stats.bytes_body += s.bytes_body;
3223     stats.udp_dgram_recv += s.udp_dgram_recv;
3224     stats.udp_dgram_sent += s.udp_dgram_sent;
3225 
3226     for (size_t i = 0; i < stats.status.size(); ++i) {
3227       stats.status[i] += s.status[i];
3228     }
3229   }
3230 
3231   auto ts = process_time_stats(workers);
3232 
3233   // Requests which have not been issued due to connection errors, are
3234   // counted towards req_failed and req_error.
3235   auto req_not_issued =
3236       (stats.req_todo - stats.req_status_success - stats.req_failed);
3237   stats.req_failed += req_not_issued;
3238   stats.req_error += req_not_issued;
3239 
3240   // UI is heavily inspired by weighttp[1] and wrk[2]
3241   //
3242   // [1] https://github.com/lighttpd/weighttp
3243   // [2] https://github.com/wg/wrk
3244   double rps = 0;
3245   int64_t bps = 0;
3246   if (duration.count() > 0) {
3247     if (config.is_timing_based_mode()) {
3248       // we only want to consider the main duration if warm-up is given
3249       rps = stats.req_success / config.duration;
3250       bps = stats.bytes_total / config.duration;
3251     } else {
3252       auto secd = std::chrono::duration_cast<
3253           std::chrono::duration<double, std::chrono::seconds::period>>(
3254           duration);
3255       rps = stats.req_success / secd.count();
3256       bps = stats.bytes_total / secd.count();
3257     }
3258   }
3259 
3260   double header_space_savings = 0.;
3261   if (stats.bytes_head_decomp > 0) {
3262     header_space_savings =
3263         1. - static_cast<double>(stats.bytes_head) / stats.bytes_head_decomp;
3264   }
3265 
3266   std::cout << std::fixed << std::setprecision(2) << R"(
3267 finished in )"
3268             << util::format_duration(duration) << ", " << rps << " req/s, "
3269             << util::utos_funit(bps) << R"(B/s
3270 requests: )" << stats.req_todo
3271             << " total, " << stats.req_started << " started, " << stats.req_done
3272             << " done, " << stats.req_status_success << " succeeded, "
3273             << stats.req_failed << " failed, " << stats.req_error
3274             << " errored, " << stats.req_timedout << R"( timeout
3275 status codes: )"
3276             << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
3277             << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
3278 traffic: )" << util::utos_funit(stats.bytes_total)
3279             << "B (" << stats.bytes_total << ") total, "
3280             << util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
3281             << ") headers (space savings " << header_space_savings * 100
3282             << "%), " << util::utos_funit(stats.bytes_body) << "B ("
3283             << stats.bytes_body << R"() data)" << std::endl;
3284 #ifdef ENABLE_HTTP3
3285   if (config.is_quic()) {
3286     std::cout << "UDP datagram: " << stats.udp_dgram_sent << " sent, "
3287               << stats.udp_dgram_recv << " received" << std::endl;
3288   }
3289 #endif // ENABLE_HTTP3
3290   std::cout
3291       << R"(                     min         max         mean         sd        +/- sd
3292 time for request: )"
3293       << std::setw(10) << util::format_duration(ts.request.min) << "  "
3294       << std::setw(10) << util::format_duration(ts.request.max) << "  "
3295       << std::setw(10) << util::format_duration(ts.request.mean) << "  "
3296       << std::setw(10) << util::format_duration(ts.request.sd) << std::setw(9)
3297       << util::dtos(ts.request.within_sd) << "%"
3298       << "\ntime for connect: " << std::setw(10)
3299       << util::format_duration(ts.connect.min) << "  " << std::setw(10)
3300       << util::format_duration(ts.connect.max) << "  " << std::setw(10)
3301       << util::format_duration(ts.connect.mean) << "  " << std::setw(10)
3302       << util::format_duration(ts.connect.sd) << std::setw(9)
3303       << util::dtos(ts.connect.within_sd) << "%"
3304       << "\ntime to 1st byte: " << std::setw(10)
3305       << util::format_duration(ts.ttfb.min) << "  " << std::setw(10)
3306       << util::format_duration(ts.ttfb.max) << "  " << std::setw(10)
3307       << util::format_duration(ts.ttfb.mean) << "  " << std::setw(10)
3308       << util::format_duration(ts.ttfb.sd) << std::setw(9)
3309       << util::dtos(ts.ttfb.within_sd) << "%"
3310       << "\nreq/s           : " << std::setw(10) << ts.rps.min << "  "
3311       << std::setw(10) << ts.rps.max << "  " << std::setw(10) << ts.rps.mean
3312       << "  " << std::setw(10) << ts.rps.sd << std::setw(9)
3313       << util::dtos(ts.rps.within_sd) << "%" << std::endl;
3314 
3315   SSL_CTX_free(ssl_ctx);
3316 
3317   if (config.log_fd != -1) {
3318     close(config.log_fd);
3319   }
3320 
3321   return 0;
3322 }
3323 
3324 } // namespace h2load
3325 
main(int argc,char ** argv)3326 int main(int argc, char **argv) { return h2load::main(argc, argv); }
3327