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