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 &ltiming = 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