1#include "httplib.h"
    2namespace httplib {
    3
    4
    5/*
    6 * Implementation that will be part of the .cc file if split into .h + .cc.
    7 */
    8
    9namespace detail {
   10
   11bool is_hex(char c, int &v) {
   12  if (isdigit(c)) {
   13    v = c - '0';
   14    return true;
   15  } else if ('A' <= c && c <= 'F') {
   16    v = c - 'A' + 10;
   17    return true;
   18  } else if ('a' <= c && c <= 'f') {
   19    v = c - 'a' + 10;
   20    return true;
   21  }
   22  return false;
   23}
   24
   25bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
   26                          int &val) {
   27  if (i >= s.size()) { return false; }
   28
   29  val = 0;
   30  for (; cnt; i++, cnt--) {
   31    if (!s[i]) { return false; }
   32    auto v = 0;
   33    if (is_hex(s[i], v)) {
   34      val = val * 16 + v;
   35    } else {
   36      return false;
   37    }
   38  }
   39  return true;
   40}
   41
   42std::string from_i_to_hex(size_t n) {
   43  static const auto charset = "0123456789abcdef";
   44  std::string ret;
   45  do {
   46    ret = charset[n & 15] + ret;
   47    n >>= 4;
   48  } while (n > 0);
   49  return ret;
   50}
   51
   52std::string compute_etag(const FileStat &fs) {
   53  if (!fs.is_file()) { return std::string(); }
   54
   55  // If mtime cannot be determined (negative value indicates an error
   56  // or sentinel), do not generate an ETag. Returning a neutral / fixed
   57  // value like 0 could collide with a real file that legitimately has
   58  // mtime == 0 (epoch) and lead to misleading validators.
   59  auto mtime_raw = fs.mtime();
   60  if (mtime_raw < 0) { return std::string(); }
   61
   62  auto mtime = static_cast<size_t>(mtime_raw);
   63  auto size = fs.size();
   64
   65  return std::string("W/\"") + from_i_to_hex(mtime) + "-" +
   66         from_i_to_hex(size) + "\"";
   67}
   68
   69// Format time_t as HTTP-date (RFC 9110 Section 5.6.7): "Sun, 06 Nov 1994
   70// 08:49:37 GMT" This implementation is defensive: it validates `mtime`, checks
   71// return values from `gmtime_r`/`gmtime_s`, and ensures `strftime` succeeds.
   72std::string file_mtime_to_http_date(time_t mtime) {
   73  if (mtime < 0) { return std::string(); }
   74
   75  struct tm tm_buf;
   76#ifdef _WIN32
   77  if (gmtime_s(&tm_buf, &mtime) != 0) { return std::string(); }
   78#else
   79  if (gmtime_r(&mtime, &tm_buf) == nullptr) { return std::string(); }
   80#endif
   81  char buf[64];
   82  if (strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", &tm_buf) == 0) {
   83    return std::string();
   84  }
   85
   86  return std::string(buf);
   87}
   88
   89// Parse HTTP-date (RFC 9110 Section 5.6.7) to time_t. Returns -1 on failure.
   90time_t parse_http_date(const std::string &date_str) {
   91  struct tm tm_buf;
   92
   93  // Create a classic locale object once for all parsing attempts
   94  const std::locale classic_locale = std::locale::classic();
   95
   96  // Try to parse using std::get_time (C++11, cross-platform)
   97  auto try_parse = [&](const char *fmt) -> bool {
   98    std::istringstream ss(date_str);
   99    ss.imbue(classic_locale);
  100
  101    memset(&tm_buf, 0, sizeof(tm_buf));
  102    ss >> std::get_time(&tm_buf, fmt);
  103
  104    return !ss.fail();
  105  };
  106
  107  // RFC 9110 preferred format (HTTP-date): "Sun, 06 Nov 1994 08:49:37 GMT"
  108  if (!try_parse("%a, %d %b %Y %H:%M:%S")) {
  109    // RFC 850 format: "Sunday, 06-Nov-94 08:49:37 GMT"
  110    if (!try_parse("%A, %d-%b-%y %H:%M:%S")) {
  111      // asctime format: "Sun Nov  6 08:49:37 1994"
  112      if (!try_parse("%a %b %d %H:%M:%S %Y")) {
  113        return static_cast<time_t>(-1);
  114      }
  115    }
  116  }
  117
  118#ifdef _WIN32
  119  return _mkgmtime(&tm_buf);
  120#elif defined _AIX
  121  return mktime(&tm_buf);
  122#else
  123  return timegm(&tm_buf);
  124#endif
  125}
  126
  127bool is_weak_etag(const std::string &s) {
  128  // Check if the string is a weak ETag (starts with 'W/"')
  129  return s.size() > 3 && s[0] == 'W' && s[1] == '/' && s[2] == '"';
  130}
  131
  132bool is_strong_etag(const std::string &s) {
  133  // Check if the string is a strong ETag (starts and ends with '"', at least 2
  134  // chars)
  135  return s.size() >= 2 && s[0] == '"' && s.back() == '"';
  136}
  137
  138size_t to_utf8(int code, char *buff) {
  139  if (code < 0x0080) {
  140    buff[0] = static_cast<char>(code & 0x7F);
  141    return 1;
  142  } else if (code < 0x0800) {
  143    buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
  144    buff[1] = static_cast<char>(0x80 | (code & 0x3F));
  145    return 2;
  146  } else if (code < 0xD800) {
  147    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
  148    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
  149    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
  150    return 3;
  151  } else if (code < 0xE000) { // D800 - DFFF is invalid...
  152    return 0;
  153  } else if (code < 0x10000) {
  154    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
  155    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
  156    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
  157    return 3;
  158  } else if (code < 0x110000) {
  159    buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
  160    buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
  161    buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
  162    buff[3] = static_cast<char>(0x80 | (code & 0x3F));
  163    return 4;
  164  }
  165
  166  // NOTREACHED
  167  return 0;
  168}
  169
  170// NOTE: This code came up with the following stackoverflow post:
  171// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
  172std::string base64_encode(const std::string &in) {
  173  static const auto lookup =
  174      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  175
  176  std::string out;
  177  out.reserve(in.size());
  178
  179  auto val = 0;
  180  auto valb = -6;
  181
  182  for (auto c : in) {
  183    val = (val << 8) + static_cast<uint8_t>(c);
  184    valb += 8;
  185    while (valb >= 0) {
  186      out.push_back(lookup[(val >> valb) & 0x3F]);
  187      valb -= 6;
  188    }
  189  }
  190
  191  if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
  192
  193  while (out.size() % 4) {
  194    out.push_back('=');
  195  }
  196
  197  return out;
  198}
  199
  200bool is_valid_path(const std::string &path) {
  201  size_t level = 0;
  202  size_t i = 0;
  203
  204  // Skip slash
  205  while (i < path.size() && path[i] == '/') {
  206    i++;
  207  }
  208
  209  while (i < path.size()) {
  210    // Read component
  211    auto beg = i;
  212    while (i < path.size() && path[i] != '/') {
  213      if (path[i] == '\0') {
  214        return false;
  215      } else if (path[i] == '\\') {
  216        return false;
  217      }
  218      i++;
  219    }
  220
  221    auto len = i - beg;
  222    assert(len > 0);
  223
  224    if (!path.compare(beg, len, ".")) {
  225      ;
  226    } else if (!path.compare(beg, len, "..")) {
  227      if (level == 0) { return false; }
  228      level--;
  229    } else {
  230      level++;
  231    }
  232
  233    // Skip slash
  234    while (i < path.size() && path[i] == '/') {
  235      i++;
  236    }
  237  }
  238
  239  return true;
  240}
  241
  242FileStat::FileStat(const std::string &path) {
  243#if defined(_WIN32)
  244  auto wpath = u8string_to_wstring(path.c_str());
  245  ret_ = _wstat(wpath.c_str(), &st_);
  246#else
  247  ret_ = stat(path.c_str(), &st_);
  248#endif
  249}
  250bool FileStat::is_file() const {
  251  return ret_ >= 0 && S_ISREG(st_.st_mode);
  252}
  253bool FileStat::is_dir() const {
  254  return ret_ >= 0 && S_ISDIR(st_.st_mode);
  255}
  256
  257time_t FileStat::mtime() const {
  258  return ret_ >= 0 ? static_cast<time_t>(st_.st_mtime)
  259                   : static_cast<time_t>(-1);
  260}
  261
  262size_t FileStat::size() const {
  263  return ret_ >= 0 ? static_cast<size_t>(st_.st_size) : 0;
  264}
  265
  266std::string encode_path(const std::string &s) {
  267  std::string result;
  268  result.reserve(s.size());
  269
  270  for (size_t i = 0; s[i]; i++) {
  271    switch (s[i]) {
  272    case ' ': result += "%20"; break;
  273    case '+': result += "%2B"; break;
  274    case '\r': result += "%0D"; break;
  275    case '\n': result += "%0A"; break;
  276    case '\'': result += "%27"; break;
  277    case ',': result += "%2C"; break;
  278    // case ':': result += "%3A"; break; // ok? probably...
  279    case ';': result += "%3B"; break;
  280    default:
  281      auto c = static_cast<uint8_t>(s[i]);
  282      if (c >= 0x80) {
  283        result += '%';
  284        char hex[4];
  285        auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
  286        assert(len == 2);
  287        result.append(hex, static_cast<size_t>(len));
  288      } else {
  289        result += s[i];
  290      }
  291      break;
  292    }
  293  }
  294
  295  return result;
  296}
  297
  298std::string file_extension(const std::string &path) {
  299  std::smatch m;
  300  thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
  301  if (std::regex_search(path, m, re)) { return m[1].str(); }
  302  return std::string();
  303}
  304
  305bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
  306
  307template <typename T>
  308bool parse_header(const char *beg, const char *end, T fn);
  309
  310template <typename T>
  311bool parse_header(const char *beg, const char *end, T fn) {
  312  // Skip trailing spaces and tabs.
  313  while (beg < end && is_space_or_tab(end[-1])) {
  314    end--;
  315  }
  316
  317  auto p = beg;
  318  while (p < end && *p != ':') {
  319    p++;
  320  }
  321
  322  auto name = std::string(beg, p);
  323  if (!detail::fields::is_field_name(name)) { return false; }
  324
  325  if (p == end) { return false; }
  326
  327  auto key_end = p;
  328
  329  if (*p++ != ':') { return false; }
  330
  331  while (p < end && is_space_or_tab(*p)) {
  332    p++;
  333  }
  334
  335  if (p <= end) {
  336    auto key_len = key_end - beg;
  337    if (!key_len) { return false; }
  338
  339    auto key = std::string(beg, key_end);
  340    auto val = std::string(p, end);
  341
  342    if (!detail::fields::is_field_value(val)) { return false; }
  343
  344    if (case_ignore::equal(key, "Location") ||
  345        case_ignore::equal(key, "Referer")) {
  346      fn(key, val);
  347    } else {
  348      fn(key, decode_path_component(val));
  349    }
  350
  351    return true;
  352  }
  353
  354  return false;
  355}
  356
  357bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
  358                           const Headers &src_headers) {
  359  // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked
  360  // transfer coding is complete when a chunk with a chunk-size of zero is
  361  // received, possibly followed by a trailer section, and finally terminated by
  362  // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
  363  //
  364  // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
  365  // doesn't care for the existence of the final CRLF. In other words, it seems
  366  // to be ok whether the final CRLF exists or not in the chunked data.
  367  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
  368  //
  369  // According to the reference code in RFC 9112, cpp-httplib now allows
  370  // chunked transfer coding data without the final CRLF.
  371
  372  // RFC 7230 Section 4.1.2 - Headers prohibited in trailers
  373  thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {
  374      "transfer-encoding",
  375      "content-length",
  376      "host",
  377      "authorization",
  378      "www-authenticate",
  379      "proxy-authenticate",
  380      "proxy-authorization",
  381      "cookie",
  382      "set-cookie",
  383      "cache-control",
  384      "expect",
  385      "max-forwards",
  386      "pragma",
  387      "range",
  388      "te",
  389      "age",
  390      "expires",
  391      "date",
  392      "location",
  393      "retry-after",
  394      "vary",
  395      "warning",
  396      "content-encoding",
  397      "content-type",
  398      "content-range",
  399      "trailer"};
  400
  401  case_ignore::unordered_set<std::string> declared_trailers;
  402  auto trailer_header = get_header_value(src_headers, "Trailer", "", 0);
  403  if (trailer_header && std::strlen(trailer_header)) {
  404    auto len = std::strlen(trailer_header);
  405    split(trailer_header, trailer_header + len, ',',
  406          [&](const char *b, const char *e) {
  407            const char *kbeg = b;
  408            const char *kend = e;
  409            while (kbeg < kend && (*kbeg == ' ' || *kbeg == '\t')) {
  410              ++kbeg;
  411            }
  412            while (kend > kbeg && (kend[-1] == ' ' || kend[-1] == '\t')) {
  413              --kend;
  414            }
  415            std::string key(kbeg, static_cast<size_t>(kend - kbeg));
  416            if (!key.empty() &&
  417                prohibited_trailers.find(key) == prohibited_trailers.end()) {
  418              declared_trailers.insert(key);
  419            }
  420          });
  421  }
  422
  423  size_t trailer_header_count = 0;
  424  while (strcmp(line_reader.ptr(), "\r\n") != 0) {
  425    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
  426    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
  427
  428    constexpr auto line_terminator_len = 2;
  429    auto line_beg = line_reader.ptr();
  430    auto line_end =
  431        line_reader.ptr() + line_reader.size() - line_terminator_len;
  432
  433    if (!parse_header(line_beg, line_end,
  434                      [&](const std::string &key, const std::string &val) {
  435                        if (declared_trailers.find(key) !=
  436                            declared_trailers.end()) {
  437                          dest.emplace(key, val);
  438                          trailer_header_count++;
  439                        }
  440                      })) {
  441      return false;
  442    }
  443
  444    if (!line_reader.getline()) { return false; }
  445  }
  446
  447  return true;
  448}
  449
  450std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
  451                                      size_t right) {
  452  while (b + left < e && is_space_or_tab(b[left])) {
  453    left++;
  454  }
  455  while (right > 0 && is_space_or_tab(b[right - 1])) {
  456    right--;
  457  }
  458  return std::make_pair(left, right);
  459}
  460
  461std::string trim_copy(const std::string &s) {
  462  auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
  463  return s.substr(r.first, r.second - r.first);
  464}
  465
  466std::string trim_double_quotes_copy(const std::string &s) {
  467  if (s.length() >= 2 && s.front() == '"' && s.back() == '"') {
  468    return s.substr(1, s.size() - 2);
  469  }
  470  return s;
  471}
  472
  473void
  474divide(const char *data, std::size_t size, char d,
  475       std::function<void(const char *, std::size_t, const char *, std::size_t)>
  476           fn) {
  477  const auto it = std::find(data, data + size, d);
  478  const auto found = static_cast<std::size_t>(it != data + size);
  479  const auto lhs_data = data;
  480  const auto lhs_size = static_cast<std::size_t>(it - data);
  481  const auto rhs_data = it + found;
  482  const auto rhs_size = size - lhs_size - found;
  483
  484  fn(lhs_data, lhs_size, rhs_data, rhs_size);
  485}
  486
  487void
  488divide(const std::string &str, char d,
  489       std::function<void(const char *, std::size_t, const char *, std::size_t)>
  490           fn) {
  491  divide(str.data(), str.size(), d, std::move(fn));
  492}
  493
  494void split(const char *b, const char *e, char d,
  495                  std::function<void(const char *, const char *)> fn) {
  496  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
  497}
  498
  499void split(const char *b, const char *e, char d, size_t m,
  500                  std::function<void(const char *, const char *)> fn) {
  501  size_t i = 0;
  502  size_t beg = 0;
  503  size_t count = 1;
  504
  505  while (e ? (b + i < e) : (b[i] != '\0')) {
  506    if (b[i] == d && count < m) {
  507      auto r = trim(b, e, beg, i);
  508      if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
  509      beg = i + 1;
  510      count++;
  511    }
  512    i++;
  513  }
  514
  515  if (i) {
  516    auto r = trim(b, e, beg, i);
  517    if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
  518  }
  519}
  520
  521bool split_find(const char *b, const char *e, char d, size_t m,
  522                       std::function<bool(const char *, const char *)> fn) {
  523  size_t i = 0;
  524  size_t beg = 0;
  525  size_t count = 1;
  526
  527  while (e ? (b + i < e) : (b[i] != '\0')) {
  528    if (b[i] == d && count < m) {
  529      auto r = trim(b, e, beg, i);
  530      if (r.first < r.second) {
  531        auto found = fn(&b[r.first], &b[r.second]);
  532        if (found) { return true; }
  533      }
  534      beg = i + 1;
  535      count++;
  536    }
  537    i++;
  538  }
  539
  540  if (i) {
  541    auto r = trim(b, e, beg, i);
  542    if (r.first < r.second) {
  543      auto found = fn(&b[r.first], &b[r.second]);
  544      if (found) { return true; }
  545    }
  546  }
  547
  548  return false;
  549}
  550
  551bool split_find(const char *b, const char *e, char d,
  552                       std::function<bool(const char *, const char *)> fn) {
  553  return split_find(b, e, d, (std::numeric_limits<size_t>::max)(),
  554                    std::move(fn));
  555}
  556
  557stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
  558                                              size_t fixed_buffer_size)
  559    : strm_(strm), fixed_buffer_(fixed_buffer),
  560      fixed_buffer_size_(fixed_buffer_size) {}
  561
  562const char *stream_line_reader::ptr() const {
  563  if (growable_buffer_.empty()) {
  564    return fixed_buffer_;
  565  } else {
  566    return growable_buffer_.data();
  567  }
  568}
  569
  570size_t stream_line_reader::size() const {
  571  if (growable_buffer_.empty()) {
  572    return fixed_buffer_used_size_;
  573  } else {
  574    return growable_buffer_.size();
  575  }
  576}
  577
  578bool stream_line_reader::end_with_crlf() const {
  579  auto end = ptr() + size();
  580  return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
  581}
  582
  583bool stream_line_reader::getline() {
  584  fixed_buffer_used_size_ = 0;
  585  growable_buffer_.clear();
  586
  587#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
  588  char prev_byte = 0;
  589#endif
  590
  591  for (size_t i = 0;; i++) {
  592    if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH) {
  593      // Treat exceptionally long lines as an error to
  594      // prevent infinite loops/memory exhaustion
  595      return false;
  596    }
  597    char byte;
  598    auto n = strm_.read(&byte, 1);
  599
  600    if (n < 0) {
  601      return false;
  602    } else if (n == 0) {
  603      if (i == 0) {
  604        return false;
  605      } else {
  606        break;
  607      }
  608    }
  609
  610    append(byte);
  611
  612#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
  613    if (byte == '\n') { break; }
  614#else
  615    if (prev_byte == '\r' && byte == '\n') { break; }
  616    prev_byte = byte;
  617#endif
  618  }
  619
  620  return true;
  621}
  622
  623void stream_line_reader::append(char c) {
  624  if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
  625    fixed_buffer_[fixed_buffer_used_size_++] = c;
  626    fixed_buffer_[fixed_buffer_used_size_] = '\0';
  627  } else {
  628    if (growable_buffer_.empty()) {
  629      assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
  630      growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
  631    }
  632    growable_buffer_ += c;
  633  }
  634}
  635
  636mmap::mmap(const char *path) { open(path); }
  637
  638mmap::~mmap() { close(); }
  639
  640bool mmap::open(const char *path) {
  641  close();
  642
  643#if defined(_WIN32)
  644  auto wpath = u8string_to_wstring(path);
  645  if (wpath.empty()) { return false; }
  646
  647  hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
  648                         OPEN_EXISTING, NULL);
  649
  650  if (hFile_ == INVALID_HANDLE_VALUE) { return false; }
  651
  652  LARGE_INTEGER size{};
  653  if (!::GetFileSizeEx(hFile_, &size)) { return false; }
  654  // If the following line doesn't compile due to QuadPart, update Windows SDK.
  655  // See:
  656  // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
  657  if (static_cast<ULONGLONG>(size.QuadPart) >
  658      (std::numeric_limits<decltype(size_)>::max)()) {
  659    // `size_t` might be 32-bits, on 32-bits Windows.
  660    return false;
  661  }
  662  size_ = static_cast<size_t>(size.QuadPart);
  663
  664  hMapping_ =
  665      ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
  666
  667  // Special treatment for an empty file...
  668  if (hMapping_ == NULL && size_ == 0) {
  669    close();
  670    is_open_empty_file = true;
  671    return true;
  672  }
  673
  674  if (hMapping_ == NULL) {
  675    close();
  676    return false;
  677  }
  678
  679  addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
  680
  681  if (addr_ == nullptr) {
  682    close();
  683    return false;
  684  }
  685#else
  686  fd_ = ::open(path, O_RDONLY);
  687  if (fd_ == -1) { return false; }
  688
  689  struct stat sb;
  690  if (fstat(fd_, &sb) == -1) {
  691    close();
  692    return false;
  693  }
  694  size_ = static_cast<size_t>(sb.st_size);
  695
  696  addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
  697
  698  // Special treatment for an empty file...
  699  if (addr_ == MAP_FAILED && size_ == 0) {
  700    close();
  701    is_open_empty_file = true;
  702    return false;
  703  }
  704#endif
  705
  706  return true;
  707}
  708
  709bool mmap::is_open() const {
  710  return is_open_empty_file ? true : addr_ != nullptr;
  711}
  712
  713size_t mmap::size() const { return size_; }
  714
  715const char *mmap::data() const {
  716  return is_open_empty_file ? "" : static_cast<const char *>(addr_);
  717}
  718
  719void mmap::close() {
  720#if defined(_WIN32)
  721  if (addr_) {
  722    ::UnmapViewOfFile(addr_);
  723    addr_ = nullptr;
  724  }
  725
  726  if (hMapping_) {
  727    ::CloseHandle(hMapping_);
  728    hMapping_ = NULL;
  729  }
  730
  731  if (hFile_ != INVALID_HANDLE_VALUE) {
  732    ::CloseHandle(hFile_);
  733    hFile_ = INVALID_HANDLE_VALUE;
  734  }
  735
  736  is_open_empty_file = false;
  737#else
  738  if (addr_ != nullptr) {
  739    munmap(addr_, size_);
  740    addr_ = nullptr;
  741  }
  742
  743  if (fd_ != -1) {
  744    ::close(fd_);
  745    fd_ = -1;
  746  }
  747#endif
  748  size_ = 0;
  749}
  750int close_socket(socket_t sock) {
  751#ifdef _WIN32
  752  return closesocket(sock);
  753#else
  754  return close(sock);
  755#endif
  756}
  757
  758template <typename T> inline ssize_t handle_EINTR(T fn) {
  759  ssize_t res = 0;
  760  while (true) {
  761    res = fn();
  762    if (res < 0 && errno == EINTR) {
  763      std::this_thread::sleep_for(std::chrono::microseconds{1});
  764      continue;
  765    }
  766    break;
  767  }
  768  return res;
  769}
  770
  771ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
  772  return handle_EINTR([&]() {
  773    return recv(sock,
  774#ifdef _WIN32
  775                static_cast<char *>(ptr), static_cast<int>(size),
  776#else
  777                ptr, size,
  778#endif
  779                flags);
  780  });
  781}
  782
  783ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
  784                           int flags) {
  785  return handle_EINTR([&]() {
  786    return send(sock,
  787#ifdef _WIN32
  788                static_cast<const char *>(ptr), static_cast<int>(size),
  789#else
  790                ptr, size,
  791#endif
  792                flags);
  793  });
  794}
  795
  796int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
  797#ifdef _WIN32
  798  return ::WSAPoll(fds, nfds, timeout);
  799#else
  800  return ::poll(fds, nfds, timeout);
  801#endif
  802}
  803
  804template <bool Read>
  805ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
  806#ifdef __APPLE__
  807  if (sock >= FD_SETSIZE) { return -1; }
  808
  809  fd_set fds, *rfds, *wfds;
  810  FD_ZERO(&fds);
  811  FD_SET(sock, &fds);
  812  rfds = (Read ? &fds : nullptr);
  813  wfds = (Read ? nullptr : &fds);
  814
  815  timeval tv;
  816  tv.tv_sec = static_cast<long>(sec);
  817  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
  818
  819  return handle_EINTR([&]() {
  820    return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);
  821  });
  822#else
  823  struct pollfd pfd;
  824  pfd.fd = sock;
  825  pfd.events = (Read ? POLLIN : POLLOUT);
  826
  827  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
  828
  829  return handle_EINTR([&]() { return poll_wrapper(&pfd, 1, timeout); });
  830#endif
  831}
  832
  833ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
  834  return select_impl<true>(sock, sec, usec);
  835}
  836
  837ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
  838  return select_impl<false>(sock, sec, usec);
  839}
  840
  841Error wait_until_socket_is_ready(socket_t sock, time_t sec,
  842                                        time_t usec) {
  843#ifdef __APPLE__
  844  if (sock >= FD_SETSIZE) { return Error::Connection; }
  845
  846  fd_set fdsr, fdsw;
  847  FD_ZERO(&fdsr);
  848  FD_ZERO(&fdsw);
  849  FD_SET(sock, &fdsr);
  850  FD_SET(sock, &fdsw);
  851
  852  timeval tv;
  853  tv.tv_sec = static_cast<long>(sec);
  854  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
  855
  856  auto ret = handle_EINTR([&]() {
  857    return select(static_cast<int>(sock + 1), &fdsr, &fdsw, nullptr, &tv);
  858  });
  859
  860  if (ret == 0) { return Error::ConnectionTimeout; }
  861
  862  if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
  863    auto error = 0;
  864    socklen_t len = sizeof(error);
  865    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
  866                          reinterpret_cast<char *>(&error), &len);
  867    auto successful = res >= 0 && !error;
  868    return successful ? Error::Success : Error::Connection;
  869  }
  870
  871  return Error::Connection;
  872#else
  873  struct pollfd pfd_read;
  874  pfd_read.fd = sock;
  875  pfd_read.events = POLLIN | POLLOUT;
  876
  877  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
  878
  879  auto poll_res =
  880      handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });
  881
  882  if (poll_res == 0) { return Error::ConnectionTimeout; }
  883
  884  if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
  885    auto error = 0;
  886    socklen_t len = sizeof(error);
  887    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
  888                          reinterpret_cast<char *>(&error), &len);
  889    auto successful = res >= 0 && !error;
  890    return successful ? Error::Success : Error::Connection;
  891  }
  892
  893  return Error::Connection;
  894#endif
  895}
  896
  897bool is_socket_alive(socket_t sock) {
  898  const auto val = detail::select_read(sock, 0, 0);
  899  if (val == 0) {
  900    return true;
  901  } else if (val < 0 && errno == EBADF) {
  902    return false;
  903  }
  904  char buf[1];
  905  return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
  906}
  907
  908class SocketStream final : public Stream {
  909public:
  910  SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
  911               time_t write_timeout_sec, time_t write_timeout_usec,
  912               time_t max_timeout_msec = 0,
  913               std::chrono::time_point<std::chrono::steady_clock> start_time =
  914                   (std::chrono::steady_clock::time_point::min)());
  915  ~SocketStream() override;
  916
  917  bool is_readable() const override;
  918  bool wait_readable() const override;
  919  bool wait_writable() const override;
  920  ssize_t read(char *ptr, size_t size) override;
  921  ssize_t write(const char *ptr, size_t size) override;
  922  void get_remote_ip_and_port(std::string &ip, int &port) const override;
  923  void get_local_ip_and_port(std::string &ip, int &port) const override;
  924  socket_t socket() const override;
  925  time_t duration() const override;
  926
  927private:
  928  socket_t sock_;
  929  time_t read_timeout_sec_;
  930  time_t read_timeout_usec_;
  931  time_t write_timeout_sec_;
  932  time_t write_timeout_usec_;
  933  time_t max_timeout_msec_;
  934  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
  935
  936  std::vector<char> read_buff_;
  937  size_t read_buff_off_ = 0;
  938  size_t read_buff_content_size_ = 0;
  939
  940  static const size_t read_buff_size_ = 1024l * 4;
  941};
  942
  943#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  944class SSLSocketStream final : public Stream {
  945public:
  946  SSLSocketStream(
  947      socket_t sock, SSL *ssl, time_t read_timeout_sec,
  948      time_t read_timeout_usec, time_t write_timeout_sec,
  949      time_t write_timeout_usec, time_t max_timeout_msec = 0,
  950      std::chrono::time_point<std::chrono::steady_clock> start_time =
  951          (std::chrono::steady_clock::time_point::min)());
  952  ~SSLSocketStream() override;
  953
  954  bool is_readable() const override;
  955  bool wait_readable() const override;
  956  bool wait_writable() const override;
  957  ssize_t read(char *ptr, size_t size) override;
  958  ssize_t write(const char *ptr, size_t size) override;
  959  void get_remote_ip_and_port(std::string &ip, int &port) const override;
  960  void get_local_ip_and_port(std::string &ip, int &port) const override;
  961  socket_t socket() const override;
  962  time_t duration() const override;
  963
  964private:
  965  socket_t sock_;
  966  SSL *ssl_;
  967  time_t read_timeout_sec_;
  968  time_t read_timeout_usec_;
  969  time_t write_timeout_sec_;
  970  time_t write_timeout_usec_;
  971  time_t max_timeout_msec_;
  972  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
  973};
  974#endif
  975
  976bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,
  977                       time_t keep_alive_timeout_sec) {
  978  using namespace std::chrono;
  979
  980  const auto interval_usec =
  981      CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
  982
  983  // Avoid expensive `steady_clock::now()` call for the first time
  984  if (select_read(sock, 0, interval_usec) > 0) { return true; }
  985
  986  const auto start = steady_clock::now() - microseconds{interval_usec};
  987  const auto timeout = seconds{keep_alive_timeout_sec};
  988
  989  while (true) {
  990    if (svr_sock == INVALID_SOCKET) {
  991      break; // Server socket is closed
  992    }
  993
  994    auto val = select_read(sock, 0, interval_usec);
  995    if (val < 0) {
  996      break; // Ssocket error
  997    } else if (val == 0) {
  998      if (steady_clock::now() - start > timeout) {
  999        break; // Timeout
 1000      }
 1001    } else {
 1002      return true; // Ready for read
 1003    }
 1004  }
 1005
 1006  return false;
 1007}
 1008
 1009template <typename T>
 1010bool
 1011process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
 1012                           size_t keep_alive_max_count,
 1013                           time_t keep_alive_timeout_sec, T callback) {
 1014  assert(keep_alive_max_count > 0);
 1015  auto ret = false;
 1016  auto count = keep_alive_max_count;
 1017  while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
 1018    auto close_connection = count == 1;
 1019    auto connection_closed = false;
 1020    ret = callback(close_connection, connection_closed);
 1021    if (!ret || connection_closed) { break; }
 1022    count--;
 1023  }
 1024  return ret;
 1025}
 1026
 1027template <typename T>
 1028bool
 1029process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
 1030                      size_t keep_alive_max_count,
 1031                      time_t keep_alive_timeout_sec, time_t read_timeout_sec,
 1032                      time_t read_timeout_usec, time_t write_timeout_sec,
 1033                      time_t write_timeout_usec, T callback) {
 1034  return process_server_socket_core(
 1035      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
 1036      [&](bool close_connection, bool &connection_closed) {
 1037        SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
 1038                          write_timeout_sec, write_timeout_usec);
 1039        return callback(strm, close_connection, connection_closed);
 1040      });
 1041}
 1042
 1043bool process_client_socket(
 1044    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
 1045    time_t write_timeout_sec, time_t write_timeout_usec,
 1046    time_t max_timeout_msec,
 1047    std::chrono::time_point<std::chrono::steady_clock> start_time,
 1048    std::function<bool(Stream &)> callback) {
 1049  SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
 1050                    write_timeout_sec, write_timeout_usec, max_timeout_msec,
 1051                    start_time);
 1052  return callback(strm);
 1053}
 1054
 1055int shutdown_socket(socket_t sock) {
 1056#ifdef _WIN32
 1057  return shutdown(sock, SD_BOTH);
 1058#else
 1059  return shutdown(sock, SHUT_RDWR);
 1060#endif
 1061}
 1062
 1063std::string escape_abstract_namespace_unix_domain(const std::string &s) {
 1064  if (s.size() > 1 && s[0] == '\0') {
 1065    auto ret = s;
 1066    ret[0] = '@';
 1067    return ret;
 1068  }
 1069  return s;
 1070}
 1071
 1072std::string
 1073unescape_abstract_namespace_unix_domain(const std::string &s) {
 1074  if (s.size() > 1 && s[0] == '@') {
 1075    auto ret = s;
 1076    ret[0] = '\0';
 1077    return ret;
 1078  }
 1079  return s;
 1080}
 1081
 1082int getaddrinfo_with_timeout(const char *node, const char *service,
 1083                                    const struct addrinfo *hints,
 1084                                    struct addrinfo **res, time_t timeout_sec) {
 1085#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
 1086  if (timeout_sec <= 0) {
 1087    // No timeout specified, use standard getaddrinfo
 1088    return getaddrinfo(node, service, hints, res);
 1089  }
 1090
 1091#ifdef _WIN32
 1092  // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
 1093  OVERLAPPED overlapped = {0};
 1094  HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
 1095  if (!event) { return EAI_FAIL; }
 1096
 1097  overlapped.hEvent = event;
 1098
 1099  PADDRINFOEXW result_addrinfo = nullptr;
 1100  HANDLE cancel_handle = nullptr;
 1101
 1102  ADDRINFOEXW hints_ex = {0};
 1103  if (hints) {
 1104    hints_ex.ai_flags = hints->ai_flags;
 1105    hints_ex.ai_family = hints->ai_family;
 1106    hints_ex.ai_socktype = hints->ai_socktype;
 1107    hints_ex.ai_protocol = hints->ai_protocol;
 1108  }
 1109
 1110  auto wnode = u8string_to_wstring(node);
 1111  auto wservice = u8string_to_wstring(service);
 1112
 1113  auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,
 1114                              hints ? &hints_ex : nullptr, &result_addrinfo,
 1115                              nullptr, &overlapped, nullptr, &cancel_handle);
 1116
 1117  if (ret == WSA_IO_PENDING) {
 1118    auto wait_result =
 1119        ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
 1120    if (wait_result == WAIT_TIMEOUT) {
 1121      if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
 1122      ::CloseHandle(event);
 1123      return EAI_AGAIN;
 1124    }
 1125
 1126    DWORD bytes_returned;
 1127    if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,
 1128                               &bytes_returned, FALSE)) {
 1129      ::CloseHandle(event);
 1130      return ::WSAGetLastError();
 1131    }
 1132  }
 1133
 1134  ::CloseHandle(event);
 1135
 1136  if (ret == NO_ERROR || ret == WSA_IO_PENDING) {
 1137    *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);
 1138    return 0;
 1139  }
 1140
 1141  return ret;
 1142#elif TARGET_OS_MAC
 1143  if (!node) { return EAI_NONAME; }
 1144  // macOS implementation using CFHost API for asynchronous DNS resolution
 1145  CFStringRef hostname_ref = CFStringCreateWithCString(
 1146      kCFAllocatorDefault, node, kCFStringEncodingUTF8);
 1147  if (!hostname_ref) { return EAI_MEMORY; }
 1148
 1149  CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
 1150  CFRelease(hostname_ref);
 1151  if (!host_ref) { return EAI_MEMORY; }
 1152
 1153  // Set up context for callback
 1154  struct CFHostContext {
 1155    bool completed = false;
 1156    bool success = false;
 1157    CFArrayRef addresses = nullptr;
 1158    std::mutex mutex;
 1159    std::condition_variable cv;
 1160  } context;
 1161
 1162  CFHostClientContext client_context;
 1163  memset(&client_context, 0, sizeof(client_context));
 1164  client_context.info = &context;
 1165
 1166  // Set callback
 1167  auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,
 1168                     const CFStreamError *error, void *info) {
 1169    auto ctx = static_cast<CFHostContext *>(info);
 1170    std::lock_guard<std::mutex> lock(ctx->mutex);
 1171
 1172    if (error && error->error != 0) {
 1173      ctx->success = false;
 1174    } else {
 1175      Boolean hasBeenResolved;
 1176      ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
 1177      if (ctx->addresses && hasBeenResolved) {
 1178        CFRetain(ctx->addresses);
 1179        ctx->success = true;
 1180      } else {
 1181        ctx->success = false;
 1182      }
 1183    }
 1184    ctx->completed = true;
 1185    ctx->cv.notify_one();
 1186  };
 1187
 1188  if (!CFHostSetClient(host_ref, callback, &client_context)) {
 1189    CFRelease(host_ref);
 1190    return EAI_SYSTEM;
 1191  }
 1192
 1193  // Schedule on run loop
 1194  CFRunLoopRef run_loop = CFRunLoopGetCurrent();
 1195  CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
 1196
 1197  // Start resolution
 1198  CFStreamError stream_error;
 1199  if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
 1200    CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
 1201    CFRelease(host_ref);
 1202    return EAI_FAIL;
 1203  }
 1204
 1205  // Wait for completion with timeout
 1206  auto timeout_time =
 1207      std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
 1208  bool timed_out = false;
 1209
 1210  {
 1211    std::unique_lock<std::mutex> lock(context.mutex);
 1212
 1213    while (!context.completed) {
 1214      auto now = std::chrono::steady_clock::now();
 1215      if (now >= timeout_time) {
 1216        timed_out = true;
 1217        break;
 1218      }
 1219
 1220      // Run the runloop for a short time
 1221      lock.unlock();
 1222      CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
 1223      lock.lock();
 1224    }
 1225  }
 1226
 1227  // Clean up
 1228  CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
 1229  CFHostSetClient(host_ref, nullptr, nullptr);
 1230
 1231  if (timed_out || !context.completed) {
 1232    CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
 1233    CFRelease(host_ref);
 1234    return EAI_AGAIN;
 1235  }
 1236
 1237  if (!context.success || !context.addresses) {
 1238    CFRelease(host_ref);
 1239    return EAI_NODATA;
 1240  }
 1241
 1242  // Convert CFArray to addrinfo
 1243  CFIndex count = CFArrayGetCount(context.addresses);
 1244  if (count == 0) {
 1245    CFRelease(context.addresses);
 1246    CFRelease(host_ref);
 1247    return EAI_NODATA;
 1248  }
 1249
 1250  struct addrinfo *result_addrinfo = nullptr;
 1251  struct addrinfo **current = &result_addrinfo;
 1252
 1253  for (CFIndex i = 0; i < count; i++) {
 1254    CFDataRef addr_data =
 1255        static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));
 1256    if (!addr_data) continue;
 1257
 1258    const struct sockaddr *sockaddr_ptr =
 1259        reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));
 1260    socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));
 1261
 1262    // Allocate addrinfo structure
 1263    *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));
 1264    if (!*current) {
 1265      freeaddrinfo(result_addrinfo);
 1266      CFRelease(context.addresses);
 1267      CFRelease(host_ref);
 1268      return EAI_MEMORY;
 1269    }
 1270
 1271    memset(*current, 0, sizeof(struct addrinfo));
 1272
 1273    // Set up addrinfo fields
 1274    (*current)->ai_family = sockaddr_ptr->sa_family;
 1275    (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
 1276    (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
 1277    (*current)->ai_addrlen = sockaddr_len;
 1278
 1279    // Copy sockaddr
 1280    (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));
 1281    if (!(*current)->ai_addr) {
 1282      freeaddrinfo(result_addrinfo);
 1283      CFRelease(context.addresses);
 1284      CFRelease(host_ref);
 1285      return EAI_MEMORY;
 1286    }
 1287    memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
 1288
 1289    // Set port if service is specified
 1290    if (service && strlen(service) > 0) {
 1291      int port = atoi(service);
 1292      if (port > 0) {
 1293        if (sockaddr_ptr->sa_family == AF_INET) {
 1294          reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)
 1295              ->sin_port = htons(static_cast<uint16_t>(port));
 1296        } else if (sockaddr_ptr->sa_family == AF_INET6) {
 1297          reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)
 1298              ->sin6_port = htons(static_cast<uint16_t>(port));
 1299        }
 1300      }
 1301    }
 1302
 1303    current = &((*current)->ai_next);
 1304  }
 1305
 1306  CFRelease(context.addresses);
 1307  CFRelease(host_ref);
 1308
 1309  *res = result_addrinfo;
 1310  return 0;
 1311#elif defined(_GNU_SOURCE) && defined(__GLIBC__) &&                            \
 1312    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
 1313  // Linux implementation using getaddrinfo_a for asynchronous DNS resolution
 1314  struct gaicb request;
 1315  struct gaicb *requests[1] = {&request};
 1316  struct sigevent sevp;
 1317  struct timespec timeout;
 1318
 1319  // Initialize the request structure
 1320  memset(&request, 0, sizeof(request));
 1321  request.ar_name = node;
 1322  request.ar_service = service;
 1323  request.ar_request = hints;
 1324
 1325  // Set up timeout
 1326  timeout.tv_sec = timeout_sec;
 1327  timeout.tv_nsec = 0;
 1328
 1329  // Initialize sigevent structure (not used, but required)
 1330  memset(&sevp, 0, sizeof(sevp));
 1331  sevp.sigev_notify = SIGEV_NONE;
 1332
 1333  // Start asynchronous resolution
 1334  int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
 1335  if (start_result != 0) { return start_result; }
 1336
 1337  // Wait for completion with timeout
 1338  int wait_result =
 1339      gai_suspend((const struct gaicb *const *)requests, 1, &timeout);
 1340
 1341  if (wait_result == 0 || wait_result == EAI_ALLDONE) {
 1342    // Completed successfully, get the result
 1343    int gai_result = gai_error(&request);
 1344    if (gai_result == 0) {
 1345      *res = request.ar_result;
 1346      return 0;
 1347    } else {
 1348      // Clean up on error
 1349      if (request.ar_result) { freeaddrinfo(request.ar_result); }
 1350      return gai_result;
 1351    }
 1352  } else if (wait_result == EAI_AGAIN) {
 1353    // Timeout occurred, cancel the request
 1354    gai_cancel(&request);
 1355    return EAI_AGAIN;
 1356  } else {
 1357    // Other error occurred
 1358    gai_cancel(&request);
 1359    return wait_result;
 1360  }
 1361#else
 1362  // Fallback implementation using thread-based timeout for other Unix systems
 1363
 1364  struct GetAddrInfoState {
 1365    ~GetAddrInfoState() {
 1366      if (info) { freeaddrinfo(info); }
 1367    }
 1368
 1369    std::mutex mutex;
 1370    std::condition_variable result_cv;
 1371    bool completed = false;
 1372    int result = EAI_SYSTEM;
 1373    std::string node;
 1374    std::string service;
 1375    struct addrinfo hints;
 1376    struct addrinfo *info = nullptr;
 1377  };
 1378
 1379  // Allocate on the heap, so the resolver thread can keep using the data.
 1380  auto state = std::make_shared<GetAddrInfoState>();
 1381  if (node) { state->node = node; }
 1382  state->service = service;
 1383  state->hints = *hints;
 1384
 1385  std::thread resolve_thread([state]() {
 1386    auto thread_result =
 1387        getaddrinfo(state->node.c_str(), state->service.c_str(), &state->hints,
 1388                    &state->info);
 1389
 1390    std::lock_guard<std::mutex> lock(state->mutex);
 1391    state->result = thread_result;
 1392    state->completed = true;
 1393    state->result_cv.notify_one();
 1394  });
 1395
 1396  // Wait for completion or timeout
 1397  std::unique_lock<std::mutex> lock(state->mutex);
 1398  auto finished =
 1399      state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
 1400                                [&] { return state->completed; });
 1401
 1402  if (finished) {
 1403    // Operation completed within timeout
 1404    resolve_thread.join();
 1405    *res = state->info;
 1406    state->info = nullptr; // Pass ownership to caller
 1407    return state->result;
 1408  } else {
 1409    // Timeout occurred
 1410    resolve_thread.detach(); // Let the thread finish in background
 1411    return EAI_AGAIN;        // Return timeout error
 1412  }
 1413#endif
 1414#else
 1415  (void)(timeout_sec); // Unused parameter for non-blocking getaddrinfo
 1416  return getaddrinfo(node, service, hints, res);
 1417#endif
 1418}
 1419
 1420template <typename BindOrConnect>
 1421socket_t create_socket(const std::string &host, const std::string &ip, int port,
 1422                       int address_family, int socket_flags, bool tcp_nodelay,
 1423                       bool ipv6_v6only, SocketOptions socket_options,
 1424                       BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
 1425  // Get address info
 1426  const char *node = nullptr;
 1427  struct addrinfo hints;
 1428  struct addrinfo *result;
 1429
 1430  memset(&hints, 0, sizeof(struct addrinfo));
 1431  hints.ai_socktype = SOCK_STREAM;
 1432  hints.ai_protocol = IPPROTO_IP;
 1433
 1434  if (!ip.empty()) {
 1435    node = ip.c_str();
 1436    // Ask getaddrinfo to convert IP in c-string to address
 1437    hints.ai_family = AF_UNSPEC;
 1438    hints.ai_flags = AI_NUMERICHOST;
 1439  } else {
 1440    if (!host.empty()) { node = host.c_str(); }
 1441    hints.ai_family = address_family;
 1442    hints.ai_flags = socket_flags;
 1443  }
 1444
 1445#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
 1446  if (hints.ai_family == AF_UNIX) {
 1447    const auto addrlen = host.length();
 1448    if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
 1449
 1450#ifdef SOCK_CLOEXEC
 1451    auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
 1452                       hints.ai_protocol);
 1453#else
 1454    auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
 1455#endif
 1456
 1457    if (sock != INVALID_SOCKET) {
 1458      sockaddr_un addr{};
 1459      addr.sun_family = AF_UNIX;
 1460
 1461      auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
 1462      std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
 1463
 1464      hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
 1465      hints.ai_addrlen = static_cast<socklen_t>(
 1466          sizeof(addr) - sizeof(addr.sun_path) + addrlen);
 1467
 1468#ifndef SOCK_CLOEXEC
 1469#ifndef _WIN32
 1470      fcntl(sock, F_SETFD, FD_CLOEXEC);
 1471#endif
 1472#endif
 1473
 1474      if (socket_options) { socket_options(sock); }
 1475
 1476#ifdef _WIN32
 1477      // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so
 1478      // remove the option.
 1479      detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
 1480#endif
 1481
 1482      bool dummy;
 1483      if (!bind_or_connect(sock, hints, dummy)) {
 1484        close_socket(sock);
 1485        sock = INVALID_SOCKET;
 1486      }
 1487    }
 1488    return sock;
 1489  }
 1490#endif
 1491
 1492  auto service = std::to_string(port);
 1493
 1494  if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
 1495                               timeout_sec)) {
 1496#if defined __linux__ && !defined __ANDROID__
 1497    res_init();
 1498#endif
 1499    return INVALID_SOCKET;
 1500  }
 1501  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
 1502
 1503  for (auto rp = result; rp; rp = rp->ai_next) {
 1504    // Create a socket
 1505#ifdef _WIN32
 1506    auto sock =
 1507        WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
 1508                   WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
 1509    /**
 1510     * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
 1511     * and above the socket creation fails on older Windows Systems.
 1512     *
 1513     * Let's try to create a socket the old way in this case.
 1514     *
 1515     * Reference:
 1516     * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
 1517     *
 1518     * WSA_FLAG_NO_HANDLE_INHERIT:
 1519     * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
 1520     * SP1, and later
 1521     *
 1522     */
 1523    if (sock == INVALID_SOCKET) {
 1524      sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
 1525    }
 1526#else
 1527
 1528#ifdef SOCK_CLOEXEC
 1529    auto sock =
 1530        socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
 1531#else
 1532    auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
 1533#endif
 1534
 1535#endif
 1536    if (sock == INVALID_SOCKET) { continue; }
 1537
 1538#if !defined _WIN32 && !defined SOCK_CLOEXEC
 1539    if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
 1540      close_socket(sock);
 1541      continue;
 1542    }
 1543#endif
 1544
 1545    if (tcp_nodelay) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
 1546
 1547    if (rp->ai_family == AF_INET6) {
 1548      set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
 1549    }
 1550
 1551    if (socket_options) { socket_options(sock); }
 1552
 1553    // bind or connect
 1554    auto quit = false;
 1555    if (bind_or_connect(sock, *rp, quit)) { return sock; }
 1556
 1557    close_socket(sock);
 1558
 1559    if (quit) { break; }
 1560  }
 1561
 1562  return INVALID_SOCKET;
 1563}
 1564
 1565void set_nonblocking(socket_t sock, bool nonblocking) {
 1566#ifdef _WIN32
 1567  auto flags = nonblocking ? 1UL : 0UL;
 1568  ioctlsocket(sock, FIONBIO, &flags);
 1569#else
 1570  auto flags = fcntl(sock, F_GETFL, 0);
 1571  fcntl(sock, F_SETFL,
 1572        nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
 1573#endif
 1574}
 1575
 1576bool is_connection_error() {
 1577#ifdef _WIN32
 1578  return WSAGetLastError() != WSAEWOULDBLOCK;
 1579#else
 1580  return errno != EINPROGRESS;
 1581#endif
 1582}
 1583
 1584bool bind_ip_address(socket_t sock, const std::string &host) {
 1585  struct addrinfo hints;
 1586  struct addrinfo *result;
 1587
 1588  memset(&hints, 0, sizeof(struct addrinfo));
 1589  hints.ai_family = AF_UNSPEC;
 1590  hints.ai_socktype = SOCK_STREAM;
 1591  hints.ai_protocol = 0;
 1592
 1593  if (getaddrinfo_with_timeout(host.c_str(), "0", &hints, &result, 0)) {
 1594    return false;
 1595  }
 1596
 1597  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
 1598
 1599  auto ret = false;
 1600  for (auto rp = result; rp; rp = rp->ai_next) {
 1601    const auto &ai = *rp;
 1602    if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
 1603      ret = true;
 1604      break;
 1605    }
 1606  }
 1607
 1608  return ret;
 1609}
 1610
 1611#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
 1612#define USE_IF2IP
 1613#endif
 1614
 1615#ifdef USE_IF2IP
 1616std::string if2ip(int address_family, const std::string &ifn) {
 1617  struct ifaddrs *ifap;
 1618  getifaddrs(&ifap);
 1619  auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
 1620
 1621  std::string addr_candidate;
 1622  for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
 1623    if (ifa->ifa_addr && ifn == ifa->ifa_name &&
 1624        (AF_UNSPEC == address_family ||
 1625         ifa->ifa_addr->sa_family == address_family)) {
 1626      if (ifa->ifa_addr->sa_family == AF_INET) {
 1627        auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
 1628        char buf[INET_ADDRSTRLEN];
 1629        if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
 1630          return std::string(buf, INET_ADDRSTRLEN);
 1631        }
 1632      } else if (ifa->ifa_addr->sa_family == AF_INET6) {
 1633        auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
 1634        if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
 1635          char buf[INET6_ADDRSTRLEN] = {};
 1636          if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
 1637            // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
 1638            auto s6_addr_head = sa->sin6_addr.s6_addr[0];
 1639            if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
 1640              addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
 1641            } else {
 1642              return std::string(buf, INET6_ADDRSTRLEN);
 1643            }
 1644          }
 1645        }
 1646      }
 1647    }
 1648  }
 1649  return addr_candidate;
 1650}
 1651#endif
 1652
 1653socket_t create_client_socket(
 1654    const std::string &host, const std::string &ip, int port,
 1655    int address_family, bool tcp_nodelay, bool ipv6_v6only,
 1656    SocketOptions socket_options, time_t connection_timeout_sec,
 1657    time_t connection_timeout_usec, time_t read_timeout_sec,
 1658    time_t read_timeout_usec, time_t write_timeout_sec,
 1659    time_t write_timeout_usec, const std::string &intf, Error &error) {
 1660  auto sock = create_socket(
 1661      host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
 1662      std::move(socket_options),
 1663      [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {
 1664        if (!intf.empty()) {
 1665#ifdef USE_IF2IP
 1666          auto ip_from_if = if2ip(address_family, intf);
 1667          if (ip_from_if.empty()) { ip_from_if = intf; }
 1668          if (!bind_ip_address(sock2, ip_from_if)) {
 1669            error = Error::BindIPAddress;
 1670            return false;
 1671          }
 1672#endif
 1673        }
 1674
 1675        set_nonblocking(sock2, true);
 1676
 1677        auto ret =
 1678            ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
 1679
 1680        if (ret < 0) {
 1681          if (is_connection_error()) {
 1682            error = Error::Connection;
 1683            return false;
 1684          }
 1685          error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
 1686                                             connection_timeout_usec);
 1687          if (error != Error::Success) {
 1688            if (error == Error::ConnectionTimeout) { quit = true; }
 1689            return false;
 1690          }
 1691        }
 1692
 1693        set_nonblocking(sock2, false);
 1694        set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
 1695                            read_timeout_usec);
 1696        set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
 1697                            write_timeout_usec);
 1698
 1699        error = Error::Success;
 1700        return true;
 1701      },
 1702      connection_timeout_sec); // Pass DNS timeout
 1703
 1704  if (sock != INVALID_SOCKET) {
 1705    error = Error::Success;
 1706  } else {
 1707    if (error == Error::Success) { error = Error::Connection; }
 1708  }
 1709
 1710  return sock;
 1711}
 1712
 1713bool get_ip_and_port(const struct sockaddr_storage &addr,
 1714                            socklen_t addr_len, std::string &ip, int &port) {
 1715  if (addr.ss_family == AF_INET) {
 1716    port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
 1717  } else if (addr.ss_family == AF_INET6) {
 1718    port =
 1719        ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
 1720  } else {
 1721    return false;
 1722  }
 1723
 1724  std::array<char, NI_MAXHOST> ipstr{};
 1725  if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
 1726                  ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
 1727                  0, NI_NUMERICHOST)) {
 1728    return false;
 1729  }
 1730
 1731  ip = ipstr.data();
 1732  return true;
 1733}
 1734
 1735void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {
 1736  struct sockaddr_storage addr;
 1737  socklen_t addr_len = sizeof(addr);
 1738  if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),
 1739                   &addr_len)) {
 1740    get_ip_and_port(addr, addr_len, ip, port);
 1741  }
 1742}
 1743
 1744void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
 1745  struct sockaddr_storage addr;
 1746  socklen_t addr_len = sizeof(addr);
 1747
 1748  if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
 1749                   &addr_len)) {
 1750#ifndef _WIN32
 1751    if (addr.ss_family == AF_UNIX) {
 1752#if defined(__linux__)
 1753      struct ucred ucred;
 1754      socklen_t len = sizeof(ucred);
 1755      if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
 1756        port = ucred.pid;
 1757      }
 1758#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
 1759      pid_t pid;
 1760      socklen_t len = sizeof(pid);
 1761      if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
 1762        port = pid;
 1763      }
 1764#endif
 1765      return;
 1766    }
 1767#endif
 1768    get_ip_and_port(addr, addr_len, ip, port);
 1769  }
 1770}
 1771
 1772constexpr unsigned int str2tag_core(const char *s, size_t l,
 1773                                           unsigned int h) {
 1774  return (l == 0)
 1775             ? h
 1776             : str2tag_core(
 1777                   s + 1, l - 1,
 1778                   // Unsets the 6 high bits of h, therefore no overflow happens
 1779                   (((std::numeric_limits<unsigned int>::max)() >> 6) &
 1780                    h * 33) ^
 1781                       static_cast<unsigned char>(*s));
 1782}
 1783
 1784unsigned int str2tag(const std::string &s) {
 1785  return str2tag_core(s.data(), s.size(), 0);
 1786}
 1787
 1788namespace udl {
 1789
 1790constexpr unsigned int operator""_t(const char *s, size_t l) {
 1791  return str2tag_core(s, l, 0);
 1792}
 1793
 1794} // namespace udl
 1795
 1796std::string
 1797find_content_type(const std::string &path,
 1798                  const std::map<std::string, std::string> &user_data,
 1799                  const std::string &default_content_type) {
 1800  auto ext = file_extension(path);
 1801
 1802  auto it = user_data.find(ext);
 1803  if (it != user_data.end()) { return it->second; }
 1804
 1805  using udl::operator""_t;
 1806
 1807  switch (str2tag(ext)) {
 1808  default: return default_content_type;
 1809
 1810  case "css"_t: return "text/css";
 1811  case "csv"_t: return "text/csv";
 1812  case "htm"_t:
 1813  case "html"_t: return "text/html";
 1814  case "js"_t:
 1815  case "mjs"_t: return "text/javascript";
 1816  case "txt"_t: return "text/plain";
 1817  case "vtt"_t: return "text/vtt";
 1818
 1819  case "apng"_t: return "image/apng";
 1820  case "avif"_t: return "image/avif";
 1821  case "bmp"_t: return "image/bmp";
 1822  case "gif"_t: return "image/gif";
 1823  case "png"_t: return "image/png";
 1824  case "svg"_t: return "image/svg+xml";
 1825  case "webp"_t: return "image/webp";
 1826  case "ico"_t: return "image/x-icon";
 1827  case "tif"_t: return "image/tiff";
 1828  case "tiff"_t: return "image/tiff";
 1829  case "jpg"_t:
 1830  case "jpeg"_t: return "image/jpeg";
 1831
 1832  case "mp4"_t: return "video/mp4";
 1833  case "mpeg"_t: return "video/mpeg";
 1834  case "webm"_t: return "video/webm";
 1835
 1836  case "mp3"_t: return "audio/mp3";
 1837  case "mpga"_t: return "audio/mpeg";
 1838  case "weba"_t: return "audio/webm";
 1839  case "wav"_t: return "audio/wave";
 1840
 1841  case "otf"_t: return "font/otf";
 1842  case "ttf"_t: return "font/ttf";
 1843  case "woff"_t: return "font/woff";
 1844  case "woff2"_t: return "font/woff2";
 1845
 1846  case "7z"_t: return "application/x-7z-compressed";
 1847  case "atom"_t: return "application/atom+xml";
 1848  case "pdf"_t: return "application/pdf";
 1849  case "json"_t: return "application/json";
 1850  case "rss"_t: return "application/rss+xml";
 1851  case "tar"_t: return "application/x-tar";
 1852  case "xht"_t:
 1853  case "xhtml"_t: return "application/xhtml+xml";
 1854  case "xslt"_t: return "application/xslt+xml";
 1855  case "xml"_t: return "application/xml";
 1856  case "gz"_t: return "application/gzip";
 1857  case "zip"_t: return "application/zip";
 1858  case "wasm"_t: return "application/wasm";
 1859  }
 1860}
 1861
 1862bool can_compress_content_type(const std::string &content_type) {
 1863  using udl::operator""_t;
 1864
 1865  auto tag = str2tag(content_type);
 1866
 1867  switch (tag) {
 1868  case "image/svg+xml"_t:
 1869  case "application/javascript"_t:
 1870  case "application/json"_t:
 1871  case "application/xml"_t:
 1872  case "application/protobuf"_t:
 1873  case "application/xhtml+xml"_t: return true;
 1874
 1875  case "text/event-stream"_t: return false;
 1876
 1877  default: return !content_type.rfind("text/", 0);
 1878  }
 1879}
 1880
 1881EncodingType encoding_type(const Request &req, const Response &res) {
 1882  auto ret =
 1883      detail::can_compress_content_type(res.get_header_value("Content-Type"));
 1884  if (!ret) { return EncodingType::None; }
 1885
 1886  const auto &s = req.get_header_value("Accept-Encoding");
 1887  (void)(s);
 1888
 1889#ifdef CPPHTTPLIB_BROTLI_SUPPORT
 1890  // TODO: 'Accept-Encoding' has br, not br;q=0
 1891  ret = s.find("br") != std::string::npos;
 1892  if (ret) { return EncodingType::Brotli; }
 1893#endif
 1894
 1895#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 1896  // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
 1897  ret = s.find("gzip") != std::string::npos;
 1898  if (ret) { return EncodingType::Gzip; }
 1899#endif
 1900
 1901#ifdef CPPHTTPLIB_ZSTD_SUPPORT
 1902  // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
 1903  ret = s.find("zstd") != std::string::npos;
 1904  if (ret) { return EncodingType::Zstd; }
 1905#endif
 1906
 1907  return EncodingType::None;
 1908}
 1909
 1910bool nocompressor::compress(const char *data, size_t data_length,
 1911                                   bool /*last*/, Callback callback) {
 1912  if (!data_length) { return true; }
 1913  return callback(data, data_length);
 1914}
 1915
 1916#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 1917gzip_compressor::gzip_compressor() {
 1918  std::memset(&strm_, 0, sizeof(strm_));
 1919  strm_.zalloc = Z_NULL;
 1920  strm_.zfree = Z_NULL;
 1921  strm_.opaque = Z_NULL;
 1922
 1923  is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
 1924                           Z_DEFAULT_STRATEGY) == Z_OK;
 1925}
 1926
 1927gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
 1928
 1929bool gzip_compressor::compress(const char *data, size_t data_length,
 1930                                      bool last, Callback callback) {
 1931  assert(is_valid_);
 1932
 1933  do {
 1934    constexpr size_t max_avail_in =
 1935        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
 1936
 1937    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
 1938        (std::min)(data_length, max_avail_in));
 1939    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
 1940
 1941    data_length -= strm_.avail_in;
 1942    data += strm_.avail_in;
 1943
 1944    auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
 1945    auto ret = Z_OK;
 1946
 1947    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
 1948    do {
 1949      strm_.avail_out = static_cast<uInt>(buff.size());
 1950      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
 1951
 1952      ret = deflate(&strm_, flush);
 1953      if (ret == Z_STREAM_ERROR) { return false; }
 1954
 1955      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
 1956        return false;
 1957      }
 1958    } while (strm_.avail_out == 0);
 1959
 1960    assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
 1961           (flush == Z_NO_FLUSH && ret == Z_OK));
 1962    assert(strm_.avail_in == 0);
 1963  } while (data_length > 0);
 1964
 1965  return true;
 1966}
 1967
 1968gzip_decompressor::gzip_decompressor() {
 1969  std::memset(&strm_, 0, sizeof(strm_));
 1970  strm_.zalloc = Z_NULL;
 1971  strm_.zfree = Z_NULL;
 1972  strm_.opaque = Z_NULL;
 1973
 1974  // 15 is the value of wbits, which should be at the maximum possible value
 1975  // to ensure that any gzip stream can be decoded. The offset of 32 specifies
 1976  // that the stream type should be automatically detected either gzip or
 1977  // deflate.
 1978  is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
 1979}
 1980
 1981gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
 1982
 1983bool gzip_decompressor::is_valid() const { return is_valid_; }
 1984
 1985bool gzip_decompressor::decompress(const char *data, size_t data_length,
 1986                                          Callback callback) {
 1987  assert(is_valid_);
 1988
 1989  auto ret = Z_OK;
 1990
 1991  do {
 1992    constexpr size_t max_avail_in =
 1993        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
 1994
 1995    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
 1996        (std::min)(data_length, max_avail_in));
 1997    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
 1998
 1999    data_length -= strm_.avail_in;
 2000    data += strm_.avail_in;
 2001
 2002    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
 2003    while (strm_.avail_in > 0 && ret == Z_OK) {
 2004      strm_.avail_out = static_cast<uInt>(buff.size());
 2005      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
 2006
 2007      ret = inflate(&strm_, Z_NO_FLUSH);
 2008
 2009      assert(ret != Z_STREAM_ERROR);
 2010      switch (ret) {
 2011      case Z_NEED_DICT:
 2012      case Z_DATA_ERROR:
 2013      case Z_MEM_ERROR: inflateEnd(&strm_); return false;
 2014      }
 2015
 2016      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
 2017        return false;
 2018      }
 2019    }
 2020
 2021    if (ret != Z_OK && ret != Z_STREAM_END) { return false; }
 2022
 2023  } while (data_length > 0);
 2024
 2025  return true;
 2026}
 2027#endif
 2028
 2029#ifdef CPPHTTPLIB_BROTLI_SUPPORT
 2030brotli_compressor::brotli_compressor() {
 2031  state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
 2032}
 2033
 2034brotli_compressor::~brotli_compressor() {
 2035  BrotliEncoderDestroyInstance(state_);
 2036}
 2037
 2038bool brotli_compressor::compress(const char *data, size_t data_length,
 2039                                        bool last, Callback callback) {
 2040  std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
 2041
 2042  auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
 2043  auto available_in = data_length;
 2044  auto next_in = reinterpret_cast<const uint8_t *>(data);
 2045
 2046  for (;;) {
 2047    if (last) {
 2048      if (BrotliEncoderIsFinished(state_)) { break; }
 2049    } else {
 2050      if (!available_in) { break; }
 2051    }
 2052
 2053    auto available_out = buff.size();
 2054    auto next_out = buff.data();
 2055
 2056    if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
 2057                                     &available_out, &next_out, nullptr)) {
 2058      return false;
 2059    }
 2060
 2061    auto output_bytes = buff.size() - available_out;
 2062    if (output_bytes) {
 2063      callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
 2064    }
 2065  }
 2066
 2067  return true;
 2068}
 2069
 2070brotli_decompressor::brotli_decompressor() {
 2071  decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
 2072  decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
 2073                        : BROTLI_DECODER_RESULT_ERROR;
 2074}
 2075
 2076brotli_decompressor::~brotli_decompressor() {
 2077  if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
 2078}
 2079
 2080bool brotli_decompressor::is_valid() const { return decoder_s; }
 2081
 2082bool brotli_decompressor::decompress(const char *data,
 2083                                            size_t data_length,
 2084                                            Callback callback) {
 2085  if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
 2086      decoder_r == BROTLI_DECODER_RESULT_ERROR) {
 2087    return 0;
 2088  }
 2089
 2090  auto next_in = reinterpret_cast<const uint8_t *>(data);
 2091  size_t avail_in = data_length;
 2092  size_t total_out;
 2093
 2094  decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
 2095
 2096  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
 2097  while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
 2098    char *next_out = buff.data();
 2099    size_t avail_out = buff.size();
 2100
 2101    decoder_r = BrotliDecoderDecompressStream(
 2102        decoder_s, &avail_in, &next_in, &avail_out,
 2103        reinterpret_cast<uint8_t **>(&next_out), &total_out);
 2104
 2105    if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
 2106
 2107    if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
 2108  }
 2109
 2110  return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
 2111         decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
 2112}
 2113#endif
 2114
 2115#ifdef CPPHTTPLIB_ZSTD_SUPPORT
 2116zstd_compressor::zstd_compressor() {
 2117  ctx_ = ZSTD_createCCtx();
 2118  ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
 2119}
 2120
 2121zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
 2122
 2123bool zstd_compressor::compress(const char *data, size_t data_length,
 2124                                      bool last, Callback callback) {
 2125  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
 2126
 2127  ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
 2128  ZSTD_inBuffer input = {data, data_length, 0};
 2129
 2130  bool finished;
 2131  do {
 2132    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
 2133    size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
 2134
 2135    if (ZSTD_isError(remaining)) { return false; }
 2136
 2137    if (!callback(buff.data(), output.pos)) { return false; }
 2138
 2139    finished = last ? (remaining == 0) : (input.pos == input.size);
 2140
 2141  } while (!finished);
 2142
 2143  return true;
 2144}
 2145
 2146zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
 2147
 2148zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
 2149
 2150bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }
 2151
 2152bool zstd_decompressor::decompress(const char *data, size_t data_length,
 2153                                          Callback callback) {
 2154  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
 2155  ZSTD_inBuffer input = {data, data_length, 0};
 2156
 2157  while (input.pos < input.size) {
 2158    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
 2159    size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
 2160
 2161    if (ZSTD_isError(remaining)) { return false; }
 2162
 2163    if (!callback(buff.data(), output.pos)) { return false; }
 2164  }
 2165
 2166  return true;
 2167}
 2168#endif
 2169
 2170std::unique_ptr<decompressor>
 2171create_decompressor(const std::string &encoding) {
 2172  std::unique_ptr<decompressor> decompressor;
 2173
 2174  if (encoding == "gzip" || encoding == "deflate") {
 2175#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 2176    decompressor = detail::make_unique<gzip_decompressor>();
 2177#endif
 2178  } else if (encoding.find("br") != std::string::npos) {
 2179#ifdef CPPHTTPLIB_BROTLI_SUPPORT
 2180    decompressor = detail::make_unique<brotli_decompressor>();
 2181#endif
 2182  } else if (encoding == "zstd" || encoding.find("zstd") != std::string::npos) {
 2183#ifdef CPPHTTPLIB_ZSTD_SUPPORT
 2184    decompressor = detail::make_unique<zstd_decompressor>();
 2185#endif
 2186  }
 2187
 2188  return decompressor;
 2189}
 2190
 2191bool is_prohibited_header_name(const std::string &name) {
 2192  using udl::operator""_t;
 2193
 2194  switch (str2tag(name)) {
 2195  case "REMOTE_ADDR"_t:
 2196  case "REMOTE_PORT"_t:
 2197  case "LOCAL_ADDR"_t:
 2198  case "LOCAL_PORT"_t: return true;
 2199  default: return false;
 2200  }
 2201}
 2202
 2203bool has_header(const Headers &headers, const std::string &key) {
 2204  if (is_prohibited_header_name(key)) { return false; }
 2205  return headers.find(key) != headers.end();
 2206}
 2207
 2208const char *get_header_value(const Headers &headers,
 2209                                    const std::string &key, const char *def,
 2210                                    size_t id) {
 2211  if (is_prohibited_header_name(key)) {
 2212#ifndef CPPHTTPLIB_NO_EXCEPTIONS
 2213    std::string msg = "Prohibited header name '" + key + "' is specified.";
 2214    throw std::invalid_argument(msg);
 2215#else
 2216    return "";
 2217#endif
 2218  }
 2219
 2220  auto rng = headers.equal_range(key);
 2221  auto it = rng.first;
 2222  std::advance(it, static_cast<ssize_t>(id));
 2223  if (it != rng.second) { return it->second.c_str(); }
 2224  return def;
 2225}
 2226
 2227bool read_headers(Stream &strm, Headers &headers) {
 2228  const auto bufsiz = 2048;
 2229  char buf[bufsiz];
 2230  stream_line_reader line_reader(strm, buf, bufsiz);
 2231
 2232  size_t header_count = 0;
 2233
 2234  for (;;) {
 2235    if (!line_reader.getline()) { return false; }
 2236
 2237    // Check if the line ends with CRLF.
 2238    auto line_terminator_len = 2;
 2239    if (line_reader.end_with_crlf()) {
 2240      // Blank line indicates end of headers.
 2241      if (line_reader.size() == 2) { break; }
 2242    } else {
 2243#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
 2244      // Blank line indicates end of headers.
 2245      if (line_reader.size() == 1) { break; }
 2246      line_terminator_len = 1;
 2247#else
 2248      continue; // Skip invalid line.
 2249#endif
 2250    }
 2251
 2252    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
 2253
 2254    // Check header count limit
 2255    if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
 2256
 2257    // Exclude line terminator
 2258    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
 2259
 2260    if (!parse_header(line_reader.ptr(), end,
 2261                      [&](const std::string &key, const std::string &val) {
 2262                        headers.emplace(key, val);
 2263                      })) {
 2264      return false;
 2265    }
 2266
 2267    header_count++;
 2268  }
 2269
 2270  return true;
 2271}
 2272
 2273bool read_content_with_length(Stream &strm, size_t len,
 2274                                     DownloadProgress progress,
 2275                                     ContentReceiverWithProgress out) {
 2276  char buf[CPPHTTPLIB_RECV_BUFSIZ];
 2277
 2278  detail::BodyReader br;
 2279  br.stream = &strm;
 2280  br.content_length = len;
 2281  br.chunked = false;
 2282  br.bytes_read = 0;
 2283  br.last_error = Error::Success;
 2284
 2285  size_t r = 0;
 2286  while (r < len) {
 2287    auto read_len = static_cast<size_t>(len - r);
 2288    auto to_read = (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ);
 2289    auto n = detail::read_body_content(&strm, br, buf, to_read);
 2290    if (n <= 0) { return false; }
 2291
 2292    if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
 2293    r += static_cast<size_t>(n);
 2294
 2295    if (progress) {
 2296      if (!progress(r, len)) { return false; }
 2297    }
 2298  }
 2299
 2300  return true;
 2301}
 2302
 2303void skip_content_with_length(Stream &strm, size_t len) {
 2304  char buf[CPPHTTPLIB_RECV_BUFSIZ];
 2305  size_t r = 0;
 2306  while (r < len) {
 2307    auto read_len = static_cast<size_t>(len - r);
 2308    auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
 2309    if (n <= 0) { return; }
 2310    r += static_cast<size_t>(n);
 2311  }
 2312}
 2313
 2314enum class ReadContentResult {
 2315  Success,         // Successfully read the content
 2316  PayloadTooLarge, // The content exceeds the specified payload limit
 2317  Error            // An error occurred while reading the content
 2318};
 2319
 2320ReadContentResult
 2321read_content_without_length(Stream &strm, size_t payload_max_length,
 2322                            ContentReceiverWithProgress out) {
 2323  char buf[CPPHTTPLIB_RECV_BUFSIZ];
 2324  size_t r = 0;
 2325  for (;;) {
 2326    auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
 2327    if (n == 0) { return ReadContentResult::Success; }
 2328    if (n < 0) { return ReadContentResult::Error; }
 2329
 2330    // Check if adding this data would exceed the payload limit
 2331    if (r > payload_max_length ||
 2332        payload_max_length - r < static_cast<size_t>(n)) {
 2333      return ReadContentResult::PayloadTooLarge;
 2334    }
 2335
 2336    if (!out(buf, static_cast<size_t>(n), r, 0)) {
 2337      return ReadContentResult::Error;
 2338    }
 2339    r += static_cast<size_t>(n);
 2340  }
 2341
 2342  return ReadContentResult::Success;
 2343}
 2344
 2345template <typename T>
 2346ReadContentResult read_content_chunked(Stream &strm, T &x,
 2347                                              size_t payload_max_length,
 2348                                              ContentReceiverWithProgress out) {
 2349  detail::ChunkedDecoder dec(strm);
 2350
 2351  char buf[CPPHTTPLIB_RECV_BUFSIZ];
 2352  size_t total_len = 0;
 2353
 2354  for (;;) {
 2355    size_t chunk_offset = 0;
 2356    size_t chunk_total = 0;
 2357    auto n = dec.read_payload(buf, sizeof(buf), chunk_offset, chunk_total);
 2358    if (n < 0) { return ReadContentResult::Error; }
 2359
 2360    if (n == 0) {
 2361      if (!dec.parse_trailers_into(x.trailers, x.headers)) {
 2362        return ReadContentResult::Error;
 2363      }
 2364      return ReadContentResult::Success;
 2365    }
 2366
 2367    if (total_len > payload_max_length ||
 2368        payload_max_length - total_len < static_cast<size_t>(n)) {
 2369      return ReadContentResult::PayloadTooLarge;
 2370    }
 2371
 2372    if (!out(buf, static_cast<size_t>(n), chunk_offset, chunk_total)) {
 2373      return ReadContentResult::Error;
 2374    }
 2375
 2376    total_len += static_cast<size_t>(n);
 2377  }
 2378}
 2379
 2380bool is_chunked_transfer_encoding(const Headers &headers) {
 2381  return case_ignore::equal(
 2382      get_header_value(headers, "Transfer-Encoding", "", 0), "chunked");
 2383}
 2384
 2385template <typename T, typename U>
 2386bool prepare_content_receiver(T &x, int &status,
 2387                              ContentReceiverWithProgress receiver,
 2388                              bool decompress, U callback) {
 2389  if (decompress) {
 2390    std::string encoding = x.get_header_value("Content-Encoding");
 2391    std::unique_ptr<decompressor> decompressor;
 2392
 2393    if (!encoding.empty()) {
 2394      decompressor = detail::create_decompressor(encoding);
 2395      if (!decompressor) {
 2396        // Unsupported encoding or no support compiled in
 2397        status = StatusCode::UnsupportedMediaType_415;
 2398        return false;
 2399      }
 2400    }
 2401
 2402    if (decompressor) {
 2403      if (decompressor->is_valid()) {
 2404        ContentReceiverWithProgress out = [&](const char *buf, size_t n,
 2405                                              size_t off, size_t len) {
 2406          return decompressor->decompress(buf, n,
 2407                                          [&](const char *buf2, size_t n2) {
 2408                                            return receiver(buf2, n2, off, len);
 2409                                          });
 2410        };
 2411        return callback(std::move(out));
 2412      } else {
 2413        status = StatusCode::InternalServerError_500;
 2414        return false;
 2415      }
 2416    }
 2417  }
 2418
 2419  ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,
 2420                                        size_t len) {
 2421    return receiver(buf, n, off, len);
 2422  };
 2423  return callback(std::move(out));
 2424}
 2425
 2426template <typename T>
 2427bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
 2428                  DownloadProgress progress,
 2429                  ContentReceiverWithProgress receiver, bool decompress) {
 2430  return prepare_content_receiver(
 2431      x, status, std::move(receiver), decompress,
 2432      [&](const ContentReceiverWithProgress &out) {
 2433        auto ret = true;
 2434        auto exceed_payload_max_length = false;
 2435
 2436        if (is_chunked_transfer_encoding(x.headers)) {
 2437          auto result = read_content_chunked(strm, x, payload_max_length, out);
 2438          if (result == ReadContentResult::Success) {
 2439            ret = true;
 2440          } else if (result == ReadContentResult::PayloadTooLarge) {
 2441            exceed_payload_max_length = true;
 2442            ret = false;
 2443          } else {
 2444            ret = false;
 2445          }
 2446        } else if (!has_header(x.headers, "Content-Length")) {
 2447          auto result =
 2448              read_content_without_length(strm, payload_max_length, out);
 2449          if (result == ReadContentResult::Success) {
 2450            ret = true;
 2451          } else if (result == ReadContentResult::PayloadTooLarge) {
 2452            exceed_payload_max_length = true;
 2453            ret = false;
 2454          } else {
 2455            ret = false;
 2456          }
 2457        } else {
 2458          auto is_invalid_value = false;
 2459          auto len = get_header_value_u64(x.headers, "Content-Length",
 2460                                          (std::numeric_limits<size_t>::max)(),
 2461                                          0, is_invalid_value);
 2462
 2463          if (is_invalid_value) {
 2464            ret = false;
 2465          } else if (len > payload_max_length) {
 2466            exceed_payload_max_length = true;
 2467            skip_content_with_length(strm, len);
 2468            ret = false;
 2469          } else if (len > 0) {
 2470            ret = read_content_with_length(strm, len, std::move(progress), out);
 2471          }
 2472        }
 2473
 2474        if (!ret) {
 2475          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
 2476                                             : StatusCode::BadRequest_400;
 2477        }
 2478        return ret;
 2479      });
 2480}
 2481
 2482ssize_t write_request_line(Stream &strm, const std::string &method,
 2483                                  const std::string &path) {
 2484  std::string s = method;
 2485  s += ' ';
 2486  s += path;
 2487  s += " HTTP/1.1\r\n";
 2488  return strm.write(s.data(), s.size());
 2489}
 2490
 2491ssize_t write_response_line(Stream &strm, int status) {
 2492  std::string s = "HTTP/1.1 ";
 2493  s += std::to_string(status);
 2494  s += ' ';
 2495  s += httplib::status_message(status);
 2496  s += "\r\n";
 2497  return strm.write(s.data(), s.size());
 2498}
 2499
 2500ssize_t write_headers(Stream &strm, const Headers &headers) {
 2501  ssize_t write_len = 0;
 2502  for (const auto &x : headers) {
 2503    std::string s;
 2504    s = x.first;
 2505    s += ": ";
 2506    s += x.second;
 2507    s += "\r\n";
 2508
 2509    auto len = strm.write(s.data(), s.size());
 2510    if (len < 0) { return len; }
 2511    write_len += len;
 2512  }
 2513  auto len = strm.write("\r\n");
 2514  if (len < 0) { return len; }
 2515  write_len += len;
 2516  return write_len;
 2517}
 2518
 2519bool write_data(Stream &strm, const char *d, size_t l) {
 2520  size_t offset = 0;
 2521  while (offset < l) {
 2522    auto length = strm.write(d + offset, l - offset);
 2523    if (length < 0) { return false; }
 2524    offset += static_cast<size_t>(length);
 2525  }
 2526  return true;
 2527}
 2528
 2529template <typename T>
 2530bool write_content_with_progress(Stream &strm,
 2531                                        const ContentProvider &content_provider,
 2532                                        size_t offset, size_t length,
 2533                                        T is_shutting_down,
 2534                                        const UploadProgress &upload_progress,
 2535                                        Error &error) {
 2536  size_t end_offset = offset + length;
 2537  size_t start_offset = offset;
 2538  auto ok = true;
 2539  DataSink data_sink;
 2540
 2541  data_sink.write = [&](const char *d, size_t l) -> bool {
 2542    if (ok) {
 2543      if (write_data(strm, d, l)) {
 2544        offset += l;
 2545
 2546        if (upload_progress && length > 0) {
 2547          size_t current_written = offset - start_offset;
 2548          if (!upload_progress(current_written, length)) {
 2549            ok = false;
 2550            return false;
 2551          }
 2552        }
 2553      } else {
 2554        ok = false;
 2555      }
 2556    }
 2557    return ok;
 2558  };
 2559
 2560  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
 2561
 2562  while (offset < end_offset && !is_shutting_down()) {
 2563    if (!strm.wait_writable()) {
 2564      error = Error::Write;
 2565      return false;
 2566    } else if (!content_provider(offset, end_offset - offset, data_sink)) {
 2567      error = Error::Canceled;
 2568      return false;
 2569    } else if (!ok) {
 2570      error = Error::Write;
 2571      return false;
 2572    }
 2573  }
 2574
 2575  error = Error::Success;
 2576  return true;
 2577}
 2578
 2579template <typename T>
 2580bool write_content(Stream &strm, const ContentProvider &content_provider,
 2581                          size_t offset, size_t length, T is_shutting_down,
 2582                          Error &error) {
 2583  return write_content_with_progress<T>(strm, content_provider, offset, length,
 2584                                        is_shutting_down, nullptr, error);
 2585}
 2586
 2587template <typename T>
 2588bool write_content(Stream &strm, const ContentProvider &content_provider,
 2589                          size_t offset, size_t length,
 2590                          const T &is_shutting_down) {
 2591  auto error = Error::Success;
 2592  return write_content(strm, content_provider, offset, length, is_shutting_down,
 2593                       error);
 2594}
 2595
 2596template <typename T>
 2597bool
 2598write_content_without_length(Stream &strm,
 2599                             const ContentProvider &content_provider,
 2600                             const T &is_shutting_down) {
 2601  size_t offset = 0;
 2602  auto data_available = true;
 2603  auto ok = true;
 2604  DataSink data_sink;
 2605
 2606  data_sink.write = [&](const char *d, size_t l) -> bool {
 2607    if (ok) {
 2608      offset += l;
 2609      if (!write_data(strm, d, l)) { ok = false; }
 2610    }
 2611    return ok;
 2612  };
 2613
 2614  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
 2615
 2616  data_sink.done = [&](void) { data_available = false; };
 2617
 2618  while (data_available && !is_shutting_down()) {
 2619    if (!strm.wait_writable()) {
 2620      return false;
 2621    } else if (!content_provider(offset, 0, data_sink)) {
 2622      return false;
 2623    } else if (!ok) {
 2624      return false;
 2625    }
 2626  }
 2627  return true;
 2628}
 2629
 2630template <typename T, typename U>
 2631bool
 2632write_content_chunked(Stream &strm, const ContentProvider &content_provider,
 2633                      const T &is_shutting_down, U &compressor, Error &error) {
 2634  size_t offset = 0;
 2635  auto data_available = true;
 2636  auto ok = true;
 2637  DataSink data_sink;
 2638
 2639  data_sink.write = [&](const char *d, size_t l) -> bool {
 2640    if (ok) {
 2641      data_available = l > 0;
 2642      offset += l;
 2643
 2644      std::string payload;
 2645      if (compressor.compress(d, l, false,
 2646                              [&](const char *data, size_t data_len) {
 2647                                payload.append(data, data_len);
 2648                                return true;
 2649                              })) {
 2650        if (!payload.empty()) {
 2651          // Emit chunked response header and footer for each chunk
 2652          auto chunk =
 2653              from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
 2654          if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; }
 2655        }
 2656      } else {
 2657        ok = false;
 2658      }
 2659    }
 2660    return ok;
 2661  };
 2662
 2663  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
 2664
 2665  auto done_with_trailer = [&](const Headers *trailer) {
 2666    if (!ok) { return; }
 2667
 2668    data_available = false;
 2669
 2670    std::string payload;
 2671    if (!compressor.compress(nullptr, 0, true,
 2672                             [&](const char *data, size_t data_len) {
 2673                               payload.append(data, data_len);
 2674                               return true;
 2675                             })) {
 2676      ok = false;
 2677      return;
 2678    }
 2679
 2680    if (!payload.empty()) {
 2681      // Emit chunked response header and footer for each chunk
 2682      auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
 2683      if (!write_data(strm, chunk.data(), chunk.size())) {
 2684        ok = false;
 2685        return;
 2686      }
 2687    }
 2688
 2689    constexpr const char done_marker[] = "0\r\n";
 2690    if (!write_data(strm, done_marker, str_len(done_marker))) { ok = false; }
 2691
 2692    // Trailer
 2693    if (trailer) {
 2694      for (const auto &kv : *trailer) {
 2695        std::string field_line = kv.first + ": " + kv.second + "\r\n";
 2696        if (!write_data(strm, field_line.data(), field_line.size())) {
 2697          ok = false;
 2698        }
 2699      }
 2700    }
 2701
 2702    constexpr const char crlf[] = "\r\n";
 2703    if (!write_data(strm, crlf, str_len(crlf))) { ok = false; }
 2704  };
 2705
 2706  data_sink.done = [&](void) { done_with_trailer(nullptr); };
 2707
 2708  data_sink.done_with_trailer = [&](const Headers &trailer) {
 2709    done_with_trailer(&trailer);
 2710  };
 2711
 2712  while (data_available && !is_shutting_down()) {
 2713    if (!strm.wait_writable()) {
 2714      error = Error::Write;
 2715      return false;
 2716    } else if (!content_provider(offset, 0, data_sink)) {
 2717      error = Error::Canceled;
 2718      return false;
 2719    } else if (!ok) {
 2720      error = Error::Write;
 2721      return false;
 2722    }
 2723  }
 2724
 2725  error = Error::Success;
 2726  return true;
 2727}
 2728
 2729template <typename T, typename U>
 2730bool write_content_chunked(Stream &strm,
 2731                                  const ContentProvider &content_provider,
 2732                                  const T &is_shutting_down, U &compressor) {
 2733  auto error = Error::Success;
 2734  return write_content_chunked(strm, content_provider, is_shutting_down,
 2735                               compressor, error);
 2736}
 2737
 2738template <typename T>
 2739bool redirect(T &cli, Request &req, Response &res,
 2740                     const std::string &path, const std::string &location,
 2741                     Error &error) {
 2742  Request new_req = req;
 2743  new_req.path = path;
 2744  new_req.redirect_count_ -= 1;
 2745
 2746  if (res.status == StatusCode::SeeOther_303 &&
 2747      (req.method != "GET" && req.method != "HEAD")) {
 2748    new_req.method = "GET";
 2749    new_req.body.clear();
 2750    new_req.headers.clear();
 2751  }
 2752
 2753  Response new_res;
 2754
 2755  auto ret = cli.send(new_req, new_res, error);
 2756  if (ret) {
 2757    req = std::move(new_req);
 2758    res = std::move(new_res);
 2759
 2760    if (res.location.empty()) { res.location = location; }
 2761  }
 2762  return ret;
 2763}
 2764
 2765std::string params_to_query_str(const Params &params) {
 2766  std::string query;
 2767
 2768  for (auto it = params.begin(); it != params.end(); ++it) {
 2769    if (it != params.begin()) { query += '&'; }
 2770    query += encode_query_component(it->first);
 2771    query += '=';
 2772    query += encode_query_component(it->second);
 2773  }
 2774  return query;
 2775}
 2776
 2777void parse_query_text(const char *data, std::size_t size,
 2778                             Params &params) {
 2779  std::set<std::string> cache;
 2780  split(data, data + size, '&', [&](const char *b, const char *e) {
 2781    std::string kv(b, e);
 2782    if (cache.find(kv) != cache.end()) { return; }
 2783    cache.insert(std::move(kv));
 2784
 2785    std::string key;
 2786    std::string val;
 2787    divide(b, static_cast<std::size_t>(e - b), '=',
 2788           [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
 2789               std::size_t rhs_size) {
 2790             key.assign(lhs_data, lhs_size);
 2791             val.assign(rhs_data, rhs_size);
 2792           });
 2793
 2794    if (!key.empty()) {
 2795      params.emplace(decode_query_component(key), decode_query_component(val));
 2796    }
 2797  });
 2798}
 2799
 2800void parse_query_text(const std::string &s, Params &params) {
 2801  parse_query_text(s.data(), s.size(), params);
 2802}
 2803
 2804// Normalize a query string by decoding and re-encoding each key/value pair
 2805// while preserving the original parameter order. This avoids double-encoding
 2806// and ensures consistent encoding without reordering (unlike Params which
 2807// uses std::multimap and sorts keys).
 2808std::string normalize_query_string(const std::string &query) {
 2809  std::string result;
 2810  split(query.data(), query.data() + query.size(), '&',
 2811        [&](const char *b, const char *e) {
 2812          std::string key;
 2813          std::string val;
 2814          divide(b, static_cast<std::size_t>(e - b), '=',
 2815                 [&](const char *lhs_data, std::size_t lhs_size,
 2816                     const char *rhs_data, std::size_t rhs_size) {
 2817                   key.assign(lhs_data, lhs_size);
 2818                   val.assign(rhs_data, rhs_size);
 2819                 });
 2820
 2821          if (!key.empty()) {
 2822            auto dec_key = decode_query_component(key);
 2823            auto dec_val = decode_query_component(val);
 2824
 2825            if (!result.empty()) { result += '&'; }
 2826            result += encode_query_component(dec_key);
 2827            if (!val.empty() || std::find(b, e, '=') != e) {
 2828              result += '=';
 2829              result += encode_query_component(dec_val);
 2830            }
 2831          }
 2832        });
 2833  return result;
 2834}
 2835
 2836bool parse_multipart_boundary(const std::string &content_type,
 2837                                     std::string &boundary) {
 2838  auto boundary_keyword = "boundary=";
 2839  auto pos = content_type.find(boundary_keyword);
 2840  if (pos == std::string::npos) { return false; }
 2841  auto end = content_type.find(';', pos);
 2842  auto beg = pos + strlen(boundary_keyword);
 2843  boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
 2844  return !boundary.empty();
 2845}
 2846
 2847void parse_disposition_params(const std::string &s, Params &params) {
 2848  std::set<std::string> cache;
 2849  split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {
 2850    std::string kv(b, e);
 2851    if (cache.find(kv) != cache.end()) { return; }
 2852    cache.insert(kv);
 2853
 2854    std::string key;
 2855    std::string val;
 2856    split(b, e, '=', [&](const char *b2, const char *e2) {
 2857      if (key.empty()) {
 2858        key.assign(b2, e2);
 2859      } else {
 2860        val.assign(b2, e2);
 2861      }
 2862    });
 2863
 2864    if (!key.empty()) {
 2865      params.emplace(trim_double_quotes_copy((key)),
 2866                     trim_double_quotes_copy((val)));
 2867    }
 2868  });
 2869}
 2870
 2871#ifdef CPPHTTPLIB_NO_EXCEPTIONS
 2872bool parse_range_header(const std::string &s, Ranges &ranges) {
 2873#else
 2874bool parse_range_header(const std::string &s, Ranges &ranges) try {
 2875#endif
 2876  auto is_valid = [](const std::string &str) {
 2877    return std::all_of(str.cbegin(), str.cend(),
 2878                       [](unsigned char c) { return std::isdigit(c); });
 2879  };
 2880
 2881  if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) {
 2882    const auto pos = static_cast<size_t>(6);
 2883    const auto len = static_cast<size_t>(s.size() - 6);
 2884    auto all_valid_ranges = true;
 2885    split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
 2886      if (!all_valid_ranges) { return; }
 2887
 2888      const auto it = std::find(b, e, '-');
 2889      if (it == e) {
 2890        all_valid_ranges = false;
 2891        return;
 2892      }
 2893
 2894      const auto lhs = std::string(b, it);
 2895      const auto rhs = std::string(it + 1, e);
 2896      if (!is_valid(lhs) || !is_valid(rhs)) {
 2897        all_valid_ranges = false;
 2898        return;
 2899      }
 2900
 2901      ssize_t first = -1;
 2902      if (!lhs.empty()) {
 2903        ssize_t v;
 2904        auto res = detail::from_chars(lhs.data(), lhs.data() + lhs.size(), v);
 2905        if (res.ec == std::errc{}) { first = v; }
 2906      }
 2907
 2908      ssize_t last = -1;
 2909      if (!rhs.empty()) {
 2910        ssize_t v;
 2911        auto res = detail::from_chars(rhs.data(), rhs.data() + rhs.size(), v);
 2912        if (res.ec == std::errc{}) { last = v; }
 2913      }
 2914
 2915      if ((first == -1 && last == -1) ||
 2916          (first != -1 && last != -1 && first > last)) {
 2917        all_valid_ranges = false;
 2918        return;
 2919      }
 2920
 2921      ranges.emplace_back(first, last);
 2922    });
 2923    return all_valid_ranges && !ranges.empty();
 2924  }
 2925  return false;
 2926#ifdef CPPHTTPLIB_NO_EXCEPTIONS
 2927}
 2928#else
 2929} catch (...) { return false; }
 2930#endif
 2931
 2932bool parse_accept_header(const std::string &s,
 2933                                std::vector<std::string> &content_types) {
 2934  content_types.clear();
 2935
 2936  // Empty string is considered valid (no preference)
 2937  if (s.empty()) { return true; }
 2938
 2939  // Check for invalid patterns: leading/trailing commas or consecutive commas
 2940  if (s.front() == ',' || s.back() == ',' ||
 2941      s.find(",,") != std::string::npos) {
 2942    return false;
 2943  }
 2944
 2945  struct AcceptEntry {
 2946    std::string media_type;
 2947    double quality;
 2948    int order; // Original order in header
 2949  };
 2950
 2951  std::vector<AcceptEntry> entries;
 2952  int order = 0;
 2953  bool has_invalid_entry = false;
 2954
 2955  // Split by comma and parse each entry
 2956  split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e) {
 2957    std::string entry(b, e);
 2958    entry = trim_copy(entry);
 2959
 2960    if (entry.empty()) {
 2961      has_invalid_entry = true;
 2962      return;
 2963    }
 2964
 2965    AcceptEntry accept_entry;
 2966    accept_entry.quality = 1.0; // Default quality
 2967    accept_entry.order = order++;
 2968
 2969    // Find q= parameter
 2970    auto q_pos = entry.find(";q=");
 2971    if (q_pos == std::string::npos) { q_pos = entry.find("; q="); }
 2972
 2973    if (q_pos != std::string::npos) {
 2974      // Extract media type (before q parameter)
 2975      accept_entry.media_type = trim_copy(entry.substr(0, q_pos));
 2976
 2977      // Extract quality value
 2978      auto q_start = entry.find('=', q_pos) + 1;
 2979      auto q_end = entry.find(';', q_start);
 2980      if (q_end == std::string::npos) { q_end = entry.length(); }
 2981
 2982      std::string quality_str =
 2983          trim_copy(entry.substr(q_start, q_end - q_start));
 2984      if (quality_str.empty()) {
 2985        has_invalid_entry = true;
 2986        return;
 2987      }
 2988
 2989      {
 2990        double v = 0.0;
 2991        auto res = detail::from_chars(
 2992            quality_str.data(), quality_str.data() + quality_str.size(), v);
 2993        if (res.ec == std::errc{}) {
 2994          accept_entry.quality = v;
 2995        } else {
 2996          has_invalid_entry = true;
 2997          return;
 2998        }
 2999      }
 3000      // Check if quality is in valid range [0.0, 1.0]
 3001      if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0) {
 3002        has_invalid_entry = true;
 3003        return;
 3004      }
 3005    } else {
 3006      // No quality parameter, use entire entry as media type
 3007      accept_entry.media_type = entry;
 3008    }
 3009
 3010    // Remove additional parameters from media type
 3011    auto param_pos = accept_entry.media_type.find(';');
 3012    if (param_pos != std::string::npos) {
 3013      accept_entry.media_type =
 3014          trim_copy(accept_entry.media_type.substr(0, param_pos));
 3015    }
 3016
 3017    // Basic validation of media type format
 3018    if (accept_entry.media_type.empty()) {
 3019      has_invalid_entry = true;
 3020      return;
 3021    }
 3022
 3023    // Check for basic media type format (should contain '/' or be '*')
 3024    if (accept_entry.media_type != "*" &&
 3025        accept_entry.media_type.find('/') == std::string::npos) {
 3026      has_invalid_entry = true;
 3027      return;
 3028    }
 3029
 3030    entries.push_back(std::move(accept_entry));
 3031  });
 3032
 3033  // Return false if any invalid entry was found
 3034  if (has_invalid_entry) { return false; }
 3035
 3036  // Sort by quality (descending), then by original order (ascending)
 3037  std::sort(entries.begin(), entries.end(),
 3038            [](const AcceptEntry &a, const AcceptEntry &b) {
 3039              if (a.quality != b.quality) {
 3040                return a.quality > b.quality; // Higher quality first
 3041              }
 3042              return a.order < b.order; // Earlier order first for same quality
 3043            });
 3044
 3045  // Extract sorted media types
 3046  content_types.reserve(entries.size());
 3047  for (auto &entry : entries) {
 3048    content_types.push_back(std::move(entry.media_type));
 3049  }
 3050
 3051  return true;
 3052}
 3053
 3054class FormDataParser {
 3055public:
 3056  FormDataParser() = default;
 3057
 3058  void set_boundary(std::string &&boundary) {
 3059    boundary_ = std::move(boundary);
 3060    dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
 3061    crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
 3062  }
 3063
 3064  bool is_valid() const { return is_valid_; }
 3065
 3066  bool parse(const char *buf, size_t n, const FormDataHeader &header_callback,
 3067             const ContentReceiver &content_callback) {
 3068
 3069    buf_append(buf, n);
 3070
 3071    while (buf_size() > 0) {
 3072      switch (state_) {
 3073      case 0: { // Initial boundary
 3074        auto pos = buf_find(dash_boundary_crlf_);
 3075        if (pos == buf_size()) { return true; }
 3076        buf_erase(pos + dash_boundary_crlf_.size());
 3077        state_ = 1;
 3078        break;
 3079      }
 3080      case 1: { // New entry
 3081        clear_file_info();
 3082        state_ = 2;
 3083        break;
 3084      }
 3085      case 2: { // Headers
 3086        auto pos = buf_find(crlf_);
 3087        if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
 3088        while (pos < buf_size()) {
 3089          // Empty line
 3090          if (pos == 0) {
 3091            if (!header_callback(file_)) {
 3092              is_valid_ = false;
 3093              return false;
 3094            }
 3095            buf_erase(crlf_.size());
 3096            state_ = 3;
 3097            break;
 3098          }
 3099
 3100          const auto header = buf_head(pos);
 3101
 3102          if (!parse_header(header.data(), header.data() + header.size(),
 3103                            [&](const std::string &, const std::string &) {})) {
 3104            is_valid_ = false;
 3105            return false;
 3106          }
 3107
 3108          // Parse and emplace space trimmed headers into a map
 3109          if (!parse_header(
 3110                  header.data(), header.data() + header.size(),
 3111                  [&](const std::string &key, const std::string &val) {
 3112                    file_.headers.emplace(key, val);
 3113                  })) {
 3114            is_valid_ = false;
 3115            return false;
 3116          }
 3117
 3118          constexpr const char header_content_type[] = "Content-Type:";
 3119
 3120          if (start_with_case_ignore(header, header_content_type)) {
 3121            file_.content_type =
 3122                trim_copy(header.substr(str_len(header_content_type)));
 3123          } else {
 3124            thread_local const std::regex re_content_disposition(
 3125                R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
 3126                std::regex_constants::icase);
 3127
 3128            std::smatch m;
 3129            if (std::regex_match(header, m, re_content_disposition)) {
 3130              Params params;
 3131              parse_disposition_params(m[1], params);
 3132
 3133              auto it = params.find("name");
 3134              if (it != params.end()) {
 3135                file_.name = it->second;
 3136              } else {
 3137                is_valid_ = false;
 3138                return false;
 3139              }
 3140
 3141              it = params.find("filename");
 3142              if (it != params.end()) { file_.filename = it->second; }
 3143
 3144              it = params.find("filename*");
 3145              if (it != params.end()) {
 3146                // Only allow UTF-8 encoding...
 3147                thread_local const std::regex re_rfc5987_encoding(
 3148                    R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
 3149
 3150                std::smatch m2;
 3151                if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
 3152                  file_.filename = decode_path_component(m2[1]); // override...
 3153                } else {
 3154                  is_valid_ = false;
 3155                  return false;
 3156                }
 3157              }
 3158            }
 3159          }
 3160          buf_erase(pos + crlf_.size());
 3161          pos = buf_find(crlf_);
 3162        }
 3163        if (state_ != 3) { return true; }
 3164        break;
 3165      }
 3166      case 3: { // Body
 3167        if (crlf_dash_boundary_.size() > buf_size()) { return true; }
 3168        auto pos = buf_find(crlf_dash_boundary_);
 3169        if (pos < buf_size()) {
 3170          if (!content_callback(buf_data(), pos)) {
 3171            is_valid_ = false;
 3172            return false;
 3173          }
 3174          buf_erase(pos + crlf_dash_boundary_.size());
 3175          state_ = 4;
 3176        } else {
 3177          auto len = buf_size() - crlf_dash_boundary_.size();
 3178          if (len > 0) {
 3179            if (!content_callback(buf_data(), len)) {
 3180              is_valid_ = false;
 3181              return false;
 3182            }
 3183            buf_erase(len);
 3184          }
 3185          return true;
 3186        }
 3187        break;
 3188      }
 3189      case 4: { // Boundary
 3190        if (crlf_.size() > buf_size()) { return true; }
 3191        if (buf_start_with(crlf_)) {
 3192          buf_erase(crlf_.size());
 3193          state_ = 1;
 3194        } else {
 3195          if (dash_.size() > buf_size()) { return true; }
 3196          if (buf_start_with(dash_)) {
 3197            buf_erase(dash_.size());
 3198            is_valid_ = true;
 3199            buf_erase(buf_size()); // Remove epilogue
 3200          } else {
 3201            return true;
 3202          }
 3203        }
 3204        break;
 3205      }
 3206      }
 3207    }
 3208
 3209    return true;
 3210  }
 3211
 3212private:
 3213  void clear_file_info() {
 3214    file_.name.clear();
 3215    file_.filename.clear();
 3216    file_.content_type.clear();
 3217    file_.headers.clear();
 3218  }
 3219
 3220  bool start_with_case_ignore(const std::string &a, const char *b) const {
 3221    const auto b_len = strlen(b);
 3222    if (a.size() < b_len) { return false; }
 3223    for (size_t i = 0; i < b_len; i++) {
 3224      if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
 3225        return false;
 3226      }
 3227    }
 3228    return true;
 3229  }
 3230
 3231  const std::string dash_ = "--";
 3232  const std::string crlf_ = "\r\n";
 3233  std::string boundary_;
 3234  std::string dash_boundary_crlf_;
 3235  std::string crlf_dash_boundary_;
 3236
 3237  size_t state_ = 0;
 3238  bool is_valid_ = false;
 3239  FormData file_;
 3240
 3241  // Buffer
 3242  bool start_with(const std::string &a, size_t spos, size_t epos,
 3243                  const std::string &b) const {
 3244    if (epos - spos < b.size()) { return false; }
 3245    for (size_t i = 0; i < b.size(); i++) {
 3246      if (a[i + spos] != b[i]) { return false; }
 3247    }
 3248    return true;
 3249  }
 3250
 3251  size_t buf_size() const { return buf_epos_ - buf_spos_; }
 3252
 3253  const char *buf_data() const { return &buf_[buf_spos_]; }
 3254
 3255  std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
 3256
 3257  bool buf_start_with(const std::string &s) const {
 3258    return start_with(buf_, buf_spos_, buf_epos_, s);
 3259  }
 3260
 3261  size_t buf_find(const std::string &s) const {
 3262    auto c = s.front();
 3263
 3264    size_t off = buf_spos_;
 3265    while (off < buf_epos_) {
 3266      auto pos = off;
 3267      while (true) {
 3268        if (pos == buf_epos_) { return buf_size(); }
 3269        if (buf_[pos] == c) { break; }
 3270        pos++;
 3271      }
 3272
 3273      auto remaining_size = buf_epos_ - pos;
 3274      if (s.size() > remaining_size) { return buf_size(); }
 3275
 3276      if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
 3277
 3278      off = pos + 1;
 3279    }
 3280
 3281    return buf_size();
 3282  }
 3283
 3284  void buf_append(const char *data, size_t n) {
 3285    auto remaining_size = buf_size();
 3286    if (remaining_size > 0 && buf_spos_ > 0) {
 3287      for (size_t i = 0; i < remaining_size; i++) {
 3288        buf_[i] = buf_[buf_spos_ + i];
 3289      }
 3290    }
 3291    buf_spos_ = 0;
 3292    buf_epos_ = remaining_size;
 3293
 3294    if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
 3295
 3296    for (size_t i = 0; i < n; i++) {
 3297      buf_[buf_epos_ + i] = data[i];
 3298    }
 3299    buf_epos_ += n;
 3300  }
 3301
 3302  void buf_erase(size_t size) { buf_spos_ += size; }
 3303
 3304  std::string buf_;
 3305  size_t buf_spos_ = 0;
 3306  size_t buf_epos_ = 0;
 3307};
 3308
 3309std::string random_string(size_t length) {
 3310  constexpr const char data[] =
 3311      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 3312
 3313  thread_local auto engine([]() {
 3314    // std::random_device might actually be deterministic on some
 3315    // platforms, but due to lack of support in the c++ standard library,
 3316    // doing better requires either some ugly hacks or breaking portability.
 3317    std::random_device seed_gen;
 3318    // Request 128 bits of entropy for initialization
 3319    std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
 3320    return std::mt19937(seed_sequence);
 3321  }());
 3322
 3323  std::string result;
 3324  for (size_t i = 0; i < length; i++) {
 3325    result += data[engine() % (sizeof(data) - 1)];
 3326  }
 3327  return result;
 3328}
 3329
 3330std::string make_multipart_data_boundary() {
 3331  return "--cpp-httplib-multipart-data-" + detail::random_string(16);
 3332}
 3333
 3334bool is_multipart_boundary_chars_valid(const std::string &boundary) {
 3335  auto valid = true;
 3336  for (size_t i = 0; i < boundary.size(); i++) {
 3337    auto c = boundary[i];
 3338    if (!std::isalnum(c) && c != '-' && c != '_') {
 3339      valid = false;
 3340      break;
 3341    }
 3342  }
 3343  return valid;
 3344}
 3345
 3346template <typename T>
 3347std::string
 3348serialize_multipart_formdata_item_begin(const T &item,
 3349                                        const std::string &boundary) {
 3350  std::string body = "--" + boundary + "\r\n";
 3351  body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
 3352  if (!item.filename.empty()) {
 3353    body += "; filename=\"" + item.filename + "\"";
 3354  }
 3355  body += "\r\n";
 3356  if (!item.content_type.empty()) {
 3357    body += "Content-Type: " + item.content_type + "\r\n";
 3358  }
 3359  body += "\r\n";
 3360
 3361  return body;
 3362}
 3363
 3364std::string serialize_multipart_formdata_item_end() { return "\r\n"; }
 3365
 3366std::string
 3367serialize_multipart_formdata_finish(const std::string &boundary) {
 3368  return "--" + boundary + "--\r\n";
 3369}
 3370
 3371std::string
 3372serialize_multipart_formdata_get_content_type(const std::string &boundary) {
 3373  return "multipart/form-data; boundary=" + boundary;
 3374}
 3375
 3376std::string
 3377serialize_multipart_formdata(const UploadFormDataItems &items,
 3378                             const std::string &boundary, bool finish = true) {
 3379  std::string body;
 3380
 3381  for (const auto &item : items) {
 3382    body += serialize_multipart_formdata_item_begin(item, boundary);
 3383    body += item.content + serialize_multipart_formdata_item_end();
 3384  }
 3385
 3386  if (finish) { body += serialize_multipart_formdata_finish(boundary); }
 3387
 3388  return body;
 3389}
 3390
 3391void coalesce_ranges(Ranges &ranges, size_t content_length) {
 3392  if (ranges.size() <= 1) return;
 3393
 3394  // Sort ranges by start position
 3395  std::sort(ranges.begin(), ranges.end(),
 3396            [](const Range &a, const Range &b) { return a.first < b.first; });
 3397
 3398  Ranges coalesced;
 3399  coalesced.reserve(ranges.size());
 3400
 3401  for (auto &r : ranges) {
 3402    auto first_pos = r.first;
 3403    auto last_pos = r.second;
 3404
 3405    // Handle special cases like in range_error
 3406    if (first_pos == -1 && last_pos == -1) {
 3407      first_pos = 0;
 3408      last_pos = static_cast<ssize_t>(content_length);
 3409    }
 3410
 3411    if (first_pos == -1) {
 3412      first_pos = static_cast<ssize_t>(content_length) - last_pos;
 3413      last_pos = static_cast<ssize_t>(content_length) - 1;
 3414    }
 3415
 3416    if (last_pos == -1 || last_pos >= static_cast<ssize_t>(content_length)) {
 3417      last_pos = static_cast<ssize_t>(content_length) - 1;
 3418    }
 3419
 3420    // Skip invalid ranges
 3421    if (!(0 <= first_pos && first_pos <= last_pos &&
 3422          last_pos < static_cast<ssize_t>(content_length))) {
 3423      continue;
 3424    }
 3425
 3426    // Coalesce with previous range if overlapping or adjacent (but not
 3427    // identical)
 3428    if (!coalesced.empty()) {
 3429      auto &prev = coalesced.back();
 3430      // Check if current range overlaps or is adjacent to previous range
 3431      // but don't coalesce identical ranges (allow duplicates)
 3432      if (first_pos <= prev.second + 1 &&
 3433          !(first_pos == prev.first && last_pos == prev.second)) {
 3434        // Extend the previous range
 3435        prev.second = (std::max)(prev.second, last_pos);
 3436        continue;
 3437      }
 3438    }
 3439
 3440    // Add new range
 3441    coalesced.emplace_back(first_pos, last_pos);
 3442  }
 3443
 3444  ranges = std::move(coalesced);
 3445}
 3446
 3447bool range_error(Request &req, Response &res) {
 3448  if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
 3449    ssize_t content_len = static_cast<ssize_t>(
 3450        res.content_length_ ? res.content_length_ : res.body.size());
 3451
 3452    std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
 3453    size_t overwrapping_count = 0;
 3454
 3455    // NOTE: The following Range check is based on '14.2. Range' in RFC 9110
 3456    // 'HTTP Semantics' to avoid potential denial-of-service attacks.
 3457    // https://www.rfc-editor.org/rfc/rfc9110#section-14.2
 3458
 3459    // Too many ranges
 3460    if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; }
 3461
 3462    for (auto &r : req.ranges) {
 3463      auto &first_pos = r.first;
 3464      auto &last_pos = r.second;
 3465
 3466      if (first_pos == -1 && last_pos == -1) {
 3467        first_pos = 0;
 3468        last_pos = content_len;
 3469      }
 3470
 3471      if (first_pos == -1) {
 3472        first_pos = content_len - last_pos;
 3473        last_pos = content_len - 1;
 3474      }
 3475
 3476      // NOTE: RFC-9110 '14.1.2. Byte Ranges':
 3477      // A client can limit the number of bytes requested without knowing the
 3478      // size of the selected representation. If the last-pos value is absent,
 3479      // or if the value is greater than or equal to the current length of the
 3480      // representation data, the byte range is interpreted as the remainder of
 3481      // the representation (i.e., the server replaces the value of last-pos
 3482      // with a value that is one less than the current length of the selected
 3483      // representation).
 3484      // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6
 3485      if (last_pos == -1 || last_pos >= content_len) {
 3486        last_pos = content_len - 1;
 3487      }
 3488
 3489      // Range must be within content length
 3490      if (!(0 <= first_pos && first_pos <= last_pos &&
 3491            last_pos <= content_len - 1)) {
 3492        return true;
 3493      }
 3494
 3495      // Request must not have more than two overlapping ranges
 3496      for (const auto &processed_range : processed_ranges) {
 3497        if (!(last_pos < processed_range.first ||
 3498              first_pos > processed_range.second)) {
 3499          overwrapping_count++;
 3500          if (overwrapping_count > 2) { return true; }
 3501          break; // Only count once per range
 3502        }
 3503      }
 3504
 3505      processed_ranges.emplace_back(first_pos, last_pos);
 3506    }
 3507
 3508    // After validation, coalesce overlapping ranges as per RFC 9110
 3509    coalesce_ranges(req.ranges, static_cast<size_t>(content_len));
 3510  }
 3511
 3512  return false;
 3513}
 3514
 3515std::pair<size_t, size_t>
 3516get_range_offset_and_length(Range r, size_t content_length) {
 3517  assert(r.first != -1 && r.second != -1);
 3518  assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
 3519  assert(r.first <= r.second &&
 3520         r.second < static_cast<ssize_t>(content_length));
 3521  (void)(content_length);
 3522  return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
 3523}
 3524
 3525std::string make_content_range_header_field(
 3526    const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {
 3527  auto st = offset_and_length.first;
 3528  auto ed = st + offset_and_length.second - 1;
 3529
 3530  std::string field = "bytes ";
 3531  field += std::to_string(st);
 3532  field += '-';
 3533  field += std::to_string(ed);
 3534  field += '/';
 3535  field += std::to_string(content_length);
 3536  return field;
 3537}
 3538
 3539template <typename SToken, typename CToken, typename Content>
 3540bool process_multipart_ranges_data(const Request &req,
 3541                                   const std::string &boundary,
 3542                                   const std::string &content_type,
 3543                                   size_t content_length, SToken stoken,
 3544                                   CToken ctoken, Content content) {
 3545  for (size_t i = 0; i < req.ranges.size(); i++) {
 3546    ctoken("--");
 3547    stoken(boundary);
 3548    ctoken("\r\n");
 3549    if (!content_type.empty()) {
 3550      ctoken("Content-Type: ");
 3551      stoken(content_type);
 3552      ctoken("\r\n");
 3553    }
 3554
 3555    auto offset_and_length =
 3556        get_range_offset_and_length(req.ranges[i], content_length);
 3557
 3558    ctoken("Content-Range: ");
 3559    stoken(make_content_range_header_field(offset_and_length, content_length));
 3560    ctoken("\r\n");
 3561    ctoken("\r\n");
 3562
 3563    if (!content(offset_and_length.first, offset_and_length.second)) {
 3564      return false;
 3565    }
 3566    ctoken("\r\n");
 3567  }
 3568
 3569  ctoken("--");
 3570  stoken(boundary);
 3571  ctoken("--");
 3572
 3573  return true;
 3574}
 3575
 3576void make_multipart_ranges_data(const Request &req, Response &res,
 3577                                       const std::string &boundary,
 3578                                       const std::string &content_type,
 3579                                       size_t content_length,
 3580                                       std::string &data) {
 3581  process_multipart_ranges_data(
 3582      req, boundary, content_type, content_length,
 3583      [&](const std::string &token) { data += token; },
 3584      [&](const std::string &token) { data += token; },
 3585      [&](size_t offset, size_t length) {
 3586        assert(offset + length <= content_length);
 3587        data += res.body.substr(offset, length);
 3588        return true;
 3589      });
 3590}
 3591
 3592size_t get_multipart_ranges_data_length(const Request &req,
 3593                                               const std::string &boundary,
 3594                                               const std::string &content_type,
 3595                                               size_t content_length) {
 3596  size_t data_length = 0;
 3597
 3598  process_multipart_ranges_data(
 3599      req, boundary, content_type, content_length,
 3600      [&](const std::string &token) { data_length += token.size(); },
 3601      [&](const std::string &token) { data_length += token.size(); },
 3602      [&](size_t /*offset*/, size_t length) {
 3603        data_length += length;
 3604        return true;
 3605      });
 3606
 3607  return data_length;
 3608}
 3609
 3610template <typename T>
 3611bool
 3612write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
 3613                            const std::string &boundary,
 3614                            const std::string &content_type,
 3615                            size_t content_length, const T &is_shutting_down) {
 3616  return process_multipart_ranges_data(
 3617      req, boundary, content_type, content_length,
 3618      [&](const std::string &token) { strm.write(token); },
 3619      [&](const std::string &token) { strm.write(token); },
 3620      [&](size_t offset, size_t length) {
 3621        return write_content(strm, res.content_provider_, offset, length,
 3622                             is_shutting_down);
 3623      });
 3624}
 3625
 3626bool expect_content(const Request &req) {
 3627  if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
 3628      req.method == "DELETE") {
 3629    return true;
 3630  }
 3631  if (req.has_header("Content-Length") &&
 3632      req.get_header_value_u64("Content-Length") > 0) {
 3633    return true;
 3634  }
 3635  if (is_chunked_transfer_encoding(req.headers)) { return true; }
 3636  return false;
 3637}
 3638
 3639bool has_crlf(const std::string &s) {
 3640  auto p = s.c_str();
 3641  while (*p) {
 3642    if (*p == '\r' || *p == '\n') { return true; }
 3643    p++;
 3644  }
 3645  return false;
 3646}
 3647
 3648#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 3649std::string message_digest(const std::string &s, const EVP_MD *algo) {
 3650  auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
 3651      EVP_MD_CTX_new(), EVP_MD_CTX_free);
 3652
 3653  unsigned int hash_length = 0;
 3654  unsigned char hash[EVP_MAX_MD_SIZE];
 3655
 3656  EVP_DigestInit_ex(context.get(), algo, nullptr);
 3657  EVP_DigestUpdate(context.get(), s.c_str(), s.size());
 3658  EVP_DigestFinal_ex(context.get(), hash, &hash_length);
 3659
 3660  std::stringstream ss;
 3661  for (auto i = 0u; i < hash_length; ++i) {
 3662    ss << std::hex << std::setw(2) << std::setfill('0')
 3663       << static_cast<unsigned int>(hash[i]);
 3664  }
 3665
 3666  return ss.str();
 3667}
 3668
 3669std::string MD5(const std::string &s) {
 3670  return message_digest(s, EVP_md5());
 3671}
 3672
 3673std::string SHA_256(const std::string &s) {
 3674  return message_digest(s, EVP_sha256());
 3675}
 3676
 3677std::string SHA_512(const std::string &s) {
 3678  return message_digest(s, EVP_sha512());
 3679}
 3680
 3681std::pair<std::string, std::string> make_digest_authentication_header(
 3682    const Request &req, const std::map<std::string, std::string> &auth,
 3683    size_t cnonce_count, const std::string &cnonce, const std::string &username,
 3684    const std::string &password, bool is_proxy = false) {
 3685  std::string nc;
 3686  {
 3687    std::stringstream ss;
 3688    ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
 3689    nc = ss.str();
 3690  }
 3691
 3692  std::string qop;
 3693  if (auth.find("qop") != auth.end()) {
 3694    qop = auth.at("qop");
 3695    if (qop.find("auth-int") != std::string::npos) {
 3696      qop = "auth-int";
 3697    } else if (qop.find("auth") != std::string::npos) {
 3698      qop = "auth";
 3699    } else {
 3700      qop.clear();
 3701    }
 3702  }
 3703
 3704  std::string algo = "MD5";
 3705  if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
 3706
 3707  std::string response;
 3708  {
 3709    auto H = algo == "SHA-256"   ? detail::SHA_256
 3710             : algo == "SHA-512" ? detail::SHA_512
 3711                                 : detail::MD5;
 3712
 3713    auto A1 = username + ":" + auth.at("realm") + ":" + password;
 3714
 3715    auto A2 = req.method + ":" + req.path;
 3716    if (qop == "auth-int") { A2 += ":" + H(req.body); }
 3717
 3718    if (qop.empty()) {
 3719      response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
 3720    } else {
 3721      response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
 3722                   ":" + qop + ":" + H(A2));
 3723    }
 3724  }
 3725
 3726  auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
 3727
 3728  auto field = "Digest username=\"" + username + "\", realm=\"" +
 3729               auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
 3730               "\", uri=\"" + req.path + "\", algorithm=" + algo +
 3731               (qop.empty() ? ", response=\""
 3732                            : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
 3733                                  cnonce + "\", response=\"") +
 3734               response + "\"" +
 3735               (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
 3736
 3737  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
 3738  return std::make_pair(key, field);
 3739}
 3740
 3741bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
 3742  detail::set_nonblocking(sock, true);
 3743  auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
 3744
 3745  char buf[1];
 3746  return !SSL_peek(ssl, buf, 1) &&
 3747         SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
 3748}
 3749
 3750#ifdef _WIN32
 3751// NOTE: This code came up with the following stackoverflow post:
 3752// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
 3753bool load_system_certs_on_windows(X509_STORE *store) {
 3754  auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
 3755  if (!hStore) { return false; }
 3756
 3757  auto result = false;
 3758  PCCERT_CONTEXT pContext = NULL;
 3759  while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
 3760         nullptr) {
 3761    auto encoded_cert =
 3762        static_cast<const unsigned char *>(pContext->pbCertEncoded);
 3763
 3764    auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
 3765    if (x509) {
 3766      X509_STORE_add_cert(store, x509);
 3767      X509_free(x509);
 3768      result = true;
 3769    }
 3770  }
 3771
 3772  CertFreeCertificateContext(pContext);
 3773  CertCloseStore(hStore, 0);
 3774
 3775  return result;
 3776}
 3777#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
 3778template <typename T>
 3779using CFObjectPtr =
 3780    std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
 3781
 3782void cf_object_ptr_deleter(CFTypeRef obj) {
 3783  if (obj) { CFRelease(obj); }
 3784}
 3785
 3786bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
 3787  CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
 3788  CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
 3789                        kCFBooleanTrue};
 3790
 3791  CFObjectPtr<CFDictionaryRef> query(
 3792      CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
 3793                         sizeof(keys) / sizeof(keys[0]),
 3794                         &kCFTypeDictionaryKeyCallBacks,
 3795                         &kCFTypeDictionaryValueCallBacks),
 3796      cf_object_ptr_deleter);
 3797
 3798  if (!query) { return false; }
 3799
 3800  CFTypeRef security_items = nullptr;
 3801  if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
 3802      CFArrayGetTypeID() != CFGetTypeID(security_items)) {
 3803    return false;
 3804  }
 3805
 3806  certs.reset(reinterpret_cast<CFArrayRef>(security_items));
 3807  return true;
 3808}
 3809
 3810bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
 3811  CFArrayRef root_security_items = nullptr;
 3812  if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
 3813    return false;
 3814  }
 3815
 3816  certs.reset(root_security_items);
 3817  return true;
 3818}
 3819
 3820bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
 3821  auto result = false;
 3822  for (auto i = 0; i < CFArrayGetCount(certs); ++i) {
 3823    const auto cert = reinterpret_cast<const __SecCertificate *>(
 3824        CFArrayGetValueAtIndex(certs, i));
 3825
 3826    if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
 3827
 3828    CFDataRef cert_data = nullptr;
 3829    if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
 3830        errSecSuccess) {
 3831      continue;
 3832    }
 3833
 3834    CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
 3835
 3836    auto encoded_cert = static_cast<const unsigned char *>(
 3837        CFDataGetBytePtr(cert_data_ptr.get()));
 3838
 3839    auto x509 =
 3840        d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
 3841
 3842    if (x509) {
 3843      X509_STORE_add_cert(store, x509);
 3844      X509_free(x509);
 3845      result = true;
 3846    }
 3847  }
 3848
 3849  return result;
 3850}
 3851
 3852bool load_system_certs_on_macos(X509_STORE *store) {
 3853  auto result = false;
 3854  CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
 3855  if (retrieve_certs_from_keychain(certs) && certs) {
 3856    result = add_certs_to_x509_store(certs.get(), store);
 3857  }
 3858
 3859  if (retrieve_root_certs_from_keychain(certs) && certs) {
 3860    result = add_certs_to_x509_store(certs.get(), store) || result;
 3861  }
 3862
 3863  return result;
 3864}
 3865#endif // _WIN32
 3866#endif // CPPHTTPLIB_OPENSSL_SUPPORT
 3867
 3868#ifdef _WIN32
 3869class WSInit {
 3870public:
 3871  WSInit() {
 3872    WSADATA wsaData;
 3873    if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
 3874  }
 3875
 3876  ~WSInit() {
 3877    if (is_valid_) WSACleanup();
 3878  }
 3879
 3880  bool is_valid_ = false;
 3881};
 3882
 3883static WSInit wsinit_;
 3884#endif
 3885
 3886bool parse_www_authenticate(const Response &res,
 3887                                   std::map<std::string, std::string> &auth,
 3888                                   bool is_proxy) {
 3889  auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
 3890  if (res.has_header(auth_key)) {
 3891    thread_local auto re =
 3892        std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
 3893    auto s = res.get_header_value(auth_key);
 3894    auto pos = s.find(' ');
 3895    if (pos != std::string::npos) {
 3896      auto type = s.substr(0, pos);
 3897      if (type == "Basic") {
 3898        return false;
 3899      } else if (type == "Digest") {
 3900        s = s.substr(pos + 1);
 3901        auto beg = std::sregex_iterator(s.begin(), s.end(), re);
 3902        for (auto i = beg; i != std::sregex_iterator(); ++i) {
 3903          const auto &m = *i;
 3904          auto key = s.substr(static_cast<size_t>(m.position(1)),
 3905                              static_cast<size_t>(m.length(1)));
 3906          auto val = m.length(2) > 0
 3907                         ? s.substr(static_cast<size_t>(m.position(2)),
 3908                                    static_cast<size_t>(m.length(2)))
 3909                         : s.substr(static_cast<size_t>(m.position(3)),
 3910                                    static_cast<size_t>(m.length(3)));
 3911          auth[std::move(key)] = std::move(val);
 3912        }
 3913        return true;
 3914      }
 3915    }
 3916  }
 3917  return false;
 3918}
 3919
 3920class ContentProviderAdapter {
 3921public:
 3922  explicit ContentProviderAdapter(
 3923      ContentProviderWithoutLength &&content_provider)
 3924      : content_provider_(std::move(content_provider)) {}
 3925
 3926  bool operator()(size_t offset, size_t, DataSink &sink) {
 3927    return content_provider_(offset, sink);
 3928  }
 3929
 3930private:
 3931  ContentProviderWithoutLength content_provider_;
 3932};
 3933
 3934// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
 3935namespace fields {
 3936
 3937bool is_token_char(char c) {
 3938  return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
 3939         c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
 3940         c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
 3941}
 3942
 3943bool is_token(const std::string &s) {
 3944  if (s.empty()) { return false; }
 3945  for (auto c : s) {
 3946    if (!is_token_char(c)) { return false; }
 3947  }
 3948  return true;
 3949}
 3950
 3951bool is_field_name(const std::string &s) { return is_token(s); }
 3952
 3953bool is_vchar(char c) { return c >= 33 && c <= 126; }
 3954
 3955bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }
 3956
 3957bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }
 3958
 3959bool is_field_content(const std::string &s) {
 3960  if (s.empty()) { return true; }
 3961
 3962  if (s.size() == 1) {
 3963    return is_field_vchar(s[0]);
 3964  } else if (s.size() == 2) {
 3965    return is_field_vchar(s[0]) && is_field_vchar(s[1]);
 3966  } else {
 3967    size_t i = 0;
 3968
 3969    if (!is_field_vchar(s[i])) { return false; }
 3970    i++;
 3971
 3972    while (i < s.size() - 1) {
 3973      auto c = s[i++];
 3974      if (c == ' ' || c == '\t' || is_field_vchar(c)) {
 3975      } else {
 3976        return false;
 3977      }
 3978    }
 3979
 3980    return is_field_vchar(s[i]);
 3981  }
 3982}
 3983
 3984bool is_field_value(const std::string &s) { return is_field_content(s); }
 3985
 3986} // namespace fields
 3987
 3988} // namespace detail
 3989
 3990const char *status_message(int status) {
 3991  switch (status) {
 3992  case StatusCode::Continue_100: return "Continue";
 3993  case StatusCode::SwitchingProtocol_101: return "Switching Protocol";
 3994  case StatusCode::Processing_102: return "Processing";
 3995  case StatusCode::EarlyHints_103: return "Early Hints";
 3996  case StatusCode::OK_200: return "OK";
 3997  case StatusCode::Created_201: return "Created";
 3998  case StatusCode::Accepted_202: return "Accepted";
 3999  case StatusCode::NonAuthoritativeInformation_203:
 4000    return "Non-Authoritative Information";
 4001  case StatusCode::NoContent_204: return "No Content";
 4002  case StatusCode::ResetContent_205: return "Reset Content";
 4003  case StatusCode::PartialContent_206: return "Partial Content";
 4004  case StatusCode::MultiStatus_207: return "Multi-Status";
 4005  case StatusCode::AlreadyReported_208: return "Already Reported";
 4006  case StatusCode::IMUsed_226: return "IM Used";
 4007  case StatusCode::MultipleChoices_300: return "Multiple Choices";
 4008  case StatusCode::MovedPermanently_301: return "Moved Permanently";
 4009  case StatusCode::Found_302: return "Found";
 4010  case StatusCode::SeeOther_303: return "See Other";
 4011  case StatusCode::NotModified_304: return "Not Modified";
 4012  case StatusCode::UseProxy_305: return "Use Proxy";
 4013  case StatusCode::unused_306: return "unused";
 4014  case StatusCode::TemporaryRedirect_307: return "Temporary Redirect";
 4015  case StatusCode::PermanentRedirect_308: return "Permanent Redirect";
 4016  case StatusCode::BadRequest_400: return "Bad Request";
 4017  case StatusCode::Unauthorized_401: return "Unauthorized";
 4018  case StatusCode::PaymentRequired_402: return "Payment Required";
 4019  case StatusCode::Forbidden_403: return "Forbidden";
 4020  case StatusCode::NotFound_404: return "Not Found";
 4021  case StatusCode::MethodNotAllowed_405: return "Method Not Allowed";
 4022  case StatusCode::NotAcceptable_406: return "Not Acceptable";
 4023  case StatusCode::ProxyAuthenticationRequired_407:
 4024    return "Proxy Authentication Required";
 4025  case StatusCode::RequestTimeout_408: return "Request Timeout";
 4026  case StatusCode::Conflict_409: return "Conflict";
 4027  case StatusCode::Gone_410: return "Gone";
 4028  case StatusCode::LengthRequired_411: return "Length Required";
 4029  case StatusCode::PreconditionFailed_412: return "Precondition Failed";
 4030  case StatusCode::PayloadTooLarge_413: return "Payload Too Large";
 4031  case StatusCode::UriTooLong_414: return "URI Too Long";
 4032  case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type";
 4033  case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable";
 4034  case StatusCode::ExpectationFailed_417: return "Expectation Failed";
 4035  case StatusCode::ImATeapot_418: return "I'm a teapot";
 4036  case StatusCode::MisdirectedRequest_421: return "Misdirected Request";
 4037  case StatusCode::UnprocessableContent_422: return "Unprocessable Content";
 4038  case StatusCode::Locked_423: return "Locked";
 4039  case StatusCode::FailedDependency_424: return "Failed Dependency";
 4040  case StatusCode::TooEarly_425: return "Too Early";
 4041  case StatusCode::UpgradeRequired_426: return "Upgrade Required";
 4042  case StatusCode::PreconditionRequired_428: return "Precondition Required";
 4043  case StatusCode::TooManyRequests_429: return "Too Many Requests";
 4044  case StatusCode::RequestHeaderFieldsTooLarge_431:
 4045    return "Request Header Fields Too Large";
 4046  case StatusCode::UnavailableForLegalReasons_451:
 4047    return "Unavailable For Legal Reasons";
 4048  case StatusCode::NotImplemented_501: return "Not Implemented";
 4049  case StatusCode::BadGateway_502: return "Bad Gateway";
 4050  case StatusCode::ServiceUnavailable_503: return "Service Unavailable";
 4051  case StatusCode::GatewayTimeout_504: return "Gateway Timeout";
 4052  case StatusCode::HttpVersionNotSupported_505:
 4053    return "HTTP Version Not Supported";
 4054  case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates";
 4055  case StatusCode::InsufficientStorage_507: return "Insufficient Storage";
 4056  case StatusCode::LoopDetected_508: return "Loop Detected";
 4057  case StatusCode::NotExtended_510: return "Not Extended";
 4058  case StatusCode::NetworkAuthenticationRequired_511:
 4059    return "Network Authentication Required";
 4060
 4061  default:
 4062  case StatusCode::InternalServerError_500: return "Internal Server Error";
 4063  }
 4064}
 4065
 4066std::string to_string(const Error error) {
 4067  switch (error) {
 4068  case Error::Success: return "Success (no error)";
 4069  case Error::Unknown: return "Unknown";
 4070  case Error::Connection: return "Could not establish connection";
 4071  case Error::BindIPAddress: return "Failed to bind IP address";
 4072  case Error::Read: return "Failed to read connection";
 4073  case Error::Write: return "Failed to write connection";
 4074  case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";
 4075  case Error::Canceled: return "Connection handling canceled";
 4076  case Error::SSLConnection: return "SSL connection failed";
 4077  case Error::SSLLoadingCerts: return "SSL certificate loading failed";
 4078  case Error::SSLServerVerification: return "SSL server verification failed";
 4079  case Error::SSLServerHostnameVerification:
 4080    return "SSL server hostname verification failed";
 4081  case Error::UnsupportedMultipartBoundaryChars:
 4082    return "Unsupported HTTP multipart boundary characters";
 4083  case Error::Compression: return "Compression failed";
 4084  case Error::ConnectionTimeout: return "Connection timed out";
 4085  case Error::ProxyConnection: return "Proxy connection failed";
 4086  case Error::ConnectionClosed: return "Connection closed by server";
 4087  case Error::Timeout: return "Read timeout";
 4088  case Error::ResourceExhaustion: return "Resource exhaustion";
 4089  case Error::TooManyFormDataFiles: return "Too many form data files";
 4090  case Error::ExceedMaxPayloadSize: return "Exceeded maximum payload size";
 4091  case Error::ExceedUriMaxLength: return "Exceeded maximum URI length";
 4092  case Error::ExceedMaxSocketDescriptorCount:
 4093    return "Exceeded maximum socket descriptor count";
 4094  case Error::InvalidRequestLine: return "Invalid request line";
 4095  case Error::InvalidHTTPMethod: return "Invalid HTTP method";
 4096  case Error::InvalidHTTPVersion: return "Invalid HTTP version";
 4097  case Error::InvalidHeaders: return "Invalid headers";
 4098  case Error::MultipartParsing: return "Multipart parsing failed";
 4099  case Error::OpenFile: return "Failed to open file";
 4100  case Error::Listen: return "Failed to listen on socket";
 4101  case Error::GetSockName: return "Failed to get socket name";
 4102  case Error::UnsupportedAddressFamily: return "Unsupported address family";
 4103  case Error::HTTPParsing: return "HTTP parsing failed";
 4104  case Error::InvalidRangeHeader: return "Invalid Range header";
 4105  default: break;
 4106  }
 4107
 4108  return "Invalid";
 4109}
 4110
 4111std::ostream &operator<<(std::ostream &os, const Error &obj) {
 4112  os << to_string(obj);
 4113  os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
 4114  return os;
 4115}
 4116
 4117std::string hosted_at(const std::string &hostname) {
 4118  std::vector<std::string> addrs;
 4119  hosted_at(hostname, addrs);
 4120  if (addrs.empty()) { return std::string(); }
 4121  return addrs[0];
 4122}
 4123
 4124void hosted_at(const std::string &hostname,
 4125                      std::vector<std::string> &addrs) {
 4126  struct addrinfo hints;
 4127  struct addrinfo *result;
 4128
 4129  memset(&hints, 0, sizeof(struct addrinfo));
 4130  hints.ai_family = AF_UNSPEC;
 4131  hints.ai_socktype = SOCK_STREAM;
 4132  hints.ai_protocol = 0;
 4133
 4134  if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,
 4135                                       &result, 0)) {
 4136#if defined __linux__ && !defined __ANDROID__
 4137    res_init();
 4138#endif
 4139    return;
 4140  }
 4141  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
 4142
 4143  for (auto rp = result; rp; rp = rp->ai_next) {
 4144    const auto &addr =
 4145        *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
 4146    std::string ip;
 4147    auto dummy = -1;
 4148    if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,
 4149                                dummy)) {
 4150      addrs.emplace_back(std::move(ip));
 4151    }
 4152  }
 4153}
 4154
 4155std::string encode_uri_component(const std::string &value) {
 4156  std::ostringstream escaped;
 4157  escaped.fill('0');
 4158  escaped << std::hex;
 4159
 4160  for (auto c : value) {
 4161    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
 4162        c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
 4163        c == ')') {
 4164      escaped << c;
 4165    } else {
 4166      escaped << std::uppercase;
 4167      escaped << '%' << std::setw(2)
 4168              << static_cast<int>(static_cast<unsigned char>(c));
 4169      escaped << std::nouppercase;
 4170    }
 4171  }
 4172
 4173  return escaped.str();
 4174}
 4175
 4176std::string encode_uri(const std::string &value) {
 4177  std::ostringstream escaped;
 4178  escaped.fill('0');
 4179  escaped << std::hex;
 4180
 4181  for (auto c : value) {
 4182    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
 4183        c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
 4184        c == ')' || c == ';' || c == '/' || c == '?' || c == ':' || c == '@' ||
 4185        c == '&' || c == '=' || c == '+' || c == '$' || c == ',' || c == '#') {
 4186      escaped << c;
 4187    } else {
 4188      escaped << std::uppercase;
 4189      escaped << '%' << std::setw(2)
 4190              << static_cast<int>(static_cast<unsigned char>(c));
 4191      escaped << std::nouppercase;
 4192    }
 4193  }
 4194
 4195  return escaped.str();
 4196}
 4197
 4198std::string decode_uri_component(const std::string &value) {
 4199  std::string result;
 4200
 4201  for (size_t i = 0; i < value.size(); i++) {
 4202    if (value[i] == '%' && i + 2 < value.size()) {
 4203      auto val = 0;
 4204      if (detail::from_hex_to_i(value, i + 1, 2, val)) {
 4205        result += static_cast<char>(val);
 4206        i += 2;
 4207      } else {
 4208        result += value[i];
 4209      }
 4210    } else {
 4211      result += value[i];
 4212    }
 4213  }
 4214
 4215  return result;
 4216}
 4217
 4218std::string decode_uri(const std::string &value) {
 4219  std::string result;
 4220
 4221  for (size_t i = 0; i < value.size(); i++) {
 4222    if (value[i] == '%' && i + 2 < value.size()) {
 4223      auto val = 0;
 4224      if (detail::from_hex_to_i(value, i + 1, 2, val)) {
 4225        result += static_cast<char>(val);
 4226        i += 2;
 4227      } else {
 4228        result += value[i];
 4229      }
 4230    } else {
 4231      result += value[i];
 4232    }
 4233  }
 4234
 4235  return result;
 4236}
 4237
 4238std::string encode_path_component(const std::string &component) {
 4239  std::string result;
 4240  result.reserve(component.size() * 3);
 4241
 4242  for (size_t i = 0; i < component.size(); i++) {
 4243    auto c = static_cast<unsigned char>(component[i]);
 4244
 4245    // Unreserved characters per RFC 3986: ALPHA / DIGIT / "-" / "." / "_" / "~"
 4246    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
 4247      result += static_cast<char>(c);
 4248    }
 4249    // Path-safe sub-delimiters: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" /
 4250    // "," / ";" / "="
 4251    else if (c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' ||
 4252             c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||
 4253             c == '=') {
 4254      result += static_cast<char>(c);
 4255    }
 4256    // Colon is allowed in path segments except first segment
 4257    else if (c == ':') {
 4258      result += static_cast<char>(c);
 4259    }
 4260    // @ is allowed in path
 4261    else if (c == '@') {
 4262      result += static_cast<char>(c);
 4263    } else {
 4264      result += '%';
 4265      char hex[3];
 4266      snprintf(hex, sizeof(hex), "%02X", c);
 4267      result.append(hex, 2);
 4268    }
 4269  }
 4270  return result;
 4271}
 4272
 4273std::string decode_path_component(const std::string &component) {
 4274  std::string result;
 4275  result.reserve(component.size());
 4276
 4277  for (size_t i = 0; i < component.size(); i++) {
 4278    if (component[i] == '%' && i + 1 < component.size()) {
 4279      if (component[i + 1] == 'u') {
 4280        // Unicode %uXXXX encoding
 4281        auto val = 0;
 4282        if (detail::from_hex_to_i(component, i + 2, 4, val)) {
 4283          // 4 digits Unicode codes
 4284          char buff[4];
 4285          size_t len = detail::to_utf8(val, buff);
 4286          if (len > 0) { result.append(buff, len); }
 4287          i += 5; // 'u0000'
 4288        } else {
 4289          result += component[i];
 4290        }
 4291      } else {
 4292        // Standard %XX encoding
 4293        auto val = 0;
 4294        if (detail::from_hex_to_i(component, i + 1, 2, val)) {
 4295          // 2 digits hex codes
 4296          result += static_cast<char>(val);
 4297          i += 2; // 'XX'
 4298        } else {
 4299          result += component[i];
 4300        }
 4301      }
 4302    } else {
 4303      result += component[i];
 4304    }
 4305  }
 4306  return result;
 4307}
 4308
 4309std::string encode_query_component(const std::string &component,
 4310                                          bool space_as_plus) {
 4311  std::string result;
 4312  result.reserve(component.size() * 3);
 4313
 4314  for (size_t i = 0; i < component.size(); i++) {
 4315    auto c = static_cast<unsigned char>(component[i]);
 4316
 4317    // Unreserved characters per RFC 3986
 4318    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
 4319      result += static_cast<char>(c);
 4320    }
 4321    // Space handling
 4322    else if (c == ' ') {
 4323      if (space_as_plus) {
 4324        result += '+';
 4325      } else {
 4326        result += "%20";
 4327      }
 4328    }
 4329    // Plus sign handling
 4330    else if (c == '+') {
 4331      if (space_as_plus) {
 4332        result += "%2B";
 4333      } else {
 4334        result += static_cast<char>(c);
 4335      }
 4336    }
 4337    // Query-safe sub-delimiters (excluding & and = which are query delimiters)
 4338    else if (c == '!' || c == '$' || c == '\'' || c == '(' || c == ')' ||
 4339             c == '*' || c == ',' || c == ';') {
 4340      result += static_cast<char>(c);
 4341    }
 4342    // Colon and @ are allowed in query
 4343    else if (c == ':' || c == '@') {
 4344      result += static_cast<char>(c);
 4345    }
 4346    // Forward slash is allowed in query values
 4347    else if (c == '/') {
 4348      result += static_cast<char>(c);
 4349    }
 4350    // Question mark is allowed in query values (after first ?)
 4351    else if (c == '?') {
 4352      result += static_cast<char>(c);
 4353    } else {
 4354      result += '%';
 4355      char hex[3];
 4356      snprintf(hex, sizeof(hex), "%02X", c);
 4357      result.append(hex, 2);
 4358    }
 4359  }
 4360  return result;
 4361}
 4362
 4363std::string decode_query_component(const std::string &component,
 4364                                          bool plus_as_space) {
 4365  std::string result;
 4366  result.reserve(component.size());
 4367
 4368  for (size_t i = 0; i < component.size(); i++) {
 4369    if (component[i] == '%' && i + 2 < component.size()) {
 4370      std::string hex = component.substr(i + 1, 2);
 4371      char *end;
 4372      unsigned long value = std::strtoul(hex.c_str(), &end, 16);
 4373      if (end == hex.c_str() + 2) {
 4374        result += static_cast<char>(value);
 4375        i += 2;
 4376      } else {
 4377        result += component[i];
 4378      }
 4379    } else if (component[i] == '+' && plus_as_space) {
 4380      result += ' '; // + becomes space in form-urlencoded
 4381    } else {
 4382      result += component[i];
 4383    }
 4384  }
 4385  return result;
 4386}
 4387
 4388std::string append_query_params(const std::string &path,
 4389                                       const Params &params) {
 4390  std::string path_with_query = path;
 4391  thread_local const std::regex re("[^?]+\\?.*");
 4392  auto delm = std::regex_match(path, re) ? '&' : '?';
 4393  path_with_query += delm + detail::params_to_query_str(params);
 4394  return path_with_query;
 4395}
 4396
 4397// Header utilities
 4398std::pair<std::string, std::string>
 4399make_range_header(const Ranges &ranges) {
 4400  std::string field = "bytes=";
 4401  auto i = 0;
 4402  for (const auto &r : ranges) {
 4403    if (i != 0) { field += ", "; }
 4404    if (r.first != -1) { field += std::to_string(r.first); }
 4405    field += '-';
 4406    if (r.second != -1) { field += std::to_string(r.second); }
 4407    i++;
 4408  }
 4409  return std::make_pair("Range", std::move(field));
 4410}
 4411
 4412std::pair<std::string, std::string>
 4413make_basic_authentication_header(const std::string &username,
 4414                                 const std::string &password, bool is_proxy) {
 4415  auto field = "Basic " + detail::base64_encode(username + ":" + password);
 4416  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
 4417  return std::make_pair(key, std::move(field));
 4418}
 4419
 4420std::pair<std::string, std::string>
 4421make_bearer_token_authentication_header(const std::string &token,
 4422                                        bool is_proxy = false) {
 4423  auto field = "Bearer " + token;
 4424  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
 4425  return std::make_pair(key, std::move(field));
 4426}
 4427
 4428// Request implementation
 4429bool Request::has_header(const std::string &key) const {
 4430  return detail::has_header(headers, key);
 4431}
 4432
 4433std::string Request::get_header_value(const std::string &key,
 4434                                             const char *def, size_t id) const {
 4435  return detail::get_header_value(headers, key, def, id);
 4436}
 4437
 4438size_t Request::get_header_value_count(const std::string &key) const {
 4439  auto r = headers.equal_range(key);
 4440  return static_cast<size_t>(std::distance(r.first, r.second));
 4441}
 4442
 4443void Request::set_header(const std::string &key,
 4444                                const std::string &val) {
 4445  if (detail::fields::is_field_name(key) &&
 4446      detail::fields::is_field_value(val)) {
 4447    headers.emplace(key, val);
 4448  }
 4449}
 4450
 4451bool Request::has_trailer(const std::string &key) const {
 4452  return trailers.find(key) != trailers.end();
 4453}
 4454
 4455std::string Request::get_trailer_value(const std::string &key,
 4456                                              size_t id) const {
 4457  auto rng = trailers.equal_range(key);
 4458  auto it = rng.first;
 4459  std::advance(it, static_cast<ssize_t>(id));
 4460  if (it != rng.second) { return it->second; }
 4461  return std::string();
 4462}
 4463
 4464size_t Request::get_trailer_value_count(const std::string &key) const {
 4465  auto r = trailers.equal_range(key);
 4466  return static_cast<size_t>(std::distance(r.first, r.second));
 4467}
 4468
 4469bool Request::has_param(const std::string &key) const {
 4470  return params.find(key) != params.end();
 4471}
 4472
 4473std::string Request::get_param_value(const std::string &key,
 4474                                            size_t id) const {
 4475  auto rng = params.equal_range(key);
 4476  auto it = rng.first;
 4477  std::advance(it, static_cast<ssize_t>(id));
 4478  if (it != rng.second) { return it->second; }
 4479  return std::string();
 4480}
 4481
 4482size_t Request::get_param_value_count(const std::string &key) const {
 4483  auto r = params.equal_range(key);
 4484  return static_cast<size_t>(std::distance(r.first, r.second));
 4485}
 4486
 4487bool Request::is_multipart_form_data() const {
 4488  const auto &content_type = get_header_value("Content-Type");
 4489  return !content_type.rfind("multipart/form-data", 0);
 4490}
 4491
 4492// Multipart FormData implementation
 4493std::string MultipartFormData::get_field(const std::string &key,
 4494                                                size_t id) const {
 4495  auto rng = fields.equal_range(key);
 4496  auto it = rng.first;
 4497  std::advance(it, static_cast<ssize_t>(id));
 4498  if (it != rng.second) { return it->second.content; }
 4499  return std::string();
 4500}
 4501
 4502std::vector<std::string>
 4503MultipartFormData::get_fields(const std::string &key) const {
 4504  std::vector<std::string> values;
 4505  auto rng = fields.equal_range(key);
 4506  for (auto it = rng.first; it != rng.second; it++) {
 4507    values.push_back(it->second.content);
 4508  }
 4509  return values;
 4510}
 4511
 4512bool MultipartFormData::has_field(const std::string &key) const {
 4513  return fields.find(key) != fields.end();
 4514}
 4515
 4516size_t MultipartFormData::get_field_count(const std::string &key) const {
 4517  auto r = fields.equal_range(key);
 4518  return static_cast<size_t>(std::distance(r.first, r.second));
 4519}
 4520
 4521FormData MultipartFormData::get_file(const std::string &key,
 4522                                            size_t id) const {
 4523  auto rng = files.equal_range(key);
 4524  auto it = rng.first;
 4525  std::advance(it, static_cast<ssize_t>(id));
 4526  if (it != rng.second) { return it->second; }
 4527  return FormData();
 4528}
 4529
 4530std::vector<FormData>
 4531MultipartFormData::get_files(const std::string &key) const {
 4532  std::vector<FormData> values;
 4533  auto rng = files.equal_range(key);
 4534  for (auto it = rng.first; it != rng.second; it++) {
 4535    values.push_back(it->second);
 4536  }
 4537  return values;
 4538}
 4539
 4540bool MultipartFormData::has_file(const std::string &key) const {
 4541  return files.find(key) != files.end();
 4542}
 4543
 4544size_t MultipartFormData::get_file_count(const std::string &key) const {
 4545  auto r = files.equal_range(key);
 4546  return static_cast<size_t>(std::distance(r.first, r.second));
 4547}
 4548
 4549// Response implementation
 4550bool Response::has_header(const std::string &key) const {
 4551  return headers.find(key) != headers.end();
 4552}
 4553
 4554std::string Response::get_header_value(const std::string &key,
 4555                                              const char *def,
 4556                                              size_t id) const {
 4557  return detail::get_header_value(headers, key, def, id);
 4558}
 4559
 4560size_t Response::get_header_value_count(const std::string &key) const {
 4561  auto r = headers.equal_range(key);
 4562  return static_cast<size_t>(std::distance(r.first, r.second));
 4563}
 4564
 4565void Response::set_header(const std::string &key,
 4566                                 const std::string &val) {
 4567  if (detail::fields::is_field_name(key) &&
 4568      detail::fields::is_field_value(val)) {
 4569    headers.emplace(key, val);
 4570  }
 4571}
 4572bool Response::has_trailer(const std::string &key) const {
 4573  return trailers.find(key) != trailers.end();
 4574}
 4575
 4576std::string Response::get_trailer_value(const std::string &key,
 4577                                               size_t id) const {
 4578  auto rng = trailers.equal_range(key);
 4579  auto it = rng.first;
 4580  std::advance(it, static_cast<ssize_t>(id));
 4581  if (it != rng.second) { return it->second; }
 4582  return std::string();
 4583}
 4584
 4585size_t Response::get_trailer_value_count(const std::string &key) const {
 4586  auto r = trailers.equal_range(key);
 4587  return static_cast<size_t>(std::distance(r.first, r.second));
 4588}
 4589
 4590void Response::set_redirect(const std::string &url, int stat) {
 4591  if (detail::fields::is_field_value(url)) {
 4592    set_header("Location", url);
 4593    if (300 <= stat && stat < 400) {
 4594      this->status = stat;
 4595    } else {
 4596      this->status = StatusCode::Found_302;
 4597    }
 4598  }
 4599}
 4600
 4601void Response::set_content(const char *s, size_t n,
 4602                                  const std::string &content_type) {
 4603  body.assign(s, n);
 4604
 4605  auto rng = headers.equal_range("Content-Type");
 4606  headers.erase(rng.first, rng.second);
 4607  set_header("Content-Type", content_type);
 4608}
 4609
 4610void Response::set_content(const std::string &s,
 4611                                  const std::string &content_type) {
 4612  set_content(s.data(), s.size(), content_type);
 4613}
 4614
 4615void Response::set_content(std::string &&s,
 4616                                  const std::string &content_type) {
 4617  body = std::move(s);
 4618
 4619  auto rng = headers.equal_range("Content-Type");
 4620  headers.erase(rng.first, rng.second);
 4621  set_header("Content-Type", content_type);
 4622}
 4623
 4624void Response::set_content_provider(
 4625    size_t in_length, const std::string &content_type, ContentProvider provider,
 4626    ContentProviderResourceReleaser resource_releaser) {
 4627  set_header("Content-Type", content_type);
 4628  content_length_ = in_length;
 4629  if (in_length > 0) { content_provider_ = std::move(provider); }
 4630  content_provider_resource_releaser_ = std::move(resource_releaser);
 4631  is_chunked_content_provider_ = false;
 4632}
 4633
 4634void Response::set_content_provider(
 4635    const std::string &content_type, ContentProviderWithoutLength provider,
 4636    ContentProviderResourceReleaser resource_releaser) {
 4637  set_header("Content-Type", content_type);
 4638  content_length_ = 0;
 4639  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
 4640  content_provider_resource_releaser_ = std::move(resource_releaser);
 4641  is_chunked_content_provider_ = false;
 4642}
 4643
 4644void Response::set_chunked_content_provider(
 4645    const std::string &content_type, ContentProviderWithoutLength provider,
 4646    ContentProviderResourceReleaser resource_releaser) {
 4647  set_header("Content-Type", content_type);
 4648  content_length_ = 0;
 4649  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
 4650  content_provider_resource_releaser_ = std::move(resource_releaser);
 4651  is_chunked_content_provider_ = true;
 4652}
 4653
 4654void Response::set_file_content(const std::string &path,
 4655                                       const std::string &content_type) {
 4656  file_content_path_ = path;
 4657  file_content_content_type_ = content_type;
 4658}
 4659
 4660void Response::set_file_content(const std::string &path) {
 4661  file_content_path_ = path;
 4662}
 4663
 4664// Result implementation
 4665bool Result::has_request_header(const std::string &key) const {
 4666  return request_headers_.find(key) != request_headers_.end();
 4667}
 4668
 4669std::string Result::get_request_header_value(const std::string &key,
 4670                                                    const char *def,
 4671                                                    size_t id) const {
 4672  return detail::get_header_value(request_headers_, key, def, id);
 4673}
 4674
 4675size_t
 4676Result::get_request_header_value_count(const std::string &key) const {
 4677  auto r = request_headers_.equal_range(key);
 4678  return static_cast<size_t>(std::distance(r.first, r.second));
 4679}
 4680
 4681// Stream implementation
 4682ssize_t Stream::write(const char *ptr) {
 4683  return write(ptr, strlen(ptr));
 4684}
 4685
 4686ssize_t Stream::write(const std::string &s) {
 4687  return write(s.data(), s.size());
 4688}
 4689
 4690// BodyReader implementation
 4691ssize_t detail::BodyReader::read(char *buf, size_t len) {
 4692  if (!stream) {
 4693    last_error = Error::Connection;
 4694    return -1;
 4695  }
 4696  if (eof) { return 0; }
 4697
 4698  if (!chunked) {
 4699    // Content-Length based reading
 4700    if (bytes_read >= content_length) {
 4701      eof = true;
 4702      return 0;
 4703    }
 4704
 4705    auto remaining = content_length - bytes_read;
 4706    auto to_read = (std::min)(len, remaining);
 4707    auto n = stream->read(buf, to_read);
 4708
 4709    if (n < 0) {
 4710      last_error = stream->get_error();
 4711      if (last_error == Error::Success) { last_error = Error::Read; }
 4712      eof = true;
 4713      return n;
 4714    }
 4715    if (n == 0) {
 4716      // Unexpected EOF before content_length
 4717      last_error = stream->get_error();
 4718      if (last_error == Error::Success) { last_error = Error::Read; }
 4719      eof = true;
 4720      return 0;
 4721    }
 4722
 4723    bytes_read += static_cast<size_t>(n);
 4724    if (bytes_read >= content_length) { eof = true; }
 4725    return n;
 4726  }
 4727
 4728  // Chunked transfer encoding: delegate to shared decoder instance.
 4729  if (!chunked_decoder) { chunked_decoder.reset(new ChunkedDecoder(*stream)); }
 4730
 4731  size_t chunk_offset = 0;
 4732  size_t chunk_total = 0;
 4733  auto n = chunked_decoder->read_payload(buf, len, chunk_offset, chunk_total);
 4734  if (n < 0) {
 4735    last_error = stream->get_error();
 4736    if (last_error == Error::Success) { last_error = Error::Read; }
 4737    eof = true;
 4738    return n;
 4739  }
 4740
 4741  if (n == 0) {
 4742    // Final chunk observed. Leave trailer parsing to the caller (StreamHandle).
 4743    eof = true;
 4744    return 0;
 4745  }
 4746
 4747  bytes_read += static_cast<size_t>(n);
 4748  return n;
 4749}
 4750
 4751namespace detail {
 4752
 4753void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
 4754                                time_t timeout_sec, time_t timeout_usec,
 4755                                time_t &actual_timeout_sec,
 4756                                time_t &actual_timeout_usec) {
 4757  auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
 4758
 4759  auto actual_timeout_msec =
 4760      (std::min)(max_timeout_msec - duration_msec, timeout_msec);
 4761
 4762  if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }
 4763
 4764  actual_timeout_sec = actual_timeout_msec / 1000;
 4765  actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
 4766}
 4767
 4768// Socket stream implementation
 4769SocketStream::SocketStream(
 4770    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
 4771    time_t write_timeout_sec, time_t write_timeout_usec,
 4772    time_t max_timeout_msec,
 4773    std::chrono::time_point<std::chrono::steady_clock> start_time)
 4774    : sock_(sock), read_timeout_sec_(read_timeout_sec),
 4775      read_timeout_usec_(read_timeout_usec),
 4776      write_timeout_sec_(write_timeout_sec),
 4777      write_timeout_usec_(write_timeout_usec),
 4778      max_timeout_msec_(max_timeout_msec), start_time_(start_time),
 4779      read_buff_(read_buff_size_, 0) {}
 4780
 4781SocketStream::~SocketStream() = default;
 4782
 4783bool SocketStream::is_readable() const {
 4784  return read_buff_off_ < read_buff_content_size_;
 4785}
 4786
 4787bool SocketStream::wait_readable() const {
 4788  if (max_timeout_msec_ <= 0) {
 4789    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
 4790  }
 4791
 4792  time_t read_timeout_sec;
 4793  time_t read_timeout_usec;
 4794  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
 4795                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
 4796
 4797  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
 4798}
 4799
 4800bool SocketStream::wait_writable() const {
 4801  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
 4802         is_socket_alive(sock_);
 4803}
 4804
 4805ssize_t SocketStream::read(char *ptr, size_t size) {
 4806#ifdef _WIN32
 4807  size =
 4808      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
 4809#else
 4810  size = (std::min)(size,
 4811                    static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
 4812#endif
 4813
 4814  if (read_buff_off_ < read_buff_content_size_) {
 4815    auto remaining_size = read_buff_content_size_ - read_buff_off_;
 4816    if (size <= remaining_size) {
 4817      memcpy(ptr, read_buff_.data() + read_buff_off_, size);
 4818      read_buff_off_ += size;
 4819      return static_cast<ssize_t>(size);
 4820    } else {
 4821      memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
 4822      read_buff_off_ += remaining_size;
 4823      return static_cast<ssize_t>(remaining_size);
 4824    }
 4825  }
 4826
 4827  if (!wait_readable()) {
 4828    error_ = Error::Timeout;
 4829    return -1;
 4830  }
 4831
 4832  read_buff_off_ = 0;
 4833  read_buff_content_size_ = 0;
 4834
 4835  if (size < read_buff_size_) {
 4836    auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
 4837                         CPPHTTPLIB_RECV_FLAGS);
 4838    if (n <= 0) {
 4839      if (n == 0) {
 4840        error_ = Error::ConnectionClosed;
 4841      } else {
 4842        error_ = Error::Read;
 4843      }
 4844      return n;
 4845    } else if (n <= static_cast<ssize_t>(size)) {
 4846      memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
 4847      return n;
 4848    } else {
 4849      memcpy(ptr, read_buff_.data(), size);
 4850      read_buff_off_ = size;
 4851      read_buff_content_size_ = static_cast<size_t>(n);
 4852      return static_cast<ssize_t>(size);
 4853    }
 4854  } else {
 4855    auto n = read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
 4856    if (n <= 0) {
 4857      if (n == 0) {
 4858        error_ = Error::ConnectionClosed;
 4859      } else {
 4860        error_ = Error::Read;
 4861      }
 4862    }
 4863    return n;
 4864  }
 4865}
 4866
 4867ssize_t SocketStream::write(const char *ptr, size_t size) {
 4868  if (!wait_writable()) { return -1; }
 4869
 4870#if defined(_WIN32) && !defined(_WIN64)
 4871  size =
 4872      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
 4873#endif
 4874
 4875  return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
 4876}
 4877
 4878void SocketStream::get_remote_ip_and_port(std::string &ip,
 4879                                                 int &port) const {
 4880  return detail::get_remote_ip_and_port(sock_, ip, port);
 4881}
 4882
 4883void SocketStream::get_local_ip_and_port(std::string &ip,
 4884                                                int &port) const {
 4885  return detail::get_local_ip_and_port(sock_, ip, port);
 4886}
 4887
 4888socket_t SocketStream::socket() const { return sock_; }
 4889
 4890time_t SocketStream::duration() const {
 4891  return std::chrono::duration_cast<std::chrono::milliseconds>(
 4892             std::chrono::steady_clock::now() - start_time_)
 4893      .count();
 4894}
 4895
 4896// Buffer stream implementation
 4897bool BufferStream::is_readable() const { return true; }
 4898
 4899bool BufferStream::wait_readable() const { return true; }
 4900
 4901bool BufferStream::wait_writable() const { return true; }
 4902
 4903ssize_t BufferStream::read(char *ptr, size_t size) {
 4904#if defined(_MSC_VER) && _MSC_VER < 1910
 4905  auto len_read = buffer._Copy_s(ptr, size, size, position);
 4906#else
 4907  auto len_read = buffer.copy(ptr, size, position);
 4908#endif
 4909  position += static_cast<size_t>(len_read);
 4910  return static_cast<ssize_t>(len_read);
 4911}
 4912
 4913ssize_t BufferStream::write(const char *ptr, size_t size) {
 4914  buffer.append(ptr, size);
 4915  return static_cast<ssize_t>(size);
 4916}
 4917
 4918void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
 4919                                                 int & /*port*/) const {}
 4920
 4921void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
 4922                                                int & /*port*/) const {}
 4923
 4924socket_t BufferStream::socket() const { return 0; }
 4925
 4926time_t BufferStream::duration() const { return 0; }
 4927
 4928const std::string &BufferStream::get_buffer() const { return buffer; }
 4929
 4930PathParamsMatcher::PathParamsMatcher(const std::string &pattern)
 4931    : MatcherBase(pattern) {
 4932  constexpr const char marker[] = "/:";
 4933
 4934  // One past the last ending position of a path param substring
 4935  std::size_t last_param_end = 0;
 4936
 4937#ifndef CPPHTTPLIB_NO_EXCEPTIONS
 4938  // Needed to ensure that parameter names are unique during matcher
 4939  // construction
 4940  // If exceptions are disabled, only last duplicate path
 4941  // parameter will be set
 4942  std::unordered_set<std::string> param_name_set;
 4943#endif
 4944
 4945  while (true) {
 4946    const auto marker_pos = pattern.find(
 4947        marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
 4948    if (marker_pos == std::string::npos) { break; }
 4949
 4950    static_fragments_.push_back(
 4951        pattern.substr(last_param_end, marker_pos - last_param_end + 1));
 4952
 4953    const auto param_name_start = marker_pos + str_len(marker);
 4954
 4955    auto sep_pos = pattern.find(separator, param_name_start);
 4956    if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
 4957
 4958    auto param_name =
 4959        pattern.substr(param_name_start, sep_pos - param_name_start);
 4960
 4961#ifndef CPPHTTPLIB_NO_EXCEPTIONS
 4962    if (param_name_set.find(param_name) != param_name_set.cend()) {
 4963      std::string msg = "Encountered path parameter '" + param_name +
 4964                        "' multiple times in route pattern '" + pattern + "'.";
 4965      throw std::invalid_argument(msg);
 4966    }
 4967#endif
 4968
 4969    param_names_.push_back(std::move(param_name));
 4970
 4971    last_param_end = sep_pos + 1;
 4972  }
 4973
 4974  if (last_param_end < pattern.length()) {
 4975    static_fragments_.push_back(pattern.substr(last_param_end));
 4976  }
 4977}
 4978
 4979bool PathParamsMatcher::match(Request &request) const {
 4980  request.matches = std::smatch();
 4981  request.path_params.clear();
 4982  request.path_params.reserve(param_names_.size());
 4983
 4984  // One past the position at which the path matched the pattern last time
 4985  std::size_t starting_pos = 0;
 4986  for (size_t i = 0; i < static_fragments_.size(); ++i) {
 4987    const auto &fragment = static_fragments_[i];
 4988
 4989    if (starting_pos + fragment.length() > request.path.length()) {
 4990      return false;
 4991    }
 4992
 4993    // Avoid unnecessary allocation by using strncmp instead of substr +
 4994    // comparison
 4995    if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),
 4996                     fragment.length()) != 0) {
 4997      return false;
 4998    }
 4999
 5000    starting_pos += fragment.length();
 5001
 5002    // Should only happen when we have a static fragment after a param
 5003    // Example: '/users/:id/subscriptions'
 5004    // The 'subscriptions' fragment here does not have a corresponding param
 5005    if (i >= param_names_.size()) { continue; }
 5006
 5007    auto sep_pos = request.path.find(separator, starting_pos);
 5008    if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }
 5009
 5010    const auto &param_name = param_names_[i];
 5011
 5012    request.path_params.emplace(
 5013        param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
 5014
 5015    // Mark everything up to '/' as matched
 5016    starting_pos = sep_pos + 1;
 5017  }
 5018  // Returns false if the path is longer than the pattern
 5019  return starting_pos >= request.path.length();
 5020}
 5021
 5022bool RegexMatcher::match(Request &request) const {
 5023  request.path_params.clear();
 5024  return std::regex_match(request.path, request.matches, regex_);
 5025}
 5026
 5027// Enclose IPv6 address in brackets if needed
 5028std::string prepare_host_string(const std::string &host) {
 5029  // Enclose IPv6 address in brackets (but not if already enclosed)
 5030  if (host.find(':') == std::string::npos ||
 5031      (!host.empty() && host[0] == '[')) {
 5032    // IPv4, hostname, or already bracketed IPv6
 5033    return host;
 5034  } else {
 5035    // IPv6 address without brackets
 5036    return "[" + host + "]";
 5037  }
 5038}
 5039
 5040std::string make_host_and_port_string(const std::string &host, int port,
 5041                                             bool is_ssl) {
 5042  auto result = prepare_host_string(host);
 5043
 5044  // Append port if not default
 5045  if ((!is_ssl && port == 80) || (is_ssl && port == 443)) {
 5046    ; // do nothing
 5047  } else {
 5048    result += ":" + std::to_string(port);
 5049  }
 5050
 5051  return result;
 5052}
 5053
 5054// Create "host:port" string always including port number (for CONNECT method)
 5055std::string
 5056make_host_and_port_string_always_port(const std::string &host, int port) {
 5057  return prepare_host_string(host) + ":" + std::to_string(port);
 5058}
 5059
 5060template <typename T>
 5061bool check_and_write_headers(Stream &strm, Headers &headers,
 5062                                    T header_writer, Error &error) {
 5063  for (const auto &h : headers) {
 5064    if (!detail::fields::is_field_name(h.first) ||
 5065        !detail::fields::is_field_value(h.second)) {
 5066      error = Error::InvalidHeaders;
 5067      return false;
 5068    }
 5069  }
 5070  if (header_writer(strm, headers) <= 0) {
 5071    error = Error::Write;
 5072    return false;
 5073  }
 5074  return true;
 5075}
 5076
 5077} // namespace detail
 5078
 5079// HTTP server implementation
 5080Server::Server()
 5081    : new_task_queue(
 5082          [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
 5083#ifndef _WIN32
 5084  signal(SIGPIPE, SIG_IGN);
 5085#endif
 5086}
 5087
 5088Server::~Server() = default;
 5089
 5090std::unique_ptr<detail::MatcherBase>
 5091Server::make_matcher(const std::string &pattern) {
 5092  if (pattern.find("/:") != std::string::npos) {
 5093    return detail::make_unique<detail::PathParamsMatcher>(pattern);
 5094  } else {
 5095    return detail::make_unique<detail::RegexMatcher>(pattern);
 5096  }
 5097}
 5098
 5099Server &Server::Get(const std::string &pattern, Handler handler) {
 5100  get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
 5101  return *this;
 5102}
 5103
 5104Server &Server::Post(const std::string &pattern, Handler handler) {
 5105  post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
 5106  return *this;
 5107}
 5108
 5109Server &Server::Post(const std::string &pattern,
 5110                            HandlerWithContentReader handler) {
 5111  post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
 5112                                                 std::move(handler));
 5113  return *this;
 5114}
 5115
 5116Server &Server::Put(const std::string &pattern, Handler handler) {
 5117  put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
 5118  return *this;
 5119}
 5120
 5121Server &Server::Put(const std::string &pattern,
 5122                           HandlerWithContentReader handler) {
 5123  put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
 5124                                                std::move(handler));
 5125  return *this;
 5126}
 5127
 5128Server &Server::Patch(const std::string &pattern, Handler handler) {
 5129  patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
 5130  return *this;
 5131}
 5132
 5133Server &Server::Patch(const std::string &pattern,
 5134                             HandlerWithContentReader handler) {
 5135  patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
 5136                                                  std::move(handler));
 5137  return *this;
 5138}
 5139
 5140Server &Server::Delete(const std::string &pattern, Handler handler) {
 5141  delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
 5142  return *this;
 5143}
 5144
 5145Server &Server::Delete(const std::string &pattern,
 5146                              HandlerWithContentReader handler) {
 5147  delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
 5148                                                   std::move(handler));
 5149  return *this;
 5150}
 5151
 5152Server &Server::Options(const std::string &pattern, Handler handler) {
 5153  options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
 5154  return *this;
 5155}
 5156
 5157bool Server::set_base_dir(const std::string &dir,
 5158                                 const std::string &mount_point) {
 5159  return set_mount_point(mount_point, dir);
 5160}
 5161
 5162bool Server::set_mount_point(const std::string &mount_point,
 5163                                    const std::string &dir, Headers headers) {
 5164  detail::FileStat stat(dir);
 5165  if (stat.is_dir()) {
 5166    std::string mnt = !mount_point.empty() ? mount_point : "/";
 5167    if (!mnt.empty() && mnt[0] == '/') {
 5168      base_dirs_.push_back({std::move(mnt), dir, std::move(headers)});
 5169      return true;
 5170    }
 5171  }
 5172  return false;
 5173}
 5174
 5175bool Server::remove_mount_point(const std::string &mount_point) {
 5176  for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
 5177    if (it->mount_point == mount_point) {
 5178      base_dirs_.erase(it);
 5179      return true;
 5180    }
 5181  }
 5182  return false;
 5183}
 5184
 5185Server &
 5186Server::set_file_extension_and_mimetype_mapping(const std::string &ext,
 5187                                                const std::string &mime) {
 5188  file_extension_and_mimetype_map_[ext] = mime;
 5189  return *this;
 5190}
 5191
 5192Server &Server::set_default_file_mimetype(const std::string &mime) {
 5193  default_file_mimetype_ = mime;
 5194  return *this;
 5195}
 5196
 5197Server &Server::set_file_request_handler(Handler handler) {
 5198  file_request_handler_ = std::move(handler);
 5199  return *this;
 5200}
 5201
 5202Server &Server::set_error_handler_core(HandlerWithResponse handler,
 5203                                              std::true_type) {
 5204  error_handler_ = std::move(handler);
 5205  return *this;
 5206}
 5207
 5208Server &Server::set_error_handler_core(Handler handler,
 5209                                              std::false_type) {
 5210  error_handler_ = [handler](const Request &req, Response &res) {
 5211    handler(req, res);
 5212    return HandlerResponse::Handled;
 5213  };
 5214  return *this;
 5215}
 5216
 5217Server &Server::set_exception_handler(ExceptionHandler handler) {
 5218  exception_handler_ = std::move(handler);
 5219  return *this;
 5220}
 5221
 5222Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
 5223  pre_routing_handler_ = std::move(handler);
 5224  return *this;
 5225}
 5226
 5227Server &Server::set_post_routing_handler(Handler handler) {
 5228  post_routing_handler_ = std::move(handler);
 5229  return *this;
 5230}
 5231
 5232Server &Server::set_pre_request_handler(HandlerWithResponse handler) {
 5233  pre_request_handler_ = std::move(handler);
 5234  return *this;
 5235}
 5236
 5237Server &Server::set_logger(Logger logger) {
 5238  logger_ = std::move(logger);
 5239  return *this;
 5240}
 5241
 5242Server &Server::set_error_logger(ErrorLogger error_logger) {
 5243  error_logger_ = std::move(error_logger);
 5244  return *this;
 5245}
 5246
 5247Server &Server::set_pre_compression_logger(Logger logger) {
 5248  pre_compression_logger_ = std::move(logger);
 5249  return *this;
 5250}
 5251
 5252Server &
 5253Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
 5254  expect_100_continue_handler_ = std::move(handler);
 5255  return *this;
 5256}
 5257
 5258Server &Server::set_address_family(int family) {
 5259  address_family_ = family;
 5260  return *this;
 5261}
 5262
 5263Server &Server::set_tcp_nodelay(bool on) {
 5264  tcp_nodelay_ = on;
 5265  return *this;
 5266}
 5267
 5268Server &Server::set_ipv6_v6only(bool on) {
 5269  ipv6_v6only_ = on;
 5270  return *this;
 5271}
 5272
 5273Server &Server::set_socket_options(SocketOptions socket_options) {
 5274  socket_options_ = std::move(socket_options);
 5275  return *this;
 5276}
 5277
 5278Server &Server::set_default_headers(Headers headers) {
 5279  default_headers_ = std::move(headers);
 5280  return *this;
 5281}
 5282
 5283Server &Server::set_header_writer(
 5284    std::function<ssize_t(Stream &, Headers &)> const &writer) {
 5285  header_writer_ = writer;
 5286  return *this;
 5287}
 5288
 5289Server &
 5290Server::set_trusted_proxies(const std::vector<std::string> &proxies) {
 5291  trusted_proxies_ = proxies;
 5292  return *this;
 5293}
 5294
 5295Server &Server::set_keep_alive_max_count(size_t count) {
 5296  keep_alive_max_count_ = count;
 5297  return *this;
 5298}
 5299
 5300Server &Server::set_keep_alive_timeout(time_t sec) {
 5301  keep_alive_timeout_sec_ = sec;
 5302  return *this;
 5303}
 5304
 5305Server &Server::set_read_timeout(time_t sec, time_t usec) {
 5306  read_timeout_sec_ = sec;
 5307  read_timeout_usec_ = usec;
 5308  return *this;
 5309}
 5310
 5311Server &Server::set_write_timeout(time_t sec, time_t usec) {
 5312  write_timeout_sec_ = sec;
 5313  write_timeout_usec_ = usec;
 5314  return *this;
 5315}
 5316
 5317Server &Server::set_idle_interval(time_t sec, time_t usec) {
 5318  idle_interval_sec_ = sec;
 5319  idle_interval_usec_ = usec;
 5320  return *this;
 5321}
 5322
 5323Server &Server::set_payload_max_length(size_t length) {
 5324  payload_max_length_ = length;
 5325  return *this;
 5326}
 5327
 5328bool Server::bind_to_port(const std::string &host, int port,
 5329                                 int socket_flags) {
 5330  auto ret = bind_internal(host, port, socket_flags);
 5331  if (ret == -1) { is_decommissioned = true; }
 5332  return ret >= 0;
 5333}
 5334int Server::bind_to_any_port(const std::string &host, int socket_flags) {
 5335  auto ret = bind_internal(host, 0, socket_flags);
 5336  if (ret == -1) { is_decommissioned = true; }
 5337  return ret;
 5338}
 5339
 5340bool Server::listen_after_bind() { return listen_internal(); }
 5341
 5342bool Server::listen(const std::string &host, int port,
 5343                           int socket_flags) {
 5344  return bind_to_port(host, port, socket_flags) && listen_internal();
 5345}
 5346
 5347bool Server::is_running() const { return is_running_; }
 5348
 5349void Server::wait_until_ready() const {
 5350  while (!is_running_ && !is_decommissioned) {
 5351    std::this_thread::sleep_for(std::chrono::milliseconds{1});
 5352  }
 5353}
 5354
 5355void Server::stop() {
 5356  if (is_running_) {
 5357    assert(svr_sock_ != INVALID_SOCKET);
 5358    std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
 5359    detail::shutdown_socket(sock);
 5360    detail::close_socket(sock);
 5361  }
 5362  is_decommissioned = false;
 5363}
 5364
 5365void Server::decommission() { is_decommissioned = true; }
 5366
 5367bool Server::parse_request_line(const char *s, Request &req) const {
 5368  auto len = strlen(s);
 5369  if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
 5370  len -= 2;
 5371
 5372  {
 5373    size_t count = 0;
 5374
 5375    detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
 5376      switch (count) {
 5377      case 0: req.method = std::string(b, e); break;
 5378      case 1: req.target = std::string(b, e); break;
 5379      case 2: req.version = std::string(b, e); break;
 5380      default: break;
 5381      }
 5382      count++;
 5383    });
 5384
 5385    if (count != 3) { return false; }
 5386  }
 5387
 5388  thread_local const std::set<std::string> methods{
 5389      "GET",     "HEAD",    "POST",  "PUT",   "DELETE",
 5390      "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
 5391
 5392  if (methods.find(req.method) == methods.end()) {
 5393    output_error_log(Error::InvalidHTTPMethod, &req);
 5394    return false;
 5395  }
 5396
 5397  if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") {
 5398    output_error_log(Error::InvalidHTTPVersion, &req);
 5399    return false;
 5400  }
 5401
 5402  {
 5403    // Skip URL fragment
 5404    for (size_t i = 0; i < req.target.size(); i++) {
 5405      if (req.target[i] == '#') {
 5406        req.target.erase(i);
 5407        break;
 5408      }
 5409    }
 5410
 5411    detail::divide(req.target, '?',
 5412                   [&](const char *lhs_data, std::size_t lhs_size,
 5413                       const char *rhs_data, std::size_t rhs_size) {
 5414                     req.path =
 5415                         decode_path_component(std::string(lhs_data, lhs_size));
 5416                     detail::parse_query_text(rhs_data, rhs_size, req.params);
 5417                   });
 5418  }
 5419
 5420  return true;
 5421}
 5422
 5423bool Server::write_response(Stream &strm, bool close_connection,
 5424                                   Request &req, Response &res) {
 5425  // NOTE: `req.ranges` should be empty, otherwise it will be applied
 5426  // incorrectly to the error content.
 5427  req.ranges.clear();
 5428  return write_response_core(strm, close_connection, req, res, false);
 5429}
 5430
 5431bool Server::write_response_with_content(Stream &strm,
 5432                                                bool close_connection,
 5433                                                const Request &req,
 5434                                                Response &res) {
 5435  return write_response_core(strm, close_connection, req, res, true);
 5436}
 5437
 5438bool Server::write_response_core(Stream &strm, bool close_connection,
 5439                                        const Request &req, Response &res,
 5440                                        bool need_apply_ranges) {
 5441  assert(res.status != -1);
 5442
 5443  if (400 <= res.status && error_handler_ &&
 5444      error_handler_(req, res) == HandlerResponse::Handled) {
 5445    need_apply_ranges = true;
 5446  }
 5447
 5448  std::string content_type;
 5449  std::string boundary;
 5450  if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
 5451
 5452  // Prepare additional headers
 5453  if (close_connection || req.get_header_value("Connection") == "close" ||
 5454      400 <= res.status) { // Don't leave connections open after errors
 5455    res.set_header("Connection", "close");
 5456  } else {
 5457    std::string s = "timeout=";
 5458    s += std::to_string(keep_alive_timeout_sec_);
 5459    s += ", max=";
 5460    s += std::to_string(keep_alive_max_count_);
 5461    res.set_header("Keep-Alive", s);
 5462  }
 5463
 5464  if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
 5465      !res.has_header("Content-Type")) {
 5466    res.set_header("Content-Type", "text/plain");
 5467  }
 5468
 5469  if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
 5470      !res.has_header("Content-Length")) {
 5471    res.set_header("Content-Length", "0");
 5472  }
 5473
 5474  if (req.method == "HEAD" && !res.has_header("Accept-Ranges")) {
 5475    res.set_header("Accept-Ranges", "bytes");
 5476  }
 5477
 5478  if (post_routing_handler_) { post_routing_handler_(req, res); }
 5479
 5480  // Response line and headers
 5481  {
 5482    detail::BufferStream bstrm;
 5483    if (!detail::write_response_line(bstrm, res.status)) { return false; }
 5484    if (header_writer_(bstrm, res.headers) <= 0) { return false; }
 5485
 5486    // Flush buffer
 5487    auto &data = bstrm.get_buffer();
 5488    detail::write_data(strm, data.data(), data.size());
 5489  }
 5490
 5491  // Body
 5492  auto ret = true;
 5493  if (req.method != "HEAD") {
 5494    if (!res.body.empty()) {
 5495      if (!detail::write_data(strm, res.body.data(), res.body.size())) {
 5496        ret = false;
 5497      }
 5498    } else if (res.content_provider_) {
 5499      if (write_content_with_provider(strm, req, res, boundary, content_type)) {
 5500        res.content_provider_success_ = true;
 5501      } else {
 5502        ret = false;
 5503      }
 5504    }
 5505  }
 5506
 5507  // Log
 5508  output_log(req, res);
 5509
 5510  return ret;
 5511}
 5512
 5513bool
 5514Server::write_content_with_provider(Stream &strm, const Request &req,
 5515                                    Response &res, const std::string &boundary,
 5516                                    const std::string &content_type) {
 5517  auto is_shutting_down = [this]() {
 5518    return this->svr_sock_ == INVALID_SOCKET;
 5519  };
 5520
 5521  if (res.content_length_ > 0) {
 5522    if (req.ranges.empty()) {
 5523      return detail::write_content(strm, res.content_provider_, 0,
 5524                                   res.content_length_, is_shutting_down);
 5525    } else if (req.ranges.size() == 1) {
 5526      auto offset_and_length = detail::get_range_offset_and_length(
 5527          req.ranges[0], res.content_length_);
 5528
 5529      return detail::write_content(strm, res.content_provider_,
 5530                                   offset_and_length.first,
 5531                                   offset_and_length.second, is_shutting_down);
 5532    } else {
 5533      return detail::write_multipart_ranges_data(
 5534          strm, req, res, boundary, content_type, res.content_length_,
 5535          is_shutting_down);
 5536    }
 5537  } else {
 5538    if (res.is_chunked_content_provider_) {
 5539      auto type = detail::encoding_type(req, res);
 5540
 5541      std::unique_ptr<detail::compressor> compressor;
 5542      if (type == detail::EncodingType::Gzip) {
 5543#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 5544        compressor = detail::make_unique<detail::gzip_compressor>();
 5545#endif
 5546      } else if (type == detail::EncodingType::Brotli) {
 5547#ifdef CPPHTTPLIB_BROTLI_SUPPORT
 5548        compressor = detail::make_unique<detail::brotli_compressor>();
 5549#endif
 5550      } else if (type == detail::EncodingType::Zstd) {
 5551#ifdef CPPHTTPLIB_ZSTD_SUPPORT
 5552        compressor = detail::make_unique<detail::zstd_compressor>();
 5553#endif
 5554      } else {
 5555        compressor = detail::make_unique<detail::nocompressor>();
 5556      }
 5557      assert(compressor != nullptr);
 5558
 5559      return detail::write_content_chunked(strm, res.content_provider_,
 5560                                           is_shutting_down, *compressor);
 5561    } else {
 5562      return detail::write_content_without_length(strm, res.content_provider_,
 5563                                                  is_shutting_down);
 5564    }
 5565  }
 5566}
 5567
 5568bool Server::read_content(Stream &strm, Request &req, Response &res) {
 5569  FormFields::iterator cur_field;
 5570  FormFiles::iterator cur_file;
 5571  auto is_text_field = false;
 5572  size_t count = 0;
 5573  if (read_content_core(
 5574          strm, req, res,
 5575          // Regular
 5576          [&](const char *buf, size_t n) {
 5577            // Prevent arithmetic overflow when checking sizes.
 5578            // Avoid computing (req.body.size() + n) directly because
 5579            // adding two unsigned `size_t` values can wrap around and
 5580            // produce a small result instead of indicating overflow.
 5581            // Instead, check using subtraction: ensure `n` does not
 5582            // exceed the remaining capacity `max_size() - size()`.
 5583            if (req.body.size() >= req.body.max_size() ||
 5584                n > req.body.max_size() - req.body.size()) {
 5585              return false;
 5586            }
 5587
 5588            // Limit decompressed body size to payload_max_length_ to protect
 5589            // against "zip bomb" attacks where a small compressed payload
 5590            // decompresses to a massive size.
 5591            if (payload_max_length_ > 0 &&
 5592                (req.body.size() >= payload_max_length_ ||
 5593                 n > payload_max_length_ - req.body.size())) {
 5594              return false;
 5595            }
 5596
 5597            req.body.append(buf, n);
 5598            return true;
 5599          },
 5600          // Multipart FormData
 5601          [&](const FormData &file) {
 5602            if (count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
 5603              output_error_log(Error::TooManyFormDataFiles, &req);
 5604              return false;
 5605            }
 5606
 5607            if (file.filename.empty()) {
 5608              cur_field = req.form.fields.emplace(
 5609                  file.name, FormField{file.name, file.content, file.headers});
 5610              is_text_field = true;
 5611            } else {
 5612              cur_file = req.form.files.emplace(file.name, file);
 5613              is_text_field = false;
 5614            }
 5615            return true;
 5616          },
 5617          [&](const char *buf, size_t n) {
 5618            if (is_text_field) {
 5619              auto &content = cur_field->second.content;
 5620              if (content.size() + n > content.max_size()) { return false; }
 5621              content.append(buf, n);
 5622            } else {
 5623              auto &content = cur_file->second.content;
 5624              if (content.size() + n > content.max_size()) { return false; }
 5625              content.append(buf, n);
 5626            }
 5627            return true;
 5628          })) {
 5629    const auto &content_type = req.get_header_value("Content-Type");
 5630    if (!content_type.find("application/x-www-form-urlencoded")) {
 5631      if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
 5632        res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
 5633        output_error_log(Error::ExceedMaxPayloadSize, &req);
 5634        return false;
 5635      }
 5636      detail::parse_query_text(req.body, req.params);
 5637    }
 5638    return true;
 5639  }
 5640  return false;
 5641}
 5642
 5643bool Server::read_content_with_content_receiver(
 5644    Stream &strm, Request &req, Response &res, ContentReceiver receiver,
 5645    FormDataHeader multipart_header, ContentReceiver multipart_receiver) {
 5646  return read_content_core(strm, req, res, std::move(receiver),
 5647                           std::move(multipart_header),
 5648                           std::move(multipart_receiver));
 5649}
 5650
 5651bool Server::read_content_core(
 5652    Stream &strm, Request &req, Response &res, ContentReceiver receiver,
 5653    FormDataHeader multipart_header, ContentReceiver multipart_receiver) const {
 5654  detail::FormDataParser multipart_form_data_parser;
 5655  ContentReceiverWithProgress out;
 5656
 5657  if (req.is_multipart_form_data()) {
 5658    const auto &content_type = req.get_header_value("Content-Type");
 5659    std::string boundary;
 5660    if (!detail::parse_multipart_boundary(content_type, boundary)) {
 5661      res.status = StatusCode::BadRequest_400;
 5662      output_error_log(Error::MultipartParsing, &req);
 5663      return false;
 5664    }
 5665
 5666    multipart_form_data_parser.set_boundary(std::move(boundary));
 5667    out = [&](const char *buf, size_t n, size_t /*off*/, size_t /*len*/) {
 5668      return multipart_form_data_parser.parse(buf, n, multipart_header,
 5669                                              multipart_receiver);
 5670    };
 5671  } else {
 5672    out = [receiver](const char *buf, size_t n, size_t /*off*/,
 5673                     size_t /*len*/) { return receiver(buf, n); };
 5674  }
 5675
 5676  // RFC 7230 Section 3.3.3: If this is a request message and none of the above
 5677  // are true (no Transfer-Encoding and no Content-Length), then the message
 5678  // body length is zero (no message body is present).
 5679  //
 5680  // For non-SSL builds, peek into the socket to detect clients that send a
 5681  // body without a Content-Length header (raw HTTP over TCP). If there is
 5682  // pending data that exceeds the configured payload limit, treat this as an
 5683  // oversized request and fail early (causing connection close). For SSL
 5684  // builds we cannot reliably peek the decrypted application bytes, so keep
 5685  // the original behaviour.
 5686#if !defined(CPPHTTPLIB_OPENSSL_SUPPORT)
 5687  if (!req.has_header("Content-Length") &&
 5688      !detail::is_chunked_transfer_encoding(req.headers)) {
 5689    // Only peek if payload_max_length is set to a finite value
 5690    if (payload_max_length_ > 0 &&
 5691        payload_max_length_ < (std::numeric_limits<size_t>::max)()) {
 5692      socket_t s = strm.socket();
 5693      if (s != INVALID_SOCKET) {
 5694        // Peek to check if there is any pending data
 5695        char peekbuf[1];
 5696        ssize_t n = ::recv(s, peekbuf, 1, MSG_PEEK);
 5697        if (n > 0) {
 5698          // There is data, so read it with payload limit enforcement
 5699          auto result = detail::read_content_without_length(
 5700              strm, payload_max_length_, out);
 5701          if (result == detail::ReadContentResult::PayloadTooLarge) {
 5702            res.status = StatusCode::PayloadTooLarge_413;
 5703            return false;
 5704          } else if (result != detail::ReadContentResult::Success) {
 5705            return false;
 5706          }
 5707          return true;
 5708        }
 5709      }
 5710    }
 5711    return true;
 5712  }
 5713#else
 5714  if (!req.has_header("Content-Length") &&
 5715      !detail::is_chunked_transfer_encoding(req.headers)) {
 5716    return true;
 5717  }
 5718#endif
 5719
 5720  if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
 5721                            out, true)) {
 5722    return false;
 5723  }
 5724
 5725  if (req.is_multipart_form_data()) {
 5726    if (!multipart_form_data_parser.is_valid()) {
 5727      res.status = StatusCode::BadRequest_400;
 5728      output_error_log(Error::MultipartParsing, &req);
 5729      return false;
 5730    }
 5731  }
 5732
 5733  return true;
 5734}
 5735
 5736bool Server::handle_file_request(Request &req, Response &res) {
 5737  for (const auto &entry : base_dirs_) {
 5738    // Prefix match
 5739    if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
 5740      std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
 5741      if (detail::is_valid_path(sub_path)) {
 5742        auto path = entry.base_dir + sub_path;
 5743        if (path.back() == '/') { path += "index.html"; }
 5744
 5745        detail::FileStat stat(path);
 5746
 5747        if (stat.is_dir()) {
 5748          res.set_redirect(sub_path + "/", StatusCode::MovedPermanently_301);
 5749          return true;
 5750        }
 5751
 5752        if (stat.is_file()) {
 5753          for (const auto &kv : entry.headers) {
 5754            res.set_header(kv.first, kv.second);
 5755          }
 5756
 5757          auto etag = detail::compute_etag(stat);
 5758          if (!etag.empty()) { res.set_header("ETag", etag); }
 5759
 5760          auto mtime = stat.mtime();
 5761
 5762          auto last_modified = detail::file_mtime_to_http_date(mtime);
 5763          if (!last_modified.empty()) {
 5764            res.set_header("Last-Modified", last_modified);
 5765          }
 5766
 5767          if (check_if_not_modified(req, res, etag, mtime)) { return true; }
 5768
 5769          check_if_range(req, etag, mtime);
 5770
 5771          auto mm = std::make_shared<detail::mmap>(path.c_str());
 5772          if (!mm->is_open()) {
 5773            output_error_log(Error::OpenFile, &req);
 5774            return false;
 5775          }
 5776
 5777          res.set_content_provider(
 5778              mm->size(),
 5779              detail::find_content_type(path, file_extension_and_mimetype_map_,
 5780                                        default_file_mimetype_),
 5781              [mm](size_t offset, size_t length, DataSink &sink) -> bool {
 5782                sink.write(mm->data() + offset, length);
 5783                return true;
 5784              });
 5785
 5786          if (req.method != "HEAD" && file_request_handler_) {
 5787            file_request_handler_(req, res);
 5788          }
 5789
 5790          return true;
 5791        } else {
 5792          output_error_log(Error::OpenFile, &req);
 5793        }
 5794      }
 5795    }
 5796  }
 5797  return false;
 5798}
 5799
 5800bool Server::check_if_not_modified(const Request &req, Response &res,
 5801                                          const std::string &etag,
 5802                                          time_t mtime) const {
 5803  // Handle conditional GET:
 5804  // 1. If-None-Match takes precedence (RFC 9110 Section 13.1.2)
 5805  // 2. If-Modified-Since is checked only when If-None-Match is absent
 5806  if (req.has_header("If-None-Match")) {
 5807    if (!etag.empty()) {
 5808      auto val = req.get_header_value("If-None-Match");
 5809
 5810      // NOTE: We use exact string matching here. This works correctly
 5811      // because our server always generates weak ETags (W/"..."), and
 5812      // clients typically send back the same ETag they received.
 5813      // RFC 9110 Section 8.8.3.2 allows weak comparison for
 5814      // If-None-Match, where W/"x" and "x" would match, but this
 5815      // simplified implementation requires exact matches.
 5816      auto ret = detail::split_find(val.data(), val.data() + val.size(), ',',
 5817                                    [&](const char *b, const char *e) {
 5818                                      return std::equal(b, e, "*") ||
 5819                                             std::equal(b, e, etag.begin());
 5820                                    });
 5821
 5822      if (ret) {
 5823        res.status = StatusCode::NotModified_304;
 5824        return true;
 5825      }
 5826    }
 5827  } else if (req.has_header("If-Modified-Since")) {
 5828    auto val = req.get_header_value("If-Modified-Since");
 5829    auto t = detail::parse_http_date(val);
 5830
 5831    if (t != static_cast<time_t>(-1) && mtime <= t) {
 5832      res.status = StatusCode::NotModified_304;
 5833      return true;
 5834    }
 5835  }
 5836  return false;
 5837}
 5838
 5839bool Server::check_if_range(Request &req, const std::string &etag,
 5840                                   time_t mtime) const {
 5841  // Handle If-Range for partial content requests (RFC 9110
 5842  // Section 13.1.5). If-Range is only evaluated when Range header is
 5843  // present. If the validator matches, serve partial content; otherwise
 5844  // serve full content.
 5845  if (!req.ranges.empty() && req.has_header("If-Range")) {
 5846    auto val = req.get_header_value("If-Range");
 5847
 5848    auto is_valid_range = [&]() {
 5849      if (detail::is_strong_etag(val)) {
 5850        // RFC 9110 Section 13.1.5: If-Range requires strong ETag
 5851        // comparison.
 5852        return (!etag.empty() && val == etag);
 5853      } else if (detail::is_weak_etag(val)) {
 5854        // Weak ETags are not valid for If-Range (RFC 9110 Section 13.1.5)
 5855        return false;
 5856      } else {
 5857        // HTTP-date comparison
 5858        auto t = detail::parse_http_date(val);
 5859        return (t != static_cast<time_t>(-1) && mtime <= t);
 5860      }
 5861    };
 5862
 5863    if (!is_valid_range()) {
 5864      // Validator doesn't match: ignore Range and serve full content
 5865      req.ranges.clear();
 5866      return false;
 5867    }
 5868  }
 5869
 5870  return true;
 5871}
 5872
 5873socket_t
 5874Server::create_server_socket(const std::string &host, int port,
 5875                             int socket_flags,
 5876                             SocketOptions socket_options) const {
 5877  return detail::create_socket(
 5878      host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
 5879      ipv6_v6only_, std::move(socket_options),
 5880      [&](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
 5881        if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
 5882          output_error_log(Error::BindIPAddress, nullptr);
 5883          return false;
 5884        }
 5885        if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
 5886          output_error_log(Error::Listen, nullptr);
 5887          return false;
 5888        }
 5889        return true;
 5890      });
 5891}
 5892
 5893int Server::bind_internal(const std::string &host, int port,
 5894                                 int socket_flags) {
 5895  if (is_decommissioned) { return -1; }
 5896
 5897  if (!is_valid()) { return -1; }
 5898
 5899  svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
 5900  if (svr_sock_ == INVALID_SOCKET) { return -1; }
 5901
 5902  if (port == 0) {
 5903    struct sockaddr_storage addr;
 5904    socklen_t addr_len = sizeof(addr);
 5905    if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
 5906                    &addr_len) == -1) {
 5907      output_error_log(Error::GetSockName, nullptr);
 5908      return -1;
 5909    }
 5910    if (addr.ss_family == AF_INET) {
 5911      return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
 5912    } else if (addr.ss_family == AF_INET6) {
 5913      return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
 5914    } else {
 5915      output_error_log(Error::UnsupportedAddressFamily, nullptr);
 5916      return -1;
 5917    }
 5918  } else {
 5919    return port;
 5920  }
 5921}
 5922
 5923bool Server::listen_internal() {
 5924  if (is_decommissioned) { return false; }
 5925
 5926  auto ret = true;
 5927  is_running_ = true;
 5928  auto se = detail::scope_exit([&]() { is_running_ = false; });
 5929
 5930  {
 5931    std::unique_ptr<TaskQueue> task_queue(new_task_queue());
 5932
 5933    while (svr_sock_ != INVALID_SOCKET) {
 5934#ifndef _WIN32
 5935      if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
 5936#endif
 5937        auto val = detail::select_read(svr_sock_, idle_interval_sec_,
 5938                                       idle_interval_usec_);
 5939        if (val == 0) { // Timeout
 5940          task_queue->on_idle();
 5941          continue;
 5942        }
 5943#ifndef _WIN32
 5944      }
 5945#endif
 5946
 5947#if defined _WIN32
 5948      // sockets connected via WASAccept inherit flags NO_HANDLE_INHERIT,
 5949      // OVERLAPPED
 5950      socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
 5951#elif defined SOCK_CLOEXEC
 5952      socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);
 5953#else
 5954      socket_t sock = accept(svr_sock_, nullptr, nullptr);
 5955#endif
 5956
 5957      if (sock == INVALID_SOCKET) {
 5958        if (errno == EMFILE) {
 5959          // The per-process limit of open file descriptors has been reached.
 5960          // Try to accept new connections after a short sleep.
 5961          std::this_thread::sleep_for(std::chrono::microseconds{1});
 5962          continue;
 5963        } else if (errno == EINTR || errno == EAGAIN) {
 5964          continue;
 5965        }
 5966        if (svr_sock_ != INVALID_SOCKET) {
 5967          detail::close_socket(svr_sock_);
 5968          ret = false;
 5969          output_error_log(Error::Connection, nullptr);
 5970        } else {
 5971          ; // The server socket was closed by user.
 5972        }
 5973        break;
 5974      }
 5975
 5976      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
 5977                                  read_timeout_sec_, read_timeout_usec_);
 5978      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
 5979                                  write_timeout_sec_, write_timeout_usec_);
 5980
 5981      if (!task_queue->enqueue(
 5982              [this, sock]() { process_and_close_socket(sock); })) {
 5983        output_error_log(Error::ResourceExhaustion, nullptr);
 5984        detail::shutdown_socket(sock);
 5985        detail::close_socket(sock);
 5986      }
 5987    }
 5988
 5989    task_queue->shutdown();
 5990  }
 5991
 5992  is_decommissioned = !ret;
 5993  return ret;
 5994}
 5995
 5996bool Server::routing(Request &req, Response &res, Stream &strm) {
 5997  if (pre_routing_handler_ &&
 5998      pre_routing_handler_(req, res) == HandlerResponse::Handled) {
 5999    return true;
 6000  }
 6001
 6002  // File handler
 6003  if ((req.method == "GET" || req.method == "HEAD") &&
 6004      handle_file_request(req, res)) {
 6005    return true;
 6006  }
 6007
 6008  if (detail::expect_content(req)) {
 6009    // Content reader handler
 6010    {
 6011      ContentReader reader(
 6012          [&](ContentReceiver receiver) {
 6013            auto result = read_content_with_content_receiver(
 6014                strm, req, res, std::move(receiver), nullptr, nullptr);
 6015            if (!result) { output_error_log(Error::Read, &req); }
 6016            return result;
 6017          },
 6018          [&](FormDataHeader header, ContentReceiver receiver) {
 6019            auto result = read_content_with_content_receiver(
 6020                strm, req, res, nullptr, std::move(header),
 6021                std::move(receiver));
 6022            if (!result) { output_error_log(Error::Read, &req); }
 6023            return result;
 6024          });
 6025
 6026      if (req.method == "POST") {
 6027        if (dispatch_request_for_content_reader(
 6028                req, res, std::move(reader),
 6029                post_handlers_for_content_reader_)) {
 6030          return true;
 6031        }
 6032      } else if (req.method == "PUT") {
 6033        if (dispatch_request_for_content_reader(
 6034                req, res, std::move(reader),
 6035                put_handlers_for_content_reader_)) {
 6036          return true;
 6037        }
 6038      } else if (req.method == "PATCH") {
 6039        if (dispatch_request_for_content_reader(
 6040                req, res, std::move(reader),
 6041                patch_handlers_for_content_reader_)) {
 6042          return true;
 6043        }
 6044      } else if (req.method == "DELETE") {
 6045        if (dispatch_request_for_content_reader(
 6046                req, res, std::move(reader),
 6047                delete_handlers_for_content_reader_)) {
 6048          return true;
 6049        }
 6050      }
 6051    }
 6052
 6053    // Read content into `req.body`
 6054    if (!read_content(strm, req, res)) {
 6055      output_error_log(Error::Read, &req);
 6056      return false;
 6057    }
 6058  }
 6059
 6060  // Regular handler
 6061  if (req.method == "GET" || req.method == "HEAD") {
 6062    return dispatch_request(req, res, get_handlers_);
 6063  } else if (req.method == "POST") {
 6064    return dispatch_request(req, res, post_handlers_);
 6065  } else if (req.method == "PUT") {
 6066    return dispatch_request(req, res, put_handlers_);
 6067  } else if (req.method == "DELETE") {
 6068    return dispatch_request(req, res, delete_handlers_);
 6069  } else if (req.method == "OPTIONS") {
 6070    return dispatch_request(req, res, options_handlers_);
 6071  } else if (req.method == "PATCH") {
 6072    return dispatch_request(req, res, patch_handlers_);
 6073  }
 6074
 6075  res.status = StatusCode::BadRequest_400;
 6076  return false;
 6077}
 6078
 6079bool Server::dispatch_request(Request &req, Response &res,
 6080                                     const Handlers &handlers) const {
 6081  for (const auto &x : handlers) {
 6082    const auto &matcher = x.first;
 6083    const auto &handler = x.second;
 6084
 6085    if (matcher->match(req)) {
 6086      req.matched_route = matcher->pattern();
 6087      if (!pre_request_handler_ ||
 6088          pre_request_handler_(req, res) != HandlerResponse::Handled) {
 6089        handler(req, res);
 6090      }
 6091      return true;
 6092    }
 6093  }
 6094  return false;
 6095}
 6096
 6097void Server::apply_ranges(const Request &req, Response &res,
 6098                                 std::string &content_type,
 6099                                 std::string &boundary) const {
 6100  if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
 6101    auto it = res.headers.find("Content-Type");
 6102    if (it != res.headers.end()) {
 6103      content_type = it->second;
 6104      res.headers.erase(it);
 6105    }
 6106
 6107    boundary = detail::make_multipart_data_boundary();
 6108
 6109    res.set_header("Content-Type",
 6110                   "multipart/byteranges; boundary=" + boundary);
 6111  }
 6112
 6113  auto type = detail::encoding_type(req, res);
 6114
 6115  if (res.body.empty()) {
 6116    if (res.content_length_ > 0) {
 6117      size_t length = 0;
 6118      if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
 6119        length = res.content_length_;
 6120      } else if (req.ranges.size() == 1) {
 6121        auto offset_and_length = detail::get_range_offset_and_length(
 6122            req.ranges[0], res.content_length_);
 6123
 6124        length = offset_and_length.second;
 6125
 6126        auto content_range = detail::make_content_range_header_field(
 6127            offset_and_length, res.content_length_);
 6128        res.set_header("Content-Range", content_range);
 6129      } else {
 6130        length = detail::get_multipart_ranges_data_length(
 6131            req, boundary, content_type, res.content_length_);
 6132      }
 6133      res.set_header("Content-Length", std::to_string(length));
 6134    } else {
 6135      if (res.content_provider_) {
 6136        if (res.is_chunked_content_provider_) {
 6137          res.set_header("Transfer-Encoding", "chunked");
 6138          if (type == detail::EncodingType::Gzip) {
 6139            res.set_header("Content-Encoding", "gzip");
 6140            res.set_header("Vary", "Accept-Encoding");
 6141          } else if (type == detail::EncodingType::Brotli) {
 6142            res.set_header("Content-Encoding", "br");
 6143            res.set_header("Vary", "Accept-Encoding");
 6144          } else if (type == detail::EncodingType::Zstd) {
 6145            res.set_header("Content-Encoding", "zstd");
 6146            res.set_header("Vary", "Accept-Encoding");
 6147          }
 6148        }
 6149      }
 6150    }
 6151  } else {
 6152    if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
 6153      ;
 6154    } else if (req.ranges.size() == 1) {
 6155      auto offset_and_length =
 6156          detail::get_range_offset_and_length(req.ranges[0], res.body.size());
 6157      auto offset = offset_and_length.first;
 6158      auto length = offset_and_length.second;
 6159
 6160      auto content_range = detail::make_content_range_header_field(
 6161          offset_and_length, res.body.size());
 6162      res.set_header("Content-Range", content_range);
 6163
 6164      assert(offset + length <= res.body.size());
 6165      res.body = res.body.substr(offset, length);
 6166    } else {
 6167      std::string data;
 6168      detail::make_multipart_ranges_data(req, res, boundary, content_type,
 6169                                         res.body.size(), data);
 6170      res.body.swap(data);
 6171    }
 6172
 6173    if (type != detail::EncodingType::None) {
 6174      output_pre_compression_log(req, res);
 6175
 6176      std::unique_ptr<detail::compressor> compressor;
 6177      std::string content_encoding;
 6178
 6179      if (type == detail::EncodingType::Gzip) {
 6180#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 6181        compressor = detail::make_unique<detail::gzip_compressor>();
 6182        content_encoding = "gzip";
 6183#endif
 6184      } else if (type == detail::EncodingType::Brotli) {
 6185#ifdef CPPHTTPLIB_BROTLI_SUPPORT
 6186        compressor = detail::make_unique<detail::brotli_compressor>();
 6187        content_encoding = "br";
 6188#endif
 6189      } else if (type == detail::EncodingType::Zstd) {
 6190#ifdef CPPHTTPLIB_ZSTD_SUPPORT
 6191        compressor = detail::make_unique<detail::zstd_compressor>();
 6192        content_encoding = "zstd";
 6193#endif
 6194      }
 6195
 6196      if (compressor) {
 6197        std::string compressed;
 6198        if (compressor->compress(res.body.data(), res.body.size(), true,
 6199                                 [&](const char *data, size_t data_len) {
 6200                                   compressed.append(data, data_len);
 6201                                   return true;
 6202                                 })) {
 6203          res.body.swap(compressed);
 6204          res.set_header("Content-Encoding", content_encoding);
 6205          res.set_header("Vary", "Accept-Encoding");
 6206        }
 6207      }
 6208    }
 6209
 6210    auto length = std::to_string(res.body.size());
 6211    res.set_header("Content-Length", length);
 6212  }
 6213}
 6214
 6215bool Server::dispatch_request_for_content_reader(
 6216    Request &req, Response &res, ContentReader content_reader,
 6217    const HandlersForContentReader &handlers) const {
 6218  for (const auto &x : handlers) {
 6219    const auto &matcher = x.first;
 6220    const auto &handler = x.second;
 6221
 6222    if (matcher->match(req)) {
 6223      req.matched_route = matcher->pattern();
 6224      if (!pre_request_handler_ ||
 6225          pre_request_handler_(req, res) != HandlerResponse::Handled) {
 6226        handler(req, res, content_reader);
 6227      }
 6228      return true;
 6229    }
 6230  }
 6231  return false;
 6232}
 6233
 6234std::string
 6235get_client_ip(const std::string &x_forwarded_for,
 6236              const std::vector<std::string> &trusted_proxies) {
 6237  // X-Forwarded-For is a comma-separated list per RFC 7239
 6238  std::vector<std::string> ip_list;
 6239  detail::split(x_forwarded_for.data(),
 6240                x_forwarded_for.data() + x_forwarded_for.size(), ',',
 6241                [&](const char *b, const char *e) {
 6242                  auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));
 6243                  ip_list.emplace_back(std::string(b + r.first, b + r.second));
 6244                });
 6245
 6246  for (size_t i = 0; i < ip_list.size(); ++i) {
 6247    auto ip = ip_list[i];
 6248
 6249    auto is_trusted_proxy =
 6250        std::any_of(trusted_proxies.begin(), trusted_proxies.end(),
 6251                    [&](const std::string &proxy) { return ip == proxy; });
 6252
 6253    if (is_trusted_proxy) {
 6254      if (i == 0) {
 6255        // If the trusted proxy is the first IP, there's no preceding client IP
 6256        return ip;
 6257      } else {
 6258        // Return the IP immediately before the trusted proxy
 6259        return ip_list[i - 1];
 6260      }
 6261    }
 6262  }
 6263
 6264  // If no trusted proxy is found, return the first IP in the list
 6265  return ip_list.front();
 6266}
 6267
 6268bool
 6269Server::process_request(Stream &strm, const std::string &remote_addr,
 6270                        int remote_port, const std::string &local_addr,
 6271                        int local_port, bool close_connection,
 6272                        bool &connection_closed,
 6273                        const std::function<void(Request &)> &setup_request) {
 6274  std::array<char, 2048> buf{};
 6275
 6276  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
 6277
 6278  // Connection has been closed on client
 6279  if (!line_reader.getline()) { return false; }
 6280
 6281  Request req;
 6282  req.start_time_ = std::chrono::steady_clock::now();
 6283  req.remote_addr = remote_addr;
 6284  req.remote_port = remote_port;
 6285  req.local_addr = local_addr;
 6286  req.local_port = local_port;
 6287
 6288  Response res;
 6289  res.version = "HTTP/1.1";
 6290  res.headers = default_headers_;
 6291
 6292#ifdef __APPLE__
 6293  // Socket file descriptor exceeded FD_SETSIZE...
 6294  if (strm.socket() >= FD_SETSIZE) {
 6295    Headers dummy;
 6296    detail::read_headers(strm, dummy);
 6297    res.status = StatusCode::InternalServerError_500;
 6298    output_error_log(Error::ExceedMaxSocketDescriptorCount, &req);
 6299    return write_response(strm, close_connection, req, res);
 6300  }
 6301#endif
 6302
 6303  // Request line and headers
 6304  if (!parse_request_line(line_reader.ptr(), req)) {
 6305    res.status = StatusCode::BadRequest_400;
 6306    output_error_log(Error::InvalidRequestLine, &req);
 6307    return write_response(strm, close_connection, req, res);
 6308  }
 6309
 6310  // Request headers
 6311  if (!detail::read_headers(strm, req.headers)) {
 6312    res.status = StatusCode::BadRequest_400;
 6313    output_error_log(Error::InvalidHeaders, &req);
 6314    return write_response(strm, close_connection, req, res);
 6315  }
 6316
 6317  // Check if the request URI doesn't exceed the limit
 6318  if (req.target.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
 6319    res.status = StatusCode::UriTooLong_414;
 6320    output_error_log(Error::ExceedUriMaxLength, &req);
 6321    return write_response(strm, close_connection, req, res);
 6322  }
 6323
 6324  if (req.get_header_value("Connection") == "close") {
 6325    connection_closed = true;
 6326  }
 6327
 6328  if (req.version == "HTTP/1.0" &&
 6329      req.get_header_value("Connection") != "Keep-Alive") {
 6330    connection_closed = true;
 6331  }
 6332
 6333  if (!trusted_proxies_.empty() && req.has_header("X-Forwarded-For")) {
 6334    auto x_forwarded_for = req.get_header_value("X-Forwarded-For");
 6335    req.remote_addr = get_client_ip(x_forwarded_for, trusted_proxies_);
 6336  } else {
 6337    req.remote_addr = remote_addr;
 6338  }
 6339  req.remote_port = remote_port;
 6340
 6341  req.local_addr = local_addr;
 6342  req.local_port = local_port;
 6343
 6344  if (req.has_header("Accept")) {
 6345    const auto &accept_header = req.get_header_value("Accept");
 6346    if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
 6347      res.status = StatusCode::BadRequest_400;
 6348      output_error_log(Error::HTTPParsing, &req);
 6349      return write_response(strm, close_connection, req, res);
 6350    }
 6351  }
 6352
 6353  if (req.has_header("Range")) {
 6354    const auto &range_header_value = req.get_header_value("Range");
 6355    if (!detail::parse_range_header(range_header_value, req.ranges)) {
 6356      res.status = StatusCode::RangeNotSatisfiable_416;
 6357      output_error_log(Error::InvalidRangeHeader, &req);
 6358      return write_response(strm, close_connection, req, res);
 6359    }
 6360  }
 6361
 6362  if (setup_request) { setup_request(req); }
 6363
 6364  if (req.get_header_value("Expect") == "100-continue") {
 6365    int status = StatusCode::Continue_100;
 6366    if (expect_100_continue_handler_) {
 6367      status = expect_100_continue_handler_(req, res);
 6368    }
 6369    switch (status) {
 6370    case StatusCode::Continue_100:
 6371    case StatusCode::ExpectationFailed_417:
 6372      detail::write_response_line(strm, status);
 6373      strm.write("\r\n");
 6374      break;
 6375    default:
 6376      connection_closed = true;
 6377      return write_response(strm, true, req, res);
 6378    }
 6379  }
 6380
 6381  // Setup `is_connection_closed` method
 6382  auto sock = strm.socket();
 6383  req.is_connection_closed = [sock]() {
 6384    return !detail::is_socket_alive(sock);
 6385  };
 6386
 6387  // Routing
 6388  auto routed = false;
 6389#ifdef CPPHTTPLIB_NO_EXCEPTIONS
 6390  routed = routing(req, res, strm);
 6391#else
 6392  try {
 6393    routed = routing(req, res, strm);
 6394  } catch (std::exception &e) {
 6395    if (exception_handler_) {
 6396      auto ep = std::current_exception();
 6397      exception_handler_(req, res, ep);
 6398      routed = true;
 6399    } else {
 6400      res.status = StatusCode::InternalServerError_500;
 6401      std::string val;
 6402      auto s = e.what();
 6403      for (size_t i = 0; s[i]; i++) {
 6404        switch (s[i]) {
 6405        case '\r': val += "\\r"; break;
 6406        case '\n': val += "\\n"; break;
 6407        default: val += s[i]; break;
 6408        }
 6409      }
 6410      res.set_header("EXCEPTION_WHAT", val);
 6411    }
 6412  } catch (...) {
 6413    if (exception_handler_) {
 6414      auto ep = std::current_exception();
 6415      exception_handler_(req, res, ep);
 6416      routed = true;
 6417    } else {
 6418      res.status = StatusCode::InternalServerError_500;
 6419      res.set_header("EXCEPTION_WHAT", "UNKNOWN");
 6420    }
 6421  }
 6422#endif
 6423  if (routed) {
 6424    if (res.status == -1) {
 6425      res.status = req.ranges.empty() ? StatusCode::OK_200
 6426                                      : StatusCode::PartialContent_206;
 6427    }
 6428
 6429    // Serve file content by using a content provider
 6430    if (!res.file_content_path_.empty()) {
 6431      const auto &path = res.file_content_path_;
 6432      auto mm = std::make_shared<detail::mmap>(path.c_str());
 6433      if (!mm->is_open()) {
 6434        res.body.clear();
 6435        res.content_length_ = 0;
 6436        res.content_provider_ = nullptr;
 6437        res.status = StatusCode::NotFound_404;
 6438        output_error_log(Error::OpenFile, &req);
 6439        return write_response(strm, close_connection, req, res);
 6440      }
 6441
 6442      auto content_type = res.file_content_content_type_;
 6443      if (content_type.empty()) {
 6444        content_type = detail::find_content_type(
 6445            path, file_extension_and_mimetype_map_, default_file_mimetype_);
 6446      }
 6447
 6448      res.set_content_provider(
 6449          mm->size(), content_type,
 6450          [mm](size_t offset, size_t length, DataSink &sink) -> bool {
 6451            sink.write(mm->data() + offset, length);
 6452            return true;
 6453          });
 6454    }
 6455
 6456    if (detail::range_error(req, res)) {
 6457      res.body.clear();
 6458      res.content_length_ = 0;
 6459      res.content_provider_ = nullptr;
 6460      res.status = StatusCode::RangeNotSatisfiable_416;
 6461      return write_response(strm, close_connection, req, res);
 6462    }
 6463
 6464    return write_response_with_content(strm, close_connection, req, res);
 6465  } else {
 6466    if (res.status == -1) { res.status = StatusCode::NotFound_404; }
 6467
 6468    return write_response(strm, close_connection, req, res);
 6469  }
 6470}
 6471
 6472bool Server::is_valid() const { return true; }
 6473
 6474bool Server::process_and_close_socket(socket_t sock) {
 6475  std::string remote_addr;
 6476  int remote_port = 0;
 6477  detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
 6478
 6479  std::string local_addr;
 6480  int local_port = 0;
 6481  detail::get_local_ip_and_port(sock, local_addr, local_port);
 6482
 6483  auto ret = detail::process_server_socket(
 6484      svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
 6485      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
 6486      write_timeout_usec_,
 6487      [&](Stream &strm, bool close_connection, bool &connection_closed) {
 6488        return process_request(strm, remote_addr, remote_port, local_addr,
 6489                               local_port, close_connection, connection_closed,
 6490                               nullptr);
 6491      });
 6492
 6493  detail::shutdown_socket(sock);
 6494  detail::close_socket(sock);
 6495  return ret;
 6496}
 6497
 6498void Server::output_log(const Request &req, const Response &res) const {
 6499  if (logger_) {
 6500    std::lock_guard<std::mutex> guard(logger_mutex_);
 6501    logger_(req, res);
 6502  }
 6503}
 6504
 6505void Server::output_pre_compression_log(const Request &req,
 6506                                               const Response &res) const {
 6507  if (pre_compression_logger_) {
 6508    std::lock_guard<std::mutex> guard(logger_mutex_);
 6509    pre_compression_logger_(req, res);
 6510  }
 6511}
 6512
 6513void Server::output_error_log(const Error &err,
 6514                                     const Request *req) const {
 6515  if (error_logger_) {
 6516    std::lock_guard<std::mutex> guard(logger_mutex_);
 6517    error_logger_(err, req);
 6518  }
 6519}
 6520
 6521// HTTP client implementation
 6522ClientImpl::ClientImpl(const std::string &host)
 6523    : ClientImpl(host, 80, std::string(), std::string()) {}
 6524
 6525ClientImpl::ClientImpl(const std::string &host, int port)
 6526    : ClientImpl(host, port, std::string(), std::string()) {}
 6527
 6528ClientImpl::ClientImpl(const std::string &host, int port,
 6529                              const std::string &client_cert_path,
 6530                              const std::string &client_key_path)
 6531    : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
 6532      client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
 6533
 6534ClientImpl::~ClientImpl() {
 6535  // Wait until all the requests in flight are handled.
 6536  size_t retry_count = 10;
 6537  while (retry_count-- > 0) {
 6538    {
 6539      std::lock_guard<std::mutex> guard(socket_mutex_);
 6540      if (socket_requests_in_flight_ == 0) { break; }
 6541    }
 6542    std::this_thread::sleep_for(std::chrono::milliseconds{1});
 6543  }
 6544
 6545  std::lock_guard<std::mutex> guard(socket_mutex_);
 6546  shutdown_socket(socket_);
 6547  close_socket(socket_);
 6548}
 6549
 6550bool ClientImpl::is_valid() const { return true; }
 6551
 6552void ClientImpl::copy_settings(const ClientImpl &rhs) {
 6553  client_cert_path_ = rhs.client_cert_path_;
 6554  client_key_path_ = rhs.client_key_path_;
 6555  connection_timeout_sec_ = rhs.connection_timeout_sec_;
 6556  read_timeout_sec_ = rhs.read_timeout_sec_;
 6557  read_timeout_usec_ = rhs.read_timeout_usec_;
 6558  write_timeout_sec_ = rhs.write_timeout_sec_;
 6559  write_timeout_usec_ = rhs.write_timeout_usec_;
 6560  max_timeout_msec_ = rhs.max_timeout_msec_;
 6561  basic_auth_username_ = rhs.basic_auth_username_;
 6562  basic_auth_password_ = rhs.basic_auth_password_;
 6563  bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
 6564#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6565  digest_auth_username_ = rhs.digest_auth_username_;
 6566  digest_auth_password_ = rhs.digest_auth_password_;
 6567#endif
 6568  keep_alive_ = rhs.keep_alive_;
 6569  follow_location_ = rhs.follow_location_;
 6570  path_encode_ = rhs.path_encode_;
 6571  address_family_ = rhs.address_family_;
 6572  tcp_nodelay_ = rhs.tcp_nodelay_;
 6573  ipv6_v6only_ = rhs.ipv6_v6only_;
 6574  socket_options_ = rhs.socket_options_;
 6575  compress_ = rhs.compress_;
 6576  decompress_ = rhs.decompress_;
 6577  interface_ = rhs.interface_;
 6578  proxy_host_ = rhs.proxy_host_;
 6579  proxy_port_ = rhs.proxy_port_;
 6580  proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
 6581  proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
 6582  proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
 6583#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6584  proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
 6585  proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
 6586#endif
 6587#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6588  ca_cert_file_path_ = rhs.ca_cert_file_path_;
 6589  ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
 6590  ca_cert_store_ = rhs.ca_cert_store_;
 6591#endif
 6592#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6593  server_certificate_verification_ = rhs.server_certificate_verification_;
 6594  server_hostname_verification_ = rhs.server_hostname_verification_;
 6595  server_certificate_verifier_ = rhs.server_certificate_verifier_;
 6596#endif
 6597  logger_ = rhs.logger_;
 6598  error_logger_ = rhs.error_logger_;
 6599}
 6600
 6601socket_t ClientImpl::create_client_socket(Error &error) const {
 6602  if (!proxy_host_.empty() && proxy_port_ != -1) {
 6603    return detail::create_client_socket(
 6604        proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
 6605        ipv6_v6only_, socket_options_, connection_timeout_sec_,
 6606        connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
 6607        write_timeout_sec_, write_timeout_usec_, interface_, error);
 6608  }
 6609
 6610  // Check is custom IP specified for host_
 6611  std::string ip;
 6612  auto it = addr_map_.find(host_);
 6613  if (it != addr_map_.end()) { ip = it->second; }
 6614
 6615  return detail::create_client_socket(
 6616      host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
 6617      socket_options_, connection_timeout_sec_, connection_timeout_usec_,
 6618      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
 6619      write_timeout_usec_, interface_, error);
 6620}
 6621
 6622bool ClientImpl::create_and_connect_socket(Socket &socket,
 6623                                                  Error &error) {
 6624  auto sock = create_client_socket(error);
 6625  if (sock == INVALID_SOCKET) { return false; }
 6626  socket.sock = sock;
 6627  return true;
 6628}
 6629
 6630bool ClientImpl::ensure_socket_connection(Socket &socket, Error &error) {
 6631  return create_and_connect_socket(socket, error);
 6632}
 6633
 6634#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6635bool SSLClient::ensure_socket_connection(Socket &socket, Error &error) {
 6636  if (!ClientImpl::ensure_socket_connection(socket, error)) { return false; }
 6637
 6638  if (!proxy_host_.empty() && proxy_port_ != -1) { return true; }
 6639
 6640  if (!initialize_ssl(socket, error)) {
 6641    shutdown_socket(socket);
 6642    close_socket(socket);
 6643    return false;
 6644  }
 6645
 6646  return true;
 6647}
 6648#endif
 6649
 6650void ClientImpl::shutdown_ssl(Socket & /*socket*/,
 6651                                     bool /*shutdown_gracefully*/) {
 6652  // If there are any requests in flight from threads other than us, then it's
 6653  // a thread-unsafe race because individual ssl* objects are not thread-safe.
 6654  assert(socket_requests_in_flight_ == 0 ||
 6655         socket_requests_are_from_thread_ == std::this_thread::get_id());
 6656}
 6657
 6658void ClientImpl::shutdown_socket(Socket &socket) const {
 6659  if (socket.sock == INVALID_SOCKET) { return; }
 6660  detail::shutdown_socket(socket.sock);
 6661}
 6662
 6663void ClientImpl::close_socket(Socket &socket) {
 6664  // If there are requests in flight in another thread, usually closing
 6665  // the socket will be fine and they will simply receive an error when
 6666  // using the closed socket, but it is still a bug since rarely the OS
 6667  // may reassign the socket id to be used for a new socket, and then
 6668  // suddenly they will be operating on a live socket that is different
 6669  // than the one they intended!
 6670  assert(socket_requests_in_flight_ == 0 ||
 6671         socket_requests_are_from_thread_ == std::this_thread::get_id());
 6672
 6673  // It is also a bug if this happens while SSL is still active
 6674#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6675  assert(socket.ssl == nullptr);
 6676#endif
 6677  if (socket.sock == INVALID_SOCKET) { return; }
 6678  detail::close_socket(socket.sock);
 6679  socket.sock = INVALID_SOCKET;
 6680}
 6681
 6682bool ClientImpl::read_response_line(Stream &strm, const Request &req,
 6683                                           Response &res,
 6684                                           bool skip_100_continue) const {
 6685  std::array<char, 2048> buf{};
 6686
 6687  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
 6688
 6689  if (!line_reader.getline()) { return false; }
 6690
 6691#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
 6692  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
 6693#else
 6694  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
 6695#endif
 6696
 6697  std::cmatch m;
 6698  if (!std::regex_match(line_reader.ptr(), m, re)) {
 6699    return req.method == "CONNECT";
 6700  }
 6701  res.version = std::string(m[1]);
 6702  res.status = std::stoi(std::string(m[2]));
 6703  res.reason = std::string(m[3]);
 6704
 6705  // Ignore '100 Continue' (only when not using Expect: 100-continue explicitly)
 6706  while (skip_100_continue && res.status == StatusCode::Continue_100) {
 6707    if (!line_reader.getline()) { return false; } // CRLF
 6708    if (!line_reader.getline()) { return false; } // next response line
 6709
 6710    if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
 6711    res.version = std::string(m[1]);
 6712    res.status = std::stoi(std::string(m[2]));
 6713    res.reason = std::string(m[3]);
 6714  }
 6715
 6716  return true;
 6717}
 6718
 6719bool ClientImpl::send(Request &req, Response &res, Error &error) {
 6720  std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
 6721  auto ret = send_(req, res, error);
 6722  if (error == Error::SSLPeerCouldBeClosed_) {
 6723    assert(!ret);
 6724    ret = send_(req, res, error);
 6725  }
 6726  return ret;
 6727}
 6728
 6729bool ClientImpl::send_(Request &req, Response &res, Error &error) {
 6730  {
 6731    std::lock_guard<std::mutex> guard(socket_mutex_);
 6732
 6733    // Set this to false immediately - if it ever gets set to true by the end
 6734    // of the request, we know another thread instructed us to close the
 6735    // socket.
 6736    socket_should_be_closed_when_request_is_done_ = false;
 6737
 6738    auto is_alive = false;
 6739    if (socket_.is_open()) {
 6740      is_alive = detail::is_socket_alive(socket_.sock);
 6741
 6742#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6743      if (is_alive && is_ssl()) {
 6744        if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
 6745          is_alive = false;
 6746        }
 6747      }
 6748#endif
 6749
 6750      if (!is_alive) {
 6751        // Attempt to avoid sigpipe by shutting down non-gracefully if it
 6752        // seems like the other side has already closed the connection Also,
 6753        // there cannot be any requests in flight from other threads since we
 6754        // locked request_mutex_, so safe to close everything immediately
 6755        const bool shutdown_gracefully = false;
 6756        shutdown_ssl(socket_, shutdown_gracefully);
 6757        shutdown_socket(socket_);
 6758        close_socket(socket_);
 6759      }
 6760    }
 6761
 6762    if (!is_alive) {
 6763      if (!ensure_socket_connection(socket_, error)) {
 6764        output_error_log(error, &req);
 6765        return false;
 6766      }
 6767
 6768#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6769      // TODO: refactoring
 6770      if (is_ssl()) {
 6771        auto &scli = static_cast<SSLClient &>(*this);
 6772        if (!proxy_host_.empty() && proxy_port_ != -1) {
 6773          auto success = false;
 6774          if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
 6775                                       error)) {
 6776            if (!success) { output_error_log(error, &req); }
 6777            return success;
 6778          }
 6779        }
 6780
 6781        if (!proxy_host_.empty() && proxy_port_ != -1) {
 6782          if (!scli.initialize_ssl(socket_, error)) {
 6783            output_error_log(error, &req);
 6784            return false;
 6785          }
 6786        }
 6787      }
 6788#endif
 6789    }
 6790
 6791    // Mark the current socket as being in use so that it cannot be closed by
 6792    // anyone else while this request is ongoing, even though we will be
 6793    // releasing the mutex.
 6794    if (socket_requests_in_flight_ > 1) {
 6795      assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
 6796    }
 6797    socket_requests_in_flight_ += 1;
 6798    socket_requests_are_from_thread_ = std::this_thread::get_id();
 6799  }
 6800
 6801  for (const auto &header : default_headers_) {
 6802    if (req.headers.find(header.first) == req.headers.end()) {
 6803      req.headers.insert(header);
 6804    }
 6805  }
 6806
 6807  auto ret = false;
 6808  auto close_connection = !keep_alive_;
 6809
 6810  auto se = detail::scope_exit([&]() {
 6811    // Briefly lock mutex in order to mark that a request is no longer ongoing
 6812    std::lock_guard<std::mutex> guard(socket_mutex_);
 6813    socket_requests_in_flight_ -= 1;
 6814    if (socket_requests_in_flight_ <= 0) {
 6815      assert(socket_requests_in_flight_ == 0);
 6816      socket_requests_are_from_thread_ = std::thread::id();
 6817    }
 6818
 6819    if (socket_should_be_closed_when_request_is_done_ || close_connection ||
 6820        !ret) {
 6821      shutdown_ssl(socket_, true);
 6822      shutdown_socket(socket_);
 6823      close_socket(socket_);
 6824    }
 6825  });
 6826
 6827  ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {
 6828    return handle_request(strm, req, res, close_connection, error);
 6829  });
 6830
 6831  if (!ret) {
 6832    if (error == Error::Success) {
 6833      error = Error::Unknown;
 6834      output_error_log(error, &req);
 6835    }
 6836  }
 6837
 6838  return ret;
 6839}
 6840
 6841Result ClientImpl::send(const Request &req) {
 6842  auto req2 = req;
 6843  return send_(std::move(req2));
 6844}
 6845
 6846Result ClientImpl::send_(Request &&req) {
 6847  auto res = detail::make_unique<Response>();
 6848  auto error = Error::Success;
 6849  auto ret = send(req, *res, error);
 6850#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6851  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers),
 6852                last_ssl_error_, last_openssl_error_};
 6853#else
 6854  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
 6855#endif
 6856}
 6857
 6858void ClientImpl::prepare_default_headers(Request &r, bool for_stream,
 6859                                                const std::string &ct) {
 6860  (void)for_stream;
 6861  for (const auto &header : default_headers_) {
 6862    if (!r.has_header(header.first)) { r.headers.insert(header); }
 6863  }
 6864
 6865  if (!r.has_header("Host")) {
 6866    if (address_family_ == AF_UNIX) {
 6867      r.headers.emplace("Host", "localhost");
 6868    } else {
 6869      r.headers.emplace(
 6870          "Host", detail::make_host_and_port_string(host_, port_, is_ssl()));
 6871    }
 6872  }
 6873
 6874  if (!r.has_header("Accept")) { r.headers.emplace("Accept", "*/*"); }
 6875
 6876  if (!r.content_receiver) {
 6877    if (!r.has_header("Accept-Encoding")) {
 6878      std::string accept_encoding;
 6879#ifdef CPPHTTPLIB_BROTLI_SUPPORT
 6880      accept_encoding = "br";
 6881#endif
 6882#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 6883      if (!accept_encoding.empty()) { accept_encoding += ", "; }
 6884      accept_encoding += "gzip, deflate";
 6885#endif
 6886#ifdef CPPHTTPLIB_ZSTD_SUPPORT
 6887      if (!accept_encoding.empty()) { accept_encoding += ", "; }
 6888      accept_encoding += "zstd";
 6889#endif
 6890      r.set_header("Accept-Encoding", accept_encoding);
 6891    }
 6892
 6893#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
 6894    if (!r.has_header("User-Agent")) {
 6895      auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
 6896      r.set_header("User-Agent", agent);
 6897    }
 6898#endif
 6899  }
 6900
 6901  if (!r.body.empty()) {
 6902    if (!ct.empty() && !r.has_header("Content-Type")) {
 6903      r.headers.emplace("Content-Type", ct);
 6904    }
 6905    if (!r.has_header("Content-Length")) {
 6906      r.headers.emplace("Content-Length", std::to_string(r.body.size()));
 6907    }
 6908  }
 6909}
 6910
 6911ClientImpl::StreamHandle
 6912ClientImpl::open_stream(const std::string &method, const std::string &path,
 6913                        const Params &params, const Headers &headers,
 6914                        const std::string &body,
 6915                        const std::string &content_type) {
 6916  StreamHandle handle;
 6917  handle.response = detail::make_unique<Response>();
 6918  handle.error = Error::Success;
 6919
 6920  auto query_path = params.empty() ? path : append_query_params(path, params);
 6921  handle.connection_ = detail::make_unique<ClientConnection>();
 6922
 6923  {
 6924    std::lock_guard<std::mutex> guard(socket_mutex_);
 6925
 6926    auto is_alive = false;
 6927    if (socket_.is_open()) {
 6928      is_alive = detail::is_socket_alive(socket_.sock);
 6929#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6930      if (is_alive && is_ssl()) {
 6931        if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
 6932          is_alive = false;
 6933        }
 6934      }
 6935#endif
 6936      if (!is_alive) {
 6937        shutdown_ssl(socket_, false);
 6938        shutdown_socket(socket_);
 6939        close_socket(socket_);
 6940      }
 6941    }
 6942
 6943    if (!is_alive) {
 6944      if (!ensure_socket_connection(socket_, handle.error)) {
 6945        handle.response.reset();
 6946        return handle;
 6947      }
 6948
 6949#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6950      if (is_ssl()) {
 6951        auto &scli = static_cast<SSLClient &>(*this);
 6952        if (!proxy_host_.empty() && proxy_port_ != -1) {
 6953          if (!scli.initialize_ssl(socket_, handle.error)) {
 6954            handle.response.reset();
 6955            return handle;
 6956          }
 6957        }
 6958      }
 6959#endif
 6960    }
 6961
 6962    transfer_socket_ownership_to_handle(handle);
 6963  }
 6964
 6965#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 6966  if (is_ssl() && handle.connection_->ssl) {
 6967    handle.socket_stream_ = detail::make_unique<detail::SSLSocketStream>(
 6968        handle.connection_->sock, handle.connection_->ssl, read_timeout_sec_,
 6969        read_timeout_usec_, write_timeout_sec_, write_timeout_usec_);
 6970  } else {
 6971    handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
 6972        handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
 6973        write_timeout_sec_, write_timeout_usec_);
 6974  }
 6975#else
 6976  handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
 6977      handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
 6978      write_timeout_sec_, write_timeout_usec_);
 6979#endif
 6980  handle.stream_ = handle.socket_stream_.get();
 6981
 6982  Request req;
 6983  req.method = method;
 6984  req.path = query_path;
 6985  req.headers = headers;
 6986  req.body = body;
 6987
 6988  prepare_default_headers(req, true, content_type);
 6989
 6990  auto &strm = *handle.stream_;
 6991  if (detail::write_request_line(strm, req.method, req.path) < 0) {
 6992    handle.error = Error::Write;
 6993    handle.response.reset();
 6994    return handle;
 6995  }
 6996
 6997  if (!detail::check_and_write_headers(strm, req.headers, header_writer_,
 6998                                       handle.error)) {
 6999    handle.response.reset();
 7000    return handle;
 7001  }
 7002
 7003  if (!body.empty()) {
 7004    if (strm.write(body.data(), body.size()) < 0) {
 7005      handle.error = Error::Write;
 7006      handle.response.reset();
 7007      return handle;
 7008    }
 7009  }
 7010
 7011  if (!read_response_line(strm, req, *handle.response) ||
 7012      !detail::read_headers(strm, handle.response->headers)) {
 7013    handle.error = Error::Read;
 7014    handle.response.reset();
 7015    return handle;
 7016  }
 7017
 7018  handle.body_reader_.stream = handle.stream_;
 7019
 7020  auto content_length_str = handle.response->get_header_value("Content-Length");
 7021  if (!content_length_str.empty()) {
 7022    handle.body_reader_.content_length =
 7023        static_cast<size_t>(std::stoull(content_length_str));
 7024  }
 7025
 7026  auto transfer_encoding =
 7027      handle.response->get_header_value("Transfer-Encoding");
 7028  handle.body_reader_.chunked = (transfer_encoding == "chunked");
 7029
 7030  auto content_encoding = handle.response->get_header_value("Content-Encoding");
 7031  if (!content_encoding.empty()) {
 7032    handle.decompressor_ = detail::create_decompressor(content_encoding);
 7033  }
 7034
 7035  return handle;
 7036}
 7037
 7038ssize_t ClientImpl::StreamHandle::read(char *buf, size_t len) {
 7039  if (!is_valid() || !response) { return -1; }
 7040
 7041  if (decompressor_) { return read_with_decompression(buf, len); }
 7042  auto n = detail::read_body_content(stream_, body_reader_, buf, len);
 7043
 7044  if (n <= 0 && body_reader_.chunked && !trailers_parsed_ && stream_) {
 7045    trailers_parsed_ = true;
 7046    if (body_reader_.chunked_decoder) {
 7047      if (!body_reader_.chunked_decoder->parse_trailers_into(
 7048              response->trailers, response->headers)) {
 7049        return n;
 7050      }
 7051    } else {
 7052      detail::ChunkedDecoder dec(*stream_);
 7053      if (!dec.parse_trailers_into(response->trailers, response->headers)) {
 7054        return n;
 7055      }
 7056    }
 7057  }
 7058
 7059  return n;
 7060}
 7061
 7062ssize_t ClientImpl::StreamHandle::read_with_decompression(char *buf,
 7063                                                                 size_t len) {
 7064  if (decompress_offset_ < decompress_buffer_.size()) {
 7065    auto available = decompress_buffer_.size() - decompress_offset_;
 7066    auto to_copy = (std::min)(len, available);
 7067    std::memcpy(buf, decompress_buffer_.data() + decompress_offset_, to_copy);
 7068    decompress_offset_ += to_copy;
 7069    return static_cast<ssize_t>(to_copy);
 7070  }
 7071
 7072  decompress_buffer_.clear();
 7073  decompress_offset_ = 0;
 7074
 7075  constexpr size_t kDecompressionBufferSize = 8192;
 7076  char compressed_buf[kDecompressionBufferSize];
 7077
 7078  while (true) {
 7079    auto n = detail::read_body_content(stream_, body_reader_, compressed_buf,
 7080                                       sizeof(compressed_buf));
 7081
 7082    if (n <= 0) { return n; }
 7083
 7084    bool decompress_ok =
 7085        decompressor_->decompress(compressed_buf, static_cast<size_t>(n),
 7086                                  [this](const char *data, size_t data_len) {
 7087                                    decompress_buffer_.append(data, data_len);
 7088                                    return true;
 7089                                  });
 7090
 7091    if (!decompress_ok) {
 7092      body_reader_.last_error = Error::Read;
 7093      return -1;
 7094    }
 7095
 7096    if (!decompress_buffer_.empty()) { break; }
 7097  }
 7098
 7099  auto to_copy = (std::min)(len, decompress_buffer_.size());
 7100  std::memcpy(buf, decompress_buffer_.data(), to_copy);
 7101  decompress_offset_ = to_copy;
 7102  return static_cast<ssize_t>(to_copy);
 7103}
 7104
 7105void ClientImpl::StreamHandle::parse_trailers_if_needed() {
 7106  if (!response || !stream_ || !body_reader_.chunked || trailers_parsed_) {
 7107    return;
 7108  }
 7109
 7110  trailers_parsed_ = true;
 7111
 7112  const auto bufsiz = 128;
 7113  char line_buf[bufsiz];
 7114  detail::stream_line_reader line_reader(*stream_, line_buf, bufsiz);
 7115
 7116  if (!line_reader.getline()) { return; }
 7117
 7118  if (!detail::parse_trailers(line_reader, response->trailers,
 7119                              response->headers)) {
 7120    return;
 7121  }
 7122}
 7123
 7124// Inline method implementations for `ChunkedDecoder`.
 7125namespace detail {
 7126
 7127ChunkedDecoder::ChunkedDecoder(Stream &s) : strm(s) {}
 7128
 7129ssize_t ChunkedDecoder::read_payload(char *buf, size_t len,
 7130                                            size_t &out_chunk_offset,
 7131                                            size_t &out_chunk_total) {
 7132  if (finished) { return 0; }
 7133
 7134  if (chunk_remaining == 0) {
 7135    stream_line_reader lr(strm, line_buf, sizeof(line_buf));
 7136    if (!lr.getline()) { return -1; }
 7137
 7138    char *endptr = nullptr;
 7139    unsigned long chunk_len = std::strtoul(lr.ptr(), &endptr, 16);
 7140    if (endptr == lr.ptr()) { return -1; }
 7141    if (chunk_len == ULONG_MAX) { return -1; }
 7142
 7143    if (chunk_len == 0) {
 7144      chunk_remaining = 0;
 7145      finished = true;
 7146      out_chunk_offset = 0;
 7147      out_chunk_total = 0;
 7148      return 0;
 7149    }
 7150
 7151    chunk_remaining = static_cast<size_t>(chunk_len);
 7152    last_chunk_total = chunk_remaining;
 7153    last_chunk_offset = 0;
 7154  }
 7155
 7156  auto to_read = (std::min)(chunk_remaining, len);
 7157  auto n = strm.read(buf, to_read);
 7158  if (n <= 0) { return -1; }
 7159
 7160  auto offset_before = last_chunk_offset;
 7161  last_chunk_offset += static_cast<size_t>(n);
 7162  chunk_remaining -= static_cast<size_t>(n);
 7163
 7164  out_chunk_offset = offset_before;
 7165  out_chunk_total = last_chunk_total;
 7166
 7167  if (chunk_remaining == 0) {
 7168    stream_line_reader lr(strm, line_buf, sizeof(line_buf));
 7169    if (!lr.getline()) { return -1; }
 7170    if (std::strcmp(lr.ptr(), "\r\n") != 0) { return -1; }
 7171  }
 7172
 7173  return n;
 7174}
 7175
 7176bool ChunkedDecoder::parse_trailers_into(Headers &dest,
 7177                                                const Headers &src_headers) {
 7178  stream_line_reader lr(strm, line_buf, sizeof(line_buf));
 7179  if (!lr.getline()) { return false; }
 7180  return parse_trailers(lr, dest, src_headers);
 7181}
 7182
 7183} // namespace detail
 7184
 7185void
 7186ClientImpl::transfer_socket_ownership_to_handle(StreamHandle &handle) {
 7187  handle.connection_->sock = socket_.sock;
 7188#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 7189  handle.connection_->ssl = socket_.ssl;
 7190  socket_.ssl = nullptr;
 7191#endif
 7192  socket_.sock = INVALID_SOCKET;
 7193}
 7194
 7195bool ClientImpl::handle_request(Stream &strm, Request &req,
 7196                                       Response &res, bool close_connection,
 7197                                       Error &error) {
 7198  if (req.path.empty()) {
 7199    error = Error::Connection;
 7200    output_error_log(error, &req);
 7201    return false;
 7202  }
 7203
 7204  auto req_save = req;
 7205
 7206  bool ret;
 7207
 7208  if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
 7209    auto req2 = req;
 7210    req2.path = "http://" +
 7211                detail::make_host_and_port_string(host_, port_, false) +
 7212                req.path;
 7213    ret = process_request(strm, req2, res, close_connection, error);
 7214    req = std::move(req2);
 7215    req.path = req_save.path;
 7216  } else {
 7217    ret = process_request(strm, req, res, close_connection, error);
 7218  }
 7219
 7220  if (!ret) { return false; }
 7221
 7222  if (res.get_header_value("Connection") == "close" ||
 7223      (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
 7224    // TODO this requires a not-entirely-obvious chain of calls to be correct
 7225    // for this to be safe.
 7226
 7227    // This is safe to call because handle_request is only called by send_
 7228    // which locks the request mutex during the process. It would be a bug
 7229    // to call it from a different thread since it's a thread-safety issue
 7230    // to do these things to the socket if another thread is using the socket.
 7231    std::lock_guard<std::mutex> guard(socket_mutex_);
 7232    shutdown_ssl(socket_, true);
 7233    shutdown_socket(socket_);
 7234    close_socket(socket_);
 7235  }
 7236
 7237  if (300 < res.status && res.status < 400 && follow_location_) {
 7238    req = std::move(req_save);
 7239    ret = redirect(req, res, error);
 7240  }
 7241
 7242#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 7243  if ((res.status == StatusCode::Unauthorized_401 ||
 7244       res.status == StatusCode::ProxyAuthenticationRequired_407) &&
 7245      req.authorization_count_ < 5) {
 7246    auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
 7247    const auto &username =
 7248        is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
 7249    const auto &password =
 7250        is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
 7251
 7252    if (!username.empty() && !password.empty()) {
 7253      std::map<std::string, std::string> auth;
 7254      if (detail::parse_www_authenticate(res, auth, is_proxy)) {
 7255        Request new_req = req;
 7256        new_req.authorization_count_ += 1;
 7257        new_req.headers.erase(is_proxy ? "Proxy-Authorization"
 7258                                       : "Authorization");
 7259        new_req.headers.insert(detail::make_digest_authentication_header(
 7260            req, auth, new_req.authorization_count_, detail::random_string(10),
 7261            username, password, is_proxy));
 7262
 7263        Response new_res;
 7264
 7265        ret = send(new_req, new_res, error);
 7266        if (ret) { res = std::move(new_res); }
 7267      }
 7268    }
 7269  }
 7270#endif
 7271
 7272  return ret;
 7273}
 7274
 7275bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
 7276  if (req.redirect_count_ == 0) {
 7277    error = Error::ExceedRedirectCount;
 7278    output_error_log(error, &req);
 7279    return false;
 7280  }
 7281
 7282  auto location = res.get_header_value("location");
 7283  if (location.empty()) { return false; }
 7284
 7285  thread_local const std::regex re(
 7286      R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
 7287
 7288  std::smatch m;
 7289  if (!std::regex_match(location, m, re)) { return false; }
 7290
 7291  auto scheme = is_ssl() ? "https" : "http";
 7292
 7293  auto next_scheme = m[1].str();
 7294  auto next_host = m[2].str();
 7295  if (next_host.empty()) { next_host = m[3].str(); }
 7296  auto port_str = m[4].str();
 7297  auto next_path = m[5].str();
 7298  auto next_query = m[6].str();
 7299
 7300  auto next_port = port_;
 7301  if (!port_str.empty()) {
 7302    next_port = std::stoi(port_str);
 7303  } else if (!next_scheme.empty()) {
 7304    next_port = next_scheme == "https" ? 443 : 80;
 7305  }
 7306
 7307  if (next_scheme.empty()) { next_scheme = scheme; }
 7308  if (next_host.empty()) { next_host = host_; }
 7309  if (next_path.empty()) { next_path = "/"; }
 7310
 7311  auto path = decode_query_component(next_path, true) + next_query;
 7312
 7313  // Same host redirect - use current client
 7314  if (next_scheme == scheme && next_host == host_ && next_port == port_) {
 7315    return detail::redirect(*this, req, res, path, location, error);
 7316  }
 7317
 7318  // Cross-host/scheme redirect - create new client with robust setup
 7319  return create_redirect_client(next_scheme, next_host, next_port, req, res,
 7320                                path, location, error);
 7321}
 7322
 7323// New method for robust redirect client creation
 7324bool ClientImpl::create_redirect_client(
 7325    const std::string &scheme, const std::string &host, int port, Request &req,
 7326    Response &res, const std::string &path, const std::string &location,
 7327    Error &error) {
 7328  // Determine if we need SSL
 7329  auto need_ssl = (scheme == "https");
 7330
 7331  // Clean up request headers that are host/client specific
 7332  // Remove headers that should not be carried over to new host
 7333  auto headers_to_remove =
 7334      std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
 7335
 7336  for (const auto &header_name : headers_to_remove) {
 7337    auto it = req.headers.find(header_name);
 7338    while (it != req.headers.end()) {
 7339      it = req.headers.erase(it);
 7340      it = req.headers.find(header_name);
 7341    }
 7342  }
 7343
 7344  // Create appropriate client type and handle redirect
 7345  if (need_ssl) {
 7346#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 7347    // Create SSL client for HTTPS redirect
 7348    SSLClient redirect_client(host, port);
 7349
 7350    // Setup basic client configuration first
 7351    setup_redirect_client(redirect_client);
 7352
 7353    // SSL-specific configuration for proxy environments
 7354    if (!proxy_host_.empty() && proxy_port_ != -1) {
 7355      // Critical: Disable SSL verification for proxy environments
 7356      redirect_client.enable_server_certificate_verification(false);
 7357      redirect_client.enable_server_hostname_verification(false);
 7358    } else {
 7359      // For direct SSL connections, copy SSL verification settings
 7360      redirect_client.enable_server_certificate_verification(
 7361          server_certificate_verification_);
 7362      redirect_client.enable_server_hostname_verification(
 7363          server_hostname_verification_);
 7364    }
 7365
 7366    // Handle CA certificate store and paths if available
 7367    if (ca_cert_store_ && X509_STORE_up_ref(ca_cert_store_)) {
 7368      redirect_client.set_ca_cert_store(ca_cert_store_);
 7369    }
 7370    if (!ca_cert_file_path_.empty()) {
 7371      redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
 7372    }
 7373
 7374    // Client certificates are set through constructor for SSLClient
 7375    // NOTE: SSLClient constructor already takes client_cert_path and
 7376    // client_key_path so we need to create it properly if client certs are
 7377    // needed
 7378
 7379    // Execute the redirect
 7380    return detail::redirect(redirect_client, req, res, path, location, error);
 7381#else
 7382    // SSL not supported - set appropriate error
 7383    error = Error::SSLConnection;
 7384    output_error_log(error, &req);
 7385    return false;
 7386#endif
 7387  } else {
 7388    // HTTP redirect
 7389    ClientImpl redirect_client(host, port);
 7390
 7391    // Setup client with robust configuration
 7392    setup_redirect_client(redirect_client);
 7393
 7394    // Execute the redirect
 7395    return detail::redirect(redirect_client, req, res, path, location, error);
 7396  }
 7397}
 7398
 7399// New method for robust client setup (based on basic_manual_redirect.cpp
 7400// logic)
 7401template <typename ClientType>
 7402void ClientImpl::setup_redirect_client(ClientType &client) {
 7403  // Copy basic settings first
 7404  client.set_connection_timeout(connection_timeout_sec_);
 7405  client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
 7406  client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
 7407  client.set_keep_alive(keep_alive_);
 7408  client.set_follow_location(
 7409      true); // Enable redirects to handle multi-step redirects
 7410  client.set_path_encode(path_encode_);
 7411  client.set_compress(compress_);
 7412  client.set_decompress(decompress_);
 7413
 7414  // Copy authentication settings BEFORE proxy setup
 7415  if (!basic_auth_username_.empty()) {
 7416    client.set_basic_auth(basic_auth_username_, basic_auth_password_);
 7417  }
 7418  if (!bearer_token_auth_token_.empty()) {
 7419    client.set_bearer_token_auth(bearer_token_auth_token_);
 7420  }
 7421#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 7422  if (!digest_auth_username_.empty()) {
 7423    client.set_digest_auth(digest_auth_username_, digest_auth_password_);
 7424  }
 7425#endif
 7426
 7427  // Setup proxy configuration (CRITICAL ORDER - proxy must be set
 7428  // before proxy auth)
 7429  if (!proxy_host_.empty() && proxy_port_ != -1) {
 7430    // First set proxy host and port
 7431    client.set_proxy(proxy_host_, proxy_port_);
 7432
 7433    // Then set proxy authentication (order matters!)
 7434    if (!proxy_basic_auth_username_.empty()) {
 7435      client.set_proxy_basic_auth(proxy_basic_auth_username_,
 7436                                  proxy_basic_auth_password_);
 7437    }
 7438    if (!proxy_bearer_token_auth_token_.empty()) {
 7439      client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
 7440    }
 7441#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 7442    if (!proxy_digest_auth_username_.empty()) {
 7443      client.set_proxy_digest_auth(proxy_digest_auth_username_,
 7444                                   proxy_digest_auth_password_);
 7445    }
 7446#endif
 7447  }
 7448
 7449  // Copy network and socket settings
 7450  client.set_address_family(address_family_);
 7451  client.set_tcp_nodelay(tcp_nodelay_);
 7452  client.set_ipv6_v6only(ipv6_v6only_);
 7453  if (socket_options_) { client.set_socket_options(socket_options_); }
 7454  if (!interface_.empty()) { client.set_interface(interface_); }
 7455
 7456  // Copy logging and headers
 7457  if (logger_) { client.set_logger(logger_); }
 7458  if (error_logger_) { client.set_error_logger(error_logger_); }
 7459
 7460  // NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
 7461  // Each new client should generate its own headers based on its target host
 7462}
 7463
 7464bool ClientImpl::write_content_with_provider(Stream &strm,
 7465                                                    const Request &req,
 7466                                                    Error &error) const {
 7467  auto is_shutting_down = []() { return false; };
 7468
 7469  if (req.is_chunked_content_provider_) {
 7470    // TODO: Brotli support
 7471    std::unique_ptr<detail::compressor> compressor;
 7472#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 7473    if (compress_) {
 7474      compressor = detail::make_unique<detail::gzip_compressor>();
 7475    } else
 7476#endif
 7477    {
 7478      compressor = detail::make_unique<detail::nocompressor>();
 7479    }
 7480
 7481    return detail::write_content_chunked(strm, req.content_provider_,
 7482                                         is_shutting_down, *compressor, error);
 7483  } else {
 7484    return detail::write_content_with_progress(
 7485        strm, req.content_provider_, 0, req.content_length_, is_shutting_down,
 7486        req.upload_progress, error);
 7487  }
 7488}
 7489
 7490bool ClientImpl::write_request(Stream &strm, Request &req,
 7491                                      bool close_connection, Error &error,
 7492                                      bool skip_body) {
 7493  // Prepare additional headers
 7494  if (close_connection) {
 7495    if (!req.has_header("Connection")) {
 7496      req.set_header("Connection", "close");
 7497    }
 7498  }
 7499
 7500  std::string ct_for_defaults;
 7501  if (!req.has_header("Content-Type") && !req.body.empty()) {
 7502    ct_for_defaults = "text/plain";
 7503  }
 7504  prepare_default_headers(req, false, ct_for_defaults);
 7505
 7506  if (req.body.empty()) {
 7507    if (req.content_provider_) {
 7508      if (!req.is_chunked_content_provider_) {
 7509        if (!req.has_header("Content-Length")) {
 7510          auto length = std::to_string(req.content_length_);
 7511          req.set_header("Content-Length", length);
 7512        }
 7513      }
 7514    } else {
 7515      if (req.method == "POST" || req.method == "PUT" ||
 7516          req.method == "PATCH") {
 7517        req.set_header("Content-Length", "0");
 7518      }
 7519    }
 7520  }
 7521
 7522  if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
 7523    if (!req.has_header("Authorization")) {
 7524      req.headers.insert(make_basic_authentication_header(
 7525          basic_auth_username_, basic_auth_password_, false));
 7526    }
 7527  }
 7528
 7529  if (!proxy_basic_auth_username_.empty() &&
 7530      !proxy_basic_auth_password_.empty()) {
 7531    if (!req.has_header("Proxy-Authorization")) {
 7532      req.headers.insert(make_basic_authentication_header(
 7533          proxy_basic_auth_username_, proxy_basic_auth_password_, true));
 7534    }
 7535  }
 7536
 7537  if (!bearer_token_auth_token_.empty()) {
 7538    if (!req.has_header("Authorization")) {
 7539      req.headers.insert(make_bearer_token_authentication_header(
 7540          bearer_token_auth_token_, false));
 7541    }
 7542  }
 7543
 7544  if (!proxy_bearer_token_auth_token_.empty()) {
 7545    if (!req.has_header("Proxy-Authorization")) {
 7546      req.headers.insert(make_bearer_token_authentication_header(
 7547          proxy_bearer_token_auth_token_, true));
 7548    }
 7549  }
 7550
 7551  // Request line and headers
 7552  {
 7553    detail::BufferStream bstrm;
 7554
 7555    // Extract path and query from req.path
 7556    std::string path_part, query_part;
 7557    auto query_pos = req.path.find('?');
 7558    if (query_pos != std::string::npos) {
 7559      path_part = req.path.substr(0, query_pos);
 7560      query_part = req.path.substr(query_pos + 1);
 7561    } else {
 7562      path_part = req.path;
 7563      query_part = "";
 7564    }
 7565
 7566    // Encode path part. If the original `req.path` already contained a
 7567    // query component, preserve its raw query string (including parameter
 7568    // order) instead of reparsing and reassembling it which may reorder
 7569    // parameters due to container ordering (e.g. `Params` uses
 7570    // `std::multimap`). When there is no query in `req.path`, fall back to
 7571    // building a query from `req.params` so existing callers that pass
 7572    // `Params` continue to work.
 7573    auto path_with_query =
 7574        path_encode_ ? detail::encode_path(path_part) : path_part;
 7575
 7576    if (!query_part.empty()) {
 7577      // Normalize the query string (decode then re-encode) while preserving
 7578      // the original parameter order.
 7579      auto normalized = detail::normalize_query_string(query_part);
 7580      if (!normalized.empty()) { path_with_query += '?' + normalized; }
 7581
 7582      // Still populate req.params for handlers/users who read them.
 7583      detail::parse_query_text(query_part, req.params);
 7584    } else {
 7585      // No query in path; parse any query_part (empty) and append params
 7586      // from `req.params` when present (preserves prior behavior for
 7587      // callers who provide Params separately).
 7588      detail::parse_query_text(query_part, req.params);
 7589      if (!req.params.empty()) {
 7590        path_with_query = append_query_params(path_with_query, req.params);
 7591      }
 7592    }
 7593
 7594    // Write request line and headers
 7595    detail::write_request_line(bstrm, req.method, path_with_query);
 7596    if (!detail::check_and_write_headers(bstrm, req.headers, header_writer_,
 7597                                         error)) {
 7598      output_error_log(error, &req);
 7599      return false;
 7600    }
 7601
 7602    // Flush buffer
 7603    auto &data = bstrm.get_buffer();
 7604    if (!detail::write_data(strm, data.data(), data.size())) {
 7605      error = Error::Write;
 7606      output_error_log(error, &req);
 7607      return false;
 7608    }
 7609  }
 7610
 7611  // After sending request line and headers, wait briefly for an early server
 7612  // response (e.g. 4xx) and avoid sending a potentially large request body
 7613  // unnecessarily. This workaround is only enabled on Windows because Unix
 7614  // platforms surface write errors (EPIPE) earlier; on Windows kernel send
 7615  // buffering can accept large writes even when the peer already responded.
 7616  // Check the stream first (which covers SSL via `is_readable()`), then
 7617  // fall back to select on the socket. Only perform the wait for very large
 7618  // request bodies to avoid interfering with normal small requests and
 7619  // reduce side-effects. Poll briefly (up to 50ms as default) for an early
 7620  // response. Skip this check when using Expect: 100-continue, as the protocol
 7621  // handles early responses properly.
 7622#if defined(_WIN32)
 7623  if (!skip_body &&
 7624      req.body.size() > CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD &&
 7625      req.path.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
 7626    auto start = std::chrono::high_resolution_clock::now();
 7627
 7628    for (;;) {
 7629      // Prefer socket-level readiness to avoid SSL_pending() false-positives
 7630      // from SSL internals. If the underlying socket is readable, assume an
 7631      // early response may be present.
 7632      auto sock = strm.socket();
 7633      if (sock != INVALID_SOCKET && detail::select_read(sock, 0, 0) > 0) {
 7634        return false;
 7635      }
 7636
 7637      // Fallback to stream-level check for non-socket streams or when the
 7638      // socket isn't reporting readable. Avoid using `is_readable()` for
 7639      // SSL, since `SSL_pending()` may report buffered records that do not
 7640      // indicate a complete application-level response yet.
 7641      if (!is_ssl() && strm.is_readable()) { return false; }
 7642
 7643      auto now = std::chrono::high_resolution_clock::now();
 7644      auto elapsed =
 7645          std::chrono::duration_cast<std::chrono::milliseconds>(now - start)
 7646              .count();
 7647      if (elapsed >= CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND) {
 7648        break;
 7649      }
 7650
 7651      std::this_thread::sleep_for(std::chrono::milliseconds(1));
 7652    }
 7653  }
 7654#endif
 7655
 7656  // Body
 7657  if (skip_body) { return true; }
 7658
 7659  return write_request_body(strm, req, error);
 7660}
 7661
 7662bool ClientImpl::write_request_body(Stream &strm, Request &req,
 7663                                           Error &error) {
 7664  if (req.body.empty()) {
 7665    return write_content_with_provider(strm, req, error);
 7666  }
 7667
 7668  if (req.upload_progress) {
 7669    auto body_size = req.body.size();
 7670    size_t written = 0;
 7671    auto data = req.body.data();
 7672
 7673    while (written < body_size) {
 7674      size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
 7675      if (!detail::write_data(strm, data + written, to_write)) {
 7676        error = Error::Write;
 7677        output_error_log(error, &req);
 7678        return false;
 7679      }
 7680      written += to_write;
 7681
 7682      if (!req.upload_progress(written, body_size)) {
 7683        error = Error::Canceled;
 7684        output_error_log(error, &req);
 7685        return false;
 7686      }
 7687    }
 7688  } else {
 7689    if (!detail::write_data(strm, req.body.data(), req.body.size())) {
 7690      error = Error::Write;
 7691      output_error_log(error, &req);
 7692      return false;
 7693    }
 7694  }
 7695
 7696  return true;
 7697}
 7698
 7699std::unique_ptr<Response>
 7700ClientImpl::send_with_content_provider_and_receiver(
 7701    Request &req, const char *body, size_t content_length,
 7702    ContentProvider content_provider,
 7703    ContentProviderWithoutLength content_provider_without_length,
 7704    const std::string &content_type, ContentReceiver content_receiver,
 7705    Error &error) {
 7706  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
 7707
 7708#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 7709  if (compress_) { req.set_header("Content-Encoding", "gzip"); }
 7710#endif
 7711
 7712#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 7713  if (compress_ && !content_provider_without_length) {
 7714    // TODO: Brotli support
 7715    detail::gzip_compressor compressor;
 7716
 7717    if (content_provider) {
 7718      auto ok = true;
 7719      size_t offset = 0;
 7720      DataSink data_sink;
 7721
 7722      data_sink.write = [&](const char *data, size_t data_len) -> bool {
 7723        if (ok) {
 7724          auto last = offset + data_len == content_length;
 7725
 7726          auto ret = compressor.compress(
 7727              data, data_len, last,
 7728              [&](const char *compressed_data, size_t compressed_data_len) {
 7729                req.body.append(compressed_data, compressed_data_len);
 7730                return true;
 7731              });
 7732
 7733          if (ret) {
 7734            offset += data_len;
 7735          } else {
 7736            ok = false;
 7737          }
 7738        }
 7739        return ok;
 7740      };
 7741
 7742      while (ok && offset < content_length) {
 7743        if (!content_provider(offset, content_length - offset, data_sink)) {
 7744          error = Error::Canceled;
 7745          output_error_log(error, &req);
 7746          return nullptr;
 7747        }
 7748      }
 7749    } else {
 7750      if (!compressor.compress(body, content_length, true,
 7751                               [&](const char *data, size_t data_len) {
 7752                                 req.body.append(data, data_len);
 7753                                 return true;
 7754                               })) {
 7755        error = Error::Compression;
 7756        output_error_log(error, &req);
 7757        return nullptr;
 7758      }
 7759    }
 7760  } else
 7761#endif
 7762  {
 7763    if (content_provider) {
 7764      req.content_length_ = content_length;
 7765      req.content_provider_ = std::move(content_provider);
 7766      req.is_chunked_content_provider_ = false;
 7767    } else if (content_provider_without_length) {
 7768      req.content_length_ = 0;
 7769      req.content_provider_ = detail::ContentProviderAdapter(
 7770          std::move(content_provider_without_length));
 7771      req.is_chunked_content_provider_ = true;
 7772      req.set_header("Transfer-Encoding", "chunked");
 7773    } else {
 7774      req.body.assign(body, content_length);
 7775    }
 7776  }
 7777
 7778  if (content_receiver) {
 7779    req.content_receiver =
 7780        [content_receiver](const char *data, size_t data_length,
 7781                           size_t /*offset*/, size_t /*total_length*/) {
 7782          return content_receiver(data, data_length);
 7783        };
 7784  }
 7785
 7786  auto res = detail::make_unique<Response>();
 7787  return send(req, *res, error) ? std::move(res) : nullptr;
 7788}
 7789
 7790Result ClientImpl::send_with_content_provider_and_receiver(
 7791    const std::string &method, const std::string &path, const Headers &headers,
 7792    const char *body, size_t content_length, ContentProvider content_provider,
 7793    ContentProviderWithoutLength content_provider_without_length,
 7794    const std::string &content_type, ContentReceiver content_receiver,
 7795    UploadProgress progress) {
 7796  Request req;
 7797  req.method = method;
 7798  req.headers = headers;
 7799  req.path = path;
 7800  req.upload_progress = std::move(progress);
 7801  if (max_timeout_msec_ > 0) {
 7802    req.start_time_ = std::chrono::steady_clock::now();
 7803  }
 7804
 7805  auto error = Error::Success;
 7806
 7807  auto res = send_with_content_provider_and_receiver(
 7808      req, body, content_length, std::move(content_provider),
 7809      std::move(content_provider_without_length), content_type,
 7810      std::move(content_receiver), error);
 7811
 7812#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 7813  return Result{std::move(res), error, std::move(req.headers), last_ssl_error_,
 7814                last_openssl_error_};
 7815#else
 7816  return Result{std::move(res), error, std::move(req.headers)};
 7817#endif
 7818}
 7819
 7820void ClientImpl::output_log(const Request &req,
 7821                                   const Response &res) const {
 7822  if (logger_) {
 7823    std::lock_guard<std::mutex> guard(logger_mutex_);
 7824    logger_(req, res);
 7825  }
 7826}
 7827
 7828void ClientImpl::output_error_log(const Error &err,
 7829                                         const Request *req) const {
 7830  if (error_logger_) {
 7831    std::lock_guard<std::mutex> guard(logger_mutex_);
 7832    error_logger_(err, req);
 7833  }
 7834}
 7835
 7836bool ClientImpl::process_request(Stream &strm, Request &req,
 7837                                        Response &res, bool close_connection,
 7838                                        Error &error) {
 7839  // Auto-add Expect: 100-continue for large bodies
 7840  if (CPPHTTPLIB_EXPECT_100_THRESHOLD > 0 && !req.has_header("Expect")) {
 7841    auto body_size = req.body.empty() ? req.content_length_ : req.body.size();
 7842    if (body_size >= CPPHTTPLIB_EXPECT_100_THRESHOLD) {
 7843      req.set_header("Expect", "100-continue");
 7844    }
 7845  }
 7846
 7847  // Check for Expect: 100-continue
 7848  auto expect_100_continue = req.get_header_value("Expect") == "100-continue";
 7849
 7850  // Send request (skip body if using Expect: 100-continue)
 7851  auto write_request_success =
 7852      write_request(strm, req, close_connection, error, expect_100_continue);
 7853
 7854#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 7855  if (is_ssl()) {
 7856    auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
 7857    if (!is_proxy_enabled) {
 7858      if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
 7859        error = Error::SSLPeerCouldBeClosed_;
 7860        output_error_log(error, &req);
 7861        return false;
 7862      }
 7863    }
 7864  }
 7865#endif
 7866
 7867  // Handle Expect: 100-continue with timeout
 7868  if (expect_100_continue && CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND > 0) {
 7869    time_t sec = CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND / 1000;
 7870    time_t usec = (CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND % 1000) * 1000;
 7871    auto ret = detail::select_read(strm.socket(), sec, usec);
 7872    if (ret <= 0) {
 7873      // Timeout or error: send body anyway (server didn't respond in time)
 7874      if (!write_request_body(strm, req, error)) { return false; }
 7875      expect_100_continue = false; // Switch to normal response handling
 7876    }
 7877  }
 7878
 7879  // Receive response and headers
 7880  // When using Expect: 100-continue, don't auto-skip `100 Continue` response
 7881  if (!read_response_line(strm, req, res, !expect_100_continue) ||
 7882      !detail::read_headers(strm, res.headers)) {
 7883    if (write_request_success) { error = Error::Read; }
 7884    output_error_log(error, &req);
 7885    return false;
 7886  }
 7887
 7888  if (!write_request_success) { return false; }
 7889
 7890  // Handle Expect: 100-continue response
 7891  if (expect_100_continue) {
 7892    if (res.status == StatusCode::Continue_100) {
 7893      // Server accepted, send the body
 7894      if (!write_request_body(strm, req, error)) { return false; }
 7895
 7896      // Read the actual response
 7897      res.headers.clear();
 7898      res.body.clear();
 7899      if (!read_response_line(strm, req, res) ||
 7900          !detail::read_headers(strm, res.headers)) {
 7901        error = Error::Read;
 7902        output_error_log(error, &req);
 7903        return false;
 7904      }
 7905    }
 7906    // If not 100 Continue, server returned an error; proceed with that response
 7907  }
 7908
 7909  // Body
 7910  if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&
 7911      req.method != "CONNECT") {
 7912    auto redirect = 300 < res.status && res.status < 400 &&
 7913                    res.status != StatusCode::NotModified_304 &&
 7914                    follow_location_;
 7915
 7916    if (req.response_handler && !redirect) {
 7917      if (!req.response_handler(res)) {
 7918        error = Error::Canceled;
 7919        output_error_log(error, &req);
 7920        return false;
 7921      }
 7922    }
 7923
 7924    auto out =
 7925        req.content_receiver
 7926            ? static_cast<ContentReceiverWithProgress>(
 7927                  [&](const char *buf, size_t n, size_t off, size_t len) {
 7928                    if (redirect) { return true; }
 7929                    auto ret = req.content_receiver(buf, n, off, len);
 7930                    if (!ret) {
 7931                      error = Error::Canceled;
 7932                      output_error_log(error, &req);
 7933                    }
 7934                    return ret;
 7935                  })
 7936            : static_cast<ContentReceiverWithProgress>(
 7937                  [&](const char *buf, size_t n, size_t /*off*/,
 7938                      size_t /*len*/) {
 7939                    assert(res.body.size() + n <= res.body.max_size());
 7940                    res.body.append(buf, n);
 7941                    return true;
 7942                  });
 7943
 7944    auto progress = [&](size_t current, size_t total) {
 7945      if (!req.download_progress || redirect) { return true; }
 7946      auto ret = req.download_progress(current, total);
 7947      if (!ret) {
 7948        error = Error::Canceled;
 7949        output_error_log(error, &req);
 7950      }
 7951      return ret;
 7952    };
 7953
 7954    if (res.has_header("Content-Length")) {
 7955      if (!req.content_receiver) {
 7956        auto len = res.get_header_value_u64("Content-Length");
 7957        if (len > res.body.max_size()) {
 7958          error = Error::Read;
 7959          output_error_log(error, &req);
 7960          return false;
 7961        }
 7962        res.body.reserve(static_cast<size_t>(len));
 7963      }
 7964    }
 7965
 7966    if (res.status != StatusCode::NotModified_304) {
 7967      int dummy_status;
 7968      if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
 7969                                dummy_status, std::move(progress),
 7970                                std::move(out), decompress_)) {
 7971        if (error != Error::Canceled) { error = Error::Read; }
 7972        output_error_log(error, &req);
 7973        return false;
 7974      }
 7975    }
 7976  }
 7977
 7978  // Log
 7979  output_log(req, res);
 7980
 7981  return true;
 7982}
 7983
 7984ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
 7985    const std::string &boundary, const UploadFormDataItems &items,
 7986    const FormDataProviderItems &provider_items) const {
 7987  size_t cur_item = 0;
 7988  size_t cur_start = 0;
 7989  // cur_item and cur_start are copied to within the std::function and
 7990  // maintain state between successive calls
 7991  return [&, cur_item, cur_start](size_t offset,
 7992                                  DataSink &sink) mutable -> bool {
 7993    if (!offset && !items.empty()) {
 7994      sink.os << detail::serialize_multipart_formdata(items, boundary, false);
 7995      return true;
 7996    } else if (cur_item < provider_items.size()) {
 7997      if (!cur_start) {
 7998        const auto &begin = detail::serialize_multipart_formdata_item_begin(
 7999            provider_items[cur_item], boundary);
 8000        offset += begin.size();
 8001        cur_start = offset;
 8002        sink.os << begin;
 8003      }
 8004
 8005      DataSink cur_sink;
 8006      auto has_data = true;
 8007      cur_sink.write = sink.write;
 8008      cur_sink.done = [&]() { has_data = false; };
 8009
 8010      if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
 8011        return false;
 8012      }
 8013
 8014      if (!has_data) {
 8015        sink.os << detail::serialize_multipart_formdata_item_end();
 8016        cur_item++;
 8017        cur_start = 0;
 8018      }
 8019      return true;
 8020    } else {
 8021      sink.os << detail::serialize_multipart_formdata_finish(boundary);
 8022      sink.done();
 8023      return true;
 8024    }
 8025  };
 8026}
 8027
 8028bool ClientImpl::process_socket(
 8029    const Socket &socket,
 8030    std::chrono::time_point<std::chrono::steady_clock> start_time,
 8031    std::function<bool(Stream &strm)> callback) {
 8032  return detail::process_client_socket(
 8033      socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
 8034      write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
 8035}
 8036
 8037bool ClientImpl::is_ssl() const { return false; }
 8038
 8039Result ClientImpl::Get(const std::string &path,
 8040                              DownloadProgress progress) {
 8041  return Get(path, Headers(), std::move(progress));
 8042}
 8043
 8044Result ClientImpl::Get(const std::string &path, const Params &params,
 8045                              const Headers &headers,
 8046                              DownloadProgress progress) {
 8047  if (params.empty()) { return Get(path, headers); }
 8048
 8049  std::string path_with_query = append_query_params(path, params);
 8050  return Get(path_with_query, headers, std::move(progress));
 8051}
 8052
 8053Result ClientImpl::Get(const std::string &path, const Headers &headers,
 8054                              DownloadProgress progress) {
 8055  Request req;
 8056  req.method = "GET";
 8057  req.path = path;
 8058  req.headers = headers;
 8059  req.download_progress = std::move(progress);
 8060  if (max_timeout_msec_ > 0) {
 8061    req.start_time_ = std::chrono::steady_clock::now();
 8062  }
 8063
 8064  return send_(std::move(req));
 8065}
 8066
 8067Result ClientImpl::Get(const std::string &path,
 8068                              ContentReceiver content_receiver,
 8069                              DownloadProgress progress) {
 8070  return Get(path, Headers(), nullptr, std::move(content_receiver),
 8071             std::move(progress));
 8072}
 8073
 8074Result ClientImpl::Get(const std::string &path, const Headers &headers,
 8075                              ContentReceiver content_receiver,
 8076                              DownloadProgress progress) {
 8077  return Get(path, headers, nullptr, std::move(content_receiver),
 8078             std::move(progress));
 8079}
 8080
 8081Result ClientImpl::Get(const std::string &path,
 8082                              ResponseHandler response_handler,
 8083                              ContentReceiver content_receiver,
 8084                              DownloadProgress progress) {
 8085  return Get(path, Headers(), std::move(response_handler),
 8086             std::move(content_receiver), std::move(progress));
 8087}
 8088
 8089Result ClientImpl::Get(const std::string &path, const Headers &headers,
 8090                              ResponseHandler response_handler,
 8091                              ContentReceiver content_receiver,
 8092                              DownloadProgress progress) {
 8093  Request req;
 8094  req.method = "GET";
 8095  req.path = path;
 8096  req.headers = headers;
 8097  req.response_handler = std::move(response_handler);
 8098  req.content_receiver =
 8099      [content_receiver](const char *data, size_t data_length,
 8100                         size_t /*offset*/, size_t /*total_length*/) {
 8101        return content_receiver(data, data_length);
 8102      };
 8103  req.download_progress = std::move(progress);
 8104  if (max_timeout_msec_ > 0) {
 8105    req.start_time_ = std::chrono::steady_clock::now();
 8106  }
 8107
 8108  return send_(std::move(req));
 8109}
 8110
 8111Result ClientImpl::Get(const std::string &path, const Params &params,
 8112                              const Headers &headers,
 8113                              ContentReceiver content_receiver,
 8114                              DownloadProgress progress) {
 8115  return Get(path, params, headers, nullptr, std::move(content_receiver),
 8116             std::move(progress));
 8117}
 8118
 8119Result ClientImpl::Get(const std::string &path, const Params &params,
 8120                              const Headers &headers,
 8121                              ResponseHandler response_handler,
 8122                              ContentReceiver content_receiver,
 8123                              DownloadProgress progress) {
 8124  if (params.empty()) {
 8125    return Get(path, headers, std::move(response_handler),
 8126               std::move(content_receiver), std::move(progress));
 8127  }
 8128
 8129  std::string path_with_query = append_query_params(path, params);
 8130  return Get(path_with_query, headers, std::move(response_handler),
 8131             std::move(content_receiver), std::move(progress));
 8132}
 8133
 8134Result ClientImpl::Head(const std::string &path) {
 8135  return Head(path, Headers());
 8136}
 8137
 8138Result ClientImpl::Head(const std::string &path,
 8139                               const Headers &headers) {
 8140  Request req;
 8141  req.method = "HEAD";
 8142  req.headers = headers;
 8143  req.path = path;
 8144  if (max_timeout_msec_ > 0) {
 8145    req.start_time_ = std::chrono::steady_clock::now();
 8146  }
 8147
 8148  return send_(std::move(req));
 8149}
 8150
 8151Result ClientImpl::Post(const std::string &path) {
 8152  return Post(path, std::string(), std::string());
 8153}
 8154
 8155Result ClientImpl::Post(const std::string &path,
 8156                               const Headers &headers) {
 8157  return Post(path, headers, nullptr, 0, std::string());
 8158}
 8159
 8160Result ClientImpl::Post(const std::string &path, const char *body,
 8161                               size_t content_length,
 8162                               const std::string &content_type,
 8163                               UploadProgress progress) {
 8164  return Post(path, Headers(), body, content_length, content_type, progress);
 8165}
 8166
 8167Result ClientImpl::Post(const std::string &path, const std::string &body,
 8168                               const std::string &content_type,
 8169                               UploadProgress progress) {
 8170  return Post(path, Headers(), body, content_type, progress);
 8171}
 8172
 8173Result ClientImpl::Post(const std::string &path, const Params &params) {
 8174  return Post(path, Headers(), params);
 8175}
 8176
 8177Result ClientImpl::Post(const std::string &path, size_t content_length,
 8178                               ContentProvider content_provider,
 8179                               const std::string &content_type,
 8180                               UploadProgress progress) {
 8181  return Post(path, Headers(), content_length, std::move(content_provider),
 8182              content_type, progress);
 8183}
 8184
 8185Result ClientImpl::Post(const std::string &path, size_t content_length,
 8186                               ContentProvider content_provider,
 8187                               const std::string &content_type,
 8188                               ContentReceiver content_receiver,
 8189                               UploadProgress progress) {
 8190  return Post(path, Headers(), content_length, std::move(content_provider),
 8191              content_type, std::move(content_receiver), progress);
 8192}
 8193
 8194Result ClientImpl::Post(const std::string &path,
 8195                               ContentProviderWithoutLength content_provider,
 8196                               const std::string &content_type,
 8197                               UploadProgress progress) {
 8198  return Post(path, Headers(), std::move(content_provider), content_type,
 8199              progress);
 8200}
 8201
 8202Result ClientImpl::Post(const std::string &path,
 8203                               ContentProviderWithoutLength content_provider,
 8204                               const std::string &content_type,
 8205                               ContentReceiver content_receiver,
 8206                               UploadProgress progress) {
 8207  return Post(path, Headers(), std::move(content_provider), content_type,
 8208              std::move(content_receiver), progress);
 8209}
 8210
 8211Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8212                               const Params &params) {
 8213  auto query = detail::params_to_query_str(params);
 8214  return Post(path, headers, query, "application/x-www-form-urlencoded");
 8215}
 8216
 8217Result ClientImpl::Post(const std::string &path,
 8218                               const UploadFormDataItems &items,
 8219                               UploadProgress progress) {
 8220  return Post(path, Headers(), items, progress);
 8221}
 8222
 8223Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8224                               const UploadFormDataItems &items,
 8225                               UploadProgress progress) {
 8226  const auto &boundary = detail::make_multipart_data_boundary();
 8227  const auto &content_type =
 8228      detail::serialize_multipart_formdata_get_content_type(boundary);
 8229  const auto &body = detail::serialize_multipart_formdata(items, boundary);
 8230  return Post(path, headers, body, content_type, progress);
 8231}
 8232
 8233Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8234                               const UploadFormDataItems &items,
 8235                               const std::string &boundary,
 8236                               UploadProgress progress) {
 8237  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
 8238    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
 8239  }
 8240
 8241  const auto &content_type =
 8242      detail::serialize_multipart_formdata_get_content_type(boundary);
 8243  const auto &body = detail::serialize_multipart_formdata(items, boundary);
 8244  return Post(path, headers, body, content_type, progress);
 8245}
 8246
 8247Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8248                               const char *body, size_t content_length,
 8249                               const std::string &content_type,
 8250                               UploadProgress progress) {
 8251  return send_with_content_provider_and_receiver(
 8252      "POST", path, headers, body, content_length, nullptr, nullptr,
 8253      content_type, nullptr, progress);
 8254}
 8255
 8256Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8257                               const std::string &body,
 8258                               const std::string &content_type,
 8259                               UploadProgress progress) {
 8260  return send_with_content_provider_and_receiver(
 8261      "POST", path, headers, body.data(), body.size(), nullptr, nullptr,
 8262      content_type, nullptr, progress);
 8263}
 8264
 8265Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8266                               size_t content_length,
 8267                               ContentProvider content_provider,
 8268                               const std::string &content_type,
 8269                               UploadProgress progress) {
 8270  return send_with_content_provider_and_receiver(
 8271      "POST", path, headers, nullptr, content_length,
 8272      std::move(content_provider), nullptr, content_type, nullptr, progress);
 8273}
 8274
 8275Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8276                               size_t content_length,
 8277                               ContentProvider content_provider,
 8278                               const std::string &content_type,
 8279                               ContentReceiver content_receiver,
 8280                               DownloadProgress progress) {
 8281  return send_with_content_provider_and_receiver(
 8282      "POST", path, headers, nullptr, content_length,
 8283      std::move(content_provider), nullptr, content_type,
 8284      std::move(content_receiver), std::move(progress));
 8285}
 8286
 8287Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8288                               ContentProviderWithoutLength content_provider,
 8289                               const std::string &content_type,
 8290                               UploadProgress progress) {
 8291  return send_with_content_provider_and_receiver(
 8292      "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
 8293      content_type, nullptr, progress);
 8294}
 8295
 8296Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8297                               ContentProviderWithoutLength content_provider,
 8298                               const std::string &content_type,
 8299                               ContentReceiver content_receiver,
 8300                               DownloadProgress progress) {
 8301  return send_with_content_provider_and_receiver(
 8302      "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
 8303      content_type, std::move(content_receiver), std::move(progress));
 8304}
 8305
 8306Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8307                               const UploadFormDataItems &items,
 8308                               const FormDataProviderItems &provider_items,
 8309                               UploadProgress progress) {
 8310  const auto &boundary = detail::make_multipart_data_boundary();
 8311  const auto &content_type =
 8312      detail::serialize_multipart_formdata_get_content_type(boundary);
 8313  return send_with_content_provider_and_receiver(
 8314      "POST", path, headers, nullptr, 0, nullptr,
 8315      get_multipart_content_provider(boundary, items, provider_items),
 8316      content_type, nullptr, progress);
 8317}
 8318
 8319Result ClientImpl::Post(const std::string &path, const Headers &headers,
 8320                               const std::string &body,
 8321                               const std::string &content_type,
 8322                               ContentReceiver content_receiver,
 8323                               DownloadProgress progress) {
 8324  Request req;
 8325  req.method = "POST";
 8326  req.path = path;
 8327  req.headers = headers;
 8328  req.body = body;
 8329  req.content_receiver =
 8330      [content_receiver](const char *data, size_t data_length,
 8331                         size_t /*offset*/, size_t /*total_length*/) {
 8332        return content_receiver(data, data_length);
 8333      };
 8334  req.download_progress = std::move(progress);
 8335
 8336  if (max_timeout_msec_ > 0) {
 8337    req.start_time_ = std::chrono::steady_clock::now();
 8338  }
 8339
 8340  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
 8341
 8342  return send_(std::move(req));
 8343}
 8344
 8345Result ClientImpl::Put(const std::string &path) {
 8346  return Put(path, std::string(), std::string());
 8347}
 8348
 8349Result ClientImpl::Put(const std::string &path, const Headers &headers) {
 8350  return Put(path, headers, nullptr, 0, std::string());
 8351}
 8352
 8353Result ClientImpl::Put(const std::string &path, const char *body,
 8354                              size_t content_length,
 8355                              const std::string &content_type,
 8356                              UploadProgress progress) {
 8357  return Put(path, Headers(), body, content_length, content_type, progress);
 8358}
 8359
 8360Result ClientImpl::Put(const std::string &path, const std::string &body,
 8361                              const std::string &content_type,
 8362                              UploadProgress progress) {
 8363  return Put(path, Headers(), body, content_type, progress);
 8364}
 8365
 8366Result ClientImpl::Put(const std::string &path, const Params &params) {
 8367  return Put(path, Headers(), params);
 8368}
 8369
 8370Result ClientImpl::Put(const std::string &path, size_t content_length,
 8371                              ContentProvider content_provider,
 8372                              const std::string &content_type,
 8373                              UploadProgress progress) {
 8374  return Put(path, Headers(), content_length, std::move(content_provider),
 8375             content_type, progress);
 8376}
 8377
 8378Result ClientImpl::Put(const std::string &path, size_t content_length,
 8379                              ContentProvider content_provider,
 8380                              const std::string &content_type,
 8381                              ContentReceiver content_receiver,
 8382                              UploadProgress progress) {
 8383  return Put(path, Headers(), content_length, std::move(content_provider),
 8384             content_type, std::move(content_receiver), progress);
 8385}
 8386
 8387Result ClientImpl::Put(const std::string &path,
 8388                              ContentProviderWithoutLength content_provider,
 8389                              const std::string &content_type,
 8390                              UploadProgress progress) {
 8391  return Put(path, Headers(), std::move(content_provider), content_type,
 8392             progress);
 8393}
 8394
 8395Result ClientImpl::Put(const std::string &path,
 8396                              ContentProviderWithoutLength content_provider,
 8397                              const std::string &content_type,
 8398                              ContentReceiver content_receiver,
 8399                              UploadProgress progress) {
 8400  return Put(path, Headers(), std::move(content_provider), content_type,
 8401             std::move(content_receiver), progress);
 8402}
 8403
 8404Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8405                              const Params &params) {
 8406  auto query = detail::params_to_query_str(params);
 8407  return Put(path, headers, query, "application/x-www-form-urlencoded");
 8408}
 8409
 8410Result ClientImpl::Put(const std::string &path,
 8411                              const UploadFormDataItems &items,
 8412                              UploadProgress progress) {
 8413  return Put(path, Headers(), items, progress);
 8414}
 8415
 8416Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8417                              const UploadFormDataItems &items,
 8418                              UploadProgress progress) {
 8419  const auto &boundary = detail::make_multipart_data_boundary();
 8420  const auto &content_type =
 8421      detail::serialize_multipart_formdata_get_content_type(boundary);
 8422  const auto &body = detail::serialize_multipart_formdata(items, boundary);
 8423  return Put(path, headers, body, content_type, progress);
 8424}
 8425
 8426Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8427                              const UploadFormDataItems &items,
 8428                              const std::string &boundary,
 8429                              UploadProgress progress) {
 8430  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
 8431    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
 8432  }
 8433
 8434  const auto &content_type =
 8435      detail::serialize_multipart_formdata_get_content_type(boundary);
 8436  const auto &body = detail::serialize_multipart_formdata(items, boundary);
 8437  return Put(path, headers, body, content_type, progress);
 8438}
 8439
 8440Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8441                              const char *body, size_t content_length,
 8442                              const std::string &content_type,
 8443                              UploadProgress progress) {
 8444  return send_with_content_provider_and_receiver(
 8445      "PUT", path, headers, body, content_length, nullptr, nullptr,
 8446      content_type, nullptr, progress);
 8447}
 8448
 8449Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8450                              const std::string &body,
 8451                              const std::string &content_type,
 8452                              UploadProgress progress) {
 8453  return send_with_content_provider_and_receiver(
 8454      "PUT", path, headers, body.data(), body.size(), nullptr, nullptr,
 8455      content_type, nullptr, progress);
 8456}
 8457
 8458Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8459                              size_t content_length,
 8460                              ContentProvider content_provider,
 8461                              const std::string &content_type,
 8462                              UploadProgress progress) {
 8463  return send_with_content_provider_and_receiver(
 8464      "PUT", path, headers, nullptr, content_length,
 8465      std::move(content_provider), nullptr, content_type, nullptr, progress);
 8466}
 8467
 8468Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8469                              size_t content_length,
 8470                              ContentProvider content_provider,
 8471                              const std::string &content_type,
 8472                              ContentReceiver content_receiver,
 8473                              UploadProgress progress) {
 8474  return send_with_content_provider_and_receiver(
 8475      "PUT", path, headers, nullptr, content_length,
 8476      std::move(content_provider), nullptr, content_type,
 8477      std::move(content_receiver), progress);
 8478}
 8479
 8480Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8481                              ContentProviderWithoutLength content_provider,
 8482                              const std::string &content_type,
 8483                              UploadProgress progress) {
 8484  return send_with_content_provider_and_receiver(
 8485      "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
 8486      content_type, nullptr, progress);
 8487}
 8488
 8489Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8490                              ContentProviderWithoutLength content_provider,
 8491                              const std::string &content_type,
 8492                              ContentReceiver content_receiver,
 8493                              UploadProgress progress) {
 8494  return send_with_content_provider_and_receiver(
 8495      "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
 8496      content_type, std::move(content_receiver), progress);
 8497}
 8498
 8499Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8500                              const UploadFormDataItems &items,
 8501                              const FormDataProviderItems &provider_items,
 8502                              UploadProgress progress) {
 8503  const auto &boundary = detail::make_multipart_data_boundary();
 8504  const auto &content_type =
 8505      detail::serialize_multipart_formdata_get_content_type(boundary);
 8506  return send_with_content_provider_and_receiver(
 8507      "PUT", path, headers, nullptr, 0, nullptr,
 8508      get_multipart_content_provider(boundary, items, provider_items),
 8509      content_type, nullptr, progress);
 8510}
 8511
 8512Result ClientImpl::Put(const std::string &path, const Headers &headers,
 8513                              const std::string &body,
 8514                              const std::string &content_type,
 8515                              ContentReceiver content_receiver,
 8516                              DownloadProgress progress) {
 8517  Request req;
 8518  req.method = "PUT";
 8519  req.path = path;
 8520  req.headers = headers;
 8521  req.body = body;
 8522  req.content_receiver =
 8523      [content_receiver](const char *data, size_t data_length,
 8524                         size_t /*offset*/, size_t /*total_length*/) {
 8525        return content_receiver(data, data_length);
 8526      };
 8527  req.download_progress = std::move(progress);
 8528
 8529  if (max_timeout_msec_ > 0) {
 8530    req.start_time_ = std::chrono::steady_clock::now();
 8531  }
 8532
 8533  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
 8534
 8535  return send_(std::move(req));
 8536}
 8537
 8538Result ClientImpl::Patch(const std::string &path) {
 8539  return Patch(path, std::string(), std::string());
 8540}
 8541
 8542Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8543                                UploadProgress progress) {
 8544  return Patch(path, headers, nullptr, 0, std::string(), progress);
 8545}
 8546
 8547Result ClientImpl::Patch(const std::string &path, const char *body,
 8548                                size_t content_length,
 8549                                const std::string &content_type,
 8550                                UploadProgress progress) {
 8551  return Patch(path, Headers(), body, content_length, content_type, progress);
 8552}
 8553
 8554Result ClientImpl::Patch(const std::string &path,
 8555                                const std::string &body,
 8556                                const std::string &content_type,
 8557                                UploadProgress progress) {
 8558  return Patch(path, Headers(), body, content_type, progress);
 8559}
 8560
 8561Result ClientImpl::Patch(const std::string &path, const Params &params) {
 8562  return Patch(path, Headers(), params);
 8563}
 8564
 8565Result ClientImpl::Patch(const std::string &path, size_t content_length,
 8566                                ContentProvider content_provider,
 8567                                const std::string &content_type,
 8568                                UploadProgress progress) {
 8569  return Patch(path, Headers(), content_length, std::move(content_provider),
 8570               content_type, progress);
 8571}
 8572
 8573Result ClientImpl::Patch(const std::string &path, size_t content_length,
 8574                                ContentProvider content_provider,
 8575                                const std::string &content_type,
 8576                                ContentReceiver content_receiver,
 8577                                UploadProgress progress) {
 8578  return Patch(path, Headers(), content_length, std::move(content_provider),
 8579               content_type, std::move(content_receiver), progress);
 8580}
 8581
 8582Result ClientImpl::Patch(const std::string &path,
 8583                                ContentProviderWithoutLength content_provider,
 8584                                const std::string &content_type,
 8585                                UploadProgress progress) {
 8586  return Patch(path, Headers(), std::move(content_provider), content_type,
 8587               progress);
 8588}
 8589
 8590Result ClientImpl::Patch(const std::string &path,
 8591                                ContentProviderWithoutLength content_provider,
 8592                                const std::string &content_type,
 8593                                ContentReceiver content_receiver,
 8594                                UploadProgress progress) {
 8595  return Patch(path, Headers(), std::move(content_provider), content_type,
 8596               std::move(content_receiver), progress);
 8597}
 8598
 8599Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8600                                const Params &params) {
 8601  auto query = detail::params_to_query_str(params);
 8602  return Patch(path, headers, query, "application/x-www-form-urlencoded");
 8603}
 8604
 8605Result ClientImpl::Patch(const std::string &path,
 8606                                const UploadFormDataItems &items,
 8607                                UploadProgress progress) {
 8608  return Patch(path, Headers(), items, progress);
 8609}
 8610
 8611Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8612                                const UploadFormDataItems &items,
 8613                                UploadProgress progress) {
 8614  const auto &boundary = detail::make_multipart_data_boundary();
 8615  const auto &content_type =
 8616      detail::serialize_multipart_formdata_get_content_type(boundary);
 8617  const auto &body = detail::serialize_multipart_formdata(items, boundary);
 8618  return Patch(path, headers, body, content_type, progress);
 8619}
 8620
 8621Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8622                                const UploadFormDataItems &items,
 8623                                const std::string &boundary,
 8624                                UploadProgress progress) {
 8625  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
 8626    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
 8627  }
 8628
 8629  const auto &content_type =
 8630      detail::serialize_multipart_formdata_get_content_type(boundary);
 8631  const auto &body = detail::serialize_multipart_formdata(items, boundary);
 8632  return Patch(path, headers, body, content_type, progress);
 8633}
 8634
 8635Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8636                                const char *body, size_t content_length,
 8637                                const std::string &content_type,
 8638                                UploadProgress progress) {
 8639  return send_with_content_provider_and_receiver(
 8640      "PATCH", path, headers, body, content_length, nullptr, nullptr,
 8641      content_type, nullptr, progress);
 8642}
 8643
 8644Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8645                                const std::string &body,
 8646                                const std::string &content_type,
 8647                                UploadProgress progress) {
 8648  return send_with_content_provider_and_receiver(
 8649      "PATCH", path, headers, body.data(), body.size(), nullptr, nullptr,
 8650      content_type, nullptr, progress);
 8651}
 8652
 8653Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8654                                size_t content_length,
 8655                                ContentProvider content_provider,
 8656                                const std::string &content_type,
 8657                                UploadProgress progress) {
 8658  return send_with_content_provider_and_receiver(
 8659      "PATCH", path, headers, nullptr, content_length,
 8660      std::move(content_provider), nullptr, content_type, nullptr, progress);
 8661}
 8662
 8663Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8664                                size_t content_length,
 8665                                ContentProvider content_provider,
 8666                                const std::string &content_type,
 8667                                ContentReceiver content_receiver,
 8668                                UploadProgress progress) {
 8669  return send_with_content_provider_and_receiver(
 8670      "PATCH", path, headers, nullptr, content_length,
 8671      std::move(content_provider), nullptr, content_type,
 8672      std::move(content_receiver), progress);
 8673}
 8674
 8675Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8676                                ContentProviderWithoutLength content_provider,
 8677                                const std::string &content_type,
 8678                                UploadProgress progress) {
 8679  return send_with_content_provider_and_receiver(
 8680      "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
 8681      content_type, nullptr, progress);
 8682}
 8683
 8684Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8685                                ContentProviderWithoutLength content_provider,
 8686                                const std::string &content_type,
 8687                                ContentReceiver content_receiver,
 8688                                UploadProgress progress) {
 8689  return send_with_content_provider_and_receiver(
 8690      "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
 8691      content_type, std::move(content_receiver), progress);
 8692}
 8693
 8694Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8695                                const UploadFormDataItems &items,
 8696                                const FormDataProviderItems &provider_items,
 8697                                UploadProgress progress) {
 8698  const auto &boundary = detail::make_multipart_data_boundary();
 8699  const auto &content_type =
 8700      detail::serialize_multipart_formdata_get_content_type(boundary);
 8701  return send_with_content_provider_and_receiver(
 8702      "PATCH", path, headers, nullptr, 0, nullptr,
 8703      get_multipart_content_provider(boundary, items, provider_items),
 8704      content_type, nullptr, progress);
 8705}
 8706
 8707Result ClientImpl::Patch(const std::string &path, const Headers &headers,
 8708                                const std::string &body,
 8709                                const std::string &content_type,
 8710                                ContentReceiver content_receiver,
 8711                                DownloadProgress progress) {
 8712  Request req;
 8713  req.method = "PATCH";
 8714  req.path = path;
 8715  req.headers = headers;
 8716  req.body = body;
 8717  req.content_receiver =
 8718      [content_receiver](const char *data, size_t data_length,
 8719                         size_t /*offset*/, size_t /*total_length*/) {
 8720        return content_receiver(data, data_length);
 8721      };
 8722  req.download_progress = std::move(progress);
 8723
 8724  if (max_timeout_msec_ > 0) {
 8725    req.start_time_ = std::chrono::steady_clock::now();
 8726  }
 8727
 8728  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
 8729
 8730  return send_(std::move(req));
 8731}
 8732
 8733Result ClientImpl::Delete(const std::string &path,
 8734                                 DownloadProgress progress) {
 8735  return Delete(path, Headers(), std::string(), std::string(), progress);
 8736}
 8737
 8738Result ClientImpl::Delete(const std::string &path,
 8739                                 const Headers &headers,
 8740                                 DownloadProgress progress) {
 8741  return Delete(path, headers, std::string(), std::string(), progress);
 8742}
 8743
 8744Result ClientImpl::Delete(const std::string &path, const char *body,
 8745                                 size_t content_length,
 8746                                 const std::string &content_type,
 8747                                 DownloadProgress progress) {
 8748  return Delete(path, Headers(), body, content_length, content_type, progress);
 8749}
 8750
 8751Result ClientImpl::Delete(const std::string &path,
 8752                                 const std::string &body,
 8753                                 const std::string &content_type,
 8754                                 DownloadProgress progress) {
 8755  return Delete(path, Headers(), body.data(), body.size(), content_type,
 8756                progress);
 8757}
 8758
 8759Result ClientImpl::Delete(const std::string &path,
 8760                                 const Headers &headers,
 8761                                 const std::string &body,
 8762                                 const std::string &content_type,
 8763                                 DownloadProgress progress) {
 8764  return Delete(path, headers, body.data(), body.size(), content_type,
 8765                progress);
 8766}
 8767
 8768Result ClientImpl::Delete(const std::string &path, const Params &params,
 8769                                 DownloadProgress progress) {
 8770  return Delete(path, Headers(), params, progress);
 8771}
 8772
 8773Result ClientImpl::Delete(const std::string &path,
 8774                                 const Headers &headers, const Params &params,
 8775                                 DownloadProgress progress) {
 8776  auto query = detail::params_to_query_str(params);
 8777  return Delete(path, headers, query, "application/x-www-form-urlencoded",
 8778                progress);
 8779}
 8780
 8781Result ClientImpl::Delete(const std::string &path,
 8782                                 const Headers &headers, const char *body,
 8783                                 size_t content_length,
 8784                                 const std::string &content_type,
 8785                                 DownloadProgress progress) {
 8786  Request req;
 8787  req.method = "DELETE";
 8788  req.headers = headers;
 8789  req.path = path;
 8790  req.download_progress = std::move(progress);
 8791  if (max_timeout_msec_ > 0) {
 8792    req.start_time_ = std::chrono::steady_clock::now();
 8793  }
 8794
 8795  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
 8796  req.body.assign(body, content_length);
 8797
 8798  return send_(std::move(req));
 8799}
 8800
 8801Result ClientImpl::Options(const std::string &path) {
 8802  return Options(path, Headers());
 8803}
 8804
 8805Result ClientImpl::Options(const std::string &path,
 8806                                  const Headers &headers) {
 8807  Request req;
 8808  req.method = "OPTIONS";
 8809  req.headers = headers;
 8810  req.path = path;
 8811  if (max_timeout_msec_ > 0) {
 8812    req.start_time_ = std::chrono::steady_clock::now();
 8813  }
 8814
 8815  return send_(std::move(req));
 8816}
 8817
 8818void ClientImpl::stop() {
 8819  std::lock_guard<std::mutex> guard(socket_mutex_);
 8820
 8821  // If there is anything ongoing right now, the ONLY thread-safe thing we can
 8822  // do is to shutdown_socket, so that threads using this socket suddenly
 8823  // discover they can't read/write any more and error out. Everything else
 8824  // (closing the socket, shutting ssl down) is unsafe because these actions
 8825  // are not thread-safe.
 8826  if (socket_requests_in_flight_ > 0) {
 8827    shutdown_socket(socket_);
 8828
 8829    // Aside from that, we set a flag for the socket to be closed when we're
 8830    // done.
 8831    socket_should_be_closed_when_request_is_done_ = true;
 8832    return;
 8833  }
 8834
 8835  // Otherwise, still holding the mutex, we can shut everything down ourselves
 8836  shutdown_ssl(socket_, true);
 8837  shutdown_socket(socket_);
 8838  close_socket(socket_);
 8839}
 8840
 8841std::string ClientImpl::host() const { return host_; }
 8842
 8843int ClientImpl::port() const { return port_; }
 8844
 8845size_t ClientImpl::is_socket_open() const {
 8846  std::lock_guard<std::mutex> guard(socket_mutex_);
 8847  return socket_.is_open();
 8848}
 8849
 8850socket_t ClientImpl::socket() const { return socket_.sock; }
 8851
 8852void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
 8853  connection_timeout_sec_ = sec;
 8854  connection_timeout_usec_ = usec;
 8855}
 8856
 8857void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
 8858  read_timeout_sec_ = sec;
 8859  read_timeout_usec_ = usec;
 8860}
 8861
 8862void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
 8863  write_timeout_sec_ = sec;
 8864  write_timeout_usec_ = usec;
 8865}
 8866
 8867void ClientImpl::set_max_timeout(time_t msec) {
 8868  max_timeout_msec_ = msec;
 8869}
 8870
 8871void ClientImpl::set_basic_auth(const std::string &username,
 8872                                       const std::string &password) {
 8873  basic_auth_username_ = username;
 8874  basic_auth_password_ = password;
 8875}
 8876
 8877void ClientImpl::set_bearer_token_auth(const std::string &token) {
 8878  bearer_token_auth_token_ = token;
 8879}
 8880
 8881#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 8882void ClientImpl::set_digest_auth(const std::string &username,
 8883                                        const std::string &password) {
 8884  digest_auth_username_ = username;
 8885  digest_auth_password_ = password;
 8886}
 8887#endif
 8888
 8889void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
 8890
 8891void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
 8892
 8893void ClientImpl::set_path_encode(bool on) { path_encode_ = on; }
 8894
 8895void
 8896ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
 8897  addr_map_ = std::move(addr_map);
 8898}
 8899
 8900void ClientImpl::set_default_headers(Headers headers) {
 8901  default_headers_ = std::move(headers);
 8902}
 8903
 8904void ClientImpl::set_header_writer(
 8905    std::function<ssize_t(Stream &, Headers &)> const &writer) {
 8906  header_writer_ = writer;
 8907}
 8908
 8909void ClientImpl::set_address_family(int family) {
 8910  address_family_ = family;
 8911}
 8912
 8913void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
 8914
 8915void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }
 8916
 8917void ClientImpl::set_socket_options(SocketOptions socket_options) {
 8918  socket_options_ = std::move(socket_options);
 8919}
 8920
 8921void ClientImpl::set_compress(bool on) { compress_ = on; }
 8922
 8923void ClientImpl::set_decompress(bool on) { decompress_ = on; }
 8924
 8925void ClientImpl::set_interface(const std::string &intf) {
 8926  interface_ = intf;
 8927}
 8928
 8929void ClientImpl::set_proxy(const std::string &host, int port) {
 8930  proxy_host_ = host;
 8931  proxy_port_ = port;
 8932}
 8933
 8934void ClientImpl::set_proxy_basic_auth(const std::string &username,
 8935                                             const std::string &password) {
 8936  proxy_basic_auth_username_ = username;
 8937  proxy_basic_auth_password_ = password;
 8938}
 8939
 8940void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {
 8941  proxy_bearer_token_auth_token_ = token;
 8942}
 8943
 8944#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 8945void ClientImpl::set_proxy_digest_auth(const std::string &username,
 8946                                              const std::string &password) {
 8947  proxy_digest_auth_username_ = username;
 8948  proxy_digest_auth_password_ = password;
 8949}
 8950
 8951void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,
 8952                                         const std::string &ca_cert_dir_path) {
 8953  ca_cert_file_path_ = ca_cert_file_path;
 8954  ca_cert_dir_path_ = ca_cert_dir_path;
 8955}
 8956
 8957void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
 8958  if (ca_cert_store && ca_cert_store != ca_cert_store_) {
 8959    ca_cert_store_ = ca_cert_store;
 8960  }
 8961}
 8962
 8963X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
 8964                                                    std::size_t size) const {
 8965  auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
 8966  auto se = detail::scope_exit([&] { BIO_free_all(mem); });
 8967  if (!mem) { return nullptr; }
 8968
 8969  auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
 8970  if (!inf) { return nullptr; }
 8971
 8972  auto cts = X509_STORE_new();
 8973  if (cts) {
 8974    for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
 8975      auto itmp = sk_X509_INFO_value(inf, i);
 8976      if (!itmp) { continue; }
 8977
 8978      if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }
 8979      if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }
 8980    }
 8981  }
 8982
 8983  sk_X509_INFO_pop_free(inf, X509_INFO_free);
 8984  return cts;
 8985}
 8986
 8987void ClientImpl::enable_server_certificate_verification(bool enabled) {
 8988  server_certificate_verification_ = enabled;
 8989}
 8990
 8991void ClientImpl::enable_server_hostname_verification(bool enabled) {
 8992  server_hostname_verification_ = enabled;
 8993}
 8994
 8995void ClientImpl::set_server_certificate_verifier(
 8996    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
 8997  server_certificate_verifier_ = verifier;
 8998}
 8999#endif
 9000
 9001void ClientImpl::set_logger(Logger logger) {
 9002  logger_ = std::move(logger);
 9003}
 9004
 9005void ClientImpl::set_error_logger(ErrorLogger error_logger) {
 9006  error_logger_ = std::move(error_logger);
 9007}
 9008
 9009/*
 9010 * SSL Implementation
 9011 */
 9012#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 9013namespace detail {
 9014
 9015bool is_ip_address(const std::string &host) {
 9016  struct in_addr addr4;
 9017  struct in6_addr addr6;
 9018  return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
 9019         inet_pton(AF_INET6, host.c_str(), &addr6) == 1;
 9020}
 9021
 9022template <typename U, typename V>
 9023SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
 9024                    U SSL_connect_or_accept, V setup) {
 9025  SSL *ssl = nullptr;
 9026  {
 9027    std::lock_guard<std::mutex> guard(ctx_mutex);
 9028    ssl = SSL_new(ctx);
 9029  }
 9030
 9031  if (ssl) {
 9032    set_nonblocking(sock, true);
 9033    auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
 9034    BIO_set_nbio(bio, 1);
 9035    SSL_set_bio(ssl, bio, bio);
 9036
 9037    if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
 9038      SSL_shutdown(ssl);
 9039      {
 9040        std::lock_guard<std::mutex> guard(ctx_mutex);
 9041        SSL_free(ssl);
 9042      }
 9043      set_nonblocking(sock, false);
 9044      return nullptr;
 9045    }
 9046    BIO_set_nbio(bio, 0);
 9047    set_nonblocking(sock, false);
 9048  }
 9049
 9050  return ssl;
 9051}
 9052
 9053void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,
 9054                       bool shutdown_gracefully) {
 9055  // sometimes we may want to skip this to try to avoid SIGPIPE if we know
 9056  // the remote has closed the network connection
 9057  // Note that it is not always possible to avoid SIGPIPE, this is merely a
 9058  // best-efforts.
 9059  if (shutdown_gracefully) {
 9060    (void)(sock);
 9061    // SSL_shutdown() returns 0 on first call (indicating close_notify alert
 9062    // sent) and 1 on subsequent call (indicating close_notify alert received)
 9063    if (SSL_shutdown(ssl) == 0) {
 9064      // Expected to return 1, but even if it doesn't, we free ssl
 9065      SSL_shutdown(ssl);
 9066    }
 9067  }
 9068
 9069  std::lock_guard<std::mutex> guard(ctx_mutex);
 9070  SSL_free(ssl);
 9071}
 9072
 9073template <typename U>
 9074bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
 9075                                       U ssl_connect_or_accept,
 9076                                       time_t timeout_sec, time_t timeout_usec,
 9077                                       int *ssl_error) {
 9078  auto res = 0;
 9079  while ((res = ssl_connect_or_accept(ssl)) != 1) {
 9080    auto err = SSL_get_error(ssl, res);
 9081    switch (err) {
 9082    case SSL_ERROR_WANT_READ:
 9083      if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
 9084      break;
 9085    case SSL_ERROR_WANT_WRITE:
 9086      if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
 9087      break;
 9088    default: break;
 9089    }
 9090    if (ssl_error) { *ssl_error = err; }
 9091    return false;
 9092  }
 9093  return true;
 9094}
 9095
 9096template <typename T>
 9097bool process_server_socket_ssl(
 9098    const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,
 9099    size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
 9100    time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
 9101    time_t write_timeout_usec, T callback) {
 9102  return process_server_socket_core(
 9103      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
 9104      [&](bool close_connection, bool &connection_closed) {
 9105        SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
 9106                             write_timeout_sec, write_timeout_usec);
 9107        return callback(strm, close_connection, connection_closed);
 9108      });
 9109}
 9110
 9111template <typename T>
 9112bool process_client_socket_ssl(
 9113    SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
 9114    time_t write_timeout_sec, time_t write_timeout_usec,
 9115    time_t max_timeout_msec,
 9116    std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
 9117  SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
 9118                       write_timeout_sec, write_timeout_usec, max_timeout_msec,
 9119                       start_time);
 9120  return callback(strm);
 9121}
 9122
 9123// SSL socket stream implementation
 9124SSLSocketStream::SSLSocketStream(
 9125    socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
 9126    time_t write_timeout_sec, time_t write_timeout_usec,
 9127    time_t max_timeout_msec,
 9128    std::chrono::time_point<std::chrono::steady_clock> start_time)
 9129    : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
 9130      read_timeout_usec_(read_timeout_usec),
 9131      write_timeout_sec_(write_timeout_sec),
 9132      write_timeout_usec_(write_timeout_usec),
 9133      max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
 9134  SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
 9135}
 9136
 9137SSLSocketStream::~SSLSocketStream() = default;
 9138
 9139bool SSLSocketStream::is_readable() const {
 9140  return SSL_pending(ssl_) > 0;
 9141}
 9142
 9143bool SSLSocketStream::wait_readable() const {
 9144  if (max_timeout_msec_ <= 0) {
 9145    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
 9146  }
 9147
 9148  time_t read_timeout_sec;
 9149  time_t read_timeout_usec;
 9150  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
 9151                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
 9152
 9153  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
 9154}
 9155
 9156bool SSLSocketStream::wait_writable() const {
 9157  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
 9158         is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);
 9159}
 9160
 9161ssize_t SSLSocketStream::read(char *ptr, size_t size) {
 9162  if (SSL_pending(ssl_) > 0) {
 9163    auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
 9164    if (ret == 0) { error_ = Error::ConnectionClosed; }
 9165    return ret;
 9166  } else if (wait_readable()) {
 9167    auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
 9168    if (ret < 0) {
 9169      auto err = SSL_get_error(ssl_, ret);
 9170      auto n = 1000;
 9171#ifdef _WIN32
 9172      while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
 9173                          (err == SSL_ERROR_SYSCALL &&
 9174                           WSAGetLastError() == WSAETIMEDOUT))) {
 9175#else
 9176      while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
 9177#endif
 9178        if (SSL_pending(ssl_) > 0) {
 9179          return SSL_read(ssl_, ptr, static_cast<int>(size));
 9180        } else if (wait_readable()) {
 9181          std::this_thread::sleep_for(std::chrono::microseconds{10});
 9182          ret = SSL_read(ssl_, ptr, static_cast<int>(size));
 9183          if (ret >= 0) { return ret; }
 9184          err = SSL_get_error(ssl_, ret);
 9185        } else {
 9186          break;
 9187        }
 9188      }
 9189      assert(ret < 0);
 9190    } else if (ret == 0) {
 9191      error_ = Error::ConnectionClosed;
 9192    }
 9193    return ret;
 9194  } else {
 9195    error_ = Error::Timeout;
 9196    return -1;
 9197  }
 9198}
 9199
 9200ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
 9201  if (wait_writable()) {
 9202    auto handle_size = static_cast<int>(
 9203        std::min<size_t>(size, (std::numeric_limits<int>::max)()));
 9204
 9205    auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
 9206    if (ret < 0) {
 9207      auto err = SSL_get_error(ssl_, ret);
 9208      auto n = 1000;
 9209#ifdef _WIN32
 9210      while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
 9211                          (err == SSL_ERROR_SYSCALL &&
 9212                           WSAGetLastError() == WSAETIMEDOUT))) {
 9213#else
 9214      while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
 9215#endif
 9216        if (wait_writable()) {
 9217          std::this_thread::sleep_for(std::chrono::microseconds{10});
 9218          ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
 9219          if (ret >= 0) { return ret; }
 9220          err = SSL_get_error(ssl_, ret);
 9221        } else {
 9222          break;
 9223        }
 9224      }
 9225      assert(ret < 0);
 9226    }
 9227    return ret;
 9228  }
 9229  return -1;
 9230}
 9231
 9232void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
 9233                                                    int &port) const {
 9234  detail::get_remote_ip_and_port(sock_, ip, port);
 9235}
 9236
 9237void SSLSocketStream::get_local_ip_and_port(std::string &ip,
 9238                                                   int &port) const {
 9239  detail::get_local_ip_and_port(sock_, ip, port);
 9240}
 9241
 9242socket_t SSLSocketStream::socket() const { return sock_; }
 9243
 9244time_t SSLSocketStream::duration() const {
 9245  return std::chrono::duration_cast<std::chrono::milliseconds>(
 9246             std::chrono::steady_clock::now() - start_time_)
 9247      .count();
 9248}
 9249
 9250} // namespace detail
 9251
 9252// SSL HTTP server implementation
 9253SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
 9254                            const char *client_ca_cert_file_path,
 9255                            const char *client_ca_cert_dir_path,
 9256                            const char *private_key_password) {
 9257  ctx_ = SSL_CTX_new(TLS_server_method());
 9258
 9259  if (ctx_) {
 9260    SSL_CTX_set_options(ctx_,
 9261                        SSL_OP_NO_COMPRESSION |
 9262                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
 9263
 9264    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
 9265
 9266    if (private_key_password != nullptr && (private_key_password[0] != '\0')) {
 9267      SSL_CTX_set_default_passwd_cb_userdata(
 9268          ctx_,
 9269          reinterpret_cast<void *>(const_cast<char *>(private_key_password)));
 9270    }
 9271
 9272    if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
 9273        SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
 9274            1 ||
 9275        SSL_CTX_check_private_key(ctx_) != 1) {
 9276      last_ssl_error_ = static_cast<int>(ERR_get_error());
 9277      SSL_CTX_free(ctx_);
 9278      ctx_ = nullptr;
 9279    } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
 9280      SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
 9281                                    client_ca_cert_dir_path);
 9282
 9283      // Set client CA list to be sent to clients during TLS handshake
 9284      if (client_ca_cert_file_path) {
 9285        auto ca_list = SSL_load_client_CA_file(client_ca_cert_file_path);
 9286        if (ca_list != nullptr) {
 9287          SSL_CTX_set_client_CA_list(ctx_, ca_list);
 9288        } else {
 9289          // Failed to load client CA list, but we continue since
 9290          // SSL_CTX_load_verify_locations already succeeded and
 9291          // certificate verification will still work
 9292          last_ssl_error_ = static_cast<int>(ERR_get_error());
 9293        }
 9294      }
 9295
 9296      SSL_CTX_set_verify(
 9297          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
 9298    }
 9299  }
 9300}
 9301
 9302SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
 9303                            X509_STORE *client_ca_cert_store) {
 9304  ctx_ = SSL_CTX_new(TLS_server_method());
 9305
 9306  if (ctx_) {
 9307    SSL_CTX_set_options(ctx_,
 9308                        SSL_OP_NO_COMPRESSION |
 9309                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
 9310
 9311    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
 9312
 9313    if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
 9314        SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
 9315      SSL_CTX_free(ctx_);
 9316      ctx_ = nullptr;
 9317    } else if (client_ca_cert_store) {
 9318      SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
 9319
 9320      // Extract CA names from the store and set them as the client CA list
 9321      auto ca_list = extract_ca_names_from_x509_store(client_ca_cert_store);
 9322      if (ca_list) {
 9323        SSL_CTX_set_client_CA_list(ctx_, ca_list);
 9324      } else {
 9325        // Failed to extract CA names, record the error
 9326        last_ssl_error_ = static_cast<int>(ERR_get_error());
 9327      }
 9328
 9329      SSL_CTX_set_verify(
 9330          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
 9331    }
 9332  }
 9333}
 9334
 9335SSLServer::SSLServer(
 9336    const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
 9337  ctx_ = SSL_CTX_new(TLS_method());
 9338  if (ctx_) {
 9339    if (!setup_ssl_ctx_callback(*ctx_)) {
 9340      SSL_CTX_free(ctx_);
 9341      ctx_ = nullptr;
 9342    }
 9343  }
 9344}
 9345
 9346SSLServer::~SSLServer() {
 9347  if (ctx_) { SSL_CTX_free(ctx_); }
 9348}
 9349
 9350bool SSLServer::is_valid() const { return ctx_; }
 9351
 9352SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
 9353
 9354void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
 9355                                    X509_STORE *client_ca_cert_store) {
 9356
 9357  std::lock_guard<std::mutex> guard(ctx_mutex_);
 9358
 9359  SSL_CTX_use_certificate(ctx_, cert);
 9360  SSL_CTX_use_PrivateKey(ctx_, private_key);
 9361
 9362  if (client_ca_cert_store != nullptr) {
 9363    SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
 9364  }
 9365}
 9366
 9367bool SSLServer::process_and_close_socket(socket_t sock) {
 9368  auto ssl = detail::ssl_new(
 9369      sock, ctx_, ctx_mutex_,
 9370      [&](SSL *ssl2) {
 9371        return detail::ssl_connect_or_accept_nonblocking(
 9372            sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_,
 9373            &last_ssl_error_);
 9374      },
 9375      [](SSL * /*ssl2*/) { return true; });
 9376
 9377  auto ret = false;
 9378  if (ssl) {
 9379    std::string remote_addr;
 9380    int remote_port = 0;
 9381    detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
 9382
 9383    std::string local_addr;
 9384    int local_port = 0;
 9385    detail::get_local_ip_and_port(sock, local_addr, local_port);
 9386
 9387    ret = detail::process_server_socket_ssl(
 9388        svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
 9389        read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
 9390        write_timeout_usec_,
 9391        [&](Stream &strm, bool close_connection, bool &connection_closed) {
 9392          return process_request(strm, remote_addr, remote_port, local_addr,
 9393                                 local_port, close_connection,
 9394                                 connection_closed,
 9395                                 [&](Request &req) { req.ssl = ssl; });
 9396        });
 9397
 9398    // Shutdown gracefully if the result seemed successful, non-gracefully if
 9399    // the connection appeared to be closed.
 9400    const bool shutdown_gracefully = ret;
 9401    detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
 9402  }
 9403
 9404  detail::shutdown_socket(sock);
 9405  detail::close_socket(sock);
 9406  return ret;
 9407}
 9408
 9409STACK_OF(X509_NAME) * SSLServer::extract_ca_names_from_x509_store(
 9410                                 X509_STORE *store) {
 9411  if (!store) { return nullptr; }
 9412
 9413  auto ca_list = sk_X509_NAME_new_null();
 9414  if (!ca_list) { return nullptr; }
 9415
 9416  // Get all objects from the store
 9417  auto objs = X509_STORE_get0_objects(store);
 9418  if (!objs) {
 9419    sk_X509_NAME_free(ca_list);
 9420    return nullptr;
 9421  }
 9422
 9423  // Iterate through objects and extract certificate subject names
 9424  for (int i = 0; i < sk_X509_OBJECT_num(objs); i++) {
 9425    auto obj = sk_X509_OBJECT_value(objs, i);
 9426    if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
 9427      auto cert = X509_OBJECT_get0_X509(obj);
 9428      if (cert) {
 9429        auto subject = X509_get_subject_name(cert);
 9430        if (subject) {
 9431          auto name_dup = X509_NAME_dup(subject);
 9432          if (name_dup) { sk_X509_NAME_push(ca_list, name_dup); }
 9433        }
 9434      }
 9435    }
 9436  }
 9437
 9438  // If no names were extracted, free the list and return nullptr
 9439  if (sk_X509_NAME_num(ca_list) == 0) {
 9440    sk_X509_NAME_free(ca_list);
 9441    return nullptr;
 9442  }
 9443
 9444  return ca_list;
 9445}
 9446
 9447// SSL HTTP client implementation
 9448SSLClient::SSLClient(const std::string &host)
 9449    : SSLClient(host, 443, std::string(), std::string()) {}
 9450
 9451SSLClient::SSLClient(const std::string &host, int port)
 9452    : SSLClient(host, port, std::string(), std::string()) {}
 9453
 9454SSLClient::SSLClient(const std::string &host, int port,
 9455                            const std::string &client_cert_path,
 9456                            const std::string &client_key_path,
 9457                            const std::string &private_key_password)
 9458    : ClientImpl(host, port, client_cert_path, client_key_path) {
 9459  ctx_ = SSL_CTX_new(TLS_client_method());
 9460
 9461  SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
 9462
 9463  detail::split(&host_[0], &host_[host_.size()], '.',
 9464                [&](const char *b, const char *e) {
 9465                  host_components_.emplace_back(b, e);
 9466                });
 9467
 9468  if (!client_cert_path.empty() && !client_key_path.empty()) {
 9469    if (!private_key_password.empty()) {
 9470      SSL_CTX_set_default_passwd_cb_userdata(
 9471          ctx_, reinterpret_cast<void *>(
 9472                    const_cast<char *>(private_key_password.c_str())));
 9473    }
 9474
 9475    if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
 9476                                     SSL_FILETYPE_PEM) != 1 ||
 9477        SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
 9478                                    SSL_FILETYPE_PEM) != 1) {
 9479      last_openssl_error_ = ERR_get_error();
 9480      SSL_CTX_free(ctx_);
 9481      ctx_ = nullptr;
 9482    }
 9483  }
 9484}
 9485
 9486SSLClient::SSLClient(const std::string &host, int port,
 9487                            X509 *client_cert, EVP_PKEY *client_key,
 9488                            const std::string &private_key_password)
 9489    : ClientImpl(host, port) {
 9490  ctx_ = SSL_CTX_new(TLS_client_method());
 9491
 9492  detail::split(&host_[0], &host_[host_.size()], '.',
 9493                [&](const char *b, const char *e) {
 9494                  host_components_.emplace_back(b, e);
 9495                });
 9496
 9497  if (client_cert != nullptr && client_key != nullptr) {
 9498    if (!private_key_password.empty()) {
 9499      SSL_CTX_set_default_passwd_cb_userdata(
 9500          ctx_, reinterpret_cast<void *>(
 9501                    const_cast<char *>(private_key_password.c_str())));
 9502    }
 9503
 9504    if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
 9505        SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
 9506      last_openssl_error_ = ERR_get_error();
 9507      SSL_CTX_free(ctx_);
 9508      ctx_ = nullptr;
 9509    }
 9510  }
 9511}
 9512
 9513SSLClient::~SSLClient() {
 9514  if (ctx_) { SSL_CTX_free(ctx_); }
 9515  // Make sure to shut down SSL since shutdown_ssl will resolve to the
 9516  // base function rather than the derived function once we get to the
 9517  // base class destructor, and won't free the SSL (causing a leak).
 9518  shutdown_ssl_impl(socket_, true);
 9519}
 9520
 9521bool SSLClient::is_valid() const { return ctx_; }
 9522
 9523void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
 9524  if (ca_cert_store) {
 9525    if (ctx_) {
 9526      if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
 9527        // Free memory allocated for old cert and use new store
 9528        // `ca_cert_store`
 9529        SSL_CTX_set_cert_store(ctx_, ca_cert_store);
 9530        ca_cert_store_ = ca_cert_store;
 9531      }
 9532    } else {
 9533      X509_STORE_free(ca_cert_store);
 9534    }
 9535  }
 9536}
 9537
 9538void SSLClient::load_ca_cert_store(const char *ca_cert,
 9539                                          std::size_t size) {
 9540  set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
 9541}
 9542
 9543long SSLClient::get_openssl_verify_result() const {
 9544  return verify_result_;
 9545}
 9546
 9547SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
 9548
 9549bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
 9550  if (!is_valid()) {
 9551    error = Error::SSLConnection;
 9552    return false;
 9553  }
 9554  return ClientImpl::create_and_connect_socket(socket, error);
 9555}
 9556
 9557// Assumes that socket_mutex_ is locked and that there are no requests in
 9558// flight
 9559bool SSLClient::connect_with_proxy(
 9560    Socket &socket,
 9561    std::chrono::time_point<std::chrono::steady_clock> start_time,
 9562    Response &res, bool &success, Error &error) {
 9563  success = true;
 9564  Response proxy_res;
 9565  if (!detail::process_client_socket(
 9566          socket.sock, read_timeout_sec_, read_timeout_usec_,
 9567          write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
 9568          start_time, [&](Stream &strm) {
 9569            Request req2;
 9570            req2.method = "CONNECT";
 9571            req2.path =
 9572                detail::make_host_and_port_string_always_port(host_, port_);
 9573            if (max_timeout_msec_ > 0) {
 9574              req2.start_time_ = std::chrono::steady_clock::now();
 9575            }
 9576            return process_request(strm, req2, proxy_res, false, error);
 9577          })) {
 9578    // Thread-safe to close everything because we are assuming there are no
 9579    // requests in flight
 9580    shutdown_ssl(socket, true);
 9581    shutdown_socket(socket);
 9582    close_socket(socket);
 9583    success = false;
 9584    return false;
 9585  }
 9586
 9587  if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
 9588    if (!proxy_digest_auth_username_.empty() &&
 9589        !proxy_digest_auth_password_.empty()) {
 9590      std::map<std::string, std::string> auth;
 9591      if (detail::parse_www_authenticate(proxy_res, auth, true)) {
 9592        // Close the current socket and create a new one for the authenticated
 9593        // request
 9594        shutdown_ssl(socket, true);
 9595        shutdown_socket(socket);
 9596        close_socket(socket);
 9597
 9598        // Create a new socket for the authenticated CONNECT request
 9599        if (!ensure_socket_connection(socket, error)) {
 9600          success = false;
 9601          output_error_log(error, nullptr);
 9602          return false;
 9603        }
 9604
 9605        proxy_res = Response();
 9606        if (!detail::process_client_socket(
 9607                socket.sock, read_timeout_sec_, read_timeout_usec_,
 9608                write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
 9609                start_time, [&](Stream &strm) {
 9610                  Request req3;
 9611                  req3.method = "CONNECT";
 9612                  req3.path = detail::make_host_and_port_string_always_port(
 9613                      host_, port_);
 9614                  req3.headers.insert(detail::make_digest_authentication_header(
 9615                      req3, auth, 1, detail::random_string(10),
 9616                      proxy_digest_auth_username_, proxy_digest_auth_password_,
 9617                      true));
 9618                  if (max_timeout_msec_ > 0) {
 9619                    req3.start_time_ = std::chrono::steady_clock::now();
 9620                  }
 9621                  return process_request(strm, req3, proxy_res, false, error);
 9622                })) {
 9623          // Thread-safe to close everything because we are assuming there are
 9624          // no requests in flight
 9625          shutdown_ssl(socket, true);
 9626          shutdown_socket(socket);
 9627          close_socket(socket);
 9628          success = false;
 9629          return false;
 9630        }
 9631      }
 9632    }
 9633  }
 9634
 9635  // If status code is not 200, proxy request is failed.
 9636  // Set error to ProxyConnection and return proxy response
 9637  // as the response of the request
 9638  if (proxy_res.status != StatusCode::OK_200) {
 9639    error = Error::ProxyConnection;
 9640    output_error_log(error, nullptr);
 9641    res = std::move(proxy_res);
 9642    // Thread-safe to close everything because we are assuming there are
 9643    // no requests in flight
 9644    shutdown_ssl(socket, true);
 9645    shutdown_socket(socket);
 9646    close_socket(socket);
 9647    return false;
 9648  }
 9649
 9650  return true;
 9651}
 9652
 9653bool SSLClient::load_certs() {
 9654  auto ret = true;
 9655
 9656  std::call_once(initialize_cert_, [&]() {
 9657    std::lock_guard<std::mutex> guard(ctx_mutex_);
 9658    if (!ca_cert_file_path_.empty()) {
 9659      if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
 9660                                         nullptr)) {
 9661        last_openssl_error_ = ERR_get_error();
 9662        ret = false;
 9663      }
 9664    } else if (!ca_cert_dir_path_.empty()) {
 9665      if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
 9666                                         ca_cert_dir_path_.c_str())) {
 9667        last_openssl_error_ = ERR_get_error();
 9668        ret = false;
 9669      }
 9670    } else if (!ca_cert_store_) {
 9671      auto loaded = false;
 9672#ifdef _WIN32
 9673      loaded =
 9674          detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
 9675#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
 9676      loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
 9677#endif // _WIN32
 9678      if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
 9679    }
 9680  });
 9681
 9682  return ret;
 9683}
 9684
 9685bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
 9686  auto ssl = detail::ssl_new(
 9687      socket.sock, ctx_, ctx_mutex_,
 9688      [&](SSL *ssl2) {
 9689        if (server_certificate_verification_) {
 9690          if (!load_certs()) {
 9691            error = Error::SSLLoadingCerts;
 9692            output_error_log(error, nullptr);
 9693            return false;
 9694          }
 9695          SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
 9696        }
 9697
 9698        if (!detail::ssl_connect_or_accept_nonblocking(
 9699                socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
 9700                connection_timeout_usec_, &last_ssl_error_)) {
 9701          error = Error::SSLConnection;
 9702          output_error_log(error, nullptr);
 9703          return false;
 9704        }
 9705
 9706        if (server_certificate_verification_) {
 9707          auto verification_status = SSLVerifierResponse::NoDecisionMade;
 9708
 9709          if (server_certificate_verifier_) {
 9710            verification_status = server_certificate_verifier_(ssl2);
 9711          }
 9712
 9713          if (verification_status == SSLVerifierResponse::CertificateRejected) {
 9714            last_openssl_error_ = ERR_get_error();
 9715            error = Error::SSLServerVerification;
 9716            output_error_log(error, nullptr);
 9717            return false;
 9718          }
 9719
 9720          if (verification_status == SSLVerifierResponse::NoDecisionMade) {
 9721            verify_result_ = SSL_get_verify_result(ssl2);
 9722
 9723            if (verify_result_ != X509_V_OK) {
 9724              last_openssl_error_ = static_cast<unsigned long>(verify_result_);
 9725              error = Error::SSLServerVerification;
 9726              output_error_log(error, nullptr);
 9727              return false;
 9728            }
 9729
 9730            auto server_cert = SSL_get1_peer_certificate(ssl2);
 9731            auto se = detail::scope_exit([&] { X509_free(server_cert); });
 9732
 9733            if (server_cert == nullptr) {
 9734              last_openssl_error_ = ERR_get_error();
 9735              error = Error::SSLServerVerification;
 9736              output_error_log(error, nullptr);
 9737              return false;
 9738            }
 9739
 9740            if (server_hostname_verification_) {
 9741              if (!verify_host(server_cert)) {
 9742                last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
 9743                error = Error::SSLServerHostnameVerification;
 9744                output_error_log(error, nullptr);
 9745                return false;
 9746              }
 9747            }
 9748          }
 9749        }
 9750
 9751        return true;
 9752      },
 9753      [&](SSL *ssl2) {
 9754        // Set SNI only if host is not IP address
 9755        if (!detail::is_ip_address(host_)) {
 9756#if defined(OPENSSL_IS_BORINGSSL)
 9757          SSL_set_tlsext_host_name(ssl2, host_.c_str());
 9758#else
 9759          // NOTE: Direct call instead of using the OpenSSL macro to suppress
 9760          // -Wold-style-cast warning
 9761          SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME,
 9762                   TLSEXT_NAMETYPE_host_name,
 9763                   static_cast<void *>(const_cast<char *>(host_.c_str())));
 9764#endif
 9765        }
 9766        return true;
 9767      });
 9768
 9769  if (ssl) {
 9770    socket.ssl = ssl;
 9771    return true;
 9772  }
 9773
 9774  if (ctx_ == nullptr) {
 9775    error = Error::SSLConnection;
 9776    last_openssl_error_ = ERR_get_error();
 9777  }
 9778
 9779  shutdown_socket(socket);
 9780  close_socket(socket);
 9781  return false;
 9782}
 9783
 9784void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
 9785  shutdown_ssl_impl(socket, shutdown_gracefully);
 9786}
 9787
 9788void SSLClient::shutdown_ssl_impl(Socket &socket,
 9789                                         bool shutdown_gracefully) {
 9790  if (socket.sock == INVALID_SOCKET) {
 9791    assert(socket.ssl == nullptr);
 9792    return;
 9793  }
 9794  if (socket.ssl) {
 9795    detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
 9796                       shutdown_gracefully);
 9797    socket.ssl = nullptr;
 9798  }
 9799  assert(socket.ssl == nullptr);
 9800}
 9801
 9802bool SSLClient::process_socket(
 9803    const Socket &socket,
 9804    std::chrono::time_point<std::chrono::steady_clock> start_time,
 9805    std::function<bool(Stream &strm)> callback) {
 9806  assert(socket.ssl);
 9807  return detail::process_client_socket_ssl(
 9808      socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
 9809      write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
 9810      std::move(callback));
 9811}
 9812
 9813bool SSLClient::is_ssl() const { return true; }
 9814
 9815bool SSLClient::verify_host(X509 *server_cert) const {
 9816  /* Quote from RFC2818 section 3.1 "Server Identity"
 9817
 9818     If a subjectAltName extension of type dNSName is present, that MUST
 9819     be used as the identity. Otherwise, the (most specific) Common Name
 9820     field in the Subject field of the certificate MUST be used. Although
 9821     the use of the Common Name is existing practice, it is deprecated and
 9822     Certification Authorities are encouraged to use the dNSName instead.
 9823
 9824     Matching is performed using the matching rules specified by
 9825     [RFC2459].  If more than one identity of a given type is present in
 9826     the certificate (e.g., more than one dNSName name, a match in any one
 9827     of the set is considered acceptable.) Names may contain the wildcard
 9828     character * which is considered to match any single domain name
 9829     component or component fragment. E.g., *.a.com matches foo.a.com but
 9830     not bar.foo.a.com. f*.com matches foo.com but not bar.com.
 9831
 9832     In some cases, the URI is specified as an IP address rather than a
 9833     hostname. In this case, the iPAddress subjectAltName must be present
 9834     in the certificate and must exactly match the IP in the URI.
 9835
 9836  */
 9837  return verify_host_with_subject_alt_name(server_cert) ||
 9838         verify_host_with_common_name(server_cert);
 9839}
 9840
 9841bool
 9842SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
 9843  auto ret = false;
 9844
 9845  auto type = GEN_DNS;
 9846
 9847  struct in6_addr addr6 = {};
 9848  struct in_addr addr = {};
 9849  size_t addr_len = 0;
 9850
 9851#ifndef __MINGW32__
 9852  if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
 9853    type = GEN_IPADD;
 9854    addr_len = sizeof(struct in6_addr);
 9855  } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
 9856    type = GEN_IPADD;
 9857    addr_len = sizeof(struct in_addr);
 9858  }
 9859#endif
 9860
 9861  auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
 9862      X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
 9863
 9864  if (alt_names) {
 9865    auto dsn_matched = false;
 9866    auto ip_matched = false;
 9867
 9868    auto count = sk_GENERAL_NAME_num(alt_names);
 9869
 9870    for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
 9871      auto val = sk_GENERAL_NAME_value(alt_names, i);
 9872      if (!val || val->type != type) { continue; }
 9873
 9874      auto name =
 9875          reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));
 9876      if (name == nullptr) { continue; }
 9877
 9878      auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
 9879
 9880      switch (type) {
 9881      case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
 9882
 9883      case GEN_IPADD:
 9884        if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {
 9885          ip_matched = true;
 9886        }
 9887        break;
 9888      }
 9889    }
 9890
 9891    if (dsn_matched || ip_matched) { ret = true; }
 9892  }
 9893
 9894  GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(
 9895      reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));
 9896  return ret;
 9897}
 9898
 9899bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
 9900  const auto subject_name = X509_get_subject_name(server_cert);
 9901
 9902  if (subject_name != nullptr) {
 9903    char name[BUFSIZ];
 9904    auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
 9905                                              name, sizeof(name));
 9906
 9907    if (name_len != -1) {
 9908      return check_host_name(name, static_cast<size_t>(name_len));
 9909    }
 9910  }
 9911
 9912  return false;
 9913}
 9914
 9915bool SSLClient::check_host_name(const char *pattern,
 9916                                       size_t pattern_len) const {
 9917  // Exact match (case-insensitive)
 9918  if (host_.size() == pattern_len &&
 9919      detail::case_ignore::equal(host_, std::string(pattern, pattern_len))) {
 9920    return true;
 9921  }
 9922
 9923  // Wildcard match
 9924  // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
 9925  std::vector<std::string> pattern_components;
 9926  detail::split(&pattern[0], &pattern[pattern_len], '.',
 9927                [&](const char *b, const char *e) {
 9928                  pattern_components.emplace_back(b, e);
 9929                });
 9930
 9931  if (host_components_.size() != pattern_components.size()) { return false; }
 9932
 9933  auto itr = pattern_components.begin();
 9934  for (const auto &h : host_components_) {
 9935    auto &p = *itr;
 9936    if (!httplib::detail::case_ignore::equal(p, h) && p != "*") {
 9937      bool partial_match = false;
 9938      if (!p.empty() && p[p.size() - 1] == '*') {
 9939        const auto prefix_length = p.size() - 1;
 9940        if (prefix_length == 0) {
 9941          partial_match = true;
 9942        } else if (h.size() >= prefix_length) {
 9943          partial_match =
 9944              std::equal(p.begin(),
 9945                         p.begin() + static_cast<std::string::difference_type>(
 9946                                         prefix_length),
 9947                         h.begin(), [](const char ca, const char cb) {
 9948                           return httplib::detail::case_ignore::to_lower(ca) ==
 9949                                  httplib::detail::case_ignore::to_lower(cb);
 9950                         });
 9951        }
 9952      }
 9953      if (!partial_match) { return false; }
 9954    }
 9955    ++itr;
 9956  }
 9957
 9958  return true;
 9959}
 9960#endif
 9961
 9962// Universal client implementation
 9963Client::Client(const std::string &scheme_host_port)
 9964    : Client(scheme_host_port, std::string(), std::string()) {}
 9965
 9966Client::Client(const std::string &scheme_host_port,
 9967                      const std::string &client_cert_path,
 9968                      const std::string &client_key_path) {
 9969  const static std::regex re(
 9970      R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
 9971
 9972  std::smatch m;
 9973  if (std::regex_match(scheme_host_port, m, re)) {
 9974    auto scheme = m[1].str();
 9975
 9976#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 9977    if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
 9978#else
 9979    if (!scheme.empty() && scheme != "http") {
 9980#endif
 9981#ifndef CPPHTTPLIB_NO_EXCEPTIONS
 9982      std::string msg = "'" + scheme + "' scheme is not supported.";
 9983      throw std::invalid_argument(msg);
 9984#endif
 9985      return;
 9986    }
 9987
 9988    auto is_ssl = scheme == "https";
 9989
 9990    auto host = m[2].str();
 9991    if (host.empty()) { host = m[3].str(); }
 9992
 9993    auto port_str = m[4].str();
 9994    auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
 9995
 9996    if (is_ssl) {
 9997#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 9998      cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
 9999                                            client_key_path);
10000      is_ssl_ = is_ssl;
10001#endif
10002    } else {
10003      cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
10004                                             client_key_path);
10005    }
10006  } else {
10007    // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)
10008    // if port param below changes.
10009    cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
10010                                           client_cert_path, client_key_path);
10011  }
10012} // namespace detail
10013
10014Client::Client(const std::string &host, int port)
10015    : cli_(detail::make_unique<ClientImpl>(host, port)) {}
10016
10017Client::Client(const std::string &host, int port,
10018                      const std::string &client_cert_path,
10019                      const std::string &client_key_path)
10020    : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
10021                                           client_key_path)) {}
10022
10023Client::~Client() = default;
10024
10025bool Client::is_valid() const {
10026  return cli_ != nullptr && cli_->is_valid();
10027}
10028
10029Result Client::Get(const std::string &path, DownloadProgress progress) {
10030  return cli_->Get(path, std::move(progress));
10031}
10032Result Client::Get(const std::string &path, const Headers &headers,
10033                          DownloadProgress progress) {
10034  return cli_->Get(path, headers, std::move(progress));
10035}
10036Result Client::Get(const std::string &path,
10037                          ContentReceiver content_receiver,
10038                          DownloadProgress progress) {
10039  return cli_->Get(path, std::move(content_receiver), std::move(progress));
10040}
10041Result Client::Get(const std::string &path, const Headers &headers,
10042                          ContentReceiver content_receiver,
10043                          DownloadProgress progress) {
10044  return cli_->Get(path, headers, std::move(content_receiver),
10045                   std::move(progress));
10046}
10047Result Client::Get(const std::string &path,
10048                          ResponseHandler response_handler,
10049                          ContentReceiver content_receiver,
10050                          DownloadProgress progress) {
10051  return cli_->Get(path, std::move(response_handler),
10052                   std::move(content_receiver), std::move(progress));
10053}
10054Result Client::Get(const std::string &path, const Headers &headers,
10055                          ResponseHandler response_handler,
10056                          ContentReceiver content_receiver,
10057                          DownloadProgress progress) {
10058  return cli_->Get(path, headers, std::move(response_handler),
10059                   std::move(content_receiver), std::move(progress));
10060}
10061Result Client::Get(const std::string &path, const Params &params,
10062                          const Headers &headers, DownloadProgress progress) {
10063  return cli_->Get(path, params, headers, std::move(progress));
10064}
10065Result Client::Get(const std::string &path, const Params &params,
10066                          const Headers &headers,
10067                          ContentReceiver content_receiver,
10068                          DownloadProgress progress) {
10069  return cli_->Get(path, params, headers, std::move(content_receiver),
10070                   std::move(progress));
10071}
10072Result Client::Get(const std::string &path, const Params &params,
10073                          const Headers &headers,
10074                          ResponseHandler response_handler,
10075                          ContentReceiver content_receiver,
10076                          DownloadProgress progress) {
10077  return cli_->Get(path, params, headers, std::move(response_handler),
10078                   std::move(content_receiver), std::move(progress));
10079}
10080
10081Result Client::Head(const std::string &path) { return cli_->Head(path); }
10082Result Client::Head(const std::string &path, const Headers &headers) {
10083  return cli_->Head(path, headers);
10084}
10085
10086Result Client::Post(const std::string &path) { return cli_->Post(path); }
10087Result Client::Post(const std::string &path, const Headers &headers) {
10088  return cli_->Post(path, headers);
10089}
10090Result Client::Post(const std::string &path, const char *body,
10091                           size_t content_length,
10092                           const std::string &content_type,
10093                           UploadProgress progress) {
10094  return cli_->Post(path, body, content_length, content_type, progress);
10095}
10096Result Client::Post(const std::string &path, const Headers &headers,
10097                           const char *body, size_t content_length,
10098                           const std::string &content_type,
10099                           UploadProgress progress) {
10100  return cli_->Post(path, headers, body, content_length, content_type,
10101                    progress);
10102}
10103Result Client::Post(const std::string &path, const std::string &body,
10104                           const std::string &content_type,
10105                           UploadProgress progress) {
10106  return cli_->Post(path, body, content_type, progress);
10107}
10108Result Client::Post(const std::string &path, const Headers &headers,
10109                           const std::string &body,
10110                           const std::string &content_type,
10111                           UploadProgress progress) {
10112  return cli_->Post(path, headers, body, content_type, progress);
10113}
10114Result Client::Post(const std::string &path, size_t content_length,
10115                           ContentProvider content_provider,
10116                           const std::string &content_type,
10117                           UploadProgress progress) {
10118  return cli_->Post(path, content_length, std::move(content_provider),
10119                    content_type, progress);
10120}
10121Result Client::Post(const std::string &path, size_t content_length,
10122                           ContentProvider content_provider,
10123                           const std::string &content_type,
10124                           ContentReceiver content_receiver,
10125                           UploadProgress progress) {
10126  return cli_->Post(path, content_length, std::move(content_provider),
10127                    content_type, std::move(content_receiver), progress);
10128}
10129Result Client::Post(const std::string &path,
10130                           ContentProviderWithoutLength content_provider,
10131                           const std::string &content_type,
10132                           UploadProgress progress) {
10133  return cli_->Post(path, std::move(content_provider), content_type, progress);
10134}
10135Result Client::Post(const std::string &path,
10136                           ContentProviderWithoutLength content_provider,
10137                           const std::string &content_type,
10138                           ContentReceiver content_receiver,
10139                           UploadProgress progress) {
10140  return cli_->Post(path, std::move(content_provider), content_type,
10141                    std::move(content_receiver), progress);
10142}
10143Result Client::Post(const std::string &path, const Headers &headers,
10144                           size_t content_length,
10145                           ContentProvider content_provider,
10146                           const std::string &content_type,
10147                           UploadProgress progress) {
10148  return cli_->Post(path, headers, content_length, std::move(content_provider),
10149                    content_type, progress);
10150}
10151Result Client::Post(const std::string &path, const Headers &headers,
10152                           size_t content_length,
10153                           ContentProvider content_provider,
10154                           const std::string &content_type,
10155                           ContentReceiver content_receiver,
10156                           DownloadProgress progress) {
10157  return cli_->Post(path, headers, content_length, std::move(content_provider),
10158                    content_type, std::move(content_receiver), progress);
10159}
10160Result Client::Post(const std::string &path, const Headers &headers,
10161                           ContentProviderWithoutLength content_provider,
10162                           const std::string &content_type,
10163                           UploadProgress progress) {
10164  return cli_->Post(path, headers, std::move(content_provider), content_type,
10165                    progress);
10166}
10167Result Client::Post(const std::string &path, const Headers &headers,
10168                           ContentProviderWithoutLength content_provider,
10169                           const std::string &content_type,
10170                           ContentReceiver content_receiver,
10171                           DownloadProgress progress) {
10172  return cli_->Post(path, headers, std::move(content_provider), content_type,
10173                    std::move(content_receiver), progress);
10174}
10175Result Client::Post(const std::string &path, const Params &params) {
10176  return cli_->Post(path, params);
10177}
10178Result Client::Post(const std::string &path, const Headers &headers,
10179                           const Params &params) {
10180  return cli_->Post(path, headers, params);
10181}
10182Result Client::Post(const std::string &path,
10183                           const UploadFormDataItems &items,
10184                           UploadProgress progress) {
10185  return cli_->Post(path, items, progress);
10186}
10187Result Client::Post(const std::string &path, const Headers &headers,
10188                           const UploadFormDataItems &items,
10189                           UploadProgress progress) {
10190  return cli_->Post(path, headers, items, progress);
10191}
10192Result Client::Post(const std::string &path, const Headers &headers,
10193                           const UploadFormDataItems &items,
10194                           const std::string &boundary,
10195                           UploadProgress progress) {
10196  return cli_->Post(path, headers, items, boundary, progress);
10197}
10198Result Client::Post(const std::string &path, const Headers &headers,
10199                           const UploadFormDataItems &items,
10200                           const FormDataProviderItems &provider_items,
10201                           UploadProgress progress) {
10202  return cli_->Post(path, headers, items, provider_items, progress);
10203}
10204Result Client::Post(const std::string &path, const Headers &headers,
10205                           const std::string &body,
10206                           const std::string &content_type,
10207                           ContentReceiver content_receiver,
10208                           DownloadProgress progress) {
10209  return cli_->Post(path, headers, body, content_type,
10210                    std::move(content_receiver), progress);
10211}
10212
10213Result Client::Put(const std::string &path) { return cli_->Put(path); }
10214Result Client::Put(const std::string &path, const Headers &headers) {
10215  return cli_->Put(path, headers);
10216}
10217Result Client::Put(const std::string &path, const char *body,
10218                          size_t content_length,
10219                          const std::string &content_type,
10220                          UploadProgress progress) {
10221  return cli_->Put(path, body, content_length, content_type, progress);
10222}
10223Result Client::Put(const std::string &path, const Headers &headers,
10224                          const char *body, size_t content_length,
10225                          const std::string &content_type,
10226                          UploadProgress progress) {
10227  return cli_->Put(path, headers, body, content_length, content_type, progress);
10228}
10229Result Client::Put(const std::string &path, const std::string &body,
10230                          const std::string &content_type,
10231                          UploadProgress progress) {
10232  return cli_->Put(path, body, content_type, progress);
10233}
10234Result Client::Put(const std::string &path, const Headers &headers,
10235                          const std::string &body,
10236                          const std::string &content_type,
10237                          UploadProgress progress) {
10238  return cli_->Put(path, headers, body, content_type, progress);
10239}
10240Result Client::Put(const std::string &path, size_t content_length,
10241                          ContentProvider content_provider,
10242                          const std::string &content_type,
10243                          UploadProgress progress) {
10244  return cli_->Put(path, content_length, std::move(content_provider),
10245                   content_type, progress);
10246}
10247Result Client::Put(const std::string &path, size_t content_length,
10248                          ContentProvider content_provider,
10249                          const std::string &content_type,
10250                          ContentReceiver content_receiver,
10251                          UploadProgress progress) {
10252  return cli_->Put(path, content_length, std::move(content_provider),
10253                   content_type, std::move(content_receiver), progress);
10254}
10255Result Client::Put(const std::string &path,
10256                          ContentProviderWithoutLength content_provider,
10257                          const std::string &content_type,
10258                          UploadProgress progress) {
10259  return cli_->Put(path, std::move(content_provider), content_type, progress);
10260}
10261Result Client::Put(const std::string &path,
10262                          ContentProviderWithoutLength content_provider,
10263                          const std::string &content_type,
10264                          ContentReceiver content_receiver,
10265                          UploadProgress progress) {
10266  return cli_->Put(path, std::move(content_provider), content_type,
10267                   std::move(content_receiver), progress);
10268}
10269Result Client::Put(const std::string &path, const Headers &headers,
10270                          size_t content_length,
10271                          ContentProvider content_provider,
10272                          const std::string &content_type,
10273                          UploadProgress progress) {
10274  return cli_->Put(path, headers, content_length, std::move(content_provider),
10275                   content_type, progress);
10276}
10277Result Client::Put(const std::string &path, const Headers &headers,
10278                          size_t content_length,
10279                          ContentProvider content_provider,
10280                          const std::string &content_type,
10281                          ContentReceiver content_receiver,
10282                          UploadProgress progress) {
10283  return cli_->Put(path, headers, content_length, std::move(content_provider),
10284                   content_type, std::move(content_receiver), progress);
10285}
10286Result Client::Put(const std::string &path, const Headers &headers,
10287                          ContentProviderWithoutLength content_provider,
10288                          const std::string &content_type,
10289                          UploadProgress progress) {
10290  return cli_->Put(path, headers, std::move(content_provider), content_type,
10291                   progress);
10292}
10293Result Client::Put(const std::string &path, const Headers &headers,
10294                          ContentProviderWithoutLength content_provider,
10295                          const std::string &content_type,
10296                          ContentReceiver content_receiver,
10297                          UploadProgress progress) {
10298  return cli_->Put(path, headers, std::move(content_provider), content_type,
10299                   std::move(content_receiver), progress);
10300}
10301Result Client::Put(const std::string &path, const Params &params) {
10302  return cli_->Put(path, params);
10303}
10304Result Client::Put(const std::string &path, const Headers &headers,
10305                          const Params &params) {
10306  return cli_->Put(path, headers, params);
10307}
10308Result Client::Put(const std::string &path,
10309                          const UploadFormDataItems &items,
10310                          UploadProgress progress) {
10311  return cli_->Put(path, items, progress);
10312}
10313Result Client::Put(const std::string &path, const Headers &headers,
10314                          const UploadFormDataItems &items,
10315                          UploadProgress progress) {
10316  return cli_->Put(path, headers, items, progress);
10317}
10318Result Client::Put(const std::string &path, const Headers &headers,
10319                          const UploadFormDataItems &items,
10320                          const std::string &boundary,
10321                          UploadProgress progress) {
10322  return cli_->Put(path, headers, items, boundary, progress);
10323}
10324Result Client::Put(const std::string &path, const Headers &headers,
10325                          const UploadFormDataItems &items,
10326                          const FormDataProviderItems &provider_items,
10327                          UploadProgress progress) {
10328  return cli_->Put(path, headers, items, provider_items, progress);
10329}
10330Result Client::Put(const std::string &path, const Headers &headers,
10331                          const std::string &body,
10332                          const std::string &content_type,
10333                          ContentReceiver content_receiver,
10334                          DownloadProgress progress) {
10335  return cli_->Put(path, headers, body, content_type, content_receiver,
10336                   progress);
10337}
10338
10339Result Client::Patch(const std::string &path) {
10340  return cli_->Patch(path);
10341}
10342Result Client::Patch(const std::string &path, const Headers &headers) {
10343  return cli_->Patch(path, headers);
10344}
10345Result Client::Patch(const std::string &path, const char *body,
10346                            size_t content_length,
10347                            const std::string &content_type,
10348                            UploadProgress progress) {
10349  return cli_->Patch(path, body, content_length, content_type, progress);
10350}
10351Result Client::Patch(const std::string &path, const Headers &headers,
10352                            const char *body, size_t content_length,
10353                            const std::string &content_type,
10354                            UploadProgress progress) {
10355  return cli_->Patch(path, headers, body, content_length, content_type,
10356                     progress);
10357}
10358Result Client::Patch(const std::string &path, const std::string &body,
10359                            const std::string &content_type,
10360                            UploadProgress progress) {
10361  return cli_->Patch(path, body, content_type, progress);
10362}
10363Result Client::Patch(const std::string &path, const Headers &headers,
10364                            const std::string &body,
10365                            const std::string &content_type,
10366                            UploadProgress progress) {
10367  return cli_->Patch(path, headers, body, content_type, progress);
10368}
10369Result Client::Patch(const std::string &path, size_t content_length,
10370                            ContentProvider content_provider,
10371                            const std::string &content_type,
10372                            UploadProgress progress) {
10373  return cli_->Patch(path, content_length, std::move(content_provider),
10374                     content_type, progress);
10375}
10376Result Client::Patch(const std::string &path, size_t content_length,
10377                            ContentProvider content_provider,
10378                            const std::string &content_type,
10379                            ContentReceiver content_receiver,
10380                            UploadProgress progress) {
10381  return cli_->Patch(path, content_length, std::move(content_provider),
10382                     content_type, std::move(content_receiver), progress);
10383}
10384Result Client::Patch(const std::string &path,
10385                            ContentProviderWithoutLength content_provider,
10386                            const std::string &content_type,
10387                            UploadProgress progress) {
10388  return cli_->Patch(path, std::move(content_provider), content_type, progress);
10389}
10390Result Client::Patch(const std::string &path,
10391                            ContentProviderWithoutLength content_provider,
10392                            const std::string &content_type,
10393                            ContentReceiver content_receiver,
10394                            UploadProgress progress) {
10395  return cli_->Patch(path, std::move(content_provider), content_type,
10396                     std::move(content_receiver), progress);
10397}
10398Result Client::Patch(const std::string &path, const Headers &headers,
10399                            size_t content_length,
10400                            ContentProvider content_provider,
10401                            const std::string &content_type,
10402                            UploadProgress progress) {
10403  return cli_->Patch(path, headers, content_length, std::move(content_provider),
10404                     content_type, progress);
10405}
10406Result Client::Patch(const std::string &path, const Headers &headers,
10407                            size_t content_length,
10408                            ContentProvider content_provider,
10409                            const std::string &content_type,
10410                            ContentReceiver content_receiver,
10411                            UploadProgress progress) {
10412  return cli_->Patch(path, headers, content_length, std::move(content_provider),
10413                     content_type, std::move(content_receiver), progress);
10414}
10415Result Client::Patch(const std::string &path, const Headers &headers,
10416                            ContentProviderWithoutLength content_provider,
10417                            const std::string &content_type,
10418                            UploadProgress progress) {
10419  return cli_->Patch(path, headers, std::move(content_provider), content_type,
10420                     progress);
10421}
10422Result Client::Patch(const std::string &path, const Headers &headers,
10423                            ContentProviderWithoutLength content_provider,
10424                            const std::string &content_type,
10425                            ContentReceiver content_receiver,
10426                            UploadProgress progress) {
10427  return cli_->Patch(path, headers, std::move(content_provider), content_type,
10428                     std::move(content_receiver), progress);
10429}
10430Result Client::Patch(const std::string &path, const Params &params) {
10431  return cli_->Patch(path, params);
10432}
10433Result Client::Patch(const std::string &path, const Headers &headers,
10434                            const Params &params) {
10435  return cli_->Patch(path, headers, params);
10436}
10437Result Client::Patch(const std::string &path,
10438                            const UploadFormDataItems &items,
10439                            UploadProgress progress) {
10440  return cli_->Patch(path, items, progress);
10441}
10442Result Client::Patch(const std::string &path, const Headers &headers,
10443                            const UploadFormDataItems &items,
10444                            UploadProgress progress) {
10445  return cli_->Patch(path, headers, items, progress);
10446}
10447Result Client::Patch(const std::string &path, const Headers &headers,
10448                            const UploadFormDataItems &items,
10449                            const std::string &boundary,
10450                            UploadProgress progress) {
10451  return cli_->Patch(path, headers, items, boundary, progress);
10452}
10453Result Client::Patch(const std::string &path, const Headers &headers,
10454                            const UploadFormDataItems &items,
10455                            const FormDataProviderItems &provider_items,
10456                            UploadProgress progress) {
10457  return cli_->Patch(path, headers, items, provider_items, progress);
10458}
10459Result Client::Patch(const std::string &path, const Headers &headers,
10460                            const std::string &body,
10461                            const std::string &content_type,
10462                            ContentReceiver content_receiver,
10463                            DownloadProgress progress) {
10464  return cli_->Patch(path, headers, body, content_type, content_receiver,
10465                     progress);
10466}
10467
10468Result Client::Delete(const std::string &path,
10469                             DownloadProgress progress) {
10470  return cli_->Delete(path, progress);
10471}
10472Result Client::Delete(const std::string &path, const Headers &headers,
10473                             DownloadProgress progress) {
10474  return cli_->Delete(path, headers, progress);
10475}
10476Result Client::Delete(const std::string &path, const char *body,
10477                             size_t content_length,
10478                             const std::string &content_type,
10479                             DownloadProgress progress) {
10480  return cli_->Delete(path, body, content_length, content_type, progress);
10481}
10482Result Client::Delete(const std::string &path, const Headers &headers,
10483                             const char *body, size_t content_length,
10484                             const std::string &content_type,
10485                             DownloadProgress progress) {
10486  return cli_->Delete(path, headers, body, content_length, content_type,
10487                      progress);
10488}
10489Result Client::Delete(const std::string &path, const std::string &body,
10490                             const std::string &content_type,
10491                             DownloadProgress progress) {
10492  return cli_->Delete(path, body, content_type, progress);
10493}
10494Result Client::Delete(const std::string &path, const Headers &headers,
10495                             const std::string &body,
10496                             const std::string &content_type,
10497                             DownloadProgress progress) {
10498  return cli_->Delete(path, headers, body, content_type, progress);
10499}
10500Result Client::Delete(const std::string &path, const Params &params,
10501                             DownloadProgress progress) {
10502  return cli_->Delete(path, params, progress);
10503}
10504Result Client::Delete(const std::string &path, const Headers &headers,
10505                             const Params &params, DownloadProgress progress) {
10506  return cli_->Delete(path, headers, params, progress);
10507}
10508
10509Result Client::Options(const std::string &path) {
10510  return cli_->Options(path);
10511}
10512Result Client::Options(const std::string &path, const Headers &headers) {
10513  return cli_->Options(path, headers);
10514}
10515
10516ClientImpl::StreamHandle
10517Client::open_stream(const std::string &method, const std::string &path,
10518                    const Params &params, const Headers &headers,
10519                    const std::string &body, const std::string &content_type) {
10520  return cli_->open_stream(method, path, params, headers, body, content_type);
10521}
10522
10523bool Client::send(Request &req, Response &res, Error &error) {
10524  return cli_->send(req, res, error);
10525}
10526
10527Result Client::send(const Request &req) { return cli_->send(req); }
10528
10529void Client::stop() { cli_->stop(); }
10530
10531std::string Client::host() const { return cli_->host(); }
10532
10533int Client::port() const { return cli_->port(); }
10534
10535size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
10536
10537socket_t Client::socket() const { return cli_->socket(); }
10538
10539void
10540Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
10541  cli_->set_hostname_addr_map(std::move(addr_map));
10542}
10543
10544void Client::set_default_headers(Headers headers) {
10545  cli_->set_default_headers(std::move(headers));
10546}
10547
10548void Client::set_header_writer(
10549    std::function<ssize_t(Stream &, Headers &)> const &writer) {
10550  cli_->set_header_writer(writer);
10551}
10552
10553void Client::set_address_family(int family) {
10554  cli_->set_address_family(family);
10555}
10556
10557void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
10558
10559void Client::set_socket_options(SocketOptions socket_options) {
10560  cli_->set_socket_options(std::move(socket_options));
10561}
10562
10563void Client::set_connection_timeout(time_t sec, time_t usec) {
10564  cli_->set_connection_timeout(sec, usec);
10565}
10566
10567void Client::set_read_timeout(time_t sec, time_t usec) {
10568  cli_->set_read_timeout(sec, usec);
10569}
10570
10571void Client::set_write_timeout(time_t sec, time_t usec) {
10572  cli_->set_write_timeout(sec, usec);
10573}
10574
10575void Client::set_basic_auth(const std::string &username,
10576                                   const std::string &password) {
10577  cli_->set_basic_auth(username, password);
10578}
10579void Client::set_bearer_token_auth(const std::string &token) {
10580  cli_->set_bearer_token_auth(token);
10581}
10582#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10583void Client::set_digest_auth(const std::string &username,
10584                                    const std::string &password) {
10585  cli_->set_digest_auth(username, password);
10586}
10587#endif
10588
10589void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
10590void Client::set_follow_location(bool on) {
10591  cli_->set_follow_location(on);
10592}
10593
10594void Client::set_path_encode(bool on) { cli_->set_path_encode(on); }
10595
10596[[deprecated("Use set_path_encode instead")]]
10597void Client::set_url_encode(bool on) {
10598  cli_->set_path_encode(on);
10599}
10600
10601void Client::set_compress(bool on) { cli_->set_compress(on); }
10602
10603void Client::set_decompress(bool on) { cli_->set_decompress(on); }
10604
10605void Client::set_interface(const std::string &intf) {
10606  cli_->set_interface(intf);
10607}
10608
10609void Client::set_proxy(const std::string &host, int port) {
10610  cli_->set_proxy(host, port);
10611}
10612void Client::set_proxy_basic_auth(const std::string &username,
10613                                         const std::string &password) {
10614  cli_->set_proxy_basic_auth(username, password);
10615}
10616void Client::set_proxy_bearer_token_auth(const std::string &token) {
10617  cli_->set_proxy_bearer_token_auth(token);
10618}
10619#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10620void Client::set_proxy_digest_auth(const std::string &username,
10621                                          const std::string &password) {
10622  cli_->set_proxy_digest_auth(username, password);
10623}
10624#endif
10625
10626#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10627void Client::enable_server_certificate_verification(bool enabled) {
10628  cli_->enable_server_certificate_verification(enabled);
10629}
10630
10631void Client::enable_server_hostname_verification(bool enabled) {
10632  cli_->enable_server_hostname_verification(enabled);
10633}
10634
10635void Client::set_server_certificate_verifier(
10636    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
10637  cli_->set_server_certificate_verifier(verifier);
10638}
10639#endif
10640
10641void Client::set_logger(Logger logger) {
10642  cli_->set_logger(std::move(logger));
10643}
10644
10645void Client::set_error_logger(ErrorLogger error_logger) {
10646  cli_->set_error_logger(std::move(error_logger));
10647}
10648
10649#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10650void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
10651                                     const std::string &ca_cert_dir_path) {
10652  cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
10653}
10654
10655void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
10656  if (is_ssl_) {
10657    static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
10658  } else {
10659    cli_->set_ca_cert_store(ca_cert_store);
10660  }
10661}
10662
10663void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
10664  set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
10665}
10666
10667long Client::get_openssl_verify_result() const {
10668  if (is_ssl_) {
10669    return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
10670  }
10671  return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
10672}
10673
10674SSL_CTX *Client::ssl_context() const {
10675  if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
10676  return nullptr;
10677}
10678#endif
10679
10680} // namespace httplib