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