12c593315Sopenharmony_ci/* 22c593315Sopenharmony_ci * nghttp2 - HTTP/2 C Library 32c593315Sopenharmony_ci * 42c593315Sopenharmony_ci * Copyright (c) 2013 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 "nghttp.h" 262c593315Sopenharmony_ci 272c593315Sopenharmony_ci#include <sys/stat.h> 282c593315Sopenharmony_ci#ifdef HAVE_UNISTD_H 292c593315Sopenharmony_ci# include <unistd.h> 302c593315Sopenharmony_ci#endif // HAVE_UNISTD_H 312c593315Sopenharmony_ci#ifdef HAVE_FCNTL_H 322c593315Sopenharmony_ci# include <fcntl.h> 332c593315Sopenharmony_ci#endif // HAVE_FCNTL_H 342c593315Sopenharmony_ci#ifdef HAVE_NETINET_IN_H 352c593315Sopenharmony_ci# include <netinet/in.h> 362c593315Sopenharmony_ci#endif // HAVE_NETINET_IN_H 372c593315Sopenharmony_ci#include <netinet/tcp.h> 382c593315Sopenharmony_ci#include <getopt.h> 392c593315Sopenharmony_ci 402c593315Sopenharmony_ci#include <cassert> 412c593315Sopenharmony_ci#include <cstdio> 422c593315Sopenharmony_ci#include <cerrno> 432c593315Sopenharmony_ci#include <cstdlib> 442c593315Sopenharmony_ci#include <cstring> 452c593315Sopenharmony_ci#include <iostream> 462c593315Sopenharmony_ci#include <iomanip> 472c593315Sopenharmony_ci#include <sstream> 482c593315Sopenharmony_ci#include <tuple> 492c593315Sopenharmony_ci 502c593315Sopenharmony_ci#include <openssl/err.h> 512c593315Sopenharmony_ci 522c593315Sopenharmony_ci#ifdef HAVE_JANSSON 532c593315Sopenharmony_ci# include <jansson.h> 542c593315Sopenharmony_ci#endif // HAVE_JANSSON 552c593315Sopenharmony_ci 562c593315Sopenharmony_ci#include "app_helper.h" 572c593315Sopenharmony_ci#include "HtmlParser.h" 582c593315Sopenharmony_ci#include "util.h" 592c593315Sopenharmony_ci#include "base64.h" 602c593315Sopenharmony_ci#include "tls.h" 612c593315Sopenharmony_ci#include "template.h" 622c593315Sopenharmony_ci#include "ssl_compat.h" 632c593315Sopenharmony_ci 642c593315Sopenharmony_ci#ifndef O_BINARY 652c593315Sopenharmony_ci# define O_BINARY (0) 662c593315Sopenharmony_ci#endif // O_BINARY 672c593315Sopenharmony_ci 682c593315Sopenharmony_cinamespace nghttp2 { 692c593315Sopenharmony_ci 702c593315Sopenharmony_ci// The anchor stream nodes when --no-dep is not used. The stream ID = 712c593315Sopenharmony_ci// 1 is excluded since it is used as first stream in upgrade case. We 722c593315Sopenharmony_ci// follows the same dependency anchor nodes as Firefox does. 732c593315Sopenharmony_cistruct Anchor { 742c593315Sopenharmony_ci int32_t stream_id; 752c593315Sopenharmony_ci // stream ID this anchor depends on 762c593315Sopenharmony_ci int32_t dep_stream_id; 772c593315Sopenharmony_ci // .. with this weight. 782c593315Sopenharmony_ci int32_t weight; 792c593315Sopenharmony_ci}; 802c593315Sopenharmony_ci 812c593315Sopenharmony_ci// This is index into anchors. Firefox uses ANCHOR_FOLLOWERS for html 822c593315Sopenharmony_ci// file. 832c593315Sopenharmony_cienum { 842c593315Sopenharmony_ci ANCHOR_LEADERS, 852c593315Sopenharmony_ci ANCHOR_UNBLOCKED, 862c593315Sopenharmony_ci ANCHOR_BACKGROUND, 872c593315Sopenharmony_ci ANCHOR_SPECULATIVE, 882c593315Sopenharmony_ci ANCHOR_FOLLOWERS, 892c593315Sopenharmony_ci}; 902c593315Sopenharmony_ci 912c593315Sopenharmony_cinamespace { 922c593315Sopenharmony_ciconstexpr auto anchors = std::array<Anchor, 5>{{ 932c593315Sopenharmony_ci {3, 0, 201}, 942c593315Sopenharmony_ci {5, 0, 101}, 952c593315Sopenharmony_ci {7, 0, 1}, 962c593315Sopenharmony_ci {9, 7, 1}, 972c593315Sopenharmony_ci {11, 3, 1}, 982c593315Sopenharmony_ci}}; 992c593315Sopenharmony_ci} // namespace 1002c593315Sopenharmony_ci 1012c593315Sopenharmony_ciConfig::Config() 1022c593315Sopenharmony_ci : header_table_size(-1), 1032c593315Sopenharmony_ci min_header_table_size(std::numeric_limits<uint32_t>::max()), 1042c593315Sopenharmony_ci encoder_header_table_size(-1), 1052c593315Sopenharmony_ci padding(0), 1062c593315Sopenharmony_ci max_concurrent_streams(100), 1072c593315Sopenharmony_ci peer_max_concurrent_streams(100), 1082c593315Sopenharmony_ci multiply(1), 1092c593315Sopenharmony_ci timeout(0.), 1102c593315Sopenharmony_ci window_bits(-1), 1112c593315Sopenharmony_ci connection_window_bits(-1), 1122c593315Sopenharmony_ci verbose(0), 1132c593315Sopenharmony_ci port_override(0), 1142c593315Sopenharmony_ci null_out(false), 1152c593315Sopenharmony_ci remote_name(false), 1162c593315Sopenharmony_ci get_assets(false), 1172c593315Sopenharmony_ci stat(false), 1182c593315Sopenharmony_ci upgrade(false), 1192c593315Sopenharmony_ci continuation(false), 1202c593315Sopenharmony_ci no_content_length(false), 1212c593315Sopenharmony_ci no_dep(false), 1222c593315Sopenharmony_ci hexdump(false), 1232c593315Sopenharmony_ci no_push(false), 1242c593315Sopenharmony_ci expect_continue(false), 1252c593315Sopenharmony_ci verify_peer(true), 1262c593315Sopenharmony_ci ktls(false), 1272c593315Sopenharmony_ci no_rfc7540_pri(false) { 1282c593315Sopenharmony_ci nghttp2_option_new(&http2_option); 1292c593315Sopenharmony_ci nghttp2_option_set_peer_max_concurrent_streams(http2_option, 1302c593315Sopenharmony_ci peer_max_concurrent_streams); 1312c593315Sopenharmony_ci nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ALTSVC); 1322c593315Sopenharmony_ci nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ORIGIN); 1332c593315Sopenharmony_ci} 1342c593315Sopenharmony_ci 1352c593315Sopenharmony_ciConfig::~Config() { nghttp2_option_del(http2_option); } 1362c593315Sopenharmony_ci 1372c593315Sopenharmony_cinamespace { 1382c593315Sopenharmony_ciConfig config; 1392c593315Sopenharmony_ci} // namespace 1402c593315Sopenharmony_ci 1412c593315Sopenharmony_cinamespace { 1422c593315Sopenharmony_civoid print_protocol_nego_error() { 1432c593315Sopenharmony_ci std::cerr << "[ERROR] HTTP/2 protocol was not selected." 1442c593315Sopenharmony_ci << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")" 1452c593315Sopenharmony_ci << std::endl; 1462c593315Sopenharmony_ci} 1472c593315Sopenharmony_ci} // namespace 1482c593315Sopenharmony_ci 1492c593315Sopenharmony_cinamespace { 1502c593315Sopenharmony_cistd::string strip_fragment(const char *raw_uri) { 1512c593315Sopenharmony_ci const char *end; 1522c593315Sopenharmony_ci for (end = raw_uri; *end && *end != '#'; ++end) 1532c593315Sopenharmony_ci ; 1542c593315Sopenharmony_ci size_t len = end - raw_uri; 1552c593315Sopenharmony_ci return std::string(raw_uri, len); 1562c593315Sopenharmony_ci} 1572c593315Sopenharmony_ci} // namespace 1582c593315Sopenharmony_ci 1592c593315Sopenharmony_ciRequest::Request(const std::string &uri, const http_parser_url &u, 1602c593315Sopenharmony_ci const nghttp2_data_provider *data_prd, int64_t data_length, 1612c593315Sopenharmony_ci const nghttp2_priority_spec &pri_spec, int level) 1622c593315Sopenharmony_ci : uri(uri), 1632c593315Sopenharmony_ci u(u), 1642c593315Sopenharmony_ci pri_spec(pri_spec), 1652c593315Sopenharmony_ci data_length(data_length), 1662c593315Sopenharmony_ci data_offset(0), 1672c593315Sopenharmony_ci response_len(0), 1682c593315Sopenharmony_ci inflater(nullptr), 1692c593315Sopenharmony_ci data_prd(data_prd), 1702c593315Sopenharmony_ci header_buffer_size(0), 1712c593315Sopenharmony_ci stream_id(-1), 1722c593315Sopenharmony_ci status(0), 1732c593315Sopenharmony_ci level(level), 1742c593315Sopenharmony_ci expect_final_response(false) { 1752c593315Sopenharmony_ci http2::init_hdidx(res_hdidx); 1762c593315Sopenharmony_ci http2::init_hdidx(req_hdidx); 1772c593315Sopenharmony_ci} 1782c593315Sopenharmony_ci 1792c593315Sopenharmony_ciRequest::~Request() { nghttp2_gzip_inflate_del(inflater); } 1802c593315Sopenharmony_ci 1812c593315Sopenharmony_civoid Request::init_inflater() { 1822c593315Sopenharmony_ci int rv; 1832c593315Sopenharmony_ci // This is required with --disable-assert. 1842c593315Sopenharmony_ci (void)rv; 1852c593315Sopenharmony_ci rv = nghttp2_gzip_inflate_new(&inflater); 1862c593315Sopenharmony_ci assert(rv == 0); 1872c593315Sopenharmony_ci} 1882c593315Sopenharmony_ci 1892c593315Sopenharmony_ciStringRef Request::get_real_scheme() const { 1902c593315Sopenharmony_ci return config.scheme_override.empty() 1912c593315Sopenharmony_ci ? util::get_uri_field(uri.c_str(), u, UF_SCHEMA) 1922c593315Sopenharmony_ci : StringRef{config.scheme_override}; 1932c593315Sopenharmony_ci} 1942c593315Sopenharmony_ci 1952c593315Sopenharmony_ciStringRef Request::get_real_host() const { 1962c593315Sopenharmony_ci return config.host_override.empty() 1972c593315Sopenharmony_ci ? util::get_uri_field(uri.c_str(), u, UF_HOST) 1982c593315Sopenharmony_ci : StringRef{config.host_override}; 1992c593315Sopenharmony_ci} 2002c593315Sopenharmony_ci 2012c593315Sopenharmony_ciuint16_t Request::get_real_port() const { 2022c593315Sopenharmony_ci auto scheme = get_real_scheme(); 2032c593315Sopenharmony_ci return config.host_override.empty() ? util::has_uri_field(u, UF_PORT) ? u.port 2042c593315Sopenharmony_ci : scheme == "https" ? 443 2052c593315Sopenharmony_ci : 80 2062c593315Sopenharmony_ci : config.port_override == 0 ? scheme == "https" ? 443 : 80 2072c593315Sopenharmony_ci : config.port_override; 2082c593315Sopenharmony_ci} 2092c593315Sopenharmony_ci 2102c593315Sopenharmony_civoid Request::init_html_parser() { 2112c593315Sopenharmony_ci // We crawl HTML using overridden scheme, host, and port. 2122c593315Sopenharmony_ci auto scheme = get_real_scheme(); 2132c593315Sopenharmony_ci auto host = get_real_host(); 2142c593315Sopenharmony_ci auto port = get_real_port(); 2152c593315Sopenharmony_ci auto ipv6_lit = 2162c593315Sopenharmony_ci std::find(std::begin(host), std::end(host), ':') != std::end(host); 2172c593315Sopenharmony_ci 2182c593315Sopenharmony_ci auto base_uri = scheme.str(); 2192c593315Sopenharmony_ci base_uri += "://"; 2202c593315Sopenharmony_ci if (ipv6_lit) { 2212c593315Sopenharmony_ci base_uri += '['; 2222c593315Sopenharmony_ci } 2232c593315Sopenharmony_ci base_uri += host; 2242c593315Sopenharmony_ci if (ipv6_lit) { 2252c593315Sopenharmony_ci base_uri += ']'; 2262c593315Sopenharmony_ci } 2272c593315Sopenharmony_ci if (!((scheme == "https" && port == 443) || 2282c593315Sopenharmony_ci (scheme == "http" && port == 80))) { 2292c593315Sopenharmony_ci base_uri += ':'; 2302c593315Sopenharmony_ci base_uri += util::utos(port); 2312c593315Sopenharmony_ci } 2322c593315Sopenharmony_ci base_uri += util::get_uri_field(uri.c_str(), u, UF_PATH); 2332c593315Sopenharmony_ci if (util::has_uri_field(u, UF_QUERY)) { 2342c593315Sopenharmony_ci base_uri += '?'; 2352c593315Sopenharmony_ci base_uri += util::get_uri_field(uri.c_str(), u, UF_QUERY); 2362c593315Sopenharmony_ci } 2372c593315Sopenharmony_ci 2382c593315Sopenharmony_ci html_parser = std::make_unique<HtmlParser>(base_uri); 2392c593315Sopenharmony_ci} 2402c593315Sopenharmony_ci 2412c593315Sopenharmony_ciint Request::update_html_parser(const uint8_t *data, size_t len, int fin) { 2422c593315Sopenharmony_ci if (!html_parser) { 2432c593315Sopenharmony_ci return 0; 2442c593315Sopenharmony_ci } 2452c593315Sopenharmony_ci return html_parser->parse_chunk(reinterpret_cast<const char *>(data), len, 2462c593315Sopenharmony_ci fin); 2472c593315Sopenharmony_ci} 2482c593315Sopenharmony_ci 2492c593315Sopenharmony_cistd::string Request::make_reqpath() const { 2502c593315Sopenharmony_ci std::string path = util::has_uri_field(u, UF_PATH) 2512c593315Sopenharmony_ci ? util::get_uri_field(uri.c_str(), u, UF_PATH).str() 2522c593315Sopenharmony_ci : "/"; 2532c593315Sopenharmony_ci if (util::has_uri_field(u, UF_QUERY)) { 2542c593315Sopenharmony_ci path += '?'; 2552c593315Sopenharmony_ci path.append(uri.c_str() + u.field_data[UF_QUERY].off, 2562c593315Sopenharmony_ci u.field_data[UF_QUERY].len); 2572c593315Sopenharmony_ci } 2582c593315Sopenharmony_ci return path; 2592c593315Sopenharmony_ci} 2602c593315Sopenharmony_ci 2612c593315Sopenharmony_cinamespace { 2622c593315Sopenharmony_ci// Perform special handling |host| if it is IPv6 literal and includes 2632c593315Sopenharmony_ci// zone ID per RFC 6874. 2642c593315Sopenharmony_cistd::string decode_host(const StringRef &host) { 2652c593315Sopenharmony_ci auto zone_start = std::find(std::begin(host), std::end(host), '%'); 2662c593315Sopenharmony_ci if (zone_start == std::end(host) || 2672c593315Sopenharmony_ci !util::ipv6_numeric_addr( 2682c593315Sopenharmony_ci std::string(std::begin(host), zone_start).c_str())) { 2692c593315Sopenharmony_ci return host.str(); 2702c593315Sopenharmony_ci } 2712c593315Sopenharmony_ci // case: ::1% 2722c593315Sopenharmony_ci if (zone_start + 1 == std::end(host)) { 2732c593315Sopenharmony_ci return StringRef{host.c_str(), host.size() - 1}.str(); 2742c593315Sopenharmony_ci } 2752c593315Sopenharmony_ci // case: ::1%12 or ::1%1 2762c593315Sopenharmony_ci if (zone_start + 3 >= std::end(host)) { 2772c593315Sopenharmony_ci return host.str(); 2782c593315Sopenharmony_ci } 2792c593315Sopenharmony_ci // If we see "%25", followed by more characters, then decode %25 as 2802c593315Sopenharmony_ci // '%'. 2812c593315Sopenharmony_ci auto zone_id_src = (*(zone_start + 1) == '2' && *(zone_start + 2) == '5') 2822c593315Sopenharmony_ci ? zone_start + 3 2832c593315Sopenharmony_ci : zone_start + 1; 2842c593315Sopenharmony_ci auto zone_id = util::percent_decode(zone_id_src, std::end(host)); 2852c593315Sopenharmony_ci auto res = std::string(std::begin(host), zone_start + 1); 2862c593315Sopenharmony_ci res += zone_id; 2872c593315Sopenharmony_ci return res; 2882c593315Sopenharmony_ci} 2892c593315Sopenharmony_ci} // namespace 2902c593315Sopenharmony_ci 2912c593315Sopenharmony_cinamespace { 2922c593315Sopenharmony_cinghttp2_priority_spec resolve_dep(int res_type) { 2932c593315Sopenharmony_ci nghttp2_priority_spec pri_spec; 2942c593315Sopenharmony_ci 2952c593315Sopenharmony_ci if (config.no_dep) { 2962c593315Sopenharmony_ci nghttp2_priority_spec_default_init(&pri_spec); 2972c593315Sopenharmony_ci 2982c593315Sopenharmony_ci return pri_spec; 2992c593315Sopenharmony_ci } 3002c593315Sopenharmony_ci 3012c593315Sopenharmony_ci int32_t anchor_id; 3022c593315Sopenharmony_ci int32_t weight; 3032c593315Sopenharmony_ci switch (res_type) { 3042c593315Sopenharmony_ci case REQ_CSS: 3052c593315Sopenharmony_ci case REQ_JS: 3062c593315Sopenharmony_ci anchor_id = anchors[ANCHOR_LEADERS].stream_id; 3072c593315Sopenharmony_ci weight = 32; 3082c593315Sopenharmony_ci break; 3092c593315Sopenharmony_ci case REQ_UNBLOCK_JS: 3102c593315Sopenharmony_ci anchor_id = anchors[ANCHOR_UNBLOCKED].stream_id; 3112c593315Sopenharmony_ci weight = 32; 3122c593315Sopenharmony_ci break; 3132c593315Sopenharmony_ci case REQ_IMG: 3142c593315Sopenharmony_ci anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id; 3152c593315Sopenharmony_ci weight = 12; 3162c593315Sopenharmony_ci break; 3172c593315Sopenharmony_ci default: 3182c593315Sopenharmony_ci anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id; 3192c593315Sopenharmony_ci weight = 32; 3202c593315Sopenharmony_ci } 3212c593315Sopenharmony_ci 3222c593315Sopenharmony_ci nghttp2_priority_spec_init(&pri_spec, anchor_id, weight, 0); 3232c593315Sopenharmony_ci return pri_spec; 3242c593315Sopenharmony_ci} 3252c593315Sopenharmony_ci} // namespace 3262c593315Sopenharmony_ci 3272c593315Sopenharmony_cibool Request::is_ipv6_literal_addr() const { 3282c593315Sopenharmony_ci if (util::has_uri_field(u, UF_HOST)) { 3292c593315Sopenharmony_ci return memchr(uri.c_str() + u.field_data[UF_HOST].off, ':', 3302c593315Sopenharmony_ci u.field_data[UF_HOST].len); 3312c593315Sopenharmony_ci } else { 3322c593315Sopenharmony_ci return false; 3332c593315Sopenharmony_ci } 3342c593315Sopenharmony_ci} 3352c593315Sopenharmony_ci 3362c593315Sopenharmony_ciHeaders::value_type *Request::get_res_header(int32_t token) { 3372c593315Sopenharmony_ci auto idx = res_hdidx[token]; 3382c593315Sopenharmony_ci if (idx == -1) { 3392c593315Sopenharmony_ci return nullptr; 3402c593315Sopenharmony_ci } 3412c593315Sopenharmony_ci return &res_nva[idx]; 3422c593315Sopenharmony_ci} 3432c593315Sopenharmony_ci 3442c593315Sopenharmony_ciHeaders::value_type *Request::get_req_header(int32_t token) { 3452c593315Sopenharmony_ci auto idx = req_hdidx[token]; 3462c593315Sopenharmony_ci if (idx == -1) { 3472c593315Sopenharmony_ci return nullptr; 3482c593315Sopenharmony_ci } 3492c593315Sopenharmony_ci return &req_nva[idx]; 3502c593315Sopenharmony_ci} 3512c593315Sopenharmony_ci 3522c593315Sopenharmony_civoid Request::record_request_start_time() { 3532c593315Sopenharmony_ci timing.state = RequestState::ON_REQUEST; 3542c593315Sopenharmony_ci timing.request_start_time = get_time(); 3552c593315Sopenharmony_ci} 3562c593315Sopenharmony_ci 3572c593315Sopenharmony_civoid Request::record_response_start_time() { 3582c593315Sopenharmony_ci timing.state = RequestState::ON_RESPONSE; 3592c593315Sopenharmony_ci timing.response_start_time = get_time(); 3602c593315Sopenharmony_ci} 3612c593315Sopenharmony_ci 3622c593315Sopenharmony_civoid Request::record_response_end_time() { 3632c593315Sopenharmony_ci timing.state = RequestState::ON_COMPLETE; 3642c593315Sopenharmony_ci timing.response_end_time = get_time(); 3652c593315Sopenharmony_ci} 3662c593315Sopenharmony_ci 3672c593315Sopenharmony_cinamespace { 3682c593315Sopenharmony_civoid continue_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { 3692c593315Sopenharmony_ci auto client = static_cast<HttpClient *>(ev_userdata(loop)); 3702c593315Sopenharmony_ci auto req = static_cast<Request *>(w->data); 3712c593315Sopenharmony_ci int error; 3722c593315Sopenharmony_ci 3732c593315Sopenharmony_ci error = nghttp2_submit_data(client->session, NGHTTP2_FLAG_END_STREAM, 3742c593315Sopenharmony_ci req->stream_id, req->data_prd); 3752c593315Sopenharmony_ci 3762c593315Sopenharmony_ci if (error) { 3772c593315Sopenharmony_ci std::cerr << "[ERROR] nghttp2_submit_data() returned error: " 3782c593315Sopenharmony_ci << nghttp2_strerror(error) << std::endl; 3792c593315Sopenharmony_ci nghttp2_submit_rst_stream(client->session, NGHTTP2_FLAG_NONE, 3802c593315Sopenharmony_ci req->stream_id, NGHTTP2_INTERNAL_ERROR); 3812c593315Sopenharmony_ci } 3822c593315Sopenharmony_ci 3832c593315Sopenharmony_ci client->signal_write(); 3842c593315Sopenharmony_ci} 3852c593315Sopenharmony_ci} // namespace 3862c593315Sopenharmony_ci 3872c593315Sopenharmony_ciContinueTimer::ContinueTimer(struct ev_loop *loop, Request *req) : loop(loop) { 3882c593315Sopenharmony_ci ev_timer_init(&timer, continue_timeout_cb, 1., 0.); 3892c593315Sopenharmony_ci timer.data = req; 3902c593315Sopenharmony_ci} 3912c593315Sopenharmony_ci 3922c593315Sopenharmony_ciContinueTimer::~ContinueTimer() { stop(); } 3932c593315Sopenharmony_ci 3942c593315Sopenharmony_civoid ContinueTimer::start() { ev_timer_start(loop, &timer); } 3952c593315Sopenharmony_ci 3962c593315Sopenharmony_civoid ContinueTimer::stop() { ev_timer_stop(loop, &timer); } 3972c593315Sopenharmony_ci 3982c593315Sopenharmony_civoid ContinueTimer::dispatch_continue() { 3992c593315Sopenharmony_ci // Only dispatch the timeout callback if it hasn't already been called. 4002c593315Sopenharmony_ci if (ev_is_active(&timer)) { 4012c593315Sopenharmony_ci ev_feed_event(loop, &timer, 0); 4022c593315Sopenharmony_ci } 4032c593315Sopenharmony_ci} 4042c593315Sopenharmony_ci 4052c593315Sopenharmony_cinamespace { 4062c593315Sopenharmony_ciint htp_msg_begincb(llhttp_t *htp) { 4072c593315Sopenharmony_ci if (config.verbose) { 4082c593315Sopenharmony_ci print_timer(); 4092c593315Sopenharmony_ci std::cout << " HTTP Upgrade response" << std::endl; 4102c593315Sopenharmony_ci } 4112c593315Sopenharmony_ci return 0; 4122c593315Sopenharmony_ci} 4132c593315Sopenharmony_ci} // namespace 4142c593315Sopenharmony_ci 4152c593315Sopenharmony_cinamespace { 4162c593315Sopenharmony_ciint htp_msg_completecb(llhttp_t *htp) { 4172c593315Sopenharmony_ci auto client = static_cast<HttpClient *>(htp->data); 4182c593315Sopenharmony_ci client->upgrade_response_status_code = htp->status_code; 4192c593315Sopenharmony_ci client->upgrade_response_complete = true; 4202c593315Sopenharmony_ci return 0; 4212c593315Sopenharmony_ci} 4222c593315Sopenharmony_ci} // namespace 4232c593315Sopenharmony_ci 4242c593315Sopenharmony_cinamespace { 4252c593315Sopenharmony_ciconstexpr llhttp_settings_t htp_hooks = { 4262c593315Sopenharmony_ci htp_msg_begincb, // llhttp_cb on_message_begin; 4272c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_url; 4282c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_status; 4292c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_method; 4302c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_version; 4312c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_header_field; 4322c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_header_value; 4332c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_chunk_extension_name; 4342c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_chunk_extension_value; 4352c593315Sopenharmony_ci nullptr, // llhttp_cb on_headers_complete; 4362c593315Sopenharmony_ci nullptr, // llhttp_data_cb on_body; 4372c593315Sopenharmony_ci htp_msg_completecb, // llhttp_cb on_message_complete; 4382c593315Sopenharmony_ci nullptr, // llhttp_cb on_url_complete; 4392c593315Sopenharmony_ci nullptr, // llhttp_cb on_status_complete; 4402c593315Sopenharmony_ci nullptr, // llhttp_cb on_method_complete; 4412c593315Sopenharmony_ci nullptr, // llhttp_cb on_version_complete; 4422c593315Sopenharmony_ci nullptr, // llhttp_cb on_header_field_complete; 4432c593315Sopenharmony_ci nullptr, // llhttp_cb on_header_value_complete; 4442c593315Sopenharmony_ci nullptr, // llhttp_cb on_chunk_extension_name_complete; 4452c593315Sopenharmony_ci nullptr, // llhttp_cb on_chunk_extension_value_complete; 4462c593315Sopenharmony_ci nullptr, // llhttp_cb on_chunk_header; 4472c593315Sopenharmony_ci nullptr, // llhttp_cb on_chunk_complete; 4482c593315Sopenharmony_ci nullptr, // llhttp_cb on_reset; 4492c593315Sopenharmony_ci}; 4502c593315Sopenharmony_ci} // namespace 4512c593315Sopenharmony_ci 4522c593315Sopenharmony_cinamespace { 4532c593315Sopenharmony_ciint submit_request(HttpClient *client, const Headers &headers, Request *req) { 4542c593315Sopenharmony_ci auto scheme = util::get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA); 4552c593315Sopenharmony_ci auto build_headers = Headers{{":method", req->data_prd ? "POST" : "GET"}, 4562c593315Sopenharmony_ci {":path", req->make_reqpath()}, 4572c593315Sopenharmony_ci {":scheme", scheme.str()}, 4582c593315Sopenharmony_ci {":authority", client->hostport}, 4592c593315Sopenharmony_ci {"accept", "*/*"}, 4602c593315Sopenharmony_ci {"accept-encoding", "gzip, deflate"}, 4612c593315Sopenharmony_ci {"user-agent", "nghttp2/" NGHTTP2_VERSION}}; 4622c593315Sopenharmony_ci bool expect_continue = false; 4632c593315Sopenharmony_ci 4642c593315Sopenharmony_ci if (config.continuation) { 4652c593315Sopenharmony_ci for (size_t i = 0; i < 6; ++i) { 4662c593315Sopenharmony_ci build_headers.emplace_back("continuation-test-" + util::utos(i + 1), 4672c593315Sopenharmony_ci std::string(4_k, '-')); 4682c593315Sopenharmony_ci } 4692c593315Sopenharmony_ci } 4702c593315Sopenharmony_ci 4712c593315Sopenharmony_ci auto num_initial_headers = build_headers.size(); 4722c593315Sopenharmony_ci 4732c593315Sopenharmony_ci if (req->data_prd) { 4742c593315Sopenharmony_ci if (!config.no_content_length) { 4752c593315Sopenharmony_ci build_headers.emplace_back("content-length", 4762c593315Sopenharmony_ci util::utos(req->data_length)); 4772c593315Sopenharmony_ci } 4782c593315Sopenharmony_ci if (config.expect_continue) { 4792c593315Sopenharmony_ci expect_continue = true; 4802c593315Sopenharmony_ci build_headers.emplace_back("expect", "100-continue"); 4812c593315Sopenharmony_ci } 4822c593315Sopenharmony_ci } 4832c593315Sopenharmony_ci 4842c593315Sopenharmony_ci for (auto &kv : headers) { 4852c593315Sopenharmony_ci size_t i; 4862c593315Sopenharmony_ci for (i = 0; i < num_initial_headers; ++i) { 4872c593315Sopenharmony_ci if (kv.name == build_headers[i].name) { 4882c593315Sopenharmony_ci build_headers[i].value = kv.value; 4892c593315Sopenharmony_ci break; 4902c593315Sopenharmony_ci } 4912c593315Sopenharmony_ci } 4922c593315Sopenharmony_ci if (i < num_initial_headers) { 4932c593315Sopenharmony_ci continue; 4942c593315Sopenharmony_ci } 4952c593315Sopenharmony_ci 4962c593315Sopenharmony_ci build_headers.emplace_back(kv.name, kv.value, kv.no_index); 4972c593315Sopenharmony_ci } 4982c593315Sopenharmony_ci 4992c593315Sopenharmony_ci auto nva = std::vector<nghttp2_nv>(); 5002c593315Sopenharmony_ci nva.reserve(build_headers.size()); 5012c593315Sopenharmony_ci 5022c593315Sopenharmony_ci for (auto &kv : build_headers) { 5032c593315Sopenharmony_ci nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); 5042c593315Sopenharmony_ci } 5052c593315Sopenharmony_ci 5062c593315Sopenharmony_ci auto method = http2::get_header(build_headers, ":method"); 5072c593315Sopenharmony_ci assert(method); 5082c593315Sopenharmony_ci 5092c593315Sopenharmony_ci req->method = method->value; 5102c593315Sopenharmony_ci 5112c593315Sopenharmony_ci std::string trailer_names; 5122c593315Sopenharmony_ci if (!config.trailer.empty()) { 5132c593315Sopenharmony_ci trailer_names = config.trailer[0].name; 5142c593315Sopenharmony_ci for (size_t i = 1; i < config.trailer.size(); ++i) { 5152c593315Sopenharmony_ci trailer_names += ", "; 5162c593315Sopenharmony_ci trailer_names += config.trailer[i].name; 5172c593315Sopenharmony_ci } 5182c593315Sopenharmony_ci nva.push_back(http2::make_nv_ls("trailer", trailer_names)); 5192c593315Sopenharmony_ci } 5202c593315Sopenharmony_ci 5212c593315Sopenharmony_ci int32_t stream_id; 5222c593315Sopenharmony_ci 5232c593315Sopenharmony_ci if (expect_continue) { 5242c593315Sopenharmony_ci stream_id = nghttp2_submit_headers(client->session, 0, -1, &req->pri_spec, 5252c593315Sopenharmony_ci nva.data(), nva.size(), req); 5262c593315Sopenharmony_ci } else { 5272c593315Sopenharmony_ci stream_id = 5282c593315Sopenharmony_ci nghttp2_submit_request(client->session, &req->pri_spec, nva.data(), 5292c593315Sopenharmony_ci nva.size(), req->data_prd, req); 5302c593315Sopenharmony_ci } 5312c593315Sopenharmony_ci 5322c593315Sopenharmony_ci if (stream_id < 0) { 5332c593315Sopenharmony_ci std::cerr << "[ERROR] nghttp2_submit_" 5342c593315Sopenharmony_ci << (expect_continue ? "headers" : "request") 5352c593315Sopenharmony_ci << "() returned error: " << nghttp2_strerror(stream_id) 5362c593315Sopenharmony_ci << std::endl; 5372c593315Sopenharmony_ci return -1; 5382c593315Sopenharmony_ci } 5392c593315Sopenharmony_ci 5402c593315Sopenharmony_ci req->stream_id = stream_id; 5412c593315Sopenharmony_ci client->request_done(req); 5422c593315Sopenharmony_ci 5432c593315Sopenharmony_ci req->req_nva = std::move(build_headers); 5442c593315Sopenharmony_ci 5452c593315Sopenharmony_ci if (expect_continue) { 5462c593315Sopenharmony_ci auto timer = std::make_unique<ContinueTimer>(client->loop, req); 5472c593315Sopenharmony_ci req->continue_timer = std::move(timer); 5482c593315Sopenharmony_ci } 5492c593315Sopenharmony_ci 5502c593315Sopenharmony_ci return 0; 5512c593315Sopenharmony_ci} 5522c593315Sopenharmony_ci} // namespace 5532c593315Sopenharmony_ci 5542c593315Sopenharmony_cinamespace { 5552c593315Sopenharmony_civoid readcb(struct ev_loop *loop, ev_io *w, int revents) { 5562c593315Sopenharmony_ci auto client = static_cast<HttpClient *>(w->data); 5572c593315Sopenharmony_ci if (client->do_read() != 0) { 5582c593315Sopenharmony_ci client->disconnect(); 5592c593315Sopenharmony_ci } 5602c593315Sopenharmony_ci} 5612c593315Sopenharmony_ci} // namespace 5622c593315Sopenharmony_ci 5632c593315Sopenharmony_cinamespace { 5642c593315Sopenharmony_civoid writecb(struct ev_loop *loop, ev_io *w, int revents) { 5652c593315Sopenharmony_ci auto client = static_cast<HttpClient *>(w->data); 5662c593315Sopenharmony_ci auto rv = client->do_write(); 5672c593315Sopenharmony_ci if (rv == HttpClient::ERR_CONNECT_FAIL) { 5682c593315Sopenharmony_ci client->connect_fail(); 5692c593315Sopenharmony_ci return; 5702c593315Sopenharmony_ci } 5712c593315Sopenharmony_ci if (rv != 0) { 5722c593315Sopenharmony_ci client->disconnect(); 5732c593315Sopenharmony_ci } 5742c593315Sopenharmony_ci} 5752c593315Sopenharmony_ci} // namespace 5762c593315Sopenharmony_ci 5772c593315Sopenharmony_cinamespace { 5782c593315Sopenharmony_civoid timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { 5792c593315Sopenharmony_ci auto client = static_cast<HttpClient *>(w->data); 5802c593315Sopenharmony_ci std::cerr << "[ERROR] Timeout" << std::endl; 5812c593315Sopenharmony_ci client->disconnect(); 5822c593315Sopenharmony_ci} 5832c593315Sopenharmony_ci} // namespace 5842c593315Sopenharmony_ci 5852c593315Sopenharmony_cinamespace { 5862c593315Sopenharmony_civoid settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { 5872c593315Sopenharmony_ci auto client = static_cast<HttpClient *>(w->data); 5882c593315Sopenharmony_ci ev_timer_stop(loop, w); 5892c593315Sopenharmony_ci 5902c593315Sopenharmony_ci nghttp2_session_terminate_session(client->session, NGHTTP2_SETTINGS_TIMEOUT); 5912c593315Sopenharmony_ci 5922c593315Sopenharmony_ci client->signal_write(); 5932c593315Sopenharmony_ci} 5942c593315Sopenharmony_ci} // namespace 5952c593315Sopenharmony_ci 5962c593315Sopenharmony_ciHttpClient::HttpClient(const nghttp2_session_callbacks *callbacks, 5972c593315Sopenharmony_ci struct ev_loop *loop, SSL_CTX *ssl_ctx) 5982c593315Sopenharmony_ci : wb(&mcpool), 5992c593315Sopenharmony_ci session(nullptr), 6002c593315Sopenharmony_ci callbacks(callbacks), 6012c593315Sopenharmony_ci loop(loop), 6022c593315Sopenharmony_ci ssl_ctx(ssl_ctx), 6032c593315Sopenharmony_ci ssl(nullptr), 6042c593315Sopenharmony_ci addrs(nullptr), 6052c593315Sopenharmony_ci next_addr(nullptr), 6062c593315Sopenharmony_ci cur_addr(nullptr), 6072c593315Sopenharmony_ci complete(0), 6082c593315Sopenharmony_ci success(0), 6092c593315Sopenharmony_ci settings_payloadlen(0), 6102c593315Sopenharmony_ci state(ClientState::IDLE), 6112c593315Sopenharmony_ci upgrade_response_status_code(0), 6122c593315Sopenharmony_ci fd(-1), 6132c593315Sopenharmony_ci upgrade_response_complete(false) { 6142c593315Sopenharmony_ci ev_io_init(&wev, writecb, 0, EV_WRITE); 6152c593315Sopenharmony_ci ev_io_init(&rev, readcb, 0, EV_READ); 6162c593315Sopenharmony_ci 6172c593315Sopenharmony_ci wev.data = this; 6182c593315Sopenharmony_ci rev.data = this; 6192c593315Sopenharmony_ci 6202c593315Sopenharmony_ci ev_timer_init(&wt, timeoutcb, 0., config.timeout); 6212c593315Sopenharmony_ci ev_timer_init(&rt, timeoutcb, 0., config.timeout); 6222c593315Sopenharmony_ci 6232c593315Sopenharmony_ci wt.data = this; 6242c593315Sopenharmony_ci rt.data = this; 6252c593315Sopenharmony_ci 6262c593315Sopenharmony_ci ev_timer_init(&settings_timer, settings_timeout_cb, 0., 10.); 6272c593315Sopenharmony_ci 6282c593315Sopenharmony_ci settings_timer.data = this; 6292c593315Sopenharmony_ci} 6302c593315Sopenharmony_ci 6312c593315Sopenharmony_ciHttpClient::~HttpClient() { 6322c593315Sopenharmony_ci disconnect(); 6332c593315Sopenharmony_ci 6342c593315Sopenharmony_ci if (addrs) { 6352c593315Sopenharmony_ci freeaddrinfo(addrs); 6362c593315Sopenharmony_ci addrs = nullptr; 6372c593315Sopenharmony_ci next_addr = nullptr; 6382c593315Sopenharmony_ci } 6392c593315Sopenharmony_ci} 6402c593315Sopenharmony_ci 6412c593315Sopenharmony_cibool HttpClient::need_upgrade() const { 6422c593315Sopenharmony_ci return config.upgrade && scheme == "http"; 6432c593315Sopenharmony_ci} 6442c593315Sopenharmony_ci 6452c593315Sopenharmony_ciint HttpClient::resolve_host(const std::string &host, uint16_t port) { 6462c593315Sopenharmony_ci int rv; 6472c593315Sopenharmony_ci this->host = host; 6482c593315Sopenharmony_ci addrinfo hints{}; 6492c593315Sopenharmony_ci hints.ai_family = AF_UNSPEC; 6502c593315Sopenharmony_ci hints.ai_socktype = SOCK_STREAM; 6512c593315Sopenharmony_ci hints.ai_protocol = 0; 6522c593315Sopenharmony_ci hints.ai_flags = AI_ADDRCONFIG; 6532c593315Sopenharmony_ci rv = getaddrinfo(host.c_str(), util::utos(port).c_str(), &hints, &addrs); 6542c593315Sopenharmony_ci if (rv != 0) { 6552c593315Sopenharmony_ci std::cerr << "[ERROR] getaddrinfo() failed: " << gai_strerror(rv) 6562c593315Sopenharmony_ci << std::endl; 6572c593315Sopenharmony_ci return -1; 6582c593315Sopenharmony_ci } 6592c593315Sopenharmony_ci if (addrs == nullptr) { 6602c593315Sopenharmony_ci std::cerr << "[ERROR] No address returned" << std::endl; 6612c593315Sopenharmony_ci return -1; 6622c593315Sopenharmony_ci } 6632c593315Sopenharmony_ci next_addr = addrs; 6642c593315Sopenharmony_ci return 0; 6652c593315Sopenharmony_ci} 6662c593315Sopenharmony_ci 6672c593315Sopenharmony_cinamespace { 6682c593315Sopenharmony_ci// Just returns 1 to continue handshake. 6692c593315Sopenharmony_ciint verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } 6702c593315Sopenharmony_ci} // namespace 6712c593315Sopenharmony_ci 6722c593315Sopenharmony_ciint HttpClient::initiate_connection() { 6732c593315Sopenharmony_ci int rv; 6742c593315Sopenharmony_ci 6752c593315Sopenharmony_ci cur_addr = nullptr; 6762c593315Sopenharmony_ci while (next_addr) { 6772c593315Sopenharmony_ci cur_addr = next_addr; 6782c593315Sopenharmony_ci next_addr = next_addr->ai_next; 6792c593315Sopenharmony_ci fd = util::create_nonblock_socket(cur_addr->ai_family); 6802c593315Sopenharmony_ci if (fd == -1) { 6812c593315Sopenharmony_ci continue; 6822c593315Sopenharmony_ci } 6832c593315Sopenharmony_ci 6842c593315Sopenharmony_ci if (ssl_ctx) { 6852c593315Sopenharmony_ci // We are establishing TLS connection. 6862c593315Sopenharmony_ci ssl = SSL_new(ssl_ctx); 6872c593315Sopenharmony_ci if (!ssl) { 6882c593315Sopenharmony_ci std::cerr << "[ERROR] SSL_new() failed: " 6892c593315Sopenharmony_ci << ERR_error_string(ERR_get_error(), nullptr) << std::endl; 6902c593315Sopenharmony_ci return -1; 6912c593315Sopenharmony_ci } 6922c593315Sopenharmony_ci 6932c593315Sopenharmony_ci SSL_set_connect_state(ssl); 6942c593315Sopenharmony_ci 6952c593315Sopenharmony_ci // If the user overrode the :authority or host header, use that 6962c593315Sopenharmony_ci // value for the SNI extension 6972c593315Sopenharmony_ci const auto &host_string = 6982c593315Sopenharmony_ci config.host_override.empty() ? host : config.host_override; 6992c593315Sopenharmony_ci 7002c593315Sopenharmony_ci#if LIBRESSL_2_7_API || \ 7012c593315Sopenharmony_ci (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L) || \ 7022c593315Sopenharmony_ci defined(OPENSSL_IS_BORINGSSL) 7032c593315Sopenharmony_ci auto param = SSL_get0_param(ssl); 7042c593315Sopenharmony_ci X509_VERIFY_PARAM_set_hostflags(param, 0); 7052c593315Sopenharmony_ci X509_VERIFY_PARAM_set1_host(param, host_string.c_str(), 7062c593315Sopenharmony_ci host_string.size()); 7072c593315Sopenharmony_ci#endif // LIBRESSL_2_7_API || (!LIBRESSL_IN_USE && 7082c593315Sopenharmony_ci // OPENSSL_VERSION_NUMBER >= 0x10002000L) || 7092c593315Sopenharmony_ci // defined(OPENSSL_IS_BORINGSSL) 7102c593315Sopenharmony_ci SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb); 7112c593315Sopenharmony_ci 7122c593315Sopenharmony_ci if (!util::numeric_host(host_string.c_str())) { 7132c593315Sopenharmony_ci SSL_set_tlsext_host_name(ssl, host_string.c_str()); 7142c593315Sopenharmony_ci } 7152c593315Sopenharmony_ci } 7162c593315Sopenharmony_ci 7172c593315Sopenharmony_ci rv = connect(fd, cur_addr->ai_addr, cur_addr->ai_addrlen); 7182c593315Sopenharmony_ci 7192c593315Sopenharmony_ci if (rv != 0 && errno != EINPROGRESS) { 7202c593315Sopenharmony_ci if (ssl) { 7212c593315Sopenharmony_ci SSL_free(ssl); 7222c593315Sopenharmony_ci ssl = nullptr; 7232c593315Sopenharmony_ci } 7242c593315Sopenharmony_ci close(fd); 7252c593315Sopenharmony_ci fd = -1; 7262c593315Sopenharmony_ci continue; 7272c593315Sopenharmony_ci } 7282c593315Sopenharmony_ci break; 7292c593315Sopenharmony_ci } 7302c593315Sopenharmony_ci 7312c593315Sopenharmony_ci if (fd == -1) { 7322c593315Sopenharmony_ci return -1; 7332c593315Sopenharmony_ci } 7342c593315Sopenharmony_ci 7352c593315Sopenharmony_ci writefn = &HttpClient::connected; 7362c593315Sopenharmony_ci 7372c593315Sopenharmony_ci if (need_upgrade()) { 7382c593315Sopenharmony_ci on_readfn = &HttpClient::on_upgrade_read; 7392c593315Sopenharmony_ci on_writefn = &HttpClient::on_upgrade_connect; 7402c593315Sopenharmony_ci } else { 7412c593315Sopenharmony_ci on_readfn = &HttpClient::on_read; 7422c593315Sopenharmony_ci on_writefn = &HttpClient::on_write; 7432c593315Sopenharmony_ci } 7442c593315Sopenharmony_ci 7452c593315Sopenharmony_ci ev_io_set(&rev, fd, EV_READ); 7462c593315Sopenharmony_ci ev_io_set(&wev, fd, EV_WRITE); 7472c593315Sopenharmony_ci 7482c593315Sopenharmony_ci ev_io_start(loop, &wev); 7492c593315Sopenharmony_ci 7502c593315Sopenharmony_ci ev_timer_again(loop, &wt); 7512c593315Sopenharmony_ci 7522c593315Sopenharmony_ci return 0; 7532c593315Sopenharmony_ci} 7542c593315Sopenharmony_ci 7552c593315Sopenharmony_civoid HttpClient::disconnect() { 7562c593315Sopenharmony_ci state = ClientState::IDLE; 7572c593315Sopenharmony_ci 7582c593315Sopenharmony_ci for (auto req = std::begin(reqvec); req != std::end(reqvec); ++req) { 7592c593315Sopenharmony_ci if ((*req)->continue_timer) { 7602c593315Sopenharmony_ci (*req)->continue_timer->stop(); 7612c593315Sopenharmony_ci } 7622c593315Sopenharmony_ci } 7632c593315Sopenharmony_ci 7642c593315Sopenharmony_ci ev_timer_stop(loop, &settings_timer); 7652c593315Sopenharmony_ci 7662c593315Sopenharmony_ci ev_timer_stop(loop, &rt); 7672c593315Sopenharmony_ci ev_timer_stop(loop, &wt); 7682c593315Sopenharmony_ci 7692c593315Sopenharmony_ci ev_io_stop(loop, &rev); 7702c593315Sopenharmony_ci ev_io_stop(loop, &wev); 7712c593315Sopenharmony_ci 7722c593315Sopenharmony_ci nghttp2_session_del(session); 7732c593315Sopenharmony_ci session = nullptr; 7742c593315Sopenharmony_ci 7752c593315Sopenharmony_ci if (ssl) { 7762c593315Sopenharmony_ci SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN); 7772c593315Sopenharmony_ci ERR_clear_error(); 7782c593315Sopenharmony_ci SSL_shutdown(ssl); 7792c593315Sopenharmony_ci SSL_free(ssl); 7802c593315Sopenharmony_ci ssl = nullptr; 7812c593315Sopenharmony_ci } 7822c593315Sopenharmony_ci 7832c593315Sopenharmony_ci if (fd != -1) { 7842c593315Sopenharmony_ci shutdown(fd, SHUT_WR); 7852c593315Sopenharmony_ci close(fd); 7862c593315Sopenharmony_ci fd = -1; 7872c593315Sopenharmony_ci } 7882c593315Sopenharmony_ci} 7892c593315Sopenharmony_ci 7902c593315Sopenharmony_ciint HttpClient::read_clear() { 7912c593315Sopenharmony_ci ev_timer_again(loop, &rt); 7922c593315Sopenharmony_ci 7932c593315Sopenharmony_ci std::array<uint8_t, 8_k> buf; 7942c593315Sopenharmony_ci 7952c593315Sopenharmony_ci for (;;) { 7962c593315Sopenharmony_ci ssize_t nread; 7972c593315Sopenharmony_ci while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR) 7982c593315Sopenharmony_ci ; 7992c593315Sopenharmony_ci if (nread == -1) { 8002c593315Sopenharmony_ci if (errno == EAGAIN || errno == EWOULDBLOCK) { 8012c593315Sopenharmony_ci return 0; 8022c593315Sopenharmony_ci } 8032c593315Sopenharmony_ci return -1; 8042c593315Sopenharmony_ci } 8052c593315Sopenharmony_ci 8062c593315Sopenharmony_ci if (nread == 0) { 8072c593315Sopenharmony_ci return -1; 8082c593315Sopenharmony_ci } 8092c593315Sopenharmony_ci 8102c593315Sopenharmony_ci if (on_readfn(*this, buf.data(), nread) != 0) { 8112c593315Sopenharmony_ci return -1; 8122c593315Sopenharmony_ci } 8132c593315Sopenharmony_ci } 8142c593315Sopenharmony_ci 8152c593315Sopenharmony_ci return 0; 8162c593315Sopenharmony_ci} 8172c593315Sopenharmony_ci 8182c593315Sopenharmony_ciint HttpClient::write_clear() { 8192c593315Sopenharmony_ci ev_timer_again(loop, &rt); 8202c593315Sopenharmony_ci 8212c593315Sopenharmony_ci std::array<struct iovec, 2> iov; 8222c593315Sopenharmony_ci 8232c593315Sopenharmony_ci for (;;) { 8242c593315Sopenharmony_ci if (on_writefn(*this) != 0) { 8252c593315Sopenharmony_ci return -1; 8262c593315Sopenharmony_ci } 8272c593315Sopenharmony_ci 8282c593315Sopenharmony_ci auto iovcnt = wb.riovec(iov.data(), iov.size()); 8292c593315Sopenharmony_ci 8302c593315Sopenharmony_ci if (iovcnt == 0) { 8312c593315Sopenharmony_ci break; 8322c593315Sopenharmony_ci } 8332c593315Sopenharmony_ci 8342c593315Sopenharmony_ci ssize_t nwrite; 8352c593315Sopenharmony_ci while ((nwrite = writev(fd, iov.data(), iovcnt)) == -1 && errno == EINTR) 8362c593315Sopenharmony_ci ; 8372c593315Sopenharmony_ci if (nwrite == -1) { 8382c593315Sopenharmony_ci if (errno == EAGAIN || errno == EWOULDBLOCK) { 8392c593315Sopenharmony_ci ev_io_start(loop, &wev); 8402c593315Sopenharmony_ci ev_timer_again(loop, &wt); 8412c593315Sopenharmony_ci return 0; 8422c593315Sopenharmony_ci } 8432c593315Sopenharmony_ci return -1; 8442c593315Sopenharmony_ci } 8452c593315Sopenharmony_ci 8462c593315Sopenharmony_ci wb.drain(nwrite); 8472c593315Sopenharmony_ci } 8482c593315Sopenharmony_ci 8492c593315Sopenharmony_ci ev_io_stop(loop, &wev); 8502c593315Sopenharmony_ci ev_timer_stop(loop, &wt); 8512c593315Sopenharmony_ci 8522c593315Sopenharmony_ci return 0; 8532c593315Sopenharmony_ci} 8542c593315Sopenharmony_ci 8552c593315Sopenharmony_ciint HttpClient::noop() { return 0; } 8562c593315Sopenharmony_ci 8572c593315Sopenharmony_civoid HttpClient::connect_fail() { 8582c593315Sopenharmony_ci if (state == ClientState::IDLE) { 8592c593315Sopenharmony_ci std::cerr << "[ERROR] Could not connect to the address " 8602c593315Sopenharmony_ci << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) 8612c593315Sopenharmony_ci << std::endl; 8622c593315Sopenharmony_ci } 8632c593315Sopenharmony_ci auto cur_state = state; 8642c593315Sopenharmony_ci disconnect(); 8652c593315Sopenharmony_ci if (cur_state == ClientState::IDLE) { 8662c593315Sopenharmony_ci if (initiate_connection() == 0) { 8672c593315Sopenharmony_ci std::cerr << "Trying next address " 8682c593315Sopenharmony_ci << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) 8692c593315Sopenharmony_ci << std::endl; 8702c593315Sopenharmony_ci } 8712c593315Sopenharmony_ci } 8722c593315Sopenharmony_ci} 8732c593315Sopenharmony_ci 8742c593315Sopenharmony_ciint HttpClient::connected() { 8752c593315Sopenharmony_ci if (!util::check_socket_connected(fd)) { 8762c593315Sopenharmony_ci return ERR_CONNECT_FAIL; 8772c593315Sopenharmony_ci } 8782c593315Sopenharmony_ci 8792c593315Sopenharmony_ci if (config.verbose) { 8802c593315Sopenharmony_ci print_timer(); 8812c593315Sopenharmony_ci std::cout << " Connected" << std::endl; 8822c593315Sopenharmony_ci } 8832c593315Sopenharmony_ci 8842c593315Sopenharmony_ci state = ClientState::CONNECTED; 8852c593315Sopenharmony_ci 8862c593315Sopenharmony_ci ev_io_start(loop, &rev); 8872c593315Sopenharmony_ci ev_io_stop(loop, &wev); 8882c593315Sopenharmony_ci 8892c593315Sopenharmony_ci ev_timer_again(loop, &rt); 8902c593315Sopenharmony_ci ev_timer_stop(loop, &wt); 8912c593315Sopenharmony_ci 8922c593315Sopenharmony_ci if (ssl) { 8932c593315Sopenharmony_ci SSL_set_fd(ssl, fd); 8942c593315Sopenharmony_ci 8952c593315Sopenharmony_ci readfn = &HttpClient::tls_handshake; 8962c593315Sopenharmony_ci writefn = &HttpClient::tls_handshake; 8972c593315Sopenharmony_ci 8982c593315Sopenharmony_ci return do_write(); 8992c593315Sopenharmony_ci } 9002c593315Sopenharmony_ci 9012c593315Sopenharmony_ci readfn = &HttpClient::read_clear; 9022c593315Sopenharmony_ci writefn = &HttpClient::write_clear; 9032c593315Sopenharmony_ci 9042c593315Sopenharmony_ci if (need_upgrade()) { 9052c593315Sopenharmony_ci htp = std::make_unique<llhttp_t>(); 9062c593315Sopenharmony_ci llhttp_init(htp.get(), HTTP_RESPONSE, &htp_hooks); 9072c593315Sopenharmony_ci htp->data = this; 9082c593315Sopenharmony_ci 9092c593315Sopenharmony_ci return do_write(); 9102c593315Sopenharmony_ci } 9112c593315Sopenharmony_ci 9122c593315Sopenharmony_ci if (connection_made() != 0) { 9132c593315Sopenharmony_ci return -1; 9142c593315Sopenharmony_ci } 9152c593315Sopenharmony_ci 9162c593315Sopenharmony_ci return 0; 9172c593315Sopenharmony_ci} 9182c593315Sopenharmony_ci 9192c593315Sopenharmony_cinamespace { 9202c593315Sopenharmony_cisize_t populate_settings(nghttp2_settings_entry *iv) { 9212c593315Sopenharmony_ci size_t niv = 2; 9222c593315Sopenharmony_ci 9232c593315Sopenharmony_ci iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; 9242c593315Sopenharmony_ci iv[0].value = config.max_concurrent_streams; 9252c593315Sopenharmony_ci 9262c593315Sopenharmony_ci iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; 9272c593315Sopenharmony_ci if (config.window_bits != -1) { 9282c593315Sopenharmony_ci iv[1].value = (1 << config.window_bits) - 1; 9292c593315Sopenharmony_ci } else { 9302c593315Sopenharmony_ci iv[1].value = NGHTTP2_INITIAL_WINDOW_SIZE; 9312c593315Sopenharmony_ci } 9322c593315Sopenharmony_ci 9332c593315Sopenharmony_ci if (config.header_table_size >= 0) { 9342c593315Sopenharmony_ci if (config.min_header_table_size < config.header_table_size) { 9352c593315Sopenharmony_ci iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; 9362c593315Sopenharmony_ci iv[niv].value = config.min_header_table_size; 9372c593315Sopenharmony_ci ++niv; 9382c593315Sopenharmony_ci } 9392c593315Sopenharmony_ci 9402c593315Sopenharmony_ci iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; 9412c593315Sopenharmony_ci iv[niv].value = config.header_table_size; 9422c593315Sopenharmony_ci ++niv; 9432c593315Sopenharmony_ci } 9442c593315Sopenharmony_ci 9452c593315Sopenharmony_ci if (config.no_push) { 9462c593315Sopenharmony_ci iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; 9472c593315Sopenharmony_ci iv[niv].value = 0; 9482c593315Sopenharmony_ci ++niv; 9492c593315Sopenharmony_ci } 9502c593315Sopenharmony_ci 9512c593315Sopenharmony_ci if (config.no_rfc7540_pri) { 9522c593315Sopenharmony_ci iv[niv].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; 9532c593315Sopenharmony_ci iv[niv].value = 1; 9542c593315Sopenharmony_ci ++niv; 9552c593315Sopenharmony_ci } 9562c593315Sopenharmony_ci 9572c593315Sopenharmony_ci return niv; 9582c593315Sopenharmony_ci} 9592c593315Sopenharmony_ci} // namespace 9602c593315Sopenharmony_ci 9612c593315Sopenharmony_ciint HttpClient::on_upgrade_connect() { 9622c593315Sopenharmony_ci ssize_t rv; 9632c593315Sopenharmony_ci record_connect_end_time(); 9642c593315Sopenharmony_ci assert(!reqvec.empty()); 9652c593315Sopenharmony_ci std::array<nghttp2_settings_entry, 16> iv; 9662c593315Sopenharmony_ci size_t niv = populate_settings(iv.data()); 9672c593315Sopenharmony_ci assert(settings_payload.size() >= 8 * niv); 9682c593315Sopenharmony_ci rv = nghttp2_pack_settings_payload(settings_payload.data(), 9692c593315Sopenharmony_ci settings_payload.size(), iv.data(), niv); 9702c593315Sopenharmony_ci if (rv < 0) { 9712c593315Sopenharmony_ci return -1; 9722c593315Sopenharmony_ci } 9732c593315Sopenharmony_ci settings_payloadlen = rv; 9742c593315Sopenharmony_ci auto token68 = 9752c593315Sopenharmony_ci base64::encode(std::begin(settings_payload), 9762c593315Sopenharmony_ci std::begin(settings_payload) + settings_payloadlen); 9772c593315Sopenharmony_ci util::to_token68(token68); 9782c593315Sopenharmony_ci 9792c593315Sopenharmony_ci std::string req; 9802c593315Sopenharmony_ci if (reqvec[0]->data_prd) { 9812c593315Sopenharmony_ci // If the request contains upload data, use OPTIONS * to upgrade 9822c593315Sopenharmony_ci req = "OPTIONS *"; 9832c593315Sopenharmony_ci } else { 9842c593315Sopenharmony_ci auto meth = std::find_if( 9852c593315Sopenharmony_ci std::begin(config.headers), std::end(config.headers), 9862c593315Sopenharmony_ci [](const Header &kv) { return util::streq_l(":method", kv.name); }); 9872c593315Sopenharmony_ci 9882c593315Sopenharmony_ci if (meth == std::end(config.headers)) { 9892c593315Sopenharmony_ci req = "GET "; 9902c593315Sopenharmony_ci reqvec[0]->method = "GET"; 9912c593315Sopenharmony_ci } else { 9922c593315Sopenharmony_ci req = (*meth).value; 9932c593315Sopenharmony_ci req += ' '; 9942c593315Sopenharmony_ci reqvec[0]->method = (*meth).value; 9952c593315Sopenharmony_ci } 9962c593315Sopenharmony_ci req += reqvec[0]->make_reqpath(); 9972c593315Sopenharmony_ci } 9982c593315Sopenharmony_ci 9992c593315Sopenharmony_ci auto headers = Headers{{"host", hostport}, 10002c593315Sopenharmony_ci {"connection", "Upgrade, HTTP2-Settings"}, 10012c593315Sopenharmony_ci {"upgrade", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID}, 10022c593315Sopenharmony_ci {"http2-settings", token68}, 10032c593315Sopenharmony_ci {"accept", "*/*"}, 10042c593315Sopenharmony_ci {"user-agent", "nghttp2/" NGHTTP2_VERSION}}; 10052c593315Sopenharmony_ci auto initial_headerslen = headers.size(); 10062c593315Sopenharmony_ci 10072c593315Sopenharmony_ci for (auto &kv : config.headers) { 10082c593315Sopenharmony_ci size_t i; 10092c593315Sopenharmony_ci if (kv.name.empty() || kv.name[0] == ':') { 10102c593315Sopenharmony_ci continue; 10112c593315Sopenharmony_ci } 10122c593315Sopenharmony_ci for (i = 0; i < initial_headerslen; ++i) { 10132c593315Sopenharmony_ci if (kv.name == headers[i].name) { 10142c593315Sopenharmony_ci headers[i].value = kv.value; 10152c593315Sopenharmony_ci break; 10162c593315Sopenharmony_ci } 10172c593315Sopenharmony_ci } 10182c593315Sopenharmony_ci if (i < initial_headerslen) { 10192c593315Sopenharmony_ci continue; 10202c593315Sopenharmony_ci } 10212c593315Sopenharmony_ci headers.emplace_back(kv.name, kv.value, kv.no_index); 10222c593315Sopenharmony_ci } 10232c593315Sopenharmony_ci 10242c593315Sopenharmony_ci req += " HTTP/1.1\r\n"; 10252c593315Sopenharmony_ci 10262c593315Sopenharmony_ci for (auto &kv : headers) { 10272c593315Sopenharmony_ci req += kv.name; 10282c593315Sopenharmony_ci req += ": "; 10292c593315Sopenharmony_ci req += kv.value; 10302c593315Sopenharmony_ci req += "\r\n"; 10312c593315Sopenharmony_ci } 10322c593315Sopenharmony_ci req += "\r\n"; 10332c593315Sopenharmony_ci 10342c593315Sopenharmony_ci wb.append(req); 10352c593315Sopenharmony_ci 10362c593315Sopenharmony_ci if (config.verbose) { 10372c593315Sopenharmony_ci print_timer(); 10382c593315Sopenharmony_ci std::cout << " HTTP Upgrade request\n" << req << std::endl; 10392c593315Sopenharmony_ci } 10402c593315Sopenharmony_ci 10412c593315Sopenharmony_ci if (!reqvec[0]->data_prd) { 10422c593315Sopenharmony_ci // record request time if this is a part of real request. 10432c593315Sopenharmony_ci reqvec[0]->record_request_start_time(); 10442c593315Sopenharmony_ci reqvec[0]->req_nva = std::move(headers); 10452c593315Sopenharmony_ci } 10462c593315Sopenharmony_ci 10472c593315Sopenharmony_ci on_writefn = &HttpClient::noop; 10482c593315Sopenharmony_ci 10492c593315Sopenharmony_ci signal_write(); 10502c593315Sopenharmony_ci 10512c593315Sopenharmony_ci return 0; 10522c593315Sopenharmony_ci} 10532c593315Sopenharmony_ci 10542c593315Sopenharmony_ciint HttpClient::on_upgrade_read(const uint8_t *data, size_t len) { 10552c593315Sopenharmony_ci int rv; 10562c593315Sopenharmony_ci 10572c593315Sopenharmony_ci auto htperr = 10582c593315Sopenharmony_ci llhttp_execute(htp.get(), reinterpret_cast<const char *>(data), len); 10592c593315Sopenharmony_ci auto nread = htperr == HPE_OK 10602c593315Sopenharmony_ci ? len 10612c593315Sopenharmony_ci : static_cast<size_t>(reinterpret_cast<const uint8_t *>( 10622c593315Sopenharmony_ci llhttp_get_error_pos(htp.get())) - 10632c593315Sopenharmony_ci data); 10642c593315Sopenharmony_ci 10652c593315Sopenharmony_ci if (config.verbose) { 10662c593315Sopenharmony_ci std::cout.write(reinterpret_cast<const char *>(data), nread); 10672c593315Sopenharmony_ci } 10682c593315Sopenharmony_ci 10692c593315Sopenharmony_ci if (htperr != HPE_OK && htperr != HPE_PAUSED_UPGRADE) { 10702c593315Sopenharmony_ci std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: " 10712c593315Sopenharmony_ci << "(" << llhttp_errno_name(htperr) << ") " 10722c593315Sopenharmony_ci << llhttp_get_error_reason(htp.get()) << std::endl; 10732c593315Sopenharmony_ci return -1; 10742c593315Sopenharmony_ci } 10752c593315Sopenharmony_ci 10762c593315Sopenharmony_ci if (!upgrade_response_complete) { 10772c593315Sopenharmony_ci return 0; 10782c593315Sopenharmony_ci } 10792c593315Sopenharmony_ci 10802c593315Sopenharmony_ci if (config.verbose) { 10812c593315Sopenharmony_ci std::cout << std::endl; 10822c593315Sopenharmony_ci } 10832c593315Sopenharmony_ci 10842c593315Sopenharmony_ci if (upgrade_response_status_code != 101) { 10852c593315Sopenharmony_ci std::cerr << "[ERROR] HTTP Upgrade failed" << std::endl; 10862c593315Sopenharmony_ci 10872c593315Sopenharmony_ci return -1; 10882c593315Sopenharmony_ci } 10892c593315Sopenharmony_ci 10902c593315Sopenharmony_ci if (config.verbose) { 10912c593315Sopenharmony_ci print_timer(); 10922c593315Sopenharmony_ci std::cout << " HTTP Upgrade success" << std::endl; 10932c593315Sopenharmony_ci } 10942c593315Sopenharmony_ci 10952c593315Sopenharmony_ci on_readfn = &HttpClient::on_read; 10962c593315Sopenharmony_ci on_writefn = &HttpClient::on_write; 10972c593315Sopenharmony_ci 10982c593315Sopenharmony_ci rv = connection_made(); 10992c593315Sopenharmony_ci if (rv != 0) { 11002c593315Sopenharmony_ci return rv; 11012c593315Sopenharmony_ci } 11022c593315Sopenharmony_ci 11032c593315Sopenharmony_ci // Read remaining data in the buffer because it is not notified 11042c593315Sopenharmony_ci // callback anymore. 11052c593315Sopenharmony_ci rv = on_readfn(*this, data + nread, len - nread); 11062c593315Sopenharmony_ci if (rv != 0) { 11072c593315Sopenharmony_ci return rv; 11082c593315Sopenharmony_ci } 11092c593315Sopenharmony_ci 11102c593315Sopenharmony_ci return 0; 11112c593315Sopenharmony_ci} 11122c593315Sopenharmony_ci 11132c593315Sopenharmony_ciint HttpClient::do_read() { return readfn(*this); } 11142c593315Sopenharmony_ciint HttpClient::do_write() { return writefn(*this); } 11152c593315Sopenharmony_ci 11162c593315Sopenharmony_ciint HttpClient::connection_made() { 11172c593315Sopenharmony_ci int rv; 11182c593315Sopenharmony_ci 11192c593315Sopenharmony_ci if (!need_upgrade()) { 11202c593315Sopenharmony_ci record_connect_end_time(); 11212c593315Sopenharmony_ci } 11222c593315Sopenharmony_ci 11232c593315Sopenharmony_ci if (ssl) { 11242c593315Sopenharmony_ci // Check NPN or ALPN result 11252c593315Sopenharmony_ci const unsigned char *next_proto = nullptr; 11262c593315Sopenharmony_ci unsigned int next_proto_len; 11272c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG 11282c593315Sopenharmony_ci SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); 11292c593315Sopenharmony_ci#endif // !OPENSSL_NO_NEXTPROTONEG 11302c593315Sopenharmony_ci for (int i = 0; i < 2; ++i) { 11312c593315Sopenharmony_ci if (next_proto) { 11322c593315Sopenharmony_ci auto proto = StringRef{next_proto, next_proto_len}; 11332c593315Sopenharmony_ci if (config.verbose) { 11342c593315Sopenharmony_ci std::cout << "The negotiated protocol: " << proto << std::endl; 11352c593315Sopenharmony_ci } 11362c593315Sopenharmony_ci if (!util::check_h2_is_selected(proto)) { 11372c593315Sopenharmony_ci next_proto = nullptr; 11382c593315Sopenharmony_ci } 11392c593315Sopenharmony_ci break; 11402c593315Sopenharmony_ci } 11412c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x10002000L 11422c593315Sopenharmony_ci SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); 11432c593315Sopenharmony_ci#else // OPENSSL_VERSION_NUMBER < 0x10002000L 11442c593315Sopenharmony_ci break; 11452c593315Sopenharmony_ci#endif // OPENSSL_VERSION_NUMBER < 0x10002000L 11462c593315Sopenharmony_ci } 11472c593315Sopenharmony_ci if (!next_proto) { 11482c593315Sopenharmony_ci print_protocol_nego_error(); 11492c593315Sopenharmony_ci return -1; 11502c593315Sopenharmony_ci } 11512c593315Sopenharmony_ci } 11522c593315Sopenharmony_ci 11532c593315Sopenharmony_ci rv = nghttp2_session_client_new2(&session, callbacks, this, 11542c593315Sopenharmony_ci config.http2_option); 11552c593315Sopenharmony_ci 11562c593315Sopenharmony_ci if (rv != 0) { 11572c593315Sopenharmony_ci return -1; 11582c593315Sopenharmony_ci } 11592c593315Sopenharmony_ci if (need_upgrade()) { 11602c593315Sopenharmony_ci // Adjust stream user-data depending on the existence of upload 11612c593315Sopenharmony_ci // data 11622c593315Sopenharmony_ci Request *stream_user_data = nullptr; 11632c593315Sopenharmony_ci if (!reqvec[0]->data_prd) { 11642c593315Sopenharmony_ci stream_user_data = reqvec[0].get(); 11652c593315Sopenharmony_ci } 11662c593315Sopenharmony_ci // If HEAD is used, that is only when user specified it with -H 11672c593315Sopenharmony_ci // option. 11682c593315Sopenharmony_ci auto head_request = stream_user_data && stream_user_data->method == "HEAD"; 11692c593315Sopenharmony_ci rv = nghttp2_session_upgrade2(session, settings_payload.data(), 11702c593315Sopenharmony_ci settings_payloadlen, head_request, 11712c593315Sopenharmony_ci stream_user_data); 11722c593315Sopenharmony_ci if (rv != 0) { 11732c593315Sopenharmony_ci std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: " 11742c593315Sopenharmony_ci << nghttp2_strerror(rv) << std::endl; 11752c593315Sopenharmony_ci return -1; 11762c593315Sopenharmony_ci } 11772c593315Sopenharmony_ci if (stream_user_data) { 11782c593315Sopenharmony_ci stream_user_data->stream_id = 1; 11792c593315Sopenharmony_ci request_done(stream_user_data); 11802c593315Sopenharmony_ci } 11812c593315Sopenharmony_ci } 11822c593315Sopenharmony_ci // If upgrade succeeds, the SETTINGS value sent with 11832c593315Sopenharmony_ci // HTTP2-Settings header field has already been submitted to 11842c593315Sopenharmony_ci // session object. 11852c593315Sopenharmony_ci if (!need_upgrade()) { 11862c593315Sopenharmony_ci std::array<nghttp2_settings_entry, 16> iv; 11872c593315Sopenharmony_ci auto niv = populate_settings(iv.data()); 11882c593315Sopenharmony_ci rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv.data(), niv); 11892c593315Sopenharmony_ci if (rv != 0) { 11902c593315Sopenharmony_ci return -1; 11912c593315Sopenharmony_ci } 11922c593315Sopenharmony_ci } 11932c593315Sopenharmony_ci if (!config.no_dep) { 11942c593315Sopenharmony_ci // Create anchor stream nodes 11952c593315Sopenharmony_ci nghttp2_priority_spec pri_spec; 11962c593315Sopenharmony_ci 11972c593315Sopenharmony_ci for (auto &anchor : anchors) { 11982c593315Sopenharmony_ci nghttp2_priority_spec_init(&pri_spec, anchor.dep_stream_id, anchor.weight, 11992c593315Sopenharmony_ci 0); 12002c593315Sopenharmony_ci rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, anchor.stream_id, 12012c593315Sopenharmony_ci &pri_spec); 12022c593315Sopenharmony_ci if (rv != 0) { 12032c593315Sopenharmony_ci return -1; 12042c593315Sopenharmony_ci } 12052c593315Sopenharmony_ci } 12062c593315Sopenharmony_ci 12072c593315Sopenharmony_ci rv = nghttp2_session_set_next_stream_id( 12082c593315Sopenharmony_ci session, anchors[ANCHOR_FOLLOWERS].stream_id + 2); 12092c593315Sopenharmony_ci if (rv != 0) { 12102c593315Sopenharmony_ci return -1; 12112c593315Sopenharmony_ci } 12122c593315Sopenharmony_ci 12132c593315Sopenharmony_ci if (need_upgrade() && !reqvec[0]->data_prd) { 12142c593315Sopenharmony_ci // Amend the priority because we cannot send priority in 12152c593315Sopenharmony_ci // HTTP/1.1 Upgrade. 12162c593315Sopenharmony_ci auto &anchor = anchors[ANCHOR_FOLLOWERS]; 12172c593315Sopenharmony_ci nghttp2_priority_spec_init(&pri_spec, anchor.stream_id, 12182c593315Sopenharmony_ci reqvec[0]->pri_spec.weight, 0); 12192c593315Sopenharmony_ci 12202c593315Sopenharmony_ci rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec); 12212c593315Sopenharmony_ci if (rv != 0) { 12222c593315Sopenharmony_ci return -1; 12232c593315Sopenharmony_ci } 12242c593315Sopenharmony_ci } 12252c593315Sopenharmony_ci } else if (need_upgrade() && !reqvec[0]->data_prd && 12262c593315Sopenharmony_ci reqvec[0]->pri_spec.weight != NGHTTP2_DEFAULT_WEIGHT) { 12272c593315Sopenharmony_ci // Amend the priority because we cannot send priority in HTTP/1.1 12282c593315Sopenharmony_ci // Upgrade. 12292c593315Sopenharmony_ci nghttp2_priority_spec pri_spec; 12302c593315Sopenharmony_ci 12312c593315Sopenharmony_ci nghttp2_priority_spec_init(&pri_spec, 0, reqvec[0]->pri_spec.weight, 0); 12322c593315Sopenharmony_ci 12332c593315Sopenharmony_ci rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec); 12342c593315Sopenharmony_ci if (rv != 0) { 12352c593315Sopenharmony_ci return -1; 12362c593315Sopenharmony_ci } 12372c593315Sopenharmony_ci } 12382c593315Sopenharmony_ci 12392c593315Sopenharmony_ci ev_timer_again(loop, &settings_timer); 12402c593315Sopenharmony_ci 12412c593315Sopenharmony_ci if (config.connection_window_bits != -1) { 12422c593315Sopenharmony_ci int32_t window_size = (1 << config.connection_window_bits) - 1; 12432c593315Sopenharmony_ci rv = nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0, 12442c593315Sopenharmony_ci window_size); 12452c593315Sopenharmony_ci if (rv != 0) { 12462c593315Sopenharmony_ci return -1; 12472c593315Sopenharmony_ci } 12482c593315Sopenharmony_ci } 12492c593315Sopenharmony_ci // Adjust first request depending on the existence of the upload 12502c593315Sopenharmony_ci // data 12512c593315Sopenharmony_ci for (auto i = std::begin(reqvec) + (need_upgrade() && !reqvec[0]->data_prd); 12522c593315Sopenharmony_ci i != std::end(reqvec); ++i) { 12532c593315Sopenharmony_ci if (submit_request(this, config.headers, (*i).get()) != 0) { 12542c593315Sopenharmony_ci return -1; 12552c593315Sopenharmony_ci } 12562c593315Sopenharmony_ci } 12572c593315Sopenharmony_ci 12582c593315Sopenharmony_ci signal_write(); 12592c593315Sopenharmony_ci 12602c593315Sopenharmony_ci return 0; 12612c593315Sopenharmony_ci} 12622c593315Sopenharmony_ci 12632c593315Sopenharmony_ciint HttpClient::on_read(const uint8_t *data, size_t len) { 12642c593315Sopenharmony_ci if (config.hexdump) { 12652c593315Sopenharmony_ci util::hexdump(stdout, data, len); 12662c593315Sopenharmony_ci } 12672c593315Sopenharmony_ci 12682c593315Sopenharmony_ci auto rv = nghttp2_session_mem_recv(session, data, len); 12692c593315Sopenharmony_ci if (rv < 0) { 12702c593315Sopenharmony_ci std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: " 12712c593315Sopenharmony_ci << nghttp2_strerror(rv) << std::endl; 12722c593315Sopenharmony_ci return -1; 12732c593315Sopenharmony_ci } 12742c593315Sopenharmony_ci 12752c593315Sopenharmony_ci assert(static_cast<size_t>(rv) == len); 12762c593315Sopenharmony_ci 12772c593315Sopenharmony_ci if (nghttp2_session_want_read(session) == 0 && 12782c593315Sopenharmony_ci nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { 12792c593315Sopenharmony_ci return -1; 12802c593315Sopenharmony_ci } 12812c593315Sopenharmony_ci 12822c593315Sopenharmony_ci signal_write(); 12832c593315Sopenharmony_ci 12842c593315Sopenharmony_ci return 0; 12852c593315Sopenharmony_ci} 12862c593315Sopenharmony_ci 12872c593315Sopenharmony_ciint HttpClient::on_write() { 12882c593315Sopenharmony_ci for (;;) { 12892c593315Sopenharmony_ci if (wb.rleft() >= 16384) { 12902c593315Sopenharmony_ci return 0; 12912c593315Sopenharmony_ci } 12922c593315Sopenharmony_ci 12932c593315Sopenharmony_ci const uint8_t *data; 12942c593315Sopenharmony_ci auto len = nghttp2_session_mem_send(session, &data); 12952c593315Sopenharmony_ci if (len < 0) { 12962c593315Sopenharmony_ci std::cerr << "[ERROR] nghttp2_session_send() returned error: " 12972c593315Sopenharmony_ci << nghttp2_strerror(len) << std::endl; 12982c593315Sopenharmony_ci return -1; 12992c593315Sopenharmony_ci } 13002c593315Sopenharmony_ci 13012c593315Sopenharmony_ci if (len == 0) { 13022c593315Sopenharmony_ci break; 13032c593315Sopenharmony_ci } 13042c593315Sopenharmony_ci 13052c593315Sopenharmony_ci wb.append(data, len); 13062c593315Sopenharmony_ci } 13072c593315Sopenharmony_ci 13082c593315Sopenharmony_ci if (nghttp2_session_want_read(session) == 0 && 13092c593315Sopenharmony_ci nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { 13102c593315Sopenharmony_ci return -1; 13112c593315Sopenharmony_ci } 13122c593315Sopenharmony_ci 13132c593315Sopenharmony_ci return 0; 13142c593315Sopenharmony_ci} 13152c593315Sopenharmony_ci 13162c593315Sopenharmony_ciint HttpClient::tls_handshake() { 13172c593315Sopenharmony_ci ev_timer_again(loop, &rt); 13182c593315Sopenharmony_ci 13192c593315Sopenharmony_ci ERR_clear_error(); 13202c593315Sopenharmony_ci 13212c593315Sopenharmony_ci auto rv = SSL_do_handshake(ssl); 13222c593315Sopenharmony_ci 13232c593315Sopenharmony_ci if (rv <= 0) { 13242c593315Sopenharmony_ci auto err = SSL_get_error(ssl, rv); 13252c593315Sopenharmony_ci switch (err) { 13262c593315Sopenharmony_ci case SSL_ERROR_WANT_READ: 13272c593315Sopenharmony_ci ev_io_stop(loop, &wev); 13282c593315Sopenharmony_ci ev_timer_stop(loop, &wt); 13292c593315Sopenharmony_ci return 0; 13302c593315Sopenharmony_ci case SSL_ERROR_WANT_WRITE: 13312c593315Sopenharmony_ci ev_io_start(loop, &wev); 13322c593315Sopenharmony_ci ev_timer_again(loop, &wt); 13332c593315Sopenharmony_ci return 0; 13342c593315Sopenharmony_ci default: 13352c593315Sopenharmony_ci return -1; 13362c593315Sopenharmony_ci } 13372c593315Sopenharmony_ci } 13382c593315Sopenharmony_ci 13392c593315Sopenharmony_ci ev_io_stop(loop, &wev); 13402c593315Sopenharmony_ci ev_timer_stop(loop, &wt); 13412c593315Sopenharmony_ci 13422c593315Sopenharmony_ci readfn = &HttpClient::read_tls; 13432c593315Sopenharmony_ci writefn = &HttpClient::write_tls; 13442c593315Sopenharmony_ci 13452c593315Sopenharmony_ci if (config.verify_peer) { 13462c593315Sopenharmony_ci auto verify_res = SSL_get_verify_result(ssl); 13472c593315Sopenharmony_ci if (verify_res != X509_V_OK) { 13482c593315Sopenharmony_ci std::cerr << "[WARNING] Certificate verification failed: " 13492c593315Sopenharmony_ci << X509_verify_cert_error_string(verify_res) << std::endl; 13502c593315Sopenharmony_ci } 13512c593315Sopenharmony_ci } 13522c593315Sopenharmony_ci 13532c593315Sopenharmony_ci if (connection_made() != 0) { 13542c593315Sopenharmony_ci return -1; 13552c593315Sopenharmony_ci } 13562c593315Sopenharmony_ci 13572c593315Sopenharmony_ci return 0; 13582c593315Sopenharmony_ci} 13592c593315Sopenharmony_ci 13602c593315Sopenharmony_ciint HttpClient::read_tls() { 13612c593315Sopenharmony_ci ev_timer_again(loop, &rt); 13622c593315Sopenharmony_ci 13632c593315Sopenharmony_ci ERR_clear_error(); 13642c593315Sopenharmony_ci 13652c593315Sopenharmony_ci std::array<uint8_t, 8_k> buf; 13662c593315Sopenharmony_ci for (;;) { 13672c593315Sopenharmony_ci auto rv = SSL_read(ssl, buf.data(), buf.size()); 13682c593315Sopenharmony_ci 13692c593315Sopenharmony_ci if (rv <= 0) { 13702c593315Sopenharmony_ci auto err = SSL_get_error(ssl, rv); 13712c593315Sopenharmony_ci switch (err) { 13722c593315Sopenharmony_ci case SSL_ERROR_WANT_READ: 13732c593315Sopenharmony_ci return 0; 13742c593315Sopenharmony_ci case SSL_ERROR_WANT_WRITE: 13752c593315Sopenharmony_ci // renegotiation started 13762c593315Sopenharmony_ci return -1; 13772c593315Sopenharmony_ci default: 13782c593315Sopenharmony_ci return -1; 13792c593315Sopenharmony_ci } 13802c593315Sopenharmony_ci } 13812c593315Sopenharmony_ci 13822c593315Sopenharmony_ci if (on_readfn(*this, buf.data(), rv) != 0) { 13832c593315Sopenharmony_ci return -1; 13842c593315Sopenharmony_ci } 13852c593315Sopenharmony_ci } 13862c593315Sopenharmony_ci} 13872c593315Sopenharmony_ci 13882c593315Sopenharmony_ciint HttpClient::write_tls() { 13892c593315Sopenharmony_ci ev_timer_again(loop, &rt); 13902c593315Sopenharmony_ci 13912c593315Sopenharmony_ci ERR_clear_error(); 13922c593315Sopenharmony_ci 13932c593315Sopenharmony_ci struct iovec iov; 13942c593315Sopenharmony_ci 13952c593315Sopenharmony_ci for (;;) { 13962c593315Sopenharmony_ci if (on_writefn(*this) != 0) { 13972c593315Sopenharmony_ci return -1; 13982c593315Sopenharmony_ci } 13992c593315Sopenharmony_ci 14002c593315Sopenharmony_ci auto iovcnt = wb.riovec(&iov, 1); 14012c593315Sopenharmony_ci 14022c593315Sopenharmony_ci if (iovcnt == 0) { 14032c593315Sopenharmony_ci break; 14042c593315Sopenharmony_ci } 14052c593315Sopenharmony_ci 14062c593315Sopenharmony_ci auto rv = SSL_write(ssl, iov.iov_base, iov.iov_len); 14072c593315Sopenharmony_ci 14082c593315Sopenharmony_ci if (rv <= 0) { 14092c593315Sopenharmony_ci auto err = SSL_get_error(ssl, rv); 14102c593315Sopenharmony_ci switch (err) { 14112c593315Sopenharmony_ci case SSL_ERROR_WANT_READ: 14122c593315Sopenharmony_ci // renegotiation started 14132c593315Sopenharmony_ci return -1; 14142c593315Sopenharmony_ci case SSL_ERROR_WANT_WRITE: 14152c593315Sopenharmony_ci ev_io_start(loop, &wev); 14162c593315Sopenharmony_ci ev_timer_again(loop, &wt); 14172c593315Sopenharmony_ci return 0; 14182c593315Sopenharmony_ci default: 14192c593315Sopenharmony_ci return -1; 14202c593315Sopenharmony_ci } 14212c593315Sopenharmony_ci } 14222c593315Sopenharmony_ci 14232c593315Sopenharmony_ci wb.drain(rv); 14242c593315Sopenharmony_ci } 14252c593315Sopenharmony_ci 14262c593315Sopenharmony_ci ev_io_stop(loop, &wev); 14272c593315Sopenharmony_ci ev_timer_stop(loop, &wt); 14282c593315Sopenharmony_ci 14292c593315Sopenharmony_ci return 0; 14302c593315Sopenharmony_ci} 14312c593315Sopenharmony_ci 14322c593315Sopenharmony_civoid HttpClient::signal_write() { ev_io_start(loop, &wev); } 14332c593315Sopenharmony_ci 14342c593315Sopenharmony_cibool HttpClient::all_requests_processed() const { 14352c593315Sopenharmony_ci return complete == reqvec.size(); 14362c593315Sopenharmony_ci} 14372c593315Sopenharmony_ci 14382c593315Sopenharmony_civoid HttpClient::update_hostport() { 14392c593315Sopenharmony_ci if (reqvec.empty()) { 14402c593315Sopenharmony_ci return; 14412c593315Sopenharmony_ci } 14422c593315Sopenharmony_ci scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_SCHEMA) 14432c593315Sopenharmony_ci .str(); 14442c593315Sopenharmony_ci std::stringstream ss; 14452c593315Sopenharmony_ci if (reqvec[0]->is_ipv6_literal_addr()) { 14462c593315Sopenharmony_ci // we may have zone ID, which must start with "%25", or "%". RFC 14472c593315Sopenharmony_ci // 6874 defines "%25" only, and just "%" is allowed for just 14482c593315Sopenharmony_ci // convenience to end-user input. 14492c593315Sopenharmony_ci auto host = 14502c593315Sopenharmony_ci util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); 14512c593315Sopenharmony_ci auto end = std::find(std::begin(host), std::end(host), '%'); 14522c593315Sopenharmony_ci ss << "["; 14532c593315Sopenharmony_ci ss.write(host.c_str(), end - std::begin(host)); 14542c593315Sopenharmony_ci ss << "]"; 14552c593315Sopenharmony_ci } else { 14562c593315Sopenharmony_ci util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); 14572c593315Sopenharmony_ci } 14582c593315Sopenharmony_ci if (util::has_uri_field(reqvec[0]->u, UF_PORT) && 14592c593315Sopenharmony_ci reqvec[0]->u.port != 14602c593315Sopenharmony_ci util::get_default_port(reqvec[0]->uri.c_str(), reqvec[0]->u)) { 14612c593315Sopenharmony_ci ss << ":" << reqvec[0]->u.port; 14622c593315Sopenharmony_ci } 14632c593315Sopenharmony_ci hostport = ss.str(); 14642c593315Sopenharmony_ci} 14652c593315Sopenharmony_ci 14662c593315Sopenharmony_cibool HttpClient::add_request(const std::string &uri, 14672c593315Sopenharmony_ci const nghttp2_data_provider *data_prd, 14682c593315Sopenharmony_ci int64_t data_length, 14692c593315Sopenharmony_ci const nghttp2_priority_spec &pri_spec, int level) { 14702c593315Sopenharmony_ci http_parser_url u{}; 14712c593315Sopenharmony_ci if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { 14722c593315Sopenharmony_ci return false; 14732c593315Sopenharmony_ci } 14742c593315Sopenharmony_ci if (path_cache.count(uri)) { 14752c593315Sopenharmony_ci return false; 14762c593315Sopenharmony_ci } 14772c593315Sopenharmony_ci 14782c593315Sopenharmony_ci if (config.multiply == 1) { 14792c593315Sopenharmony_ci path_cache.insert(uri); 14802c593315Sopenharmony_ci } 14812c593315Sopenharmony_ci 14822c593315Sopenharmony_ci reqvec.push_back(std::make_unique<Request>(uri, u, data_prd, data_length, 14832c593315Sopenharmony_ci pri_spec, level)); 14842c593315Sopenharmony_ci return true; 14852c593315Sopenharmony_ci} 14862c593315Sopenharmony_ci 14872c593315Sopenharmony_civoid HttpClient::record_start_time() { 14882c593315Sopenharmony_ci timing.system_start_time = std::chrono::system_clock::now(); 14892c593315Sopenharmony_ci timing.start_time = get_time(); 14902c593315Sopenharmony_ci} 14912c593315Sopenharmony_ci 14922c593315Sopenharmony_civoid HttpClient::record_domain_lookup_end_time() { 14932c593315Sopenharmony_ci timing.domain_lookup_end_time = get_time(); 14942c593315Sopenharmony_ci} 14952c593315Sopenharmony_ci 14962c593315Sopenharmony_civoid HttpClient::record_connect_end_time() { 14972c593315Sopenharmony_ci timing.connect_end_time = get_time(); 14982c593315Sopenharmony_ci} 14992c593315Sopenharmony_ci 15002c593315Sopenharmony_civoid HttpClient::request_done(Request *req) { 15012c593315Sopenharmony_ci if (req->stream_id % 2 == 0) { 15022c593315Sopenharmony_ci return; 15032c593315Sopenharmony_ci } 15042c593315Sopenharmony_ci} 15052c593315Sopenharmony_ci 15062c593315Sopenharmony_ci#ifdef HAVE_JANSSON 15072c593315Sopenharmony_civoid HttpClient::output_har(FILE *outfile) { 15082c593315Sopenharmony_ci static auto PAGE_ID = "page_0"; 15092c593315Sopenharmony_ci 15102c593315Sopenharmony_ci auto root = json_object(); 15112c593315Sopenharmony_ci auto log = json_object(); 15122c593315Sopenharmony_ci json_object_set_new(root, "log", log); 15132c593315Sopenharmony_ci json_object_set_new(log, "version", json_string("1.2")); 15142c593315Sopenharmony_ci 15152c593315Sopenharmony_ci auto creator = json_object(); 15162c593315Sopenharmony_ci json_object_set_new(log, "creator", creator); 15172c593315Sopenharmony_ci 15182c593315Sopenharmony_ci json_object_set_new(creator, "name", json_string("nghttp")); 15192c593315Sopenharmony_ci json_object_set_new(creator, "version", json_string(NGHTTP2_VERSION)); 15202c593315Sopenharmony_ci 15212c593315Sopenharmony_ci auto pages = json_array(); 15222c593315Sopenharmony_ci json_object_set_new(log, "pages", pages); 15232c593315Sopenharmony_ci 15242c593315Sopenharmony_ci auto page = json_object(); 15252c593315Sopenharmony_ci json_array_append_new(pages, page); 15262c593315Sopenharmony_ci 15272c593315Sopenharmony_ci json_object_set_new( 15282c593315Sopenharmony_ci page, "startedDateTime", 15292c593315Sopenharmony_ci json_string(util::format_iso8601(timing.system_start_time).c_str())); 15302c593315Sopenharmony_ci json_object_set_new(page, "id", json_string(PAGE_ID)); 15312c593315Sopenharmony_ci json_object_set_new(page, "title", json_string("")); 15322c593315Sopenharmony_ci 15332c593315Sopenharmony_ci json_object_set_new(page, "pageTimings", json_object()); 15342c593315Sopenharmony_ci 15352c593315Sopenharmony_ci auto entries = json_array(); 15362c593315Sopenharmony_ci json_object_set_new(log, "entries", entries); 15372c593315Sopenharmony_ci 15382c593315Sopenharmony_ci auto dns_delta = std::chrono::duration_cast<std::chrono::microseconds>( 15392c593315Sopenharmony_ci timing.domain_lookup_end_time - timing.start_time) 15402c593315Sopenharmony_ci .count() / 15412c593315Sopenharmony_ci 1000.0; 15422c593315Sopenharmony_ci auto connect_delta = 15432c593315Sopenharmony_ci std::chrono::duration_cast<std::chrono::microseconds>( 15442c593315Sopenharmony_ci timing.connect_end_time - timing.domain_lookup_end_time) 15452c593315Sopenharmony_ci .count() / 15462c593315Sopenharmony_ci 1000.0; 15472c593315Sopenharmony_ci 15482c593315Sopenharmony_ci for (size_t i = 0; i < reqvec.size(); ++i) { 15492c593315Sopenharmony_ci auto &req = reqvec[i]; 15502c593315Sopenharmony_ci 15512c593315Sopenharmony_ci if (req->timing.state != RequestState::ON_COMPLETE) { 15522c593315Sopenharmony_ci continue; 15532c593315Sopenharmony_ci } 15542c593315Sopenharmony_ci 15552c593315Sopenharmony_ci auto entry = json_object(); 15562c593315Sopenharmony_ci json_array_append_new(entries, entry); 15572c593315Sopenharmony_ci 15582c593315Sopenharmony_ci auto &req_timing = req->timing; 15592c593315Sopenharmony_ci auto request_time = 15602c593315Sopenharmony_ci (i == 0) ? timing.system_start_time 15612c593315Sopenharmony_ci : timing.system_start_time + 15622c593315Sopenharmony_ci std::chrono::duration_cast< 15632c593315Sopenharmony_ci std::chrono::system_clock::duration>( 15642c593315Sopenharmony_ci req_timing.request_start_time - timing.start_time); 15652c593315Sopenharmony_ci 15662c593315Sopenharmony_ci auto wait_delta = 15672c593315Sopenharmony_ci std::chrono::duration_cast<std::chrono::microseconds>( 15682c593315Sopenharmony_ci req_timing.response_start_time - req_timing.request_start_time) 15692c593315Sopenharmony_ci .count() / 15702c593315Sopenharmony_ci 1000.0; 15712c593315Sopenharmony_ci auto receive_delta = 15722c593315Sopenharmony_ci std::chrono::duration_cast<std::chrono::microseconds>( 15732c593315Sopenharmony_ci req_timing.response_end_time - req_timing.response_start_time) 15742c593315Sopenharmony_ci .count() / 15752c593315Sopenharmony_ci 1000.0; 15762c593315Sopenharmony_ci 15772c593315Sopenharmony_ci auto time_sum = 15782c593315Sopenharmony_ci std::chrono::duration_cast<std::chrono::microseconds>( 15792c593315Sopenharmony_ci (i == 0) ? (req_timing.response_end_time - timing.start_time) 15802c593315Sopenharmony_ci : (req_timing.response_end_time - 15812c593315Sopenharmony_ci req_timing.request_start_time)) 15822c593315Sopenharmony_ci .count() / 15832c593315Sopenharmony_ci 1000.0; 15842c593315Sopenharmony_ci 15852c593315Sopenharmony_ci json_object_set_new( 15862c593315Sopenharmony_ci entry, "startedDateTime", 15872c593315Sopenharmony_ci json_string(util::format_iso8601(request_time).c_str())); 15882c593315Sopenharmony_ci json_object_set_new(entry, "time", json_real(time_sum)); 15892c593315Sopenharmony_ci 15902c593315Sopenharmony_ci auto pushed = req->stream_id % 2 == 0; 15912c593315Sopenharmony_ci 15922c593315Sopenharmony_ci json_object_set_new(entry, "comment", 15932c593315Sopenharmony_ci json_string(pushed ? "Pushed Object" : "")); 15942c593315Sopenharmony_ci 15952c593315Sopenharmony_ci auto request = json_object(); 15962c593315Sopenharmony_ci json_object_set_new(entry, "request", request); 15972c593315Sopenharmony_ci 15982c593315Sopenharmony_ci auto req_headers = json_array(); 15992c593315Sopenharmony_ci json_object_set_new(request, "headers", req_headers); 16002c593315Sopenharmony_ci 16012c593315Sopenharmony_ci for (auto &nv : req->req_nva) { 16022c593315Sopenharmony_ci auto hd = json_object(); 16032c593315Sopenharmony_ci json_array_append_new(req_headers, hd); 16042c593315Sopenharmony_ci 16052c593315Sopenharmony_ci json_object_set_new(hd, "name", json_string(nv.name.c_str())); 16062c593315Sopenharmony_ci json_object_set_new(hd, "value", json_string(nv.value.c_str())); 16072c593315Sopenharmony_ci } 16082c593315Sopenharmony_ci 16092c593315Sopenharmony_ci json_object_set_new(request, "method", json_string(req->method.c_str())); 16102c593315Sopenharmony_ci json_object_set_new(request, "url", json_string(req->uri.c_str())); 16112c593315Sopenharmony_ci json_object_set_new(request, "httpVersion", json_string("HTTP/2.0")); 16122c593315Sopenharmony_ci json_object_set_new(request, "cookies", json_array()); 16132c593315Sopenharmony_ci json_object_set_new(request, "queryString", json_array()); 16142c593315Sopenharmony_ci json_object_set_new(request, "headersSize", json_integer(-1)); 16152c593315Sopenharmony_ci json_object_set_new(request, "bodySize", json_integer(-1)); 16162c593315Sopenharmony_ci 16172c593315Sopenharmony_ci auto response = json_object(); 16182c593315Sopenharmony_ci json_object_set_new(entry, "response", response); 16192c593315Sopenharmony_ci 16202c593315Sopenharmony_ci auto res_headers = json_array(); 16212c593315Sopenharmony_ci json_object_set_new(response, "headers", res_headers); 16222c593315Sopenharmony_ci 16232c593315Sopenharmony_ci for (auto &nv : req->res_nva) { 16242c593315Sopenharmony_ci auto hd = json_object(); 16252c593315Sopenharmony_ci json_array_append_new(res_headers, hd); 16262c593315Sopenharmony_ci 16272c593315Sopenharmony_ci json_object_set_new(hd, "name", json_string(nv.name.c_str())); 16282c593315Sopenharmony_ci json_object_set_new(hd, "value", json_string(nv.value.c_str())); 16292c593315Sopenharmony_ci } 16302c593315Sopenharmony_ci 16312c593315Sopenharmony_ci json_object_set_new(response, "status", json_integer(req->status)); 16322c593315Sopenharmony_ci json_object_set_new(response, "statusText", json_string("")); 16332c593315Sopenharmony_ci json_object_set_new(response, "httpVersion", json_string("HTTP/2.0")); 16342c593315Sopenharmony_ci json_object_set_new(response, "cookies", json_array()); 16352c593315Sopenharmony_ci 16362c593315Sopenharmony_ci auto content = json_object(); 16372c593315Sopenharmony_ci json_object_set_new(response, "content", content); 16382c593315Sopenharmony_ci 16392c593315Sopenharmony_ci json_object_set_new(content, "size", json_integer(req->response_len)); 16402c593315Sopenharmony_ci 16412c593315Sopenharmony_ci auto content_type_ptr = http2::get_header(req->res_nva, "content-type"); 16422c593315Sopenharmony_ci 16432c593315Sopenharmony_ci const char *content_type = ""; 16442c593315Sopenharmony_ci if (content_type_ptr) { 16452c593315Sopenharmony_ci content_type = content_type_ptr->value.c_str(); 16462c593315Sopenharmony_ci } 16472c593315Sopenharmony_ci 16482c593315Sopenharmony_ci json_object_set_new(content, "mimeType", json_string(content_type)); 16492c593315Sopenharmony_ci 16502c593315Sopenharmony_ci json_object_set_new(response, "redirectURL", json_string("")); 16512c593315Sopenharmony_ci json_object_set_new(response, "headersSize", json_integer(-1)); 16522c593315Sopenharmony_ci json_object_set_new(response, "bodySize", json_integer(-1)); 16532c593315Sopenharmony_ci json_object_set_new(entry, "cache", json_object()); 16542c593315Sopenharmony_ci 16552c593315Sopenharmony_ci auto timings = json_object(); 16562c593315Sopenharmony_ci json_object_set_new(entry, "timings", timings); 16572c593315Sopenharmony_ci 16582c593315Sopenharmony_ci auto dns_timing = (i == 0) ? dns_delta : 0; 16592c593315Sopenharmony_ci auto connect_timing = (i == 0) ? connect_delta : 0; 16602c593315Sopenharmony_ci 16612c593315Sopenharmony_ci json_object_set_new(timings, "dns", json_real(dns_timing)); 16622c593315Sopenharmony_ci json_object_set_new(timings, "connect", json_real(connect_timing)); 16632c593315Sopenharmony_ci 16642c593315Sopenharmony_ci json_object_set_new(timings, "blocked", json_real(0.0)); 16652c593315Sopenharmony_ci json_object_set_new(timings, "send", json_real(0.0)); 16662c593315Sopenharmony_ci json_object_set_new(timings, "wait", json_real(wait_delta)); 16672c593315Sopenharmony_ci json_object_set_new(timings, "receive", json_real(receive_delta)); 16682c593315Sopenharmony_ci 16692c593315Sopenharmony_ci json_object_set_new(entry, "pageref", json_string(PAGE_ID)); 16702c593315Sopenharmony_ci json_object_set_new(entry, "connection", 16712c593315Sopenharmony_ci json_string(util::utos(req->stream_id).c_str())); 16722c593315Sopenharmony_ci } 16732c593315Sopenharmony_ci 16742c593315Sopenharmony_ci json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2)); 16752c593315Sopenharmony_ci json_decref(root); 16762c593315Sopenharmony_ci} 16772c593315Sopenharmony_ci#endif // HAVE_JANSSON 16782c593315Sopenharmony_ci 16792c593315Sopenharmony_cinamespace { 16802c593315Sopenharmony_civoid update_html_parser(HttpClient *client, Request *req, const uint8_t *data, 16812c593315Sopenharmony_ci size_t len, int fin) { 16822c593315Sopenharmony_ci if (!req->html_parser) { 16832c593315Sopenharmony_ci return; 16842c593315Sopenharmony_ci } 16852c593315Sopenharmony_ci req->update_html_parser(data, len, fin); 16862c593315Sopenharmony_ci 16872c593315Sopenharmony_ci auto scheme = req->get_real_scheme(); 16882c593315Sopenharmony_ci auto host = req->get_real_host(); 16892c593315Sopenharmony_ci auto port = req->get_real_port(); 16902c593315Sopenharmony_ci 16912c593315Sopenharmony_ci for (auto &p : req->html_parser->get_links()) { 16922c593315Sopenharmony_ci auto uri = strip_fragment(p.first.c_str()); 16932c593315Sopenharmony_ci auto res_type = p.second; 16942c593315Sopenharmony_ci 16952c593315Sopenharmony_ci http_parser_url u{}; 16962c593315Sopenharmony_ci if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { 16972c593315Sopenharmony_ci continue; 16982c593315Sopenharmony_ci } 16992c593315Sopenharmony_ci 17002c593315Sopenharmony_ci if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, scheme) || 17012c593315Sopenharmony_ci !util::fieldeq(uri.c_str(), u, UF_HOST, host)) { 17022c593315Sopenharmony_ci continue; 17032c593315Sopenharmony_ci } 17042c593315Sopenharmony_ci 17052c593315Sopenharmony_ci auto link_port = util::has_uri_field(u, UF_PORT) ? u.port 17062c593315Sopenharmony_ci : scheme == "https" ? 443 17072c593315Sopenharmony_ci : 80; 17082c593315Sopenharmony_ci 17092c593315Sopenharmony_ci if (port != link_port) { 17102c593315Sopenharmony_ci continue; 17112c593315Sopenharmony_ci } 17122c593315Sopenharmony_ci 17132c593315Sopenharmony_ci // No POST data for assets 17142c593315Sopenharmony_ci auto pri_spec = resolve_dep(res_type); 17152c593315Sopenharmony_ci 17162c593315Sopenharmony_ci if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) { 17172c593315Sopenharmony_ci submit_request(client, config.headers, client->reqvec.back().get()); 17182c593315Sopenharmony_ci } 17192c593315Sopenharmony_ci } 17202c593315Sopenharmony_ci req->html_parser->clear_links(); 17212c593315Sopenharmony_ci} 17222c593315Sopenharmony_ci} // namespace 17232c593315Sopenharmony_ci 17242c593315Sopenharmony_cinamespace { 17252c593315Sopenharmony_ciHttpClient *get_client(void *user_data) { 17262c593315Sopenharmony_ci return static_cast<HttpClient *>(user_data); 17272c593315Sopenharmony_ci} 17282c593315Sopenharmony_ci} // namespace 17292c593315Sopenharmony_ci 17302c593315Sopenharmony_cinamespace { 17312c593315Sopenharmony_ciint on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, 17322c593315Sopenharmony_ci int32_t stream_id, const uint8_t *data, 17332c593315Sopenharmony_ci size_t len, void *user_data) { 17342c593315Sopenharmony_ci auto client = get_client(user_data); 17352c593315Sopenharmony_ci auto req = static_cast<Request *>( 17362c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, stream_id)); 17372c593315Sopenharmony_ci 17382c593315Sopenharmony_ci if (!req) { 17392c593315Sopenharmony_ci return 0; 17402c593315Sopenharmony_ci } 17412c593315Sopenharmony_ci 17422c593315Sopenharmony_ci if (config.verbose >= 2) { 17432c593315Sopenharmony_ci verbose_on_data_chunk_recv_callback(session, flags, stream_id, data, len, 17442c593315Sopenharmony_ci user_data); 17452c593315Sopenharmony_ci } 17462c593315Sopenharmony_ci 17472c593315Sopenharmony_ci req->response_len += len; 17482c593315Sopenharmony_ci 17492c593315Sopenharmony_ci if (req->inflater) { 17502c593315Sopenharmony_ci while (len > 0) { 17512c593315Sopenharmony_ci const size_t MAX_OUTLEN = 4_k; 17522c593315Sopenharmony_ci std::array<uint8_t, MAX_OUTLEN> out; 17532c593315Sopenharmony_ci size_t outlen = MAX_OUTLEN; 17542c593315Sopenharmony_ci size_t tlen = len; 17552c593315Sopenharmony_ci int rv = 17562c593315Sopenharmony_ci nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen); 17572c593315Sopenharmony_ci if (rv != 0) { 17582c593315Sopenharmony_ci nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, 17592c593315Sopenharmony_ci NGHTTP2_INTERNAL_ERROR); 17602c593315Sopenharmony_ci break; 17612c593315Sopenharmony_ci } 17622c593315Sopenharmony_ci 17632c593315Sopenharmony_ci if (!config.null_out) { 17642c593315Sopenharmony_ci std::cout.write(reinterpret_cast<const char *>(out.data()), outlen); 17652c593315Sopenharmony_ci } 17662c593315Sopenharmony_ci 17672c593315Sopenharmony_ci update_html_parser(client, req, out.data(), outlen, 0); 17682c593315Sopenharmony_ci data += tlen; 17692c593315Sopenharmony_ci len -= tlen; 17702c593315Sopenharmony_ci } 17712c593315Sopenharmony_ci 17722c593315Sopenharmony_ci return 0; 17732c593315Sopenharmony_ci } 17742c593315Sopenharmony_ci 17752c593315Sopenharmony_ci if (!config.null_out) { 17762c593315Sopenharmony_ci std::cout.write(reinterpret_cast<const char *>(data), len); 17772c593315Sopenharmony_ci } 17782c593315Sopenharmony_ci 17792c593315Sopenharmony_ci update_html_parser(client, req, data, len, 0); 17802c593315Sopenharmony_ci 17812c593315Sopenharmony_ci return 0; 17822c593315Sopenharmony_ci} 17832c593315Sopenharmony_ci} // namespace 17842c593315Sopenharmony_ci 17852c593315Sopenharmony_cinamespace { 17862c593315Sopenharmony_cissize_t select_padding_callback(nghttp2_session *session, 17872c593315Sopenharmony_ci const nghttp2_frame *frame, size_t max_payload, 17882c593315Sopenharmony_ci void *user_data) { 17892c593315Sopenharmony_ci return std::min(max_payload, frame->hd.length + config.padding); 17902c593315Sopenharmony_ci} 17912c593315Sopenharmony_ci} // namespace 17922c593315Sopenharmony_ci 17932c593315Sopenharmony_cinamespace { 17942c593315Sopenharmony_civoid check_response_header(nghttp2_session *session, Request *req) { 17952c593315Sopenharmony_ci bool gzip = false; 17962c593315Sopenharmony_ci 17972c593315Sopenharmony_ci req->expect_final_response = false; 17982c593315Sopenharmony_ci 17992c593315Sopenharmony_ci auto status_hd = req->get_res_header(http2::HD__STATUS); 18002c593315Sopenharmony_ci 18012c593315Sopenharmony_ci if (!status_hd) { 18022c593315Sopenharmony_ci nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, 18032c593315Sopenharmony_ci NGHTTP2_PROTOCOL_ERROR); 18042c593315Sopenharmony_ci return; 18052c593315Sopenharmony_ci } 18062c593315Sopenharmony_ci 18072c593315Sopenharmony_ci auto status = http2::parse_http_status_code(StringRef{status_hd->value}); 18082c593315Sopenharmony_ci if (status == -1) { 18092c593315Sopenharmony_ci nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, 18102c593315Sopenharmony_ci NGHTTP2_PROTOCOL_ERROR); 18112c593315Sopenharmony_ci return; 18122c593315Sopenharmony_ci } 18132c593315Sopenharmony_ci 18142c593315Sopenharmony_ci req->status = status; 18152c593315Sopenharmony_ci 18162c593315Sopenharmony_ci for (auto &nv : req->res_nva) { 18172c593315Sopenharmony_ci if ("content-encoding" == nv.name) { 18182c593315Sopenharmony_ci gzip = util::strieq_l("gzip", nv.value) || 18192c593315Sopenharmony_ci util::strieq_l("deflate", nv.value); 18202c593315Sopenharmony_ci continue; 18212c593315Sopenharmony_ci } 18222c593315Sopenharmony_ci } 18232c593315Sopenharmony_ci 18242c593315Sopenharmony_ci if (req->status / 100 == 1) { 18252c593315Sopenharmony_ci if (req->continue_timer && (req->status == 100)) { 18262c593315Sopenharmony_ci // If the request is waiting for a 100 Continue, complete the handshake. 18272c593315Sopenharmony_ci req->continue_timer->dispatch_continue(); 18282c593315Sopenharmony_ci } 18292c593315Sopenharmony_ci 18302c593315Sopenharmony_ci req->expect_final_response = true; 18312c593315Sopenharmony_ci req->status = 0; 18322c593315Sopenharmony_ci req->res_nva.clear(); 18332c593315Sopenharmony_ci http2::init_hdidx(req->res_hdidx); 18342c593315Sopenharmony_ci return; 18352c593315Sopenharmony_ci } else if (req->continue_timer) { 18362c593315Sopenharmony_ci // A final response stops any pending Expect/Continue handshake. 18372c593315Sopenharmony_ci req->continue_timer->stop(); 18382c593315Sopenharmony_ci } 18392c593315Sopenharmony_ci 18402c593315Sopenharmony_ci if (gzip) { 18412c593315Sopenharmony_ci if (!req->inflater) { 18422c593315Sopenharmony_ci req->init_inflater(); 18432c593315Sopenharmony_ci } 18442c593315Sopenharmony_ci } 18452c593315Sopenharmony_ci if (config.get_assets && req->level == 0) { 18462c593315Sopenharmony_ci if (!req->html_parser) { 18472c593315Sopenharmony_ci req->init_html_parser(); 18482c593315Sopenharmony_ci } 18492c593315Sopenharmony_ci } 18502c593315Sopenharmony_ci} 18512c593315Sopenharmony_ci} // namespace 18522c593315Sopenharmony_ci 18532c593315Sopenharmony_cinamespace { 18542c593315Sopenharmony_ciint on_begin_headers_callback(nghttp2_session *session, 18552c593315Sopenharmony_ci const nghttp2_frame *frame, void *user_data) { 18562c593315Sopenharmony_ci auto client = get_client(user_data); 18572c593315Sopenharmony_ci switch (frame->hd.type) { 18582c593315Sopenharmony_ci case NGHTTP2_HEADERS: { 18592c593315Sopenharmony_ci auto req = static_cast<Request *>( 18602c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); 18612c593315Sopenharmony_ci if (!req) { 18622c593315Sopenharmony_ci break; 18632c593315Sopenharmony_ci } 18642c593315Sopenharmony_ci 18652c593315Sopenharmony_ci switch (frame->headers.cat) { 18662c593315Sopenharmony_ci case NGHTTP2_HCAT_RESPONSE: 18672c593315Sopenharmony_ci case NGHTTP2_HCAT_PUSH_RESPONSE: 18682c593315Sopenharmony_ci req->record_response_start_time(); 18692c593315Sopenharmony_ci break; 18702c593315Sopenharmony_ci default: 18712c593315Sopenharmony_ci break; 18722c593315Sopenharmony_ci } 18732c593315Sopenharmony_ci 18742c593315Sopenharmony_ci break; 18752c593315Sopenharmony_ci } 18762c593315Sopenharmony_ci case NGHTTP2_PUSH_PROMISE: { 18772c593315Sopenharmony_ci auto stream_id = frame->push_promise.promised_stream_id; 18782c593315Sopenharmony_ci http_parser_url u{}; 18792c593315Sopenharmony_ci // TODO Set pri and level 18802c593315Sopenharmony_ci nghttp2_priority_spec pri_spec; 18812c593315Sopenharmony_ci 18822c593315Sopenharmony_ci nghttp2_priority_spec_default_init(&pri_spec); 18832c593315Sopenharmony_ci 18842c593315Sopenharmony_ci auto req = std::make_unique<Request>("", u, nullptr, 0, pri_spec); 18852c593315Sopenharmony_ci req->stream_id = stream_id; 18862c593315Sopenharmony_ci 18872c593315Sopenharmony_ci nghttp2_session_set_stream_user_data(session, stream_id, req.get()); 18882c593315Sopenharmony_ci 18892c593315Sopenharmony_ci client->request_done(req.get()); 18902c593315Sopenharmony_ci req->record_request_start_time(); 18912c593315Sopenharmony_ci client->reqvec.push_back(std::move(req)); 18922c593315Sopenharmony_ci 18932c593315Sopenharmony_ci break; 18942c593315Sopenharmony_ci } 18952c593315Sopenharmony_ci } 18962c593315Sopenharmony_ci return 0; 18972c593315Sopenharmony_ci} 18982c593315Sopenharmony_ci} // namespace 18992c593315Sopenharmony_ci 19002c593315Sopenharmony_cinamespace { 19012c593315Sopenharmony_ciint on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, 19022c593315Sopenharmony_ci const uint8_t *name, size_t namelen, 19032c593315Sopenharmony_ci const uint8_t *value, size_t valuelen, uint8_t flags, 19042c593315Sopenharmony_ci void *user_data) { 19052c593315Sopenharmony_ci if (config.verbose) { 19062c593315Sopenharmony_ci verbose_on_header_callback(session, frame, name, namelen, value, valuelen, 19072c593315Sopenharmony_ci flags, user_data); 19082c593315Sopenharmony_ci } 19092c593315Sopenharmony_ci 19102c593315Sopenharmony_ci switch (frame->hd.type) { 19112c593315Sopenharmony_ci case NGHTTP2_HEADERS: { 19122c593315Sopenharmony_ci auto req = static_cast<Request *>( 19132c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); 19142c593315Sopenharmony_ci 19152c593315Sopenharmony_ci if (!req) { 19162c593315Sopenharmony_ci break; 19172c593315Sopenharmony_ci } 19182c593315Sopenharmony_ci 19192c593315Sopenharmony_ci /* ignore trailer header */ 19202c593315Sopenharmony_ci if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && 19212c593315Sopenharmony_ci !req->expect_final_response) { 19222c593315Sopenharmony_ci break; 19232c593315Sopenharmony_ci } 19242c593315Sopenharmony_ci 19252c593315Sopenharmony_ci if (req->header_buffer_size + namelen + valuelen > 64_k) { 19262c593315Sopenharmony_ci nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, 19272c593315Sopenharmony_ci NGHTTP2_INTERNAL_ERROR); 19282c593315Sopenharmony_ci return 0; 19292c593315Sopenharmony_ci } 19302c593315Sopenharmony_ci 19312c593315Sopenharmony_ci req->header_buffer_size += namelen + valuelen; 19322c593315Sopenharmony_ci 19332c593315Sopenharmony_ci auto token = http2::lookup_token(name, namelen); 19342c593315Sopenharmony_ci 19352c593315Sopenharmony_ci http2::index_header(req->res_hdidx, token, req->res_nva.size()); 19362c593315Sopenharmony_ci http2::add_header(req->res_nva, name, namelen, value, valuelen, 19372c593315Sopenharmony_ci flags & NGHTTP2_NV_FLAG_NO_INDEX, token); 19382c593315Sopenharmony_ci break; 19392c593315Sopenharmony_ci } 19402c593315Sopenharmony_ci case NGHTTP2_PUSH_PROMISE: { 19412c593315Sopenharmony_ci auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data( 19422c593315Sopenharmony_ci session, frame->push_promise.promised_stream_id)); 19432c593315Sopenharmony_ci 19442c593315Sopenharmony_ci if (!req) { 19452c593315Sopenharmony_ci break; 19462c593315Sopenharmony_ci } 19472c593315Sopenharmony_ci 19482c593315Sopenharmony_ci if (req->header_buffer_size + namelen + valuelen > 64_k) { 19492c593315Sopenharmony_ci nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 19502c593315Sopenharmony_ci frame->push_promise.promised_stream_id, 19512c593315Sopenharmony_ci NGHTTP2_INTERNAL_ERROR); 19522c593315Sopenharmony_ci return 0; 19532c593315Sopenharmony_ci } 19542c593315Sopenharmony_ci 19552c593315Sopenharmony_ci req->header_buffer_size += namelen + valuelen; 19562c593315Sopenharmony_ci 19572c593315Sopenharmony_ci auto token = http2::lookup_token(name, namelen); 19582c593315Sopenharmony_ci 19592c593315Sopenharmony_ci http2::index_header(req->req_hdidx, token, req->req_nva.size()); 19602c593315Sopenharmony_ci http2::add_header(req->req_nva, name, namelen, value, valuelen, 19612c593315Sopenharmony_ci flags & NGHTTP2_NV_FLAG_NO_INDEX, token); 19622c593315Sopenharmony_ci break; 19632c593315Sopenharmony_ci } 19642c593315Sopenharmony_ci } 19652c593315Sopenharmony_ci return 0; 19662c593315Sopenharmony_ci} 19672c593315Sopenharmony_ci} // namespace 19682c593315Sopenharmony_ci 19692c593315Sopenharmony_cinamespace { 19702c593315Sopenharmony_ciint on_frame_recv_callback2(nghttp2_session *session, 19712c593315Sopenharmony_ci const nghttp2_frame *frame, void *user_data) { 19722c593315Sopenharmony_ci int rv = 0; 19732c593315Sopenharmony_ci 19742c593315Sopenharmony_ci if (config.verbose) { 19752c593315Sopenharmony_ci verbose_on_frame_recv_callback(session, frame, user_data); 19762c593315Sopenharmony_ci } 19772c593315Sopenharmony_ci 19782c593315Sopenharmony_ci auto client = get_client(user_data); 19792c593315Sopenharmony_ci switch (frame->hd.type) { 19802c593315Sopenharmony_ci case NGHTTP2_DATA: { 19812c593315Sopenharmony_ci auto req = static_cast<Request *>( 19822c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); 19832c593315Sopenharmony_ci if (!req) { 19842c593315Sopenharmony_ci return 0; 19852c593315Sopenharmony_ci ; 19862c593315Sopenharmony_ci } 19872c593315Sopenharmony_ci 19882c593315Sopenharmony_ci if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { 19892c593315Sopenharmony_ci req->record_response_end_time(); 19902c593315Sopenharmony_ci ++client->success; 19912c593315Sopenharmony_ci } 19922c593315Sopenharmony_ci 19932c593315Sopenharmony_ci break; 19942c593315Sopenharmony_ci } 19952c593315Sopenharmony_ci case NGHTTP2_HEADERS: { 19962c593315Sopenharmony_ci auto req = static_cast<Request *>( 19972c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); 19982c593315Sopenharmony_ci // If this is the HTTP Upgrade with OPTIONS method to avoid POST, 19992c593315Sopenharmony_ci // req is nullptr. 20002c593315Sopenharmony_ci if (!req) { 20012c593315Sopenharmony_ci return 0; 20022c593315Sopenharmony_ci ; 20032c593315Sopenharmony_ci } 20042c593315Sopenharmony_ci 20052c593315Sopenharmony_ci switch (frame->headers.cat) { 20062c593315Sopenharmony_ci case NGHTTP2_HCAT_RESPONSE: 20072c593315Sopenharmony_ci case NGHTTP2_HCAT_PUSH_RESPONSE: 20082c593315Sopenharmony_ci check_response_header(session, req); 20092c593315Sopenharmony_ci break; 20102c593315Sopenharmony_ci case NGHTTP2_HCAT_HEADERS: 20112c593315Sopenharmony_ci if (req->expect_final_response) { 20122c593315Sopenharmony_ci check_response_header(session, req); 20132c593315Sopenharmony_ci break; 20142c593315Sopenharmony_ci } 20152c593315Sopenharmony_ci if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { 20162c593315Sopenharmony_ci nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 20172c593315Sopenharmony_ci frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); 20182c593315Sopenharmony_ci return 0; 20192c593315Sopenharmony_ci } 20202c593315Sopenharmony_ci break; 20212c593315Sopenharmony_ci default: 20222c593315Sopenharmony_ci assert(0); 20232c593315Sopenharmony_ci } 20242c593315Sopenharmony_ci 20252c593315Sopenharmony_ci if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { 20262c593315Sopenharmony_ci req->record_response_end_time(); 20272c593315Sopenharmony_ci ++client->success; 20282c593315Sopenharmony_ci } 20292c593315Sopenharmony_ci 20302c593315Sopenharmony_ci break; 20312c593315Sopenharmony_ci } 20322c593315Sopenharmony_ci case NGHTTP2_SETTINGS: 20332c593315Sopenharmony_ci if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { 20342c593315Sopenharmony_ci break; 20352c593315Sopenharmony_ci } 20362c593315Sopenharmony_ci ev_timer_stop(client->loop, &client->settings_timer); 20372c593315Sopenharmony_ci break; 20382c593315Sopenharmony_ci case NGHTTP2_PUSH_PROMISE: { 20392c593315Sopenharmony_ci auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data( 20402c593315Sopenharmony_ci session, frame->push_promise.promised_stream_id)); 20412c593315Sopenharmony_ci if (!req) { 20422c593315Sopenharmony_ci break; 20432c593315Sopenharmony_ci } 20442c593315Sopenharmony_ci 20452c593315Sopenharmony_ci // Reset for response header field reception 20462c593315Sopenharmony_ci req->header_buffer_size = 0; 20472c593315Sopenharmony_ci 20482c593315Sopenharmony_ci auto scheme = req->get_req_header(http2::HD__SCHEME); 20492c593315Sopenharmony_ci auto authority = req->get_req_header(http2::HD__AUTHORITY); 20502c593315Sopenharmony_ci auto path = req->get_req_header(http2::HD__PATH); 20512c593315Sopenharmony_ci 20522c593315Sopenharmony_ci if (!authority) { 20532c593315Sopenharmony_ci authority = req->get_req_header(http2::HD_HOST); 20542c593315Sopenharmony_ci } 20552c593315Sopenharmony_ci 20562c593315Sopenharmony_ci // libnghttp2 guarantees :scheme, :method, :path and (:authority | 20572c593315Sopenharmony_ci // host) exist and non-empty. 20582c593315Sopenharmony_ci if (path->value[0] != '/') { 20592c593315Sopenharmony_ci nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 20602c593315Sopenharmony_ci frame->push_promise.promised_stream_id, 20612c593315Sopenharmony_ci NGHTTP2_PROTOCOL_ERROR); 20622c593315Sopenharmony_ci break; 20632c593315Sopenharmony_ci } 20642c593315Sopenharmony_ci std::string uri = scheme->value; 20652c593315Sopenharmony_ci uri += "://"; 20662c593315Sopenharmony_ci uri += authority->value; 20672c593315Sopenharmony_ci uri += path->value; 20682c593315Sopenharmony_ci http_parser_url u{}; 20692c593315Sopenharmony_ci if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { 20702c593315Sopenharmony_ci nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 20712c593315Sopenharmony_ci frame->push_promise.promised_stream_id, 20722c593315Sopenharmony_ci NGHTTP2_PROTOCOL_ERROR); 20732c593315Sopenharmony_ci break; 20742c593315Sopenharmony_ci } 20752c593315Sopenharmony_ci req->uri = uri; 20762c593315Sopenharmony_ci req->u = u; 20772c593315Sopenharmony_ci 20782c593315Sopenharmony_ci if (client->path_cache.count(uri)) { 20792c593315Sopenharmony_ci nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 20802c593315Sopenharmony_ci frame->push_promise.promised_stream_id, 20812c593315Sopenharmony_ci NGHTTP2_CANCEL); 20822c593315Sopenharmony_ci break; 20832c593315Sopenharmony_ci } 20842c593315Sopenharmony_ci 20852c593315Sopenharmony_ci if (config.multiply == 1) { 20862c593315Sopenharmony_ci client->path_cache.insert(uri); 20872c593315Sopenharmony_ci } 20882c593315Sopenharmony_ci 20892c593315Sopenharmony_ci break; 20902c593315Sopenharmony_ci } 20912c593315Sopenharmony_ci } 20922c593315Sopenharmony_ci return rv; 20932c593315Sopenharmony_ci} 20942c593315Sopenharmony_ci} // namespace 20952c593315Sopenharmony_ci 20962c593315Sopenharmony_cinamespace { 20972c593315Sopenharmony_ciint before_frame_send_callback(nghttp2_session *session, 20982c593315Sopenharmony_ci const nghttp2_frame *frame, void *user_data) { 20992c593315Sopenharmony_ci if (frame->hd.type != NGHTTP2_HEADERS || 21002c593315Sopenharmony_ci frame->headers.cat != NGHTTP2_HCAT_REQUEST) { 21012c593315Sopenharmony_ci return 0; 21022c593315Sopenharmony_ci } 21032c593315Sopenharmony_ci auto req = static_cast<Request *>( 21042c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); 21052c593315Sopenharmony_ci assert(req); 21062c593315Sopenharmony_ci req->record_request_start_time(); 21072c593315Sopenharmony_ci return 0; 21082c593315Sopenharmony_ci} 21092c593315Sopenharmony_ci 21102c593315Sopenharmony_ci} // namespace 21112c593315Sopenharmony_ci 21122c593315Sopenharmony_cinamespace { 21132c593315Sopenharmony_ciint on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, 21142c593315Sopenharmony_ci void *user_data) { 21152c593315Sopenharmony_ci if (config.verbose) { 21162c593315Sopenharmony_ci verbose_on_frame_send_callback(session, frame, user_data); 21172c593315Sopenharmony_ci } 21182c593315Sopenharmony_ci 21192c593315Sopenharmony_ci if (frame->hd.type != NGHTTP2_HEADERS || 21202c593315Sopenharmony_ci frame->headers.cat != NGHTTP2_HCAT_REQUEST) { 21212c593315Sopenharmony_ci return 0; 21222c593315Sopenharmony_ci } 21232c593315Sopenharmony_ci 21242c593315Sopenharmony_ci auto req = static_cast<Request *>( 21252c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); 21262c593315Sopenharmony_ci if (!req) { 21272c593315Sopenharmony_ci return 0; 21282c593315Sopenharmony_ci } 21292c593315Sopenharmony_ci 21302c593315Sopenharmony_ci // If this request is using Expect/Continue, start its ContinueTimer. 21312c593315Sopenharmony_ci if (req->continue_timer) { 21322c593315Sopenharmony_ci req->continue_timer->start(); 21332c593315Sopenharmony_ci } 21342c593315Sopenharmony_ci 21352c593315Sopenharmony_ci return 0; 21362c593315Sopenharmony_ci} 21372c593315Sopenharmony_ci} // namespace 21382c593315Sopenharmony_ci 21392c593315Sopenharmony_cinamespace { 21402c593315Sopenharmony_ciint on_frame_not_send_callback(nghttp2_session *session, 21412c593315Sopenharmony_ci const nghttp2_frame *frame, int lib_error_code, 21422c593315Sopenharmony_ci void *user_data) { 21432c593315Sopenharmony_ci if (frame->hd.type != NGHTTP2_HEADERS || 21442c593315Sopenharmony_ci frame->headers.cat != NGHTTP2_HCAT_REQUEST) { 21452c593315Sopenharmony_ci return 0; 21462c593315Sopenharmony_ci } 21472c593315Sopenharmony_ci 21482c593315Sopenharmony_ci auto req = static_cast<Request *>( 21492c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); 21502c593315Sopenharmony_ci if (!req) { 21512c593315Sopenharmony_ci return 0; 21522c593315Sopenharmony_ci } 21532c593315Sopenharmony_ci 21542c593315Sopenharmony_ci std::cerr << "[ERROR] request " << req->uri 21552c593315Sopenharmony_ci << " failed: " << nghttp2_strerror(lib_error_code) << std::endl; 21562c593315Sopenharmony_ci 21572c593315Sopenharmony_ci return 0; 21582c593315Sopenharmony_ci} 21592c593315Sopenharmony_ci} // namespace 21602c593315Sopenharmony_ci 21612c593315Sopenharmony_cinamespace { 21622c593315Sopenharmony_ciint on_stream_close_callback(nghttp2_session *session, int32_t stream_id, 21632c593315Sopenharmony_ci uint32_t error_code, void *user_data) { 21642c593315Sopenharmony_ci auto client = get_client(user_data); 21652c593315Sopenharmony_ci auto req = static_cast<Request *>( 21662c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, stream_id)); 21672c593315Sopenharmony_ci 21682c593315Sopenharmony_ci if (!req) { 21692c593315Sopenharmony_ci return 0; 21702c593315Sopenharmony_ci } 21712c593315Sopenharmony_ci 21722c593315Sopenharmony_ci // If this request is using Expect/Continue, stop its ContinueTimer. 21732c593315Sopenharmony_ci if (req->continue_timer) { 21742c593315Sopenharmony_ci req->continue_timer->stop(); 21752c593315Sopenharmony_ci } 21762c593315Sopenharmony_ci 21772c593315Sopenharmony_ci update_html_parser(client, req, nullptr, 0, 1); 21782c593315Sopenharmony_ci ++client->complete; 21792c593315Sopenharmony_ci 21802c593315Sopenharmony_ci if (client->all_requests_processed()) { 21812c593315Sopenharmony_ci nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); 21822c593315Sopenharmony_ci } 21832c593315Sopenharmony_ci 21842c593315Sopenharmony_ci return 0; 21852c593315Sopenharmony_ci} 21862c593315Sopenharmony_ci} // namespace 21872c593315Sopenharmony_ci 21882c593315Sopenharmony_cistruct RequestResult { 21892c593315Sopenharmony_ci std::chrono::microseconds time; 21902c593315Sopenharmony_ci}; 21912c593315Sopenharmony_ci 21922c593315Sopenharmony_cinamespace { 21932c593315Sopenharmony_civoid print_stats(const HttpClient &client) { 21942c593315Sopenharmony_ci std::cout << "***** Statistics *****" << std::endl; 21952c593315Sopenharmony_ci 21962c593315Sopenharmony_ci std::vector<Request *> reqs; 21972c593315Sopenharmony_ci reqs.reserve(client.reqvec.size()); 21982c593315Sopenharmony_ci for (const auto &req : client.reqvec) { 21992c593315Sopenharmony_ci if (req->timing.state == RequestState::ON_COMPLETE) { 22002c593315Sopenharmony_ci reqs.push_back(req.get()); 22012c593315Sopenharmony_ci } 22022c593315Sopenharmony_ci } 22032c593315Sopenharmony_ci 22042c593315Sopenharmony_ci std::sort(std::begin(reqs), std::end(reqs), 22052c593315Sopenharmony_ci [](const Request *lhs, const Request *rhs) { 22062c593315Sopenharmony_ci const auto <iming = lhs->timing; 22072c593315Sopenharmony_ci const auto &rtiming = rhs->timing; 22082c593315Sopenharmony_ci return ltiming.response_end_time < rtiming.response_end_time || 22092c593315Sopenharmony_ci (ltiming.response_end_time == rtiming.response_end_time && 22102c593315Sopenharmony_ci ltiming.request_start_time < rtiming.request_start_time); 22112c593315Sopenharmony_ci }); 22122c593315Sopenharmony_ci 22132c593315Sopenharmony_ci std::cout << R"( 22142c593315Sopenharmony_ciRequest timing: 22152c593315Sopenharmony_ci responseEnd: the time when last byte of response was received 22162c593315Sopenharmony_ci relative to connectEnd 22172c593315Sopenharmony_ci requestStart: the time just before first byte of request was sent 22182c593315Sopenharmony_ci relative to connectEnd. If '*' is shown, this was 22192c593315Sopenharmony_ci pushed by server. 22202c593315Sopenharmony_ci process: responseEnd - requestStart 22212c593315Sopenharmony_ci code: HTTP status code 22222c593315Sopenharmony_ci size: number of bytes received as response body without 22232c593315Sopenharmony_ci inflation. 22242c593315Sopenharmony_ci URI: request URI 22252c593315Sopenharmony_ci 22262c593315Sopenharmony_cisee http://www.w3.org/TR/resource-timing/#processing-model 22272c593315Sopenharmony_ci 22282c593315Sopenharmony_cisorted by 'complete' 22292c593315Sopenharmony_ci 22302c593315Sopenharmony_ciid responseEnd requestStart process code size request path)" 22312c593315Sopenharmony_ci << std::endl; 22322c593315Sopenharmony_ci 22332c593315Sopenharmony_ci const auto &base = client.timing.connect_end_time; 22342c593315Sopenharmony_ci for (const auto &req : reqs) { 22352c593315Sopenharmony_ci auto response_end = std::chrono::duration_cast<std::chrono::microseconds>( 22362c593315Sopenharmony_ci req->timing.response_end_time - base); 22372c593315Sopenharmony_ci auto request_start = std::chrono::duration_cast<std::chrono::microseconds>( 22382c593315Sopenharmony_ci req->timing.request_start_time - base); 22392c593315Sopenharmony_ci auto total = std::chrono::duration_cast<std::chrono::microseconds>( 22402c593315Sopenharmony_ci req->timing.response_end_time - req->timing.request_start_time); 22412c593315Sopenharmony_ci auto pushed = req->stream_id % 2 == 0; 22422c593315Sopenharmony_ci 22432c593315Sopenharmony_ci std::cout << std::setw(3) << req->stream_id << " " << std::setw(11) 22442c593315Sopenharmony_ci << ("+" + util::format_duration(response_end)) << " " 22452c593315Sopenharmony_ci << (pushed ? "*" : " ") << std::setw(11) 22462c593315Sopenharmony_ci << ("+" + util::format_duration(request_start)) << " " 22472c593315Sopenharmony_ci << std::setw(8) << util::format_duration(total) << " " 22482c593315Sopenharmony_ci << std::setw(4) << req->status << " " << std::setw(4) 22492c593315Sopenharmony_ci << util::utos_unit(req->response_len) << " " 22502c593315Sopenharmony_ci << req->make_reqpath() << std::endl; 22512c593315Sopenharmony_ci } 22522c593315Sopenharmony_ci} 22532c593315Sopenharmony_ci} // namespace 22542c593315Sopenharmony_ci 22552c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG 22562c593315Sopenharmony_cinamespace { 22572c593315Sopenharmony_ciint client_select_next_proto_cb(SSL *ssl, unsigned char **out, 22582c593315Sopenharmony_ci unsigned char *outlen, const unsigned char *in, 22592c593315Sopenharmony_ci unsigned int inlen, void *arg) { 22602c593315Sopenharmony_ci if (config.verbose) { 22612c593315Sopenharmony_ci print_timer(); 22622c593315Sopenharmony_ci std::cout << "[NPN] server offers:" << std::endl; 22632c593315Sopenharmony_ci } 22642c593315Sopenharmony_ci for (unsigned int i = 0; i < inlen; i += in[i] + 1) { 22652c593315Sopenharmony_ci if (config.verbose) { 22662c593315Sopenharmony_ci std::cout << " * "; 22672c593315Sopenharmony_ci std::cout.write(reinterpret_cast<const char *>(&in[i + 1]), in[i]); 22682c593315Sopenharmony_ci std::cout << std::endl; 22692c593315Sopenharmony_ci } 22702c593315Sopenharmony_ci } 22712c593315Sopenharmony_ci if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in, 22722c593315Sopenharmony_ci inlen)) { 22732c593315Sopenharmony_ci print_protocol_nego_error(); 22742c593315Sopenharmony_ci return SSL_TLSEXT_ERR_NOACK; 22752c593315Sopenharmony_ci } 22762c593315Sopenharmony_ci return SSL_TLSEXT_ERR_OK; 22772c593315Sopenharmony_ci} 22782c593315Sopenharmony_ci} // namespace 22792c593315Sopenharmony_ci#endif // !OPENSSL_NO_NEXTPROTONEG 22802c593315Sopenharmony_ci 22812c593315Sopenharmony_cinamespace { 22822c593315Sopenharmony_ciint communicate( 22832c593315Sopenharmony_ci const std::string &scheme, const std::string &host, uint16_t port, 22842c593315Sopenharmony_ci std::vector< 22852c593315Sopenharmony_ci std::tuple<std::string, nghttp2_data_provider *, int64_t, int32_t>> 22862c593315Sopenharmony_ci requests, 22872c593315Sopenharmony_ci const nghttp2_session_callbacks *callbacks) { 22882c593315Sopenharmony_ci int result = 0; 22892c593315Sopenharmony_ci auto loop = EV_DEFAULT; 22902c593315Sopenharmony_ci SSL_CTX *ssl_ctx = nullptr; 22912c593315Sopenharmony_ci if (scheme == "https") { 22922c593315Sopenharmony_ci ssl_ctx = SSL_CTX_new(TLS_client_method()); 22932c593315Sopenharmony_ci if (!ssl_ctx) { 22942c593315Sopenharmony_ci std::cerr << "[ERROR] Failed to create SSL_CTX: " 22952c593315Sopenharmony_ci << ERR_error_string(ERR_get_error(), nullptr) << std::endl; 22962c593315Sopenharmony_ci result = -1; 22972c593315Sopenharmony_ci goto fin; 22982c593315Sopenharmony_ci } 22992c593315Sopenharmony_ci 23002c593315Sopenharmony_ci auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | 23012c593315Sopenharmony_ci SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | 23022c593315Sopenharmony_ci SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; 23032c593315Sopenharmony_ci 23042c593315Sopenharmony_ci#ifdef SSL_OP_ENABLE_KTLS 23052c593315Sopenharmony_ci if (config.ktls) { 23062c593315Sopenharmony_ci ssl_opts |= SSL_OP_ENABLE_KTLS; 23072c593315Sopenharmony_ci } 23082c593315Sopenharmony_ci#endif // SSL_OP_ENABLE_KTLS 23092c593315Sopenharmony_ci 23102c593315Sopenharmony_ci SSL_CTX_set_options(ssl_ctx, ssl_opts); 23112c593315Sopenharmony_ci SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); 23122c593315Sopenharmony_ci SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); 23132c593315Sopenharmony_ci 23142c593315Sopenharmony_ci if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { 23152c593315Sopenharmony_ci std::cerr << "[WARNING] Could not load system trusted CA certificates: " 23162c593315Sopenharmony_ci << ERR_error_string(ERR_get_error(), nullptr) << std::endl; 23172c593315Sopenharmony_ci } 23182c593315Sopenharmony_ci 23192c593315Sopenharmony_ci if (nghttp2::tls::ssl_ctx_set_proto_versions( 23202c593315Sopenharmony_ci ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION, 23212c593315Sopenharmony_ci nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) { 23222c593315Sopenharmony_ci std::cerr << "[ERROR] Could not set TLS versions" << std::endl; 23232c593315Sopenharmony_ci result = -1; 23242c593315Sopenharmony_ci goto fin; 23252c593315Sopenharmony_ci } 23262c593315Sopenharmony_ci 23272c593315Sopenharmony_ci if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) { 23282c593315Sopenharmony_ci std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) 23292c593315Sopenharmony_ci << std::endl; 23302c593315Sopenharmony_ci result = -1; 23312c593315Sopenharmony_ci goto fin; 23322c593315Sopenharmony_ci } 23332c593315Sopenharmony_ci if (!config.keyfile.empty()) { 23342c593315Sopenharmony_ci if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(), 23352c593315Sopenharmony_ci SSL_FILETYPE_PEM) != 1) { 23362c593315Sopenharmony_ci std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) 23372c593315Sopenharmony_ci << std::endl; 23382c593315Sopenharmony_ci result = -1; 23392c593315Sopenharmony_ci goto fin; 23402c593315Sopenharmony_ci } 23412c593315Sopenharmony_ci } 23422c593315Sopenharmony_ci if (!config.certfile.empty()) { 23432c593315Sopenharmony_ci if (SSL_CTX_use_certificate_chain_file(ssl_ctx, 23442c593315Sopenharmony_ci config.certfile.c_str()) != 1) { 23452c593315Sopenharmony_ci std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) 23462c593315Sopenharmony_ci << std::endl; 23472c593315Sopenharmony_ci result = -1; 23482c593315Sopenharmony_ci goto fin; 23492c593315Sopenharmony_ci } 23502c593315Sopenharmony_ci } 23512c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG 23522c593315Sopenharmony_ci SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb, 23532c593315Sopenharmony_ci nullptr); 23542c593315Sopenharmony_ci#endif // !OPENSSL_NO_NEXTPROTONEG 23552c593315Sopenharmony_ci 23562c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x10002000L 23572c593315Sopenharmony_ci auto proto_list = util::get_default_alpn(); 23582c593315Sopenharmony_ci 23592c593315Sopenharmony_ci SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); 23602c593315Sopenharmony_ci#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L 23612c593315Sopenharmony_ci } 23622c593315Sopenharmony_ci { 23632c593315Sopenharmony_ci HttpClient client{callbacks, loop, ssl_ctx}; 23642c593315Sopenharmony_ci 23652c593315Sopenharmony_ci int32_t dep_stream_id = 0; 23662c593315Sopenharmony_ci 23672c593315Sopenharmony_ci if (!config.no_dep) { 23682c593315Sopenharmony_ci dep_stream_id = anchors[ANCHOR_FOLLOWERS].stream_id; 23692c593315Sopenharmony_ci } 23702c593315Sopenharmony_ci 23712c593315Sopenharmony_ci for (auto &req : requests) { 23722c593315Sopenharmony_ci nghttp2_priority_spec pri_spec; 23732c593315Sopenharmony_ci 23742c593315Sopenharmony_ci nghttp2_priority_spec_init(&pri_spec, dep_stream_id, std::get<3>(req), 0); 23752c593315Sopenharmony_ci 23762c593315Sopenharmony_ci for (int i = 0; i < config.multiply; ++i) { 23772c593315Sopenharmony_ci client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req), 23782c593315Sopenharmony_ci pri_spec); 23792c593315Sopenharmony_ci } 23802c593315Sopenharmony_ci } 23812c593315Sopenharmony_ci client.update_hostport(); 23822c593315Sopenharmony_ci 23832c593315Sopenharmony_ci client.record_start_time(); 23842c593315Sopenharmony_ci 23852c593315Sopenharmony_ci if (client.resolve_host(host, port) != 0) { 23862c593315Sopenharmony_ci goto fin; 23872c593315Sopenharmony_ci } 23882c593315Sopenharmony_ci 23892c593315Sopenharmony_ci client.record_domain_lookup_end_time(); 23902c593315Sopenharmony_ci 23912c593315Sopenharmony_ci if (client.initiate_connection() != 0) { 23922c593315Sopenharmony_ci std::cerr << "[ERROR] Could not connect to " << host << ", port " << port 23932c593315Sopenharmony_ci << std::endl; 23942c593315Sopenharmony_ci goto fin; 23952c593315Sopenharmony_ci } 23962c593315Sopenharmony_ci 23972c593315Sopenharmony_ci ev_set_userdata(loop, &client); 23982c593315Sopenharmony_ci ev_run(loop, 0); 23992c593315Sopenharmony_ci ev_set_userdata(loop, nullptr); 24002c593315Sopenharmony_ci 24012c593315Sopenharmony_ci#ifdef HAVE_JANSSON 24022c593315Sopenharmony_ci if (!config.harfile.empty()) { 24032c593315Sopenharmony_ci FILE *outfile; 24042c593315Sopenharmony_ci if (config.harfile == "-") { 24052c593315Sopenharmony_ci outfile = stdout; 24062c593315Sopenharmony_ci } else { 24072c593315Sopenharmony_ci outfile = fopen(config.harfile.c_str(), "wb"); 24082c593315Sopenharmony_ci } 24092c593315Sopenharmony_ci 24102c593315Sopenharmony_ci if (outfile) { 24112c593315Sopenharmony_ci client.output_har(outfile); 24122c593315Sopenharmony_ci 24132c593315Sopenharmony_ci if (outfile != stdout) { 24142c593315Sopenharmony_ci fclose(outfile); 24152c593315Sopenharmony_ci } 24162c593315Sopenharmony_ci } else { 24172c593315Sopenharmony_ci std::cerr << "Cannot open file " << config.harfile << ". " 24182c593315Sopenharmony_ci << "har file could not be created." << std::endl; 24192c593315Sopenharmony_ci } 24202c593315Sopenharmony_ci } 24212c593315Sopenharmony_ci#endif // HAVE_JANSSON 24222c593315Sopenharmony_ci 24232c593315Sopenharmony_ci if (client.success != client.reqvec.size()) { 24242c593315Sopenharmony_ci std::cerr << "Some requests were not processed. total=" 24252c593315Sopenharmony_ci << client.reqvec.size() << ", processed=" << client.success 24262c593315Sopenharmony_ci << std::endl; 24272c593315Sopenharmony_ci } 24282c593315Sopenharmony_ci if (config.stat) { 24292c593315Sopenharmony_ci print_stats(client); 24302c593315Sopenharmony_ci } 24312c593315Sopenharmony_ci } 24322c593315Sopenharmony_cifin: 24332c593315Sopenharmony_ci if (ssl_ctx) { 24342c593315Sopenharmony_ci SSL_CTX_free(ssl_ctx); 24352c593315Sopenharmony_ci } 24362c593315Sopenharmony_ci return result; 24372c593315Sopenharmony_ci} 24382c593315Sopenharmony_ci} // namespace 24392c593315Sopenharmony_ci 24402c593315Sopenharmony_cinamespace { 24412c593315Sopenharmony_cissize_t file_read_callback(nghttp2_session *session, int32_t stream_id, 24422c593315Sopenharmony_ci uint8_t *buf, size_t length, uint32_t *data_flags, 24432c593315Sopenharmony_ci nghttp2_data_source *source, void *user_data) { 24442c593315Sopenharmony_ci int rv; 24452c593315Sopenharmony_ci auto req = static_cast<Request *>( 24462c593315Sopenharmony_ci nghttp2_session_get_stream_user_data(session, stream_id)); 24472c593315Sopenharmony_ci assert(req); 24482c593315Sopenharmony_ci int fd = source->fd; 24492c593315Sopenharmony_ci ssize_t nread; 24502c593315Sopenharmony_ci 24512c593315Sopenharmony_ci while ((nread = pread(fd, buf, length, req->data_offset)) == -1 && 24522c593315Sopenharmony_ci errno == EINTR) 24532c593315Sopenharmony_ci ; 24542c593315Sopenharmony_ci 24552c593315Sopenharmony_ci if (nread == -1) { 24562c593315Sopenharmony_ci return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 24572c593315Sopenharmony_ci } 24582c593315Sopenharmony_ci 24592c593315Sopenharmony_ci req->data_offset += nread; 24602c593315Sopenharmony_ci 24612c593315Sopenharmony_ci if (req->data_offset == req->data_length) { 24622c593315Sopenharmony_ci *data_flags |= NGHTTP2_DATA_FLAG_EOF; 24632c593315Sopenharmony_ci if (!config.trailer.empty()) { 24642c593315Sopenharmony_ci std::vector<nghttp2_nv> nva; 24652c593315Sopenharmony_ci nva.reserve(config.trailer.size()); 24662c593315Sopenharmony_ci for (auto &kv : config.trailer) { 24672c593315Sopenharmony_ci nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); 24682c593315Sopenharmony_ci } 24692c593315Sopenharmony_ci rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); 24702c593315Sopenharmony_ci if (rv != 0) { 24712c593315Sopenharmony_ci if (nghttp2_is_fatal(rv)) { 24722c593315Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 24732c593315Sopenharmony_ci } 24742c593315Sopenharmony_ci } else { 24752c593315Sopenharmony_ci *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; 24762c593315Sopenharmony_ci } 24772c593315Sopenharmony_ci } 24782c593315Sopenharmony_ci 24792c593315Sopenharmony_ci return nread; 24802c593315Sopenharmony_ci } 24812c593315Sopenharmony_ci 24822c593315Sopenharmony_ci if (req->data_offset > req->data_length || nread == 0) { 24832c593315Sopenharmony_ci return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 24842c593315Sopenharmony_ci } 24852c593315Sopenharmony_ci 24862c593315Sopenharmony_ci return nread; 24872c593315Sopenharmony_ci} 24882c593315Sopenharmony_ci} // namespace 24892c593315Sopenharmony_ci 24902c593315Sopenharmony_cinamespace { 24912c593315Sopenharmony_ciint run(char **uris, int n) { 24922c593315Sopenharmony_ci nghttp2_session_callbacks *callbacks; 24932c593315Sopenharmony_ci 24942c593315Sopenharmony_ci nghttp2_session_callbacks_new(&callbacks); 24952c593315Sopenharmony_ci auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks); 24962c593315Sopenharmony_ci 24972c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_stream_close_callback( 24982c593315Sopenharmony_ci callbacks, on_stream_close_callback); 24992c593315Sopenharmony_ci 25002c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, 25012c593315Sopenharmony_ci on_frame_recv_callback2); 25022c593315Sopenharmony_ci 25032c593315Sopenharmony_ci if (config.verbose) { 25042c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( 25052c593315Sopenharmony_ci callbacks, verbose_on_invalid_frame_recv_callback); 25062c593315Sopenharmony_ci 25072c593315Sopenharmony_ci nghttp2_session_callbacks_set_error_callback2(callbacks, 25082c593315Sopenharmony_ci verbose_error_callback); 25092c593315Sopenharmony_ci } 25102c593315Sopenharmony_ci 25112c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_data_chunk_recv_callback( 25122c593315Sopenharmony_ci callbacks, on_data_chunk_recv_callback); 25132c593315Sopenharmony_ci 25142c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_begin_headers_callback( 25152c593315Sopenharmony_ci callbacks, on_begin_headers_callback); 25162c593315Sopenharmony_ci 25172c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_header_callback(callbacks, 25182c593315Sopenharmony_ci on_header_callback); 25192c593315Sopenharmony_ci 25202c593315Sopenharmony_ci nghttp2_session_callbacks_set_before_frame_send_callback( 25212c593315Sopenharmony_ci callbacks, before_frame_send_callback); 25222c593315Sopenharmony_ci 25232c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, 25242c593315Sopenharmony_ci on_frame_send_callback); 25252c593315Sopenharmony_ci 25262c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_not_send_callback( 25272c593315Sopenharmony_ci callbacks, on_frame_not_send_callback); 25282c593315Sopenharmony_ci 25292c593315Sopenharmony_ci if (config.padding) { 25302c593315Sopenharmony_ci nghttp2_session_callbacks_set_select_padding_callback( 25312c593315Sopenharmony_ci callbacks, select_padding_callback); 25322c593315Sopenharmony_ci } 25332c593315Sopenharmony_ci 25342c593315Sopenharmony_ci std::string prev_scheme; 25352c593315Sopenharmony_ci std::string prev_host; 25362c593315Sopenharmony_ci uint16_t prev_port = 0; 25372c593315Sopenharmony_ci int failures = 0; 25382c593315Sopenharmony_ci int data_fd = -1; 25392c593315Sopenharmony_ci nghttp2_data_provider data_prd; 25402c593315Sopenharmony_ci struct stat data_stat; 25412c593315Sopenharmony_ci 25422c593315Sopenharmony_ci if (!config.datafile.empty()) { 25432c593315Sopenharmony_ci if (config.datafile == "-") { 25442c593315Sopenharmony_ci if (fstat(0, &data_stat) == 0 && 25452c593315Sopenharmony_ci (data_stat.st_mode & S_IFMT) == S_IFREG) { 25462c593315Sopenharmony_ci // use STDIN if it is a regular file 25472c593315Sopenharmony_ci data_fd = 0; 25482c593315Sopenharmony_ci } else { 25492c593315Sopenharmony_ci // copy the contents of STDIN to a temporary file 25502c593315Sopenharmony_ci char tempfn[] = "/tmp/nghttp.temp.XXXXXX"; 25512c593315Sopenharmony_ci data_fd = mkstemp(tempfn); 25522c593315Sopenharmony_ci if (data_fd == -1) { 25532c593315Sopenharmony_ci std::cerr << "[ERROR] Could not create a temporary file in /tmp" 25542c593315Sopenharmony_ci << std::endl; 25552c593315Sopenharmony_ci return 1; 25562c593315Sopenharmony_ci } 25572c593315Sopenharmony_ci if (unlink(tempfn) != 0) { 25582c593315Sopenharmony_ci std::cerr << "[WARNING] failed to unlink temporary file:" << tempfn 25592c593315Sopenharmony_ci << std::endl; 25602c593315Sopenharmony_ci } 25612c593315Sopenharmony_ci while (1) { 25622c593315Sopenharmony_ci std::array<char, 1_k> buf; 25632c593315Sopenharmony_ci ssize_t rret, wret; 25642c593315Sopenharmony_ci while ((rret = read(0, buf.data(), buf.size())) == -1 && 25652c593315Sopenharmony_ci errno == EINTR) 25662c593315Sopenharmony_ci ; 25672c593315Sopenharmony_ci if (rret == 0) 25682c593315Sopenharmony_ci break; 25692c593315Sopenharmony_ci if (rret == -1) { 25702c593315Sopenharmony_ci std::cerr << "[ERROR] I/O error while reading from STDIN" 25712c593315Sopenharmony_ci << std::endl; 25722c593315Sopenharmony_ci return 1; 25732c593315Sopenharmony_ci } 25742c593315Sopenharmony_ci while ((wret = write(data_fd, buf.data(), rret)) == -1 && 25752c593315Sopenharmony_ci errno == EINTR) 25762c593315Sopenharmony_ci ; 25772c593315Sopenharmony_ci if (wret != rret) { 25782c593315Sopenharmony_ci std::cerr << "[ERROR] I/O error while writing to temporary file" 25792c593315Sopenharmony_ci << std::endl; 25802c593315Sopenharmony_ci return 1; 25812c593315Sopenharmony_ci } 25822c593315Sopenharmony_ci } 25832c593315Sopenharmony_ci if (fstat(data_fd, &data_stat) == -1) { 25842c593315Sopenharmony_ci close(data_fd); 25852c593315Sopenharmony_ci std::cerr << "[ERROR] Could not stat temporary file" << std::endl; 25862c593315Sopenharmony_ci return 1; 25872c593315Sopenharmony_ci } 25882c593315Sopenharmony_ci } 25892c593315Sopenharmony_ci } else { 25902c593315Sopenharmony_ci data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY); 25912c593315Sopenharmony_ci if (data_fd == -1) { 25922c593315Sopenharmony_ci std::cerr << "[ERROR] Could not open file " << config.datafile 25932c593315Sopenharmony_ci << std::endl; 25942c593315Sopenharmony_ci return 1; 25952c593315Sopenharmony_ci } 25962c593315Sopenharmony_ci if (fstat(data_fd, &data_stat) == -1) { 25972c593315Sopenharmony_ci close(data_fd); 25982c593315Sopenharmony_ci std::cerr << "[ERROR] Could not stat file " << config.datafile 25992c593315Sopenharmony_ci << std::endl; 26002c593315Sopenharmony_ci return 1; 26012c593315Sopenharmony_ci } 26022c593315Sopenharmony_ci } 26032c593315Sopenharmony_ci data_prd.source.fd = data_fd; 26042c593315Sopenharmony_ci data_prd.read_callback = file_read_callback; 26052c593315Sopenharmony_ci } 26062c593315Sopenharmony_ci std::vector< 26072c593315Sopenharmony_ci std::tuple<std::string, nghttp2_data_provider *, int64_t, int32_t>> 26082c593315Sopenharmony_ci requests; 26092c593315Sopenharmony_ci 26102c593315Sopenharmony_ci size_t next_weight_idx = 0; 26112c593315Sopenharmony_ci 26122c593315Sopenharmony_ci for (int i = 0; i < n; ++i) { 26132c593315Sopenharmony_ci http_parser_url u{}; 26142c593315Sopenharmony_ci auto uri = strip_fragment(uris[i]); 26152c593315Sopenharmony_ci if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { 26162c593315Sopenharmony_ci ++next_weight_idx; 26172c593315Sopenharmony_ci std::cerr << "[ERROR] Could not parse URI " << uri << std::endl; 26182c593315Sopenharmony_ci continue; 26192c593315Sopenharmony_ci } 26202c593315Sopenharmony_ci if (!util::has_uri_field(u, UF_SCHEMA)) { 26212c593315Sopenharmony_ci ++next_weight_idx; 26222c593315Sopenharmony_ci std::cerr << "[ERROR] URI " << uri << " does not have scheme part" 26232c593315Sopenharmony_ci << std::endl; 26242c593315Sopenharmony_ci continue; 26252c593315Sopenharmony_ci } 26262c593315Sopenharmony_ci auto port = util::has_uri_field(u, UF_PORT) 26272c593315Sopenharmony_ci ? u.port 26282c593315Sopenharmony_ci : util::get_default_port(uri.c_str(), u); 26292c593315Sopenharmony_ci auto host = decode_host(util::get_uri_field(uri.c_str(), u, UF_HOST)); 26302c593315Sopenharmony_ci if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) || 26312c593315Sopenharmony_ci host != prev_host || port != prev_port) { 26322c593315Sopenharmony_ci if (!requests.empty()) { 26332c593315Sopenharmony_ci if (communicate(prev_scheme, prev_host, prev_port, std::move(requests), 26342c593315Sopenharmony_ci callbacks) != 0) { 26352c593315Sopenharmony_ci ++failures; 26362c593315Sopenharmony_ci } 26372c593315Sopenharmony_ci requests.clear(); 26382c593315Sopenharmony_ci } 26392c593315Sopenharmony_ci prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA).str(); 26402c593315Sopenharmony_ci prev_host = std::move(host); 26412c593315Sopenharmony_ci prev_port = port; 26422c593315Sopenharmony_ci } 26432c593315Sopenharmony_ci requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd, 26442c593315Sopenharmony_ci data_stat.st_size, config.weight[next_weight_idx++]); 26452c593315Sopenharmony_ci } 26462c593315Sopenharmony_ci if (!requests.empty()) { 26472c593315Sopenharmony_ci if (communicate(prev_scheme, prev_host, prev_port, std::move(requests), 26482c593315Sopenharmony_ci callbacks) != 0) { 26492c593315Sopenharmony_ci ++failures; 26502c593315Sopenharmony_ci } 26512c593315Sopenharmony_ci } 26522c593315Sopenharmony_ci return failures; 26532c593315Sopenharmony_ci} 26542c593315Sopenharmony_ci} // namespace 26552c593315Sopenharmony_ci 26562c593315Sopenharmony_cinamespace { 26572c593315Sopenharmony_civoid print_version(std::ostream &out) { 26582c593315Sopenharmony_ci out << "nghttp nghttp2/" NGHTTP2_VERSION << std::endl; 26592c593315Sopenharmony_ci} 26602c593315Sopenharmony_ci} // namespace 26612c593315Sopenharmony_ci 26622c593315Sopenharmony_cinamespace { 26632c593315Sopenharmony_civoid print_usage(std::ostream &out) { 26642c593315Sopenharmony_ci out << R"(Usage: nghttp [OPTIONS]... <URI>... 26652c593315Sopenharmony_ciHTTP/2 client)" 26662c593315Sopenharmony_ci << std::endl; 26672c593315Sopenharmony_ci} 26682c593315Sopenharmony_ci} // namespace 26692c593315Sopenharmony_ci 26702c593315Sopenharmony_cinamespace { 26712c593315Sopenharmony_civoid print_help(std::ostream &out) { 26722c593315Sopenharmony_ci print_usage(out); 26732c593315Sopenharmony_ci out << R"( 26742c593315Sopenharmony_ci <URI> Specify URI to access. 26752c593315Sopenharmony_ciOptions: 26762c593315Sopenharmony_ci -v, --verbose 26772c593315Sopenharmony_ci Print debug information such as reception and 26782c593315Sopenharmony_ci transmission of frames and name/value pairs. Specifying 26792c593315Sopenharmony_ci this option multiple times increases verbosity. 26802c593315Sopenharmony_ci -n, --null-out 26812c593315Sopenharmony_ci Discard downloaded data. 26822c593315Sopenharmony_ci -O, --remote-name 26832c593315Sopenharmony_ci Save download data in the current directory. The 26842c593315Sopenharmony_ci filename is derived from URI. If URI ends with '/', 26852c593315Sopenharmony_ci 'index.html' is used as a filename. Not implemented 26862c593315Sopenharmony_ci yet. 26872c593315Sopenharmony_ci -t, --timeout=<DURATION> 26882c593315Sopenharmony_ci Timeout each request after <DURATION>. Set 0 to disable 26892c593315Sopenharmony_ci timeout. 26902c593315Sopenharmony_ci -w, --window-bits=<N> 26912c593315Sopenharmony_ci Sets the stream level initial window size to 2**<N>-1. 26922c593315Sopenharmony_ci -W, --connection-window-bits=<N> 26932c593315Sopenharmony_ci Sets the connection level initial window size to 26942c593315Sopenharmony_ci 2**<N>-1. 26952c593315Sopenharmony_ci -a, --get-assets 26962c593315Sopenharmony_ci Download assets such as stylesheets, images and script 26972c593315Sopenharmony_ci files linked from the downloaded resource. Only links 26982c593315Sopenharmony_ci whose origins are the same with the linking resource 26992c593315Sopenharmony_ci will be downloaded. nghttp prioritizes resources using 27002c593315Sopenharmony_ci HTTP/2 dependency based priority. The priority order, 27012c593315Sopenharmony_ci from highest to lowest, is html itself, css, javascript 27022c593315Sopenharmony_ci and images. 27032c593315Sopenharmony_ci -s, --stat Print statistics. 27042c593315Sopenharmony_ci -H, --header=<HEADER> 27052c593315Sopenharmony_ci Add a header to the requests. Example: -H':method: PUT' 27062c593315Sopenharmony_ci --trailer=<HEADER> 27072c593315Sopenharmony_ci Add a trailer header to the requests. <HEADER> must not 27082c593315Sopenharmony_ci include pseudo header field (header field name starting 27092c593315Sopenharmony_ci with ':'). To send trailer, one must use -d option to 27102c593315Sopenharmony_ci send request body. Example: --trailer 'foo: bar'. 27112c593315Sopenharmony_ci --cert=<CERT> 27122c593315Sopenharmony_ci Use the specified client certificate file. The file 27132c593315Sopenharmony_ci must be in PEM format. 27142c593315Sopenharmony_ci --key=<KEY> Use the client private key file. The file must be in 27152c593315Sopenharmony_ci PEM format. 27162c593315Sopenharmony_ci -d, --data=<PATH> 27172c593315Sopenharmony_ci Post FILE to server. If '-' is given, data will be read 27182c593315Sopenharmony_ci from stdin. 27192c593315Sopenharmony_ci -m, --multiply=<N> 27202c593315Sopenharmony_ci Request each URI <N> times. By default, same URI is not 27212c593315Sopenharmony_ci requested twice. This option disables it too. 27222c593315Sopenharmony_ci -u, --upgrade 27232c593315Sopenharmony_ci Perform HTTP Upgrade for HTTP/2. This option is ignored 27242c593315Sopenharmony_ci if the request URI has https scheme. If -d is used, the 27252c593315Sopenharmony_ci HTTP upgrade request is performed with OPTIONS method. 27262c593315Sopenharmony_ci -p, --weight=<WEIGHT> 27272c593315Sopenharmony_ci Sets weight of given URI. This option can be used 27282c593315Sopenharmony_ci multiple times, and N-th -p option sets weight of N-th 27292c593315Sopenharmony_ci URI in the command line. If the number of -p option is 27302c593315Sopenharmony_ci less than the number of URI, the last -p option value is 27312c593315Sopenharmony_ci repeated. If there is no -p option, default weight, 16, 27322c593315Sopenharmony_ci is assumed. The valid value range is 27332c593315Sopenharmony_ci [)" 27342c593315Sopenharmony_ci << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT << R"(], inclusive. 27352c593315Sopenharmony_ci -M, --peer-max-concurrent-streams=<N> 27362c593315Sopenharmony_ci Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of 27372c593315Sopenharmony_ci remote endpoint as if it is received in SETTINGS frame. 27382c593315Sopenharmony_ci Default: 100 27392c593315Sopenharmony_ci -c, --header-table-size=<SIZE> 27402c593315Sopenharmony_ci Specify decoder header table size. If this option is 27412c593315Sopenharmony_ci used multiple times, and the minimum value among the 27422c593315Sopenharmony_ci given values except for last one is strictly less than 27432c593315Sopenharmony_ci the last value, that minimum value is set in SETTINGS 27442c593315Sopenharmony_ci frame payload before the last value, to simulate 27452c593315Sopenharmony_ci multiple header table size change. 27462c593315Sopenharmony_ci --encoder-header-table-size=<SIZE> 27472c593315Sopenharmony_ci Specify encoder header table size. The decoder (server) 27482c593315Sopenharmony_ci specifies the maximum dynamic table size it accepts. 27492c593315Sopenharmony_ci Then the negotiated dynamic table size is the minimum of 27502c593315Sopenharmony_ci this option value and the value which server specified. 27512c593315Sopenharmony_ci -b, --padding=<N> 27522c593315Sopenharmony_ci Add at most <N> bytes to a frame payload as padding. 27532c593315Sopenharmony_ci Specify 0 to disable padding. 27542c593315Sopenharmony_ci -r, --har=<PATH> 27552c593315Sopenharmony_ci Output HTTP transactions <PATH> in HAR format. If '-' 27562c593315Sopenharmony_ci is given, data is written to stdout. 27572c593315Sopenharmony_ci --color Force colored log output. 27582c593315Sopenharmony_ci --continuation 27592c593315Sopenharmony_ci Send large header to test CONTINUATION. 27602c593315Sopenharmony_ci --no-content-length 27612c593315Sopenharmony_ci Don't send content-length header field. 27622c593315Sopenharmony_ci --no-dep Don't send dependency based priority hint to server. 27632c593315Sopenharmony_ci --hexdump Display the incoming traffic in hexadecimal (Canonical 27642c593315Sopenharmony_ci hex+ASCII display). If SSL/TLS is used, decrypted data 27652c593315Sopenharmony_ci are used. 27662c593315Sopenharmony_ci --no-push Disable server push. 27672c593315Sopenharmony_ci --max-concurrent-streams=<N> 27682c593315Sopenharmony_ci The number of concurrent pushed streams this client 27692c593315Sopenharmony_ci accepts. 27702c593315Sopenharmony_ci --expect-continue 27712c593315Sopenharmony_ci Perform an Expect/Continue handshake: wait to send DATA 27722c593315Sopenharmony_ci (up to a short timeout) until the server sends a 100 27732c593315Sopenharmony_ci Continue interim response. This option is ignored unless 27742c593315Sopenharmony_ci combined with the -d option. 27752c593315Sopenharmony_ci -y, --no-verify-peer 27762c593315Sopenharmony_ci Suppress warning on server certificate verification 27772c593315Sopenharmony_ci failure. 27782c593315Sopenharmony_ci --ktls Enable ktls. 27792c593315Sopenharmony_ci --no-rfc7540-pri 27802c593315Sopenharmony_ci Disable RFC7540 priorities. 27812c593315Sopenharmony_ci --version Display version information and exit. 27822c593315Sopenharmony_ci -h, --help Display this help and exit. 27832c593315Sopenharmony_ci 27842c593315Sopenharmony_ci-- 27852c593315Sopenharmony_ci 27862c593315Sopenharmony_ci The <SIZE> argument is an integer and an optional unit (e.g., 10K is 27872c593315Sopenharmony_ci 10 * 1024). Units are K, M and G (powers of 1024). 27882c593315Sopenharmony_ci 27892c593315Sopenharmony_ci The <DURATION> argument is an integer and an optional unit (e.g., 1s 27902c593315Sopenharmony_ci is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms 27912c593315Sopenharmony_ci (hours, minutes, seconds and milliseconds, respectively). If a unit 27922c593315Sopenharmony_ci is omitted, a second is used as unit.)" 27932c593315Sopenharmony_ci << std::endl; 27942c593315Sopenharmony_ci} 27952c593315Sopenharmony_ci} // namespace 27962c593315Sopenharmony_ci 27972c593315Sopenharmony_ciint main(int argc, char **argv) { 27982c593315Sopenharmony_ci tls::libssl_init(); 27992c593315Sopenharmony_ci 28002c593315Sopenharmony_ci bool color = false; 28012c593315Sopenharmony_ci while (1) { 28022c593315Sopenharmony_ci static int flag = 0; 28032c593315Sopenharmony_ci constexpr static option long_options[] = { 28042c593315Sopenharmony_ci {"verbose", no_argument, nullptr, 'v'}, 28052c593315Sopenharmony_ci {"null-out", no_argument, nullptr, 'n'}, 28062c593315Sopenharmony_ci {"remote-name", no_argument, nullptr, 'O'}, 28072c593315Sopenharmony_ci {"timeout", required_argument, nullptr, 't'}, 28082c593315Sopenharmony_ci {"window-bits", required_argument, nullptr, 'w'}, 28092c593315Sopenharmony_ci {"connection-window-bits", required_argument, nullptr, 'W'}, 28102c593315Sopenharmony_ci {"get-assets", no_argument, nullptr, 'a'}, 28112c593315Sopenharmony_ci {"stat", no_argument, nullptr, 's'}, 28122c593315Sopenharmony_ci {"help", no_argument, nullptr, 'h'}, 28132c593315Sopenharmony_ci {"header", required_argument, nullptr, 'H'}, 28142c593315Sopenharmony_ci {"data", required_argument, nullptr, 'd'}, 28152c593315Sopenharmony_ci {"multiply", required_argument, nullptr, 'm'}, 28162c593315Sopenharmony_ci {"upgrade", no_argument, nullptr, 'u'}, 28172c593315Sopenharmony_ci {"weight", required_argument, nullptr, 'p'}, 28182c593315Sopenharmony_ci {"peer-max-concurrent-streams", required_argument, nullptr, 'M'}, 28192c593315Sopenharmony_ci {"header-table-size", required_argument, nullptr, 'c'}, 28202c593315Sopenharmony_ci {"padding", required_argument, nullptr, 'b'}, 28212c593315Sopenharmony_ci {"har", required_argument, nullptr, 'r'}, 28222c593315Sopenharmony_ci {"no-verify-peer", no_argument, nullptr, 'y'}, 28232c593315Sopenharmony_ci {"cert", required_argument, &flag, 1}, 28242c593315Sopenharmony_ci {"key", required_argument, &flag, 2}, 28252c593315Sopenharmony_ci {"color", no_argument, &flag, 3}, 28262c593315Sopenharmony_ci {"continuation", no_argument, &flag, 4}, 28272c593315Sopenharmony_ci {"version", no_argument, &flag, 5}, 28282c593315Sopenharmony_ci {"no-content-length", no_argument, &flag, 6}, 28292c593315Sopenharmony_ci {"no-dep", no_argument, &flag, 7}, 28302c593315Sopenharmony_ci {"trailer", required_argument, &flag, 9}, 28312c593315Sopenharmony_ci {"hexdump", no_argument, &flag, 10}, 28322c593315Sopenharmony_ci {"no-push", no_argument, &flag, 11}, 28332c593315Sopenharmony_ci {"max-concurrent-streams", required_argument, &flag, 12}, 28342c593315Sopenharmony_ci {"expect-continue", no_argument, &flag, 13}, 28352c593315Sopenharmony_ci {"encoder-header-table-size", required_argument, &flag, 14}, 28362c593315Sopenharmony_ci {"ktls", no_argument, &flag, 15}, 28372c593315Sopenharmony_ci {"no-rfc7540-pri", no_argument, &flag, 16}, 28382c593315Sopenharmony_ci {nullptr, 0, nullptr, 0}}; 28392c593315Sopenharmony_ci int option_index = 0; 28402c593315Sopenharmony_ci int c = 28412c593315Sopenharmony_ci getopt_long(argc, argv, "M:Oab:c:d:m:np:r:hH:vst:uw:yW:", long_options, 28422c593315Sopenharmony_ci &option_index); 28432c593315Sopenharmony_ci if (c == -1) { 28442c593315Sopenharmony_ci break; 28452c593315Sopenharmony_ci } 28462c593315Sopenharmony_ci switch (c) { 28472c593315Sopenharmony_ci case 'M': { 28482c593315Sopenharmony_ci // peer-max-concurrent-streams option 28492c593315Sopenharmony_ci auto n = util::parse_uint(optarg); 28502c593315Sopenharmony_ci if (n == -1) { 28512c593315Sopenharmony_ci std::cerr << "-M: Bad option value: " << optarg << std::endl; 28522c593315Sopenharmony_ci exit(EXIT_FAILURE); 28532c593315Sopenharmony_ci } 28542c593315Sopenharmony_ci config.peer_max_concurrent_streams = n; 28552c593315Sopenharmony_ci break; 28562c593315Sopenharmony_ci } 28572c593315Sopenharmony_ci case 'O': 28582c593315Sopenharmony_ci config.remote_name = true; 28592c593315Sopenharmony_ci break; 28602c593315Sopenharmony_ci case 'h': 28612c593315Sopenharmony_ci print_help(std::cout); 28622c593315Sopenharmony_ci exit(EXIT_SUCCESS); 28632c593315Sopenharmony_ci case 'b': { 28642c593315Sopenharmony_ci auto n = util::parse_uint(optarg); 28652c593315Sopenharmony_ci if (n == -1) { 28662c593315Sopenharmony_ci std::cerr << "-b: Bad option value: " << optarg << std::endl; 28672c593315Sopenharmony_ci exit(EXIT_FAILURE); 28682c593315Sopenharmony_ci } 28692c593315Sopenharmony_ci config.padding = n; 28702c593315Sopenharmony_ci break; 28712c593315Sopenharmony_ci } 28722c593315Sopenharmony_ci case 'n': 28732c593315Sopenharmony_ci config.null_out = true; 28742c593315Sopenharmony_ci break; 28752c593315Sopenharmony_ci case 'p': { 28762c593315Sopenharmony_ci auto n = util::parse_uint(optarg); 28772c593315Sopenharmony_ci if (n == -1 || NGHTTP2_MIN_WEIGHT > n || n > NGHTTP2_MAX_WEIGHT) { 28782c593315Sopenharmony_ci std::cerr << "-p: specify the integer in the range [" 28792c593315Sopenharmony_ci << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT 28802c593315Sopenharmony_ci << "], inclusive" << std::endl; 28812c593315Sopenharmony_ci exit(EXIT_FAILURE); 28822c593315Sopenharmony_ci } 28832c593315Sopenharmony_ci config.weight.push_back(n); 28842c593315Sopenharmony_ci break; 28852c593315Sopenharmony_ci } 28862c593315Sopenharmony_ci case 'r': 28872c593315Sopenharmony_ci#ifdef HAVE_JANSSON 28882c593315Sopenharmony_ci config.harfile = optarg; 28892c593315Sopenharmony_ci#else // !HAVE_JANSSON 28902c593315Sopenharmony_ci std::cerr << "[WARNING]: -r, --har option is ignored because\n" 28912c593315Sopenharmony_ci << "the binary was not compiled with libjansson." << std::endl; 28922c593315Sopenharmony_ci#endif // !HAVE_JANSSON 28932c593315Sopenharmony_ci break; 28942c593315Sopenharmony_ci case 'v': 28952c593315Sopenharmony_ci ++config.verbose; 28962c593315Sopenharmony_ci break; 28972c593315Sopenharmony_ci case 't': 28982c593315Sopenharmony_ci config.timeout = util::parse_duration_with_unit(optarg); 28992c593315Sopenharmony_ci if (config.timeout == std::numeric_limits<double>::infinity()) { 29002c593315Sopenharmony_ci std::cerr << "-t: bad timeout value: " << optarg << std::endl; 29012c593315Sopenharmony_ci exit(EXIT_FAILURE); 29022c593315Sopenharmony_ci } 29032c593315Sopenharmony_ci break; 29042c593315Sopenharmony_ci case 'u': 29052c593315Sopenharmony_ci config.upgrade = true; 29062c593315Sopenharmony_ci break; 29072c593315Sopenharmony_ci case 'w': 29082c593315Sopenharmony_ci case 'W': { 29092c593315Sopenharmony_ci auto n = util::parse_uint(optarg); 29102c593315Sopenharmony_ci if (n == -1 || n > 30) { 29112c593315Sopenharmony_ci std::cerr << "-" << static_cast<char>(c) 29122c593315Sopenharmony_ci << ": specify the integer in the range [0, 30], inclusive" 29132c593315Sopenharmony_ci << std::endl; 29142c593315Sopenharmony_ci exit(EXIT_FAILURE); 29152c593315Sopenharmony_ci } 29162c593315Sopenharmony_ci if (c == 'w') { 29172c593315Sopenharmony_ci config.window_bits = n; 29182c593315Sopenharmony_ci } else { 29192c593315Sopenharmony_ci config.connection_window_bits = n; 29202c593315Sopenharmony_ci } 29212c593315Sopenharmony_ci break; 29222c593315Sopenharmony_ci } 29232c593315Sopenharmony_ci case 'H': { 29242c593315Sopenharmony_ci char *header = optarg; 29252c593315Sopenharmony_ci // Skip first possible ':' in the header name 29262c593315Sopenharmony_ci char *value = strchr(optarg + 1, ':'); 29272c593315Sopenharmony_ci if (!value || (header[0] == ':' && header + 1 == value)) { 29282c593315Sopenharmony_ci std::cerr << "-H: invalid header: " << optarg << std::endl; 29292c593315Sopenharmony_ci exit(EXIT_FAILURE); 29302c593315Sopenharmony_ci } 29312c593315Sopenharmony_ci *value = 0; 29322c593315Sopenharmony_ci value++; 29332c593315Sopenharmony_ci while (isspace(*value)) { 29342c593315Sopenharmony_ci value++; 29352c593315Sopenharmony_ci } 29362c593315Sopenharmony_ci if (*value == 0) { 29372c593315Sopenharmony_ci // This could also be a valid case for suppressing a header 29382c593315Sopenharmony_ci // similar to curl 29392c593315Sopenharmony_ci std::cerr << "-H: invalid header - value missing: " << optarg 29402c593315Sopenharmony_ci << std::endl; 29412c593315Sopenharmony_ci exit(EXIT_FAILURE); 29422c593315Sopenharmony_ci } 29432c593315Sopenharmony_ci config.headers.emplace_back(header, value, false); 29442c593315Sopenharmony_ci util::inp_strlower(config.headers.back().name); 29452c593315Sopenharmony_ci break; 29462c593315Sopenharmony_ci } 29472c593315Sopenharmony_ci case 'a': 29482c593315Sopenharmony_ci#ifdef HAVE_LIBXML2 29492c593315Sopenharmony_ci config.get_assets = true; 29502c593315Sopenharmony_ci#else // !HAVE_LIBXML2 29512c593315Sopenharmony_ci std::cerr << "[WARNING]: -a, --get-assets option is ignored because\n" 29522c593315Sopenharmony_ci << "the binary was not compiled with libxml2." << std::endl; 29532c593315Sopenharmony_ci#endif // !HAVE_LIBXML2 29542c593315Sopenharmony_ci break; 29552c593315Sopenharmony_ci case 's': 29562c593315Sopenharmony_ci config.stat = true; 29572c593315Sopenharmony_ci break; 29582c593315Sopenharmony_ci case 'd': 29592c593315Sopenharmony_ci config.datafile = optarg; 29602c593315Sopenharmony_ci break; 29612c593315Sopenharmony_ci case 'm': { 29622c593315Sopenharmony_ci auto n = util::parse_uint(optarg); 29632c593315Sopenharmony_ci if (n == -1) { 29642c593315Sopenharmony_ci std::cerr << "-m: Bad option value: " << optarg << std::endl; 29652c593315Sopenharmony_ci exit(EXIT_FAILURE); 29662c593315Sopenharmony_ci } 29672c593315Sopenharmony_ci config.multiply = n; 29682c593315Sopenharmony_ci break; 29692c593315Sopenharmony_ci } 29702c593315Sopenharmony_ci case 'c': { 29712c593315Sopenharmony_ci auto n = util::parse_uint_with_unit(optarg); 29722c593315Sopenharmony_ci if (n == -1) { 29732c593315Sopenharmony_ci std::cerr << "-c: Bad option value: " << optarg << std::endl; 29742c593315Sopenharmony_ci exit(EXIT_FAILURE); 29752c593315Sopenharmony_ci } 29762c593315Sopenharmony_ci if (n > std::numeric_limits<uint32_t>::max()) { 29772c593315Sopenharmony_ci std::cerr << "-c: Value too large. It should be less than or equal to " 29782c593315Sopenharmony_ci << std::numeric_limits<uint32_t>::max() << std::endl; 29792c593315Sopenharmony_ci exit(EXIT_FAILURE); 29802c593315Sopenharmony_ci } 29812c593315Sopenharmony_ci config.header_table_size = n; 29822c593315Sopenharmony_ci config.min_header_table_size = std::min(config.min_header_table_size, n); 29832c593315Sopenharmony_ci break; 29842c593315Sopenharmony_ci } 29852c593315Sopenharmony_ci case 'y': 29862c593315Sopenharmony_ci config.verify_peer = false; 29872c593315Sopenharmony_ci break; 29882c593315Sopenharmony_ci case '?': 29892c593315Sopenharmony_ci util::show_candidates(argv[optind - 1], long_options); 29902c593315Sopenharmony_ci exit(EXIT_FAILURE); 29912c593315Sopenharmony_ci case 0: 29922c593315Sopenharmony_ci switch (flag) { 29932c593315Sopenharmony_ci case 1: 29942c593315Sopenharmony_ci // cert option 29952c593315Sopenharmony_ci config.certfile = optarg; 29962c593315Sopenharmony_ci break; 29972c593315Sopenharmony_ci case 2: 29982c593315Sopenharmony_ci // key option 29992c593315Sopenharmony_ci config.keyfile = optarg; 30002c593315Sopenharmony_ci break; 30012c593315Sopenharmony_ci case 3: 30022c593315Sopenharmony_ci // color option 30032c593315Sopenharmony_ci color = true; 30042c593315Sopenharmony_ci break; 30052c593315Sopenharmony_ci case 4: 30062c593315Sopenharmony_ci // continuation option 30072c593315Sopenharmony_ci config.continuation = true; 30082c593315Sopenharmony_ci break; 30092c593315Sopenharmony_ci case 5: 30102c593315Sopenharmony_ci // version option 30112c593315Sopenharmony_ci print_version(std::cout); 30122c593315Sopenharmony_ci exit(EXIT_SUCCESS); 30132c593315Sopenharmony_ci case 6: 30142c593315Sopenharmony_ci // no-content-length option 30152c593315Sopenharmony_ci config.no_content_length = true; 30162c593315Sopenharmony_ci break; 30172c593315Sopenharmony_ci case 7: 30182c593315Sopenharmony_ci // no-dep option 30192c593315Sopenharmony_ci config.no_dep = true; 30202c593315Sopenharmony_ci break; 30212c593315Sopenharmony_ci case 9: { 30222c593315Sopenharmony_ci // trailer option 30232c593315Sopenharmony_ci auto header = optarg; 30242c593315Sopenharmony_ci auto value = strchr(optarg, ':'); 30252c593315Sopenharmony_ci if (!value) { 30262c593315Sopenharmony_ci std::cerr << "--trailer: invalid header: " << optarg << std::endl; 30272c593315Sopenharmony_ci exit(EXIT_FAILURE); 30282c593315Sopenharmony_ci } 30292c593315Sopenharmony_ci *value = 0; 30302c593315Sopenharmony_ci value++; 30312c593315Sopenharmony_ci while (isspace(*value)) { 30322c593315Sopenharmony_ci value++; 30332c593315Sopenharmony_ci } 30342c593315Sopenharmony_ci if (*value == 0) { 30352c593315Sopenharmony_ci // This could also be a valid case for suppressing a header 30362c593315Sopenharmony_ci // similar to curl 30372c593315Sopenharmony_ci std::cerr << "--trailer: invalid header - value missing: " << optarg 30382c593315Sopenharmony_ci << std::endl; 30392c593315Sopenharmony_ci exit(EXIT_FAILURE); 30402c593315Sopenharmony_ci } 30412c593315Sopenharmony_ci config.trailer.emplace_back(header, value, false); 30422c593315Sopenharmony_ci util::inp_strlower(config.trailer.back().name); 30432c593315Sopenharmony_ci break; 30442c593315Sopenharmony_ci } 30452c593315Sopenharmony_ci case 10: 30462c593315Sopenharmony_ci // hexdump option 30472c593315Sopenharmony_ci config.hexdump = true; 30482c593315Sopenharmony_ci break; 30492c593315Sopenharmony_ci case 11: 30502c593315Sopenharmony_ci // no-push option 30512c593315Sopenharmony_ci config.no_push = true; 30522c593315Sopenharmony_ci break; 30532c593315Sopenharmony_ci case 12: { 30542c593315Sopenharmony_ci // max-concurrent-streams option 30552c593315Sopenharmony_ci auto n = util::parse_uint(optarg); 30562c593315Sopenharmony_ci if (n == -1) { 30572c593315Sopenharmony_ci std::cerr << "--max-concurrent-streams: Bad option value: " << optarg 30582c593315Sopenharmony_ci << std::endl; 30592c593315Sopenharmony_ci exit(EXIT_FAILURE); 30602c593315Sopenharmony_ci } 30612c593315Sopenharmony_ci config.max_concurrent_streams = n; 30622c593315Sopenharmony_ci break; 30632c593315Sopenharmony_ci } 30642c593315Sopenharmony_ci case 13: 30652c593315Sopenharmony_ci // expect-continue option 30662c593315Sopenharmony_ci config.expect_continue = true; 30672c593315Sopenharmony_ci break; 30682c593315Sopenharmony_ci case 14: { 30692c593315Sopenharmony_ci // encoder-header-table-size option 30702c593315Sopenharmony_ci auto n = util::parse_uint_with_unit(optarg); 30712c593315Sopenharmony_ci if (n == -1) { 30722c593315Sopenharmony_ci std::cerr << "--encoder-header-table-size: Bad option value: " 30732c593315Sopenharmony_ci << optarg << std::endl; 30742c593315Sopenharmony_ci exit(EXIT_FAILURE); 30752c593315Sopenharmony_ci } 30762c593315Sopenharmony_ci if (n > std::numeric_limits<uint32_t>::max()) { 30772c593315Sopenharmony_ci std::cerr << "--encoder-header-table-size: Value too large. It " 30782c593315Sopenharmony_ci "should be less than or equal to " 30792c593315Sopenharmony_ci << std::numeric_limits<uint32_t>::max() << std::endl; 30802c593315Sopenharmony_ci exit(EXIT_FAILURE); 30812c593315Sopenharmony_ci } 30822c593315Sopenharmony_ci config.encoder_header_table_size = n; 30832c593315Sopenharmony_ci break; 30842c593315Sopenharmony_ci } 30852c593315Sopenharmony_ci case 15: 30862c593315Sopenharmony_ci // ktls option 30872c593315Sopenharmony_ci config.ktls = true; 30882c593315Sopenharmony_ci break; 30892c593315Sopenharmony_ci case 16: 30902c593315Sopenharmony_ci // no-rfc7540-pri option 30912c593315Sopenharmony_ci config.no_rfc7540_pri = true; 30922c593315Sopenharmony_ci break; 30932c593315Sopenharmony_ci } 30942c593315Sopenharmony_ci break; 30952c593315Sopenharmony_ci default: 30962c593315Sopenharmony_ci break; 30972c593315Sopenharmony_ci } 30982c593315Sopenharmony_ci } 30992c593315Sopenharmony_ci 31002c593315Sopenharmony_ci int32_t weight_to_fill; 31012c593315Sopenharmony_ci if (config.weight.empty()) { 31022c593315Sopenharmony_ci weight_to_fill = NGHTTP2_DEFAULT_WEIGHT; 31032c593315Sopenharmony_ci } else { 31042c593315Sopenharmony_ci weight_to_fill = config.weight.back(); 31052c593315Sopenharmony_ci } 31062c593315Sopenharmony_ci config.weight.insert(std::end(config.weight), argc - optind, weight_to_fill); 31072c593315Sopenharmony_ci 31082c593315Sopenharmony_ci // Find scheme overridden by extra header fields. 31092c593315Sopenharmony_ci auto scheme_it = 31102c593315Sopenharmony_ci std::find_if(std::begin(config.headers), std::end(config.headers), 31112c593315Sopenharmony_ci [](const Header &nv) { return nv.name == ":scheme"; }); 31122c593315Sopenharmony_ci if (scheme_it != std::end(config.headers)) { 31132c593315Sopenharmony_ci config.scheme_override = (*scheme_it).value; 31142c593315Sopenharmony_ci } 31152c593315Sopenharmony_ci 31162c593315Sopenharmony_ci // Find host and port overridden by extra header fields. 31172c593315Sopenharmony_ci auto authority_it = 31182c593315Sopenharmony_ci std::find_if(std::begin(config.headers), std::end(config.headers), 31192c593315Sopenharmony_ci [](const Header &nv) { return nv.name == ":authority"; }); 31202c593315Sopenharmony_ci if (authority_it == std::end(config.headers)) { 31212c593315Sopenharmony_ci authority_it = 31222c593315Sopenharmony_ci std::find_if(std::begin(config.headers), std::end(config.headers), 31232c593315Sopenharmony_ci [](const Header &nv) { return nv.name == "host"; }); 31242c593315Sopenharmony_ci } 31252c593315Sopenharmony_ci 31262c593315Sopenharmony_ci if (authority_it != std::end(config.headers)) { 31272c593315Sopenharmony_ci // authority_it may looks like "host:port". 31282c593315Sopenharmony_ci auto uri = "https://" + (*authority_it).value; 31292c593315Sopenharmony_ci http_parser_url u{}; 31302c593315Sopenharmony_ci if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { 31312c593315Sopenharmony_ci std::cerr << "[ERROR] Could not parse authority in " 31322c593315Sopenharmony_ci << (*authority_it).name << ": " << (*authority_it).value 31332c593315Sopenharmony_ci << std::endl; 31342c593315Sopenharmony_ci exit(EXIT_FAILURE); 31352c593315Sopenharmony_ci } 31362c593315Sopenharmony_ci 31372c593315Sopenharmony_ci config.host_override = util::get_uri_field(uri.c_str(), u, UF_HOST).str(); 31382c593315Sopenharmony_ci if (util::has_uri_field(u, UF_PORT)) { 31392c593315Sopenharmony_ci config.port_override = u.port; 31402c593315Sopenharmony_ci } 31412c593315Sopenharmony_ci } 31422c593315Sopenharmony_ci 31432c593315Sopenharmony_ci set_color_output(color || isatty(fileno(stdout))); 31442c593315Sopenharmony_ci 31452c593315Sopenharmony_ci nghttp2_option_set_peer_max_concurrent_streams( 31462c593315Sopenharmony_ci config.http2_option, config.peer_max_concurrent_streams); 31472c593315Sopenharmony_ci 31482c593315Sopenharmony_ci if (config.encoder_header_table_size != -1) { 31492c593315Sopenharmony_ci nghttp2_option_set_max_deflate_dynamic_table_size( 31502c593315Sopenharmony_ci config.http2_option, config.encoder_header_table_size); 31512c593315Sopenharmony_ci } 31522c593315Sopenharmony_ci 31532c593315Sopenharmony_ci struct sigaction act {}; 31542c593315Sopenharmony_ci act.sa_handler = SIG_IGN; 31552c593315Sopenharmony_ci sigaction(SIGPIPE, &act, nullptr); 31562c593315Sopenharmony_ci reset_timer(); 31572c593315Sopenharmony_ci return run(argv + optind, argc - optind); 31582c593315Sopenharmony_ci} 31592c593315Sopenharmony_ci 31602c593315Sopenharmony_ci} // namespace nghttp2 31612c593315Sopenharmony_ci 31622c593315Sopenharmony_ciint main(int argc, char **argv) { 31632c593315Sopenharmony_ci return nghttp2::run_app(nghttp2::main, argc, argv); 31642c593315Sopenharmony_ci} 3165