1//
   2//  httplib.h
   3//
   4//  Copyright (c) 2026 Yuji Hirose. All rights reserved.
   5//  MIT License
   6//
   7
   8#ifndef CPPHTTPLIB_HTTPLIB_H
   9#define CPPHTTPLIB_HTTPLIB_H
  10
  11#define CPPHTTPLIB_VERSION "0.30.2"
  12#define CPPHTTPLIB_VERSION_NUM "0x001E02"
  13
  14/*
  15 * Platform compatibility check
  16 */
  17
  18#if defined(_WIN32) && !defined(_WIN64)
  19#if defined(_MSC_VER)
  20#pragma message(                                                               \
  21    "cpp-httplib doesn't support 32-bit Windows. Please use a 64-bit compiler.")
  22#else
  23#warning                                                                       \
  24    "cpp-httplib doesn't support 32-bit Windows. Please use a 64-bit compiler."
  25#endif
  26#elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ < 8
  27#warning                                                                       \
  28    "cpp-httplib doesn't support 32-bit platforms. Please use a 64-bit compiler."
  29#elif defined(__SIZEOF_SIZE_T__) && __SIZEOF_SIZE_T__ < 8
  30#warning                                                                       \
  31    "cpp-httplib doesn't support platforms where size_t is less than 64 bits."
  32#endif
  33
  34#ifdef _WIN32
  35#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
  36#error                                                                         \
  37    "cpp-httplib doesn't support Windows 8 or lower. Please use Windows 10 or later."
  38#endif
  39#endif
  40
  41/*
  42 * Configuration
  43 */
  44
  45#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
  46#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
  47#endif
  48
  49#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
  50#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
  51#endif
  52
  53#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
  54#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
  55#endif
  56
  57#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
  58#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
  59#endif
  60
  61#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
  62#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
  63#endif
  64
  65#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
  66#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
  67#endif
  68
  69#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
  70#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
  71#endif
  72
  73#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
  74#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
  75#endif
  76
  77#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
  78#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
  79#endif
  80
  81#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
  82#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
  83#endif
  84
  85#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
  86#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
  87#endif
  88
  89#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
  90#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
  91#endif
  92
  93#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
  94#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
  95#endif
  96
  97#ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
  98#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0
  99#endif
 100
 101#ifndef CPPHTTPLIB_EXPECT_100_THRESHOLD
 102#define CPPHTTPLIB_EXPECT_100_THRESHOLD 1024
 103#endif
 104
 105#ifndef CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND
 106#define CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND 1000
 107#endif
 108
 109#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD
 110#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD (1024 * 1024)
 111#endif
 112
 113#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND
 114#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND 50
 115#endif
 116
 117#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
 118#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
 119#endif
 120
 121#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
 122#ifdef _WIN32
 123#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
 124#else
 125#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
 126#endif
 127#endif
 128
 129#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
 130#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
 131#endif
 132
 133#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
 134#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
 135#endif
 136
 137#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
 138#define CPPHTTPLIB_HEADER_MAX_COUNT 100
 139#endif
 140
 141#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
 142#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
 143#endif
 144
 145#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
 146#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
 147#endif
 148
 149#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
 150#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
 151#endif
 152
 153#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
 154#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
 155#endif
 156
 157#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
 158#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
 159#endif
 160
 161#ifndef CPPHTTPLIB_TCP_NODELAY
 162#define CPPHTTPLIB_TCP_NODELAY false
 163#endif
 164
 165#ifndef CPPHTTPLIB_IPV6_V6ONLY
 166#define CPPHTTPLIB_IPV6_V6ONLY false
 167#endif
 168
 169#ifndef CPPHTTPLIB_RECV_BUFSIZ
 170#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
 171#endif
 172
 173#ifndef CPPHTTPLIB_SEND_BUFSIZ
 174#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
 175#endif
 176
 177#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
 178#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
 179#endif
 180
 181#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
 182#define CPPHTTPLIB_THREAD_POOL_COUNT                                           \
 183  ((std::max)(8u, std::thread::hardware_concurrency() > 0                      \
 184                      ? std::thread::hardware_concurrency() - 1                \
 185                      : 0))
 186#endif
 187
 188#ifndef CPPHTTPLIB_RECV_FLAGS
 189#define CPPHTTPLIB_RECV_FLAGS 0
 190#endif
 191
 192#ifndef CPPHTTPLIB_SEND_FLAGS
 193#define CPPHTTPLIB_SEND_FLAGS 0
 194#endif
 195
 196#ifndef CPPHTTPLIB_LISTEN_BACKLOG
 197#define CPPHTTPLIB_LISTEN_BACKLOG 5
 198#endif
 199
 200#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
 201#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
 202#endif
 203
 204/*
 205 * Headers
 206 */
 207
 208#ifdef _WIN32
 209#ifndef _CRT_SECURE_NO_WARNINGS
 210#define _CRT_SECURE_NO_WARNINGS
 211#endif //_CRT_SECURE_NO_WARNINGS
 212
 213#ifndef _CRT_NONSTDC_NO_DEPRECATE
 214#define _CRT_NONSTDC_NO_DEPRECATE
 215#endif //_CRT_NONSTDC_NO_DEPRECATE
 216
 217#if defined(_MSC_VER)
 218#if _MSC_VER < 1900
 219#error Sorry, Visual Studio versions prior to 2015 are not supported
 220#endif
 221
 222#pragma comment(lib, "ws2_32.lib")
 223
 224#ifndef _SSIZE_T_DEFINED
 225using ssize_t = __int64;
 226#define _SSIZE_T_DEFINED
 227#endif
 228#endif // _MSC_VER
 229
 230#ifndef S_ISREG
 231#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
 232#endif // S_ISREG
 233
 234#ifndef S_ISDIR
 235#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
 236#endif // S_ISDIR
 237
 238#ifndef NOMINMAX
 239#define NOMINMAX
 240#endif // NOMINMAX
 241
 242#include <io.h>
 243#include <winsock2.h>
 244#include <ws2tcpip.h>
 245
 246#if defined(__has_include)
 247#if __has_include(<afunix.h>)
 248// afunix.h uses types declared in winsock2.h, so has to be included after it.
 249#include <afunix.h>
 250#define CPPHTTPLIB_HAVE_AFUNIX_H 1
 251#endif
 252#endif
 253
 254#ifndef WSA_FLAG_NO_HANDLE_INHERIT
 255#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
 256#endif
 257
 258using nfds_t = unsigned long;
 259using socket_t = SOCKET;
 260using socklen_t = int;
 261
 262#else // not _WIN32
 263
 264#include <arpa/inet.h>
 265#if !defined(_AIX) && !defined(__MVS__)
 266#include <ifaddrs.h>
 267#endif
 268#ifdef __MVS__
 269#include <strings.h>
 270#ifndef NI_MAXHOST
 271#define NI_MAXHOST 1025
 272#endif
 273#endif
 274#include <net/if.h>
 275#include <netdb.h>
 276#include <netinet/in.h>
 277#ifdef __linux__
 278#include <resolv.h>
 279#undef _res // Undefine _res macro to avoid conflicts with user code (#2278)
 280#endif
 281#include <csignal>
 282#include <netinet/tcp.h>
 283#include <poll.h>
 284#include <pthread.h>
 285#include <sys/mman.h>
 286#include <sys/socket.h>
 287#include <sys/un.h>
 288#include <unistd.h>
 289
 290using socket_t = int;
 291#ifndef INVALID_SOCKET
 292#define INVALID_SOCKET (-1)
 293#endif
 294#endif //_WIN32
 295
 296#if defined(__APPLE__)
 297#include <TargetConditionals.h>
 298#endif
 299
 300#include <algorithm>
 301#include <array>
 302#include <atomic>
 303#include <cassert>
 304#include <cctype>
 305#include <chrono>
 306#include <climits>
 307#include <condition_variable>
 308#include <cstdlib>
 309#include <cstring>
 310#include <errno.h>
 311#include <exception>
 312#include <fcntl.h>
 313#include <functional>
 314#include <iomanip>
 315#include <iostream>
 316#include <list>
 317#include <map>
 318#include <memory>
 319#include <mutex>
 320#include <random>
 321#include <regex>
 322#include <set>
 323#include <sstream>
 324#include <string>
 325#include <sys/stat.h>
 326#include <system_error>
 327#include <thread>
 328#include <unordered_map>
 329#include <unordered_set>
 330#include <utility>
 331
 332#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) ||                        \
 333    defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
 334#if TARGET_OS_MAC
 335#include <CFNetwork/CFHost.h>
 336#include <CoreFoundation/CoreFoundation.h>
 337#endif
 338#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO or
 339       // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
 340
 341#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 342#ifdef _WIN32
 343#include <wincrypt.h>
 344
 345// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
 346// used
 347#undef X509_NAME
 348#undef X509_CERT_PAIR
 349#undef X509_EXTENSIONS
 350#undef PKCS7_SIGNER_INFO
 351
 352#ifdef _MSC_VER
 353#pragma comment(lib, "crypt32.lib")
 354#endif
 355#endif // _WIN32
 356
 357#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
 358#if TARGET_OS_MAC
 359#include <Security/Security.h>
 360#endif
 361#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
 362
 363#include <openssl/err.h>
 364#include <openssl/evp.h>
 365#include <openssl/ssl.h>
 366#include <openssl/x509v3.h>
 367
 368#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
 369#include <openssl/applink.c>
 370#endif
 371
 372#include <iostream>
 373#include <sstream>
 374
 375#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
 376#if OPENSSL_VERSION_NUMBER < 0x1010107f
 377#error Please use OpenSSL or a current version of BoringSSL
 378#endif
 379#define SSL_get1_peer_certificate SSL_get_peer_certificate
 380#elif OPENSSL_VERSION_NUMBER < 0x30000000L
 381#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
 382#endif
 383
 384#endif // CPPHTTPLIB_OPENSSL_SUPPORT
 385
 386#ifdef CPPHTTPLIB_ZLIB_SUPPORT
 387#include <zlib.h>
 388#endif
 389
 390#ifdef CPPHTTPLIB_BROTLI_SUPPORT
 391#include <brotli/decode.h>
 392#include <brotli/encode.h>
 393#endif
 394
 395#ifdef CPPHTTPLIB_ZSTD_SUPPORT
 396#include <zstd.h>
 397#endif
 398
 399/*
 400 * Declaration
 401 */
 402namespace httplib {
 403
 404namespace detail {
 405
 406/*
 407 * Backport std::make_unique from C++14.
 408 *
 409 * NOTE: This code came up with the following stackoverflow post:
 410 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
 411 *
 412 */
 413
 414template <class T, class... Args>
 415typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
 416make_unique(Args &&...args) {
 417  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
 418}
 419
 420template <class T>
 421typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
 422make_unique(std::size_t n) {
 423  typedef typename std::remove_extent<T>::type RT;
 424  return std::unique_ptr<T>(new RT[n]);
 425}
 426
 427namespace case_ignore {
 428
 429inline unsigned char to_lower(int c) {
 430  const static unsigned char table[256] = {
 431      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
 432      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
 433      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
 434      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
 435      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,
 436      107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
 437      122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
 438      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
 439      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
 440      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
 441      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
 442      165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
 443      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
 444      227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
 445      242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
 446      225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
 447      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
 448      255,
 449  };
 450  return table[(unsigned char)(char)c];
 451}
 452
 453inline bool equal(const std::string &a, const std::string &b) {
 454  return a.size() == b.size() &&
 455         std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
 456           return to_lower(ca) == to_lower(cb);
 457         });
 458}
 459
 460struct equal_to {
 461  bool operator()(const std::string &a, const std::string &b) const {
 462    return equal(a, b);
 463  }
 464};
 465
 466struct hash {
 467  size_t operator()(const std::string &key) const {
 468    return hash_core(key.data(), key.size(), 0);
 469  }
 470
 471  size_t hash_core(const char *s, size_t l, size_t h) const {
 472    return (l == 0) ? h
 473                    : hash_core(s + 1, l - 1,
 474                                // Unsets the 6 high bits of h, therefore no
 475                                // overflow happens
 476                                (((std::numeric_limits<size_t>::max)() >> 6) &
 477                                 h * 33) ^
 478                                    static_cast<unsigned char>(to_lower(*s)));
 479  }
 480};
 481
 482template <typename T>
 483using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
 484                                         detail::case_ignore::equal_to>;
 485
 486} // namespace case_ignore
 487
 488// This is based on
 489// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
 490
 491struct scope_exit {
 492  explicit scope_exit(std::function<void(void)> &&f)
 493      : exit_function(std::move(f)), execute_on_destruction{true} {}
 494
 495  scope_exit(scope_exit &&rhs) noexcept
 496      : exit_function(std::move(rhs.exit_function)),
 497        execute_on_destruction{rhs.execute_on_destruction} {
 498    rhs.release();
 499  }
 500
 501  ~scope_exit() {
 502    if (execute_on_destruction) { this->exit_function(); }
 503  }
 504
 505  void release() { this->execute_on_destruction = false; }
 506
 507private:
 508  scope_exit(const scope_exit &) = delete;
 509  void operator=(const scope_exit &) = delete;
 510  scope_exit &operator=(scope_exit &&) = delete;
 511
 512  std::function<void(void)> exit_function;
 513  bool execute_on_destruction;
 514};
 515
 516// Simple from_chars implementation for integer and double types (C++17
 517// substitute)
 518template <typename T> struct from_chars_result {
 519  const char *ptr;
 520  std::errc ec;
 521};
 522
 523template <typename T>
 524inline from_chars_result<T> from_chars(const char *first, const char *last,
 525                                       T &value, int base = 10) {
 526  value = 0;
 527  const char *p = first;
 528  bool negative = false;
 529
 530  if (p != last && *p == '-') {
 531    negative = true;
 532    ++p;
 533  }
 534  if (p == last) { return {first, std::errc::invalid_argument}; }
 535
 536  T result = 0;
 537  for (; p != last; ++p) {
 538    char c = *p;
 539    int digit = -1;
 540    if ('0' <= c && c <= '9') {
 541      digit = c - '0';
 542    } else if ('a' <= c && c <= 'z') {
 543      digit = c - 'a' + 10;
 544    } else if ('A' <= c && c <= 'Z') {
 545      digit = c - 'A' + 10;
 546    } else {
 547      break;
 548    }
 549
 550    if (digit < 0 || digit >= base) { break; }
 551    if (result > ((std::numeric_limits<T>::max)() - digit) / base) {
 552      return {p, std::errc::result_out_of_range};
 553    }
 554    result = result * base + digit;
 555  }
 556
 557  if (p == first || (negative && p == first + 1)) {
 558    return {first, std::errc::invalid_argument};
 559  }
 560
 561  value = negative ? -result : result;
 562  return {p, std::errc{}};
 563}
 564
 565// from_chars for double (simple wrapper for strtod)
 566inline from_chars_result<double> from_chars(const char *first, const char *last,
 567                                            double &value) {
 568  std::string s(first, last);
 569  char *endptr = nullptr;
 570  errno = 0;
 571  value = std::strtod(s.c_str(), &endptr);
 572  if (endptr == s.c_str()) { return {first, std::errc::invalid_argument}; }
 573  if (errno == ERANGE) {
 574    return {first + (endptr - s.c_str()), std::errc::result_out_of_range};
 575  }
 576  return {first + (endptr - s.c_str()), std::errc{}};
 577}
 578
 579} // namespace detail
 580
 581enum SSLVerifierResponse {
 582  // no decision has been made, use the built-in certificate verifier
 583  NoDecisionMade,
 584  // connection certificate is verified and accepted
 585  CertificateAccepted,
 586  // connection certificate was processed but is rejected
 587  CertificateRejected
 588};
 589
 590enum StatusCode {
 591  // Information responses
 592  Continue_100 = 100,
 593  SwitchingProtocol_101 = 101,
 594  Processing_102 = 102,
 595  EarlyHints_103 = 103,
 596
 597  // Successful responses
 598  OK_200 = 200,
 599  Created_201 = 201,
 600  Accepted_202 = 202,
 601  NonAuthoritativeInformation_203 = 203,
 602  NoContent_204 = 204,
 603  ResetContent_205 = 205,
 604  PartialContent_206 = 206,
 605  MultiStatus_207 = 207,
 606  AlreadyReported_208 = 208,
 607  IMUsed_226 = 226,
 608
 609  // Redirection messages
 610  MultipleChoices_300 = 300,
 611  MovedPermanently_301 = 301,
 612  Found_302 = 302,
 613  SeeOther_303 = 303,
 614  NotModified_304 = 304,
 615  UseProxy_305 = 305,
 616  unused_306 = 306,
 617  TemporaryRedirect_307 = 307,
 618  PermanentRedirect_308 = 308,
 619
 620  // Client error responses
 621  BadRequest_400 = 400,
 622  Unauthorized_401 = 401,
 623  PaymentRequired_402 = 402,
 624  Forbidden_403 = 403,
 625  NotFound_404 = 404,
 626  MethodNotAllowed_405 = 405,
 627  NotAcceptable_406 = 406,
 628  ProxyAuthenticationRequired_407 = 407,
 629  RequestTimeout_408 = 408,
 630  Conflict_409 = 409,
 631  Gone_410 = 410,
 632  LengthRequired_411 = 411,
 633  PreconditionFailed_412 = 412,
 634  PayloadTooLarge_413 = 413,
 635  UriTooLong_414 = 414,
 636  UnsupportedMediaType_415 = 415,
 637  RangeNotSatisfiable_416 = 416,
 638  ExpectationFailed_417 = 417,
 639  ImATeapot_418 = 418,
 640  MisdirectedRequest_421 = 421,
 641  UnprocessableContent_422 = 422,
 642  Locked_423 = 423,
 643  FailedDependency_424 = 424,
 644  TooEarly_425 = 425,
 645  UpgradeRequired_426 = 426,
 646  PreconditionRequired_428 = 428,
 647  TooManyRequests_429 = 429,
 648  RequestHeaderFieldsTooLarge_431 = 431,
 649  UnavailableForLegalReasons_451 = 451,
 650
 651  // Server error responses
 652  InternalServerError_500 = 500,
 653  NotImplemented_501 = 501,
 654  BadGateway_502 = 502,
 655  ServiceUnavailable_503 = 503,
 656  GatewayTimeout_504 = 504,
 657  HttpVersionNotSupported_505 = 505,
 658  VariantAlsoNegotiates_506 = 506,
 659  InsufficientStorage_507 = 507,
 660  LoopDetected_508 = 508,
 661  NotExtended_510 = 510,
 662  NetworkAuthenticationRequired_511 = 511,
 663};
 664
 665using Headers =
 666    std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
 667                            detail::case_ignore::equal_to>;
 668
 669using Params = std::multimap<std::string, std::string>;
 670using Match = std::smatch;
 671
 672using DownloadProgress = std::function<bool(size_t current, size_t total)>;
 673using UploadProgress = std::function<bool(size_t current, size_t total)>;
 674
 675struct Response;
 676using ResponseHandler = std::function<bool(const Response &response)>;
 677
 678struct FormData {
 679  std::string name;
 680  std::string content;
 681  std::string filename;
 682  std::string content_type;
 683  Headers headers;
 684};
 685
 686struct FormField {
 687  std::string name;
 688  std::string content;
 689  Headers headers;
 690};
 691using FormFields = std::multimap<std::string, FormField>;
 692
 693using FormFiles = std::multimap<std::string, FormData>;
 694
 695struct MultipartFormData {
 696  FormFields fields; // Text fields from multipart
 697  FormFiles files;   // Files from multipart
 698
 699  // Text field access
 700  std::string get_field(const std::string &key, size_t id = 0) const;
 701  std::vector<std::string> get_fields(const std::string &key) const;
 702  bool has_field(const std::string &key) const;
 703  size_t get_field_count(const std::string &key) const;
 704
 705  // File access
 706  FormData get_file(const std::string &key, size_t id = 0) const;
 707  std::vector<FormData> get_files(const std::string &key) const;
 708  bool has_file(const std::string &key) const;
 709  size_t get_file_count(const std::string &key) const;
 710};
 711
 712struct UploadFormData {
 713  std::string name;
 714  std::string content;
 715  std::string filename;
 716  std::string content_type;
 717};
 718using UploadFormDataItems = std::vector<UploadFormData>;
 719
 720class DataSink {
 721public:
 722  DataSink() : os(&sb_), sb_(*this) {}
 723
 724  DataSink(const DataSink &) = delete;
 725  DataSink &operator=(const DataSink &) = delete;
 726  DataSink(DataSink &&) = delete;
 727  DataSink &operator=(DataSink &&) = delete;
 728
 729  std::function<bool(const char *data, size_t data_len)> write;
 730  std::function<bool()> is_writable;
 731  std::function<void()> done;
 732  std::function<void(const Headers &trailer)> done_with_trailer;
 733  std::ostream os;
 734
 735private:
 736  class data_sink_streambuf final : public std::streambuf {
 737  public:
 738    explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
 739
 740  protected:
 741    std::streamsize xsputn(const char *s, std::streamsize n) override {
 742      sink_.write(s, static_cast<size_t>(n));
 743      return n;
 744    }
 745
 746  private:
 747    DataSink &sink_;
 748  };
 749
 750  data_sink_streambuf sb_;
 751};
 752
 753using ContentProvider =
 754    std::function<bool(size_t offset, size_t length, DataSink &sink)>;
 755
 756using ContentProviderWithoutLength =
 757    std::function<bool(size_t offset, DataSink &sink)>;
 758
 759using ContentProviderResourceReleaser = std::function<void(bool success)>;
 760
 761struct FormDataProvider {
 762  std::string name;
 763  ContentProviderWithoutLength provider;
 764  std::string filename;
 765  std::string content_type;
 766};
 767using FormDataProviderItems = std::vector<FormDataProvider>;
 768
 769using ContentReceiverWithProgress = std::function<bool(
 770    const char *data, size_t data_length, size_t offset, size_t total_length)>;
 771
 772using ContentReceiver =
 773    std::function<bool(const char *data, size_t data_length)>;
 774
 775using FormDataHeader = std::function<bool(const FormData &file)>;
 776
 777class ContentReader {
 778public:
 779  using Reader = std::function<bool(ContentReceiver receiver)>;
 780  using FormDataReader =
 781      std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
 782
 783  ContentReader(Reader reader, FormDataReader multipart_reader)
 784      : reader_(std::move(reader)),
 785        formdata_reader_(std::move(multipart_reader)) {}
 786
 787  bool operator()(FormDataHeader header, ContentReceiver receiver) const {
 788    return formdata_reader_(std::move(header), std::move(receiver));
 789  }
 790
 791  bool operator()(ContentReceiver receiver) const {
 792    return reader_(std::move(receiver));
 793  }
 794
 795  Reader reader_;
 796  FormDataReader formdata_reader_;
 797};
 798
 799using Range = std::pair<ssize_t, ssize_t>;
 800using Ranges = std::vector<Range>;
 801
 802struct Request {
 803  std::string method;
 804  std::string path;
 805  std::string matched_route;
 806  Params params;
 807  Headers headers;
 808  Headers trailers;
 809  std::string body;
 810
 811  std::string remote_addr;
 812  int remote_port = -1;
 813  std::string local_addr;
 814  int local_port = -1;
 815
 816  // for server
 817  std::string version;
 818  std::string target;
 819  MultipartFormData form;
 820  Ranges ranges;
 821  Match matches;
 822  std::unordered_map<std::string, std::string> path_params;
 823  std::function<bool()> is_connection_closed = []() { return true; };
 824
 825  // for client
 826  std::vector<std::string> accept_content_types;
 827  ResponseHandler response_handler;
 828  ContentReceiverWithProgress content_receiver;
 829  DownloadProgress download_progress;
 830  UploadProgress upload_progress;
 831#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 832  const SSL *ssl = nullptr;
 833#endif
 834
 835  bool has_header(const std::string &key) const;
 836  std::string get_header_value(const std::string &key, const char *def = "",
 837                               size_t id = 0) const;
 838  size_t get_header_value_u64(const std::string &key, size_t def = 0,
 839                              size_t id = 0) const;
 840  size_t get_header_value_count(const std::string &key) const;
 841  void set_header(const std::string &key, const std::string &val);
 842
 843  bool has_trailer(const std::string &key) const;
 844  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
 845  size_t get_trailer_value_count(const std::string &key) const;
 846
 847  bool has_param(const std::string &key) const;
 848  std::string get_param_value(const std::string &key, size_t id = 0) const;
 849  size_t get_param_value_count(const std::string &key) const;
 850
 851  bool is_multipart_form_data() const;
 852
 853  // private members...
 854  size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
 855  size_t content_length_ = 0;
 856  ContentProvider content_provider_;
 857  bool is_chunked_content_provider_ = false;
 858  size_t authorization_count_ = 0;
 859  std::chrono::time_point<std::chrono::steady_clock> start_time_ =
 860      (std::chrono::steady_clock::time_point::min)();
 861};
 862
 863struct Response {
 864  std::string version;
 865  int status = -1;
 866  std::string reason;
 867  Headers headers;
 868  Headers trailers;
 869  std::string body;
 870  std::string location; // Redirect location
 871
 872  bool has_header(const std::string &key) const;
 873  std::string get_header_value(const std::string &key, const char *def = "",
 874                               size_t id = 0) const;
 875  size_t get_header_value_u64(const std::string &key, size_t def = 0,
 876                              size_t id = 0) const;
 877  size_t get_header_value_count(const std::string &key) const;
 878  void set_header(const std::string &key, const std::string &val);
 879
 880  bool has_trailer(const std::string &key) const;
 881  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
 882  size_t get_trailer_value_count(const std::string &key) const;
 883
 884  void set_redirect(const std::string &url, int status = StatusCode::Found_302);
 885  void set_content(const char *s, size_t n, const std::string &content_type);
 886  void set_content(const std::string &s, const std::string &content_type);
 887  void set_content(std::string &&s, const std::string &content_type);
 888
 889  void set_content_provider(
 890      size_t length, const std::string &content_type, ContentProvider provider,
 891      ContentProviderResourceReleaser resource_releaser = nullptr);
 892
 893  void set_content_provider(
 894      const std::string &content_type, ContentProviderWithoutLength provider,
 895      ContentProviderResourceReleaser resource_releaser = nullptr);
 896
 897  void set_chunked_content_provider(
 898      const std::string &content_type, ContentProviderWithoutLength provider,
 899      ContentProviderResourceReleaser resource_releaser = nullptr);
 900
 901  void set_file_content(const std::string &path,
 902                        const std::string &content_type);
 903  void set_file_content(const std::string &path);
 904
 905  Response() = default;
 906  Response(const Response &) = default;
 907  Response &operator=(const Response &) = default;
 908  Response(Response &&) = default;
 909  Response &operator=(Response &&) = default;
 910  ~Response() {
 911    if (content_provider_resource_releaser_) {
 912      content_provider_resource_releaser_(content_provider_success_);
 913    }
 914  }
 915
 916  // private members...
 917  size_t content_length_ = 0;
 918  ContentProvider content_provider_;
 919  ContentProviderResourceReleaser content_provider_resource_releaser_;
 920  bool is_chunked_content_provider_ = false;
 921  bool content_provider_success_ = false;
 922  std::string file_content_path_;
 923  std::string file_content_content_type_;
 924};
 925
 926enum class Error {
 927  Success = 0,
 928  Unknown,
 929  Connection,
 930  BindIPAddress,
 931  Read,
 932  Write,
 933  ExceedRedirectCount,
 934  Canceled,
 935  SSLConnection,
 936  SSLLoadingCerts,
 937  SSLServerVerification,
 938  SSLServerHostnameVerification,
 939  UnsupportedMultipartBoundaryChars,
 940  Compression,
 941  ConnectionTimeout,
 942  ProxyConnection,
 943  ConnectionClosed,
 944  Timeout,
 945  ResourceExhaustion,
 946  TooManyFormDataFiles,
 947  ExceedMaxPayloadSize,
 948  ExceedUriMaxLength,
 949  ExceedMaxSocketDescriptorCount,
 950  InvalidRequestLine,
 951  InvalidHTTPMethod,
 952  InvalidHTTPVersion,
 953  InvalidHeaders,
 954  MultipartParsing,
 955  OpenFile,
 956  Listen,
 957  GetSockName,
 958  UnsupportedAddressFamily,
 959  HTTPParsing,
 960  InvalidRangeHeader,
 961
 962  // For internal use only
 963  SSLPeerCouldBeClosed_,
 964};
 965
 966std::string to_string(Error error);
 967
 968std::ostream &operator<<(std::ostream &os, const Error &obj);
 969
 970class Stream {
 971public:
 972  virtual ~Stream() = default;
 973
 974  virtual bool is_readable() const = 0;
 975  virtual bool wait_readable() const = 0;
 976  virtual bool wait_writable() const = 0;
 977
 978  virtual ssize_t read(char *ptr, size_t size) = 0;
 979  virtual ssize_t write(const char *ptr, size_t size) = 0;
 980  virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
 981  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
 982  virtual socket_t socket() const = 0;
 983
 984  virtual time_t duration() const = 0;
 985
 986  ssize_t write(const char *ptr);
 987  ssize_t write(const std::string &s);
 988
 989  Error get_error() const { return error_; }
 990
 991protected:
 992  Error error_ = Error::Success;
 993};
 994
 995class TaskQueue {
 996public:
 997  TaskQueue() = default;
 998  virtual ~TaskQueue() = default;
 999
1000  virtual bool enqueue(std::function<void()> fn) = 0;
1001  virtual void shutdown() = 0;
1002
1003  virtual void on_idle() {}
1004};
1005
1006class ThreadPool final : public TaskQueue {
1007public:
1008  explicit ThreadPool(size_t n, size_t mqr = 0)
1009      : shutdown_(false), max_queued_requests_(mqr) {
1010    threads_.reserve(n);
1011    while (n) {
1012      threads_.emplace_back(worker(*this));
1013      n--;
1014    }
1015  }
1016
1017  ThreadPool(const ThreadPool &) = delete;
1018  ~ThreadPool() override = default;
1019
1020  bool enqueue(std::function<void()> fn) override {
1021    {
1022      std::unique_lock<std::mutex> lock(mutex_);
1023      if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
1024        return false;
1025      }
1026      jobs_.push_back(std::move(fn));
1027    }
1028
1029    cond_.notify_one();
1030    return true;
1031  }
1032
1033  void shutdown() override {
1034    // Stop all worker threads...
1035    {
1036      std::unique_lock<std::mutex> lock(mutex_);
1037      shutdown_ = true;
1038    }
1039
1040    cond_.notify_all();
1041
1042    // Join...
1043    for (auto &t : threads_) {
1044      t.join();
1045    }
1046  }
1047
1048private:
1049  struct worker {
1050    explicit worker(ThreadPool &pool) : pool_(pool) {}
1051
1052    void operator()() {
1053      for (;;) {
1054        std::function<void()> fn;
1055        {
1056          std::unique_lock<std::mutex> lock(pool_.mutex_);
1057
1058          pool_.cond_.wait(
1059              lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
1060
1061          if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
1062
1063          fn = pool_.jobs_.front();
1064          pool_.jobs_.pop_front();
1065        }
1066
1067        assert(true == static_cast<bool>(fn));
1068        fn();
1069      }
1070
1071#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) &&   \
1072    !defined(LIBRESSL_VERSION_NUMBER)
1073      OPENSSL_thread_stop();
1074#endif
1075    }
1076
1077    ThreadPool &pool_;
1078  };
1079  friend struct worker;
1080
1081  std::vector<std::thread> threads_;
1082  std::list<std::function<void()>> jobs_;
1083
1084  bool shutdown_;
1085  size_t max_queued_requests_ = 0;
1086
1087  std::condition_variable cond_;
1088  std::mutex mutex_;
1089};
1090
1091using Logger = std::function<void(const Request &, const Response &)>;
1092
1093// Forward declaration for Error type
1094enum class Error;
1095using ErrorLogger = std::function<void(const Error &, const Request *)>;
1096
1097using SocketOptions = std::function<void(socket_t sock)>;
1098
1099void default_socket_options(socket_t sock);
1100
1101const char *status_message(int status);
1102
1103std::string to_string(Error error);
1104
1105std::ostream &operator<<(std::ostream &os, const Error &obj);
1106
1107std::string get_bearer_token_auth(const Request &req);
1108
1109namespace detail {
1110
1111class MatcherBase {
1112public:
1113  MatcherBase(std::string pattern) : pattern_(std::move(pattern)) {}
1114  virtual ~MatcherBase() = default;
1115
1116  const std::string &pattern() const { return pattern_; }
1117
1118  // Match request path and populate its matches and
1119  virtual bool match(Request &request) const = 0;
1120
1121private:
1122  std::string pattern_;
1123};
1124
1125/**
1126 * Captures parameters in request path and stores them in Request::path_params
1127 *
1128 * Capture name is a substring of a pattern from : to /.
1129 * The rest of the pattern is matched against the request path directly
1130 * Parameters are captured starting from the next character after
1131 * the end of the last matched static pattern fragment until the next /.
1132 *
1133 * Example pattern:
1134 * "/path/fragments/:capture/more/fragments/:second_capture"
1135 * Static fragments:
1136 * "/path/fragments/", "more/fragments/"
1137 *
1138 * Given the following request path:
1139 * "/path/fragments/:1/more/fragments/:2"
1140 * the resulting capture will be
1141 * {{"capture", "1"}, {"second_capture", "2"}}
1142 */
1143class PathParamsMatcher final : public MatcherBase {
1144public:
1145  PathParamsMatcher(const std::string &pattern);
1146
1147  bool match(Request &request) const override;
1148
1149private:
1150  // Treat segment separators as the end of path parameter capture
1151  // Does not need to handle query parameters as they are parsed before path
1152  // matching
1153  static constexpr char separator = '/';
1154
1155  // Contains static path fragments to match against, excluding the '/' after
1156  // path params
1157  // Fragments are separated by path params
1158  std::vector<std::string> static_fragments_;
1159  // Stores the names of the path parameters to be used as keys in the
1160  // Request::path_params map
1161  std::vector<std::string> param_names_;
1162};
1163
1164/**
1165 * Performs std::regex_match on request path
1166 * and stores the result in Request::matches
1167 *
1168 * Note that regex match is performed directly on the whole request.
1169 * This means that wildcard patterns may match multiple path segments with /:
1170 * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end".
1171 */
1172class RegexMatcher final : public MatcherBase {
1173public:
1174  RegexMatcher(const std::string &pattern)
1175      : MatcherBase(pattern), regex_(pattern) {}
1176
1177  bool match(Request &request) const override;
1178
1179private:
1180  std::regex regex_;
1181};
1182
1183int close_socket(socket_t sock);
1184
1185ssize_t write_headers(Stream &strm, const Headers &headers);
1186
1187} // namespace detail
1188
1189class Server {
1190public:
1191  using Handler = std::function<void(const Request &, Response &)>;
1192
1193  using ExceptionHandler =
1194      std::function<void(const Request &, Response &, std::exception_ptr ep)>;
1195
1196  enum class HandlerResponse {
1197    Handled,
1198    Unhandled,
1199  };
1200  using HandlerWithResponse =
1201      std::function<HandlerResponse(const Request &, Response &)>;
1202
1203  using HandlerWithContentReader = std::function<void(
1204      const Request &, Response &, const ContentReader &content_reader)>;
1205
1206  using Expect100ContinueHandler =
1207      std::function<int(const Request &, Response &)>;
1208
1209  Server();
1210
1211  virtual ~Server();
1212
1213  virtual bool is_valid() const;
1214
1215  Server &Get(const std::string &pattern, Handler handler);
1216  Server &Post(const std::string &pattern, Handler handler);
1217  Server &Post(const std::string &pattern, HandlerWithContentReader handler);
1218  Server &Put(const std::string &pattern, Handler handler);
1219  Server &Put(const std::string &pattern, HandlerWithContentReader handler);
1220  Server &Patch(const std::string &pattern, Handler handler);
1221  Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
1222  Server &Delete(const std::string &pattern, Handler handler);
1223  Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
1224  Server &Options(const std::string &pattern, Handler handler);
1225
1226  bool set_base_dir(const std::string &dir,
1227                    const std::string &mount_point = std::string());
1228  bool set_mount_point(const std::string &mount_point, const std::string &dir,
1229                       Headers headers = Headers());
1230  bool remove_mount_point(const std::string &mount_point);
1231  Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
1232                                                  const std::string &mime);
1233  Server &set_default_file_mimetype(const std::string &mime);
1234  Server &set_file_request_handler(Handler handler);
1235
1236  template <class ErrorHandlerFunc>
1237  Server &set_error_handler(ErrorHandlerFunc &&handler) {
1238    return set_error_handler_core(
1239        std::forward<ErrorHandlerFunc>(handler),
1240        std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
1241  }
1242
1243  Server &set_exception_handler(ExceptionHandler handler);
1244
1245  Server &set_pre_routing_handler(HandlerWithResponse handler);
1246  Server &set_post_routing_handler(Handler handler);
1247
1248  Server &set_pre_request_handler(HandlerWithResponse handler);
1249
1250  Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
1251  Server &set_logger(Logger logger);
1252  Server &set_pre_compression_logger(Logger logger);
1253  Server &set_error_logger(ErrorLogger error_logger);
1254
1255  Server &set_address_family(int family);
1256  Server &set_tcp_nodelay(bool on);
1257  Server &set_ipv6_v6only(bool on);
1258  Server &set_socket_options(SocketOptions socket_options);
1259
1260  Server &set_default_headers(Headers headers);
1261  Server &
1262  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1263
1264  Server &set_trusted_proxies(const std::vector<std::string> &proxies);
1265
1266  Server &set_keep_alive_max_count(size_t count);
1267  Server &set_keep_alive_timeout(time_t sec);
1268
1269  Server &set_read_timeout(time_t sec, time_t usec = 0);
1270  template <class Rep, class Period>
1271  Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1272
1273  Server &set_write_timeout(time_t sec, time_t usec = 0);
1274  template <class Rep, class Period>
1275  Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1276
1277  Server &set_idle_interval(time_t sec, time_t usec = 0);
1278  template <class Rep, class Period>
1279  Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
1280
1281  Server &set_payload_max_length(size_t length);
1282
1283  bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
1284  int bind_to_any_port(const std::string &host, int socket_flags = 0);
1285  bool listen_after_bind();
1286
1287  bool listen(const std::string &host, int port, int socket_flags = 0);
1288
1289  bool is_running() const;
1290  void wait_until_ready() const;
1291  void stop();
1292  void decommission();
1293
1294  std::function<TaskQueue *(void)> new_task_queue;
1295
1296protected:
1297  bool process_request(Stream &strm, const std::string &remote_addr,
1298                       int remote_port, const std::string &local_addr,
1299                       int local_port, bool close_connection,
1300                       bool &connection_closed,
1301                       const std::function<void(Request &)> &setup_request);
1302
1303  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
1304
1305  std::vector<std::string> trusted_proxies_;
1306
1307  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
1308  time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
1309  time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
1310  time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
1311  time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
1312  time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
1313  time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
1314  time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
1315  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
1316
1317private:
1318  using Handlers =
1319      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
1320  using HandlersForContentReader =
1321      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1322                            HandlerWithContentReader>>;
1323
1324  static std::unique_ptr<detail::MatcherBase>
1325  make_matcher(const std::string &pattern);
1326
1327  Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1328  Server &set_error_handler_core(Handler handler, std::false_type);
1329
1330  socket_t create_server_socket(const std::string &host, int port,
1331                                int socket_flags,
1332                                SocketOptions socket_options) const;
1333  int bind_internal(const std::string &host, int port, int socket_flags);
1334  bool listen_internal();
1335
1336  bool routing(Request &req, Response &res, Stream &strm);
1337  bool handle_file_request(Request &req, Response &res);
1338  bool check_if_not_modified(const Request &req, Response &res,
1339                             const std::string &etag, time_t mtime) const;
1340  bool check_if_range(Request &req, const std::string &etag,
1341                      time_t mtime) const;
1342  bool dispatch_request(Request &req, Response &res,
1343                        const Handlers &handlers) const;
1344  bool dispatch_request_for_content_reader(
1345      Request &req, Response &res, ContentReader content_reader,
1346      const HandlersForContentReader &handlers) const;
1347
1348  bool parse_request_line(const char *s, Request &req) const;
1349  void apply_ranges(const Request &req, Response &res,
1350                    std::string &content_type, std::string &boundary) const;
1351  bool write_response(Stream &strm, bool close_connection, Request &req,
1352                      Response &res);
1353  bool write_response_with_content(Stream &strm, bool close_connection,
1354                                   const Request &req, Response &res);
1355  bool write_response_core(Stream &strm, bool close_connection,
1356                           const Request &req, Response &res,
1357                           bool need_apply_ranges);
1358  bool write_content_with_provider(Stream &strm, const Request &req,
1359                                   Response &res, const std::string &boundary,
1360                                   const std::string &content_type);
1361  bool read_content(Stream &strm, Request &req, Response &res);
1362  bool read_content_with_content_receiver(Stream &strm, Request &req,
1363                                          Response &res,
1364                                          ContentReceiver receiver,
1365                                          FormDataHeader multipart_header,
1366                                          ContentReceiver multipart_receiver);
1367  bool read_content_core(Stream &strm, Request &req, Response &res,
1368                         ContentReceiver receiver,
1369                         FormDataHeader multipart_header,
1370                         ContentReceiver multipart_receiver) const;
1371
1372  virtual bool process_and_close_socket(socket_t sock);
1373
1374  void output_log(const Request &req, const Response &res) const;
1375  void output_pre_compression_log(const Request &req,
1376                                  const Response &res) const;
1377  void output_error_log(const Error &err, const Request *req) const;
1378
1379  std::atomic<bool> is_running_{false};
1380  std::atomic<bool> is_decommissioned{false};
1381
1382  struct MountPointEntry {
1383    std::string mount_point;
1384    std::string base_dir;
1385    Headers headers;
1386  };
1387  std::vector<MountPointEntry> base_dirs_;
1388  std::map<std::string, std::string> file_extension_and_mimetype_map_;
1389  std::string default_file_mimetype_ = "application/octet-stream";
1390  Handler file_request_handler_;
1391
1392  Handlers get_handlers_;
1393  Handlers post_handlers_;
1394  HandlersForContentReader post_handlers_for_content_reader_;
1395  Handlers put_handlers_;
1396  HandlersForContentReader put_handlers_for_content_reader_;
1397  Handlers patch_handlers_;
1398  HandlersForContentReader patch_handlers_for_content_reader_;
1399  Handlers delete_handlers_;
1400  HandlersForContentReader delete_handlers_for_content_reader_;
1401  Handlers options_handlers_;
1402
1403  HandlerWithResponse error_handler_;
1404  ExceptionHandler exception_handler_;
1405  HandlerWithResponse pre_routing_handler_;
1406  Handler post_routing_handler_;
1407  HandlerWithResponse pre_request_handler_;
1408  Expect100ContinueHandler expect_100_continue_handler_;
1409
1410  mutable std::mutex logger_mutex_;
1411  Logger logger_;
1412  Logger pre_compression_logger_;
1413  ErrorLogger error_logger_;
1414
1415  int address_family_ = AF_UNSPEC;
1416  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1417  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1418  SocketOptions socket_options_ = default_socket_options;
1419
1420  Headers default_headers_;
1421  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1422      detail::write_headers;
1423};
1424
1425class Result {
1426public:
1427  Result() = default;
1428  Result(std::unique_ptr<Response> &&res, Error err,
1429         Headers &&request_headers = Headers{})
1430      : res_(std::move(res)), err_(err),
1431        request_headers_(std::move(request_headers)) {}
1432#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1433  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1434         int ssl_error)
1435      : res_(std::move(res)), err_(err),
1436        request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
1437  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1438         int ssl_error, unsigned long ssl_openssl_error)
1439      : res_(std::move(res)), err_(err),
1440        request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
1441        ssl_openssl_error_(ssl_openssl_error) {}
1442#endif
1443  // Response
1444  operator bool() const { return res_ != nullptr; }
1445  bool operator==(std::nullptr_t) const { return res_ == nullptr; }
1446  bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
1447  const Response &value() const { return *res_; }
1448  Response &value() { return *res_; }
1449  const Response &operator*() const { return *res_; }
1450  Response &operator*() { return *res_; }
1451  const Response *operator->() const { return res_.get(); }
1452  Response *operator->() { return res_.get(); }
1453
1454  // Error
1455  Error error() const { return err_; }
1456
1457#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1458  // SSL Error
1459  int ssl_error() const { return ssl_error_; }
1460  // OpenSSL Error
1461  unsigned long ssl_openssl_error() const { return ssl_openssl_error_; }
1462#endif
1463
1464  // Request Headers
1465  bool has_request_header(const std::string &key) const;
1466  std::string get_request_header_value(const std::string &key,
1467                                       const char *def = "",
1468                                       size_t id = 0) const;
1469  size_t get_request_header_value_u64(const std::string &key, size_t def = 0,
1470                                      size_t id = 0) const;
1471  size_t get_request_header_value_count(const std::string &key) const;
1472
1473private:
1474  std::unique_ptr<Response> res_;
1475  Error err_ = Error::Unknown;
1476  Headers request_headers_;
1477#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1478  int ssl_error_ = 0;
1479  unsigned long ssl_openssl_error_ = 0;
1480#endif
1481};
1482
1483struct ClientConnection {
1484  socket_t sock = INVALID_SOCKET;
1485#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1486  SSL *ssl = nullptr;
1487#endif
1488
1489  bool is_open() const { return sock != INVALID_SOCKET; }
1490
1491  ClientConnection() = default;
1492
1493  ~ClientConnection() {
1494#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1495    if (ssl) {
1496      SSL_free(ssl);
1497      ssl = nullptr;
1498    }
1499#endif
1500    if (sock != INVALID_SOCKET) {
1501      detail::close_socket(sock);
1502      sock = INVALID_SOCKET;
1503    }
1504  }
1505
1506  ClientConnection(const ClientConnection &) = delete;
1507  ClientConnection &operator=(const ClientConnection &) = delete;
1508
1509  ClientConnection(ClientConnection &&other) noexcept
1510      : sock(other.sock)
1511#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1512        ,
1513        ssl(other.ssl)
1514#endif
1515  {
1516    other.sock = INVALID_SOCKET;
1517#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1518    other.ssl = nullptr;
1519#endif
1520  }
1521
1522  ClientConnection &operator=(ClientConnection &&other) noexcept {
1523    if (this != &other) {
1524      sock = other.sock;
1525#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1526      ssl = other.ssl;
1527#endif
1528      other.sock = INVALID_SOCKET;
1529#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1530      other.ssl = nullptr;
1531#endif
1532    }
1533    return *this;
1534  }
1535};
1536
1537namespace detail {
1538
1539struct ChunkedDecoder;
1540
1541struct BodyReader {
1542  Stream *stream = nullptr;
1543  size_t content_length = 0;
1544  size_t bytes_read = 0;
1545  bool chunked = false;
1546  bool eof = false;
1547  std::unique_ptr<ChunkedDecoder> chunked_decoder;
1548  Error last_error = Error::Success;
1549
1550  ssize_t read(char *buf, size_t len);
1551  bool has_error() const { return last_error != Error::Success; }
1552};
1553
1554inline ssize_t read_body_content(Stream *stream, BodyReader &br, char *buf,
1555                                 size_t len) {
1556  (void)stream;
1557  return br.read(buf, len);
1558}
1559
1560class decompressor;
1561
1562} // namespace detail
1563
1564class ClientImpl {
1565public:
1566  explicit ClientImpl(const std::string &host);
1567
1568  explicit ClientImpl(const std::string &host, int port);
1569
1570  explicit ClientImpl(const std::string &host, int port,
1571                      const std::string &client_cert_path,
1572                      const std::string &client_key_path);
1573
1574  virtual ~ClientImpl();
1575
1576  virtual bool is_valid() const;
1577
1578  struct StreamHandle {
1579    std::unique_ptr<Response> response;
1580    Error error = Error::Success;
1581
1582    StreamHandle() = default;
1583    StreamHandle(const StreamHandle &) = delete;
1584    StreamHandle &operator=(const StreamHandle &) = delete;
1585    StreamHandle(StreamHandle &&) = default;
1586    StreamHandle &operator=(StreamHandle &&) = default;
1587    ~StreamHandle() = default;
1588
1589    bool is_valid() const {
1590      return response != nullptr && error == Error::Success;
1591    }
1592
1593    ssize_t read(char *buf, size_t len);
1594    void parse_trailers_if_needed();
1595    Error get_read_error() const { return body_reader_.last_error; }
1596    bool has_read_error() const { return body_reader_.has_error(); }
1597
1598    bool trailers_parsed_ = false;
1599
1600  private:
1601    friend class ClientImpl;
1602
1603    ssize_t read_with_decompression(char *buf, size_t len);
1604
1605    std::unique_ptr<ClientConnection> connection_;
1606    std::unique_ptr<Stream> socket_stream_;
1607    Stream *stream_ = nullptr;
1608    detail::BodyReader body_reader_;
1609
1610    std::unique_ptr<detail::decompressor> decompressor_;
1611    std::string decompress_buffer_;
1612    size_t decompress_offset_ = 0;
1613  };
1614
1615  // clang-format off
1616  Result Get(const std::string &path, DownloadProgress progress = nullptr);
1617  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1618  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1619  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1620  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1621  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1622  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1623  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1624  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1625
1626  Result Head(const std::string &path);
1627  Result Head(const std::string &path, const Headers &headers);
1628
1629  Result Post(const std::string &path);
1630  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1631  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1632  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1633  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1634  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1635  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1636  Result Post(const std::string &path, const Params &params);
1637  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1638  Result Post(const std::string &path, const Headers &headers);
1639  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1640  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1641  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1642  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1643  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1644  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1645  Result Post(const std::string &path, const Headers &headers, const Params &params);
1646  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1647  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1648  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1649  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1650
1651  Result Put(const std::string &path);
1652  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1653  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1654  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1655  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1656  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1657  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1658  Result Put(const std::string &path, const Params &params);
1659  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1660  Result Put(const std::string &path, const Headers &headers);
1661  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1662  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1663  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1664  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1665  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1666  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1667  Result Put(const std::string &path, const Headers &headers, const Params &params);
1668  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1669  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1670  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1671  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1672
1673  Result Patch(const std::string &path);
1674  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1675  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1676  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1677  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1678  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1679  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1680  Result Patch(const std::string &path, const Params &params);
1681  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1682  Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
1683  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1684  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1685  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1686  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1687  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1688  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1689  Result Patch(const std::string &path, const Headers &headers, const Params &params);
1690  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1691  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1692  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1693  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1694
1695  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
1696  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1697  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1698  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
1699  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1700  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1701  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1702  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
1703
1704  Result Options(const std::string &path);
1705  Result Options(const std::string &path, const Headers &headers);
1706  // clang-format on
1707
1708  // Streaming API: Open a stream for reading response body incrementally
1709  // Socket ownership is transferred to StreamHandle for true streaming
1710  // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
1711  StreamHandle open_stream(const std::string &method, const std::string &path,
1712                           const Params &params = {},
1713                           const Headers &headers = {},
1714                           const std::string &body = {},
1715                           const std::string &content_type = {});
1716
1717  bool send(Request &req, Response &res, Error &error);
1718  Result send(const Request &req);
1719
1720  void stop();
1721
1722  std::string host() const;
1723  int port() const;
1724
1725  size_t is_socket_open() const;
1726  socket_t socket() const;
1727
1728  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1729
1730  void set_default_headers(Headers headers);
1731
1732  void
1733  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1734
1735  void set_address_family(int family);
1736  void set_tcp_nodelay(bool on);
1737  void set_ipv6_v6only(bool on);
1738  void set_socket_options(SocketOptions socket_options);
1739
1740  void set_connection_timeout(time_t sec, time_t usec = 0);
1741  template <class Rep, class Period>
1742  void
1743  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1744
1745  void set_read_timeout(time_t sec, time_t usec = 0);
1746  template <class Rep, class Period>
1747  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1748
1749  void set_write_timeout(time_t sec, time_t usec = 0);
1750  template <class Rep, class Period>
1751  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1752
1753  void set_max_timeout(time_t msec);
1754  template <class Rep, class Period>
1755  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1756
1757  void set_basic_auth(const std::string &username, const std::string &password);
1758  void set_bearer_token_auth(const std::string &token);
1759#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1760  void set_digest_auth(const std::string &username,
1761                       const std::string &password);
1762#endif
1763
1764  void set_keep_alive(bool on);
1765  void set_follow_location(bool on);
1766
1767  void set_path_encode(bool on);
1768
1769  void set_compress(bool on);
1770
1771  void set_decompress(bool on);
1772
1773  void set_interface(const std::string &intf);
1774
1775  void set_proxy(const std::string &host, int port);
1776  void set_proxy_basic_auth(const std::string &username,
1777                            const std::string &password);
1778  void set_proxy_bearer_token_auth(const std::string &token);
1779#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1780  void set_proxy_digest_auth(const std::string &username,
1781                             const std::string &password);
1782#endif
1783
1784#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1785  void set_ca_cert_path(const std::string &ca_cert_file_path,
1786                        const std::string &ca_cert_dir_path = std::string());
1787  void set_ca_cert_store(X509_STORE *ca_cert_store);
1788  X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
1789#endif
1790
1791#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1792  void enable_server_certificate_verification(bool enabled);
1793  void enable_server_hostname_verification(bool enabled);
1794  void set_server_certificate_verifier(
1795      std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1796#endif
1797
1798  void set_logger(Logger logger);
1799  void set_error_logger(ErrorLogger error_logger);
1800
1801protected:
1802  struct Socket {
1803    socket_t sock = INVALID_SOCKET;
1804#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1805    SSL *ssl = nullptr;
1806#endif
1807
1808    bool is_open() const { return sock != INVALID_SOCKET; }
1809  };
1810
1811  virtual bool create_and_connect_socket(Socket &socket, Error &error);
1812  virtual bool ensure_socket_connection(Socket &socket, Error &error);
1813
1814  // All of:
1815  //   shutdown_ssl
1816  //   shutdown_socket
1817  //   close_socket
1818  // should ONLY be called when socket_mutex_ is locked.
1819  // Also, shutdown_ssl and close_socket should also NOT be called concurrently
1820  // with a DIFFERENT thread sending requests using that socket.
1821  virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
1822  void shutdown_socket(Socket &socket) const;
1823  void close_socket(Socket &socket);
1824
1825  bool process_request(Stream &strm, Request &req, Response &res,
1826                       bool close_connection, Error &error);
1827
1828  bool write_content_with_provider(Stream &strm, const Request &req,
1829                                   Error &error) const;
1830
1831  void copy_settings(const ClientImpl &rhs);
1832
1833  void output_log(const Request &req, const Response &res) const;
1834  void output_error_log(const Error &err, const Request *req) const;
1835
1836  // Socket endpoint information
1837  const std::string host_;
1838  const int port_;
1839
1840  // Current open socket
1841  Socket socket_;
1842  mutable std::mutex socket_mutex_;
1843  std::recursive_mutex request_mutex_;
1844
1845  // These are all protected under socket_mutex
1846  size_t socket_requests_in_flight_ = 0;
1847  std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1848  bool socket_should_be_closed_when_request_is_done_ = false;
1849
1850  // Hostname-IP map
1851  std::map<std::string, std::string> addr_map_;
1852
1853  // Default headers
1854  Headers default_headers_;
1855
1856  // Header writer
1857  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1858      detail::write_headers;
1859
1860  // Settings
1861  std::string client_cert_path_;
1862  std::string client_key_path_;
1863
1864  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1865  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1866  time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
1867  time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1868  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1869  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1870  time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;
1871
1872  std::string basic_auth_username_;
1873  std::string basic_auth_password_;
1874  std::string bearer_token_auth_token_;
1875#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1876  std::string digest_auth_username_;
1877  std::string digest_auth_password_;
1878#endif
1879
1880  bool keep_alive_ = false;
1881  bool follow_location_ = false;
1882
1883  bool path_encode_ = true;
1884
1885  int address_family_ = AF_UNSPEC;
1886  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1887  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1888  SocketOptions socket_options_ = nullptr;
1889
1890  bool compress_ = false;
1891  bool decompress_ = true;
1892
1893  std::string interface_;
1894
1895  std::string proxy_host_;
1896  int proxy_port_ = -1;
1897
1898  std::string proxy_basic_auth_username_;
1899  std::string proxy_basic_auth_password_;
1900  std::string proxy_bearer_token_auth_token_;
1901#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1902  std::string proxy_digest_auth_username_;
1903  std::string proxy_digest_auth_password_;
1904#endif
1905
1906#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1907  std::string ca_cert_file_path_;
1908  std::string ca_cert_dir_path_;
1909
1910  X509_STORE *ca_cert_store_ = nullptr;
1911#endif
1912
1913#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1914  bool server_certificate_verification_ = true;
1915  bool server_hostname_verification_ = true;
1916  std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
1917#endif
1918
1919  mutable std::mutex logger_mutex_;
1920  Logger logger_;
1921  ErrorLogger error_logger_;
1922
1923#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1924  int last_ssl_error_ = 0;
1925  unsigned long last_openssl_error_ = 0;
1926#endif
1927
1928private:
1929  bool send_(Request &req, Response &res, Error &error);
1930  Result send_(Request &&req);
1931
1932  socket_t create_client_socket(Error &error) const;
1933  bool read_response_line(Stream &strm, const Request &req, Response &res,
1934                          bool skip_100_continue = true) const;
1935  bool write_request(Stream &strm, Request &req, bool close_connection,
1936                     Error &error, bool skip_body = false);
1937  bool write_request_body(Stream &strm, Request &req, Error &error);
1938  void prepare_default_headers(Request &r, bool for_stream,
1939                               const std::string &ct);
1940  bool redirect(Request &req, Response &res, Error &error);
1941  bool create_redirect_client(const std::string &scheme,
1942                              const std::string &host, int port, Request &req,
1943                              Response &res, const std::string &path,
1944                              const std::string &location, Error &error);
1945  template <typename ClientType> void setup_redirect_client(ClientType &client);
1946  bool handle_request(Stream &strm, Request &req, Response &res,
1947                      bool close_connection, Error &error);
1948  std::unique_ptr<Response> send_with_content_provider_and_receiver(
1949      Request &req, const char *body, size_t content_length,
1950      ContentProvider content_provider,
1951      ContentProviderWithoutLength content_provider_without_length,
1952      const std::string &content_type, ContentReceiver content_receiver,
1953      Error &error);
1954  Result send_with_content_provider_and_receiver(
1955      const std::string &method, const std::string &path,
1956      const Headers &headers, const char *body, size_t content_length,
1957      ContentProvider content_provider,
1958      ContentProviderWithoutLength content_provider_without_length,
1959      const std::string &content_type, ContentReceiver content_receiver,
1960      UploadProgress progress);
1961  ContentProviderWithoutLength get_multipart_content_provider(
1962      const std::string &boundary, const UploadFormDataItems &items,
1963      const FormDataProviderItems &provider_items) const;
1964
1965  virtual bool
1966  process_socket(const Socket &socket,
1967                 std::chrono::time_point<std::chrono::steady_clock> start_time,
1968                 std::function<bool(Stream &strm)> callback);
1969  virtual bool is_ssl() const;
1970
1971  void transfer_socket_ownership_to_handle(StreamHandle &handle);
1972};
1973
1974class Client {
1975public:
1976  // Universal interface
1977  explicit Client(const std::string &scheme_host_port);
1978
1979  explicit Client(const std::string &scheme_host_port,
1980                  const std::string &client_cert_path,
1981                  const std::string &client_key_path);
1982
1983  // HTTP only interface
1984  explicit Client(const std::string &host, int port);
1985
1986  explicit Client(const std::string &host, int port,
1987                  const std::string &client_cert_path,
1988                  const std::string &client_key_path);
1989
1990  Client(Client &&) = default;
1991  Client &operator=(Client &&) = default;
1992
1993  ~Client();
1994
1995  bool is_valid() const;
1996
1997  // clang-format off
1998  Result Get(const std::string &path, DownloadProgress progress = nullptr);
1999  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2000  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2001  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2002  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2003  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2004  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
2005  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2006  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2007
2008  Result Head(const std::string &path);
2009  Result Head(const std::string &path, const Headers &headers);
2010
2011  Result Post(const std::string &path);
2012  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2013  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2014  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2015  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2016  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2017  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2018  Result Post(const std::string &path, const Params &params);
2019  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2020  Result Post(const std::string &path, const Headers &headers);
2021  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2022  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2023  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2024  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2025  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2026  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2027  Result Post(const std::string &path, const Headers &headers, const Params &params);
2028  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2029  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2030  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2031  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2032
2033  Result Put(const std::string &path);
2034  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2035  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2036  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2037  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2038  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2039  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2040  Result Put(const std::string &path, const Params &params);
2041  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2042  Result Put(const std::string &path, const Headers &headers);
2043  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2044  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2045  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2046  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2047  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2048  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2049  Result Put(const std::string &path, const Headers &headers, const Params &params);
2050  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2051  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2052  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2053  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2054
2055  Result Patch(const std::string &path);
2056  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2057  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2058  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2059  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2060  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2061  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2062  Result Patch(const std::string &path, const Params &params);
2063  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2064  Result Patch(const std::string &path, const Headers &headers);
2065  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2066  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2067  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2068  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2069  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2070  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2071  Result Patch(const std::string &path, const Headers &headers, const Params &params);
2072  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2073  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2074  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2075  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2076
2077  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
2078  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2079  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2080  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
2081  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2082  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2083  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2084  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
2085
2086  Result Options(const std::string &path);
2087  Result Options(const std::string &path, const Headers &headers);
2088  // clang-format on
2089
2090  // Streaming API: Open a stream for reading response body incrementally
2091  // Socket ownership is transferred to StreamHandle for true streaming
2092  // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
2093  ClientImpl::StreamHandle open_stream(const std::string &method,
2094                                       const std::string &path,
2095                                       const Params &params = {},
2096                                       const Headers &headers = {},
2097                                       const std::string &body = {},
2098                                       const std::string &content_type = {});
2099
2100  bool send(Request &req, Response &res, Error &error);
2101  Result send(const Request &req);
2102
2103  void stop();
2104
2105  std::string host() const;
2106  int port() const;
2107
2108  size_t is_socket_open() const;
2109  socket_t socket() const;
2110
2111  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
2112
2113  void set_default_headers(Headers headers);
2114
2115  void
2116  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
2117
2118  void set_address_family(int family);
2119  void set_tcp_nodelay(bool on);
2120  void set_socket_options(SocketOptions socket_options);
2121
2122  void set_connection_timeout(time_t sec, time_t usec = 0);
2123  template <class Rep, class Period>
2124  void
2125  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
2126
2127  void set_read_timeout(time_t sec, time_t usec = 0);
2128  template <class Rep, class Period>
2129  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
2130
2131  void set_write_timeout(time_t sec, time_t usec = 0);
2132  template <class Rep, class Period>
2133  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
2134
2135  void set_max_timeout(time_t msec);
2136  template <class Rep, class Period>
2137  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
2138
2139  void set_basic_auth(const std::string &username, const std::string &password);
2140  void set_bearer_token_auth(const std::string &token);
2141#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2142  void set_digest_auth(const std::string &username,
2143                       const std::string &password);
2144#endif
2145
2146  void set_keep_alive(bool on);
2147  void set_follow_location(bool on);
2148
2149  void set_path_encode(bool on);
2150  void set_url_encode(bool on);
2151
2152  void set_compress(bool on);
2153
2154  void set_decompress(bool on);
2155
2156  void set_interface(const std::string &intf);
2157
2158  void set_proxy(const std::string &host, int port);
2159  void set_proxy_basic_auth(const std::string &username,
2160                            const std::string &password);
2161  void set_proxy_bearer_token_auth(const std::string &token);
2162#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2163  void set_proxy_digest_auth(const std::string &username,
2164                             const std::string &password);
2165#endif
2166
2167#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2168  void enable_server_certificate_verification(bool enabled);
2169  void enable_server_hostname_verification(bool enabled);
2170  void set_server_certificate_verifier(
2171      std::function<SSLVerifierResponse(SSL *ssl)> verifier);
2172#endif
2173
2174  void set_logger(Logger logger);
2175  void set_error_logger(ErrorLogger error_logger);
2176
2177  // SSL
2178#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2179  void set_ca_cert_path(const std::string &ca_cert_file_path,
2180                        const std::string &ca_cert_dir_path = std::string());
2181
2182  void set_ca_cert_store(X509_STORE *ca_cert_store);
2183  void load_ca_cert_store(const char *ca_cert, std::size_t size);
2184
2185  long get_openssl_verify_result() const;
2186
2187  SSL_CTX *ssl_context() const;
2188#endif
2189
2190private:
2191  std::unique_ptr<ClientImpl> cli_;
2192
2193#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2194  bool is_ssl_ = false;
2195#endif
2196};
2197
2198#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2199class SSLServer : public Server {
2200public:
2201  SSLServer(const char *cert_path, const char *private_key_path,
2202            const char *client_ca_cert_file_path = nullptr,
2203            const char *client_ca_cert_dir_path = nullptr,
2204            const char *private_key_password = nullptr);
2205
2206  SSLServer(X509 *cert, EVP_PKEY *private_key,
2207            X509_STORE *client_ca_cert_store = nullptr);
2208
2209  SSLServer(
2210      const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
2211
2212  ~SSLServer() override;
2213
2214  bool is_valid() const override;
2215
2216  SSL_CTX *ssl_context() const;
2217
2218  void update_certs(X509 *cert, EVP_PKEY *private_key,
2219                    X509_STORE *client_ca_cert_store = nullptr);
2220
2221  int ssl_last_error() const { return last_ssl_error_; }
2222
2223private:
2224  bool process_and_close_socket(socket_t sock) override;
2225
2226  STACK_OF(X509_NAME) * extract_ca_names_from_x509_store(X509_STORE *store);
2227
2228  SSL_CTX *ctx_;
2229  std::mutex ctx_mutex_;
2230
2231  int last_ssl_error_ = 0;
2232};
2233
2234class SSLClient final : public ClientImpl {
2235public:
2236  explicit SSLClient(const std::string &host);
2237
2238  explicit SSLClient(const std::string &host, int port);
2239
2240  explicit SSLClient(const std::string &host, int port,
2241                     const std::string &client_cert_path,
2242                     const std::string &client_key_path,
2243                     const std::string &private_key_password = std::string());
2244
2245  explicit SSLClient(const std::string &host, int port, X509 *client_cert,
2246                     EVP_PKEY *client_key,
2247                     const std::string &private_key_password = std::string());
2248
2249  ~SSLClient() override;
2250
2251  bool is_valid() const override;
2252
2253  void set_ca_cert_store(X509_STORE *ca_cert_store);
2254  void load_ca_cert_store(const char *ca_cert, std::size_t size);
2255
2256  long get_openssl_verify_result() const;
2257
2258  SSL_CTX *ssl_context() const;
2259
2260private:
2261  bool create_and_connect_socket(Socket &socket, Error &error) override;
2262  bool ensure_socket_connection(Socket &socket, Error &error) override;
2263  void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
2264  void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
2265
2266  bool
2267  process_socket(const Socket &socket,
2268                 std::chrono::time_point<std::chrono::steady_clock> start_time,
2269                 std::function<bool(Stream &strm)> callback) override;
2270  bool is_ssl() const override;
2271
2272  bool connect_with_proxy(
2273      Socket &sock,
2274      std::chrono::time_point<std::chrono::steady_clock> start_time,
2275      Response &res, bool &success, Error &error);
2276  bool initialize_ssl(Socket &socket, Error &error);
2277
2278  bool load_certs();
2279
2280  bool verify_host(X509 *server_cert) const;
2281  bool verify_host_with_subject_alt_name(X509 *server_cert) const;
2282  bool verify_host_with_common_name(X509 *server_cert) const;
2283  bool check_host_name(const char *pattern, size_t pattern_len) const;
2284
2285  SSL_CTX *ctx_;
2286  std::mutex ctx_mutex_;
2287  std::once_flag initialize_cert_;
2288
2289  std::vector<std::string> host_components_;
2290
2291  long verify_result_ = 0;
2292
2293  friend class ClientImpl;
2294};
2295#endif
2296
2297/*
2298 * Implementation of template methods.
2299 */
2300
2301namespace detail {
2302
2303template <typename T, typename U>
2304inline void duration_to_sec_and_usec(const T &duration, U callback) {
2305  auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2306  auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2307                  duration - std::chrono::seconds(sec))
2308                  .count();
2309  callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
2310}
2311
2312template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
2313  return N - 1;
2314}
2315
2316inline bool is_numeric(const std::string &str) {
2317  return !str.empty() &&
2318         std::all_of(str.cbegin(), str.cend(),
2319                     [](unsigned char c) { return std::isdigit(c); });
2320}
2321
2322inline size_t get_header_value_u64(const Headers &headers,
2323                                   const std::string &key, size_t def,
2324                                   size_t id, bool &is_invalid_value) {
2325  is_invalid_value = false;
2326  auto rng = headers.equal_range(key);
2327  auto it = rng.first;
2328  std::advance(it, static_cast<ssize_t>(id));
2329  if (it != rng.second) {
2330    if (is_numeric(it->second)) {
2331      return std::strtoull(it->second.data(), nullptr, 10);
2332    } else {
2333      is_invalid_value = true;
2334    }
2335  }
2336  return def;
2337}
2338
2339inline size_t get_header_value_u64(const Headers &headers,
2340                                   const std::string &key, size_t def,
2341                                   size_t id) {
2342  auto dummy = false;
2343  return get_header_value_u64(headers, key, def, id, dummy);
2344}
2345
2346} // namespace detail
2347
2348inline size_t Request::get_header_value_u64(const std::string &key, size_t def,
2349                                            size_t id) const {
2350  return detail::get_header_value_u64(headers, key, def, id);
2351}
2352
2353inline size_t Response::get_header_value_u64(const std::string &key, size_t def,
2354                                             size_t id) const {
2355  return detail::get_header_value_u64(headers, key, def, id);
2356}
2357
2358namespace detail {
2359
2360inline bool set_socket_opt_impl(socket_t sock, int level, int optname,
2361                                const void *optval, socklen_t optlen) {
2362  return setsockopt(sock, level, optname,
2363#ifdef _WIN32
2364                    reinterpret_cast<const char *>(optval),
2365#else
2366                    optval,
2367#endif
2368                    optlen) == 0;
2369}
2370
2371inline bool set_socket_opt(socket_t sock, int level, int optname, int optval) {
2372  return set_socket_opt_impl(sock, level, optname, &optval, sizeof(optval));
2373}
2374
2375inline bool set_socket_opt_time(socket_t sock, int level, int optname,
2376                                time_t sec, time_t usec) {
2377#ifdef _WIN32
2378  auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);
2379#else
2380  timeval timeout;
2381  timeout.tv_sec = static_cast<long>(sec);
2382  timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);
2383#endif
2384  return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));
2385}
2386
2387} // namespace detail
2388
2389inline void default_socket_options(socket_t sock) {
2390  detail::set_socket_opt(sock, SOL_SOCKET,
2391#ifdef SO_REUSEPORT
2392                         SO_REUSEPORT,
2393#else
2394                         SO_REUSEADDR,
2395#endif
2396                         1);
2397}
2398
2399inline std::string get_bearer_token_auth(const Request &req) {
2400  if (req.has_header("Authorization")) {
2401    constexpr auto bearer_header_prefix_len = detail::str_len("Bearer ");
2402    return req.get_header_value("Authorization")
2403        .substr(bearer_header_prefix_len);
2404  }
2405  return "";
2406}
2407
2408template <class Rep, class Period>
2409inline Server &
2410Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2411  detail::duration_to_sec_and_usec(
2412      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2413  return *this;
2414}
2415
2416template <class Rep, class Period>
2417inline Server &
2418Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2419  detail::duration_to_sec_and_usec(
2420      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2421  return *this;
2422}
2423
2424template <class Rep, class Period>
2425inline Server &
2426Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
2427  detail::duration_to_sec_and_usec(
2428      duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
2429  return *this;
2430}
2431
2432inline size_t Result::get_request_header_value_u64(const std::string &key,
2433                                                   size_t def,
2434                                                   size_t id) const {
2435  return detail::get_header_value_u64(request_headers_, key, def, id);
2436}
2437
2438template <class Rep, class Period>
2439inline void ClientImpl::set_connection_timeout(
2440    const std::chrono::duration<Rep, Period> &duration) {
2441  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
2442    set_connection_timeout(sec, usec);
2443  });
2444}
2445
2446template <class Rep, class Period>
2447inline void ClientImpl::set_read_timeout(
2448    const std::chrono::duration<Rep, Period> &duration) {
2449  detail::duration_to_sec_and_usec(
2450      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2451}
2452
2453template <class Rep, class Period>
2454inline void ClientImpl::set_write_timeout(
2455    const std::chrono::duration<Rep, Period> &duration) {
2456  detail::duration_to_sec_and_usec(
2457      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2458}
2459
2460template <class Rep, class Period>
2461inline void ClientImpl::set_max_timeout(
2462    const std::chrono::duration<Rep, Period> &duration) {
2463  auto msec =
2464      std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2465  set_max_timeout(msec);
2466}
2467
2468template <class Rep, class Period>
2469inline void Client::set_connection_timeout(
2470    const std::chrono::duration<Rep, Period> &duration) {
2471  cli_->set_connection_timeout(duration);
2472}
2473
2474template <class Rep, class Period>
2475inline void
2476Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2477  cli_->set_read_timeout(duration);
2478}
2479
2480template <class Rep, class Period>
2481inline void
2482Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2483  cli_->set_write_timeout(duration);
2484}
2485
2486inline void Client::set_max_timeout(time_t msec) {
2487  cli_->set_max_timeout(msec);
2488}
2489
2490template <class Rep, class Period>
2491inline void
2492Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
2493  cli_->set_max_timeout(duration);
2494}
2495
2496/*
2497 * Forward declarations and types that will be part of the .h file if split into
2498 * .h + .cc.
2499 */
2500
2501std::string hosted_at(const std::string &hostname);
2502
2503void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
2504
2505// JavaScript-style URL encoding/decoding functions
2506std::string encode_uri_component(const std::string &value);
2507std::string encode_uri(const std::string &value);
2508std::string decode_uri_component(const std::string &value);
2509std::string decode_uri(const std::string &value);
2510
2511// RFC 3986 compliant URL component encoding/decoding functions
2512std::string encode_path_component(const std::string &component);
2513std::string decode_path_component(const std::string &component);
2514std::string encode_query_component(const std::string &component,
2515                                   bool space_as_plus = true);
2516std::string decode_query_component(const std::string &component,
2517                                   bool plus_as_space = true);
2518
2519std::string append_query_params(const std::string &path, const Params &params);
2520
2521std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
2522
2523std::pair<std::string, std::string>
2524make_basic_authentication_header(const std::string &username,
2525                                 const std::string &password,
2526                                 bool is_proxy = false);
2527
2528namespace detail {
2529
2530#if defined(_WIN32)
2531inline std::wstring u8string_to_wstring(const char *s) {
2532  if (!s) { return std::wstring(); }
2533
2534  auto len = static_cast<int>(strlen(s));
2535  if (!len) { return std::wstring(); }
2536
2537  auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
2538  if (!wlen) { return std::wstring(); }
2539
2540  std::wstring ws;
2541  ws.resize(wlen);
2542  wlen = ::MultiByteToWideChar(
2543      CP_UTF8, 0, s, len,
2544      const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
2545  if (wlen != static_cast<int>(ws.size())) { ws.clear(); }
2546  return ws;
2547}
2548#endif
2549
2550struct FileStat {
2551  FileStat(const std::string &path);
2552  bool is_file() const;
2553  bool is_dir() const;
2554  time_t mtime() const;
2555  size_t size() const;
2556
2557private:
2558#if defined(_WIN32)
2559  struct _stat st_;
2560#else
2561  struct stat st_;
2562#endif
2563  int ret_ = -1;
2564};
2565
2566std::string make_host_and_port_string(const std::string &host, int port,
2567                                      bool is_ssl);
2568
2569std::string trim_copy(const std::string &s);
2570
2571void divide(
2572    const char *data, std::size_t size, char d,
2573    std::function<void(const char *, std::size_t, const char *, std::size_t)>
2574        fn);
2575
2576void divide(
2577    const std::string &str, char d,
2578    std::function<void(const char *, std::size_t, const char *, std::size_t)>
2579        fn);
2580
2581void split(const char *b, const char *e, char d,
2582           std::function<void(const char *, const char *)> fn);
2583
2584void split(const char *b, const char *e, char d, size_t m,
2585           std::function<void(const char *, const char *)> fn);
2586
2587bool process_client_socket(
2588    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2589    time_t write_timeout_sec, time_t write_timeout_usec,
2590    time_t max_timeout_msec,
2591    std::chrono::time_point<std::chrono::steady_clock> start_time,
2592    std::function<bool(Stream &)> callback);
2593
2594socket_t create_client_socket(const std::string &host, const std::string &ip,
2595                              int port, int address_family, bool tcp_nodelay,
2596                              bool ipv6_v6only, SocketOptions socket_options,
2597                              time_t connection_timeout_sec,
2598                              time_t connection_timeout_usec,
2599                              time_t read_timeout_sec, time_t read_timeout_usec,
2600                              time_t write_timeout_sec,
2601                              time_t write_timeout_usec,
2602                              const std::string &intf, Error &error);
2603
2604const char *get_header_value(const Headers &headers, const std::string &key,
2605                             const char *def, size_t id);
2606
2607std::string params_to_query_str(const Params &params);
2608
2609void parse_query_text(const char *data, std::size_t size, Params &params);
2610
2611void parse_query_text(const std::string &s, Params &params);
2612
2613bool parse_multipart_boundary(const std::string &content_type,
2614                              std::string &boundary);
2615
2616bool parse_range_header(const std::string &s, Ranges &ranges);
2617
2618bool parse_accept_header(const std::string &s,
2619                         std::vector<std::string> &content_types);
2620
2621int close_socket(socket_t sock);
2622
2623ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2624
2625ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
2626
2627enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
2628
2629EncodingType encoding_type(const Request &req, const Response &res);
2630
2631class BufferStream final : public Stream {
2632public:
2633  BufferStream() = default;
2634  ~BufferStream() override = default;
2635
2636  bool is_readable() const override;
2637  bool wait_readable() const override;
2638  bool wait_writable() const override;
2639  ssize_t read(char *ptr, size_t size) override;
2640  ssize_t write(const char *ptr, size_t size) override;
2641  void get_remote_ip_and_port(std::string &ip, int &port) const override;
2642  void get_local_ip_and_port(std::string &ip, int &port) const override;
2643  socket_t socket() const override;
2644  time_t duration() const override;
2645
2646  const std::string &get_buffer() const;
2647
2648private:
2649  std::string buffer;
2650  size_t position = 0;
2651};
2652
2653class compressor {
2654public:
2655  virtual ~compressor() = default;
2656
2657  typedef std::function<bool(const char *data, size_t data_len)> Callback;
2658  virtual bool compress(const char *data, size_t data_length, bool last,
2659                        Callback callback) = 0;
2660};
2661
2662class decompressor {
2663public:
2664  virtual ~decompressor() = default;
2665
2666  virtual bool is_valid() const = 0;
2667
2668  typedef std::function<bool(const char *data, size_t data_len)> Callback;
2669  virtual bool decompress(const char *data, size_t data_length,
2670                          Callback callback) = 0;
2671};
2672
2673class nocompressor final : public compressor {
2674public:
2675  ~nocompressor() override = default;
2676
2677  bool compress(const char *data, size_t data_length, bool /*last*/,
2678                Callback callback) override;
2679};
2680
2681#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2682class gzip_compressor final : public compressor {
2683public:
2684  gzip_compressor();
2685  ~gzip_compressor() override;
2686
2687  bool compress(const char *data, size_t data_length, bool last,
2688                Callback callback) override;
2689
2690private:
2691  bool is_valid_ = false;
2692  z_stream strm_;
2693};
2694
2695class gzip_decompressor final : public decompressor {
2696public:
2697  gzip_decompressor();
2698  ~gzip_decompressor() override;
2699
2700  bool is_valid() const override;
2701
2702  bool decompress(const char *data, size_t data_length,
2703                  Callback callback) override;
2704
2705private:
2706  bool is_valid_ = false;
2707  z_stream strm_;
2708};
2709#endif
2710
2711#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2712class brotli_compressor final : public compressor {
2713public:
2714  brotli_compressor();
2715  ~brotli_compressor();
2716
2717  bool compress(const char *data, size_t data_length, bool last,
2718                Callback callback) override;
2719
2720private:
2721  BrotliEncoderState *state_ = nullptr;
2722};
2723
2724class brotli_decompressor final : public decompressor {
2725public:
2726  brotli_decompressor();
2727  ~brotli_decompressor();
2728
2729  bool is_valid() const override;
2730
2731  bool decompress(const char *data, size_t data_length,
2732                  Callback callback) override;
2733
2734private:
2735  BrotliDecoderResult decoder_r;
2736  BrotliDecoderState *decoder_s = nullptr;
2737};
2738#endif
2739
2740#ifdef CPPHTTPLIB_ZSTD_SUPPORT
2741class zstd_compressor : public compressor {
2742public:
2743  zstd_compressor();
2744  ~zstd_compressor();
2745
2746  bool compress(const char *data, size_t data_length, bool last,
2747                Callback callback) override;
2748
2749private:
2750  ZSTD_CCtx *ctx_ = nullptr;
2751};
2752
2753class zstd_decompressor : public decompressor {
2754public:
2755  zstd_decompressor();
2756  ~zstd_decompressor();
2757
2758  bool is_valid() const override;
2759
2760  bool decompress(const char *data, size_t data_length,
2761                  Callback callback) override;
2762
2763private:
2764  ZSTD_DCtx *ctx_ = nullptr;
2765};
2766#endif
2767
2768// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
2769// to store data. The call can set memory on stack for performance.
2770class stream_line_reader {
2771public:
2772  stream_line_reader(Stream &strm, char *fixed_buffer,
2773                     size_t fixed_buffer_size);
2774  const char *ptr() const;
2775  size_t size() const;
2776  bool end_with_crlf() const;
2777  bool getline();
2778
2779private:
2780  void append(char c);
2781
2782  Stream &strm_;
2783  char *fixed_buffer_;
2784  const size_t fixed_buffer_size_;
2785  size_t fixed_buffer_used_size_ = 0;
2786  std::string growable_buffer_;
2787};
2788
2789bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
2790                    const Headers &src_headers);
2791
2792struct ChunkedDecoder {
2793  Stream &strm;
2794  size_t chunk_remaining = 0;
2795  bool finished = false;
2796  char line_buf[64];
2797  size_t last_chunk_total = 0;
2798  size_t last_chunk_offset = 0;
2799
2800  explicit ChunkedDecoder(Stream &s);
2801
2802  ssize_t read_payload(char *buf, size_t len, size_t &out_chunk_offset,
2803                       size_t &out_chunk_total);
2804
2805  bool parse_trailers_into(Headers &dest, const Headers &src_headers);
2806};
2807
2808class mmap {
2809public:
2810  mmap(const char *path);
2811  ~mmap();
2812
2813  bool open(const char *path);
2814  void close();
2815
2816  bool is_open() const;
2817  size_t size() const;
2818  const char *data() const;
2819
2820private:
2821#if defined(_WIN32)
2822  HANDLE hFile_ = NULL;
2823  HANDLE hMapping_ = NULL;
2824#else
2825  int fd_ = -1;
2826#endif
2827  size_t size_ = 0;
2828  void *addr_ = nullptr;
2829  bool is_open_empty_file = false;
2830};
2831
2832// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
2833namespace fields {
2834
2835bool is_token_char(char c);
2836bool is_token(const std::string &s);
2837bool is_field_name(const std::string &s);
2838bool is_vchar(char c);
2839bool is_obs_text(char c);
2840bool is_field_vchar(char c);
2841bool is_field_content(const std::string &s);
2842bool is_field_value(const std::string &s);
2843
2844} // namespace fields
2845
2846} // namespace detail
2847
2848namespace stream {
2849
2850class Result {
2851public:
2852  Result() : chunk_size_(8192) {}
2853
2854  explicit Result(ClientImpl::StreamHandle &&handle, size_t chunk_size = 8192)
2855      : handle_(std::move(handle)), chunk_size_(chunk_size) {}
2856
2857  Result(Result &&other) noexcept
2858      : handle_(std::move(other.handle_)), buffer_(std::move(other.buffer_)),
2859        current_size_(other.current_size_), chunk_size_(other.chunk_size_),
2860        finished_(other.finished_) {
2861    other.current_size_ = 0;
2862    other.finished_ = true;
2863  }
2864
2865  Result &operator=(Result &&other) noexcept {
2866    if (this != &other) {
2867      handle_ = std::move(other.handle_);
2868      buffer_ = std::move(other.buffer_);
2869      current_size_ = other.current_size_;
2870      chunk_size_ = other.chunk_size_;
2871      finished_ = other.finished_;
2872      other.current_size_ = 0;
2873      other.finished_ = true;
2874    }
2875    return *this;
2876  }
2877
2878  Result(const Result &) = delete;
2879  Result &operator=(const Result &) = delete;
2880
2881  // Check if the result is valid (connection succeeded and response received)
2882  bool is_valid() const { return handle_.is_valid(); }
2883  explicit operator bool() const { return is_valid(); }
2884
2885  // Response status code
2886  int status() const {
2887    return handle_.response ? handle_.response->status : -1;
2888  }
2889
2890  // Response headers
2891  const Headers &headers() const {
2892    static const Headers empty_headers;
2893    return handle_.response ? handle_.response->headers : empty_headers;
2894  }
2895
2896  std::string get_header_value(const std::string &key,
2897                               const char *def = "") const {
2898    return handle_.response ? handle_.response->get_header_value(key, def)
2899                            : def;
2900  }
2901
2902  bool has_header(const std::string &key) const {
2903    return handle_.response ? handle_.response->has_header(key) : false;
2904  }
2905
2906  // Error information
2907  Error error() const { return handle_.error; }
2908  Error read_error() const { return handle_.get_read_error(); }
2909  bool has_read_error() const { return handle_.has_read_error(); }
2910
2911  // Streaming iteration API
2912  // Call next() to read the next chunk, then access data via data()/size()
2913  // Returns true if data was read, false when stream is exhausted
2914  bool next() {
2915    if (!handle_.is_valid() || finished_) { return false; }
2916
2917    if (buffer_.size() < chunk_size_) { buffer_.resize(chunk_size_); }
2918
2919    ssize_t n = handle_.read(&buffer_[0], chunk_size_);
2920    if (n > 0) {
2921      current_size_ = static_cast<size_t>(n);
2922      return true;
2923    }
2924
2925    current_size_ = 0;
2926    finished_ = true;
2927    return false;
2928  }
2929
2930  // Pointer to current chunk data (valid after next() returns true)
2931  const char *data() const { return buffer_.data(); }
2932
2933  // Size of current chunk (valid after next() returns true)
2934  size_t size() const { return current_size_; }
2935
2936  // Convenience method: read all remaining data into a string
2937  std::string read_all() {
2938    std::string result;
2939    while (next()) {
2940      result.append(data(), size());
2941    }
2942    return result;
2943  }
2944
2945private:
2946  ClientImpl::StreamHandle handle_;
2947  std::string buffer_;
2948  size_t current_size_ = 0;
2949  size_t chunk_size_;
2950  bool finished_ = false;
2951};
2952
2953// GET
2954template <typename ClientType>
2955inline Result Get(ClientType &cli, const std::string &path,
2956                  size_t chunk_size = 8192) {
2957  return Result{cli.open_stream("GET", path), chunk_size};
2958}
2959
2960template <typename ClientType>
2961inline Result Get(ClientType &cli, const std::string &path,
2962                  const Headers &headers, size_t chunk_size = 8192) {
2963  return Result{cli.open_stream("GET", path, {}, headers), chunk_size};
2964}
2965
2966template <typename ClientType>
2967inline Result Get(ClientType &cli, const std::string &path,
2968                  const Params &params, size_t chunk_size = 8192) {
2969  return Result{cli.open_stream("GET", path, params), chunk_size};
2970}
2971
2972template <typename ClientType>
2973inline Result Get(ClientType &cli, const std::string &path,
2974                  const Params &params, const Headers &headers,
2975                  size_t chunk_size = 8192) {
2976  return Result{cli.open_stream("GET", path, params, headers), chunk_size};
2977}
2978
2979// POST
2980template <typename ClientType>
2981inline Result Post(ClientType &cli, const std::string &path,
2982                   const std::string &body, const std::string &content_type,
2983                   size_t chunk_size = 8192) {
2984  return Result{cli.open_stream("POST", path, {}, {}, body, content_type),
2985                chunk_size};
2986}
2987
2988template <typename ClientType>
2989inline Result Post(ClientType &cli, const std::string &path,
2990                   const Headers &headers, const std::string &body,
2991                   const std::string &content_type, size_t chunk_size = 8192) {
2992  return Result{cli.open_stream("POST", path, {}, headers, body, content_type),
2993                chunk_size};
2994}
2995
2996template <typename ClientType>
2997inline Result Post(ClientType &cli, const std::string &path,
2998                   const Params &params, const std::string &body,
2999                   const std::string &content_type, size_t chunk_size = 8192) {
3000  return Result{cli.open_stream("POST", path, params, {}, body, content_type),
3001                chunk_size};
3002}
3003
3004template <typename ClientType>
3005inline Result Post(ClientType &cli, const std::string &path,
3006                   const Params &params, const Headers &headers,
3007                   const std::string &body, const std::string &content_type,
3008                   size_t chunk_size = 8192) {
3009  return Result{
3010      cli.open_stream("POST", path, params, headers, body, content_type),
3011      chunk_size};
3012}
3013
3014// PUT
3015template <typename ClientType>
3016inline Result Put(ClientType &cli, const std::string &path,
3017                  const std::string &body, const std::string &content_type,
3018                  size_t chunk_size = 8192) {
3019  return Result{cli.open_stream("PUT", path, {}, {}, body, content_type),
3020                chunk_size};
3021}
3022
3023template <typename ClientType>
3024inline Result Put(ClientType &cli, const std::string &path,
3025                  const Headers &headers, const std::string &body,
3026                  const std::string &content_type, size_t chunk_size = 8192) {
3027  return Result{cli.open_stream("PUT", path, {}, headers, body, content_type),
3028                chunk_size};
3029}
3030
3031template <typename ClientType>
3032inline Result Put(ClientType &cli, const std::string &path,
3033                  const Params &params, const std::string &body,
3034                  const std::string &content_type, size_t chunk_size = 8192) {
3035  return Result{cli.open_stream("PUT", path, params, {}, body, content_type),
3036                chunk_size};
3037}
3038
3039template <typename ClientType>
3040inline Result Put(ClientType &cli, const std::string &path,
3041                  const Params &params, const Headers &headers,
3042                  const std::string &body, const std::string &content_type,
3043                  size_t chunk_size = 8192) {
3044  return Result{
3045      cli.open_stream("PUT", path, params, headers, body, content_type),
3046      chunk_size};
3047}
3048
3049// PATCH
3050template <typename ClientType>
3051inline Result Patch(ClientType &cli, const std::string &path,
3052                    const std::string &body, const std::string &content_type,
3053                    size_t chunk_size = 8192) {
3054  return Result{cli.open_stream("PATCH", path, {}, {}, body, content_type),
3055                chunk_size};
3056}
3057
3058template <typename ClientType>
3059inline Result Patch(ClientType &cli, const std::string &path,
3060                    const Headers &headers, const std::string &body,
3061                    const std::string &content_type, size_t chunk_size = 8192) {
3062  return Result{cli.open_stream("PATCH", path, {}, headers, body, content_type),
3063                chunk_size};
3064}
3065
3066template <typename ClientType>
3067inline Result Patch(ClientType &cli, const std::string &path,
3068                    const Params &params, const std::string &body,
3069                    const std::string &content_type, size_t chunk_size = 8192) {
3070  return Result{cli.open_stream("PATCH", path, params, {}, body, content_type),
3071                chunk_size};
3072}
3073
3074template <typename ClientType>
3075inline Result Patch(ClientType &cli, const std::string &path,
3076                    const Params &params, const Headers &headers,
3077                    const std::string &body, const std::string &content_type,
3078                    size_t chunk_size = 8192) {
3079  return Result{
3080      cli.open_stream("PATCH", path, params, headers, body, content_type),
3081      chunk_size};
3082}
3083
3084// DELETE
3085template <typename ClientType>
3086inline Result Delete(ClientType &cli, const std::string &path,
3087                     size_t chunk_size = 8192) {
3088  return Result{cli.open_stream("DELETE", path), chunk_size};
3089}
3090
3091template <typename ClientType>
3092inline Result Delete(ClientType &cli, const std::string &path,
3093                     const Headers &headers, size_t chunk_size = 8192) {
3094  return Result{cli.open_stream("DELETE", path, {}, headers), chunk_size};
3095}
3096
3097template <typename ClientType>
3098inline Result Delete(ClientType &cli, const std::string &path,
3099                     const std::string &body, const std::string &content_type,
3100                     size_t chunk_size = 8192) {
3101  return Result{cli.open_stream("DELETE", path, {}, {}, body, content_type),
3102                chunk_size};
3103}
3104
3105template <typename ClientType>
3106inline Result Delete(ClientType &cli, const std::string &path,
3107                     const Headers &headers, const std::string &body,
3108                     const std::string &content_type,
3109                     size_t chunk_size = 8192) {
3110  return Result{
3111      cli.open_stream("DELETE", path, {}, headers, body, content_type),
3112      chunk_size};
3113}
3114
3115template <typename ClientType>
3116inline Result Delete(ClientType &cli, const std::string &path,
3117                     const Params &params, size_t chunk_size = 8192) {
3118  return Result{cli.open_stream("DELETE", path, params), chunk_size};
3119}
3120
3121template <typename ClientType>
3122inline Result Delete(ClientType &cli, const std::string &path,
3123                     const Params &params, const Headers &headers,
3124                     size_t chunk_size = 8192) {
3125  return Result{cli.open_stream("DELETE", path, params, headers), chunk_size};
3126}
3127
3128template <typename ClientType>
3129inline Result Delete(ClientType &cli, const std::string &path,
3130                     const Params &params, const std::string &body,
3131                     const std::string &content_type,
3132                     size_t chunk_size = 8192) {
3133  return Result{cli.open_stream("DELETE", path, params, {}, body, content_type),
3134                chunk_size};
3135}
3136
3137template <typename ClientType>
3138inline Result Delete(ClientType &cli, const std::string &path,
3139                     const Params &params, const Headers &headers,
3140                     const std::string &body, const std::string &content_type,
3141                     size_t chunk_size = 8192) {
3142  return Result{
3143      cli.open_stream("DELETE", path, params, headers, body, content_type),
3144      chunk_size};
3145}
3146
3147// HEAD
3148template <typename ClientType>
3149inline Result Head(ClientType &cli, const std::string &path,
3150                   size_t chunk_size = 8192) {
3151  return Result{cli.open_stream("HEAD", path), chunk_size};
3152}
3153
3154template <typename ClientType>
3155inline Result Head(ClientType &cli, const std::string &path,
3156                   const Headers &headers, size_t chunk_size = 8192) {
3157  return Result{cli.open_stream("HEAD", path, {}, headers), chunk_size};
3158}
3159
3160template <typename ClientType>
3161inline Result Head(ClientType &cli, const std::string &path,
3162                   const Params &params, size_t chunk_size = 8192) {
3163  return Result{cli.open_stream("HEAD", path, params), chunk_size};
3164}
3165
3166template <typename ClientType>
3167inline Result Head(ClientType &cli, const std::string &path,
3168                   const Params &params, const Headers &headers,
3169                   size_t chunk_size = 8192) {
3170  return Result{cli.open_stream("HEAD", path, params, headers), chunk_size};
3171}
3172
3173// OPTIONS
3174template <typename ClientType>
3175inline Result Options(ClientType &cli, const std::string &path,
3176                      size_t chunk_size = 8192) {
3177  return Result{cli.open_stream("OPTIONS", path), chunk_size};
3178}
3179
3180template <typename ClientType>
3181inline Result Options(ClientType &cli, const std::string &path,
3182                      const Headers &headers, size_t chunk_size = 8192) {
3183  return Result{cli.open_stream("OPTIONS", path, {}, headers), chunk_size};
3184}
3185
3186template <typename ClientType>
3187inline Result Options(ClientType &cli, const std::string &path,
3188                      const Params &params, size_t chunk_size = 8192) {
3189  return Result{cli.open_stream("OPTIONS", path, params), chunk_size};
3190}
3191
3192template <typename ClientType>
3193inline Result Options(ClientType &cli, const std::string &path,
3194                      const Params &params, const Headers &headers,
3195                      size_t chunk_size = 8192) {
3196  return Result{cli.open_stream("OPTIONS", path, params, headers), chunk_size};
3197}
3198
3199} // namespace stream
3200
3201namespace sse {
3202
3203struct SSEMessage {
3204  std::string event; // Event type (default: "message")
3205  std::string data;  // Event payload
3206  std::string id;    // Event ID for Last-Event-ID header
3207
3208  SSEMessage() : event("message") {}
3209
3210  void clear() {
3211    event = "message";
3212    data.clear();
3213    id.clear();
3214  }
3215};
3216
3217class SSEClient {
3218public:
3219  using MessageHandler = std::function<void(const SSEMessage &)>;
3220  using ErrorHandler = std::function<void(Error)>;
3221  using OpenHandler = std::function<void()>;
3222
3223  SSEClient(Client &client, const std::string &path)
3224      : client_(client), path_(path) {}
3225
3226  SSEClient(Client &client, const std::string &path, const Headers &headers)
3227      : client_(client), path_(path), headers_(headers) {}
3228
3229  ~SSEClient() { stop(); }
3230
3231  SSEClient(const SSEClient &) = delete;
3232  SSEClient &operator=(const SSEClient &) = delete;
3233
3234  // Event handlers
3235  SSEClient &on_message(MessageHandler handler) {
3236    on_message_ = std::move(handler);
3237    return *this;
3238  }
3239
3240  SSEClient &on_event(const std::string &type, MessageHandler handler) {
3241    event_handlers_[type] = std::move(handler);
3242    return *this;
3243  }
3244
3245  SSEClient &on_open(OpenHandler handler) {
3246    on_open_ = std::move(handler);
3247    return *this;
3248  }
3249
3250  SSEClient &on_error(ErrorHandler handler) {
3251    on_error_ = std::move(handler);
3252    return *this;
3253  }
3254
3255  SSEClient &set_reconnect_interval(int ms) {
3256    reconnect_interval_ms_ = ms;
3257    return *this;
3258  }
3259
3260  SSEClient &set_max_reconnect_attempts(int n) {
3261    max_reconnect_attempts_ = n;
3262    return *this;
3263  }
3264
3265  // State accessors
3266  bool is_connected() const { return connected_.load(); }
3267  const std::string &last_event_id() const { return last_event_id_; }
3268
3269  // Blocking start - runs event loop with auto-reconnect
3270  void start() {
3271    running_.store(true);
3272    run_event_loop();
3273  }
3274
3275  // Non-blocking start - runs in background thread
3276  void start_async() {
3277    running_.store(true);
3278    async_thread_ = std::thread([this]() { run_event_loop(); });
3279  }
3280
3281  // Stop the client (thread-safe)
3282  void stop() {
3283    running_.store(false);
3284    client_.stop(); // Cancel any pending operations
3285    if (async_thread_.joinable()) { async_thread_.join(); }
3286  }
3287
3288private:
3289  // Parse a single SSE field line
3290  // Returns true if this line ends an event (blank line)
3291  bool parse_sse_line(const std::string &line, SSEMessage &msg, int &retry_ms) {
3292    // Blank line signals end of event
3293    if (line.empty() || line == "\r") { return true; }
3294
3295    // Lines starting with ':' are comments (ignored)
3296    if (!line.empty() && line[0] == ':') { return false; }
3297
3298    // Find the colon separator
3299    auto colon_pos = line.find(':');
3300    if (colon_pos == std::string::npos) {
3301      // Line with no colon is treated as field name with empty value
3302      return false;
3303    }
3304
3305    auto field = line.substr(0, colon_pos);
3306    std::string value;
3307
3308    // Value starts after colon, skip optional single space
3309    if (colon_pos + 1 < line.size()) {
3310      auto value_start = colon_pos + 1;
3311      if (line[value_start] == ' ') { value_start++; }
3312      value = line.substr(value_start);
3313      // Remove trailing \r if present
3314      if (!value.empty() && value.back() == '\r') { value.pop_back(); }
3315    }
3316
3317    // Handle known fields
3318    if (field == "event") {
3319      msg.event = value;
3320    } else if (field == "data") {
3321      // Multiple data lines are concatenated with newlines
3322      if (!msg.data.empty()) { msg.data += "\n"; }
3323      msg.data += value;
3324    } else if (field == "id") {
3325      // Empty id is valid (clears the last event ID)
3326      msg.id = value;
3327    } else if (field == "retry") {
3328      // Parse retry interval in milliseconds
3329      {
3330        int v = 0;
3331        auto res =
3332            detail::from_chars(value.data(), value.data() + value.size(), v);
3333        if (res.ec == std::errc{}) { retry_ms = v; }
3334      }
3335    }
3336    // Unknown fields are ignored per SSE spec
3337
3338    return false;
3339  }
3340
3341  // Main event loop with auto-reconnect
3342  void run_event_loop() {
3343    auto reconnect_count = 0;
3344
3345    while (running_.load()) {
3346      // Build headers, including Last-Event-ID if we have one
3347      auto request_headers = headers_;
3348      if (!last_event_id_.empty()) {
3349        request_headers.emplace("Last-Event-ID", last_event_id_);
3350      }
3351
3352      // Open streaming connection
3353      auto result = stream::Get(client_, path_, request_headers);
3354
3355      // Connection error handling
3356      if (!result) {
3357        connected_.store(false);
3358        if (on_error_) { on_error_(result.error()); }
3359
3360        if (!should_reconnect(reconnect_count)) { break; }
3361        wait_for_reconnect();
3362        reconnect_count++;
3363        continue;
3364      }
3365
3366      if (result.status() != 200) {
3367        connected_.store(false);
3368        // For certain errors, don't reconnect
3369        if (result.status() == 204 || // No Content - server wants us to stop
3370            result.status() == 404 || // Not Found
3371            result.status() == 401 || // Unauthorized
3372            result.status() == 403) { // Forbidden
3373          if (on_error_) { on_error_(Error::Connection); }
3374          break;
3375        }
3376
3377        if (on_error_) { on_error_(Error::Connection); }
3378
3379        if (!should_reconnect(reconnect_count)) { break; }
3380        wait_for_reconnect();
3381        reconnect_count++;
3382        continue;
3383      }
3384
3385      // Connection successful
3386      connected_.store(true);
3387      reconnect_count = 0;
3388      if (on_open_) { on_open_(); }
3389
3390      // Event receiving loop
3391      std::string buffer;
3392      SSEMessage current_msg;
3393
3394      while (running_.load() && result.next()) {
3395        buffer.append(result.data(), result.size());
3396
3397        // Process complete lines in the buffer
3398        size_t line_start = 0;
3399        size_t newline_pos;
3400
3401        while ((newline_pos = buffer.find('\n', line_start)) !=
3402               std::string::npos) {
3403          auto line = buffer.substr(line_start, newline_pos - line_start);
3404          line_start = newline_pos + 1;
3405
3406          // Parse the line and check if event is complete
3407          auto event_complete =
3408              parse_sse_line(line, current_msg, reconnect_interval_ms_);
3409
3410          if (event_complete && !current_msg.data.empty()) {
3411            // Update last_event_id for reconnection
3412            if (!current_msg.id.empty()) { last_event_id_ = current_msg.id; }
3413
3414            // Dispatch event to appropriate handler
3415            dispatch_event(current_msg);
3416
3417            current_msg.clear();
3418          }
3419        }
3420
3421        // Keep unprocessed data in buffer
3422        buffer.erase(0, line_start);
3423      }
3424
3425      // Connection ended
3426      connected_.store(false);
3427
3428      if (!running_.load()) { break; }
3429
3430      // Check for read errors
3431      if (result.has_read_error()) {
3432        if (on_error_) { on_error_(result.read_error()); }
3433      }
3434
3435      if (!should_reconnect(reconnect_count)) { break; }
3436      wait_for_reconnect();
3437      reconnect_count++;
3438    }
3439
3440    connected_.store(false);
3441  }
3442
3443  // Dispatch event to appropriate handler
3444  void dispatch_event(const SSEMessage &msg) {
3445    // Check for specific event type handler first
3446    auto it = event_handlers_.find(msg.event);
3447    if (it != event_handlers_.end()) {
3448      it->second(msg);
3449      return;
3450    }
3451
3452    // Fall back to generic message handler
3453    if (on_message_) { on_message_(msg); }
3454  }
3455
3456  // Check if we should attempt to reconnect
3457  bool should_reconnect(int count) const {
3458    if (!running_.load()) { return false; }
3459    if (max_reconnect_attempts_ == 0) { return true; } // unlimited
3460    return count < max_reconnect_attempts_;
3461  }
3462
3463  // Wait for reconnect interval
3464  void wait_for_reconnect() {
3465    // Use small increments to check running_ flag frequently
3466    auto waited = 0;
3467    while (running_.load() && waited < reconnect_interval_ms_) {
3468      std::this_thread::sleep_for(std::chrono::milliseconds(100));
3469      waited += 100;
3470    }
3471  }
3472
3473  // Client and path
3474  Client &client_;
3475  std::string path_;
3476  Headers headers_;
3477
3478  // Callbacks
3479  MessageHandler on_message_;
3480  std::map<std::string, MessageHandler> event_handlers_;
3481  OpenHandler on_open_;
3482  ErrorHandler on_error_;
3483
3484  // Configuration
3485  int reconnect_interval_ms_ = 3000;
3486  int max_reconnect_attempts_ = 0; // 0 = unlimited
3487
3488  // State
3489  std::atomic<bool> running_{false};
3490  std::atomic<bool> connected_{false};
3491  std::string last_event_id_;
3492
3493  // Async support
3494  std::thread async_thread_;
3495};
3496
3497} // namespace sse
3498
3499
3500
3501} // namespace httplib
3502
3503#endif // CPPHTTPLIB_HTTPLIB_H