12c593315Sopenharmony_ci/*
22c593315Sopenharmony_ci * nghttp2 - HTTP/2 C Library
32c593315Sopenharmony_ci *
42c593315Sopenharmony_ci * Copyright (c) 2014 Tatsuhiro Tsujikawa
52c593315Sopenharmony_ci *
62c593315Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining
72c593315Sopenharmony_ci * a copy of this software and associated documentation files (the
82c593315Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
92c593315Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
102c593315Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to
112c593315Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
122c593315Sopenharmony_ci * the following conditions:
132c593315Sopenharmony_ci *
142c593315Sopenharmony_ci * The above copyright notice and this permission notice shall be
152c593315Sopenharmony_ci * included in all copies or substantial portions of the Software.
162c593315Sopenharmony_ci *
172c593315Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
182c593315Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
192c593315Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
202c593315Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
212c593315Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
222c593315Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
232c593315Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
242c593315Sopenharmony_ci */
252c593315Sopenharmony_ci#include "h2load.h"
262c593315Sopenharmony_ci
272c593315Sopenharmony_ci#include <getopt.h>
282c593315Sopenharmony_ci#include <signal.h>
292c593315Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
302c593315Sopenharmony_ci#  include <netinet/in.h>
312c593315Sopenharmony_ci#endif // HAVE_NETINET_IN_H
322c593315Sopenharmony_ci#include <netinet/tcp.h>
332c593315Sopenharmony_ci#include <sys/stat.h>
342c593315Sopenharmony_ci#ifdef HAVE_FCNTL_H
352c593315Sopenharmony_ci#  include <fcntl.h>
362c593315Sopenharmony_ci#endif // HAVE_FCNTL_H
372c593315Sopenharmony_ci#include <sys/mman.h>
382c593315Sopenharmony_ci#include <netinet/udp.h>
392c593315Sopenharmony_ci
402c593315Sopenharmony_ci#include <cstdio>
412c593315Sopenharmony_ci#include <cassert>
422c593315Sopenharmony_ci#include <cstdlib>
432c593315Sopenharmony_ci#include <iostream>
442c593315Sopenharmony_ci#include <iomanip>
452c593315Sopenharmony_ci#include <fstream>
462c593315Sopenharmony_ci#include <chrono>
472c593315Sopenharmony_ci#include <thread>
482c593315Sopenharmony_ci#include <future>
492c593315Sopenharmony_ci#include <random>
502c593315Sopenharmony_ci
512c593315Sopenharmony_ci#include <openssl/err.h>
522c593315Sopenharmony_ci
532c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
542c593315Sopenharmony_ci#  ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
552c593315Sopenharmony_ci#    include <ngtcp2/ngtcp2_crypto_quictls.h>
562c593315Sopenharmony_ci#  endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
572c593315Sopenharmony_ci#  ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
582c593315Sopenharmony_ci#    include <ngtcp2/ngtcp2_crypto_boringssl.h>
592c593315Sopenharmony_ci#  endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
602c593315Sopenharmony_ci#endif   // ENABLE_HTTP3
612c593315Sopenharmony_ci
622c593315Sopenharmony_ci#include "url-parser/url_parser.h"
632c593315Sopenharmony_ci
642c593315Sopenharmony_ci#include "h2load_http1_session.h"
652c593315Sopenharmony_ci#include "h2load_http2_session.h"
662c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
672c593315Sopenharmony_ci#  include "h2load_http3_session.h"
682c593315Sopenharmony_ci#  include "h2load_quic.h"
692c593315Sopenharmony_ci#endif // ENABLE_HTTP3
702c593315Sopenharmony_ci#include "tls.h"
712c593315Sopenharmony_ci#include "http2.h"
722c593315Sopenharmony_ci#include "util.h"
732c593315Sopenharmony_ci#include "template.h"
742c593315Sopenharmony_ci
752c593315Sopenharmony_ci#ifndef O_BINARY
762c593315Sopenharmony_ci#  define O_BINARY (0)
772c593315Sopenharmony_ci#endif // O_BINARY
782c593315Sopenharmony_ci
792c593315Sopenharmony_ciusing namespace nghttp2;
802c593315Sopenharmony_ci
812c593315Sopenharmony_cinamespace h2load {
822c593315Sopenharmony_ci
832c593315Sopenharmony_cinamespace {
842c593315Sopenharmony_cibool recorded(const std::chrono::steady_clock::time_point &t) {
852c593315Sopenharmony_ci  return std::chrono::steady_clock::duration::zero() != t.time_since_epoch();
862c593315Sopenharmony_ci}
872c593315Sopenharmony_ci} // namespace
882c593315Sopenharmony_ci
892c593315Sopenharmony_ci#if OPENSSL_1_1_1_API
902c593315Sopenharmony_cinamespace {
912c593315Sopenharmony_cistd::ofstream keylog_file;
922c593315Sopenharmony_civoid keylog_callback(const SSL *ssl, const char *line) {
932c593315Sopenharmony_ci  keylog_file.write(line, strlen(line));
942c593315Sopenharmony_ci  keylog_file.put('\n');
952c593315Sopenharmony_ci  keylog_file.flush();
962c593315Sopenharmony_ci}
972c593315Sopenharmony_ci} // namespace
982c593315Sopenharmony_ci#endif // OPENSSL_1_1_1_API
992c593315Sopenharmony_ci
1002c593315Sopenharmony_ciConfig::Config()
1012c593315Sopenharmony_ci    : ciphers(tls::DEFAULT_CIPHER_LIST),
1022c593315Sopenharmony_ci      tls13_ciphers("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_"
1032c593315Sopenharmony_ci                    "CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256"),
1042c593315Sopenharmony_ci      groups("X25519:P-256:P-384:P-521"),
1052c593315Sopenharmony_ci      data_length(-1),
1062c593315Sopenharmony_ci      data(nullptr),
1072c593315Sopenharmony_ci      addrs(nullptr),
1082c593315Sopenharmony_ci      nreqs(1),
1092c593315Sopenharmony_ci      nclients(1),
1102c593315Sopenharmony_ci      nthreads(1),
1112c593315Sopenharmony_ci      max_concurrent_streams(1),
1122c593315Sopenharmony_ci      window_bits(30),
1132c593315Sopenharmony_ci      connection_window_bits(30),
1142c593315Sopenharmony_ci      max_frame_size(16_k),
1152c593315Sopenharmony_ci      rate(0),
1162c593315Sopenharmony_ci      rate_period(1.0),
1172c593315Sopenharmony_ci      duration(0.0),
1182c593315Sopenharmony_ci      warm_up_time(0.0),
1192c593315Sopenharmony_ci      conn_active_timeout(0.),
1202c593315Sopenharmony_ci      conn_inactivity_timeout(0.),
1212c593315Sopenharmony_ci      no_tls_proto(PROTO_HTTP2),
1222c593315Sopenharmony_ci      header_table_size(4_k),
1232c593315Sopenharmony_ci      encoder_header_table_size(4_k),
1242c593315Sopenharmony_ci      data_fd(-1),
1252c593315Sopenharmony_ci      log_fd(-1),
1262c593315Sopenharmony_ci      qlog_file_base(),
1272c593315Sopenharmony_ci      port(0),
1282c593315Sopenharmony_ci      default_port(0),
1292c593315Sopenharmony_ci      connect_to_port(0),
1302c593315Sopenharmony_ci      verbose(false),
1312c593315Sopenharmony_ci      timing_script(false),
1322c593315Sopenharmony_ci      base_uri_unix(false),
1332c593315Sopenharmony_ci      unix_addr{},
1342c593315Sopenharmony_ci      rps(0.),
1352c593315Sopenharmony_ci      no_udp_gso(false),
1362c593315Sopenharmony_ci      max_udp_payload_size(0),
1372c593315Sopenharmony_ci      ktls(false) {}
1382c593315Sopenharmony_ci
1392c593315Sopenharmony_ciConfig::~Config() {
1402c593315Sopenharmony_ci  if (addrs) {
1412c593315Sopenharmony_ci    if (base_uri_unix) {
1422c593315Sopenharmony_ci      delete addrs;
1432c593315Sopenharmony_ci    } else {
1442c593315Sopenharmony_ci      freeaddrinfo(addrs);
1452c593315Sopenharmony_ci    }
1462c593315Sopenharmony_ci  }
1472c593315Sopenharmony_ci
1482c593315Sopenharmony_ci  if (data_fd != -1) {
1492c593315Sopenharmony_ci    close(data_fd);
1502c593315Sopenharmony_ci  }
1512c593315Sopenharmony_ci}
1522c593315Sopenharmony_ci
1532c593315Sopenharmony_cibool Config::is_rate_mode() const { return (this->rate != 0); }
1542c593315Sopenharmony_cibool Config::is_timing_based_mode() const { return (this->duration > 0); }
1552c593315Sopenharmony_cibool Config::has_base_uri() const { return (!this->base_uri.empty()); }
1562c593315Sopenharmony_cibool Config::rps_enabled() const { return this->rps > 0.0; }
1572c593315Sopenharmony_cibool Config::is_quic() const {
1582c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
1592c593315Sopenharmony_ci  return !npn_list.empty() &&
1602c593315Sopenharmony_ci         (npn_list[0] == NGHTTP3_ALPN_H3 || npn_list[0] == "\x5h3-29");
1612c593315Sopenharmony_ci#else  // !ENABLE_HTTP3
1622c593315Sopenharmony_ci  return false;
1632c593315Sopenharmony_ci#endif // !ENABLE_HTTP3
1642c593315Sopenharmony_ci}
1652c593315Sopenharmony_ciConfig config;
1662c593315Sopenharmony_ci
1672c593315Sopenharmony_cinamespace {
1682c593315Sopenharmony_ciconstexpr size_t MAX_SAMPLES = 1000000;
1692c593315Sopenharmony_ci} // namespace
1702c593315Sopenharmony_ci
1712c593315Sopenharmony_ciStats::Stats(size_t req_todo, size_t nclients)
1722c593315Sopenharmony_ci    : req_todo(req_todo),
1732c593315Sopenharmony_ci      req_started(0),
1742c593315Sopenharmony_ci      req_done(0),
1752c593315Sopenharmony_ci      req_success(0),
1762c593315Sopenharmony_ci      req_status_success(0),
1772c593315Sopenharmony_ci      req_failed(0),
1782c593315Sopenharmony_ci      req_error(0),
1792c593315Sopenharmony_ci      req_timedout(0),
1802c593315Sopenharmony_ci      bytes_total(0),
1812c593315Sopenharmony_ci      bytes_head(0),
1822c593315Sopenharmony_ci      bytes_head_decomp(0),
1832c593315Sopenharmony_ci      bytes_body(0),
1842c593315Sopenharmony_ci      status(),
1852c593315Sopenharmony_ci      udp_dgram_recv(0),
1862c593315Sopenharmony_ci      udp_dgram_sent(0) {}
1872c593315Sopenharmony_ci
1882c593315Sopenharmony_ciStream::Stream() : req_stat{}, status_success(-1) {}
1892c593315Sopenharmony_ci
1902c593315Sopenharmony_cinamespace {
1912c593315Sopenharmony_cistd::random_device rd;
1922c593315Sopenharmony_ci} // namespace
1932c593315Sopenharmony_ci
1942c593315Sopenharmony_cinamespace {
1952c593315Sopenharmony_cistd::mt19937 gen(rd());
1962c593315Sopenharmony_ci} // namespace
1972c593315Sopenharmony_ci
1982c593315Sopenharmony_cinamespace {
1992c593315Sopenharmony_civoid sampling_init(Sampling &smp, size_t max_samples) {
2002c593315Sopenharmony_ci  smp.n = 0;
2012c593315Sopenharmony_ci  smp.max_samples = max_samples;
2022c593315Sopenharmony_ci}
2032c593315Sopenharmony_ci} // namespace
2042c593315Sopenharmony_ci
2052c593315Sopenharmony_cinamespace {
2062c593315Sopenharmony_civoid writecb(struct ev_loop *loop, ev_io *w, int revents) {
2072c593315Sopenharmony_ci  auto client = static_cast<Client *>(w->data);
2082c593315Sopenharmony_ci  client->restart_timeout();
2092c593315Sopenharmony_ci  auto rv = client->do_write();
2102c593315Sopenharmony_ci  if (rv == Client::ERR_CONNECT_FAIL) {
2112c593315Sopenharmony_ci    client->disconnect();
2122c593315Sopenharmony_ci    // Try next address
2132c593315Sopenharmony_ci    client->current_addr = nullptr;
2142c593315Sopenharmony_ci    rv = client->connect();
2152c593315Sopenharmony_ci    if (rv != 0) {
2162c593315Sopenharmony_ci      client->fail();
2172c593315Sopenharmony_ci      client->worker->free_client(client);
2182c593315Sopenharmony_ci      delete client;
2192c593315Sopenharmony_ci      return;
2202c593315Sopenharmony_ci    }
2212c593315Sopenharmony_ci    return;
2222c593315Sopenharmony_ci  }
2232c593315Sopenharmony_ci  if (rv != 0) {
2242c593315Sopenharmony_ci    client->fail();
2252c593315Sopenharmony_ci    client->worker->free_client(client);
2262c593315Sopenharmony_ci    delete client;
2272c593315Sopenharmony_ci  }
2282c593315Sopenharmony_ci}
2292c593315Sopenharmony_ci} // namespace
2302c593315Sopenharmony_ci
2312c593315Sopenharmony_cinamespace {
2322c593315Sopenharmony_civoid readcb(struct ev_loop *loop, ev_io *w, int revents) {
2332c593315Sopenharmony_ci  auto client = static_cast<Client *>(w->data);
2342c593315Sopenharmony_ci  client->restart_timeout();
2352c593315Sopenharmony_ci  if (client->do_read() != 0) {
2362c593315Sopenharmony_ci    if (client->try_again_or_fail() == 0) {
2372c593315Sopenharmony_ci      return;
2382c593315Sopenharmony_ci    }
2392c593315Sopenharmony_ci    client->worker->free_client(client);
2402c593315Sopenharmony_ci    delete client;
2412c593315Sopenharmony_ci    return;
2422c593315Sopenharmony_ci  }
2432c593315Sopenharmony_ci  client->signal_write();
2442c593315Sopenharmony_ci}
2452c593315Sopenharmony_ci} // namespace
2462c593315Sopenharmony_ci
2472c593315Sopenharmony_cinamespace {
2482c593315Sopenharmony_ci// Called every rate_period when rate mode is being used
2492c593315Sopenharmony_civoid rate_period_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
2502c593315Sopenharmony_ci  auto worker = static_cast<Worker *>(w->data);
2512c593315Sopenharmony_ci  auto nclients_per_second = worker->rate;
2522c593315Sopenharmony_ci  auto conns_remaining = worker->nclients - worker->nconns_made;
2532c593315Sopenharmony_ci  auto nclients = std::min(nclients_per_second, conns_remaining);
2542c593315Sopenharmony_ci
2552c593315Sopenharmony_ci  for (size_t i = 0; i < nclients; ++i) {
2562c593315Sopenharmony_ci    auto req_todo = worker->nreqs_per_client;
2572c593315Sopenharmony_ci    if (worker->nreqs_rem > 0) {
2582c593315Sopenharmony_ci      ++req_todo;
2592c593315Sopenharmony_ci      --worker->nreqs_rem;
2602c593315Sopenharmony_ci    }
2612c593315Sopenharmony_ci    auto client =
2622c593315Sopenharmony_ci        std::make_unique<Client>(worker->next_client_id++, worker, req_todo);
2632c593315Sopenharmony_ci
2642c593315Sopenharmony_ci    ++worker->nconns_made;
2652c593315Sopenharmony_ci
2662c593315Sopenharmony_ci    if (client->connect() != 0) {
2672c593315Sopenharmony_ci      std::cerr << "client could not connect to host" << std::endl;
2682c593315Sopenharmony_ci      client->fail();
2692c593315Sopenharmony_ci    } else {
2702c593315Sopenharmony_ci      if (worker->config->is_timing_based_mode()) {
2712c593315Sopenharmony_ci        worker->clients.push_back(client.release());
2722c593315Sopenharmony_ci      } else {
2732c593315Sopenharmony_ci        client.release();
2742c593315Sopenharmony_ci      }
2752c593315Sopenharmony_ci    }
2762c593315Sopenharmony_ci    worker->report_rate_progress();
2772c593315Sopenharmony_ci  }
2782c593315Sopenharmony_ci  if (!worker->config->is_timing_based_mode()) {
2792c593315Sopenharmony_ci    if (worker->nconns_made >= worker->nclients) {
2802c593315Sopenharmony_ci      ev_timer_stop(worker->loop, w);
2812c593315Sopenharmony_ci    }
2822c593315Sopenharmony_ci  } else {
2832c593315Sopenharmony_ci    // To check whether all created clients are pushed correctly
2842c593315Sopenharmony_ci    assert(worker->nclients == worker->clients.size());
2852c593315Sopenharmony_ci  }
2862c593315Sopenharmony_ci}
2872c593315Sopenharmony_ci} // namespace
2882c593315Sopenharmony_ci
2892c593315Sopenharmony_cinamespace {
2902c593315Sopenharmony_ci// Called when the duration for infinite number of requests are over
2912c593315Sopenharmony_civoid duration_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
2922c593315Sopenharmony_ci  auto worker = static_cast<Worker *>(w->data);
2932c593315Sopenharmony_ci
2942c593315Sopenharmony_ci  worker->current_phase = Phase::DURATION_OVER;
2952c593315Sopenharmony_ci
2962c593315Sopenharmony_ci  std::cout << "Main benchmark duration is over for thread #" << worker->id
2972c593315Sopenharmony_ci            << ". Stopping all clients." << std::endl;
2982c593315Sopenharmony_ci  worker->stop_all_clients();
2992c593315Sopenharmony_ci  std::cout << "Stopped all clients for thread #" << worker->id << std::endl;
3002c593315Sopenharmony_ci}
3012c593315Sopenharmony_ci} // namespace
3022c593315Sopenharmony_ci
3032c593315Sopenharmony_cinamespace {
3042c593315Sopenharmony_ci// Called when the warmup duration for infinite number of requests are over
3052c593315Sopenharmony_civoid warmup_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
3062c593315Sopenharmony_ci  auto worker = static_cast<Worker *>(w->data);
3072c593315Sopenharmony_ci
3082c593315Sopenharmony_ci  std::cout << "Warm-up phase is over for thread #" << worker->id << "."
3092c593315Sopenharmony_ci            << std::endl;
3102c593315Sopenharmony_ci  std::cout << "Main benchmark duration is started for thread #" << worker->id
3112c593315Sopenharmony_ci            << "." << std::endl;
3122c593315Sopenharmony_ci  assert(worker->stats.req_started == 0);
3132c593315Sopenharmony_ci  assert(worker->stats.req_done == 0);
3142c593315Sopenharmony_ci
3152c593315Sopenharmony_ci  for (auto client : worker->clients) {
3162c593315Sopenharmony_ci    if (client) {
3172c593315Sopenharmony_ci      assert(client->req_todo == 0);
3182c593315Sopenharmony_ci      assert(client->req_left == 1);
3192c593315Sopenharmony_ci      assert(client->req_inflight == 0);
3202c593315Sopenharmony_ci      assert(client->req_started == 0);
3212c593315Sopenharmony_ci      assert(client->req_done == 0);
3222c593315Sopenharmony_ci
3232c593315Sopenharmony_ci      client->record_client_start_time();
3242c593315Sopenharmony_ci      client->clear_connect_times();
3252c593315Sopenharmony_ci      client->record_connect_start_time();
3262c593315Sopenharmony_ci    }
3272c593315Sopenharmony_ci  }
3282c593315Sopenharmony_ci
3292c593315Sopenharmony_ci  worker->current_phase = Phase::MAIN_DURATION;
3302c593315Sopenharmony_ci
3312c593315Sopenharmony_ci  ev_timer_start(worker->loop, &worker->duration_watcher);
3322c593315Sopenharmony_ci}
3332c593315Sopenharmony_ci} // namespace
3342c593315Sopenharmony_ci
3352c593315Sopenharmony_cinamespace {
3362c593315Sopenharmony_civoid rps_cb(struct ev_loop *loop, ev_timer *w, int revents) {
3372c593315Sopenharmony_ci  auto client = static_cast<Client *>(w->data);
3382c593315Sopenharmony_ci  auto &session = client->session;
3392c593315Sopenharmony_ci
3402c593315Sopenharmony_ci  assert(!config.timing_script);
3412c593315Sopenharmony_ci
3422c593315Sopenharmony_ci  if (client->req_left == 0) {
3432c593315Sopenharmony_ci    ev_timer_stop(loop, w);
3442c593315Sopenharmony_ci    return;
3452c593315Sopenharmony_ci  }
3462c593315Sopenharmony_ci
3472c593315Sopenharmony_ci  auto now = std::chrono::steady_clock::now();
3482c593315Sopenharmony_ci  auto d = now - client->rps_duration_started;
3492c593315Sopenharmony_ci  auto n = static_cast<size_t>(
3502c593315Sopenharmony_ci      round(std::chrono::duration<double>(d).count() * config.rps));
3512c593315Sopenharmony_ci  client->rps_req_pending += n;
3522c593315Sopenharmony_ci  client->rps_duration_started +=
3532c593315Sopenharmony_ci      util::duration_from(static_cast<double>(n) / config.rps);
3542c593315Sopenharmony_ci
3552c593315Sopenharmony_ci  if (client->rps_req_pending == 0) {
3562c593315Sopenharmony_ci    return;
3572c593315Sopenharmony_ci  }
3582c593315Sopenharmony_ci
3592c593315Sopenharmony_ci  auto nreq = session->max_concurrent_streams() - client->rps_req_inflight;
3602c593315Sopenharmony_ci  if (nreq == 0) {
3612c593315Sopenharmony_ci    return;
3622c593315Sopenharmony_ci  }
3632c593315Sopenharmony_ci
3642c593315Sopenharmony_ci  nreq = config.is_timing_based_mode() ? std::max(nreq, client->req_left)
3652c593315Sopenharmony_ci                                       : std::min(nreq, client->req_left);
3662c593315Sopenharmony_ci  nreq = std::min(nreq, client->rps_req_pending);
3672c593315Sopenharmony_ci
3682c593315Sopenharmony_ci  client->rps_req_inflight += nreq;
3692c593315Sopenharmony_ci  client->rps_req_pending -= nreq;
3702c593315Sopenharmony_ci
3712c593315Sopenharmony_ci  for (; nreq > 0; --nreq) {
3722c593315Sopenharmony_ci    if (client->submit_request() != 0) {
3732c593315Sopenharmony_ci      client->process_request_failure();
3742c593315Sopenharmony_ci      break;
3752c593315Sopenharmony_ci    }
3762c593315Sopenharmony_ci  }
3772c593315Sopenharmony_ci
3782c593315Sopenharmony_ci  client->signal_write();
3792c593315Sopenharmony_ci}
3802c593315Sopenharmony_ci} // namespace
3812c593315Sopenharmony_ci
3822c593315Sopenharmony_cinamespace {
3832c593315Sopenharmony_ci// Called when an a connection has been inactive for a set period of time
3842c593315Sopenharmony_ci// or a fixed amount of time after all requests have been made on a
3852c593315Sopenharmony_ci// connection
3862c593315Sopenharmony_civoid conn_timeout_cb(EV_P_ ev_timer *w, int revents) {
3872c593315Sopenharmony_ci  auto client = static_cast<Client *>(w->data);
3882c593315Sopenharmony_ci
3892c593315Sopenharmony_ci  ev_timer_stop(client->worker->loop, &client->conn_inactivity_watcher);
3902c593315Sopenharmony_ci  ev_timer_stop(client->worker->loop, &client->conn_active_watcher);
3912c593315Sopenharmony_ci
3922c593315Sopenharmony_ci  if (util::check_socket_connected(client->fd)) {
3932c593315Sopenharmony_ci    client->timeout();
3942c593315Sopenharmony_ci  }
3952c593315Sopenharmony_ci}
3962c593315Sopenharmony_ci} // namespace
3972c593315Sopenharmony_ci
3982c593315Sopenharmony_cinamespace {
3992c593315Sopenharmony_cibool check_stop_client_request_timeout(Client *client, ev_timer *w) {
4002c593315Sopenharmony_ci  if (client->req_left == 0) {
4012c593315Sopenharmony_ci    // no more requests to make, stop timer
4022c593315Sopenharmony_ci    ev_timer_stop(client->worker->loop, w);
4032c593315Sopenharmony_ci    return true;
4042c593315Sopenharmony_ci  }
4052c593315Sopenharmony_ci
4062c593315Sopenharmony_ci  return false;
4072c593315Sopenharmony_ci}
4082c593315Sopenharmony_ci} // namespace
4092c593315Sopenharmony_ci
4102c593315Sopenharmony_cinamespace {
4112c593315Sopenharmony_civoid client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
4122c593315Sopenharmony_ci  auto client = static_cast<Client *>(w->data);
4132c593315Sopenharmony_ci
4142c593315Sopenharmony_ci  if (client->streams.size() >= (size_t)config.max_concurrent_streams) {
4152c593315Sopenharmony_ci    ev_timer_stop(client->worker->loop, w);
4162c593315Sopenharmony_ci    return;
4172c593315Sopenharmony_ci  }
4182c593315Sopenharmony_ci
4192c593315Sopenharmony_ci  if (client->submit_request() != 0) {
4202c593315Sopenharmony_ci    ev_timer_stop(client->worker->loop, w);
4212c593315Sopenharmony_ci    client->process_request_failure();
4222c593315Sopenharmony_ci    return;
4232c593315Sopenharmony_ci  }
4242c593315Sopenharmony_ci  client->signal_write();
4252c593315Sopenharmony_ci
4262c593315Sopenharmony_ci  if (check_stop_client_request_timeout(client, w)) {
4272c593315Sopenharmony_ci    return;
4282c593315Sopenharmony_ci  }
4292c593315Sopenharmony_ci
4302c593315Sopenharmony_ci  auto duration =
4312c593315Sopenharmony_ci      config.timings[client->reqidx] - config.timings[client->reqidx - 1];
4322c593315Sopenharmony_ci
4332c593315Sopenharmony_ci  while (duration < std::chrono::duration<double>(1e-9)) {
4342c593315Sopenharmony_ci    if (client->submit_request() != 0) {
4352c593315Sopenharmony_ci      ev_timer_stop(client->worker->loop, w);
4362c593315Sopenharmony_ci      client->process_request_failure();
4372c593315Sopenharmony_ci      return;
4382c593315Sopenharmony_ci    }
4392c593315Sopenharmony_ci    client->signal_write();
4402c593315Sopenharmony_ci    if (check_stop_client_request_timeout(client, w)) {
4412c593315Sopenharmony_ci      return;
4422c593315Sopenharmony_ci    }
4432c593315Sopenharmony_ci
4442c593315Sopenharmony_ci    duration =
4452c593315Sopenharmony_ci        config.timings[client->reqidx] - config.timings[client->reqidx - 1];
4462c593315Sopenharmony_ci  }
4472c593315Sopenharmony_ci
4482c593315Sopenharmony_ci  client->request_timeout_watcher.repeat = util::ev_tstamp_from(duration);
4492c593315Sopenharmony_ci  ev_timer_again(client->worker->loop, &client->request_timeout_watcher);
4502c593315Sopenharmony_ci}
4512c593315Sopenharmony_ci} // namespace
4522c593315Sopenharmony_ci
4532c593315Sopenharmony_ciClient::Client(uint32_t id, Worker *worker, size_t req_todo)
4542c593315Sopenharmony_ci    : wb(&worker->mcpool),
4552c593315Sopenharmony_ci      cstat{},
4562c593315Sopenharmony_ci      worker(worker),
4572c593315Sopenharmony_ci      ssl(nullptr),
4582c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
4592c593315Sopenharmony_ci      quic{},
4602c593315Sopenharmony_ci#endif // ENABLE_HTTP3
4612c593315Sopenharmony_ci      next_addr(config.addrs),
4622c593315Sopenharmony_ci      current_addr(nullptr),
4632c593315Sopenharmony_ci      reqidx(0),
4642c593315Sopenharmony_ci      state(CLIENT_IDLE),
4652c593315Sopenharmony_ci      req_todo(req_todo),
4662c593315Sopenharmony_ci      req_left(req_todo),
4672c593315Sopenharmony_ci      req_inflight(0),
4682c593315Sopenharmony_ci      req_started(0),
4692c593315Sopenharmony_ci      req_done(0),
4702c593315Sopenharmony_ci      id(id),
4712c593315Sopenharmony_ci      fd(-1),
4722c593315Sopenharmony_ci      local_addr{},
4732c593315Sopenharmony_ci      new_connection_requested(false),
4742c593315Sopenharmony_ci      final(false),
4752c593315Sopenharmony_ci      rps_req_pending(0),
4762c593315Sopenharmony_ci      rps_req_inflight(0) {
4772c593315Sopenharmony_ci  if (req_todo == 0) { // this means infinite number of requests are to be made
4782c593315Sopenharmony_ci    // This ensures that number of requests are unbounded
4792c593315Sopenharmony_ci    // Just a positive number is fine, we chose the first positive number
4802c593315Sopenharmony_ci    req_left = 1;
4812c593315Sopenharmony_ci  }
4822c593315Sopenharmony_ci  ev_io_init(&wev, writecb, 0, EV_WRITE);
4832c593315Sopenharmony_ci  ev_io_init(&rev, readcb, 0, EV_READ);
4842c593315Sopenharmony_ci
4852c593315Sopenharmony_ci  wev.data = this;
4862c593315Sopenharmony_ci  rev.data = this;
4872c593315Sopenharmony_ci
4882c593315Sopenharmony_ci  ev_timer_init(&conn_inactivity_watcher, conn_timeout_cb, 0.,
4892c593315Sopenharmony_ci                worker->config->conn_inactivity_timeout);
4902c593315Sopenharmony_ci  conn_inactivity_watcher.data = this;
4912c593315Sopenharmony_ci
4922c593315Sopenharmony_ci  ev_timer_init(&conn_active_watcher, conn_timeout_cb,
4932c593315Sopenharmony_ci                worker->config->conn_active_timeout, 0.);
4942c593315Sopenharmony_ci  conn_active_watcher.data = this;
4952c593315Sopenharmony_ci
4962c593315Sopenharmony_ci  ev_timer_init(&request_timeout_watcher, client_request_timeout_cb, 0., 0.);
4972c593315Sopenharmony_ci  request_timeout_watcher.data = this;
4982c593315Sopenharmony_ci
4992c593315Sopenharmony_ci  ev_timer_init(&rps_watcher, rps_cb, 0., 0.);
5002c593315Sopenharmony_ci  rps_watcher.data = this;
5012c593315Sopenharmony_ci
5022c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
5032c593315Sopenharmony_ci  ev_timer_init(&quic.pkt_timer, quic_pkt_timeout_cb, 0., 0.);
5042c593315Sopenharmony_ci  quic.pkt_timer.data = this;
5052c593315Sopenharmony_ci
5062c593315Sopenharmony_ci  if (config.is_quic()) {
5072c593315Sopenharmony_ci    quic.tx.data = std::make_unique<uint8_t[]>(64_k);
5082c593315Sopenharmony_ci  }
5092c593315Sopenharmony_ci
5102c593315Sopenharmony_ci  ngtcp2_ccerr_default(&quic.last_error);
5112c593315Sopenharmony_ci#endif // ENABLE_HTTP3
5122c593315Sopenharmony_ci}
5132c593315Sopenharmony_ci
5142c593315Sopenharmony_ciClient::~Client() {
5152c593315Sopenharmony_ci  disconnect();
5162c593315Sopenharmony_ci
5172c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
5182c593315Sopenharmony_ci  if (config.is_quic()) {
5192c593315Sopenharmony_ci    quic_free();
5202c593315Sopenharmony_ci  }
5212c593315Sopenharmony_ci#endif // ENABLE_HTTP3
5222c593315Sopenharmony_ci
5232c593315Sopenharmony_ci  if (ssl) {
5242c593315Sopenharmony_ci    SSL_free(ssl);
5252c593315Sopenharmony_ci  }
5262c593315Sopenharmony_ci
5272c593315Sopenharmony_ci  worker->sample_client_stat(&cstat);
5282c593315Sopenharmony_ci  ++worker->client_smp.n;
5292c593315Sopenharmony_ci}
5302c593315Sopenharmony_ci
5312c593315Sopenharmony_ciint Client::do_read() { return readfn(*this); }
5322c593315Sopenharmony_ciint Client::do_write() { return writefn(*this); }
5332c593315Sopenharmony_ci
5342c593315Sopenharmony_ciint Client::make_socket(addrinfo *addr) {
5352c593315Sopenharmony_ci  int rv;
5362c593315Sopenharmony_ci
5372c593315Sopenharmony_ci  if (config.is_quic()) {
5382c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
5392c593315Sopenharmony_ci    fd = util::create_nonblock_udp_socket(addr->ai_family);
5402c593315Sopenharmony_ci    if (fd == -1) {
5412c593315Sopenharmony_ci      return -1;
5422c593315Sopenharmony_ci    }
5432c593315Sopenharmony_ci
5442c593315Sopenharmony_ci#  ifdef UDP_GRO
5452c593315Sopenharmony_ci    int val = 1;
5462c593315Sopenharmony_ci    if (setsockopt(fd, IPPROTO_UDP, UDP_GRO, &val, sizeof(val)) != 0) {
5472c593315Sopenharmony_ci      std::cerr << "setsockopt UDP_GRO failed" << std::endl;
5482c593315Sopenharmony_ci      return -1;
5492c593315Sopenharmony_ci    }
5502c593315Sopenharmony_ci#  endif // UDP_GRO
5512c593315Sopenharmony_ci
5522c593315Sopenharmony_ci    rv = util::bind_any_addr_udp(fd, addr->ai_family);
5532c593315Sopenharmony_ci    if (rv != 0) {
5542c593315Sopenharmony_ci      close(fd);
5552c593315Sopenharmony_ci      fd = -1;
5562c593315Sopenharmony_ci      return -1;
5572c593315Sopenharmony_ci    }
5582c593315Sopenharmony_ci
5592c593315Sopenharmony_ci    socklen_t addrlen = sizeof(local_addr.su.storage);
5602c593315Sopenharmony_ci    rv = getsockname(fd, &local_addr.su.sa, &addrlen);
5612c593315Sopenharmony_ci    if (rv == -1) {
5622c593315Sopenharmony_ci      return -1;
5632c593315Sopenharmony_ci    }
5642c593315Sopenharmony_ci    local_addr.len = addrlen;
5652c593315Sopenharmony_ci
5662c593315Sopenharmony_ci    if (quic_init(&local_addr.su.sa, local_addr.len, addr->ai_addr,
5672c593315Sopenharmony_ci                  addr->ai_addrlen) != 0) {
5682c593315Sopenharmony_ci      std::cerr << "quic_init failed" << std::endl;
5692c593315Sopenharmony_ci      return -1;
5702c593315Sopenharmony_ci    }
5712c593315Sopenharmony_ci#endif // ENABLE_HTTP3
5722c593315Sopenharmony_ci  } else {
5732c593315Sopenharmony_ci    fd = util::create_nonblock_socket(addr->ai_family);
5742c593315Sopenharmony_ci    if (fd == -1) {
5752c593315Sopenharmony_ci      return -1;
5762c593315Sopenharmony_ci    }
5772c593315Sopenharmony_ci    if (config.scheme == "https") {
5782c593315Sopenharmony_ci      if (!ssl) {
5792c593315Sopenharmony_ci        ssl = SSL_new(worker->ssl_ctx);
5802c593315Sopenharmony_ci      }
5812c593315Sopenharmony_ci
5822c593315Sopenharmony_ci      SSL_set_connect_state(ssl);
5832c593315Sopenharmony_ci    }
5842c593315Sopenharmony_ci  }
5852c593315Sopenharmony_ci
5862c593315Sopenharmony_ci  if (ssl && !util::numeric_host(config.host.c_str())) {
5872c593315Sopenharmony_ci    SSL_set_tlsext_host_name(ssl, config.host.c_str());
5882c593315Sopenharmony_ci  }
5892c593315Sopenharmony_ci
5902c593315Sopenharmony_ci  if (config.is_quic()) {
5912c593315Sopenharmony_ci    return 0;
5922c593315Sopenharmony_ci  }
5932c593315Sopenharmony_ci
5942c593315Sopenharmony_ci  rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
5952c593315Sopenharmony_ci  if (rv != 0 && errno != EINPROGRESS) {
5962c593315Sopenharmony_ci    if (ssl) {
5972c593315Sopenharmony_ci      SSL_free(ssl);
5982c593315Sopenharmony_ci      ssl = nullptr;
5992c593315Sopenharmony_ci    }
6002c593315Sopenharmony_ci    close(fd);
6012c593315Sopenharmony_ci    fd = -1;
6022c593315Sopenharmony_ci    return -1;
6032c593315Sopenharmony_ci  }
6042c593315Sopenharmony_ci  return 0;
6052c593315Sopenharmony_ci}
6062c593315Sopenharmony_ci
6072c593315Sopenharmony_ciint Client::connect() {
6082c593315Sopenharmony_ci  int rv;
6092c593315Sopenharmony_ci
6102c593315Sopenharmony_ci  if (!worker->config->is_timing_based_mode() ||
6112c593315Sopenharmony_ci      worker->current_phase == Phase::MAIN_DURATION) {
6122c593315Sopenharmony_ci    record_client_start_time();
6132c593315Sopenharmony_ci    clear_connect_times();
6142c593315Sopenharmony_ci    record_connect_start_time();
6152c593315Sopenharmony_ci  } else if (worker->current_phase == Phase::INITIAL_IDLE) {
6162c593315Sopenharmony_ci    worker->current_phase = Phase::WARM_UP;
6172c593315Sopenharmony_ci    std::cout << "Warm-up started for thread #" << worker->id << "."
6182c593315Sopenharmony_ci              << std::endl;
6192c593315Sopenharmony_ci    ev_timer_start(worker->loop, &worker->warmup_watcher);
6202c593315Sopenharmony_ci  }
6212c593315Sopenharmony_ci
6222c593315Sopenharmony_ci  if (worker->config->conn_inactivity_timeout > 0.) {
6232c593315Sopenharmony_ci    ev_timer_again(worker->loop, &conn_inactivity_watcher);
6242c593315Sopenharmony_ci  }
6252c593315Sopenharmony_ci
6262c593315Sopenharmony_ci  if (current_addr) {
6272c593315Sopenharmony_ci    rv = make_socket(current_addr);
6282c593315Sopenharmony_ci    if (rv == -1) {
6292c593315Sopenharmony_ci      return -1;
6302c593315Sopenharmony_ci    }
6312c593315Sopenharmony_ci  } else {
6322c593315Sopenharmony_ci    addrinfo *addr = nullptr;
6332c593315Sopenharmony_ci    while (next_addr) {
6342c593315Sopenharmony_ci      addr = next_addr;
6352c593315Sopenharmony_ci      next_addr = next_addr->ai_next;
6362c593315Sopenharmony_ci      rv = make_socket(addr);
6372c593315Sopenharmony_ci      if (rv == 0) {
6382c593315Sopenharmony_ci        break;
6392c593315Sopenharmony_ci      }
6402c593315Sopenharmony_ci    }
6412c593315Sopenharmony_ci
6422c593315Sopenharmony_ci    if (fd == -1) {
6432c593315Sopenharmony_ci      return -1;
6442c593315Sopenharmony_ci    }
6452c593315Sopenharmony_ci
6462c593315Sopenharmony_ci    assert(addr);
6472c593315Sopenharmony_ci
6482c593315Sopenharmony_ci    current_addr = addr;
6492c593315Sopenharmony_ci  }
6502c593315Sopenharmony_ci
6512c593315Sopenharmony_ci  ev_io_set(&rev, fd, EV_READ);
6522c593315Sopenharmony_ci  ev_io_set(&wev, fd, EV_WRITE);
6532c593315Sopenharmony_ci
6542c593315Sopenharmony_ci  ev_io_start(worker->loop, &wev);
6552c593315Sopenharmony_ci
6562c593315Sopenharmony_ci  if (config.is_quic()) {
6572c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
6582c593315Sopenharmony_ci    ev_io_start(worker->loop, &rev);
6592c593315Sopenharmony_ci
6602c593315Sopenharmony_ci    readfn = &Client::read_quic;
6612c593315Sopenharmony_ci    writefn = &Client::write_quic;
6622c593315Sopenharmony_ci#endif // ENABLE_HTTP3
6632c593315Sopenharmony_ci  } else {
6642c593315Sopenharmony_ci    writefn = &Client::connected;
6652c593315Sopenharmony_ci  }
6662c593315Sopenharmony_ci
6672c593315Sopenharmony_ci  return 0;
6682c593315Sopenharmony_ci}
6692c593315Sopenharmony_ci
6702c593315Sopenharmony_civoid Client::timeout() {
6712c593315Sopenharmony_ci  process_timedout_streams();
6722c593315Sopenharmony_ci
6732c593315Sopenharmony_ci  disconnect();
6742c593315Sopenharmony_ci}
6752c593315Sopenharmony_ci
6762c593315Sopenharmony_civoid Client::restart_timeout() {
6772c593315Sopenharmony_ci  if (worker->config->conn_inactivity_timeout > 0.) {
6782c593315Sopenharmony_ci    ev_timer_again(worker->loop, &conn_inactivity_watcher);
6792c593315Sopenharmony_ci  }
6802c593315Sopenharmony_ci}
6812c593315Sopenharmony_ci
6822c593315Sopenharmony_ciint Client::try_again_or_fail() {
6832c593315Sopenharmony_ci  disconnect();
6842c593315Sopenharmony_ci
6852c593315Sopenharmony_ci  if (new_connection_requested) {
6862c593315Sopenharmony_ci    new_connection_requested = false;
6872c593315Sopenharmony_ci
6882c593315Sopenharmony_ci    if (req_left) {
6892c593315Sopenharmony_ci
6902c593315Sopenharmony_ci      if (worker->current_phase == Phase::MAIN_DURATION) {
6912c593315Sopenharmony_ci        // At the moment, we don't have a facility to re-start request
6922c593315Sopenharmony_ci        // already in in-flight.  Make them fail.
6932c593315Sopenharmony_ci        worker->stats.req_failed += req_inflight;
6942c593315Sopenharmony_ci        worker->stats.req_error += req_inflight;
6952c593315Sopenharmony_ci
6962c593315Sopenharmony_ci        req_inflight = 0;
6972c593315Sopenharmony_ci      }
6982c593315Sopenharmony_ci
6992c593315Sopenharmony_ci      // Keep using current address
7002c593315Sopenharmony_ci      if (connect() == 0) {
7012c593315Sopenharmony_ci        return 0;
7022c593315Sopenharmony_ci      }
7032c593315Sopenharmony_ci      std::cerr << "client could not connect to host" << std::endl;
7042c593315Sopenharmony_ci    }
7052c593315Sopenharmony_ci  }
7062c593315Sopenharmony_ci
7072c593315Sopenharmony_ci  process_abandoned_streams();
7082c593315Sopenharmony_ci
7092c593315Sopenharmony_ci  return -1;
7102c593315Sopenharmony_ci}
7112c593315Sopenharmony_ci
7122c593315Sopenharmony_civoid Client::fail() {
7132c593315Sopenharmony_ci  disconnect();
7142c593315Sopenharmony_ci
7152c593315Sopenharmony_ci  process_abandoned_streams();
7162c593315Sopenharmony_ci}
7172c593315Sopenharmony_ci
7182c593315Sopenharmony_civoid Client::disconnect() {
7192c593315Sopenharmony_ci  record_client_end_time();
7202c593315Sopenharmony_ci
7212c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
7222c593315Sopenharmony_ci  if (config.is_quic()) {
7232c593315Sopenharmony_ci    quic_close_connection();
7242c593315Sopenharmony_ci  }
7252c593315Sopenharmony_ci#endif // ENABLE_HTTP3
7262c593315Sopenharmony_ci
7272c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
7282c593315Sopenharmony_ci  ev_timer_stop(worker->loop, &quic.pkt_timer);
7292c593315Sopenharmony_ci#endif // ENABLE_HTTP3
7302c593315Sopenharmony_ci  ev_timer_stop(worker->loop, &conn_inactivity_watcher);
7312c593315Sopenharmony_ci  ev_timer_stop(worker->loop, &conn_active_watcher);
7322c593315Sopenharmony_ci  ev_timer_stop(worker->loop, &rps_watcher);
7332c593315Sopenharmony_ci  ev_timer_stop(worker->loop, &request_timeout_watcher);
7342c593315Sopenharmony_ci  streams.clear();
7352c593315Sopenharmony_ci  session.reset();
7362c593315Sopenharmony_ci  wb.reset();
7372c593315Sopenharmony_ci  state = CLIENT_IDLE;
7382c593315Sopenharmony_ci  ev_io_stop(worker->loop, &wev);
7392c593315Sopenharmony_ci  ev_io_stop(worker->loop, &rev);
7402c593315Sopenharmony_ci  if (ssl) {
7412c593315Sopenharmony_ci    if (config.is_quic()) {
7422c593315Sopenharmony_ci      SSL_free(ssl);
7432c593315Sopenharmony_ci      ssl = nullptr;
7442c593315Sopenharmony_ci    } else {
7452c593315Sopenharmony_ci      SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
7462c593315Sopenharmony_ci      ERR_clear_error();
7472c593315Sopenharmony_ci
7482c593315Sopenharmony_ci      if (SSL_shutdown(ssl) != 1) {
7492c593315Sopenharmony_ci        SSL_free(ssl);
7502c593315Sopenharmony_ci        ssl = nullptr;
7512c593315Sopenharmony_ci      }
7522c593315Sopenharmony_ci    }
7532c593315Sopenharmony_ci  }
7542c593315Sopenharmony_ci  if (fd != -1) {
7552c593315Sopenharmony_ci    shutdown(fd, SHUT_WR);
7562c593315Sopenharmony_ci    close(fd);
7572c593315Sopenharmony_ci    fd = -1;
7582c593315Sopenharmony_ci  }
7592c593315Sopenharmony_ci
7602c593315Sopenharmony_ci  final = false;
7612c593315Sopenharmony_ci}
7622c593315Sopenharmony_ci
7632c593315Sopenharmony_ciint Client::submit_request() {
7642c593315Sopenharmony_ci  if (session->submit_request() != 0) {
7652c593315Sopenharmony_ci    return -1;
7662c593315Sopenharmony_ci  }
7672c593315Sopenharmony_ci
7682c593315Sopenharmony_ci  if (worker->current_phase != Phase::MAIN_DURATION) {
7692c593315Sopenharmony_ci    return 0;
7702c593315Sopenharmony_ci  }
7712c593315Sopenharmony_ci
7722c593315Sopenharmony_ci  ++worker->stats.req_started;
7732c593315Sopenharmony_ci  ++req_started;
7742c593315Sopenharmony_ci  ++req_inflight;
7752c593315Sopenharmony_ci  if (!worker->config->is_timing_based_mode()) {
7762c593315Sopenharmony_ci    --req_left;
7772c593315Sopenharmony_ci  }
7782c593315Sopenharmony_ci  // if an active timeout is set and this is the last request to be submitted
7792c593315Sopenharmony_ci  // on this connection, start the active timeout.
7802c593315Sopenharmony_ci  if (worker->config->conn_active_timeout > 0. && req_left == 0) {
7812c593315Sopenharmony_ci    ev_timer_start(worker->loop, &conn_active_watcher);
7822c593315Sopenharmony_ci  }
7832c593315Sopenharmony_ci
7842c593315Sopenharmony_ci  return 0;
7852c593315Sopenharmony_ci}
7862c593315Sopenharmony_ci
7872c593315Sopenharmony_civoid Client::process_timedout_streams() {
7882c593315Sopenharmony_ci  if (worker->current_phase != Phase::MAIN_DURATION) {
7892c593315Sopenharmony_ci    return;
7902c593315Sopenharmony_ci  }
7912c593315Sopenharmony_ci
7922c593315Sopenharmony_ci  for (auto &p : streams) {
7932c593315Sopenharmony_ci    auto &req_stat = p.second.req_stat;
7942c593315Sopenharmony_ci    if (!req_stat.completed) {
7952c593315Sopenharmony_ci      req_stat.stream_close_time = std::chrono::steady_clock::now();
7962c593315Sopenharmony_ci    }
7972c593315Sopenharmony_ci  }
7982c593315Sopenharmony_ci
7992c593315Sopenharmony_ci  worker->stats.req_timedout += req_inflight;
8002c593315Sopenharmony_ci
8012c593315Sopenharmony_ci  process_abandoned_streams();
8022c593315Sopenharmony_ci}
8032c593315Sopenharmony_ci
8042c593315Sopenharmony_civoid Client::process_abandoned_streams() {
8052c593315Sopenharmony_ci  if (worker->current_phase != Phase::MAIN_DURATION) {
8062c593315Sopenharmony_ci    return;
8072c593315Sopenharmony_ci  }
8082c593315Sopenharmony_ci
8092c593315Sopenharmony_ci  auto req_abandoned = req_inflight + req_left;
8102c593315Sopenharmony_ci
8112c593315Sopenharmony_ci  worker->stats.req_failed += req_abandoned;
8122c593315Sopenharmony_ci  worker->stats.req_error += req_abandoned;
8132c593315Sopenharmony_ci
8142c593315Sopenharmony_ci  req_inflight = 0;
8152c593315Sopenharmony_ci  req_left = 0;
8162c593315Sopenharmony_ci}
8172c593315Sopenharmony_ci
8182c593315Sopenharmony_civoid Client::process_request_failure() {
8192c593315Sopenharmony_ci  if (worker->current_phase != Phase::MAIN_DURATION) {
8202c593315Sopenharmony_ci    return;
8212c593315Sopenharmony_ci  }
8222c593315Sopenharmony_ci
8232c593315Sopenharmony_ci  worker->stats.req_failed += req_left;
8242c593315Sopenharmony_ci  worker->stats.req_error += req_left;
8252c593315Sopenharmony_ci
8262c593315Sopenharmony_ci  req_left = 0;
8272c593315Sopenharmony_ci
8282c593315Sopenharmony_ci  if (req_inflight == 0) {
8292c593315Sopenharmony_ci    terminate_session();
8302c593315Sopenharmony_ci  }
8312c593315Sopenharmony_ci  std::cout << "Process Request Failure:" << worker->stats.req_failed
8322c593315Sopenharmony_ci            << std::endl;
8332c593315Sopenharmony_ci}
8342c593315Sopenharmony_ci
8352c593315Sopenharmony_cinamespace {
8362c593315Sopenharmony_civoid print_server_tmp_key(SSL *ssl) {
8372c593315Sopenharmony_ci// libressl does not have SSL_get_server_tmp_key
8382c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x10002000L && defined(SSL_get_server_tmp_key)
8392c593315Sopenharmony_ci  EVP_PKEY *key;
8402c593315Sopenharmony_ci
8412c593315Sopenharmony_ci  if (!SSL_get_server_tmp_key(ssl, &key)) {
8422c593315Sopenharmony_ci    return;
8432c593315Sopenharmony_ci  }
8442c593315Sopenharmony_ci
8452c593315Sopenharmony_ci  auto key_del = defer(EVP_PKEY_free, key);
8462c593315Sopenharmony_ci
8472c593315Sopenharmony_ci  std::cout << "Server Temp Key: ";
8482c593315Sopenharmony_ci
8492c593315Sopenharmony_ci  auto pkey_id = EVP_PKEY_id(key);
8502c593315Sopenharmony_ci  switch (pkey_id) {
8512c593315Sopenharmony_ci  case EVP_PKEY_RSA:
8522c593315Sopenharmony_ci    std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl;
8532c593315Sopenharmony_ci    break;
8542c593315Sopenharmony_ci  case EVP_PKEY_DH:
8552c593315Sopenharmony_ci    std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl;
8562c593315Sopenharmony_ci    break;
8572c593315Sopenharmony_ci  case EVP_PKEY_EC: {
8582c593315Sopenharmony_ci#  if OPENSSL_3_0_0_API
8592c593315Sopenharmony_ci    std::array<char, 64> curve_name;
8602c593315Sopenharmony_ci    const char *cname;
8612c593315Sopenharmony_ci    if (!EVP_PKEY_get_utf8_string_param(key, "group", curve_name.data(),
8622c593315Sopenharmony_ci                                        curve_name.size(), nullptr)) {
8632c593315Sopenharmony_ci      cname = "<unknown>";
8642c593315Sopenharmony_ci    } else {
8652c593315Sopenharmony_ci      cname = curve_name.data();
8662c593315Sopenharmony_ci    }
8672c593315Sopenharmony_ci#  else  // !OPENSSL_3_0_0_API
8682c593315Sopenharmony_ci    auto ec = EVP_PKEY_get1_EC_KEY(key);
8692c593315Sopenharmony_ci    auto ec_del = defer(EC_KEY_free, ec);
8702c593315Sopenharmony_ci    auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
8712c593315Sopenharmony_ci    auto cname = EC_curve_nid2nist(nid);
8722c593315Sopenharmony_ci    if (!cname) {
8732c593315Sopenharmony_ci      cname = OBJ_nid2sn(nid);
8742c593315Sopenharmony_ci    }
8752c593315Sopenharmony_ci#  endif // !OPENSSL_3_0_0_API
8762c593315Sopenharmony_ci
8772c593315Sopenharmony_ci    std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits"
8782c593315Sopenharmony_ci              << std::endl;
8792c593315Sopenharmony_ci    break;
8802c593315Sopenharmony_ci  }
8812c593315Sopenharmony_ci  default:
8822c593315Sopenharmony_ci    std::cout << OBJ_nid2sn(pkey_id) << " " << EVP_PKEY_bits(key) << " bits"
8832c593315Sopenharmony_ci              << std::endl;
8842c593315Sopenharmony_ci    break;
8852c593315Sopenharmony_ci  }
8862c593315Sopenharmony_ci#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
8872c593315Sopenharmony_ci}
8882c593315Sopenharmony_ci} // namespace
8892c593315Sopenharmony_ci
8902c593315Sopenharmony_civoid Client::report_tls_info() {
8912c593315Sopenharmony_ci  if (worker->id == 0 && !worker->tls_info_report_done) {
8922c593315Sopenharmony_ci    worker->tls_info_report_done = true;
8932c593315Sopenharmony_ci    auto cipher = SSL_get_current_cipher(ssl);
8942c593315Sopenharmony_ci    std::cout << "TLS Protocol: " << tls::get_tls_protocol(ssl) << "\n"
8952c593315Sopenharmony_ci              << "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl;
8962c593315Sopenharmony_ci    print_server_tmp_key(ssl);
8972c593315Sopenharmony_ci  }
8982c593315Sopenharmony_ci}
8992c593315Sopenharmony_ci
9002c593315Sopenharmony_civoid Client::report_app_info() {
9012c593315Sopenharmony_ci  if (worker->id == 0 && !worker->app_info_report_done) {
9022c593315Sopenharmony_ci    worker->app_info_report_done = true;
9032c593315Sopenharmony_ci    std::cout << "Application protocol: " << selected_proto << std::endl;
9042c593315Sopenharmony_ci  }
9052c593315Sopenharmony_ci}
9062c593315Sopenharmony_ci
9072c593315Sopenharmony_civoid Client::terminate_session() {
9082c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
9092c593315Sopenharmony_ci  if (config.is_quic()) {
9102c593315Sopenharmony_ci    quic.close_requested = true;
9112c593315Sopenharmony_ci  }
9122c593315Sopenharmony_ci#endif // ENABLE_HTTP3
9132c593315Sopenharmony_ci  if (session) {
9142c593315Sopenharmony_ci    session->terminate();
9152c593315Sopenharmony_ci  }
9162c593315Sopenharmony_ci  // http1 session needs writecb to tear down session.
9172c593315Sopenharmony_ci  signal_write();
9182c593315Sopenharmony_ci}
9192c593315Sopenharmony_ci
9202c593315Sopenharmony_civoid Client::on_request(int32_t stream_id) { streams[stream_id] = Stream(); }
9212c593315Sopenharmony_ci
9222c593315Sopenharmony_civoid Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
9232c593315Sopenharmony_ci                       const uint8_t *value, size_t valuelen) {
9242c593315Sopenharmony_ci  auto itr = streams.find(stream_id);
9252c593315Sopenharmony_ci  if (itr == std::end(streams)) {
9262c593315Sopenharmony_ci    return;
9272c593315Sopenharmony_ci  }
9282c593315Sopenharmony_ci  auto &stream = (*itr).second;
9292c593315Sopenharmony_ci
9302c593315Sopenharmony_ci  if (worker->current_phase != Phase::MAIN_DURATION) {
9312c593315Sopenharmony_ci    // If the stream is for warm-up phase, then mark as a success
9322c593315Sopenharmony_ci    // But we do not update the count for 2xx, 3xx, etc status codes
9332c593315Sopenharmony_ci    // Same has been done in on_status_code function
9342c593315Sopenharmony_ci    stream.status_success = 1;
9352c593315Sopenharmony_ci    return;
9362c593315Sopenharmony_ci  }
9372c593315Sopenharmony_ci
9382c593315Sopenharmony_ci  if (stream.status_success == -1 && namelen == 7 &&
9392c593315Sopenharmony_ci      util::streq_l(":status", name, namelen)) {
9402c593315Sopenharmony_ci    int status = 0;
9412c593315Sopenharmony_ci    for (size_t i = 0; i < valuelen; ++i) {
9422c593315Sopenharmony_ci      if ('0' <= value[i] && value[i] <= '9') {
9432c593315Sopenharmony_ci        status *= 10;
9442c593315Sopenharmony_ci        status += value[i] - '0';
9452c593315Sopenharmony_ci        if (status > 999) {
9462c593315Sopenharmony_ci          stream.status_success = 0;
9472c593315Sopenharmony_ci          return;
9482c593315Sopenharmony_ci        }
9492c593315Sopenharmony_ci      } else {
9502c593315Sopenharmony_ci        break;
9512c593315Sopenharmony_ci      }
9522c593315Sopenharmony_ci    }
9532c593315Sopenharmony_ci
9542c593315Sopenharmony_ci    stream.req_stat.status = status;
9552c593315Sopenharmony_ci    if (status >= 200 && status < 300) {
9562c593315Sopenharmony_ci      ++worker->stats.status[2];
9572c593315Sopenharmony_ci      stream.status_success = 1;
9582c593315Sopenharmony_ci    } else if (status < 400) {
9592c593315Sopenharmony_ci      ++worker->stats.status[3];
9602c593315Sopenharmony_ci      stream.status_success = 1;
9612c593315Sopenharmony_ci    } else if (status < 600) {
9622c593315Sopenharmony_ci      ++worker->stats.status[status / 100];
9632c593315Sopenharmony_ci      stream.status_success = 0;
9642c593315Sopenharmony_ci    } else {
9652c593315Sopenharmony_ci      stream.status_success = 0;
9662c593315Sopenharmony_ci    }
9672c593315Sopenharmony_ci  }
9682c593315Sopenharmony_ci}
9692c593315Sopenharmony_ci
9702c593315Sopenharmony_civoid Client::on_status_code(int32_t stream_id, uint16_t status) {
9712c593315Sopenharmony_ci  auto itr = streams.find(stream_id);
9722c593315Sopenharmony_ci  if (itr == std::end(streams)) {
9732c593315Sopenharmony_ci    return;
9742c593315Sopenharmony_ci  }
9752c593315Sopenharmony_ci  auto &stream = (*itr).second;
9762c593315Sopenharmony_ci
9772c593315Sopenharmony_ci  if (worker->current_phase != Phase::MAIN_DURATION) {
9782c593315Sopenharmony_ci    stream.status_success = 1;
9792c593315Sopenharmony_ci    return;
9802c593315Sopenharmony_ci  }
9812c593315Sopenharmony_ci
9822c593315Sopenharmony_ci  stream.req_stat.status = status;
9832c593315Sopenharmony_ci  if (status >= 200 && status < 300) {
9842c593315Sopenharmony_ci    ++worker->stats.status[2];
9852c593315Sopenharmony_ci    stream.status_success = 1;
9862c593315Sopenharmony_ci  } else if (status < 400) {
9872c593315Sopenharmony_ci    ++worker->stats.status[3];
9882c593315Sopenharmony_ci    stream.status_success = 1;
9892c593315Sopenharmony_ci  } else if (status < 600) {
9902c593315Sopenharmony_ci    ++worker->stats.status[status / 100];
9912c593315Sopenharmony_ci    stream.status_success = 0;
9922c593315Sopenharmony_ci  } else {
9932c593315Sopenharmony_ci    stream.status_success = 0;
9942c593315Sopenharmony_ci  }
9952c593315Sopenharmony_ci}
9962c593315Sopenharmony_ci
9972c593315Sopenharmony_civoid Client::on_stream_close(int32_t stream_id, bool success, bool final) {
9982c593315Sopenharmony_ci  if (worker->current_phase == Phase::MAIN_DURATION) {
9992c593315Sopenharmony_ci    if (req_inflight > 0) {
10002c593315Sopenharmony_ci      --req_inflight;
10012c593315Sopenharmony_ci    }
10022c593315Sopenharmony_ci    auto req_stat = get_req_stat(stream_id);
10032c593315Sopenharmony_ci    if (!req_stat) {
10042c593315Sopenharmony_ci      return;
10052c593315Sopenharmony_ci    }
10062c593315Sopenharmony_ci
10072c593315Sopenharmony_ci    req_stat->stream_close_time = std::chrono::steady_clock::now();
10082c593315Sopenharmony_ci    if (success) {
10092c593315Sopenharmony_ci      req_stat->completed = true;
10102c593315Sopenharmony_ci      ++worker->stats.req_success;
10112c593315Sopenharmony_ci      ++cstat.req_success;
10122c593315Sopenharmony_ci
10132c593315Sopenharmony_ci      if (streams[stream_id].status_success == 1) {
10142c593315Sopenharmony_ci        ++worker->stats.req_status_success;
10152c593315Sopenharmony_ci      } else {
10162c593315Sopenharmony_ci        ++worker->stats.req_failed;
10172c593315Sopenharmony_ci      }
10182c593315Sopenharmony_ci
10192c593315Sopenharmony_ci      worker->sample_req_stat(req_stat);
10202c593315Sopenharmony_ci
10212c593315Sopenharmony_ci      // Count up in successful cases only
10222c593315Sopenharmony_ci      ++worker->request_times_smp.n;
10232c593315Sopenharmony_ci    } else {
10242c593315Sopenharmony_ci      ++worker->stats.req_failed;
10252c593315Sopenharmony_ci      ++worker->stats.req_error;
10262c593315Sopenharmony_ci    }
10272c593315Sopenharmony_ci    ++worker->stats.req_done;
10282c593315Sopenharmony_ci    ++req_done;
10292c593315Sopenharmony_ci
10302c593315Sopenharmony_ci    if (worker->config->log_fd != -1) {
10312c593315Sopenharmony_ci      auto start = std::chrono::duration_cast<std::chrono::microseconds>(
10322c593315Sopenharmony_ci          req_stat->request_wall_time.time_since_epoch());
10332c593315Sopenharmony_ci      auto delta = std::chrono::duration_cast<std::chrono::microseconds>(
10342c593315Sopenharmony_ci          req_stat->stream_close_time - req_stat->request_time);
10352c593315Sopenharmony_ci
10362c593315Sopenharmony_ci      std::array<uint8_t, 256> buf;
10372c593315Sopenharmony_ci      auto p = std::begin(buf);
10382c593315Sopenharmony_ci      p = util::utos(p, start.count());
10392c593315Sopenharmony_ci      *p++ = '\t';
10402c593315Sopenharmony_ci      if (success) {
10412c593315Sopenharmony_ci        p = util::utos(p, req_stat->status);
10422c593315Sopenharmony_ci      } else {
10432c593315Sopenharmony_ci        *p++ = '-';
10442c593315Sopenharmony_ci        *p++ = '1';
10452c593315Sopenharmony_ci      }
10462c593315Sopenharmony_ci      *p++ = '\t';
10472c593315Sopenharmony_ci      p = util::utos(p, delta.count());
10482c593315Sopenharmony_ci      *p++ = '\n';
10492c593315Sopenharmony_ci
10502c593315Sopenharmony_ci      auto nwrite = static_cast<size_t>(std::distance(std::begin(buf), p));
10512c593315Sopenharmony_ci      assert(nwrite <= buf.size());
10522c593315Sopenharmony_ci      while (write(worker->config->log_fd, buf.data(), nwrite) == -1 &&
10532c593315Sopenharmony_ci             errno == EINTR)
10542c593315Sopenharmony_ci        ;
10552c593315Sopenharmony_ci    }
10562c593315Sopenharmony_ci  }
10572c593315Sopenharmony_ci
10582c593315Sopenharmony_ci  worker->report_progress();
10592c593315Sopenharmony_ci  streams.erase(stream_id);
10602c593315Sopenharmony_ci  if (req_left == 0 && req_inflight == 0) {
10612c593315Sopenharmony_ci    terminate_session();
10622c593315Sopenharmony_ci    return;
10632c593315Sopenharmony_ci  }
10642c593315Sopenharmony_ci
10652c593315Sopenharmony_ci  if (!final && req_left > 0) {
10662c593315Sopenharmony_ci    if (config.timing_script) {
10672c593315Sopenharmony_ci      if (!ev_is_active(&request_timeout_watcher)) {
10682c593315Sopenharmony_ci        ev_feed_event(worker->loop, &request_timeout_watcher, EV_TIMER);
10692c593315Sopenharmony_ci      }
10702c593315Sopenharmony_ci    } else if (!config.rps_enabled()) {
10712c593315Sopenharmony_ci      if (submit_request() != 0) {
10722c593315Sopenharmony_ci        process_request_failure();
10732c593315Sopenharmony_ci      }
10742c593315Sopenharmony_ci    } else if (rps_req_pending) {
10752c593315Sopenharmony_ci      --rps_req_pending;
10762c593315Sopenharmony_ci      if (submit_request() != 0) {
10772c593315Sopenharmony_ci        process_request_failure();
10782c593315Sopenharmony_ci      }
10792c593315Sopenharmony_ci    } else {
10802c593315Sopenharmony_ci      assert(rps_req_inflight);
10812c593315Sopenharmony_ci      --rps_req_inflight;
10822c593315Sopenharmony_ci    }
10832c593315Sopenharmony_ci  }
10842c593315Sopenharmony_ci}
10852c593315Sopenharmony_ci
10862c593315Sopenharmony_ciRequestStat *Client::get_req_stat(int32_t stream_id) {
10872c593315Sopenharmony_ci  auto it = streams.find(stream_id);
10882c593315Sopenharmony_ci  if (it == std::end(streams)) {
10892c593315Sopenharmony_ci    return nullptr;
10902c593315Sopenharmony_ci  }
10912c593315Sopenharmony_ci
10922c593315Sopenharmony_ci  return &(*it).second.req_stat;
10932c593315Sopenharmony_ci}
10942c593315Sopenharmony_ci
10952c593315Sopenharmony_ciint Client::connection_made() {
10962c593315Sopenharmony_ci  if (ssl) {
10972c593315Sopenharmony_ci    report_tls_info();
10982c593315Sopenharmony_ci
10992c593315Sopenharmony_ci    const unsigned char *next_proto = nullptr;
11002c593315Sopenharmony_ci    unsigned int next_proto_len;
11012c593315Sopenharmony_ci
11022c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG
11032c593315Sopenharmony_ci    SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
11042c593315Sopenharmony_ci#endif // !OPENSSL_NO_NEXTPROTONEG
11052c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x10002000L
11062c593315Sopenharmony_ci    if (next_proto == nullptr) {
11072c593315Sopenharmony_ci      SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
11082c593315Sopenharmony_ci    }
11092c593315Sopenharmony_ci#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
11102c593315Sopenharmony_ci
11112c593315Sopenharmony_ci    if (next_proto) {
11122c593315Sopenharmony_ci      auto proto = StringRef{next_proto, next_proto_len};
11132c593315Sopenharmony_ci      if (config.is_quic()) {
11142c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
11152c593315Sopenharmony_ci        assert(session);
11162c593315Sopenharmony_ci        if (!util::streq(StringRef{&NGHTTP3_ALPN_H3[1]}, proto) &&
11172c593315Sopenharmony_ci            !util::streq_l("h3-29", proto)) {
11182c593315Sopenharmony_ci          return -1;
11192c593315Sopenharmony_ci        }
11202c593315Sopenharmony_ci#endif // ENABLE_HTTP3
11212c593315Sopenharmony_ci      } else if (util::check_h2_is_selected(proto)) {
11222c593315Sopenharmony_ci        session = std::make_unique<Http2Session>(this);
11232c593315Sopenharmony_ci      } else if (util::streq(NGHTTP2_H1_1, proto)) {
11242c593315Sopenharmony_ci        session = std::make_unique<Http1Session>(this);
11252c593315Sopenharmony_ci      }
11262c593315Sopenharmony_ci
11272c593315Sopenharmony_ci      // Just assign next_proto to selected_proto anyway to show the
11282c593315Sopenharmony_ci      // negotiation result.
11292c593315Sopenharmony_ci      selected_proto = proto.str();
11302c593315Sopenharmony_ci    } else if (config.is_quic()) {
11312c593315Sopenharmony_ci      std::cerr << "QUIC requires ALPN negotiation" << std::endl;
11322c593315Sopenharmony_ci      return -1;
11332c593315Sopenharmony_ci    } else {
11342c593315Sopenharmony_ci      std::cout << "No protocol negotiated. Fallback behaviour may be activated"
11352c593315Sopenharmony_ci                << std::endl;
11362c593315Sopenharmony_ci
11372c593315Sopenharmony_ci      for (const auto &proto : config.npn_list) {
11382c593315Sopenharmony_ci        if (util::streq(NGHTTP2_H1_1_ALPN, StringRef{proto})) {
11392c593315Sopenharmony_ci          std::cout
11402c593315Sopenharmony_ci              << "Server does not support NPN/ALPN. Falling back to HTTP/1.1."
11412c593315Sopenharmony_ci              << std::endl;
11422c593315Sopenharmony_ci          session = std::make_unique<Http1Session>(this);
11432c593315Sopenharmony_ci          selected_proto = NGHTTP2_H1_1.str();
11442c593315Sopenharmony_ci          break;
11452c593315Sopenharmony_ci        }
11462c593315Sopenharmony_ci      }
11472c593315Sopenharmony_ci    }
11482c593315Sopenharmony_ci
11492c593315Sopenharmony_ci    if (!selected_proto.empty()) {
11502c593315Sopenharmony_ci      report_app_info();
11512c593315Sopenharmony_ci    }
11522c593315Sopenharmony_ci
11532c593315Sopenharmony_ci    if (!session) {
11542c593315Sopenharmony_ci      std::cout
11552c593315Sopenharmony_ci          << "No supported protocol was negotiated. Supported protocols were:"
11562c593315Sopenharmony_ci          << std::endl;
11572c593315Sopenharmony_ci      for (const auto &proto : config.npn_list) {
11582c593315Sopenharmony_ci        std::cout << proto.substr(1) << std::endl;
11592c593315Sopenharmony_ci      }
11602c593315Sopenharmony_ci      disconnect();
11612c593315Sopenharmony_ci      return -1;
11622c593315Sopenharmony_ci    }
11632c593315Sopenharmony_ci  } else {
11642c593315Sopenharmony_ci    switch (config.no_tls_proto) {
11652c593315Sopenharmony_ci    case Config::PROTO_HTTP2:
11662c593315Sopenharmony_ci      session = std::make_unique<Http2Session>(this);
11672c593315Sopenharmony_ci      selected_proto = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID;
11682c593315Sopenharmony_ci      break;
11692c593315Sopenharmony_ci    case Config::PROTO_HTTP1_1:
11702c593315Sopenharmony_ci      session = std::make_unique<Http1Session>(this);
11712c593315Sopenharmony_ci      selected_proto = NGHTTP2_H1_1.str();
11722c593315Sopenharmony_ci      break;
11732c593315Sopenharmony_ci    default:
11742c593315Sopenharmony_ci      // unreachable
11752c593315Sopenharmony_ci      assert(0);
11762c593315Sopenharmony_ci    }
11772c593315Sopenharmony_ci
11782c593315Sopenharmony_ci    report_app_info();
11792c593315Sopenharmony_ci  }
11802c593315Sopenharmony_ci
11812c593315Sopenharmony_ci  state = CLIENT_CONNECTED;
11822c593315Sopenharmony_ci
11832c593315Sopenharmony_ci  session->on_connect();
11842c593315Sopenharmony_ci
11852c593315Sopenharmony_ci  record_connect_time();
11862c593315Sopenharmony_ci
11872c593315Sopenharmony_ci  if (config.rps_enabled()) {
11882c593315Sopenharmony_ci    rps_watcher.repeat = std::max(0.01, 1. / config.rps);
11892c593315Sopenharmony_ci    ev_timer_again(worker->loop, &rps_watcher);
11902c593315Sopenharmony_ci    rps_duration_started = std::chrono::steady_clock::now();
11912c593315Sopenharmony_ci  }
11922c593315Sopenharmony_ci
11932c593315Sopenharmony_ci  if (config.rps_enabled()) {
11942c593315Sopenharmony_ci    assert(req_left);
11952c593315Sopenharmony_ci
11962c593315Sopenharmony_ci    ++rps_req_inflight;
11972c593315Sopenharmony_ci
11982c593315Sopenharmony_ci    if (submit_request() != 0) {
11992c593315Sopenharmony_ci      process_request_failure();
12002c593315Sopenharmony_ci    }
12012c593315Sopenharmony_ci  } else if (!config.timing_script) {
12022c593315Sopenharmony_ci    auto nreq = config.is_timing_based_mode()
12032c593315Sopenharmony_ci                    ? std::max(req_left, session->max_concurrent_streams())
12042c593315Sopenharmony_ci                    : std::min(req_left, session->max_concurrent_streams());
12052c593315Sopenharmony_ci
12062c593315Sopenharmony_ci    for (; nreq > 0; --nreq) {
12072c593315Sopenharmony_ci      if (submit_request() != 0) {
12082c593315Sopenharmony_ci        process_request_failure();
12092c593315Sopenharmony_ci        break;
12102c593315Sopenharmony_ci      }
12112c593315Sopenharmony_ci    }
12122c593315Sopenharmony_ci  } else {
12132c593315Sopenharmony_ci
12142c593315Sopenharmony_ci    auto duration = config.timings[reqidx];
12152c593315Sopenharmony_ci
12162c593315Sopenharmony_ci    while (duration < std::chrono::duration<double>(1e-9)) {
12172c593315Sopenharmony_ci      if (submit_request() != 0) {
12182c593315Sopenharmony_ci        process_request_failure();
12192c593315Sopenharmony_ci        break;
12202c593315Sopenharmony_ci      }
12212c593315Sopenharmony_ci      duration = config.timings[reqidx];
12222c593315Sopenharmony_ci      if (reqidx == 0) {
12232c593315Sopenharmony_ci        // if reqidx wraps around back to 0, we uses up all lines and
12242c593315Sopenharmony_ci        // should break
12252c593315Sopenharmony_ci        break;
12262c593315Sopenharmony_ci      }
12272c593315Sopenharmony_ci    }
12282c593315Sopenharmony_ci
12292c593315Sopenharmony_ci    if (duration >= std::chrono::duration<double>(1e-9)) {
12302c593315Sopenharmony_ci      // double check since we may have break due to reqidx wraps
12312c593315Sopenharmony_ci      // around back to 0
12322c593315Sopenharmony_ci      request_timeout_watcher.repeat = util::ev_tstamp_from(duration);
12332c593315Sopenharmony_ci      ev_timer_again(worker->loop, &request_timeout_watcher);
12342c593315Sopenharmony_ci    }
12352c593315Sopenharmony_ci  }
12362c593315Sopenharmony_ci  signal_write();
12372c593315Sopenharmony_ci
12382c593315Sopenharmony_ci  return 0;
12392c593315Sopenharmony_ci}
12402c593315Sopenharmony_ci
12412c593315Sopenharmony_ciint Client::on_read(const uint8_t *data, size_t len) {
12422c593315Sopenharmony_ci  auto rv = session->on_read(data, len);
12432c593315Sopenharmony_ci  if (rv != 0) {
12442c593315Sopenharmony_ci    return -1;
12452c593315Sopenharmony_ci  }
12462c593315Sopenharmony_ci  if (worker->current_phase == Phase::MAIN_DURATION) {
12472c593315Sopenharmony_ci    worker->stats.bytes_total += len;
12482c593315Sopenharmony_ci  }
12492c593315Sopenharmony_ci  signal_write();
12502c593315Sopenharmony_ci  return 0;
12512c593315Sopenharmony_ci}
12522c593315Sopenharmony_ci
12532c593315Sopenharmony_ciint Client::on_write() {
12542c593315Sopenharmony_ci  if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) {
12552c593315Sopenharmony_ci    return 0;
12562c593315Sopenharmony_ci  }
12572c593315Sopenharmony_ci
12582c593315Sopenharmony_ci  if (session->on_write() != 0) {
12592c593315Sopenharmony_ci    return -1;
12602c593315Sopenharmony_ci  }
12612c593315Sopenharmony_ci  return 0;
12622c593315Sopenharmony_ci}
12632c593315Sopenharmony_ci
12642c593315Sopenharmony_ciint Client::read_clear() {
12652c593315Sopenharmony_ci  uint8_t buf[8_k];
12662c593315Sopenharmony_ci
12672c593315Sopenharmony_ci  for (;;) {
12682c593315Sopenharmony_ci    ssize_t nread;
12692c593315Sopenharmony_ci    while ((nread = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR)
12702c593315Sopenharmony_ci      ;
12712c593315Sopenharmony_ci    if (nread == -1) {
12722c593315Sopenharmony_ci      if (errno == EAGAIN || errno == EWOULDBLOCK) {
12732c593315Sopenharmony_ci        return 0;
12742c593315Sopenharmony_ci      }
12752c593315Sopenharmony_ci      return -1;
12762c593315Sopenharmony_ci    }
12772c593315Sopenharmony_ci
12782c593315Sopenharmony_ci    if (nread == 0) {
12792c593315Sopenharmony_ci      return -1;
12802c593315Sopenharmony_ci    }
12812c593315Sopenharmony_ci
12822c593315Sopenharmony_ci    if (on_read(buf, nread) != 0) {
12832c593315Sopenharmony_ci      return -1;
12842c593315Sopenharmony_ci    }
12852c593315Sopenharmony_ci  }
12862c593315Sopenharmony_ci
12872c593315Sopenharmony_ci  return 0;
12882c593315Sopenharmony_ci}
12892c593315Sopenharmony_ci
12902c593315Sopenharmony_ciint Client::write_clear() {
12912c593315Sopenharmony_ci  std::array<struct iovec, 2> iov;
12922c593315Sopenharmony_ci
12932c593315Sopenharmony_ci  for (;;) {
12942c593315Sopenharmony_ci    if (on_write() != 0) {
12952c593315Sopenharmony_ci      return -1;
12962c593315Sopenharmony_ci    }
12972c593315Sopenharmony_ci
12982c593315Sopenharmony_ci    auto iovcnt = wb.riovec(iov.data(), iov.size());
12992c593315Sopenharmony_ci
13002c593315Sopenharmony_ci    if (iovcnt == 0) {
13012c593315Sopenharmony_ci      break;
13022c593315Sopenharmony_ci    }
13032c593315Sopenharmony_ci
13042c593315Sopenharmony_ci    ssize_t nwrite;
13052c593315Sopenharmony_ci    while ((nwrite = writev(fd, iov.data(), iovcnt)) == -1 && errno == EINTR)
13062c593315Sopenharmony_ci      ;
13072c593315Sopenharmony_ci
13082c593315Sopenharmony_ci    if (nwrite == -1) {
13092c593315Sopenharmony_ci      if (errno == EAGAIN || errno == EWOULDBLOCK) {
13102c593315Sopenharmony_ci        ev_io_start(worker->loop, &wev);
13112c593315Sopenharmony_ci        return 0;
13122c593315Sopenharmony_ci      }
13132c593315Sopenharmony_ci      return -1;
13142c593315Sopenharmony_ci    }
13152c593315Sopenharmony_ci
13162c593315Sopenharmony_ci    wb.drain(nwrite);
13172c593315Sopenharmony_ci  }
13182c593315Sopenharmony_ci
13192c593315Sopenharmony_ci  ev_io_stop(worker->loop, &wev);
13202c593315Sopenharmony_ci
13212c593315Sopenharmony_ci  return 0;
13222c593315Sopenharmony_ci}
13232c593315Sopenharmony_ci
13242c593315Sopenharmony_ciint Client::connected() {
13252c593315Sopenharmony_ci  if (!util::check_socket_connected(fd)) {
13262c593315Sopenharmony_ci    return ERR_CONNECT_FAIL;
13272c593315Sopenharmony_ci  }
13282c593315Sopenharmony_ci  ev_io_start(worker->loop, &rev);
13292c593315Sopenharmony_ci  ev_io_stop(worker->loop, &wev);
13302c593315Sopenharmony_ci
13312c593315Sopenharmony_ci  if (ssl) {
13322c593315Sopenharmony_ci    SSL_set_fd(ssl, fd);
13332c593315Sopenharmony_ci
13342c593315Sopenharmony_ci    readfn = &Client::tls_handshake;
13352c593315Sopenharmony_ci    writefn = &Client::tls_handshake;
13362c593315Sopenharmony_ci
13372c593315Sopenharmony_ci    return do_write();
13382c593315Sopenharmony_ci  }
13392c593315Sopenharmony_ci
13402c593315Sopenharmony_ci  readfn = &Client::read_clear;
13412c593315Sopenharmony_ci  writefn = &Client::write_clear;
13422c593315Sopenharmony_ci
13432c593315Sopenharmony_ci  if (connection_made() != 0) {
13442c593315Sopenharmony_ci    return -1;
13452c593315Sopenharmony_ci  }
13462c593315Sopenharmony_ci
13472c593315Sopenharmony_ci  return 0;
13482c593315Sopenharmony_ci}
13492c593315Sopenharmony_ci
13502c593315Sopenharmony_ciint Client::tls_handshake() {
13512c593315Sopenharmony_ci  ERR_clear_error();
13522c593315Sopenharmony_ci
13532c593315Sopenharmony_ci  auto rv = SSL_do_handshake(ssl);
13542c593315Sopenharmony_ci
13552c593315Sopenharmony_ci  if (rv <= 0) {
13562c593315Sopenharmony_ci    auto err = SSL_get_error(ssl, rv);
13572c593315Sopenharmony_ci    switch (err) {
13582c593315Sopenharmony_ci    case SSL_ERROR_WANT_READ:
13592c593315Sopenharmony_ci      ev_io_stop(worker->loop, &wev);
13602c593315Sopenharmony_ci      return 0;
13612c593315Sopenharmony_ci    case SSL_ERROR_WANT_WRITE:
13622c593315Sopenharmony_ci      ev_io_start(worker->loop, &wev);
13632c593315Sopenharmony_ci      return 0;
13642c593315Sopenharmony_ci    default:
13652c593315Sopenharmony_ci      return -1;
13662c593315Sopenharmony_ci    }
13672c593315Sopenharmony_ci  }
13682c593315Sopenharmony_ci
13692c593315Sopenharmony_ci  ev_io_stop(worker->loop, &wev);
13702c593315Sopenharmony_ci
13712c593315Sopenharmony_ci  readfn = &Client::read_tls;
13722c593315Sopenharmony_ci  writefn = &Client::write_tls;
13732c593315Sopenharmony_ci
13742c593315Sopenharmony_ci  if (connection_made() != 0) {
13752c593315Sopenharmony_ci    return -1;
13762c593315Sopenharmony_ci  }
13772c593315Sopenharmony_ci
13782c593315Sopenharmony_ci  return 0;
13792c593315Sopenharmony_ci}
13802c593315Sopenharmony_ci
13812c593315Sopenharmony_ciint Client::read_tls() {
13822c593315Sopenharmony_ci  uint8_t buf[8_k];
13832c593315Sopenharmony_ci
13842c593315Sopenharmony_ci  ERR_clear_error();
13852c593315Sopenharmony_ci
13862c593315Sopenharmony_ci  for (;;) {
13872c593315Sopenharmony_ci    auto rv = SSL_read(ssl, buf, sizeof(buf));
13882c593315Sopenharmony_ci
13892c593315Sopenharmony_ci    if (rv <= 0) {
13902c593315Sopenharmony_ci      auto err = SSL_get_error(ssl, rv);
13912c593315Sopenharmony_ci      switch (err) {
13922c593315Sopenharmony_ci      case SSL_ERROR_WANT_READ:
13932c593315Sopenharmony_ci        return 0;
13942c593315Sopenharmony_ci      case SSL_ERROR_WANT_WRITE:
13952c593315Sopenharmony_ci        // renegotiation started
13962c593315Sopenharmony_ci        return -1;
13972c593315Sopenharmony_ci      default:
13982c593315Sopenharmony_ci        return -1;
13992c593315Sopenharmony_ci      }
14002c593315Sopenharmony_ci    }
14012c593315Sopenharmony_ci
14022c593315Sopenharmony_ci    if (on_read(buf, rv) != 0) {
14032c593315Sopenharmony_ci      return -1;
14042c593315Sopenharmony_ci    }
14052c593315Sopenharmony_ci  }
14062c593315Sopenharmony_ci}
14072c593315Sopenharmony_ci
14082c593315Sopenharmony_ciint Client::write_tls() {
14092c593315Sopenharmony_ci  ERR_clear_error();
14102c593315Sopenharmony_ci
14112c593315Sopenharmony_ci  struct iovec iov;
14122c593315Sopenharmony_ci
14132c593315Sopenharmony_ci  for (;;) {
14142c593315Sopenharmony_ci    if (on_write() != 0) {
14152c593315Sopenharmony_ci      return -1;
14162c593315Sopenharmony_ci    }
14172c593315Sopenharmony_ci
14182c593315Sopenharmony_ci    auto iovcnt = wb.riovec(&iov, 1);
14192c593315Sopenharmony_ci
14202c593315Sopenharmony_ci    if (iovcnt == 0) {
14212c593315Sopenharmony_ci      break;
14222c593315Sopenharmony_ci    }
14232c593315Sopenharmony_ci
14242c593315Sopenharmony_ci    auto rv = SSL_write(ssl, iov.iov_base, iov.iov_len);
14252c593315Sopenharmony_ci
14262c593315Sopenharmony_ci    if (rv <= 0) {
14272c593315Sopenharmony_ci      auto err = SSL_get_error(ssl, rv);
14282c593315Sopenharmony_ci      switch (err) {
14292c593315Sopenharmony_ci      case SSL_ERROR_WANT_READ:
14302c593315Sopenharmony_ci        // renegotiation started
14312c593315Sopenharmony_ci        return -1;
14322c593315Sopenharmony_ci      case SSL_ERROR_WANT_WRITE:
14332c593315Sopenharmony_ci        ev_io_start(worker->loop, &wev);
14342c593315Sopenharmony_ci        return 0;
14352c593315Sopenharmony_ci      default:
14362c593315Sopenharmony_ci        return -1;
14372c593315Sopenharmony_ci      }
14382c593315Sopenharmony_ci    }
14392c593315Sopenharmony_ci
14402c593315Sopenharmony_ci    wb.drain(rv);
14412c593315Sopenharmony_ci  }
14422c593315Sopenharmony_ci
14432c593315Sopenharmony_ci  ev_io_stop(worker->loop, &wev);
14442c593315Sopenharmony_ci
14452c593315Sopenharmony_ci  return 0;
14462c593315Sopenharmony_ci}
14472c593315Sopenharmony_ci
14482c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
14492c593315Sopenharmony_ci// Returns 1 if sendmsg is blocked.
14502c593315Sopenharmony_ciint Client::write_udp(const sockaddr *addr, socklen_t addrlen,
14512c593315Sopenharmony_ci                      const uint8_t *data, size_t datalen, size_t gso_size) {
14522c593315Sopenharmony_ci  iovec msg_iov;
14532c593315Sopenharmony_ci  msg_iov.iov_base = const_cast<uint8_t *>(data);
14542c593315Sopenharmony_ci  msg_iov.iov_len = datalen;
14552c593315Sopenharmony_ci
14562c593315Sopenharmony_ci  msghdr msg{};
14572c593315Sopenharmony_ci  msg.msg_name = const_cast<sockaddr *>(addr);
14582c593315Sopenharmony_ci  msg.msg_namelen = addrlen;
14592c593315Sopenharmony_ci  msg.msg_iov = &msg_iov;
14602c593315Sopenharmony_ci  msg.msg_iovlen = 1;
14612c593315Sopenharmony_ci
14622c593315Sopenharmony_ci#  ifdef UDP_SEGMENT
14632c593315Sopenharmony_ci  std::array<uint8_t, CMSG_SPACE(sizeof(uint16_t))> msg_ctrl{};
14642c593315Sopenharmony_ci  if (gso_size && datalen > gso_size) {
14652c593315Sopenharmony_ci    msg.msg_control = msg_ctrl.data();
14662c593315Sopenharmony_ci    msg.msg_controllen = msg_ctrl.size();
14672c593315Sopenharmony_ci
14682c593315Sopenharmony_ci    auto cm = CMSG_FIRSTHDR(&msg);
14692c593315Sopenharmony_ci    cm->cmsg_level = SOL_UDP;
14702c593315Sopenharmony_ci    cm->cmsg_type = UDP_SEGMENT;
14712c593315Sopenharmony_ci    cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
14722c593315Sopenharmony_ci    uint16_t n = gso_size;
14732c593315Sopenharmony_ci    memcpy(CMSG_DATA(cm), &n, sizeof(n));
14742c593315Sopenharmony_ci  }
14752c593315Sopenharmony_ci#  endif // UDP_SEGMENT
14762c593315Sopenharmony_ci
14772c593315Sopenharmony_ci  auto nwrite = sendmsg(fd, &msg, 0);
14782c593315Sopenharmony_ci  if (nwrite < 0) {
14792c593315Sopenharmony_ci    if (errno == EAGAIN || errno == EWOULDBLOCK) {
14802c593315Sopenharmony_ci      return 1;
14812c593315Sopenharmony_ci    }
14822c593315Sopenharmony_ci
14832c593315Sopenharmony_ci    std::cerr << "sendmsg: errno=" << errno << std::endl;
14842c593315Sopenharmony_ci  } else {
14852c593315Sopenharmony_ci    ++worker->stats.udp_dgram_sent;
14862c593315Sopenharmony_ci  }
14872c593315Sopenharmony_ci
14882c593315Sopenharmony_ci  ev_io_stop(worker->loop, &wev);
14892c593315Sopenharmony_ci
14902c593315Sopenharmony_ci  return 0;
14912c593315Sopenharmony_ci}
14922c593315Sopenharmony_ci#endif // ENABLE_HTTP3
14932c593315Sopenharmony_ci
14942c593315Sopenharmony_civoid Client::record_request_time(RequestStat *req_stat) {
14952c593315Sopenharmony_ci  req_stat->request_time = std::chrono::steady_clock::now();
14962c593315Sopenharmony_ci  req_stat->request_wall_time = std::chrono::system_clock::now();
14972c593315Sopenharmony_ci}
14982c593315Sopenharmony_ci
14992c593315Sopenharmony_civoid Client::record_connect_start_time() {
15002c593315Sopenharmony_ci  cstat.connect_start_time = std::chrono::steady_clock::now();
15012c593315Sopenharmony_ci}
15022c593315Sopenharmony_ci
15032c593315Sopenharmony_civoid Client::record_connect_time() {
15042c593315Sopenharmony_ci  cstat.connect_time = std::chrono::steady_clock::now();
15052c593315Sopenharmony_ci}
15062c593315Sopenharmony_ci
15072c593315Sopenharmony_civoid Client::record_ttfb() {
15082c593315Sopenharmony_ci  if (recorded(cstat.ttfb)) {
15092c593315Sopenharmony_ci    return;
15102c593315Sopenharmony_ci  }
15112c593315Sopenharmony_ci
15122c593315Sopenharmony_ci  cstat.ttfb = std::chrono::steady_clock::now();
15132c593315Sopenharmony_ci}
15142c593315Sopenharmony_ci
15152c593315Sopenharmony_civoid Client::clear_connect_times() {
15162c593315Sopenharmony_ci  cstat.connect_start_time = std::chrono::steady_clock::time_point();
15172c593315Sopenharmony_ci  cstat.connect_time = std::chrono::steady_clock::time_point();
15182c593315Sopenharmony_ci  cstat.ttfb = std::chrono::steady_clock::time_point();
15192c593315Sopenharmony_ci}
15202c593315Sopenharmony_ci
15212c593315Sopenharmony_civoid Client::record_client_start_time() {
15222c593315Sopenharmony_ci  // Record start time only once at the very first connection is going
15232c593315Sopenharmony_ci  // to be made.
15242c593315Sopenharmony_ci  if (recorded(cstat.client_start_time)) {
15252c593315Sopenharmony_ci    return;
15262c593315Sopenharmony_ci  }
15272c593315Sopenharmony_ci
15282c593315Sopenharmony_ci  cstat.client_start_time = std::chrono::steady_clock::now();
15292c593315Sopenharmony_ci}
15302c593315Sopenharmony_ci
15312c593315Sopenharmony_civoid Client::record_client_end_time() {
15322c593315Sopenharmony_ci  // Unlike client_start_time, we overwrite client_end_time.  This
15332c593315Sopenharmony_ci  // handles multiple connect/disconnect for HTTP/1.1 benchmark.
15342c593315Sopenharmony_ci  cstat.client_end_time = std::chrono::steady_clock::now();
15352c593315Sopenharmony_ci}
15362c593315Sopenharmony_ci
15372c593315Sopenharmony_civoid Client::signal_write() { ev_io_start(worker->loop, &wev); }
15382c593315Sopenharmony_ci
15392c593315Sopenharmony_civoid Client::try_new_connection() { new_connection_requested = true; }
15402c593315Sopenharmony_ci
15412c593315Sopenharmony_cinamespace {
15422c593315Sopenharmony_ciint get_ev_loop_flags() {
15432c593315Sopenharmony_ci  if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
15442c593315Sopenharmony_ci    return ev_recommended_backends() | EVBACKEND_KQUEUE;
15452c593315Sopenharmony_ci  }
15462c593315Sopenharmony_ci
15472c593315Sopenharmony_ci  return 0;
15482c593315Sopenharmony_ci}
15492c593315Sopenharmony_ci} // namespace
15502c593315Sopenharmony_ci
15512c593315Sopenharmony_ciWorker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
15522c593315Sopenharmony_ci               size_t rate, size_t max_samples, Config *config)
15532c593315Sopenharmony_ci    : randgen(util::make_mt19937()),
15542c593315Sopenharmony_ci      stats(req_todo, nclients),
15552c593315Sopenharmony_ci      loop(ev_loop_new(get_ev_loop_flags())),
15562c593315Sopenharmony_ci      ssl_ctx(ssl_ctx),
15572c593315Sopenharmony_ci      config(config),
15582c593315Sopenharmony_ci      id(id),
15592c593315Sopenharmony_ci      tls_info_report_done(false),
15602c593315Sopenharmony_ci      app_info_report_done(false),
15612c593315Sopenharmony_ci      nconns_made(0),
15622c593315Sopenharmony_ci      nclients(nclients),
15632c593315Sopenharmony_ci      nreqs_per_client(req_todo / nclients),
15642c593315Sopenharmony_ci      nreqs_rem(req_todo % nclients),
15652c593315Sopenharmony_ci      rate(rate),
15662c593315Sopenharmony_ci      max_samples(max_samples),
15672c593315Sopenharmony_ci      next_client_id(0) {
15682c593315Sopenharmony_ci  if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
15692c593315Sopenharmony_ci    progress_interval = std::max(static_cast<size_t>(1), req_todo / 10);
15702c593315Sopenharmony_ci  } else {
15712c593315Sopenharmony_ci    progress_interval = std::max(static_cast<size_t>(1), nclients / 10);
15722c593315Sopenharmony_ci  }
15732c593315Sopenharmony_ci
15742c593315Sopenharmony_ci  // Below timeout is not needed in case of timing-based benchmarking
15752c593315Sopenharmony_ci  // create timer that will go off every rate_period
15762c593315Sopenharmony_ci  ev_timer_init(&timeout_watcher, rate_period_timeout_w_cb, 0.,
15772c593315Sopenharmony_ci                config->rate_period);
15782c593315Sopenharmony_ci  timeout_watcher.data = this;
15792c593315Sopenharmony_ci
15802c593315Sopenharmony_ci  if (config->is_timing_based_mode()) {
15812c593315Sopenharmony_ci    stats.req_stats.reserve(std::max(req_todo, max_samples));
15822c593315Sopenharmony_ci    stats.client_stats.reserve(std::max(nclients, max_samples));
15832c593315Sopenharmony_ci  } else {
15842c593315Sopenharmony_ci    stats.req_stats.reserve(std::min(req_todo, max_samples));
15852c593315Sopenharmony_ci    stats.client_stats.reserve(std::min(nclients, max_samples));
15862c593315Sopenharmony_ci  }
15872c593315Sopenharmony_ci
15882c593315Sopenharmony_ci  sampling_init(request_times_smp, max_samples);
15892c593315Sopenharmony_ci  sampling_init(client_smp, max_samples);
15902c593315Sopenharmony_ci
15912c593315Sopenharmony_ci  ev_timer_init(&duration_watcher, duration_timeout_cb, config->duration, 0.);
15922c593315Sopenharmony_ci  duration_watcher.data = this;
15932c593315Sopenharmony_ci
15942c593315Sopenharmony_ci  ev_timer_init(&warmup_watcher, warmup_timeout_cb, config->warm_up_time, 0.);
15952c593315Sopenharmony_ci  warmup_watcher.data = this;
15962c593315Sopenharmony_ci
15972c593315Sopenharmony_ci  if (config->is_timing_based_mode()) {
15982c593315Sopenharmony_ci    current_phase = Phase::INITIAL_IDLE;
15992c593315Sopenharmony_ci  } else {
16002c593315Sopenharmony_ci    current_phase = Phase::MAIN_DURATION;
16012c593315Sopenharmony_ci  }
16022c593315Sopenharmony_ci}
16032c593315Sopenharmony_ci
16042c593315Sopenharmony_ciWorker::~Worker() {
16052c593315Sopenharmony_ci  ev_timer_stop(loop, &timeout_watcher);
16062c593315Sopenharmony_ci  ev_timer_stop(loop, &duration_watcher);
16072c593315Sopenharmony_ci  ev_timer_stop(loop, &warmup_watcher);
16082c593315Sopenharmony_ci  ev_loop_destroy(loop);
16092c593315Sopenharmony_ci}
16102c593315Sopenharmony_ci
16112c593315Sopenharmony_civoid Worker::stop_all_clients() {
16122c593315Sopenharmony_ci  for (auto client : clients) {
16132c593315Sopenharmony_ci    if (client) {
16142c593315Sopenharmony_ci      client->terminate_session();
16152c593315Sopenharmony_ci    }
16162c593315Sopenharmony_ci  }
16172c593315Sopenharmony_ci}
16182c593315Sopenharmony_ci
16192c593315Sopenharmony_civoid Worker::free_client(Client *deleted_client) {
16202c593315Sopenharmony_ci  for (auto &client : clients) {
16212c593315Sopenharmony_ci    if (client == deleted_client) {
16222c593315Sopenharmony_ci      client->req_todo = client->req_done;
16232c593315Sopenharmony_ci      stats.req_todo += client->req_todo;
16242c593315Sopenharmony_ci      auto index = &client - &clients[0];
16252c593315Sopenharmony_ci      clients[index] = nullptr;
16262c593315Sopenharmony_ci      return;
16272c593315Sopenharmony_ci    }
16282c593315Sopenharmony_ci  }
16292c593315Sopenharmony_ci}
16302c593315Sopenharmony_ci
16312c593315Sopenharmony_civoid Worker::run() {
16322c593315Sopenharmony_ci  if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
16332c593315Sopenharmony_ci    for (size_t i = 0; i < nclients; ++i) {
16342c593315Sopenharmony_ci      auto req_todo = nreqs_per_client;
16352c593315Sopenharmony_ci      if (nreqs_rem > 0) {
16362c593315Sopenharmony_ci        ++req_todo;
16372c593315Sopenharmony_ci        --nreqs_rem;
16382c593315Sopenharmony_ci      }
16392c593315Sopenharmony_ci
16402c593315Sopenharmony_ci      auto client = std::make_unique<Client>(next_client_id++, this, req_todo);
16412c593315Sopenharmony_ci      if (client->connect() != 0) {
16422c593315Sopenharmony_ci        std::cerr << "client could not connect to host" << std::endl;
16432c593315Sopenharmony_ci        client->fail();
16442c593315Sopenharmony_ci      } else {
16452c593315Sopenharmony_ci        client.release();
16462c593315Sopenharmony_ci      }
16472c593315Sopenharmony_ci    }
16482c593315Sopenharmony_ci  } else if (config->is_rate_mode()) {
16492c593315Sopenharmony_ci    ev_timer_again(loop, &timeout_watcher);
16502c593315Sopenharmony_ci
16512c593315Sopenharmony_ci    // call callback so that we don't waste the first rate_period
16522c593315Sopenharmony_ci    rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
16532c593315Sopenharmony_ci  } else {
16542c593315Sopenharmony_ci    // call the callback to start for one single time
16552c593315Sopenharmony_ci    rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
16562c593315Sopenharmony_ci  }
16572c593315Sopenharmony_ci  ev_run(loop, 0);
16582c593315Sopenharmony_ci}
16592c593315Sopenharmony_ci
16602c593315Sopenharmony_cinamespace {
16612c593315Sopenharmony_citemplate <typename Stats, typename Stat>
16622c593315Sopenharmony_civoid sample(Sampling &smp, Stats &stats, Stat *s) {
16632c593315Sopenharmony_ci  ++smp.n;
16642c593315Sopenharmony_ci  if (stats.size() < smp.max_samples) {
16652c593315Sopenharmony_ci    stats.push_back(*s);
16662c593315Sopenharmony_ci    return;
16672c593315Sopenharmony_ci  }
16682c593315Sopenharmony_ci  auto d = std::uniform_int_distribution<unsigned long>(0, smp.n - 1);
16692c593315Sopenharmony_ci  auto i = d(gen);
16702c593315Sopenharmony_ci  if (i < smp.max_samples) {
16712c593315Sopenharmony_ci    stats[i] = *s;
16722c593315Sopenharmony_ci  }
16732c593315Sopenharmony_ci}
16742c593315Sopenharmony_ci} // namespace
16752c593315Sopenharmony_ci
16762c593315Sopenharmony_civoid Worker::sample_req_stat(RequestStat *req_stat) {
16772c593315Sopenharmony_ci  sample(request_times_smp, stats.req_stats, req_stat);
16782c593315Sopenharmony_ci}
16792c593315Sopenharmony_ci
16802c593315Sopenharmony_civoid Worker::sample_client_stat(ClientStat *cstat) {
16812c593315Sopenharmony_ci  sample(client_smp, stats.client_stats, cstat);
16822c593315Sopenharmony_ci}
16832c593315Sopenharmony_ci
16842c593315Sopenharmony_civoid Worker::report_progress() {
16852c593315Sopenharmony_ci  if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval ||
16862c593315Sopenharmony_ci      config->is_timing_based_mode()) {
16872c593315Sopenharmony_ci    return;
16882c593315Sopenharmony_ci  }
16892c593315Sopenharmony_ci
16902c593315Sopenharmony_ci  std::cout << "progress: " << stats.req_done * 100 / stats.req_todo << "% done"
16912c593315Sopenharmony_ci            << std::endl;
16922c593315Sopenharmony_ci}
16932c593315Sopenharmony_ci
16942c593315Sopenharmony_civoid Worker::report_rate_progress() {
16952c593315Sopenharmony_ci  if (id != 0 || nconns_made % progress_interval) {
16962c593315Sopenharmony_ci    return;
16972c593315Sopenharmony_ci  }
16982c593315Sopenharmony_ci
16992c593315Sopenharmony_ci  std::cout << "progress: " << nconns_made * 100 / nclients
17002c593315Sopenharmony_ci            << "% of clients started" << std::endl;
17012c593315Sopenharmony_ci}
17022c593315Sopenharmony_ci
17032c593315Sopenharmony_cinamespace {
17042c593315Sopenharmony_ci// Returns percentage of number of samples within mean +/- sd.
17052c593315Sopenharmony_cidouble within_sd(const std::vector<double> &samples, double mean, double sd) {
17062c593315Sopenharmony_ci  if (samples.size() == 0) {
17072c593315Sopenharmony_ci    return 0.0;
17082c593315Sopenharmony_ci  }
17092c593315Sopenharmony_ci  auto lower = mean - sd;
17102c593315Sopenharmony_ci  auto upper = mean + sd;
17112c593315Sopenharmony_ci  auto m = std::count_if(
17122c593315Sopenharmony_ci      std::begin(samples), std::end(samples),
17132c593315Sopenharmony_ci      [&lower, &upper](double t) { return lower <= t && t <= upper; });
17142c593315Sopenharmony_ci  return (m / static_cast<double>(samples.size())) * 100;
17152c593315Sopenharmony_ci}
17162c593315Sopenharmony_ci} // namespace
17172c593315Sopenharmony_ci
17182c593315Sopenharmony_cinamespace {
17192c593315Sopenharmony_ci// Computes statistics using |samples|. The min, max, mean, sd, and
17202c593315Sopenharmony_ci// percentage of number of samples within mean +/- sd are computed.
17212c593315Sopenharmony_ci// If |sampling| is true, this computes sample variance.  Otherwise,
17222c593315Sopenharmony_ci// population variance.
17232c593315Sopenharmony_ciSDStat compute_time_stat(const std::vector<double> &samples,
17242c593315Sopenharmony_ci                         bool sampling = false) {
17252c593315Sopenharmony_ci  if (samples.empty()) {
17262c593315Sopenharmony_ci    return {0.0, 0.0, 0.0, 0.0, 0.0};
17272c593315Sopenharmony_ci  }
17282c593315Sopenharmony_ci  // standard deviation calculated using Rapid calculation method:
17292c593315Sopenharmony_ci  // https://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
17302c593315Sopenharmony_ci  double a = 0, q = 0;
17312c593315Sopenharmony_ci  size_t n = 0;
17322c593315Sopenharmony_ci  double sum = 0;
17332c593315Sopenharmony_ci  auto res = SDStat{std::numeric_limits<double>::max(),
17342c593315Sopenharmony_ci                    std::numeric_limits<double>::min()};
17352c593315Sopenharmony_ci  for (const auto &t : samples) {
17362c593315Sopenharmony_ci    ++n;
17372c593315Sopenharmony_ci    res.min = std::min(res.min, t);
17382c593315Sopenharmony_ci    res.max = std::max(res.max, t);
17392c593315Sopenharmony_ci    sum += t;
17402c593315Sopenharmony_ci
17412c593315Sopenharmony_ci    auto na = a + (t - a) / n;
17422c593315Sopenharmony_ci    q += (t - a) * (t - na);
17432c593315Sopenharmony_ci    a = na;
17442c593315Sopenharmony_ci  }
17452c593315Sopenharmony_ci
17462c593315Sopenharmony_ci  assert(n > 0);
17472c593315Sopenharmony_ci  res.mean = sum / n;
17482c593315Sopenharmony_ci  res.sd = sqrt(q / (sampling && n > 1 ? n - 1 : n));
17492c593315Sopenharmony_ci  res.within_sd = within_sd(samples, res.mean, res.sd);
17502c593315Sopenharmony_ci
17512c593315Sopenharmony_ci  return res;
17522c593315Sopenharmony_ci}
17532c593315Sopenharmony_ci} // namespace
17542c593315Sopenharmony_ci
17552c593315Sopenharmony_cinamespace {
17562c593315Sopenharmony_ciSDStats
17572c593315Sopenharmony_ciprocess_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
17582c593315Sopenharmony_ci  auto request_times_sampling = false;
17592c593315Sopenharmony_ci  auto client_times_sampling = false;
17602c593315Sopenharmony_ci  size_t nrequest_times = 0;
17612c593315Sopenharmony_ci  size_t nclient_times = 0;
17622c593315Sopenharmony_ci  for (const auto &w : workers) {
17632c593315Sopenharmony_ci    nrequest_times += w->stats.req_stats.size();
17642c593315Sopenharmony_ci    request_times_sampling = w->request_times_smp.n > w->stats.req_stats.size();
17652c593315Sopenharmony_ci
17662c593315Sopenharmony_ci    nclient_times += w->stats.client_stats.size();
17672c593315Sopenharmony_ci    client_times_sampling = w->client_smp.n > w->stats.client_stats.size();
17682c593315Sopenharmony_ci  }
17692c593315Sopenharmony_ci
17702c593315Sopenharmony_ci  std::vector<double> request_times;
17712c593315Sopenharmony_ci  request_times.reserve(nrequest_times);
17722c593315Sopenharmony_ci
17732c593315Sopenharmony_ci  std::vector<double> connect_times, ttfb_times, rps_values;
17742c593315Sopenharmony_ci  connect_times.reserve(nclient_times);
17752c593315Sopenharmony_ci  ttfb_times.reserve(nclient_times);
17762c593315Sopenharmony_ci  rps_values.reserve(nclient_times);
17772c593315Sopenharmony_ci
17782c593315Sopenharmony_ci  for (const auto &w : workers) {
17792c593315Sopenharmony_ci    for (const auto &req_stat : w->stats.req_stats) {
17802c593315Sopenharmony_ci      if (!req_stat.completed) {
17812c593315Sopenharmony_ci        continue;
17822c593315Sopenharmony_ci      }
17832c593315Sopenharmony_ci      request_times.push_back(
17842c593315Sopenharmony_ci          std::chrono::duration_cast<std::chrono::duration<double>>(
17852c593315Sopenharmony_ci              req_stat.stream_close_time - req_stat.request_time)
17862c593315Sopenharmony_ci              .count());
17872c593315Sopenharmony_ci    }
17882c593315Sopenharmony_ci
17892c593315Sopenharmony_ci    const auto &stat = w->stats;
17902c593315Sopenharmony_ci
17912c593315Sopenharmony_ci    for (const auto &cstat : stat.client_stats) {
17922c593315Sopenharmony_ci      if (recorded(cstat.client_start_time) &&
17932c593315Sopenharmony_ci          recorded(cstat.client_end_time)) {
17942c593315Sopenharmony_ci        auto t = std::chrono::duration_cast<std::chrono::duration<double>>(
17952c593315Sopenharmony_ci                     cstat.client_end_time - cstat.client_start_time)
17962c593315Sopenharmony_ci                     .count();
17972c593315Sopenharmony_ci        if (t > 1e-9) {
17982c593315Sopenharmony_ci          rps_values.push_back(cstat.req_success / t);
17992c593315Sopenharmony_ci        }
18002c593315Sopenharmony_ci      }
18012c593315Sopenharmony_ci
18022c593315Sopenharmony_ci      // We will get connect event before FFTB.
18032c593315Sopenharmony_ci      if (!recorded(cstat.connect_start_time) ||
18042c593315Sopenharmony_ci          !recorded(cstat.connect_time)) {
18052c593315Sopenharmony_ci        continue;
18062c593315Sopenharmony_ci      }
18072c593315Sopenharmony_ci
18082c593315Sopenharmony_ci      connect_times.push_back(
18092c593315Sopenharmony_ci          std::chrono::duration_cast<std::chrono::duration<double>>(
18102c593315Sopenharmony_ci              cstat.connect_time - cstat.connect_start_time)
18112c593315Sopenharmony_ci              .count());
18122c593315Sopenharmony_ci
18132c593315Sopenharmony_ci      if (!recorded(cstat.ttfb)) {
18142c593315Sopenharmony_ci        continue;
18152c593315Sopenharmony_ci      }
18162c593315Sopenharmony_ci
18172c593315Sopenharmony_ci      ttfb_times.push_back(
18182c593315Sopenharmony_ci          std::chrono::duration_cast<std::chrono::duration<double>>(
18192c593315Sopenharmony_ci              cstat.ttfb - cstat.connect_start_time)
18202c593315Sopenharmony_ci              .count());
18212c593315Sopenharmony_ci    }
18222c593315Sopenharmony_ci  }
18232c593315Sopenharmony_ci
18242c593315Sopenharmony_ci  return {compute_time_stat(request_times, request_times_sampling),
18252c593315Sopenharmony_ci          compute_time_stat(connect_times, client_times_sampling),
18262c593315Sopenharmony_ci          compute_time_stat(ttfb_times, client_times_sampling),
18272c593315Sopenharmony_ci          compute_time_stat(rps_values, client_times_sampling)};
18282c593315Sopenharmony_ci}
18292c593315Sopenharmony_ci} // namespace
18302c593315Sopenharmony_ci
18312c593315Sopenharmony_cinamespace {
18322c593315Sopenharmony_civoid resolve_host() {
18332c593315Sopenharmony_ci  if (config.base_uri_unix) {
18342c593315Sopenharmony_ci    auto res = std::make_unique<addrinfo>();
18352c593315Sopenharmony_ci    res->ai_family = config.unix_addr.sun_family;
18362c593315Sopenharmony_ci    res->ai_socktype = SOCK_STREAM;
18372c593315Sopenharmony_ci    res->ai_addrlen = sizeof(config.unix_addr);
18382c593315Sopenharmony_ci    res->ai_addr =
18392c593315Sopenharmony_ci        static_cast<struct sockaddr *>(static_cast<void *>(&config.unix_addr));
18402c593315Sopenharmony_ci
18412c593315Sopenharmony_ci    config.addrs = res.release();
18422c593315Sopenharmony_ci    return;
18432c593315Sopenharmony_ci  };
18442c593315Sopenharmony_ci
18452c593315Sopenharmony_ci  int rv;
18462c593315Sopenharmony_ci  addrinfo hints{}, *res;
18472c593315Sopenharmony_ci
18482c593315Sopenharmony_ci  hints.ai_family = AF_UNSPEC;
18492c593315Sopenharmony_ci  hints.ai_socktype = SOCK_STREAM;
18502c593315Sopenharmony_ci  hints.ai_protocol = 0;
18512c593315Sopenharmony_ci  hints.ai_flags = AI_ADDRCONFIG;
18522c593315Sopenharmony_ci
18532c593315Sopenharmony_ci  const auto &resolve_host =
18542c593315Sopenharmony_ci      config.connect_to_host.empty() ? config.host : config.connect_to_host;
18552c593315Sopenharmony_ci  auto port =
18562c593315Sopenharmony_ci      config.connect_to_port == 0 ? config.port : config.connect_to_port;
18572c593315Sopenharmony_ci
18582c593315Sopenharmony_ci  rv =
18592c593315Sopenharmony_ci      getaddrinfo(resolve_host.c_str(), util::utos(port).c_str(), &hints, &res);
18602c593315Sopenharmony_ci  if (rv != 0) {
18612c593315Sopenharmony_ci    std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl;
18622c593315Sopenharmony_ci    exit(EXIT_FAILURE);
18632c593315Sopenharmony_ci  }
18642c593315Sopenharmony_ci  if (res == nullptr) {
18652c593315Sopenharmony_ci    std::cerr << "No address returned" << std::endl;
18662c593315Sopenharmony_ci    exit(EXIT_FAILURE);
18672c593315Sopenharmony_ci  }
18682c593315Sopenharmony_ci  config.addrs = res;
18692c593315Sopenharmony_ci}
18702c593315Sopenharmony_ci} // namespace
18712c593315Sopenharmony_ci
18722c593315Sopenharmony_cinamespace {
18732c593315Sopenharmony_cistd::string get_reqline(const char *uri, const http_parser_url &u) {
18742c593315Sopenharmony_ci  std::string reqline;
18752c593315Sopenharmony_ci
18762c593315Sopenharmony_ci  if (util::has_uri_field(u, UF_PATH)) {
18772c593315Sopenharmony_ci    reqline = util::get_uri_field(uri, u, UF_PATH).str();
18782c593315Sopenharmony_ci  } else {
18792c593315Sopenharmony_ci    reqline = "/";
18802c593315Sopenharmony_ci  }
18812c593315Sopenharmony_ci
18822c593315Sopenharmony_ci  if (util::has_uri_field(u, UF_QUERY)) {
18832c593315Sopenharmony_ci    reqline += '?';
18842c593315Sopenharmony_ci    reqline += util::get_uri_field(uri, u, UF_QUERY);
18852c593315Sopenharmony_ci  }
18862c593315Sopenharmony_ci
18872c593315Sopenharmony_ci  return reqline;
18882c593315Sopenharmony_ci}
18892c593315Sopenharmony_ci} // namespace
18902c593315Sopenharmony_ci
18912c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG
18922c593315Sopenharmony_cinamespace {
18932c593315Sopenharmony_ciint client_select_next_proto_cb(SSL *ssl, unsigned char **out,
18942c593315Sopenharmony_ci                                unsigned char *outlen, const unsigned char *in,
18952c593315Sopenharmony_ci                                unsigned int inlen, void *arg) {
18962c593315Sopenharmony_ci  if (util::select_protocol(const_cast<const unsigned char **>(out), outlen, in,
18972c593315Sopenharmony_ci                            inlen, config.npn_list)) {
18982c593315Sopenharmony_ci    return SSL_TLSEXT_ERR_OK;
18992c593315Sopenharmony_ci  }
19002c593315Sopenharmony_ci
19012c593315Sopenharmony_ci  // OpenSSL will terminate handshake with fatal alert if we return
19022c593315Sopenharmony_ci  // NOACK.  So there is no way to fallback.
19032c593315Sopenharmony_ci  return SSL_TLSEXT_ERR_NOACK;
19042c593315Sopenharmony_ci}
19052c593315Sopenharmony_ci} // namespace
19062c593315Sopenharmony_ci#endif // !OPENSSL_NO_NEXTPROTONEG
19072c593315Sopenharmony_ci
19082c593315Sopenharmony_cinamespace {
19092c593315Sopenharmony_ciconstexpr char UNIX_PATH_PREFIX[] = "unix:";
19102c593315Sopenharmony_ci} // namespace
19112c593315Sopenharmony_ci
19122c593315Sopenharmony_cinamespace {
19132c593315Sopenharmony_cibool parse_base_uri(const StringRef &base_uri) {
19142c593315Sopenharmony_ci  http_parser_url u{};
19152c593315Sopenharmony_ci  if (http_parser_parse_url(base_uri.c_str(), base_uri.size(), 0, &u) != 0 ||
19162c593315Sopenharmony_ci      !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) {
19172c593315Sopenharmony_ci    return false;
19182c593315Sopenharmony_ci  }
19192c593315Sopenharmony_ci
19202c593315Sopenharmony_ci  config.scheme = util::get_uri_field(base_uri.c_str(), u, UF_SCHEMA).str();
19212c593315Sopenharmony_ci  config.host = util::get_uri_field(base_uri.c_str(), u, UF_HOST).str();
19222c593315Sopenharmony_ci  config.default_port = util::get_default_port(base_uri.c_str(), u);
19232c593315Sopenharmony_ci  if (util::has_uri_field(u, UF_PORT)) {
19242c593315Sopenharmony_ci    config.port = u.port;
19252c593315Sopenharmony_ci  } else {
19262c593315Sopenharmony_ci    config.port = config.default_port;
19272c593315Sopenharmony_ci  }
19282c593315Sopenharmony_ci
19292c593315Sopenharmony_ci  return true;
19302c593315Sopenharmony_ci}
19312c593315Sopenharmony_ci} // namespace
19322c593315Sopenharmony_cinamespace {
19332c593315Sopenharmony_ci// Use std::vector<std::string>::iterator explicitly, without that,
19342c593315Sopenharmony_ci// http_parser_url u{} fails with clang-3.4.
19352c593315Sopenharmony_cistd::vector<std::string> parse_uris(std::vector<std::string>::iterator first,
19362c593315Sopenharmony_ci                                    std::vector<std::string>::iterator last) {
19372c593315Sopenharmony_ci  std::vector<std::string> reqlines;
19382c593315Sopenharmony_ci
19392c593315Sopenharmony_ci  if (first == last) {
19402c593315Sopenharmony_ci    std::cerr << "no URI available" << std::endl;
19412c593315Sopenharmony_ci    exit(EXIT_FAILURE);
19422c593315Sopenharmony_ci  }
19432c593315Sopenharmony_ci
19442c593315Sopenharmony_ci  if (!config.has_base_uri()) {
19452c593315Sopenharmony_ci
19462c593315Sopenharmony_ci    if (!parse_base_uri(StringRef{*first})) {
19472c593315Sopenharmony_ci      std::cerr << "invalid URI: " << *first << std::endl;
19482c593315Sopenharmony_ci      exit(EXIT_FAILURE);
19492c593315Sopenharmony_ci    }
19502c593315Sopenharmony_ci
19512c593315Sopenharmony_ci    config.base_uri = *first;
19522c593315Sopenharmony_ci  }
19532c593315Sopenharmony_ci
19542c593315Sopenharmony_ci  for (; first != last; ++first) {
19552c593315Sopenharmony_ci    http_parser_url u{};
19562c593315Sopenharmony_ci
19572c593315Sopenharmony_ci    auto uri = (*first).c_str();
19582c593315Sopenharmony_ci
19592c593315Sopenharmony_ci    if (http_parser_parse_url(uri, (*first).size(), 0, &u) != 0) {
19602c593315Sopenharmony_ci      std::cerr << "invalid URI: " << uri << std::endl;
19612c593315Sopenharmony_ci      exit(EXIT_FAILURE);
19622c593315Sopenharmony_ci    }
19632c593315Sopenharmony_ci
19642c593315Sopenharmony_ci    reqlines.push_back(get_reqline(uri, u));
19652c593315Sopenharmony_ci  }
19662c593315Sopenharmony_ci
19672c593315Sopenharmony_ci  return reqlines;
19682c593315Sopenharmony_ci}
19692c593315Sopenharmony_ci} // namespace
19702c593315Sopenharmony_ci
19712c593315Sopenharmony_cinamespace {
19722c593315Sopenharmony_cistd::vector<std::string> read_uri_from_file(std::istream &infile) {
19732c593315Sopenharmony_ci  std::vector<std::string> uris;
19742c593315Sopenharmony_ci  std::string line_uri;
19752c593315Sopenharmony_ci  while (std::getline(infile, line_uri)) {
19762c593315Sopenharmony_ci    uris.push_back(line_uri);
19772c593315Sopenharmony_ci  }
19782c593315Sopenharmony_ci
19792c593315Sopenharmony_ci  return uris;
19802c593315Sopenharmony_ci}
19812c593315Sopenharmony_ci} // namespace
19822c593315Sopenharmony_ci
19832c593315Sopenharmony_cinamespace {
19842c593315Sopenharmony_civoid read_script_from_file(
19852c593315Sopenharmony_ci    std::istream &infile,
19862c593315Sopenharmony_ci    std::vector<std::chrono::steady_clock::duration> &timings,
19872c593315Sopenharmony_ci    std::vector<std::string> &uris) {
19882c593315Sopenharmony_ci  std::string script_line;
19892c593315Sopenharmony_ci  int line_count = 0;
19902c593315Sopenharmony_ci  while (std::getline(infile, script_line)) {
19912c593315Sopenharmony_ci    line_count++;
19922c593315Sopenharmony_ci    if (script_line.empty()) {
19932c593315Sopenharmony_ci      std::cerr << "Empty line detected at line " << line_count
19942c593315Sopenharmony_ci                << ". Ignoring and continuing." << std::endl;
19952c593315Sopenharmony_ci      continue;
19962c593315Sopenharmony_ci    }
19972c593315Sopenharmony_ci
19982c593315Sopenharmony_ci    std::size_t pos = script_line.find("\t");
19992c593315Sopenharmony_ci    if (pos == std::string::npos) {
20002c593315Sopenharmony_ci      std::cerr << "Invalid line format detected, no tab character at line "
20012c593315Sopenharmony_ci                << line_count << ". \n\t" << script_line << std::endl;
20022c593315Sopenharmony_ci      exit(EXIT_FAILURE);
20032c593315Sopenharmony_ci    }
20042c593315Sopenharmony_ci
20052c593315Sopenharmony_ci    const char *start = script_line.c_str();
20062c593315Sopenharmony_ci    char *end;
20072c593315Sopenharmony_ci    auto v = std::strtod(start, &end);
20082c593315Sopenharmony_ci
20092c593315Sopenharmony_ci    errno = 0;
20102c593315Sopenharmony_ci    if (v < 0.0 || !std::isfinite(v) || end == start || errno != 0) {
20112c593315Sopenharmony_ci      auto error = errno;
20122c593315Sopenharmony_ci      std::cerr << "Time value error at line " << line_count << ". \n\t"
20132c593315Sopenharmony_ci                << "value = " << script_line.substr(0, pos) << std::endl;
20142c593315Sopenharmony_ci      if (error != 0) {
20152c593315Sopenharmony_ci        std::cerr << "\t" << strerror(error) << std::endl;
20162c593315Sopenharmony_ci      }
20172c593315Sopenharmony_ci      exit(EXIT_FAILURE);
20182c593315Sopenharmony_ci    }
20192c593315Sopenharmony_ci
20202c593315Sopenharmony_ci    timings.emplace_back(
20212c593315Sopenharmony_ci        std::chrono::duration_cast<std::chrono::steady_clock::duration>(
20222c593315Sopenharmony_ci            std::chrono::duration<double, std::milli>(v)));
20232c593315Sopenharmony_ci    uris.push_back(script_line.substr(pos + 1, script_line.size()));
20242c593315Sopenharmony_ci  }
20252c593315Sopenharmony_ci}
20262c593315Sopenharmony_ci} // namespace
20272c593315Sopenharmony_ci
20282c593315Sopenharmony_cinamespace {
20292c593315Sopenharmony_cistd::unique_ptr<Worker> create_worker(uint32_t id, SSL_CTX *ssl_ctx,
20302c593315Sopenharmony_ci                                      size_t nreqs, size_t nclients,
20312c593315Sopenharmony_ci                                      size_t rate, size_t max_samples) {
20322c593315Sopenharmony_ci  std::stringstream rate_report;
20332c593315Sopenharmony_ci  if (config.is_rate_mode() && nclients > rate) {
20342c593315Sopenharmony_ci    rate_report << "Up to " << rate << " client(s) will be created every "
20352c593315Sopenharmony_ci                << util::duration_str(config.rate_period) << " ";
20362c593315Sopenharmony_ci  }
20372c593315Sopenharmony_ci
20382c593315Sopenharmony_ci  if (config.is_timing_based_mode()) {
20392c593315Sopenharmony_ci    std::cout << "spawning thread #" << id << ": " << nclients
20402c593315Sopenharmony_ci              << " total client(s). Timing-based test with "
20412c593315Sopenharmony_ci              << config.warm_up_time << "s of warm-up time and "
20422c593315Sopenharmony_ci              << config.duration << "s of main duration for measurements."
20432c593315Sopenharmony_ci              << std::endl;
20442c593315Sopenharmony_ci  } else {
20452c593315Sopenharmony_ci    std::cout << "spawning thread #" << id << ": " << nclients
20462c593315Sopenharmony_ci              << " total client(s). " << rate_report.str() << nreqs
20472c593315Sopenharmony_ci              << " total requests" << std::endl;
20482c593315Sopenharmony_ci  }
20492c593315Sopenharmony_ci
20502c593315Sopenharmony_ci  if (config.is_rate_mode()) {
20512c593315Sopenharmony_ci    return std::make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate,
20522c593315Sopenharmony_ci                                    max_samples, &config);
20532c593315Sopenharmony_ci  } else {
20542c593315Sopenharmony_ci    // Here rate is same as client because the rate_timeout callback
20552c593315Sopenharmony_ci    // will be called only once
20562c593315Sopenharmony_ci    return std::make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
20572c593315Sopenharmony_ci                                    max_samples, &config);
20582c593315Sopenharmony_ci  }
20592c593315Sopenharmony_ci}
20602c593315Sopenharmony_ci} // namespace
20612c593315Sopenharmony_ci
20622c593315Sopenharmony_cinamespace {
20632c593315Sopenharmony_ciint parse_header_table_size(uint32_t &dst, const char *opt,
20642c593315Sopenharmony_ci                            const char *optarg) {
20652c593315Sopenharmony_ci  auto n = util::parse_uint_with_unit(optarg);
20662c593315Sopenharmony_ci  if (n == -1) {
20672c593315Sopenharmony_ci    std::cerr << "--" << opt << ": Bad option value: " << optarg << std::endl;
20682c593315Sopenharmony_ci    return -1;
20692c593315Sopenharmony_ci  }
20702c593315Sopenharmony_ci  if (n > std::numeric_limits<uint32_t>::max()) {
20712c593315Sopenharmony_ci    std::cerr << "--" << opt
20722c593315Sopenharmony_ci              << ": Value too large.  It should be less than or equal to "
20732c593315Sopenharmony_ci              << std::numeric_limits<uint32_t>::max() << std::endl;
20742c593315Sopenharmony_ci    return -1;
20752c593315Sopenharmony_ci  }
20762c593315Sopenharmony_ci
20772c593315Sopenharmony_ci  dst = n;
20782c593315Sopenharmony_ci
20792c593315Sopenharmony_ci  return 0;
20802c593315Sopenharmony_ci}
20812c593315Sopenharmony_ci} // namespace
20822c593315Sopenharmony_ci
20832c593315Sopenharmony_cinamespace {
20842c593315Sopenharmony_civoid print_version(std::ostream &out) {
20852c593315Sopenharmony_ci  out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl;
20862c593315Sopenharmony_ci}
20872c593315Sopenharmony_ci} // namespace
20882c593315Sopenharmony_ci
20892c593315Sopenharmony_cinamespace {
20902c593315Sopenharmony_civoid print_usage(std::ostream &out) {
20912c593315Sopenharmony_ci  out << R"(Usage: h2load [OPTIONS]... [URI]...
20922c593315Sopenharmony_cibenchmarking tool for HTTP/2 server)"
20932c593315Sopenharmony_ci      << std::endl;
20942c593315Sopenharmony_ci}
20952c593315Sopenharmony_ci} // namespace
20962c593315Sopenharmony_ci
20972c593315Sopenharmony_cinamespace {
20982c593315Sopenharmony_ciconstexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14,http/1.1";
20992c593315Sopenharmony_ci} // namespace
21002c593315Sopenharmony_ci
21012c593315Sopenharmony_cinamespace {
21022c593315Sopenharmony_civoid print_help(std::ostream &out) {
21032c593315Sopenharmony_ci  print_usage(out);
21042c593315Sopenharmony_ci
21052c593315Sopenharmony_ci  auto config = Config();
21062c593315Sopenharmony_ci
21072c593315Sopenharmony_ci  out << R"(
21082c593315Sopenharmony_ci  <URI>       Specify URI to access.   Multiple URIs can be specified.
21092c593315Sopenharmony_ci              URIs are used  in this order for each  client.  All URIs
21102c593315Sopenharmony_ci              are used, then  first URI is used and then  2nd URI, and
21112c593315Sopenharmony_ci              so  on.  The  scheme, host  and port  in the  subsequent
21122c593315Sopenharmony_ci              URIs, if present,  are ignored.  Those in  the first URI
21132c593315Sopenharmony_ci              are used solely.  Definition of a base URI overrides all
21142c593315Sopenharmony_ci              scheme, host or port values.
21152c593315Sopenharmony_ciOptions:
21162c593315Sopenharmony_ci  -n, --requests=<N>
21172c593315Sopenharmony_ci              Number of  requests across all  clients.  If it  is used
21182c593315Sopenharmony_ci              with --timing-script-file option,  this option specifies
21192c593315Sopenharmony_ci              the number of requests  each client performs rather than
21202c593315Sopenharmony_ci              the number of requests  across all clients.  This option
21212c593315Sopenharmony_ci              is ignored if timing-based  benchmarking is enabled (see
21222c593315Sopenharmony_ci              --duration option).
21232c593315Sopenharmony_ci              Default: )"
21242c593315Sopenharmony_ci      << config.nreqs << R"(
21252c593315Sopenharmony_ci  -c, --clients=<N>
21262c593315Sopenharmony_ci              Number  of concurrent  clients.   With  -r option,  this
21272c593315Sopenharmony_ci              specifies the maximum number of connections to be made.
21282c593315Sopenharmony_ci              Default: )"
21292c593315Sopenharmony_ci      << config.nclients << R"(
21302c593315Sopenharmony_ci  -t, --threads=<N>
21312c593315Sopenharmony_ci              Number of native threads.
21322c593315Sopenharmony_ci              Default: )"
21332c593315Sopenharmony_ci      << config.nthreads << R"(
21342c593315Sopenharmony_ci  -i, --input-file=<PATH>
21352c593315Sopenharmony_ci              Path of a file with multiple URIs are separated by EOLs.
21362c593315Sopenharmony_ci              This option will disable URIs getting from command-line.
21372c593315Sopenharmony_ci              If '-' is given as <PATH>, URIs will be read from stdin.
21382c593315Sopenharmony_ci              URIs are used  in this order for each  client.  All URIs
21392c593315Sopenharmony_ci              are used, then  first URI is used and then  2nd URI, and
21402c593315Sopenharmony_ci              so  on.  The  scheme, host  and port  in the  subsequent
21412c593315Sopenharmony_ci              URIs, if present,  are ignored.  Those in  the first URI
21422c593315Sopenharmony_ci              are used solely.  Definition of a base URI overrides all
21432c593315Sopenharmony_ci              scheme, host or port values.
21442c593315Sopenharmony_ci  -m, --max-concurrent-streams=<N>
21452c593315Sopenharmony_ci              Max  concurrent  streams  to issue  per  session.   When
21462c593315Sopenharmony_ci              http/1.1  is used,  this  specifies the  number of  HTTP
21472c593315Sopenharmony_ci              pipelining requests in-flight.
21482c593315Sopenharmony_ci              Default: 1
21492c593315Sopenharmony_ci  -f, --max-frame-size=<SIZE>
21502c593315Sopenharmony_ci              Maximum frame size that the local endpoint is willing to
21512c593315Sopenharmony_ci              receive.
21522c593315Sopenharmony_ci              Default: )"
21532c593315Sopenharmony_ci      << util::utos_unit(config.max_frame_size) << R"(
21542c593315Sopenharmony_ci  -w, --window-bits=<N>
21552c593315Sopenharmony_ci              Sets the stream level initial window size to (2**<N>)-1.
21562c593315Sopenharmony_ci              For QUIC, <N> is capped to 26 (roughly 64MiB).
21572c593315Sopenharmony_ci              Default: )"
21582c593315Sopenharmony_ci      << config.window_bits << R"(
21592c593315Sopenharmony_ci  -W, --connection-window-bits=<N>
21602c593315Sopenharmony_ci              Sets  the  connection  level   initial  window  size  to
21612c593315Sopenharmony_ci              (2**<N>)-1.
21622c593315Sopenharmony_ci              Default: )"
21632c593315Sopenharmony_ci      << config.connection_window_bits << R"(
21642c593315Sopenharmony_ci  -H, --header=<HEADER>
21652c593315Sopenharmony_ci              Add/Override a header to the requests.
21662c593315Sopenharmony_ci  --ciphers=<SUITE>
21672c593315Sopenharmony_ci              Set  allowed cipher  list  for TLSv1.2  or earlier.   The
21682c593315Sopenharmony_ci              format of the string is described in OpenSSL ciphers(1).
21692c593315Sopenharmony_ci              Default: )"
21702c593315Sopenharmony_ci      << config.ciphers << R"(
21712c593315Sopenharmony_ci  --tls13-ciphers=<SUITE>
21722c593315Sopenharmony_ci              Set allowed cipher list for  TLSv1.3.  The format of the
21732c593315Sopenharmony_ci              string is described in OpenSSL ciphers(1).
21742c593315Sopenharmony_ci              Default: )"
21752c593315Sopenharmony_ci      << config.tls13_ciphers << R"(
21762c593315Sopenharmony_ci  -p, --no-tls-proto=<PROTOID>
21772c593315Sopenharmony_ci              Specify ALPN identifier of the  protocol to be used when
21782c593315Sopenharmony_ci              accessing http URI without SSL/TLS.
21792c593315Sopenharmony_ci              Available protocols: )"
21802c593315Sopenharmony_ci      << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and )" << NGHTTP2_H1_1 << R"(
21812c593315Sopenharmony_ci              Default: )"
21822c593315Sopenharmony_ci      << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
21832c593315Sopenharmony_ci  -d, --data=<PATH>
21842c593315Sopenharmony_ci              Post FILE to  server.  The request method  is changed to
21852c593315Sopenharmony_ci              POST.   For  http/1.1 connection,  if  -d  is used,  the
21862c593315Sopenharmony_ci              maximum number of in-flight pipelined requests is set to
21872c593315Sopenharmony_ci              1.
21882c593315Sopenharmony_ci  -r, --rate=<N>
21892c593315Sopenharmony_ci              Specifies  the  fixed  rate  at  which  connections  are
21902c593315Sopenharmony_ci              created.   The   rate  must   be  a   positive  integer,
21912c593315Sopenharmony_ci              representing the  number of  connections to be  made per
21922c593315Sopenharmony_ci              rate period.   The maximum  number of connections  to be
21932c593315Sopenharmony_ci              made  is  given  in  -c   option.   This  rate  will  be
21942c593315Sopenharmony_ci              distributed among  threads as  evenly as  possible.  For
21952c593315Sopenharmony_ci              example,  with   -t2  and   -r4,  each  thread   gets  2
21962c593315Sopenharmony_ci              connections per period.  When the rate is 0, the program
21972c593315Sopenharmony_ci              will run  as it  normally does, creating  connections at
21982c593315Sopenharmony_ci              whatever variable rate it  wants.  The default value for
21992c593315Sopenharmony_ci              this option is 0.  -r and -D are mutually exclusive.
22002c593315Sopenharmony_ci  --rate-period=<DURATION>
22012c593315Sopenharmony_ci              Specifies the time  period between creating connections.
22022c593315Sopenharmony_ci              The period  must be a positive  number, representing the
22032c593315Sopenharmony_ci              length of the period in time.  This option is ignored if
22042c593315Sopenharmony_ci              the rate option is not used.  The default value for this
22052c593315Sopenharmony_ci              option is 1s.
22062c593315Sopenharmony_ci  -D, --duration=<DURATION>
22072c593315Sopenharmony_ci              Specifies the main duration for the measurements in case
22082c593315Sopenharmony_ci              of timing-based  benchmarking.  -D  and -r  are mutually
22092c593315Sopenharmony_ci              exclusive.
22102c593315Sopenharmony_ci  --warm-up-time=<DURATION>
22112c593315Sopenharmony_ci              Specifies the  time  period  before  starting the actual
22122c593315Sopenharmony_ci              measurements, in  case  of  timing-based benchmarking.
22132c593315Sopenharmony_ci              Needs to provided along with -D option.
22142c593315Sopenharmony_ci  -T, --connection-active-timeout=<DURATION>
22152c593315Sopenharmony_ci              Specifies  the maximum  time that  h2load is  willing to
22162c593315Sopenharmony_ci              keep a  connection open,  regardless of the  activity on
22172c593315Sopenharmony_ci              said connection.  <DURATION> must be a positive integer,
22182c593315Sopenharmony_ci              specifying the amount of time  to wait.  When no timeout
22192c593315Sopenharmony_ci              value is  set (either  active or inactive),  h2load will
22202c593315Sopenharmony_ci              keep  a  connection  open indefinitely,  waiting  for  a
22212c593315Sopenharmony_ci              response.
22222c593315Sopenharmony_ci  -N, --connection-inactivity-timeout=<DURATION>
22232c593315Sopenharmony_ci              Specifies the amount  of time that h2load  is willing to
22242c593315Sopenharmony_ci              wait to see activity  on a given connection.  <DURATION>
22252c593315Sopenharmony_ci              must  be a  positive integer,  specifying the  amount of
22262c593315Sopenharmony_ci              time  to wait.   When no  timeout value  is set  (either
22272c593315Sopenharmony_ci              active or inactive), h2load  will keep a connection open
22282c593315Sopenharmony_ci              indefinitely, waiting for a response.
22292c593315Sopenharmony_ci  --timing-script-file=<PATH>
22302c593315Sopenharmony_ci              Path of a file containing one or more lines separated by
22312c593315Sopenharmony_ci              EOLs.  Each script line is composed of two tab-separated
22322c593315Sopenharmony_ci              fields.  The first field represents the time offset from
22332c593315Sopenharmony_ci              the start of execution, expressed as a positive value of
22342c593315Sopenharmony_ci              milliseconds  with microsecond  resolution.  The  second
22352c593315Sopenharmony_ci              field represents the URI.  This option will disable URIs
22362c593315Sopenharmony_ci              getting from  command-line.  If '-' is  given as <PATH>,
22372c593315Sopenharmony_ci              script lines will be read  from stdin.  Script lines are
22382c593315Sopenharmony_ci              used in order for each client.   If -n is given, it must
22392c593315Sopenharmony_ci              be less  than or  equal to the  number of  script lines,
22402c593315Sopenharmony_ci              larger values are clamped to the number of script lines.
22412c593315Sopenharmony_ci              If -n is not given,  the number of requests will default
22422c593315Sopenharmony_ci              to the  number of  script lines.   The scheme,  host and
22432c593315Sopenharmony_ci              port defined in  the first URI are  used solely.  Values
22442c593315Sopenharmony_ci              contained  in  other  URIs,  if  present,  are  ignored.
22452c593315Sopenharmony_ci              Definition of a  base URI overrides all  scheme, host or
22462c593315Sopenharmony_ci              port   values.   --timing-script-file   and  --rps   are
22472c593315Sopenharmony_ci              mutually exclusive.
22482c593315Sopenharmony_ci  -B, --base-uri=(<URI>|unix:<PATH>)
22492c593315Sopenharmony_ci              Specify URI from which the scheme, host and port will be
22502c593315Sopenharmony_ci              used  for  all requests.   The  base  URI overrides  all
22512c593315Sopenharmony_ci              values  defined either  at  the command  line or  inside
22522c593315Sopenharmony_ci              input files.  If argument  starts with "unix:", then the
22532c593315Sopenharmony_ci              rest  of the  argument will  be treated  as UNIX  domain
22542c593315Sopenharmony_ci              socket path.   The connection is made  through that path
22552c593315Sopenharmony_ci              instead of TCP.   In this case, scheme  is inferred from
22562c593315Sopenharmony_ci              the first  URI appeared  in the  command line  or inside
22572c593315Sopenharmony_ci              input files as usual.
22582c593315Sopenharmony_ci  --npn-list=<LIST>
22592c593315Sopenharmony_ci              Comma delimited list of  ALPN protocol identifier sorted
22602c593315Sopenharmony_ci              in the  order of preference.  That  means most desirable
22612c593315Sopenharmony_ci              protocol comes  first.  This  is used  in both  ALPN and
22622c593315Sopenharmony_ci              NPN.  The parameter must be  delimited by a single comma
22632c593315Sopenharmony_ci              only  and any  white spaces  are  treated as  a part  of
22642c593315Sopenharmony_ci              protocol string.
22652c593315Sopenharmony_ci              Default: )"
22662c593315Sopenharmony_ci      << DEFAULT_NPN_LIST << R"(
22672c593315Sopenharmony_ci  --h1        Short        hand         for        --npn-list=http/1.1
22682c593315Sopenharmony_ci              --no-tls-proto=http/1.1,    which   effectively    force
22692c593315Sopenharmony_ci              http/1.1 for both http and https URI.
22702c593315Sopenharmony_ci  --header-table-size=<SIZE>
22712c593315Sopenharmony_ci              Specify decoder header table size.
22722c593315Sopenharmony_ci              Default: )"
22732c593315Sopenharmony_ci      << util::utos_unit(config.header_table_size) << R"(
22742c593315Sopenharmony_ci  --encoder-header-table-size=<SIZE>
22752c593315Sopenharmony_ci              Specify encoder header table size.  The decoder (server)
22762c593315Sopenharmony_ci              specifies  the maximum  dynamic table  size it  accepts.
22772c593315Sopenharmony_ci              Then the negotiated dynamic table size is the minimum of
22782c593315Sopenharmony_ci              this option value and the value which server specified.
22792c593315Sopenharmony_ci              Default: )"
22802c593315Sopenharmony_ci      << util::utos_unit(config.encoder_header_table_size) << R"(
22812c593315Sopenharmony_ci  --log-file=<PATH>
22822c593315Sopenharmony_ci              Write per-request information to a file as tab-separated
22832c593315Sopenharmony_ci              columns: start  time as  microseconds since  epoch; HTTP
22842c593315Sopenharmony_ci              status code;  microseconds until end of  response.  More
22852c593315Sopenharmony_ci              columns may be added later.  Rows are ordered by end-of-
22862c593315Sopenharmony_ci              response  time when  using  one worker  thread, but  may
22872c593315Sopenharmony_ci              appear slightly  out of order with  multiple threads due
22882c593315Sopenharmony_ci              to buffering.  Status code is -1 for failed streams.
22892c593315Sopenharmony_ci  --qlog-file-base=<PATH>
22902c593315Sopenharmony_ci              Enable qlog output and specify base file name for qlogs.
22912c593315Sopenharmony_ci              Qlog is emitted  for each connection.  For  a given base
22922c593315Sopenharmony_ci              name   "base",    each   output   file    name   becomes
22932c593315Sopenharmony_ci              "base.M.N.sqlog" where M is worker ID and N is client ID
22942c593315Sopenharmony_ci              (e.g. "base.0.3.sqlog").  Only effective in QUIC runs.
22952c593315Sopenharmony_ci  --connect-to=<HOST>[:<PORT>]
22962c593315Sopenharmony_ci              Host and port to connect  instead of using the authority
22972c593315Sopenharmony_ci              in <URI>.
22982c593315Sopenharmony_ci  --rps=<N>   Specify request  per second for each  client.  --rps and
22992c593315Sopenharmony_ci              --timing-script-file are mutually exclusive.
23002c593315Sopenharmony_ci  --groups=<GROUPS>
23012c593315Sopenharmony_ci              Specify the supported groups.
23022c593315Sopenharmony_ci              Default: )"
23032c593315Sopenharmony_ci      << config.groups << R"(
23042c593315Sopenharmony_ci  --no-udp-gso
23052c593315Sopenharmony_ci              Disable UDP GSO.
23062c593315Sopenharmony_ci  --max-udp-payload-size=<SIZE>
23072c593315Sopenharmony_ci              Specify the maximum outgoing UDP datagram payload size.
23082c593315Sopenharmony_ci  --ktls      Enable ktls.
23092c593315Sopenharmony_ci  -v, --verbose
23102c593315Sopenharmony_ci              Output debug information.
23112c593315Sopenharmony_ci  --version   Display version information and exit.
23122c593315Sopenharmony_ci  -h, --help  Display this help and exit.
23132c593315Sopenharmony_ci
23142c593315Sopenharmony_ci--
23152c593315Sopenharmony_ci
23162c593315Sopenharmony_ci  The <SIZE> argument is an integer and an optional unit (e.g., 10K is
23172c593315Sopenharmony_ci  10 * 1024).  Units are K, M and G (powers of 1024).
23182c593315Sopenharmony_ci
23192c593315Sopenharmony_ci  The <DURATION> argument is an integer and an optional unit (e.g., 1s
23202c593315Sopenharmony_ci  is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
23212c593315Sopenharmony_ci  (hours, minutes, seconds and milliseconds, respectively).  If a unit
23222c593315Sopenharmony_ci  is omitted, a second is used as unit.)"
23232c593315Sopenharmony_ci      << std::endl;
23242c593315Sopenharmony_ci}
23252c593315Sopenharmony_ci} // namespace
23262c593315Sopenharmony_ci
23272c593315Sopenharmony_ciint main(int argc, char **argv) {
23282c593315Sopenharmony_ci  tls::libssl_init();
23292c593315Sopenharmony_ci
23302c593315Sopenharmony_ci#ifndef NOTHREADS
23312c593315Sopenharmony_ci  tls::LibsslGlobalLock lock;
23322c593315Sopenharmony_ci#endif // NOTHREADS
23332c593315Sopenharmony_ci
23342c593315Sopenharmony_ci  std::string datafile;
23352c593315Sopenharmony_ci  std::string logfile;
23362c593315Sopenharmony_ci  std::string qlog_base;
23372c593315Sopenharmony_ci  bool nreqs_set_manually = false;
23382c593315Sopenharmony_ci  while (1) {
23392c593315Sopenharmony_ci    static int flag = 0;
23402c593315Sopenharmony_ci    constexpr static option long_options[] = {
23412c593315Sopenharmony_ci        {"requests", required_argument, nullptr, 'n'},
23422c593315Sopenharmony_ci        {"clients", required_argument, nullptr, 'c'},
23432c593315Sopenharmony_ci        {"data", required_argument, nullptr, 'd'},
23442c593315Sopenharmony_ci        {"threads", required_argument, nullptr, 't'},
23452c593315Sopenharmony_ci        {"max-concurrent-streams", required_argument, nullptr, 'm'},
23462c593315Sopenharmony_ci        {"window-bits", required_argument, nullptr, 'w'},
23472c593315Sopenharmony_ci        {"max-frame-size", required_argument, nullptr, 'f'},
23482c593315Sopenharmony_ci        {"connection-window-bits", required_argument, nullptr, 'W'},
23492c593315Sopenharmony_ci        {"input-file", required_argument, nullptr, 'i'},
23502c593315Sopenharmony_ci        {"header", required_argument, nullptr, 'H'},
23512c593315Sopenharmony_ci        {"no-tls-proto", required_argument, nullptr, 'p'},
23522c593315Sopenharmony_ci        {"verbose", no_argument, nullptr, 'v'},
23532c593315Sopenharmony_ci        {"help", no_argument, nullptr, 'h'},
23542c593315Sopenharmony_ci        {"version", no_argument, &flag, 1},
23552c593315Sopenharmony_ci        {"ciphers", required_argument, &flag, 2},
23562c593315Sopenharmony_ci        {"rate", required_argument, nullptr, 'r'},
23572c593315Sopenharmony_ci        {"connection-active-timeout", required_argument, nullptr, 'T'},
23582c593315Sopenharmony_ci        {"connection-inactivity-timeout", required_argument, nullptr, 'N'},
23592c593315Sopenharmony_ci        {"duration", required_argument, nullptr, 'D'},
23602c593315Sopenharmony_ci        {"timing-script-file", required_argument, &flag, 3},
23612c593315Sopenharmony_ci        {"base-uri", required_argument, nullptr, 'B'},
23622c593315Sopenharmony_ci        {"npn-list", required_argument, &flag, 4},
23632c593315Sopenharmony_ci        {"rate-period", required_argument, &flag, 5},
23642c593315Sopenharmony_ci        {"h1", no_argument, &flag, 6},
23652c593315Sopenharmony_ci        {"header-table-size", required_argument, &flag, 7},
23662c593315Sopenharmony_ci        {"encoder-header-table-size", required_argument, &flag, 8},
23672c593315Sopenharmony_ci        {"warm-up-time", required_argument, &flag, 9},
23682c593315Sopenharmony_ci        {"log-file", required_argument, &flag, 10},
23692c593315Sopenharmony_ci        {"connect-to", required_argument, &flag, 11},
23702c593315Sopenharmony_ci        {"rps", required_argument, &flag, 12},
23712c593315Sopenharmony_ci        {"groups", required_argument, &flag, 13},
23722c593315Sopenharmony_ci        {"tls13-ciphers", required_argument, &flag, 14},
23732c593315Sopenharmony_ci        {"no-udp-gso", no_argument, &flag, 15},
23742c593315Sopenharmony_ci        {"qlog-file-base", required_argument, &flag, 16},
23752c593315Sopenharmony_ci        {"max-udp-payload-size", required_argument, &flag, 17},
23762c593315Sopenharmony_ci        {"ktls", no_argument, &flag, 18},
23772c593315Sopenharmony_ci        {nullptr, 0, nullptr, 0}};
23782c593315Sopenharmony_ci    int option_index = 0;
23792c593315Sopenharmony_ci    auto c = getopt_long(argc, argv,
23802c593315Sopenharmony_ci                         "hvW:c:d:m:n:p:t:w:f:H:i:r:T:N:D:B:", long_options,
23812c593315Sopenharmony_ci                         &option_index);
23822c593315Sopenharmony_ci    if (c == -1) {
23832c593315Sopenharmony_ci      break;
23842c593315Sopenharmony_ci    }
23852c593315Sopenharmony_ci    switch (c) {
23862c593315Sopenharmony_ci    case 'n': {
23872c593315Sopenharmony_ci      auto n = util::parse_uint(optarg);
23882c593315Sopenharmony_ci      if (n == -1) {
23892c593315Sopenharmony_ci        std::cerr << "-n: bad option value: " << optarg << std::endl;
23902c593315Sopenharmony_ci        exit(EXIT_FAILURE);
23912c593315Sopenharmony_ci      }
23922c593315Sopenharmony_ci      config.nreqs = n;
23932c593315Sopenharmony_ci      nreqs_set_manually = true;
23942c593315Sopenharmony_ci      break;
23952c593315Sopenharmony_ci    }
23962c593315Sopenharmony_ci    case 'c': {
23972c593315Sopenharmony_ci      auto n = util::parse_uint(optarg);
23982c593315Sopenharmony_ci      if (n == -1) {
23992c593315Sopenharmony_ci        std::cerr << "-c: bad option value: " << optarg << std::endl;
24002c593315Sopenharmony_ci        exit(EXIT_FAILURE);
24012c593315Sopenharmony_ci      }
24022c593315Sopenharmony_ci      config.nclients = n;
24032c593315Sopenharmony_ci      break;
24042c593315Sopenharmony_ci    }
24052c593315Sopenharmony_ci    case 'd':
24062c593315Sopenharmony_ci      datafile = optarg;
24072c593315Sopenharmony_ci      break;
24082c593315Sopenharmony_ci    case 't': {
24092c593315Sopenharmony_ci#ifdef NOTHREADS
24102c593315Sopenharmony_ci      std::cerr << "-t: WARNING: Threading disabled at build time, "
24112c593315Sopenharmony_ci                << "no threads created." << std::endl;
24122c593315Sopenharmony_ci#else
24132c593315Sopenharmony_ci      auto n = util::parse_uint(optarg);
24142c593315Sopenharmony_ci      if (n == -1) {
24152c593315Sopenharmony_ci        std::cerr << "-t: bad option value: " << optarg << std::endl;
24162c593315Sopenharmony_ci        exit(EXIT_FAILURE);
24172c593315Sopenharmony_ci      }
24182c593315Sopenharmony_ci      config.nthreads = n;
24192c593315Sopenharmony_ci#endif // NOTHREADS
24202c593315Sopenharmony_ci      break;
24212c593315Sopenharmony_ci    }
24222c593315Sopenharmony_ci    case 'm': {
24232c593315Sopenharmony_ci      auto n = util::parse_uint(optarg);
24242c593315Sopenharmony_ci      if (n == -1) {
24252c593315Sopenharmony_ci        std::cerr << "-m: bad option value: " << optarg << std::endl;
24262c593315Sopenharmony_ci        exit(EXIT_FAILURE);
24272c593315Sopenharmony_ci      }
24282c593315Sopenharmony_ci      config.max_concurrent_streams = n;
24292c593315Sopenharmony_ci      break;
24302c593315Sopenharmony_ci    }
24312c593315Sopenharmony_ci    case 'w':
24322c593315Sopenharmony_ci    case 'W': {
24332c593315Sopenharmony_ci      auto n = util::parse_uint(optarg);
24342c593315Sopenharmony_ci      if (n == -1 || n > 30) {
24352c593315Sopenharmony_ci        std::cerr << "-" << static_cast<char>(c)
24362c593315Sopenharmony_ci                  << ": specify the integer in the range [0, 30], inclusive"
24372c593315Sopenharmony_ci                  << std::endl;
24382c593315Sopenharmony_ci        exit(EXIT_FAILURE);
24392c593315Sopenharmony_ci      }
24402c593315Sopenharmony_ci      if (c == 'w') {
24412c593315Sopenharmony_ci        config.window_bits = n;
24422c593315Sopenharmony_ci      } else {
24432c593315Sopenharmony_ci        config.connection_window_bits = n;
24442c593315Sopenharmony_ci      }
24452c593315Sopenharmony_ci      break;
24462c593315Sopenharmony_ci    }
24472c593315Sopenharmony_ci    case 'f': {
24482c593315Sopenharmony_ci      auto n = util::parse_uint_with_unit(optarg);
24492c593315Sopenharmony_ci      if (n == -1) {
24502c593315Sopenharmony_ci        std::cerr << "--max-frame-size: bad option value: " << optarg
24512c593315Sopenharmony_ci                  << std::endl;
24522c593315Sopenharmony_ci        exit(EXIT_FAILURE);
24532c593315Sopenharmony_ci      }
24542c593315Sopenharmony_ci      if (static_cast<uint64_t>(n) < 16_k) {
24552c593315Sopenharmony_ci        std::cerr << "--max-frame-size: minimum 16384" << std::endl;
24562c593315Sopenharmony_ci        exit(EXIT_FAILURE);
24572c593315Sopenharmony_ci      }
24582c593315Sopenharmony_ci      if (static_cast<uint64_t>(n) > 16_m - 1) {
24592c593315Sopenharmony_ci        std::cerr << "--max-frame-size: maximum 16777215" << std::endl;
24602c593315Sopenharmony_ci        exit(EXIT_FAILURE);
24612c593315Sopenharmony_ci      }
24622c593315Sopenharmony_ci      config.max_frame_size = n;
24632c593315Sopenharmony_ci      break;
24642c593315Sopenharmony_ci    }
24652c593315Sopenharmony_ci    case 'H': {
24662c593315Sopenharmony_ci      char *header = optarg;
24672c593315Sopenharmony_ci      // Skip first possible ':' in the header name
24682c593315Sopenharmony_ci      char *value = strchr(optarg + 1, ':');
24692c593315Sopenharmony_ci      if (!value || (header[0] == ':' && header + 1 == value)) {
24702c593315Sopenharmony_ci        std::cerr << "-H: invalid header: " << optarg << std::endl;
24712c593315Sopenharmony_ci        exit(EXIT_FAILURE);
24722c593315Sopenharmony_ci      }
24732c593315Sopenharmony_ci      *value = 0;
24742c593315Sopenharmony_ci      value++;
24752c593315Sopenharmony_ci      while (isspace(*value)) {
24762c593315Sopenharmony_ci        value++;
24772c593315Sopenharmony_ci      }
24782c593315Sopenharmony_ci      if (*value == 0) {
24792c593315Sopenharmony_ci        // This could also be a valid case for suppressing a header
24802c593315Sopenharmony_ci        // similar to curl
24812c593315Sopenharmony_ci        std::cerr << "-H: invalid header - value missing: " << optarg
24822c593315Sopenharmony_ci                  << std::endl;
24832c593315Sopenharmony_ci        exit(EXIT_FAILURE);
24842c593315Sopenharmony_ci      }
24852c593315Sopenharmony_ci      // Note that there is no processing currently to handle multiple
24862c593315Sopenharmony_ci      // message-header fields with the same field name
24872c593315Sopenharmony_ci      config.custom_headers.emplace_back(header, value);
24882c593315Sopenharmony_ci      util::inp_strlower(config.custom_headers.back().name);
24892c593315Sopenharmony_ci      break;
24902c593315Sopenharmony_ci    }
24912c593315Sopenharmony_ci    case 'i':
24922c593315Sopenharmony_ci      config.ifile = optarg;
24932c593315Sopenharmony_ci      break;
24942c593315Sopenharmony_ci    case 'p': {
24952c593315Sopenharmony_ci      auto proto = StringRef{optarg};
24962c593315Sopenharmony_ci      if (util::strieq(StringRef::from_lit(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID),
24972c593315Sopenharmony_ci                       proto)) {
24982c593315Sopenharmony_ci        config.no_tls_proto = Config::PROTO_HTTP2;
24992c593315Sopenharmony_ci      } else if (util::strieq(NGHTTP2_H1_1, proto)) {
25002c593315Sopenharmony_ci        config.no_tls_proto = Config::PROTO_HTTP1_1;
25012c593315Sopenharmony_ci      } else {
25022c593315Sopenharmony_ci        std::cerr << "-p: unsupported protocol " << proto << std::endl;
25032c593315Sopenharmony_ci        exit(EXIT_FAILURE);
25042c593315Sopenharmony_ci      }
25052c593315Sopenharmony_ci      break;
25062c593315Sopenharmony_ci    }
25072c593315Sopenharmony_ci    case 'r': {
25082c593315Sopenharmony_ci      auto n = util::parse_uint(optarg);
25092c593315Sopenharmony_ci      if (n == -1) {
25102c593315Sopenharmony_ci        std::cerr << "-r: bad option value: " << optarg << std::endl;
25112c593315Sopenharmony_ci        exit(EXIT_FAILURE);
25122c593315Sopenharmony_ci      }
25132c593315Sopenharmony_ci      if (n == 0) {
25142c593315Sopenharmony_ci        std::cerr << "-r: the rate at which connections are made "
25152c593315Sopenharmony_ci                  << "must be positive." << std::endl;
25162c593315Sopenharmony_ci        exit(EXIT_FAILURE);
25172c593315Sopenharmony_ci      }
25182c593315Sopenharmony_ci      config.rate = n;
25192c593315Sopenharmony_ci      break;
25202c593315Sopenharmony_ci    }
25212c593315Sopenharmony_ci    case 'T':
25222c593315Sopenharmony_ci      config.conn_active_timeout = util::parse_duration_with_unit(optarg);
25232c593315Sopenharmony_ci      if (!std::isfinite(config.conn_active_timeout)) {
25242c593315Sopenharmony_ci        std::cerr << "-T: bad value for the conn_active_timeout wait time: "
25252c593315Sopenharmony_ci                  << optarg << std::endl;
25262c593315Sopenharmony_ci        exit(EXIT_FAILURE);
25272c593315Sopenharmony_ci      }
25282c593315Sopenharmony_ci      break;
25292c593315Sopenharmony_ci    case 'N':
25302c593315Sopenharmony_ci      config.conn_inactivity_timeout = util::parse_duration_with_unit(optarg);
25312c593315Sopenharmony_ci      if (!std::isfinite(config.conn_inactivity_timeout)) {
25322c593315Sopenharmony_ci        std::cerr << "-N: bad value for the conn_inactivity_timeout wait time: "
25332c593315Sopenharmony_ci                  << optarg << std::endl;
25342c593315Sopenharmony_ci        exit(EXIT_FAILURE);
25352c593315Sopenharmony_ci      }
25362c593315Sopenharmony_ci      break;
25372c593315Sopenharmony_ci    case 'B': {
25382c593315Sopenharmony_ci      auto arg = StringRef{optarg};
25392c593315Sopenharmony_ci      config.base_uri = "";
25402c593315Sopenharmony_ci      config.base_uri_unix = false;
25412c593315Sopenharmony_ci
25422c593315Sopenharmony_ci      if (util::istarts_with_l(arg, UNIX_PATH_PREFIX)) {
25432c593315Sopenharmony_ci        // UNIX domain socket path
25442c593315Sopenharmony_ci        sockaddr_un un;
25452c593315Sopenharmony_ci
25462c593315Sopenharmony_ci        auto path = StringRef{std::begin(arg) + str_size(UNIX_PATH_PREFIX),
25472c593315Sopenharmony_ci                              std::end(arg)};
25482c593315Sopenharmony_ci
25492c593315Sopenharmony_ci        if (path.size() == 0 || path.size() + 1 > sizeof(un.sun_path)) {
25502c593315Sopenharmony_ci          std::cerr << "--base-uri: invalid UNIX domain socket path: " << arg
25512c593315Sopenharmony_ci                    << std::endl;
25522c593315Sopenharmony_ci          exit(EXIT_FAILURE);
25532c593315Sopenharmony_ci        }
25542c593315Sopenharmony_ci
25552c593315Sopenharmony_ci        config.base_uri_unix = true;
25562c593315Sopenharmony_ci
25572c593315Sopenharmony_ci        auto &unix_addr = config.unix_addr;
25582c593315Sopenharmony_ci        std::copy(std::begin(path), std::end(path), unix_addr.sun_path);
25592c593315Sopenharmony_ci        unix_addr.sun_path[path.size()] = '\0';
25602c593315Sopenharmony_ci        unix_addr.sun_family = AF_UNIX;
25612c593315Sopenharmony_ci
25622c593315Sopenharmony_ci        break;
25632c593315Sopenharmony_ci      }
25642c593315Sopenharmony_ci
25652c593315Sopenharmony_ci      if (!parse_base_uri(arg)) {
25662c593315Sopenharmony_ci        std::cerr << "--base-uri: invalid base URI: " << arg << std::endl;
25672c593315Sopenharmony_ci        exit(EXIT_FAILURE);
25682c593315Sopenharmony_ci      }
25692c593315Sopenharmony_ci
25702c593315Sopenharmony_ci      config.base_uri = arg.str();
25712c593315Sopenharmony_ci      break;
25722c593315Sopenharmony_ci    }
25732c593315Sopenharmony_ci    case 'D':
25742c593315Sopenharmony_ci      config.duration = util::parse_duration_with_unit(optarg);
25752c593315Sopenharmony_ci      if (!std::isfinite(config.duration)) {
25762c593315Sopenharmony_ci        std::cerr << "-D: value error " << optarg << std::endl;
25772c593315Sopenharmony_ci        exit(EXIT_FAILURE);
25782c593315Sopenharmony_ci      }
25792c593315Sopenharmony_ci      break;
25802c593315Sopenharmony_ci    case 'v':
25812c593315Sopenharmony_ci      config.verbose = true;
25822c593315Sopenharmony_ci      break;
25832c593315Sopenharmony_ci    case 'h':
25842c593315Sopenharmony_ci      print_help(std::cout);
25852c593315Sopenharmony_ci      exit(EXIT_SUCCESS);
25862c593315Sopenharmony_ci    case '?':
25872c593315Sopenharmony_ci      util::show_candidates(argv[optind - 1], long_options);
25882c593315Sopenharmony_ci      exit(EXIT_FAILURE);
25892c593315Sopenharmony_ci    case 0:
25902c593315Sopenharmony_ci      switch (flag) {
25912c593315Sopenharmony_ci      case 1:
25922c593315Sopenharmony_ci        // version option
25932c593315Sopenharmony_ci        print_version(std::cout);
25942c593315Sopenharmony_ci        exit(EXIT_SUCCESS);
25952c593315Sopenharmony_ci      case 2:
25962c593315Sopenharmony_ci        // ciphers option
25972c593315Sopenharmony_ci        config.ciphers = optarg;
25982c593315Sopenharmony_ci        break;
25992c593315Sopenharmony_ci      case 3:
26002c593315Sopenharmony_ci        // timing-script option
26012c593315Sopenharmony_ci        config.ifile = optarg;
26022c593315Sopenharmony_ci        config.timing_script = true;
26032c593315Sopenharmony_ci        break;
26042c593315Sopenharmony_ci      case 4:
26052c593315Sopenharmony_ci        // npn-list option
26062c593315Sopenharmony_ci        config.npn_list = util::parse_config_str_list(StringRef{optarg});
26072c593315Sopenharmony_ci        break;
26082c593315Sopenharmony_ci      case 5:
26092c593315Sopenharmony_ci        // rate-period
26102c593315Sopenharmony_ci        config.rate_period = util::parse_duration_with_unit(optarg);
26112c593315Sopenharmony_ci        if (!std::isfinite(config.rate_period)) {
26122c593315Sopenharmony_ci          std::cerr << "--rate-period: value error " << optarg << std::endl;
26132c593315Sopenharmony_ci          exit(EXIT_FAILURE);
26142c593315Sopenharmony_ci        }
26152c593315Sopenharmony_ci        break;
26162c593315Sopenharmony_ci      case 6:
26172c593315Sopenharmony_ci        // --h1
26182c593315Sopenharmony_ci        config.npn_list =
26192c593315Sopenharmony_ci            util::parse_config_str_list(StringRef::from_lit("http/1.1"));
26202c593315Sopenharmony_ci        config.no_tls_proto = Config::PROTO_HTTP1_1;
26212c593315Sopenharmony_ci        break;
26222c593315Sopenharmony_ci      case 7:
26232c593315Sopenharmony_ci        // --header-table-size
26242c593315Sopenharmony_ci        if (parse_header_table_size(config.header_table_size,
26252c593315Sopenharmony_ci                                    "header-table-size", optarg) != 0) {
26262c593315Sopenharmony_ci          exit(EXIT_FAILURE);
26272c593315Sopenharmony_ci        }
26282c593315Sopenharmony_ci        break;
26292c593315Sopenharmony_ci      case 8:
26302c593315Sopenharmony_ci        // --encoder-header-table-size
26312c593315Sopenharmony_ci        if (parse_header_table_size(config.encoder_header_table_size,
26322c593315Sopenharmony_ci                                    "encoder-header-table-size", optarg) != 0) {
26332c593315Sopenharmony_ci          exit(EXIT_FAILURE);
26342c593315Sopenharmony_ci        }
26352c593315Sopenharmony_ci        break;
26362c593315Sopenharmony_ci      case 9:
26372c593315Sopenharmony_ci        // --warm-up-time
26382c593315Sopenharmony_ci        config.warm_up_time = util::parse_duration_with_unit(optarg);
26392c593315Sopenharmony_ci        if (!std::isfinite(config.warm_up_time)) {
26402c593315Sopenharmony_ci          std::cerr << "--warm-up-time: value error " << optarg << std::endl;
26412c593315Sopenharmony_ci          exit(EXIT_FAILURE);
26422c593315Sopenharmony_ci        }
26432c593315Sopenharmony_ci        break;
26442c593315Sopenharmony_ci      case 10:
26452c593315Sopenharmony_ci        // --log-file
26462c593315Sopenharmony_ci        logfile = optarg;
26472c593315Sopenharmony_ci        break;
26482c593315Sopenharmony_ci      case 11: {
26492c593315Sopenharmony_ci        // --connect-to
26502c593315Sopenharmony_ci        auto p = util::split_hostport(StringRef{optarg});
26512c593315Sopenharmony_ci        int64_t port = 0;
26522c593315Sopenharmony_ci        if (p.first.empty() ||
26532c593315Sopenharmony_ci            (!p.second.empty() && (port = util::parse_uint(p.second)) == -1)) {
26542c593315Sopenharmony_ci          std::cerr << "--connect-to: Invalid value " << optarg << std::endl;
26552c593315Sopenharmony_ci          exit(EXIT_FAILURE);
26562c593315Sopenharmony_ci        }
26572c593315Sopenharmony_ci        config.connect_to_host = p.first.str();
26582c593315Sopenharmony_ci        config.connect_to_port = port;
26592c593315Sopenharmony_ci        break;
26602c593315Sopenharmony_ci      }
26612c593315Sopenharmony_ci      case 12: {
26622c593315Sopenharmony_ci        char *end;
26632c593315Sopenharmony_ci        auto v = std::strtod(optarg, &end);
26642c593315Sopenharmony_ci        if (end == optarg || *end != '\0' || !std::isfinite(v) ||
26652c593315Sopenharmony_ci            1. / v < 1e-6) {
26662c593315Sopenharmony_ci          std::cerr << "--rps: Invalid value " << optarg << std::endl;
26672c593315Sopenharmony_ci          exit(EXIT_FAILURE);
26682c593315Sopenharmony_ci        }
26692c593315Sopenharmony_ci        config.rps = v;
26702c593315Sopenharmony_ci        break;
26712c593315Sopenharmony_ci      }
26722c593315Sopenharmony_ci      case 13:
26732c593315Sopenharmony_ci        // --groups
26742c593315Sopenharmony_ci        config.groups = optarg;
26752c593315Sopenharmony_ci        break;
26762c593315Sopenharmony_ci      case 14:
26772c593315Sopenharmony_ci        // --tls13-ciphers
26782c593315Sopenharmony_ci        config.tls13_ciphers = optarg;
26792c593315Sopenharmony_ci        break;
26802c593315Sopenharmony_ci      case 15:
26812c593315Sopenharmony_ci        // --no-udp-gso
26822c593315Sopenharmony_ci        config.no_udp_gso = true;
26832c593315Sopenharmony_ci        break;
26842c593315Sopenharmony_ci      case 16:
26852c593315Sopenharmony_ci        // --qlog-file-base
26862c593315Sopenharmony_ci        qlog_base = optarg;
26872c593315Sopenharmony_ci        break;
26882c593315Sopenharmony_ci      case 17: {
26892c593315Sopenharmony_ci        // --max-udp-payload-size
26902c593315Sopenharmony_ci        auto n = util::parse_uint_with_unit(optarg);
26912c593315Sopenharmony_ci        if (n == -1) {
26922c593315Sopenharmony_ci          std::cerr << "--max-udp-payload-size: bad option value: " << optarg
26932c593315Sopenharmony_ci                    << std::endl;
26942c593315Sopenharmony_ci          exit(EXIT_FAILURE);
26952c593315Sopenharmony_ci        }
26962c593315Sopenharmony_ci        if (static_cast<uint64_t>(n) > 64_k) {
26972c593315Sopenharmony_ci          std::cerr << "--max-udp-payload-size: must not exceed 65536"
26982c593315Sopenharmony_ci                    << std::endl;
26992c593315Sopenharmony_ci          exit(EXIT_FAILURE);
27002c593315Sopenharmony_ci        }
27012c593315Sopenharmony_ci        config.max_udp_payload_size = n;
27022c593315Sopenharmony_ci        break;
27032c593315Sopenharmony_ci      }
27042c593315Sopenharmony_ci      case 18:
27052c593315Sopenharmony_ci        // --ktls
27062c593315Sopenharmony_ci        config.ktls = true;
27072c593315Sopenharmony_ci        break;
27082c593315Sopenharmony_ci      }
27092c593315Sopenharmony_ci      break;
27102c593315Sopenharmony_ci    default:
27112c593315Sopenharmony_ci      break;
27122c593315Sopenharmony_ci    }
27132c593315Sopenharmony_ci  }
27142c593315Sopenharmony_ci
27152c593315Sopenharmony_ci  if (argc == optind) {
27162c593315Sopenharmony_ci    if (config.ifile.empty()) {
27172c593315Sopenharmony_ci      std::cerr << "no URI or input file given" << std::endl;
27182c593315Sopenharmony_ci      exit(EXIT_FAILURE);
27192c593315Sopenharmony_ci    }
27202c593315Sopenharmony_ci  }
27212c593315Sopenharmony_ci
27222c593315Sopenharmony_ci  if (config.nclients == 0) {
27232c593315Sopenharmony_ci    std::cerr << "-c: the number of clients must be strictly greater than 0."
27242c593315Sopenharmony_ci              << std::endl;
27252c593315Sopenharmony_ci    exit(EXIT_FAILURE);
27262c593315Sopenharmony_ci  }
27272c593315Sopenharmony_ci
27282c593315Sopenharmony_ci  if (config.npn_list.empty()) {
27292c593315Sopenharmony_ci    config.npn_list =
27302c593315Sopenharmony_ci        util::parse_config_str_list(StringRef::from_lit(DEFAULT_NPN_LIST));
27312c593315Sopenharmony_ci  }
27322c593315Sopenharmony_ci
27332c593315Sopenharmony_ci  // serialize the APLN tokens
27342c593315Sopenharmony_ci  for (auto &proto : config.npn_list) {
27352c593315Sopenharmony_ci    proto.insert(proto.begin(), static_cast<unsigned char>(proto.size()));
27362c593315Sopenharmony_ci  }
27372c593315Sopenharmony_ci
27382c593315Sopenharmony_ci  std::vector<std::string> reqlines;
27392c593315Sopenharmony_ci
27402c593315Sopenharmony_ci  if (config.ifile.empty()) {
27412c593315Sopenharmony_ci    std::vector<std::string> uris;
27422c593315Sopenharmony_ci    std::copy(&argv[optind], &argv[argc], std::back_inserter(uris));
27432c593315Sopenharmony_ci    reqlines = parse_uris(std::begin(uris), std::end(uris));
27442c593315Sopenharmony_ci  } else {
27452c593315Sopenharmony_ci    std::vector<std::string> uris;
27462c593315Sopenharmony_ci    if (!config.timing_script) {
27472c593315Sopenharmony_ci      if (config.ifile == "-") {
27482c593315Sopenharmony_ci        uris = read_uri_from_file(std::cin);
27492c593315Sopenharmony_ci      } else {
27502c593315Sopenharmony_ci        std::ifstream infile(config.ifile);
27512c593315Sopenharmony_ci        if (!infile) {
27522c593315Sopenharmony_ci          std::cerr << "cannot read input file: " << config.ifile << std::endl;
27532c593315Sopenharmony_ci          exit(EXIT_FAILURE);
27542c593315Sopenharmony_ci        }
27552c593315Sopenharmony_ci
27562c593315Sopenharmony_ci        uris = read_uri_from_file(infile);
27572c593315Sopenharmony_ci      }
27582c593315Sopenharmony_ci    } else {
27592c593315Sopenharmony_ci      if (config.ifile == "-") {
27602c593315Sopenharmony_ci        read_script_from_file(std::cin, config.timings, uris);
27612c593315Sopenharmony_ci      } else {
27622c593315Sopenharmony_ci        std::ifstream infile(config.ifile);
27632c593315Sopenharmony_ci        if (!infile) {
27642c593315Sopenharmony_ci          std::cerr << "cannot read input file: " << config.ifile << std::endl;
27652c593315Sopenharmony_ci          exit(EXIT_FAILURE);
27662c593315Sopenharmony_ci        }
27672c593315Sopenharmony_ci
27682c593315Sopenharmony_ci        read_script_from_file(infile, config.timings, uris);
27692c593315Sopenharmony_ci      }
27702c593315Sopenharmony_ci
27712c593315Sopenharmony_ci      if (nreqs_set_manually) {
27722c593315Sopenharmony_ci        if (config.nreqs > uris.size()) {
27732c593315Sopenharmony_ci          std::cerr << "-n: the number of requests must be less than or equal "
27742c593315Sopenharmony_ci                       "to the number of timing script entries. Setting number "
27752c593315Sopenharmony_ci                       "of requests to "
27762c593315Sopenharmony_ci                    << uris.size() << std::endl;
27772c593315Sopenharmony_ci
27782c593315Sopenharmony_ci          config.nreqs = uris.size();
27792c593315Sopenharmony_ci        }
27802c593315Sopenharmony_ci      } else {
27812c593315Sopenharmony_ci        config.nreqs = uris.size();
27822c593315Sopenharmony_ci      }
27832c593315Sopenharmony_ci    }
27842c593315Sopenharmony_ci
27852c593315Sopenharmony_ci    reqlines = parse_uris(std::begin(uris), std::end(uris));
27862c593315Sopenharmony_ci  }
27872c593315Sopenharmony_ci
27882c593315Sopenharmony_ci  if (reqlines.empty()) {
27892c593315Sopenharmony_ci    std::cerr << "No URI given" << std::endl;
27902c593315Sopenharmony_ci    exit(EXIT_FAILURE);
27912c593315Sopenharmony_ci  }
27922c593315Sopenharmony_ci
27932c593315Sopenharmony_ci  if (config.is_timing_based_mode() && config.is_rate_mode()) {
27942c593315Sopenharmony_ci    std::cerr << "-r, -D: they are mutually exclusive." << std::endl;
27952c593315Sopenharmony_ci    exit(EXIT_FAILURE);
27962c593315Sopenharmony_ci  }
27972c593315Sopenharmony_ci
27982c593315Sopenharmony_ci  if (config.timing_script && config.rps_enabled()) {
27992c593315Sopenharmony_ci    std::cerr << "--timing-script-file, --rps: they are mutually exclusive."
28002c593315Sopenharmony_ci              << std::endl;
28012c593315Sopenharmony_ci    exit(EXIT_FAILURE);
28022c593315Sopenharmony_ci  }
28032c593315Sopenharmony_ci
28042c593315Sopenharmony_ci  if (config.nreqs == 0 && !config.is_timing_based_mode()) {
28052c593315Sopenharmony_ci    std::cerr << "-n: the number of requests must be strictly greater than 0 "
28062c593315Sopenharmony_ci                 "if timing-based test is not being run."
28072c593315Sopenharmony_ci              << std::endl;
28082c593315Sopenharmony_ci    exit(EXIT_FAILURE);
28092c593315Sopenharmony_ci  }
28102c593315Sopenharmony_ci
28112c593315Sopenharmony_ci  if (config.max_concurrent_streams == 0) {
28122c593315Sopenharmony_ci    std::cerr << "-m: the max concurrent streams must be strictly greater "
28132c593315Sopenharmony_ci              << "than 0." << std::endl;
28142c593315Sopenharmony_ci    exit(EXIT_FAILURE);
28152c593315Sopenharmony_ci  }
28162c593315Sopenharmony_ci
28172c593315Sopenharmony_ci  if (config.nthreads == 0) {
28182c593315Sopenharmony_ci    std::cerr << "-t: the number of threads must be strictly greater than 0."
28192c593315Sopenharmony_ci              << std::endl;
28202c593315Sopenharmony_ci    exit(EXIT_FAILURE);
28212c593315Sopenharmony_ci  }
28222c593315Sopenharmony_ci
28232c593315Sopenharmony_ci  if (config.nthreads > std::thread::hardware_concurrency()) {
28242c593315Sopenharmony_ci    std::cerr << "-t: warning: the number of threads is greater than hardware "
28252c593315Sopenharmony_ci              << "cores." << std::endl;
28262c593315Sopenharmony_ci  }
28272c593315Sopenharmony_ci
28282c593315Sopenharmony_ci  // With timing script, we don't distribute config.nreqs to each
28292c593315Sopenharmony_ci  // client or thread.
28302c593315Sopenharmony_ci  if (!config.timing_script && config.nreqs < config.nclients &&
28312c593315Sopenharmony_ci      !config.is_timing_based_mode()) {
28322c593315Sopenharmony_ci    std::cerr << "-n, -c: the number of requests must be greater than or "
28332c593315Sopenharmony_ci              << "equal to the clients." << std::endl;
28342c593315Sopenharmony_ci    exit(EXIT_FAILURE);
28352c593315Sopenharmony_ci  }
28362c593315Sopenharmony_ci
28372c593315Sopenharmony_ci  if (config.nclients < config.nthreads) {
28382c593315Sopenharmony_ci    std::cerr << "-c, -t: the number of clients must be greater than or equal "
28392c593315Sopenharmony_ci              << "to the number of threads." << std::endl;
28402c593315Sopenharmony_ci    exit(EXIT_FAILURE);
28412c593315Sopenharmony_ci  }
28422c593315Sopenharmony_ci
28432c593315Sopenharmony_ci  if (config.is_timing_based_mode()) {
28442c593315Sopenharmony_ci    config.nreqs = 0;
28452c593315Sopenharmony_ci  }
28462c593315Sopenharmony_ci
28472c593315Sopenharmony_ci  if (config.is_rate_mode()) {
28482c593315Sopenharmony_ci    if (config.rate < config.nthreads) {
28492c593315Sopenharmony_ci      std::cerr << "-r, -t: the connection rate must be greater than or equal "
28502c593315Sopenharmony_ci                << "to the number of threads." << std::endl;
28512c593315Sopenharmony_ci      exit(EXIT_FAILURE);
28522c593315Sopenharmony_ci    }
28532c593315Sopenharmony_ci
28542c593315Sopenharmony_ci    if (config.rate > config.nclients) {
28552c593315Sopenharmony_ci      std::cerr << "-r, -c: the connection rate must be smaller than or equal "
28562c593315Sopenharmony_ci                   "to the number of clients."
28572c593315Sopenharmony_ci                << std::endl;
28582c593315Sopenharmony_ci      exit(EXIT_FAILURE);
28592c593315Sopenharmony_ci    }
28602c593315Sopenharmony_ci  }
28612c593315Sopenharmony_ci
28622c593315Sopenharmony_ci  if (!datafile.empty()) {
28632c593315Sopenharmony_ci    config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY);
28642c593315Sopenharmony_ci    if (config.data_fd == -1) {
28652c593315Sopenharmony_ci      std::cerr << "-d: Could not open file " << datafile << std::endl;
28662c593315Sopenharmony_ci      exit(EXIT_FAILURE);
28672c593315Sopenharmony_ci    }
28682c593315Sopenharmony_ci    struct stat data_stat;
28692c593315Sopenharmony_ci    if (fstat(config.data_fd, &data_stat) == -1) {
28702c593315Sopenharmony_ci      std::cerr << "-d: Could not stat file " << datafile << std::endl;
28712c593315Sopenharmony_ci      exit(EXIT_FAILURE);
28722c593315Sopenharmony_ci    }
28732c593315Sopenharmony_ci    config.data_length = data_stat.st_size;
28742c593315Sopenharmony_ci    auto addr = mmap(nullptr, config.data_length, PROT_READ, MAP_SHARED,
28752c593315Sopenharmony_ci                     config.data_fd, 0);
28762c593315Sopenharmony_ci    if (addr == MAP_FAILED) {
28772c593315Sopenharmony_ci      std::cerr << "-d: Could not mmap file " << datafile << std::endl;
28782c593315Sopenharmony_ci      exit(EXIT_FAILURE);
28792c593315Sopenharmony_ci    }
28802c593315Sopenharmony_ci    config.data = static_cast<uint8_t *>(addr);
28812c593315Sopenharmony_ci  }
28822c593315Sopenharmony_ci
28832c593315Sopenharmony_ci  if (!logfile.empty()) {
28842c593315Sopenharmony_ci    config.log_fd = open(logfile.c_str(), O_WRONLY | O_CREAT | O_APPEND,
28852c593315Sopenharmony_ci                         S_IRUSR | S_IWUSR | S_IRGRP);
28862c593315Sopenharmony_ci    if (config.log_fd == -1) {
28872c593315Sopenharmony_ci      std::cerr << "--log-file: Could not open file " << logfile << std::endl;
28882c593315Sopenharmony_ci      exit(EXIT_FAILURE);
28892c593315Sopenharmony_ci    }
28902c593315Sopenharmony_ci  }
28912c593315Sopenharmony_ci
28922c593315Sopenharmony_ci  if (!qlog_base.empty()) {
28932c593315Sopenharmony_ci    if (!config.is_quic()) {
28942c593315Sopenharmony_ci      std::cerr
28952c593315Sopenharmony_ci          << "Warning: --qlog-file-base: only effective in quic, ignoring."
28962c593315Sopenharmony_ci          << std::endl;
28972c593315Sopenharmony_ci    } else {
28982c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
28992c593315Sopenharmony_ci      config.qlog_file_base = qlog_base;
29002c593315Sopenharmony_ci#endif // ENABLE_HTTP3
29012c593315Sopenharmony_ci    }
29022c593315Sopenharmony_ci  }
29032c593315Sopenharmony_ci
29042c593315Sopenharmony_ci  struct sigaction act {};
29052c593315Sopenharmony_ci  act.sa_handler = SIG_IGN;
29062c593315Sopenharmony_ci  sigaction(SIGPIPE, &act, nullptr);
29072c593315Sopenharmony_ci
29082c593315Sopenharmony_ci  auto ssl_ctx = SSL_CTX_new(TLS_client_method());
29092c593315Sopenharmony_ci  if (!ssl_ctx) {
29102c593315Sopenharmony_ci    std::cerr << "Failed to create SSL_CTX: "
29112c593315Sopenharmony_ci              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
29122c593315Sopenharmony_ci    exit(EXIT_FAILURE);
29132c593315Sopenharmony_ci  }
29142c593315Sopenharmony_ci
29152c593315Sopenharmony_ci  auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
29162c593315Sopenharmony_ci                  SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
29172c593315Sopenharmony_ci                  SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
29182c593315Sopenharmony_ci
29192c593315Sopenharmony_ci#ifdef SSL_OP_ENABLE_KTLS
29202c593315Sopenharmony_ci  if (config.ktls) {
29212c593315Sopenharmony_ci    ssl_opts |= SSL_OP_ENABLE_KTLS;
29222c593315Sopenharmony_ci  }
29232c593315Sopenharmony_ci#endif // SSL_OP_ENABLE_KTLS
29242c593315Sopenharmony_ci
29252c593315Sopenharmony_ci  SSL_CTX_set_options(ssl_ctx, ssl_opts);
29262c593315Sopenharmony_ci  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
29272c593315Sopenharmony_ci  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
29282c593315Sopenharmony_ci
29292c593315Sopenharmony_ci  if (config.is_quic()) {
29302c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
29312c593315Sopenharmony_ci#  ifdef HAVE_LIBNGTCP2_CRYPTO_QUICTLS
29322c593315Sopenharmony_ci    if (ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) {
29332c593315Sopenharmony_ci      std::cerr << "ngtcp2_crypto_quictls_configure_client_context failed"
29342c593315Sopenharmony_ci                << std::endl;
29352c593315Sopenharmony_ci      exit(EXIT_FAILURE);
29362c593315Sopenharmony_ci    }
29372c593315Sopenharmony_ci#  endif // HAVE_LIBNGTCP2_CRYPTO_QUICTLS
29382c593315Sopenharmony_ci#  ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
29392c593315Sopenharmony_ci    if (ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
29402c593315Sopenharmony_ci      std::cerr << "ngtcp2_crypto_boringssl_configure_client_context failed"
29412c593315Sopenharmony_ci                << std::endl;
29422c593315Sopenharmony_ci      exit(EXIT_FAILURE);
29432c593315Sopenharmony_ci    }
29442c593315Sopenharmony_ci#  endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
29452c593315Sopenharmony_ci#endif   // ENABLE_HTTP3
29462c593315Sopenharmony_ci  } else if (nghttp2::tls::ssl_ctx_set_proto_versions(
29472c593315Sopenharmony_ci                 ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
29482c593315Sopenharmony_ci                 nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
29492c593315Sopenharmony_ci    std::cerr << "Could not set TLS versions" << std::endl;
29502c593315Sopenharmony_ci    exit(EXIT_FAILURE);
29512c593315Sopenharmony_ci  }
29522c593315Sopenharmony_ci
29532c593315Sopenharmony_ci  if (SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers.c_str()) == 0) {
29542c593315Sopenharmony_ci    std::cerr << "SSL_CTX_set_cipher_list with " << config.ciphers
29552c593315Sopenharmony_ci              << " failed: " << ERR_error_string(ERR_get_error(), nullptr)
29562c593315Sopenharmony_ci              << std::endl;
29572c593315Sopenharmony_ci    exit(EXIT_FAILURE);
29582c593315Sopenharmony_ci  }
29592c593315Sopenharmony_ci
29602c593315Sopenharmony_ci#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
29612c593315Sopenharmony_ci  if (SSL_CTX_set_ciphersuites(ssl_ctx, config.tls13_ciphers.c_str()) == 0) {
29622c593315Sopenharmony_ci    std::cerr << "SSL_CTX_set_ciphersuites with " << config.tls13_ciphers
29632c593315Sopenharmony_ci              << " failed: " << ERR_error_string(ERR_get_error(), nullptr)
29642c593315Sopenharmony_ci              << std::endl;
29652c593315Sopenharmony_ci    exit(EXIT_FAILURE);
29662c593315Sopenharmony_ci  }
29672c593315Sopenharmony_ci#endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
29682c593315Sopenharmony_ci
29692c593315Sopenharmony_ci#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
29702c593315Sopenharmony_ci  if (SSL_CTX_set1_groups_list(ssl_ctx, config.groups.c_str()) != 1) {
29712c593315Sopenharmony_ci    std::cerr << "SSL_CTX_set1_groups_list failed" << std::endl;
29722c593315Sopenharmony_ci    exit(EXIT_FAILURE);
29732c593315Sopenharmony_ci  }
29742c593315Sopenharmony_ci#else  // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
29752c593315Sopenharmony_ci  if (SSL_CTX_set1_curves_list(ssl_ctx, config.groups.c_str()) != 1) {
29762c593315Sopenharmony_ci    std::cerr << "SSL_CTX_set1_curves_list failed" << std::endl;
29772c593315Sopenharmony_ci    exit(EXIT_FAILURE);
29782c593315Sopenharmony_ci  }
29792c593315Sopenharmony_ci#endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
29802c593315Sopenharmony_ci
29812c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG
29822c593315Sopenharmony_ci  SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
29832c593315Sopenharmony_ci                                   nullptr);
29842c593315Sopenharmony_ci#endif // !OPENSSL_NO_NEXTPROTONEG
29852c593315Sopenharmony_ci
29862c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x10002000L
29872c593315Sopenharmony_ci  std::vector<unsigned char> proto_list;
29882c593315Sopenharmony_ci  for (const auto &proto : config.npn_list) {
29892c593315Sopenharmony_ci    std::copy_n(proto.c_str(), proto.size(), std::back_inserter(proto_list));
29902c593315Sopenharmony_ci  }
29912c593315Sopenharmony_ci
29922c593315Sopenharmony_ci  SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
29932c593315Sopenharmony_ci#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
29942c593315Sopenharmony_ci
29952c593315Sopenharmony_ci#if OPENSSL_1_1_1_API
29962c593315Sopenharmony_ci  auto keylog_filename = getenv("SSLKEYLOGFILE");
29972c593315Sopenharmony_ci  if (keylog_filename) {
29982c593315Sopenharmony_ci    keylog_file.open(keylog_filename, std::ios_base::app);
29992c593315Sopenharmony_ci    if (keylog_file) {
30002c593315Sopenharmony_ci      SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
30012c593315Sopenharmony_ci    }
30022c593315Sopenharmony_ci  }
30032c593315Sopenharmony_ci#endif // OPENSSL_1_1_1_API
30042c593315Sopenharmony_ci
30052c593315Sopenharmony_ci  std::string user_agent = "h2load nghttp2/" NGHTTP2_VERSION;
30062c593315Sopenharmony_ci  Headers shared_nva;
30072c593315Sopenharmony_ci  shared_nva.emplace_back(":scheme", config.scheme);
30082c593315Sopenharmony_ci  if (config.port != config.default_port) {
30092c593315Sopenharmony_ci    shared_nva.emplace_back(":authority",
30102c593315Sopenharmony_ci                            config.host + ":" + util::utos(config.port));
30112c593315Sopenharmony_ci  } else {
30122c593315Sopenharmony_ci    shared_nva.emplace_back(":authority", config.host);
30132c593315Sopenharmony_ci  }
30142c593315Sopenharmony_ci  shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST");
30152c593315Sopenharmony_ci  shared_nva.emplace_back("user-agent", user_agent);
30162c593315Sopenharmony_ci
30172c593315Sopenharmony_ci  // list header fields that can be overridden.
30182c593315Sopenharmony_ci  auto override_hdrs = make_array<std::string>(":authority", ":host", ":method",
30192c593315Sopenharmony_ci                                               ":scheme", "user-agent");
30202c593315Sopenharmony_ci
30212c593315Sopenharmony_ci  for (auto &kv : config.custom_headers) {
30222c593315Sopenharmony_ci    if (std::find(std::begin(override_hdrs), std::end(override_hdrs),
30232c593315Sopenharmony_ci                  kv.name) != std::end(override_hdrs)) {
30242c593315Sopenharmony_ci      // override header
30252c593315Sopenharmony_ci      for (auto &nv : shared_nva) {
30262c593315Sopenharmony_ci        if ((nv.name == ":authority" && kv.name == ":host") ||
30272c593315Sopenharmony_ci            (nv.name == kv.name)) {
30282c593315Sopenharmony_ci          nv.value = kv.value;
30292c593315Sopenharmony_ci        }
30302c593315Sopenharmony_ci      }
30312c593315Sopenharmony_ci    } else {
30322c593315Sopenharmony_ci      // add additional headers
30332c593315Sopenharmony_ci      shared_nva.push_back(kv);
30342c593315Sopenharmony_ci    }
30352c593315Sopenharmony_ci  }
30362c593315Sopenharmony_ci
30372c593315Sopenharmony_ci  std::string content_length_str;
30382c593315Sopenharmony_ci  if (config.data_fd != -1) {
30392c593315Sopenharmony_ci    content_length_str = util::utos(config.data_length);
30402c593315Sopenharmony_ci  }
30412c593315Sopenharmony_ci
30422c593315Sopenharmony_ci  auto method_it =
30432c593315Sopenharmony_ci      std::find_if(std::begin(shared_nva), std::end(shared_nva),
30442c593315Sopenharmony_ci                   [](const Header &nv) { return nv.name == ":method"; });
30452c593315Sopenharmony_ci  assert(method_it != std::end(shared_nva));
30462c593315Sopenharmony_ci
30472c593315Sopenharmony_ci  config.h1reqs.reserve(reqlines.size());
30482c593315Sopenharmony_ci  config.nva.reserve(reqlines.size());
30492c593315Sopenharmony_ci
30502c593315Sopenharmony_ci  for (auto &req : reqlines) {
30512c593315Sopenharmony_ci    // For HTTP/1.1
30522c593315Sopenharmony_ci    auto h1req = (*method_it).value;
30532c593315Sopenharmony_ci    h1req += ' ';
30542c593315Sopenharmony_ci    h1req += req;
30552c593315Sopenharmony_ci    h1req += " HTTP/1.1\r\n";
30562c593315Sopenharmony_ci    for (auto &nv : shared_nva) {
30572c593315Sopenharmony_ci      if (nv.name == ":authority") {
30582c593315Sopenharmony_ci        h1req += "Host: ";
30592c593315Sopenharmony_ci        h1req += nv.value;
30602c593315Sopenharmony_ci        h1req += "\r\n";
30612c593315Sopenharmony_ci        continue;
30622c593315Sopenharmony_ci      }
30632c593315Sopenharmony_ci      if (nv.name[0] == ':') {
30642c593315Sopenharmony_ci        continue;
30652c593315Sopenharmony_ci      }
30662c593315Sopenharmony_ci      h1req += nv.name;
30672c593315Sopenharmony_ci      h1req += ": ";
30682c593315Sopenharmony_ci      h1req += nv.value;
30692c593315Sopenharmony_ci      h1req += "\r\n";
30702c593315Sopenharmony_ci    }
30712c593315Sopenharmony_ci
30722c593315Sopenharmony_ci    if (!content_length_str.empty()) {
30732c593315Sopenharmony_ci      h1req += "Content-Length: ";
30742c593315Sopenharmony_ci      h1req += content_length_str;
30752c593315Sopenharmony_ci      h1req += "\r\n";
30762c593315Sopenharmony_ci    }
30772c593315Sopenharmony_ci    h1req += "\r\n";
30782c593315Sopenharmony_ci
30792c593315Sopenharmony_ci    config.h1reqs.push_back(std::move(h1req));
30802c593315Sopenharmony_ci
30812c593315Sopenharmony_ci    // For nghttp2
30822c593315Sopenharmony_ci    std::vector<nghttp2_nv> nva;
30832c593315Sopenharmony_ci    // 2 for :path, and possible content-length
30842c593315Sopenharmony_ci    nva.reserve(2 + shared_nva.size());
30852c593315Sopenharmony_ci
30862c593315Sopenharmony_ci    nva.push_back(http2::make_nv_ls(":path", req));
30872c593315Sopenharmony_ci
30882c593315Sopenharmony_ci    for (auto &nv : shared_nva) {
30892c593315Sopenharmony_ci      nva.push_back(http2::make_nv(nv.name, nv.value, false));
30902c593315Sopenharmony_ci    }
30912c593315Sopenharmony_ci
30922c593315Sopenharmony_ci    if (!content_length_str.empty()) {
30932c593315Sopenharmony_ci      nva.push_back(http2::make_nv(StringRef::from_lit("content-length"),
30942c593315Sopenharmony_ci                                   StringRef{content_length_str}));
30952c593315Sopenharmony_ci    }
30962c593315Sopenharmony_ci
30972c593315Sopenharmony_ci    config.nva.push_back(std::move(nva));
30982c593315Sopenharmony_ci  }
30992c593315Sopenharmony_ci
31002c593315Sopenharmony_ci  // Don't DOS our server!
31012c593315Sopenharmony_ci  if (config.host == "nghttp2.org") {
31022c593315Sopenharmony_ci    std::cerr << "Using h2load against public server " << config.host
31032c593315Sopenharmony_ci              << " should be prohibited." << std::endl;
31042c593315Sopenharmony_ci    exit(EXIT_FAILURE);
31052c593315Sopenharmony_ci  }
31062c593315Sopenharmony_ci
31072c593315Sopenharmony_ci  resolve_host();
31082c593315Sopenharmony_ci
31092c593315Sopenharmony_ci  std::cout << "starting benchmark..." << std::endl;
31102c593315Sopenharmony_ci
31112c593315Sopenharmony_ci  std::vector<std::unique_ptr<Worker>> workers;
31122c593315Sopenharmony_ci  workers.reserve(config.nthreads);
31132c593315Sopenharmony_ci
31142c593315Sopenharmony_ci#ifndef NOTHREADS
31152c593315Sopenharmony_ci  size_t nreqs_per_thread = 0;
31162c593315Sopenharmony_ci  ssize_t nreqs_rem = 0;
31172c593315Sopenharmony_ci
31182c593315Sopenharmony_ci  if (!config.timing_script) {
31192c593315Sopenharmony_ci    nreqs_per_thread = config.nreqs / config.nthreads;
31202c593315Sopenharmony_ci    nreqs_rem = config.nreqs % config.nthreads;
31212c593315Sopenharmony_ci  }
31222c593315Sopenharmony_ci
31232c593315Sopenharmony_ci  size_t nclients_per_thread = config.nclients / config.nthreads;
31242c593315Sopenharmony_ci  ssize_t nclients_rem = config.nclients % config.nthreads;
31252c593315Sopenharmony_ci
31262c593315Sopenharmony_ci  size_t rate_per_thread = config.rate / config.nthreads;
31272c593315Sopenharmony_ci  ssize_t rate_per_thread_rem = config.rate % config.nthreads;
31282c593315Sopenharmony_ci
31292c593315Sopenharmony_ci  size_t max_samples_per_thread =
31302c593315Sopenharmony_ci      std::max(static_cast<size_t>(256), MAX_SAMPLES / config.nthreads);
31312c593315Sopenharmony_ci
31322c593315Sopenharmony_ci  std::mutex mu;
31332c593315Sopenharmony_ci  std::condition_variable cv;
31342c593315Sopenharmony_ci  auto ready = false;
31352c593315Sopenharmony_ci
31362c593315Sopenharmony_ci  std::vector<std::future<void>> futures;
31372c593315Sopenharmony_ci  for (size_t i = 0; i < config.nthreads; ++i) {
31382c593315Sopenharmony_ci    auto rate = rate_per_thread;
31392c593315Sopenharmony_ci    if (rate_per_thread_rem > 0) {
31402c593315Sopenharmony_ci      --rate_per_thread_rem;
31412c593315Sopenharmony_ci      ++rate;
31422c593315Sopenharmony_ci    }
31432c593315Sopenharmony_ci    auto nclients = nclients_per_thread;
31442c593315Sopenharmony_ci    if (nclients_rem > 0) {
31452c593315Sopenharmony_ci      --nclients_rem;
31462c593315Sopenharmony_ci      ++nclients;
31472c593315Sopenharmony_ci    }
31482c593315Sopenharmony_ci
31492c593315Sopenharmony_ci    size_t nreqs;
31502c593315Sopenharmony_ci    if (config.timing_script) {
31512c593315Sopenharmony_ci      // With timing script, each client issues config.nreqs requests.
31522c593315Sopenharmony_ci      // We divide nreqs by number of clients in Worker ctor to
31532c593315Sopenharmony_ci      // distribute requests to those clients evenly, so multiply
31542c593315Sopenharmony_ci      // config.nreqs here by config.nclients.
31552c593315Sopenharmony_ci      nreqs = config.nreqs * nclients;
31562c593315Sopenharmony_ci    } else {
31572c593315Sopenharmony_ci      nreqs = nreqs_per_thread;
31582c593315Sopenharmony_ci      if (nreqs_rem > 0) {
31592c593315Sopenharmony_ci        --nreqs_rem;
31602c593315Sopenharmony_ci        ++nreqs;
31612c593315Sopenharmony_ci      }
31622c593315Sopenharmony_ci    }
31632c593315Sopenharmony_ci
31642c593315Sopenharmony_ci    workers.push_back(create_worker(i, ssl_ctx, nreqs, nclients, rate,
31652c593315Sopenharmony_ci                                    max_samples_per_thread));
31662c593315Sopenharmony_ci    auto &worker = workers.back();
31672c593315Sopenharmony_ci    futures.push_back(
31682c593315Sopenharmony_ci        std::async(std::launch::async, [&worker, &mu, &cv, &ready]() {
31692c593315Sopenharmony_ci          {
31702c593315Sopenharmony_ci            std::unique_lock<std::mutex> ulk(mu);
31712c593315Sopenharmony_ci            cv.wait(ulk, [&ready] { return ready; });
31722c593315Sopenharmony_ci          }
31732c593315Sopenharmony_ci          worker->run();
31742c593315Sopenharmony_ci        }));
31752c593315Sopenharmony_ci  }
31762c593315Sopenharmony_ci
31772c593315Sopenharmony_ci  {
31782c593315Sopenharmony_ci    std::lock_guard<std::mutex> lg(mu);
31792c593315Sopenharmony_ci    ready = true;
31802c593315Sopenharmony_ci    cv.notify_all();
31812c593315Sopenharmony_ci  }
31822c593315Sopenharmony_ci
31832c593315Sopenharmony_ci  auto start = std::chrono::steady_clock::now();
31842c593315Sopenharmony_ci
31852c593315Sopenharmony_ci  for (auto &fut : futures) {
31862c593315Sopenharmony_ci    fut.get();
31872c593315Sopenharmony_ci  }
31882c593315Sopenharmony_ci
31892c593315Sopenharmony_ci#else  // NOTHREADS
31902c593315Sopenharmony_ci  auto rate = config.rate;
31912c593315Sopenharmony_ci  auto nclients = config.nclients;
31922c593315Sopenharmony_ci  auto nreqs =
31932c593315Sopenharmony_ci      config.timing_script ? config.nreqs * config.nclients : config.nreqs;
31942c593315Sopenharmony_ci
31952c593315Sopenharmony_ci  workers.push_back(
31962c593315Sopenharmony_ci      create_worker(0, ssl_ctx, nreqs, nclients, rate, MAX_SAMPLES));
31972c593315Sopenharmony_ci
31982c593315Sopenharmony_ci  auto start = std::chrono::steady_clock::now();
31992c593315Sopenharmony_ci
32002c593315Sopenharmony_ci  workers.back()->run();
32012c593315Sopenharmony_ci#endif // NOTHREADS
32022c593315Sopenharmony_ci
32032c593315Sopenharmony_ci  auto end = std::chrono::steady_clock::now();
32042c593315Sopenharmony_ci  auto duration =
32052c593315Sopenharmony_ci      std::chrono::duration_cast<std::chrono::microseconds>(end - start);
32062c593315Sopenharmony_ci
32072c593315Sopenharmony_ci  Stats stats(0, 0);
32082c593315Sopenharmony_ci  for (const auto &w : workers) {
32092c593315Sopenharmony_ci    const auto &s = w->stats;
32102c593315Sopenharmony_ci
32112c593315Sopenharmony_ci    stats.req_todo += s.req_todo;
32122c593315Sopenharmony_ci    stats.req_started += s.req_started;
32132c593315Sopenharmony_ci    stats.req_done += s.req_done;
32142c593315Sopenharmony_ci    stats.req_timedout += s.req_timedout;
32152c593315Sopenharmony_ci    stats.req_success += s.req_success;
32162c593315Sopenharmony_ci    stats.req_status_success += s.req_status_success;
32172c593315Sopenharmony_ci    stats.req_failed += s.req_failed;
32182c593315Sopenharmony_ci    stats.req_error += s.req_error;
32192c593315Sopenharmony_ci    stats.bytes_total += s.bytes_total;
32202c593315Sopenharmony_ci    stats.bytes_head += s.bytes_head;
32212c593315Sopenharmony_ci    stats.bytes_head_decomp += s.bytes_head_decomp;
32222c593315Sopenharmony_ci    stats.bytes_body += s.bytes_body;
32232c593315Sopenharmony_ci    stats.udp_dgram_recv += s.udp_dgram_recv;
32242c593315Sopenharmony_ci    stats.udp_dgram_sent += s.udp_dgram_sent;
32252c593315Sopenharmony_ci
32262c593315Sopenharmony_ci    for (size_t i = 0; i < stats.status.size(); ++i) {
32272c593315Sopenharmony_ci      stats.status[i] += s.status[i];
32282c593315Sopenharmony_ci    }
32292c593315Sopenharmony_ci  }
32302c593315Sopenharmony_ci
32312c593315Sopenharmony_ci  auto ts = process_time_stats(workers);
32322c593315Sopenharmony_ci
32332c593315Sopenharmony_ci  // Requests which have not been issued due to connection errors, are
32342c593315Sopenharmony_ci  // counted towards req_failed and req_error.
32352c593315Sopenharmony_ci  auto req_not_issued =
32362c593315Sopenharmony_ci      (stats.req_todo - stats.req_status_success - stats.req_failed);
32372c593315Sopenharmony_ci  stats.req_failed += req_not_issued;
32382c593315Sopenharmony_ci  stats.req_error += req_not_issued;
32392c593315Sopenharmony_ci
32402c593315Sopenharmony_ci  // UI is heavily inspired by weighttp[1] and wrk[2]
32412c593315Sopenharmony_ci  //
32422c593315Sopenharmony_ci  // [1] https://github.com/lighttpd/weighttp
32432c593315Sopenharmony_ci  // [2] https://github.com/wg/wrk
32442c593315Sopenharmony_ci  double rps = 0;
32452c593315Sopenharmony_ci  int64_t bps = 0;
32462c593315Sopenharmony_ci  if (duration.count() > 0) {
32472c593315Sopenharmony_ci    if (config.is_timing_based_mode()) {
32482c593315Sopenharmony_ci      // we only want to consider the main duration if warm-up is given
32492c593315Sopenharmony_ci      rps = stats.req_success / config.duration;
32502c593315Sopenharmony_ci      bps = stats.bytes_total / config.duration;
32512c593315Sopenharmony_ci    } else {
32522c593315Sopenharmony_ci      auto secd = std::chrono::duration_cast<
32532c593315Sopenharmony_ci          std::chrono::duration<double, std::chrono::seconds::period>>(
32542c593315Sopenharmony_ci          duration);
32552c593315Sopenharmony_ci      rps = stats.req_success / secd.count();
32562c593315Sopenharmony_ci      bps = stats.bytes_total / secd.count();
32572c593315Sopenharmony_ci    }
32582c593315Sopenharmony_ci  }
32592c593315Sopenharmony_ci
32602c593315Sopenharmony_ci  double header_space_savings = 0.;
32612c593315Sopenharmony_ci  if (stats.bytes_head_decomp > 0) {
32622c593315Sopenharmony_ci    header_space_savings =
32632c593315Sopenharmony_ci        1. - static_cast<double>(stats.bytes_head) / stats.bytes_head_decomp;
32642c593315Sopenharmony_ci  }
32652c593315Sopenharmony_ci
32662c593315Sopenharmony_ci  std::cout << std::fixed << std::setprecision(2) << R"(
32672c593315Sopenharmony_cifinished in )"
32682c593315Sopenharmony_ci            << util::format_duration(duration) << ", " << rps << " req/s, "
32692c593315Sopenharmony_ci            << util::utos_funit(bps) << R"(B/s
32702c593315Sopenharmony_cirequests: )" << stats.req_todo
32712c593315Sopenharmony_ci            << " total, " << stats.req_started << " started, " << stats.req_done
32722c593315Sopenharmony_ci            << " done, " << stats.req_status_success << " succeeded, "
32732c593315Sopenharmony_ci            << stats.req_failed << " failed, " << stats.req_error
32742c593315Sopenharmony_ci            << " errored, " << stats.req_timedout << R"( timeout
32752c593315Sopenharmony_cistatus codes: )"
32762c593315Sopenharmony_ci            << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
32772c593315Sopenharmony_ci            << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
32782c593315Sopenharmony_citraffic: )" << util::utos_funit(stats.bytes_total)
32792c593315Sopenharmony_ci            << "B (" << stats.bytes_total << ") total, "
32802c593315Sopenharmony_ci            << util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
32812c593315Sopenharmony_ci            << ") headers (space savings " << header_space_savings * 100
32822c593315Sopenharmony_ci            << "%), " << util::utos_funit(stats.bytes_body) << "B ("
32832c593315Sopenharmony_ci            << stats.bytes_body << R"() data)" << std::endl;
32842c593315Sopenharmony_ci#ifdef ENABLE_HTTP3
32852c593315Sopenharmony_ci  if (config.is_quic()) {
32862c593315Sopenharmony_ci    std::cout << "UDP datagram: " << stats.udp_dgram_sent << " sent, "
32872c593315Sopenharmony_ci              << stats.udp_dgram_recv << " received" << std::endl;
32882c593315Sopenharmony_ci  }
32892c593315Sopenharmony_ci#endif // ENABLE_HTTP3
32902c593315Sopenharmony_ci  std::cout
32912c593315Sopenharmony_ci      << R"(                     min         max         mean         sd        +/- sd
32922c593315Sopenharmony_citime for request: )"
32932c593315Sopenharmony_ci      << std::setw(10) << util::format_duration(ts.request.min) << "  "
32942c593315Sopenharmony_ci      << std::setw(10) << util::format_duration(ts.request.max) << "  "
32952c593315Sopenharmony_ci      << std::setw(10) << util::format_duration(ts.request.mean) << "  "
32962c593315Sopenharmony_ci      << std::setw(10) << util::format_duration(ts.request.sd) << std::setw(9)
32972c593315Sopenharmony_ci      << util::dtos(ts.request.within_sd) << "%"
32982c593315Sopenharmony_ci      << "\ntime for connect: " << std::setw(10)
32992c593315Sopenharmony_ci      << util::format_duration(ts.connect.min) << "  " << std::setw(10)
33002c593315Sopenharmony_ci      << util::format_duration(ts.connect.max) << "  " << std::setw(10)
33012c593315Sopenharmony_ci      << util::format_duration(ts.connect.mean) << "  " << std::setw(10)
33022c593315Sopenharmony_ci      << util::format_duration(ts.connect.sd) << std::setw(9)
33032c593315Sopenharmony_ci      << util::dtos(ts.connect.within_sd) << "%"
33042c593315Sopenharmony_ci      << "\ntime to 1st byte: " << std::setw(10)
33052c593315Sopenharmony_ci      << util::format_duration(ts.ttfb.min) << "  " << std::setw(10)
33062c593315Sopenharmony_ci      << util::format_duration(ts.ttfb.max) << "  " << std::setw(10)
33072c593315Sopenharmony_ci      << util::format_duration(ts.ttfb.mean) << "  " << std::setw(10)
33082c593315Sopenharmony_ci      << util::format_duration(ts.ttfb.sd) << std::setw(9)
33092c593315Sopenharmony_ci      << util::dtos(ts.ttfb.within_sd) << "%"
33102c593315Sopenharmony_ci      << "\nreq/s           : " << std::setw(10) << ts.rps.min << "  "
33112c593315Sopenharmony_ci      << std::setw(10) << ts.rps.max << "  " << std::setw(10) << ts.rps.mean
33122c593315Sopenharmony_ci      << "  " << std::setw(10) << ts.rps.sd << std::setw(9)
33132c593315Sopenharmony_ci      << util::dtos(ts.rps.within_sd) << "%" << std::endl;
33142c593315Sopenharmony_ci
33152c593315Sopenharmony_ci  SSL_CTX_free(ssl_ctx);
33162c593315Sopenharmony_ci
33172c593315Sopenharmony_ci  if (config.log_fd != -1) {
33182c593315Sopenharmony_ci    close(config.log_fd);
33192c593315Sopenharmony_ci  }
33202c593315Sopenharmony_ci
33212c593315Sopenharmony_ci  return 0;
33222c593315Sopenharmony_ci}
33232c593315Sopenharmony_ci
33242c593315Sopenharmony_ci} // namespace h2load
33252c593315Sopenharmony_ci
33262c593315Sopenharmony_ciint main(int argc, char **argv) { return h2load::main(argc, argv); }
3327