char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
718f7b417
char/aliceai-t5-moe
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.56.0" 12#define CPPHTTPLIB_VERSION_NUM "0x003800" 13 14#ifdef _WIN32 15#if defined(_WIN32_WINNT )&& _WIN32_WINNT < 0x0A00 16#error \ 17 "cpp-httplib doesn't support Windows 8 or lower. Please use Windows 10 or later." 18#endif 19#endif 20 21/* 22* Configuration 23*/ 24 25#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 26#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 27#endif 28 29#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 30#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000 31#endif 32 33#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT 34#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100 35#endif 36 37#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 38#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 39#endif 40 41#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 42#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 43#endif 44 45#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 46#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5 47#endif 48 49#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 50#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0 51#endif 52 53#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 54#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5 55#endif 56 57#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 58#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0 59#endif 60 61#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 62#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300 63#endif 64 65#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 66#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0 67#endif 68 69#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 70#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5 71#endif 72 73#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 74#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0 75#endif 76 77#ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 78#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0 79#endif 80 81#ifndef CPPHTTPLIB_EXPECT_100_THRESHOLD 82#define CPPHTTPLIB_EXPECT_100_THRESHOLD 1024 83#endif 84 85#ifndef CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND 86#define CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND 1000 87#endif 88 89#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD 90#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD (1024 * 1024) 91#endif 92 93#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND 94#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND 50 95#endif 96 97#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND 98#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 99#endif 100 101#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND 102#ifdef _WIN32 103#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000 104#else 105#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 106#endif 107#endif 108 109#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 110#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 111#endif 112 113#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH 114#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192 115#endif 116 117#ifndef CPPHTTPLIB_HEADER_MAX_COUNT 118#define CPPHTTPLIB_HEADER_MAX_COUNT 100 119#endif 120 121#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT 122#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 123#endif 124 125#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 126#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024 127#endif 128 129#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH 130#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (100 * 1024 * 1024) // 100MB 131#endif 132 133#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 134#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192 135#endif 136 137#ifndef CPPHTTPLIB_STATIC_FILE_COMPRESSION_MIN_LENGTH 138// 1400 rather than a round number: a body that already fits in one 1500-byte 139// MTU gains nothing from being made smaller. 140#define CPPHTTPLIB_STATIC_FILE_COMPRESSION_MIN_LENGTH 1400 141#endif 142 143#ifndef CPPHTTPLIB_STATIC_FILE_COMPRESSION_MAX_LENGTH 144#define CPPHTTPLIB_STATIC_FILE_COMPRESSION_MAX_LENGTH (4 * 1024 * 1024) // 4MB 145#endif 146 147#ifndef CPPHTTPLIB_RANGE_MAX_COUNT 148#define CPPHTTPLIB_RANGE_MAX_COUNT 1024 149#endif 150 151// std::regex_match's backtracking implementation (most acutely on libstdc++) 152// recurses roughly once per matched character for quantified patterns such 153// as "(.*)", so a long enough path can exhaust the calling thread's stack; on 154// a default ~8MB thread stack that has been observed to take on the order of 155// a couple thousand characters for a simple pattern. 256 leaves a wide safety 156// margin below that (well under the 8192-byte request URI limit) while still 157// fitting any realistic route segment; raise it if a route legitimately needs 158// longer paths. Regex routes are never applied to paths longer than this. 159#ifndef CPPHTTPLIB_REGEX_ROUTE_PATH_MAX_LENGTH 160#define CPPHTTPLIB_REGEX_ROUTE_PATH_MAX_LENGTH 256 161#endif 162 163#ifndef CPPHTTPLIB_TCP_NODELAY 164#define CPPHTTPLIB_TCP_NODELAY false 165#endif 166 167#ifndef CPPHTTPLIB_IPV6_V6ONLY 168#define CPPHTTPLIB_IPV6_V6ONLY false 169#endif 170 171#ifndef CPPHTTPLIB_RECV_BUFSIZ 172#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u) 173#endif 174 175#ifndef CPPHTTPLIB_SEND_BUFSIZ 176#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u) 177#endif 178 179#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ 180#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) 181#endif 182 183#ifndef CPPHTTPLIB_THREAD_POOL_COUNT 184#define CPPHTTPLIB_THREAD_POOL_COUNT \ 185 ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ 186 ? std::thread::hardware_concurrency() - 1 \ 187 : 0)) 188#endif 189 190#ifndef CPPHTTPLIB_THREAD_POOL_MAX_COUNT 191#define CPPHTTPLIB_THREAD_POOL_MAX_COUNT (CPPHTTPLIB_THREAD_POOL_COUNT * 4) 192#endif 193 194#ifndef CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT 195#define CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT 3 // seconds 196#endif 197 198#ifndef CPPHTTPLIB_RECV_FLAGS 199#define CPPHTTPLIB_RECV_FLAGS 0 200#endif 201 202#ifndef CPPHTTPLIB_SEND_FLAGS 203#define CPPHTTPLIB_SEND_FLAGS 0 204#endif 205 206#ifndef CPPHTTPLIB_LISTEN_BACKLOG 207#define CPPHTTPLIB_LISTEN_BACKLOG 128 208#endif 209 210#ifndef CPPHTTPLIB_MAX_LINE_LENGTH 211#define CPPHTTPLIB_MAX_LINE_LENGTH 32768 212#endif 213 214#ifndef CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH 215#define CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH 16777216 216#endif 217 218// One macro used to set the read timeout for both sides. They want different 219// defaults: a client's read timeout is the caller's own tool (it waits forever 220// until asked not to), while a server keeps a ceiling that reclaims a worker 221// from a peer that has gone quiet. The old name still works and sets both. 222#ifdef CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND 223#pragma message( \ 224 "CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND is deprecated; define " \ 225 "CPPHTTPLIB_WEBSOCKET_CLIENT_READ_TIMEOUT_SECOND and/or " \ 226 "CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND instead") 227#ifndef CPPHTTPLIB_WEBSOCKET_CLIENT_READ_TIMEOUT_SECOND 228#define CPPHTTPLIB_WEBSOCKET_CLIENT_READ_TIMEOUT_SECOND \ 229 CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND 230#endif 231#ifndef CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND 232#define CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND \ 233 CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND 234#endif 235#endif 236 237// 0 waits forever. A read timeout is how a caller gets control back to send on 238// the same connection; it is not a liveness check (that is ping/pong). Only a 239// timeout set at runtime through set_read_timeout() is reported as 240// ws::Timeout; when one of these compile-time defaults elapses, read() returns 241// ws::Fail and closes the connection. 242#ifndef CPPHTTPLIB_WEBSOCKET_CLIENT_READ_TIMEOUT_SECOND 243#define CPPHTTPLIB_WEBSOCKET_CLIENT_READ_TIMEOUT_SECOND 0 244#endif 245 246#ifndef CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND 247#define CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND 300 248#endif 249 250#ifndef CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND 251#define CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND 5 252#endif 253 254#ifndef CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND 255#define CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND 30 256#endif 257 258#ifndef CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS 259#define CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS 0 260#endif 261 262/* 263* Headers 264*/ 265 266#ifdef _WIN32 267#ifndef _CRT_SECURE_NO_WARNINGS 268#define _CRT_SECURE_NO_WARNINGS 269#endif //_CRT_SECURE_NO_WARNINGS 270 271#ifndef _CRT_NONSTDC_NO_DEPRECATE 272#define _CRT_NONSTDC_NO_DEPRECATE 273#endif //_CRT_NONSTDC_NO_DEPRECATE 274 275#if defined(_MSC_VER ) 276#if _MSC_VER < 1900 277#error Sorry, Visual Studio versions prior to 2015 are not supported 278#endif 279 280#pragma comment(lib, "ws2_32.lib") 281 282#ifndef _SSIZE_T_DEFINED 283using ssize_t = __int64 ; 284#define _SSIZE_T_DEFINED 285#endif 286#endif // _MSC_VER 287 288#ifndef S_ISREG 289#define S_ISREG (m ) (((m) & S_IFREG) == S_IFREG) 290#endif // S_ISREG 291 292#ifndef S_ISDIR 293#define S_ISDIR (m ) (((m) & S_IFDIR) == S_IFDIR) 294#endif // S_ISDIR 295 296#ifndef NOMINMAX 297#define NOMINMAX 298#endif // NOMINMAX 299 300#include <io.h> 301#include <winsock2.h> 302#include <ws2tcpip.h> 303 304#if defined(__has_include ) 305#if __has_include (< afunix .h > ) 306// afunix.h uses types declared in winsock2.h, so has to be included after it. 307#include <afunix.h> 308#define CPPHTTPLIB_HAVE_AFUNIX_H 1 309#endif 310#endif 311 312#ifndef WSA_FLAG_NO_HANDLE_INHERIT 313#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 314#endif 315 316using nfds_t = unsigned long ; 317using socket_t = SOCKET ; 318using socklen_t = int ; 319 320#else // not _WIN32 321 322#include <arpa/inet.h> 323#if !defined(_AIX )&& !defined(__MVS__ ) 324#include <ifaddrs.h> 325#endif 326#ifdef __MVS__ 327#include <strings.h> 328#ifndef NI_MAXHOST 329#define NI_MAXHOST 1025 330#endif 331#endif 332#include <net/if.h> 333#include <netdb.h> 334#include <netinet/in.h> 335#ifdef __linux__ 336#include <resolv.h> 337#undef _res // Undefine _res macro to avoid conflicts with user code (#2278) 338#endif 339#include <csignal> 340#include <netinet/tcp.h> 341#include <poll.h> 342#include <pthread.h> 343#include <sys/mman.h> 344#include <sys/socket.h> 345#include <sys/un.h> 346#include <unistd.h> 347 348using socket_t = int ; 349#ifndef INVALID_SOCKET 350#define INVALID_SOCKET (-1) 351#endif 352#endif //_WIN32 353 354#if defined(__APPLE__ ) 355#include <TargetConditionals.h> 356#endif 357 358#include <algorithm> 359#include <array> 360#include <atomic> 361#include <cassert> 362#include <chrono> 363#include <climits> 364#include <condition_variable> 365#include <cstdlib> 366#include <cstring> 367#include <errno.h> 368#include <exception> 369#include <fcntl.h> 370#include <fstream> 371#include <functional> 372#include <iomanip> 373#include <iostream> 374#include <iterator> 375#include <list> 376#include <map> 377#include <memory> 378#include <mutex> 379#include <random> 380#include <regex> 381#include <set> 382#include <sstream> 383#include <string> 384#include <sys/stat.h> 385#include <system_error> 386#include <thread> 387#include <type_traits> 388#include <unordered_map> 389#include <unordered_set> 390#include <utility> 391#include <vector> 392 393// On macOS with a TLS backend, enable Keychain root certificates by default 394// unless the user explicitly opts out. Not enabled on iOS/tvOS/watchOS since 395// the SecTrustSettings APIs used to enumerate anchor certificates are macOS 396// only; on those platforms the user must provide a CA bundle explicitly. 397#if defined(__APPLE__ )&& defined(__clang__ )&& \ 398 !defined(CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES )&& \ 399 (defined(CPPHTTPLIB_OPENSSL_SUPPORT )|| \ 400 defined(CPPHTTPLIB_MBEDTLS_SUPPORT )|| \ 401 defined(CPPHTTPLIB_WOLFSSL_SUPPORT )) 402#if TARGET_OS_OSX 403#ifndef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN 404#define CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN 405#endif 406#endif 407#endif 408 409#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN )&& \ 410 defined(__APPLE__ )&& !TARGET_OS_OSX 411#error \ 412 "CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN is only supported on macOS. On iOS/tvOS/watchOS, supply a CA bundle via set_ca_cert_path()." 413#endif 414 415// On Windows, enable Schannel certificate verification by default 416// unless the user explicitly opts out. 417#if defined(_WIN32 )&& \ 418 !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE ) 419#define CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE 420#endif 421 422#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO )|| \ 423 defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN ) 424#if TARGET_OS_MAC && defined(__clang__ ) 425#include <CFNetwork/CFHost.h> 426#include <CoreFoundation/CoreFoundation.h> 427#endif 428#endif 429 430#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 431#ifdef _WIN32 432#include <wincrypt.h> 433 434// these are defined in wincrypt.h and it breaks compilation if BoringSSL is 435// used 436#undef X509_NAME 437#undef X509_CERT_PAIR 438#undef X509_EXTENSIONS 439#undef PKCS7_SIGNER_INFO 440 441#ifdef _MSC_VER 442#pragma comment(lib, "crypt32.lib") 443#endif 444#endif // _WIN32 445 446#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN 447#if TARGET_OS_OSX 448#include <Security/Security.h> 449#endif 450#endif 451 452#include <openssl/err.h> 453#include <openssl/evp.h> 454#include <openssl/ssl.h> 455#include <openssl/x509v3.h> 456 457#if defined(_WIN32 )&& defined(OPENSSL_USE_APPLINK ) 458#include <openssl/applink.c> 459#endif 460 461#include <iostream> 462#include <sstream> 463 464#if defined(OPENSSL_IS_BORINGSSL )|| defined(LIBRESSL_VERSION_NUMBER ) 465#if OPENSSL_VERSION_NUMBER < 0x1010107f 466#error Please use OpenSSL or a current version of BoringSSL 467#endif 468#define SSL_get1_peer_certificate SSL_get_peer_certificate 469#elif OPENSSL_VERSION_NUMBER < 0x30000000L 470#error Sorry, OpenSSL versions prior to 3.0.0 are not supported 471#endif 472 473#endif // CPPHTTPLIB_OPENSSL_SUPPORT 474 475#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT 476// version.h defines MBEDTLS_VERSION_MAJOR (on 2.x/3.x/4.x alike); it is pulled 477// in with this first include group so the version gating below can use it. 478#include <mbedtls/error.h> 479#include <mbedtls/net_sockets.h> 480#include <mbedtls/oid.h> 481#include <mbedtls/pk.h> 482#include <mbedtls/ssl.h> 483#include <mbedtls/version.h> 484#include <mbedtls/x509_crt.h> 485#if MBEDTLS_VERSION_MAJOR >=4 486// Mbed TLS 4.x moved hashing/RNG to PSA Crypto and removed these headers. 487#include <psa/crypto.h> 488#else 489#include <mbedtls/ctr_drbg.h> 490#include <mbedtls/entropy.h> 491#include <mbedtls/md5.h> 492#include <mbedtls/sha1.h> 493#include <mbedtls/sha256.h> 494#include <mbedtls/sha512.h> 495#endif 496#ifdef _WIN32 497#include <wincrypt.h> 498#ifdef _MSC_VER 499#pragma comment(lib, "crypt32.lib") 500#endif 501#endif // _WIN32 502#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN 503#if TARGET_OS_OSX 504#include <Security/Security.h> 505#endif 506#endif 507 508// Mbed TLS version API compatibility. Note: V4 implies V3 (both defined on 509// 4.x), so version-specific 3.x-only code must check V3 && !V4. 510#if MBEDTLS_VERSION_MAJOR >=4 511#define CPPHTTPLIB_MBEDTLS_V4 512#endif 513#if MBEDTLS_VERSION_MAJOR >=3 514#define CPPHTTPLIB_MBEDTLS_V3 515#endif 516 517#endif // CPPHTTPLIB_MBEDTLS_SUPPORT 518 519#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT 520#include <wolfssl/options.h> 521 522#include <wolfssl/openssl/x509v3.h> 523 524// Fallback definitions for older wolfSSL versions (e.g., 5.6.6) 525#ifndef WOLFSSL_GEN_EMAIL 526#define WOLFSSL_GEN_EMAIL 1 527#endif 528#ifndef WOLFSSL_GEN_DNS 529#define WOLFSSL_GEN_DNS 2 530#endif 531#ifndef WOLFSSL_GEN_URI 532#define WOLFSSL_GEN_URI 6 533#endif 534#ifndef WOLFSSL_GEN_IPADD 535#define WOLFSSL_GEN_IPADD 7 536#endif 537 538#include <wolfssl/ssl.h> 539#include <wolfssl/wolfcrypt/hash.h> 540#include <wolfssl/wolfcrypt/md5.h> 541#include <wolfssl/wolfcrypt/sha256.h> 542#include <wolfssl/wolfcrypt/sha512.h> 543#ifdef _WIN32 544#include <wincrypt.h> 545#ifdef _MSC_VER 546#pragma comment(lib, "crypt32.lib") 547#endif 548#endif // _WIN32 549#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN 550#if TARGET_OS_OSX 551#include <Security/Security.h> 552#endif 553#endif 554#endif // CPPHTTPLIB_WOLFSSL_SUPPORT 555 556// Define CPPHTTPLIB_SSL_ENABLED if any SSL backend is available 557#if defined(CPPHTTPLIB_OPENSSL_SUPPORT )|| \ 558 defined(CPPHTTPLIB_MBEDTLS_SUPPORT )|| defined(CPPHTTPLIB_WOLFSSL_SUPPORT ) 559#define CPPHTTPLIB_SSL_ENABLED 560#endif 561 562#ifdef CPPHTTPLIB_ZLIB_SUPPORT 563#include <zlib.h> 564#endif 565 566#ifdef CPPHTTPLIB_BROTLI_SUPPORT 567#include <brotli/decode.h> 568#include <brotli/encode.h> 569#endif 570 571#ifdef CPPHTTPLIB_ZSTD_SUPPORT 572#include <zstd.h> 573#endif 574 575/* 576* Declaration 577*/ 578namespace httplib { 579 580namespace ws { 581class WebSocket ; 582}// namespace ws 583 584namespace detail { 585 586/* 587* Backport std::make_unique from C++14. 588* 589* NOTE: This code came up with the following stackoverflow post: 590* https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique 591* 592*/ 593 594template < class T ,class ...Args > 595typename std ::enable_if < !std ::is_array < T > ::value ,std ::unique_ptr < T >>::type 596make_unique (Args && ...args ) { 597return std ::unique_ptr < T > (new T (std ::forward < Args > (args )...)); 598} 599 600template < class T > 601typename std ::enable_if < std ::is_array < T > ::value ,std ::unique_ptr < T >>::type 602make_unique (std ::size_t n ) { 603typedef typename std ::remove_extent < T > ::type RT ; 604return std ::unique_ptr < T > (new RT [n ]); 605} 606 607// Locale-independent ASCII character classification. The <cctype> 608// counterparts (std::isalnum, std::isdigit, ...) consult the global C locale, 609// so e.g. std::isalnum(0xC5) can return true once an embedder calls 610// setlocale(). HTTP grammars are defined over ASCII, so raw bytes must be 611// classified without regard to the locale. 612inline bool is_ascii_digit (char c ) {return '0' <=c && c <='9' ; } 613 614inline bool is_ascii_alpha (char c ) { 615return ('a' <=c && c <='z' )|| ('A' <=c && c <='Z' ); 616} 617 618inline bool is_ascii_alnum (char c ) { 619return is_ascii_digit (c )|| is_ascii_alpha (c ); 620} 621 622namespace case_ignore { 623 624inline unsigned char to_lower (int c ) { 625const static unsigned char table [256 ]= { 6260 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 , 62715 ,16 ,17 ,18 ,19 ,20 ,21 ,22 ,23 ,24 ,25 ,26 ,27 ,28 ,29 , 62830 ,31 ,32 ,33 ,34 ,35 ,36 ,37 ,38 ,39 ,40 ,41 ,42 ,43 ,44 , 62945 ,46 ,47 ,48 ,49 ,50 ,51 ,52 ,53 ,54 ,55 ,56 ,57 ,58 ,59 , 63060 ,61 ,62 ,63 ,64 ,97 ,98 ,99 ,100 ,101 ,102 ,103 ,104 ,105 ,106 , 631107 ,108 ,109 ,110 ,111 ,112 ,113 ,114 ,115 ,116 ,117 ,118 ,119 ,120 ,121 , 632122 ,91 ,92 ,93 ,94 ,95 ,96 ,97 ,98 ,99 ,100 ,101 ,102 ,103 ,104 , 633105 ,106 ,107 ,108 ,109 ,110 ,111 ,112 ,113 ,114 ,115 ,116 ,117 ,118 ,119 , 634120 ,121 ,122 ,123 ,124 ,125 ,126 ,127 ,128 ,129 ,130 ,131 ,132 ,133 ,134 , 635135 ,136 ,137 ,138 ,139 ,140 ,141 ,142 ,143 ,144 ,145 ,146 ,147 ,148 ,149 , 636150 ,151 ,152 ,153 ,154 ,155 ,156 ,157 ,158 ,159 ,160 ,161 ,162 ,163 ,164 , 637165 ,166 ,167 ,168 ,169 ,170 ,171 ,172 ,173 ,174 ,175 ,176 ,177 ,178 ,179 , 638180 ,181 ,182 ,183 ,184 ,185 ,186 ,187 ,188 ,189 ,190 ,191 ,224 ,225 ,226 , 639227 ,228 ,229 ,230 ,231 ,232 ,233 ,234 ,235 ,236 ,237 ,238 ,239 ,240 ,241 , 640242 ,243 ,244 ,245 ,246 ,215 ,248 ,249 ,250 ,251 ,252 ,253 ,254 ,223 ,224 , 641225 ,226 ,227 ,228 ,229 ,230 ,231 ,232 ,233 ,234 ,235 ,236 ,237 ,238 ,239 , 642240 ,241 ,242 ,243 ,244 ,245 ,246 ,247 ,248 ,249 ,250 ,251 ,252 ,253 ,254 , 643255 , 644 }; 645return table [(unsigned char )(char )c ]; 646} 647 648inline std ::string to_lower (const std ::string & s ) { 649std ::string result = s ; 650std ::transform ( 651result .begin (),result .end (),result .begin (), 652 [](unsignedchar c ) {return static_cast < char > (to_lower (c )); }); 653return result ; 654} 655 656inline bool equal (const std ::string & a ,const std ::string & b ) { 657return a .size ()== b .size ()&& 658std ::equal (a .begin (),a .end (),b .begin (), [](char ca ,char cb ) { 659return to_lower (ca )== to_lower (cb ); 660 }); 661} 662 663struct equal_to { 664bool operator ()(const std ::string & a ,const std ::string & b )const { 665return equal (a ,b ); 666 } 667}; 668 669struct hash { 670size_t operator ()(const std ::string & key )const { 671return hash_core (key .data (),key .size (),0 ); 672 } 673 674size_t hash_core (const char * s ,size_t l ,size_t h )const { 675return (l == 0 ) ?h 676 :hash_core (s + 1 ,l - 1 , 677// Unsets the 6 high bits of h, therefore no 678// overflow happens 679 (((std ::numeric_limits < size_t > ::max )() >>6 )& 680h * 33 ) ^ 681static_cast < unsigned char > (to_lower (* s ))); 682 } 683}; 684 685template < typename T > 686using unordered_set = std ::unordered_set < T ,detail ::case_ignore ::hash , 687detail ::case_ignore ::equal_to > ; 688 689}// namespace case_ignore 690 691// This is based on 692// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". 693 694struct scope_exit { 695explicit scope_exit (std ::function < void (void )> && f ) 696 :exit_function (std ::move (f )),execute_on_destruction {true} {} 697 698scope_exit (scope_exit && rhs )noexcept 699 :exit_function (std ::move (rhs .exit_function )), 700execute_on_destruction {rhs .execute_on_destruction } { 701rhs .release (); 702 } 703 704 ~scope_exit () { 705if (execute_on_destruction ) {this -> exit_function (); } 706 } 707 708void release () {this -> execute_on_destruction = false; } 709 710private : 711scope_exit (const scope_exit & )= delete ; 712void operator = (const scope_exit & )= delete ; 713scope_exit & operator = (scope_exit && ) = delete; 714 715std ::function < void (void) > exit_function; 716bool execute_on_destruction; 717}; 718 719// Simple from_chars implementation for integer and double types (C++17 720// substitute) 721template < typename T > struct from_chars_result { 722const char * ptr; 723std :: errc ec; 724}; 725 726template < typename T > 727inline from_chars_result < T > from_chars (const char * first, const char * last, 728T & value, int base = 10 ) { 729value = 0 ; 730const char * p = first; 731bool negative = false; 732 733if (p != last && * p == '-' ) { 734negative = true; 735++ p; 736} 737if (p == last) { return {first, std::errc::invalid_argument}; } 738 739T result = 0 ; 740for (; p != last; ++ p) { 741char c = * p; 742int digit = -1 ; 743if ( is_ascii_digit (c)) { 744digit = c - '0' ; 745} else if ( 'a' <= c && c <= 'z' ) { 746digit = c - 'a' + 10 ; 747} else if ( 'A' <= c && c <= 'Z' ) { 748digit = c - 'A' + 10 ; 749} else { 750break ; 751} 752 753if (digit < 0 || digit >= base) { break ; } 754if (result > ((std::numeric_limits < T > ::max)() - digit) / base) { 755return {p, std::errc::result_out_of_range}; 756} 757result = result * base + digit; 758} 759 760if (p == first || (negative && p == first + 1 )) { 761return {first, std::errc::invalid_argument}; 762} 763 764value = negative ? T ( 0 ) - result : result; 765return {p, std::errc{}}; 766} 767 768// from_chars for double (hand-written, locale-independent) 769// 770// The only double consumed by this library is the HTTP quality value, whose 771// grammar is (RFC 9110 12.4.2): 772// qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] ) 773// i.e. a non-negative decimal with no sign, exponent, "inf"/"nan", or wide 774// magnitude. So this parser recognizes exactly 1*DIGIT [ "." *DIGIT ] with 775// '.' always the decimal separator (std::strtod would instead read it from the 776// global C locale, mis-parsing q-values once an embedder calls 777// setlocale(LC_ALL, "") into a comma-decimal locale). The caller range-checks 778// the result to [0, 1], so inputs outside that range need not be distinguished 779// here. Allocation-free, single pass, and free of the overflow/rounding edge 780// cases that exponent and wide-range handling would introduce. 781inline from_chars_result < double > from_chars ( const char * first, const char * last, 782double & value) { 783value = 0.0 ; 784const char * p = first; 785 786// Each 1eN is exactly representable, so a single final division by the 787// matching entry yields a correctly-rounded result. 788static const double powers_of_ten[] = { 7891e0 , 1e1 , 1e2 , 1e3 , 1e4 , 1e5 , 1e6 , 1e7 , 1e8 , 1e9 , 7901e10 , 1e11 , 1e12 , 1e13 , 1e14 , 1e15 , 1e16 , 1e17 , 1e18 }; 791const int max_frac_digits = 792static_cast < int > ( sizeof (powers_of_ten) / sizeof (powers_of_ten[ 0 ])) - 1 ; 793 794// Accumulate digits into a 64-bit integer and remember how many were 795// fractional. Two independent caps keep this bounded and safe: 796// * accumulation saturates before mantissa could overflow uint64_t, and 797// * frac_digits is capped at max_frac_digits so it is always a valid index 798// into powers_of_ten (without this an input like "0.000...0" would never 799// grow mantissa, so the saturation cap alone would not bound it). 800// Both caps only drop digits far beyond the precision a q-value needs; any 801// value they would change is well outside [0, 1] and rejected by the caller. 802uint64_t mantissa = 0 ; 803int frac_digits = 0 ; 804bool seen_digit = false; 805 806const uint64_t limit = ((std::numeric_limits < uint64_t > ::max)() - 9 ) / 10 ; 807auto accumulate = [ & ]( char c) { 808if (mantissa <= limit) { 809mantissa = mantissa * 10 + static_cast < uint64_t > (c - '0' ); 810return true; 811} 812return false; 813}; 814 815for (; p != last && is_ascii_digit ( * p); ++ p) { 816seen_digit = true; 817accumulate ( * p); 818} 819 820if (p != last && * p == '.' ) { 821++ p; 822for (; p != last && is_ascii_digit ( * p); ++ p) { 823seen_digit = true; 824if (frac_digits < max_frac_digits && accumulate ( * p)) { ++ frac_digits; } 825} 826} 827 828if (!seen_digit) { return {first, std::errc::invalid_argument}; } 829 830value = static_cast < double > (mantissa) / powers_of_ten[frac_digits]; 831return {p, std::errc{}}; 832} 833 834inline bool parse_port ( const char * s, size_t len, int & port) { 835int val = 0 ; 836auto r = from_chars (s, s + len, val); 837if (r. ec != std::errc{} || val < 1 || val > 65535 ) { return false; } 838port = val; 839return true; 840} 841 842inline bool parse_port ( const std ::string & s, int & port) { 843return parse_port (s. data (), s. size (), port); 844} 845 846struct UrlComponents { 847std :: string scheme ; 848std :: string host ; 849std :: string port ; 850std :: string path ; 851std :: string query ; 852}; 853 854inline bool parse_url ( const std ::string & url, UrlComponents & uc) { 855uc = {}; 856size_t pos = 0 ; 857 858auto sep = url. find ( "://" ); 859if (sep != std::string::npos) { 860uc. scheme = url. substr ( 0 , sep); 861 862// Scheme must be [a-z]+ only 863if (uc. scheme . empty ()) { return false; } 864for (auto c : uc. scheme ) { 865if ( c < 'a' || c > 'z') { return false; } 866} 867 868pos = sep + 3 ; 869} else if ( url .compare( 0 , 2 , " //") == 0) { 870pos = 2 ; 871} 872 873auto has_authority_prefix = pos > 0 ; 874auto has_authority = has_authority_prefix || (!url. empty () && url[ 0 ] != '/' && 875url[ 0 ] != '?' && url[ 0 ] != '#' ); 876if (has_authority) { 877if (pos < url. size () && url[pos] == '[' ) { 878auto close = url. find ( ']' , pos); 879if (close == std::string::npos) { return false; } 880uc. host = url. substr (pos + 1 , close - pos - 1 ); 881 882// IPv6 host must be [a-fA-F0-9:]+ only 883if (uc. host . empty ()) { return false; } 884for (auto c : uc.host) { 885if (!( is_ascii_digit (c) || (c >= 'a' && c <= 'f' ) || 886(c >= 'A' && c <= 'F' ) || c == ':' )) { 887return false; 888} 889} 890 891pos = close + 1 ; 892 893// The IPv6 literal is the whole host, so ']' must be followed by a port, 894// path, query or fragment delimiter (or the end of input). Otherwise the 895// trailing bytes would be folded into the path while the connection 896// still targets the bracketed address. 897if (pos < url. size ()) { 898auto c = url[pos]; 899if (c != ':' && c != '/' && c != '?' && c != '#' ) { return false; } 900} 901} else { 902auto end = url. find_first_of ( ":/?#" , pos); 903if (end == std::string::npos) { end = url. size (); } 904uc. host = url. substr (pos, end - pos); 905pos = end; 906} 907 908if (pos < url. size () && url[pos] == ':' ) { 909++ pos; 910auto end = url. find_first_of ( "/?#" , pos); 911if (end == std::string::npos) { end = url. size (); } 912uc. port = url. substr (pos, end - pos); 913pos = end; 914} 915 916// Without :// or //, the entire input must be consumed as host[:port]. 917// If there is leftover (path, query, etc.), this is not a valid 918// host[:port] string — clear and reparse as a plain path. 919if (!has_authority_prefix && pos < url. size ()) { 920uc. host . clear (); 921uc. port . clear (); 922pos = 0 ; 923} 924} 925 926if (pos < url. size () && url[pos] != '?' && url[pos] != '#' ) { 927auto end = url. find_first_of ( "?#" , pos); 928if (end == std::string::npos) { end = url. size (); } 929uc. path = url. substr (pos, end - pos); 930pos = end; 931} 932 933if (pos < url. size () && url[pos] == '?' ) { 934auto end = url. find ( '#' , pos); 935if (end == std::string::npos) { end = url. size (); } 936uc. query = url. substr (pos, end - pos); 937} 938 939return true; 940} 941 942} // namespace detail 943 944enum class SSLVerifierResponse { 945// no decision has been made, use the built-in certificate verifier 946NoDecisionMade, 947// connection certificate is verified and accepted 948CertificateAccepted, 949// connection certificate was processed but is rejected 950CertificateRejected 951}; 952 953// System CA loading policy for SSL clients. Auto (the default) loads system 954// CA certs only when no custom CA is configured; enable_system_ca() switches 955// to an explicit policy. 956enum class SystemCAMode { Auto, Enabled, Disabled }; 957 958enum StatusCode { 959// Information responses 960Continue_100 = 100 , 961SwitchingProtocol_101 = 101 , 962Processing_102 = 102 , 963EarlyHints_103 = 103 , 964 965// Successful responses 966OK_200 = 200 , 967Created_201 = 201 , 968Accepted_202 = 202 , 969NonAuthoritativeInformation_203 = 203 , 970NoContent_204 = 204 , 971ResetContent_205 = 205 , 972PartialContent_206 = 206 , 973MultiStatus_207 = 207 , 974AlreadyReported_208 = 208 , 975IMUsed_226 = 226 , 976 977// Redirection messages 978MultipleChoices_300 = 300 , 979MovedPermanently_301 = 301 , 980Found_302 = 302 , 981SeeOther_303 = 303 , 982NotModified_304 = 304 , 983UseProxy_305 = 305 , 984unused_306 = 306 , 985TemporaryRedirect_307 = 307 , 986PermanentRedirect_308 = 308 , 987 988// Client error responses 989BadRequest_400 = 400 , 990Unauthorized_401 = 401 , 991PaymentRequired_402 = 402 , 992Forbidden_403 = 403 , 993NotFound_404 = 404 , 994MethodNotAllowed_405 = 405 , 995NotAcceptable_406 = 406 , 996ProxyAuthenticationRequired_407 = 407 , 997RequestTimeout_408 = 408 , 998Conflict_409 = 409 , 999Gone_410 = 410 , 1000LengthRequired_411 = 411 , 1001PreconditionFailed_412 = 412 , 1002PayloadTooLarge_413 = 413 , 1003UriTooLong_414 = 414 , 1004UnsupportedMediaType_415 = 415 , 1005RangeNotSatisfiable_416 = 416 , 1006ExpectationFailed_417 = 417 , 1007ImATeapot_418 = 418 , 1008MisdirectedRequest_421 = 421 , 1009UnprocessableContent_422 = 422 , 1010Locked_423 = 423 , 1011FailedDependency_424 = 424 , 1012TooEarly_425 = 425 , 1013UpgradeRequired_426 = 426 , 1014PreconditionRequired_428 = 428 , 1015TooManyRequests_429 = 429 , 1016RequestHeaderFieldsTooLarge_431 = 431 , 1017UnavailableForLegalReasons_451 = 451 , 1018 1019// Server error responses 1020InternalServerError_500 = 500 , 1021NotImplemented_501 = 501 , 1022BadGateway_502 = 502 , 1023ServiceUnavailable_503 = 503 , 1024GatewayTimeout_504 = 504 , 1025HttpVersionNotSupported_505 = 505 , 1026VariantAlsoNegotiates_506 = 506 , 1027InsufficientStorage_507 = 507 , 1028LoopDetected_508 = 508 , 1029NotExtended_510 = 510 , 1030NetworkAuthenticationRequired_511 = 511 , 1031}; 1032 1033namespace detail { 1034 1035// A multimap that keeps its entries in the order they were inserted. 1036// 1037// HTTP needs that order in two places. RFC 9110 5.3 makes the order of header 1038// fields sharing a field name significant and forbids a proxy from reordering 1039// them, and a query string's parameters are meaningful in the order the caller 1040// wrote them. Neither standard container expresses it: std::unordered_multimap 1041// gives no ordering guarantee at all for equivalent keys (libstdc++ yields 1042// reverse insertion order, libc++ insertion order), and std::multimap sorts by 1043// key, which would drop control data such as Host behind whatever else the 1044// message carries and alphabetise a query string. 1045// 1046// Entries are therefore kept in a flat vector, in order. Lookup is a linear 1047// scan, which beats hashing for the handful of entries a message carries 1048// (headers are capped at CPPHTTPLIB_HEADER_MAX_COUNT). 1049// 1050// KeyEqual compares keys; it is what makes Headers case-insensitive and 1051// Params, whose parameter names are case-sensitive, not. 1052template < typename Mapped, typename KeyEqual > class insertion_ordered_multimap { 1053public : 1054using key_type = std::string; 1055using mapped_type = Mapped; 1056using value_type = std::pair < std::string, Mapped > ; 1057using size_type = std:: size_t ; 1058using difference_type = std:: ptrdiff_t ; 1059using reference = value_type & ; 1060using const_reference = const value_type & ; 1061 1062private : 1063static size_type npos () { return static_cast < size_type > ( -1 ); } 1064 1065static bool keys_equal ( const std ::string & a, const std ::string & b) { 1066return KeyEqual ()(a, b); 1067} 1068 1069// Iterating yields every entry in insertion order, but equal_range() and 1070// find() have to walk only the entries sharing one key, which are not 1071// adjacent. Both are the same iterator type: key_idx_ selects between the 1072// two traversals, and since equality compares only the position, an iterator 1073// restricted to one key still compares equal to end(). 1074template < typename V > class iterator_t { 1075public : 1076using iterator_category = std::bidirectional_iterator_tag; 1077using value_type = insertion_ordered_multimap::value_type; 1078using difference_type = insertion_ordered_multimap::difference_type; 1079using pointer = V * ; 1080using reference = V & ; 1081 1082iterator_t () : data_ ( nullptr ), idx_ ( 0 ), size_ ( 0 ), key_idx_ ( npos ()) {} 1083 1084template < typename U , 1085typename std::enable_if < std::is_convertible < U * , V *> ::value, 1086int > ::type = 0 > 1087iterator_t (const iterator_t < U > & rhs) 1088: data_ (rhs. data_ ), idx_ ( rhs .idx_), size_ ( rhs .size_), 1089key_idx_ ( rhs .key_idx_) {} 1090 1091reference operator * () const { return data_[idx_]; } 1092pointer operator -> () const { return data_ + idx_; } 1093 1094iterator_t & operator ++ () { 1095// Saturating, so that advancing past the last entry of a key (which 1096// get_multimap_value() does when asked for an out-of-range id) stays at 1097// end() instead of running off the container. 1098if (idx_ >= size_) { return * this; } 1099++ idx_; 1100if (key_idx_ != npos ()) { 1101while (idx_ < size_ && ! matches (idx_)) { 1102++ idx_; 1103} 1104} 1105return * this; 1106} 1107 1108iterator_t operator ++ ( int ) { 1109auto tmp = * this; 1110++* this; 1111return tmp; 1112} 1113 1114iterator_t & operator -- () { 1115if (idx_ == 0 ) { return * this; } 1116-- idx_; 1117if (key_idx_ != npos ()) { 1118while (idx_ > 0 && ! matches (idx_)) { 1119-- idx_; 1120} 1121} 1122return * this; 1123} 1124 1125iterator_t operator -- ( int ) { 1126auto tmp = * this; 1127--* this; 1128return tmp; 1129} 1130 1131template < typename U > bool operator == ( const iterator_t < U > & rhs) const { 1132return idx_ == rhs. idx_ ; 1133} 1134 1135template < typename U > bool operator != ( const iterator_t < U > & rhs) const { 1136return idx_ != rhs. idx_ ; 1137} 1138 1139private : 1140friend class insertion_ordered_multimap; 1141template < typename > friend class iterator_t; 1142 1143iterator_t ( V * data, size_type idx, size_type size, size_type key_idx) 1144: data_ (data), idx_ (idx), size_ (size), key_idx_ (key_idx) {} 1145 1146bool matches ( size_type i) const { 1147return keys_equal (data_[i]. first , data_[key_idx_]. first ); 1148} 1149 1150V * data_; 1151size_type idx_; 1152size_type size_; 1153size_type key_idx_; 1154}; 1155 1156public : 1157using iterator = iterator_t < value_type > ; 1158using const_iterator = iterator_t < const value_type > ; 1159 1160insertion_ordered_multimap () = default; 1161insertion_ordered_multimap ( std ::initializer_list < value_type > il) 1162: entries_( il ) {} 1163template < typename InputIt > 1164insertion_ordered_multimap (InputIt first, InputIt last) 1165: entries_ (first, last) {} 1166 1167iterator begin () { return make_iter ( 0 , npos ()); } 1168iterator end () { return make_iter (entries_. size (), npos ()); } 1169const_iterator begin () const { return make_citer ( 0 , npos ()); } 1170const_iterator end () const { return make_citer (entries_. size (), npos ()); } 1171const_iterator cbegin () const { return begin (); } 1172const_iterator cend () const { return end (); } 1173 1174bool empty () const { return entries_. empty (); } 1175size_type size () const { return entries_. size (); } 1176void clear () { entries_. clear (); } 1177void swap ( insertion_ordered_multimap & rhs) { entries_. swap (rhs. entries_ ); } 1178 1179iterator insert ( const value_type & val) { 1180entries_. push_back (val); 1181return make_iter (entries_. size () - 1 , npos ()); 1182} 1183 1184iterator insert ( value_type && val) { 1185entries_. push_back (std:: move (val)); 1186return make_iter (entries_. size () - 1 , npos ()); 1187} 1188 1189template < typename... Args > iterator emplace (Args && ...args) { 1190entries_. emplace_back (std::forward < Args > (args)...); 1191return make_iter (entries_. size () - 1 , npos ()); 1192} 1193 1194// For entries that have to lead the message, such as the Host header field 1195// (RFC 9110 5.3 recommends sending control data first). 1196template < typename... Args > iterator emplace_front (Args && ...args) { 1197entries_. emplace (entries_. begin (), std::forward < Args > (args)...) ; 1198return make_iter ( 0 , npos ()); 1199} 1200 1201iterator find ( const std :: string & key ) { 1202auto i = index_of ( key ); 1203return i == npos () ? end () : make_iter ( i , i ); 1204} 1205 1206const_iterator find ( const std :: string & key ) const { 1207auto i = index_of ( key ); 1208return i == npos () ? end () : make_citer ( i , i ); 1209} 1210 1211size_type count ( const std :: string & key ) const { 1212size_type n = 0 ; 1213for ( const auto & entry : entries_ ) { 1214if ( keys_equal ( entry . first , key )) { n ++; } 1215} 1216return n ; 1217} 1218 1219std :: pair < iterator , iterator > equal_range ( const std :: string & key ) { 1220auto i = index_of ( key ); 1221return i == npos () ? std :: make_pair ( end (), end ()) 1222: std :: make_pair ( make_iter ( i , i ), end ()); 1223} 1224 1225std :: pair < const_iterator , const_iterator > 1226equal_range ( const std :: string & key ) const { 1227auto i = index_of ( key ); 1228return i == npos () ? std :: make_pair ( end (), end ()) 1229: std :: make_pair ( make_citer ( i , i ), end ()); 1230} 1231 1232size_type erase ( const std :: string & key ) { 1233auto before = entries_. size (); 1234entries_. erase (std:: remove_if (entries_. begin (), entries_. end (), 1235[ & ]( const value_type & entry) { 1236return keys_equal (entry. first , key); 1237}), 1238entries_. end ()); 1239return before - entries_. size (); 1240} 1241 1242iterator erase ( const_iterator pos) { 1243entries_. erase (entries_. begin () + static_cast < difference_type > (pos. idx_ )); 1244return make_iter (pos. idx_ , npos ()); 1245} 1246 1247// Erases what iterating [first, last) would actually visit, so erasing an 1248// equal_range() removes only the entries with that key, not everything 1249// positioned between them. 1250iterator erase ( const_iterator first, const_iterator last) { 1251auto from = first. idx_ ; 1252auto to = last. idx_ ; 1253if (from >= to) { return make_iter (from, npos ()); } 1254 1255auto begin_it = entries_. begin (); 1256auto from_it = begin_it + static_cast < difference_type > (from); 1257auto to_it = begin_it + static_cast < difference_type > (to); 1258 1259if (first. key_idx_ == npos ()) { 1260entries_. erase (from_it, to_it); 1261} else { 1262auto key = entries_[first. key_idx_ ]. first ; 1263auto keep = from_it; 1264for (auto it = from_it; it != to_it; ++ it) { 1265if (! keys_equal (it -> first , key)) { 1266if (keep != it) { * keep = std:: move ( * it); } 1267++ keep; 1268} 1269} 1270if (keep != to_it) { 1271keep = std:: move (to_it, entries_. end (), keep); 1272} else { 1273keep = entries_. end (); 1274} 1275entries_. erase (keep, entries_. end ()); 1276} 1277return make_iter (from, npos ()); 1278} 1279 1280friend bool operator == ( const insertion_ordered_multimap & lhs, 1281const insertion_ordered_multimap & rhs) { 1282return lhs. entries_ == rhs. entries_ ; 1283} 1284 1285friend bool operator != ( const insertion_ordered_multimap & lhs, 1286const insertion_ordered_multimap & rhs) { 1287return !(lhs == rhs); 1288} 1289 1290private : 1291size_type index_of ( const std ::string & key) const { 1292for ( size_type i = 0 ; i < entries_. size (); i ++ ) { 1293if ( keys_equal (entries_[i]. first , key)) { return i; } 1294} 1295return npos (); 1296} 1297 1298iterator make_iter ( size_type idx, size_type key_idx) { 1299return iterator (entries_. data (), idx, entries_. size (), key_idx); 1300} 1301 1302const_iterator make_citer ( size_type idx, size_type key_idx) const { 1303return const_iterator (entries_. data (), idx, entries_. size (), key_idx); 1304} 1305 1306std ::vector < value_type > entries_; 1307}; 1308 1309} // namespace detail 1310 1311using Headers = 1312detail::insertion_ordered_multimap < std::string, 1313detail::case_ignore::equal_to > ; 1314 1315// Query parameter names are case-sensitive, unlike header field names. 1316using Params = 1317detail::insertion_ordered_multimap < std::string, std::equal_to < std::string>>; 1318using Match = std::smatch; 1319 1320using DownloadProgress = std::function < bool (size_t current, size_t total) > ; 1321using UploadProgress = std::function < bool (size_t current, size_t total) > ; 1322 1323/* 1324* detail: type-erased storage used by UserData. 1325* ABI-stable regardless of C++ standard — always uses this custom 1326* implementation instead of std::any. 1327*/ 1328namespace detail { 1329 1330using any_type_id = const void * ; 1331 1332template < typename T > any_type_id any_typeid () noexcept { 1333static const char id = 0 ; 1334return & id; 1335} 1336 1337struct any_storage { 1338virtual ~ any_storage () = default ; 1339virtual std ::unique_ptr < any_storage > clone () const = 0 ; 1340virtual any_type_id type_id () const noexcept = 0 ; 1341}; 1342 1343template < typename T > struct any_value final : any_storage { 1344T value; 1345template < typename U > explicit any_value ( U && v) : value ( std ::forward < U > (v)) {} 1346std ::unique_ptr < any_storage > clone () const override { 1347return std::unique_ptr < any_storage > (new any_value < T > (value)); 1348} 1349any_type_id type_id () const noexcept override { return any_typeid < T > (); } 1350}; 1351 1352} // namespace detail 1353 1354class UserData { 1355public : 1356UserData () = default; 1357UserData (UserData && ) noexcept = default; 1358UserData & operator = (UserData && ) noexcept = default; 1359 1360UserData( const UserData & o) { 1361for ( const auto & e : o. entries_ ) { 1362if ( e .second) { entries_[e. first ] = e. second -> clone (); } 1363} 1364} 1365 1366UserData & operator = ( const UserData & o) { 1367if (this != & o) { 1368entries_. clear (); 1369for (const auto & e : o. entries_ ) { 1370if (e. second ) { entries_[e. first ] = e. second -> clone (); } 1371} 1372} 1373return * this; 1374} 1375 1376template < typename T > void set (const std::string & key, T && value) { 1377using D = typename std::decay < T > ::type; 1378entries_[key]. reset (new detail::any_value < D > (std::forward < T > (value))); 1379} 1380 1381template < typename T > T * get (const std::string & key) noexcept { 1382auto it = entries_. find (key); 1383if (it == entries_. end () || !it -> second ) { return nullptr; } 1384if (it -> second -> type_id () != detail::any_typeid < T > ()) { return nullptr ; } 1385return & static_cast < detail::any_value < T > * >(it -> second . get ()) -> value ; 1386} 1387 1388template < typename T > const T * get ( const std ::string & key) const noexcept { 1389auto it = entries_. find (key); 1390if (it == entries_. end () || !it -> second ) { return nullptr ; } 1391if (it -> second -> type_id () != detail::any_typeid < T > ()) { return nullptr ; } 1392return & static_cast < const detail::any_value < T > *> (it -> second . get ()) -> value ; 1393} 1394 1395bool has ( const std ::string & key) const noexcept { 1396return entries_. find (key) != entries_. end (); 1397} 1398 1399void erase ( const std ::string & key) { entries_. erase (key); } 1400 1401void clear () noexcept { entries_. clear (); } 1402 1403private : 1404std ::unordered_map < std::string, std::unique_ptr < detail::any_storage>> 1405entries_; 1406}; 1407 1408struct Response ; 1409using ResponseHandler = std::function < bool (const Response & response) > ; 1410 1411struct FormData { 1412std :: string name ; 1413std :: string content ; 1414std :: string filename ; 1415std :: string content_type ; 1416Headers headers ; 1417}; 1418 1419struct FormField { 1420std :: string name ; 1421std :: string content ; 1422Headers headers ; 1423}; 1424// RFC 7578 5.2: a form processor "SHOULD send back results in order" and 1425// "Intermediaries MUST NOT reorder the results", so a handler walking these 1426// should see the parts as they were sent. A std::multimap sorts by field name 1427// and loses that. Field names are case-sensitive, hence std::equal_to rather 1428// than the case-insensitive predicate Headers uses. 1429using FormFields = 1430detail::insertion_ordered_multimap < FormField, std::equal_to < std::string>>; 1431 1432using FormFiles = 1433detail::insertion_ordered_multimap < FormData, std::equal_to < std::string>>; 1434 1435struct MultipartFormData { 1436FormFields fields ; // Text fields from multipart 1437FormFiles files ; // Files from multipart 1438 1439// Text field access 1440std :: string get_field ( const std ::string & key, size_t id = 0 ) const ; 1441std :: vector < std ::string > get_fields (const std::string & key) const; 1442bool has_field ( const std ::string & key) const ; 1443size_t get_field_count ( const std ::string & key) const ; 1444 1445// File access 1446FormData get_file ( const std ::string & key, size_t id = 0 ) const ; 1447std :: vector < FormData > get_files ( const std ::string & key) const ; 1448bool has_file ( const std ::string & key) const ; 1449size_t get_file_count ( const std ::string & key) const ; 1450}; 1451 1452struct UploadFormData { 1453std :: string name ; 1454std :: string content ; 1455std :: string filename ; 1456std :: string content_type ; 1457}; 1458using UploadFormDataItems = std::vector < UploadFormData > ; 1459 1460class DataSink { 1461public : 1462DataSink () : os ( & sb_), sb_ ( * this) {} 1463 1464DataSink( const DataSink & ) = delete; 1465DataSink & operator = ( const DataSink & ) = delete; 1466DataSink (DataSink && ) = delete; 1467DataSink & operator = (DataSink && ) = delete; 1468 1469std ::function < bool (const char * data, size_t data_len) > write; 1470 1471// Only `write` is mandatory. The rest are defaulted so that a provider 1472// calling one on a writer that does not set it gets sensible behaviour 1473// rather than std::bad_function_call thrown from a worker thread. Capturing 1474// `this` is safe: DataSink is neither copyable nor movable. 1475std ::function < bool () > is_writable = []() { return true; }; 1476std ::function < void () > done = []() {}; 1477std ::function < void (const Headers & trailer) > done_with_trailer = 1478[this](const Headers & /*trailer*/ ) { done (); }; 1479 1480std :: ostream os; 1481 1482private : 1483class data_sink_streambuf final : public std::streambuf { 1484public : 1485explicit data_sink_streambuf ( DataSink & sink) : sink_ (sink) {} 1486 1487protected : 1488std :: streamsize xsputn( const char * s, std :: streamsize n) override { 1489if (sink_. write (s, static_cast < size_t > (n))) { return n; } 1490return 0 ; 1491} 1492 1493private : 1494DataSink & sink_; 1495}; 1496 1497data_sink_streambuf sb_; 1498}; 1499 1500using ContentProvider = 1501std::function < bool (size_t offset, size_t length, DataSink & sink) > ; 1502 1503using ContentProviderWithoutLength = 1504std::function < bool (size_t offset, DataSink & sink) > ; 1505 1506using ContentProviderResourceReleaser = std::function < void (bool success) > ; 1507 1508struct FormDataProvider { 1509std :: string name ; 1510ContentProviderWithoutLength provider ; 1511std :: string filename ; 1512std :: string content_type ; 1513}; 1514using FormDataProviderItems = std::vector < FormDataProvider > ; 1515 1516inline FormDataProvider 1517make_file_provider ( const std ::string & name, const std ::string & filepath, 1518const std ::string & filename = std:: string (), 1519const std ::string & content_type = std:: string ()) { 1520FormDataProvider fdp; 1521fdp. name = name; 1522fdp. filename = filename. empty () ? filepath : filename; 1523fdp. content_type = content_type; 1524fdp. provider = [filepath](size_t offset, DataSink & sink) -> bool { 1525std :: ifstream f( filepath , std :: ios ::binary); 1526if (!f) { return false; } 1527if (offset > 0 ) { 1528f. seekg (static_cast < std::streamoff > (offset)); 1529if (!f. good ()) { 1530sink. done (); 1531return true; 1532} 1533} 1534char buf[ 8192 ]; 1535f. read (buf, sizeof (buf)); 1536auto n = static_cast < size_t > (f. gcount ()); 1537if (n > 0 ) { return sink. write (buf, n); } 1538sink. done (); // EOF 1539return true; 1540}; 1541return fdp; 1542} 1543 1544inline std ::pair < size_t , ContentProvider > 1545make_file_body ( const std ::string & filepath) { 1546size_t size = 0 ; 1547{ 1548std :: ifstream f( filepath , std :: ios ::binary | std:: ios ::ate); 1549if (!f) { return { 0 , ContentProvider{}}; } 1550size = static_cast < size_t > (f. tellg ()); 1551} 1552 1553ContentProvider provider = [filepath]( size_t offset, size_t length, 1554DataSink & sink) -> bool { 1555std :: ifstream f( filepath , std :: ios ::binary); 1556if (!f) { return false; } 1557f. seekg (static_cast < std::streamoff > (offset)); 1558if (!f. good ()) { return false; } 1559char buf[ 8192 ]; 1560while (length > 0 ) { 1561auto to_read = ( std ::min)( sizeof ( buf ), length); 1562f. read (buf, static_cast < std::streamsize > (to_read)); 1563auto n = static_cast < size_t > (f. gcount ()); 1564// The file is shorter than the size make_file_body() measured, which the 1565// caller has already committed to as Content-Length. The body cannot be 1566// completed, so fail as every other error here does. 1567if (n == 0 ) { return false; } 1568if (!sink. write (buf, n)) { return false; } 1569length -= n; 1570} 1571return true; 1572}; 1573return {size, std:: move (provider)}; 1574} 1575 1576using ContentReceiverWithProgress = std::function < bool ( 1577const char * data, size_t data_length, size_t offset, size_t total_length) > ; 1578 1579using ContentReceiver = 1580std::function < bool (const char * data, size_t data_length) > ; 1581 1582using FormDataHeader = std::function < bool (const FormData & file) > ; 1583 1584class ContentReader { 1585public : 1586using Reader = std::function < bool (ContentReceiver receiver) > ; 1587using FormDataReader = 1588std::function < bool (FormDataHeader header, ContentReceiver receiver) > ; 1589 1590ContentReader( Reader reader, FormDataReader multipart_reader) 1591: reader_ ( std :: move ( reader )), 1592formdata_reader_ ( std :: move ( multipart_reader )) {} 1593 1594bool operator ()( FormDataHeader header, ContentReceiver receiver) const { 1595return formdata_reader_ ( std :: move ( header ), std :: move ( receiver )); 1596} 1597 1598bool operator ()( ContentReceiver receiver) const { 1599return reader_ (std:: move (receiver)); 1600} 1601 1602Reader reader_; 1603FormDataReader formdata_reader_; 1604}; 1605 1606using Range = std::pair < ssize_t, ssize_t > ; 1607using Ranges = std::vector < Range > ; 1608 1609#ifdef CPPHTTPLIB_SSL_ENABLED 1610// TLS abstraction layer - public type definitions and API 1611namespace tls { 1612 1613// Opaque handles (defined as void* for abstraction) 1614using ctx_t = void * ; 1615using session_t = void * ; 1616using const_session_t = const void * ; // For read-only session access 1617using cert_t = void * ; 1618using ca_store_t = void * ; 1619 1620// TLS versions 1621enum class Version { 1622TLS1_2 = 0x0303 , 1623TLS1_3 = 0x0304 , 1624}; 1625 1626// Subject Alternative Names (SAN) entry types 1627enum class SanType { DNS , IP , , URI , OTHER }; 1628 1629// SAN entry structure 1630struct SanEntry { 1631SanType type ; 1632std :: string value ; 1633}; 1634 1635// Verification context for certificate verification callback 1636struct VerifyContext { 1637session_t session ; // TLS session handle 1638cert_t cert ; // Current certificate being verified 1639int depth ; // Certificate chain depth (0 = leaf) 1640bool preverify_ok ; // OpenSSL/Mbed TLS pre-verification result 1641long error_code ; // Backend-specific error code (0 = no error) 1642const char * error_string ; // Human-readable error description 1643 1644// Certificate introspection methods 1645std :: string subject_cn () const ; 1646std :: string issuer_name () const ; 1647bool check_hostname ( const char * hostname) const; 1648std :: vector < SanEntry > sans () const ; 1649bool validity ( time_t & not_before, time_t & not_after) const; 1650std :: string serial () const ; 1651}; 1652 1653using VerifyCallback = std::function < bool (const VerifyContext & ctx) > ; 1654 1655// TlsError codes for TLS operations (backend-independent) 1656enum class ErrorCode : int { 1657Success = 0 , 1658WantRead, // Non-blocking: need to wait for read 1659WantWrite, // Non-blocking: need to wait for write 1660PeerClosed, // Peer closed the connection 1661Fatal, // Unrecoverable error 1662SyscallError, // System call error (check sys_errno) 1663CertVerifyFailed, // Certificate verification failed 1664HostnameMismatch, // Hostname verification failed 1665}; 1666 1667// TLS error information 1668struct TlsError { 1669ErrorCode code = ErrorCode ::Fatal; 1670uint64_t backend_code = 0 ; // OpenSSL: ERR_get_error(), mbedTLS: return value 1671int sys_errno = 0 ; // errno when SyscallError 1672 1673// Convert verification error code to human-readable string 1674static std :: string verify_error_to_string ( long error_code); 1675}; 1676 1677// RAII wrapper for peer certificate 1678class PeerCert { 1679public : 1680PeerCert (); 1681PeerCert (PeerCert && other) noexcept; 1682PeerCert & operator = (PeerCert && other) noexcept; 1683~ PeerCert (); 1684 1685PeerCert( const PeerCert & ) = delete; 1686PeerCert & operator = ( const PeerCert & ) = delete; 1687 1688explicit operator bool () const ; 1689std :: string subject_cn () const; 1690std :: string issuer_name () const; 1691bool check_hostname ( const char * hostname) const; 1692std ::vector < SanEntry > sans () const; 1693bool validity ( time_t & not_before, time_t & not_after) const; 1694std :: string serial () const; 1695 1696private : 1697explicit PeerCert ( cert_t cert); 1698cert_t cert_ = nullptr ; 1699friend PeerCert get_peer_cert_from_session ( const_session_t session); 1700}; 1701 1702// Callback for TLS context setup (used by SSLServer constructor) 1703using ContextSetupCallback = std::function < bool (ctx_t ctx) > ; 1704 1705} // namespace tls 1706#endif 1707 1708struct Request { 1709std :: string method ; 1710std :: string path ; 1711std :: string matched_route ; 1712Params params ; 1713Headers headers ; 1714Headers trailers ; 1715std :: string body ; 1716 1717std :: string remote_addr ; 1718int remote_port = -1 ; 1719std :: string local_addr ; 1720int local_port = -1 ; 1721 1722// for server 1723std :: string version ; 1724std :: string target ; 1725MultipartFormData form ; 1726Ranges ranges ; 1727Match matches ; 1728std :: unordered_map < std ::string, std ::string > path_params; 1729std :: function < bool () > is_connection_closed = []() { return true; }; 1730 1731// for client 1732std ::vector < std::string > accept_content_types; 1733ResponseHandler response_handler; 1734ContentReceiverWithProgress content_receiver; 1735DownloadProgress download_progress; 1736UploadProgress upload_progress; 1737 1738bool has_header ( const std ::string & key) const; 1739std :: string get_header_value( const std ::string & key, const char * def = "" , 1740size_t id = 0 ) const ; 1741size_t get_header_value_u64 ( const std ::string & key, size_t def = 0 , 1742size_t id = 0 ) const ; 1743size_t get_header_value_count ( const std ::string & key) const; 1744void set_header ( const std ::string & key, const std ::string & val); 1745 1746bool has_trailer ( const std ::string & key) const; 1747std :: string get_trailer_value( const std ::string & key, size_t id = 0 ) const ; 1748size_t get_trailer_value_count ( const std ::string & key) const; 1749 1750bool has_param ( const std ::string & key) const; 1751std :: string get_param_value( const std ::string & key, size_t id = 0 ) const ; 1752std ::vector < std::string > get_param_values (const std::string & key) const; 1753size_t get_param_value_count ( const std ::string & key) const; 1754 1755bool is_multipart_form_data () const ; 1756 1757// private members... 1758bool body_consumed_ = false; 1759size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT ; 1760size_t content_length_ = 0 ; 1761ContentProvider content_provider_; 1762bool is_chunked_content_provider_ = false; 1763size_t authorization_count_ = 0 ; 1764std ::chrono::time_point < std::chrono::steady_clock > start_time_ = 1765(std::chrono::steady_clock::time_point::min)(); 1766 1767#ifdef CPPHTTPLIB_SSL_ENABLED 1768tls::const_session_t ssl = nullptr ; 1769tls ::PeerCert peer_cert () const; 1770std::string sni () const; 1771#endif 1772}; 1773 1774namespace detail { 1775 1776// Declared up here, away from the rest of the compression helpers, because 1777// `Response` stores one. 1778enum class EncodingType { None = 0 , Gzip, Brotli, Zstd }; 1779 1780} // namespace detail 1781 1782struct Response { 1783std :: string version ; 1784int status = -1 ; 1785std :: string reason ; 1786Headers headers ; 1787Headers trailers ; 1788std :: string body ; 1789std :: string location ; // Redirect location 1790 1791// User-defined context — set by pre-routing/pre-request handlers and read 1792// by route handlers to pass arbitrary data (e.g. decoded auth tokens). 1793UserData user_data ; 1794 1795bool has_header ( const std ::string & key) const ; 1796std :: string get_header_value ( const std ::string & key, const char * def = "", 1797size_t id = 0 ) const ; 1798size_t get_header_value_u64 ( const std ::string & key, size_t def = 0 , 1799size_t id = 0 ) const ; 1800size_t get_header_value_count ( const std ::string & key) const ; 1801void set_header ( const std ::string & key, const std::string & val); 1802 1803bool has_trailer ( const std ::string & key) const ; 1804std :: string get_trailer_value ( const std ::string & key, size_t id = 0 ) const ; 1805size_t get_trailer_value_count ( const std ::string & key) const ; 1806 1807void set_redirect ( const std ::string & url, int status = StatusCode ::Found_302); 1808void set_content ( const char * s, size_t n, const std ::string & content_type); 1809void set_content ( const std ::string & s, const std::string & content_type); 1810void set_content ( std ::string && s, const std::string & content_type); 1811 1812void set_content_provider ( 1813size_t length, const std ::string & content_type, ContentProvider provider, 1814ContentProviderResourceReleaser resource_releaser = nullptr); 1815 1816void set_content_provider ( 1817const std ::string & content_type, ContentProviderWithoutLength provider, 1818ContentProviderResourceReleaser resource_releaser = nullptr); 1819 1820void set_chunked_content_provider ( 1821const std ::string & content_type, ContentProviderWithoutLength provider, 1822ContentProviderResourceReleaser resource_releaser = nullptr); 1823 1824void set_file_content ( const std ::string & path, 1825const std::string & content_type); 1826void set_file_content ( const std ::string & path); 1827 1828Response () = default ; 1829Response( const Response & ) = default ; 1830Response & operator = ( const Response & ) = default ; 1831Response( Response && ) = default ; 1832Response & operator = ( Response && ) = default ; 1833~Response() { 1834if ( content_provider_resource_releaser_ ) { 1835content_provider_resource_releaser_ ( content_provider_success_ ); 1836} 1837} 1838 1839// private members... 1840size_t content_length_ = 0 ; 1841ContentProvider content_provider_; 1842ContentProviderResourceReleaser content_provider_resource_releaser_; 1843bool is_chunked_content_provider_ = false; 1844bool content_provider_success_ = false; 1845std :: string file_content_path_; 1846std :: string file_content_content_type_; 1847 1848// Content coding chosen for the response body, decided once so that the 1849// headers and the body cannot disagree: where the file is opened for a 1850// file-backed content provider (keeping the ETag honest), and in 1851// `apply_ranges()` for a chunked content provider. `EncodingType::None` 1852// for every other kind of response. 1853detail :: EncodingType content_coding_ = detail:: EncodingType ::None; 1854}; 1855 1856enum class Error { 1857Success = 0 , 1858Unknown, 1859Connection, 1860BindIPAddress, 1861Read, 1862Write, 1863ExceedRedirectCount, 1864Canceled, 1865SSLConnection, 1866SSLLoadingCerts, 1867SSLServerVerification, 1868SSLServerHostnameVerification, 1869UnsupportedMultipartBoundaryChars, 1870Compression, 1871ConnectionTimeout, 1872ProxyConnection, 1873ConnectionClosed, 1874Timeout, 1875ResourceExhaustion, 1876TooManyFormDataFiles, 1877ExceedMaxPayloadSize, 1878ExceedUriMaxLength, 1879ExceedMaxSocketDescriptorCount, 1880InvalidRequestLine, 1881InvalidHTTPMethod, 1882InvalidHTTPVersion, 1883InvalidHeaders, 1884MultipartParsing, 1885OpenFile, 1886Listen, 1887GetSockName, 1888UnsupportedAddressFamily, 1889HTTPParsing, 1890InvalidRangeHeader, 1891UnsupportedContentEncoding, 1892WebSocketHandshake, 1893UserCallbackException, 1894 1895// For internal use only 1896SSLPeerCouldBeClosed_, 1897}; 1898 1899std :: string to_string ( Error error); 1900 1901std ::ostream & operator<<(std::ostream & os, const Error & obj); 1902 1903class Stream { 1904public : 1905virtual ~ Stream () = default; 1906 1907virtual bool is_readable () const = 0 ; 1908virtual bool wait_readable () const = 0 ; 1909virtual bool wait_writable () const = 0 ; 1910virtual bool is_peer_alive () const { return wait_writable (); } 1911 1912virtual ssize_t read ( char * ptr, size_t size) = 0 ; 1913virtual ssize_t write ( const char * ptr, size_t size) = 0 ; 1914virtual void get_remote_ip_and_port ( std ::string & ip, int & port) const = 0 ; 1915virtual void get_local_ip_and_port ( std ::string & ip, int & port) const = 0 ; 1916virtual socket_t socket () const = 0 ; 1917 1918virtual time_t duration () const = 0 ; 1919 1920virtual void set_read_timeout ( time_t sec, time_t usec = 0 ) { 1921( void )sec; 1922(void)usec; 1923} 1924 1925// Bytes already pulled off the socket and sitting in this stream's own 1926// buffer. Exposing them lets a line reader scan for a terminator in one 1927// pass instead of asking for a byte at a time. A stream that does no 1928// buffering of its own reports none, and readers fall back to read(). 1929virtual const char * buffered_data ( size_t & size) const { 1930size = 0 ; 1931return nullptr; 1932} 1933 1934// Discards `size` bytes previously returned by buffered_data(). 1935virtual void consume_buffered ( size_t size) { ( void )size; } 1936 1937ssize_t write ( const char * ptr); 1938ssize_t write ( const std ::string & s); 1939 1940Error get_error () const { return error_; } 1941 1942protected : 1943Error error_ = Error::Success; 1944}; 1945 1946class TaskQueue { 1947public : 1948TaskQueue () = default; 1949virtual ~ TaskQueue () = default; 1950 1951virtual bool enqueue ( std ::function < void () > fn) = 0 ; 1952virtual void shutdown () = 0 ; 1953 1954virtual void on_idle () {} 1955}; 1956 1957class ThreadPool final : public TaskQueue { 1958public : 1959explicit ThreadPool ( 1960size_t n, size_t max_n = 0 , size_t mqr = 0 , 1961time_t idle_timeout_sec = CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT ); 1962ThreadPool( const ThreadPool & ) = delete; 1963~ ThreadPool () override = default; 1964 1965bool enqueue ( std ::function < void () > fn) override; 1966void shutdown () override ; 1967 1968private : 1969void worker ( bool is_dynamic); 1970void move_to_finished ( std ::thread::id id); 1971void cleanup_finished_threads (); 1972 1973size_t base_thread_count_; 1974size_t max_thread_count_; 1975size_t max_queued_requests_; 1976time_t idle_timeout_sec_; 1977size_t idle_thread_count_; 1978 1979bool shutdown_; 1980 1981std ::list < std::function < void ()>> jobs_; 1982std ::vector < std::thread > threads_; // base threads 1983std ::list < std::thread > dynamic_threads_; // dynamic threads 1984std ::vector < std::thread > 1985finished_threads_; // exited dynamic threads awaiting join 1986 1987std :: condition_variable cond_; 1988std :: mutex mutex_; 1989}; 1990 1991using Logger = std::function < void (const Request & , const Response & ) > ; 1992 1993// Forward declaration for Error type 1994enum class Error; 1995using ErrorLogger = std::function < void (const Error & , const Request * ) > ; 1996 1997using SocketOptions = std::function < void (socket_t sock) > ; 1998 1999void default_socket_options ( socket_t sock); 2000 2001bool set_socket_opt ( socket_t sock, int level, int optname, int optval); 2002 2003const char * status_message ( int status); 2004 2005std :: string to_string ( Error error); 2006 2007std ::ostream & operator<<(std::ostream & os, const Error & obj); 2008 2009std :: string get_bearer_token_auth ( const Request & req); 2010 2011namespace detail { 2012 2013class MatcherBase { 2014public : 2015MatcherBase ( std :: string pattern) : pattern_( std :: move ( pattern )) {} 2016virtual ~ MatcherBase () = default; 2017 2018const std ::string & pattern () const { return pattern_; } 2019 2020// Match request path and populate its matches and 2021virtual bool match (Request & request) const = 0 ; 2022 2023private : 2024std :: string pattern_; 2025}; 2026 2027/** 2028* Captures parameters in request path and stores them in Request::path_params 2029* 2030* Capture name is a substring of a pattern from : to /. 2031* The rest of the pattern is matched against the request path directly 2032* Parameters are captured starting from the next character after 2033* the end of the last matched static pattern fragment until the next /. 2034* 2035* Example pattern: 2036* "/path/fragments/:capture/more/fragments/:second_capture" 2037* Static fragments: 2038* "/path/fragments/", "more/fragments/" 2039* 2040* Given the following request path: 2041* "/path/fragments/:1/more/fragments/:2" 2042* the resulting capture will be 2043* {{"capture", "1"}, {"second_capture", "2"}} 2044*/ 2045class PathParamsMatcher final : public MatcherBase { 2046public : 2047PathParamsMatcher ( const std ::string & pattern); 2048 2049bool match ( Request & request) const override; 2050 2051private : 2052// Treat segment separators as the end of path parameter capture 2053// Does not need to handle query parameters as they are parsed before path 2054// matching 2055static constexpr char separator = '/' ; 2056 2057// Contains static path fragments to match against, excluding the '/' after 2058// path params 2059// Fragments are separated by path params 2060std ::vector < std::string > static_fragments_; 2061// Stores the names of the path parameters to be used as keys in the 2062// Request::path_params map 2063std ::vector < std::string > param_names_; 2064}; 2065 2066/** 2067* Performs std::regex_match on request path 2068* and stores the result in Request::matches 2069* 2070* Note that regex match is performed directly on the whole request. 2071* This means that wildcard patterns may match multiple path segments with /: 2072* "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end". 2073*/ 2074class RegexMatcher final : public MatcherBase { 2075public : 2076RegexMatcher ( const std ::string & pattern) 2077: MatcherBase (pattern), regex_ (pattern) {} 2078 2079bool match ( Request & request) const override; 2080 2081private : 2082std :: regex regex_; 2083}; 2084 2085int close_socket ( socket_t sock) noexcept; 2086 2087bool is_accept_resource_error (); 2088 2089bool is_accept_transient_error (); 2090 2091ssize_t write_headers ( Stream & strm, const Headers & headers); 2092 2093bool set_socket_opt_time ( socket_t sock, int level, int optname, time_t sec, 2094time_t usec); 2095 2096size_t get_multipart_content_length ( const UploadFormDataItems & items, 2097const std ::string & boundary); 2098 2099ContentProvider 2100make_multipart_content_provider ( const UploadFormDataItems & items, 2101const std ::string & boundary); 2102 2103} // namespace detail 2104 2105bool is_valid_multipart_boundary ( const std ::string & boundary); 2106 2107// Serializer for multipart/form-data request bodies. The boundary is owned 2108// by the writer so that per-part framing and the final terminator always 2109// agree. Field names and filenames are escaped following the WHATWG HTML 2110// standard ('"' -> %22, CR -> %0D, LF -> %0A); CR and LF are also escaped 2111// in content types. 2112class MultipartFormDataWriter { 2113public : 2114MultipartFormDataWriter (); 2115// precondition: is_valid_multipart_boundary(boundary) 2116explicit MultipartFormDataWriter ( std ::string boundary); 2117 2118const std ::string & boundary () const ; 2119std :: string content_type () const; 2120 2121// In-memory items -> whole body (known length) 2122std :: string serialize ( const UploadFormDataItems & items) const; 2123size_t content_length ( const UploadFormDataItems & items) const; 2124 2125// Per-part framing for streaming via a content provider 2126std :: string item_begin ( const UploadFormData & item) const; 2127static std ::string item_end (); 2128std :: string finish () const; 2129 2130private : 2131std :: string boundary_; 2132}; 2133 2134class Server { 2135public : 2136using Handler = std::function < void (const Request & , Response & ) > ; 2137 2138using ExceptionHandler = 2139std::function < void (const Request & , Response & , std::exception_ptr ep) > ; 2140 2141enum class HandlerResponse { 2142Handled, 2143Unhandled, 2144}; 2145using HandlerWithResponse = 2146std::function < HandlerResponse (const Request & , Response & ) > ; 2147 2148using HandlerWithContentReader = std::function < void ( 2149const Request & , Response & , const ContentReader & content_reader) > ; 2150 2151using Expect100ContinueHandler = 2152std::function < int (const Request & , Response & ) > ; 2153 2154using StartHandler = std::function < void () > ; 2155 2156using WebSocketHandler = 2157std::function < void (const Request & , ws::WebSocket & ) > ; 2158using SubProtocolSelector = 2159std::function < std:: string (const std::vector < std::string > & protocols) > ; 2160 2161Server (); 2162 2163virtual ~ Server (); 2164 2165virtual bool is_valid () const ; 2166 2167Server & Get (const std::string & pattern, Handler handler); 2168Server & Post (const std::string & pattern, Handler handler); 2169Server & Post (const std::string & pattern, HandlerWithContentReader handler); 2170Server & Put (const std::string & pattern, Handler handler); 2171Server & Put (const std::string & pattern, HandlerWithContentReader handler); 2172Server & Patch (const std::string & pattern, Handler handler); 2173Server & Patch (const std::string & pattern, HandlerWithContentReader handler); 2174Server & Delete (const std::string & pattern, Handler handler); 2175Server & Delete (const std::string & pattern, HandlerWithContentReader handler); 2176Server & Options (const std::string & pattern, Handler handler); 2177 2178// Register a handler for an HTTP method outside the built-in set (e.g. the 2179// WebDAV methods from RFC 4918). Registering a method here is what makes the 2180// server accept it; an unregistered method is still rejected with 400. 2181// `method` must be a valid HTTP method token and must not be one of the 2182// built-in methods, which have their own registration functions above. A 2183// rejected registration makes is_valid() return false, so listen() fails. 2184Server & CustomRoute (const std::string & method, const std::string & pattern, 2185Handler handler); 2186Server & CustomRoute (const std::string & method, const std::string & pattern, 2187HandlerWithContentReader handler); 2188 2189Server & WebSocket (const std::string & pattern, WebSocketHandler handler); 2190Server & WebSocket (const std::string & pattern, WebSocketHandler handler, 2191SubProtocolSelector sub_protocol_selector); 2192 2193bool set_base_dir ( const std ::string & dir, 2194const std ::string & mount_point = std:: string ()); 2195bool set_mount_point ( const std ::string & mount_point, const std ::string & dir, 2196Headers headers = Headers ()); 2197bool remove_mount_point ( const std ::string & mount_point); 2198Server & set_file_extension_and_mimetype_mapping (const std::string & ext, 2199const std::string & mime); 2200Server & set_default_file_mimetype (const std::string & mime); 2201Server & set_file_request_handler (Handler handler); 2202 2203template < class ErrorHandlerFunc > 2204Server & set_error_handler (ErrorHandlerFunc && handler) { 2205return set_error_handler_core ( 2206std::forward < ErrorHandlerFunc > (handler), 2207std::is_convertible < ErrorHandlerFunc, HandlerWithResponse > {}); 2208} 2209 2210Server & set_exception_handler (ExceptionHandler handler); 2211 2212Server & set_pre_routing_handler (HandlerWithResponse handler); 2213Server & set_post_routing_handler (Handler handler); 2214 2215Server & set_pre_request_handler (HandlerWithResponse handler); 2216 2217Server & set_expect_100_continue_handler (Expect100ContinueHandler handler); 2218 2219Server & set_start_handler (StartHandler handler); 2220 2221Server & set_logger (Logger logger); 2222Server & set_pre_compression_logger (Logger logger); 2223Server & set_error_logger (ErrorLogger error_logger); 2224 2225Server & set_address_family (int family); 2226Server & set_tcp_nodelay (bool on); 2227Server & set_ipv6_v6only (bool on); 2228Server & set_socket_options (SocketOptions socket_options); 2229 2230Server & set_default_headers (Headers headers); 2231Server & 2232set_header_writer (std::function < ssize_t (Stream & , Headers & ) > const & writer); 2233 2234Server & set_trusted_proxies (const std::vector < std::string > & proxies); 2235 2236Server & set_keep_alive_max_count (size_t count); 2237Server & set_keep_alive_timeout (time_t sec); 2238template < class Rep, class Period > 2239Server & 2240set_keep_alive_timeout (const std::chrono::duration < Rep, Period > & duration); 2241 2242Server & set_read_timeout (time_t sec, time_t usec = 0 ); 2243template < class Rep, class Period > 2244Server & set_read_timeout (const std::chrono::duration < Rep, Period > & duration); 2245 2246Server & set_write_timeout (time_t sec, time_t usec = 0 ); 2247template < class Rep, class Period > 2248Server & set_write_timeout (const std::chrono::duration < Rep, Period > & duration); 2249 2250Server & set_idle_interval (time_t sec, time_t usec = 0 ); 2251template < class Rep, class Period > 2252Server & set_idle_interval (const std::chrono::duration < Rep, Period > & duration); 2253 2254Server & set_payload_max_length (size_t length); 2255 2256Server & set_static_file_compression (bool on); 2257Server & set_static_file_compression_min_length (size_t length); 2258Server & set_static_file_compression_max_length (size_t length); 2259 2260Server & set_websocket_ping_interval (time_t sec); 2261template < class Rep, class Period > 2262Server & set_websocket_ping_interval ( 2263const std::chrono::duration < Rep, Period > & duration); 2264 2265Server & set_websocket_max_missed_pongs (int count); 2266 2267bool bind_to_port ( const std ::string & host, int port, int socket_flags = 0 ); 2268int bind_to_any_port ( const std ::string & host, int socket_flags = 0 ); 2269bool listen_after_bind (); 2270 2271bool listen ( const std ::string & host, int port, int socket_flags = 0 ); 2272 2273bool is_running () const ; 2274void wait_until_ready () const; 2275void stop () noexcept; 2276void decommission (); 2277 2278std ::function < TaskQueue * (void) > new_task_queue; 2279 2280protected : 2281bool process_request ( Stream & strm, const std ::string & remote_addr, 2282int remote_port, const std ::string & local_addr, 2283int local_port, bool close_connection, 2284bool & connection_closed, 2285const std ::function < void (Request & ) > & setup_request, 2286bool * websocket_upgraded = nullptr ); 2287 2288// Runs the per-connection serving loop and stops an exception thrown by a 2289// user callback from escaping the worker thread. 2290// 2291// process_request() wraps only routing() in a try/catch. Content providers, 2292// the post-routing, error, logging and expect-100 handlers and WebSocket 2293// handlers all run outside it, and the task queue calls the job without a 2294// catch, so an exception from any of those would terminate the process. 2295// 2296// No 500 is possible here: by the time a content provider runs, the status 2297// line and headers are already on the wire. Report it through the error 2298// logger and drop the connection, which is what the peer observes either 2299// way. Other connections are unaffected. 2300template < typename Serve > bool serve_guarded ( Serve && serve) const { 2301#ifdef CPPHTTPLIB_NO_EXCEPTIONS 2302return serve (); 2303#else 2304try { 2305return serve (); 2306} catch (...) { 2307// The error logger is a user callback too, so it must not be able to 2308// throw the guard back open. 2309try { 2310output_error_log(Error::UserCallbackException, nullptr); 2311} catch (...) {} 2312return false; 2313} 2314#endif 2315} 2316 2317std ::atomic < socket_t > svr_sock_{ INVALID_SOCKET }; 2318 2319std ::vector < std::string > trusted_proxies_; 2320 2321size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT ; 2322time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND ; 2323time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND ; 2324time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND ; 2325time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND ; 2326time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND ; 2327time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND ; 2328time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND ; 2329size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH ; 2330bool static_file_compression_ = false; 2331size_t static_file_compression_min_length_ = 2332CPPHTTPLIB_STATIC_FILE_COMPRESSION_MIN_LENGTH ; 2333size_t static_file_compression_max_length_ = 2334CPPHTTPLIB_STATIC_FILE_COMPRESSION_MAX_LENGTH ; 2335time_t websocket_ping_interval_sec_ = 2336CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND ; 2337int websocket_max_missed_pongs_ = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS ; 2338 2339private : 2340using Handlers = 2341std::vector < std::pair < std::unique_ptr < detail::MatcherBase > , Handler>>; 2342using HandlersForContentReader = 2343std::vector < std::pair < std::unique_ptr < detail::MatcherBase > , 2344HandlerWithContentReader>>; 2345 2346// Both handler tables for one custom method live in a single entry, so that 2347// routing() needs only one map lookup per request to reach either of them. 2348struct CustomHandlerEntry { 2349Handlers handlers ; 2350HandlersForContentReader handlers_for_content_reader ; 2351}; 2352using CustomHandlers = std::map < std::string, CustomHandlerEntry > ; 2353 2354static std ::unique_ptr < detail::MatcherBase > 2355make_matcher ( const std ::string & pattern); 2356 2357static const std ::set < std::string > & builtin_methods (); 2358CustomHandlerEntry * custom_entry_for_registration ( const std ::string & method); 2359const CustomHandlerEntry * find_custom_entry ( const std ::string & method) const; 2360 2361template < typename H > 2362Server & add_handler ( 2363std::vector < std::pair < std::unique_ptr < detail::MatcherBase > , H >> & handlers, 2364const std::string & pattern, H handler) { 2365handlers. emplace_back ( make_matcher (pattern), std:: move (handler)); 2366return * this; 2367} 2368 2369Server & set_error_handler_core (HandlerWithResponse handler, std::true_type); 2370Server & set_error_handler_core (Handler handler, std::false_type); 2371 2372socket_t create_server_socket ( const std ::string & host, int port, 2373int socket_flags, 2374SocketOptions socket_options) const; 2375int bind_internal ( const std ::string & host, int port, int socket_flags); 2376bool listen_internal (); 2377 2378bool routing ( Request & req, Response & res, Stream & strm); 2379bool handle_file_request ( Request & req, Response & res); 2380bool check_if_not_modified ( const Request & req, Response & res, 2381const std ::string & etag, time_t mtime) const; 2382bool check_if_range ( Request & req, const std ::string & etag, 2383time_t mtime) const; 2384bool dispatch_request ( Request & req, Response & res, const Handlers & handlers, 2385Stream & strm); 2386bool dispatch_request_for_content_reader ( 2387Request & req, Response & res, ContentReader content_reader, 2388const HandlersForContentReader & handlers) const; 2389 2390bool parse_request_line ( const char * s, Request & req) const; 2391detail :: EncodingType static_file_encoding ( const Request & req, 2392const Response & res, 2393const std ::string & content_type, 2394size_t length) const; 2395bool apply_static_file_compression ( const Request & req, Response & res) const; 2396void apply_ranges ( const Request & req, Response & res, 2397std ::string & content_type, std ::string & boundary) const; 2398bool write_response ( Stream & strm, bool close_connection, Request & req, 2399Response & res); 2400bool write_response_with_content ( Stream & strm, bool close_connection, 2401const Request & req, Response & res); 2402bool write_response_core ( Stream & strm, bool close_connection, 2403const Request & req, Response & res, 2404bool need_apply_ranges); 2405bool write_content_with_provider ( Stream & strm, const Request & req, 2406Response & res, const std ::string & boundary, 2407const std ::string & content_type); 2408bool read_content ( Stream & strm, Request & req, Response & res); 2409bool read_content_with_content_receiver ( Stream & strm, Request & req, 2410Response & res, 2411ContentReceiver receiver, 2412FormDataHeader multipart_header, 2413ContentReceiver multipart_receiver); 2414bool read_content_core ( Stream & strm, Request & req, Response & res, 2415ContentReceiver receiver, 2416FormDataHeader multipart_header, 2417ContentReceiver multipart_receiver) const; 2418 2419virtual bool process_and_close_socket ( socket_t sock); 2420 2421void output_log ( const Request & req, const Response & res) const; 2422void output_pre_compression_log ( const Request & req, 2423const Response & res) const; 2424void output_error_log ( const Error & err, const Request * req) const; 2425 2426std ::atomic < bool > is_running_{false}; 2427std ::atomic < bool > is_decommissioned{false}; 2428 2429// Set when CustomRoute() refuses a registration. Written before listen(), 2430// read by is_valid() on the same thread, so it needs no synchronization. 2431bool has_invalid_registration_ = false; 2432 2433struct MountPointEntry { 2434std :: string mount_point ; 2435std :: string base_dir ; 2436std :: string resolved_base_dir ; 2437Headers headers ; 2438}; 2439std ::vector < MountPointEntry > base_dirs_; 2440std ::map < std::string, std::string > file_extension_and_mimetype_map_; 2441std :: string default_file_mimetype_ = "application/octet-stream" ; 2442Handler file_request_handler_; 2443 2444Handlers get_handlers_; 2445Handlers post_handlers_; 2446HandlersForContentReader post_handlers_for_content_reader_; 2447Handlers put_handlers_; 2448HandlersForContentReader put_handlers_for_content_reader_; 2449Handlers patch_handlers_; 2450HandlersForContentReader patch_handlers_for_content_reader_; 2451Handlers delete_handlers_; 2452HandlersForContentReader delete_handlers_for_content_reader_; 2453Handlers options_handlers_; 2454CustomHandlers custom_handlers_; 2455 2456struct WebSocketHandlerEntry { 2457std :: unique_ptr < detail ::MatcherBase > matcher; 2458WebSocketHandler handler ; 2459SubProtocolSelector sub_protocol_selector ; 2460}; 2461using WebSocketHandlers = std::vector < WebSocketHandlerEntry > ; 2462WebSocketHandlers websocket_handlers_; 2463 2464HandlerWithResponse error_handler_; 2465ExceptionHandler exception_handler_; 2466HandlerWithResponse pre_routing_handler_; 2467Handler post_routing_handler_; 2468HandlerWithResponse pre_request_handler_; 2469Expect100ContinueHandler expect_100_continue_handler_; 2470StartHandler start_handler_; 2471 2472mutable std::mutex logger_mutex_; 2473Logger logger_; 2474Logger pre_compression_logger_; 2475ErrorLogger error_logger_; 2476 2477int address_family_ = AF_UNSPEC ; 2478bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY ; 2479bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY ; 2480SocketOptions socket_options_ = default_socket_options; 2481 2482Headers default_headers_; 2483std ::function < ssize_t (Stream & , Headers & ) > header_writer_ = 2484detail::write_headers; 2485}; 2486 2487class Result { 2488public : 2489Result () = default; 2490Result ( std ::unique_ptr < Response > && res, Error err, 2491Headers && request_headers = Headers{}) 2492: res_( std :: move ( res )), err_ ( err ), 2493request_headers_ ( std :: move ( request_headers )) {} 2494// Response 2495operator bool () const { return res_ != nullptr ; } 2496bool operator == (std:: nullptr_t ) const { return res_ == nullptr ; } 2497bool operator != (std:: nullptr_t ) const { return res_ != nullptr ; } 2498const Response & value () const { return * res_; } 2499Response & value () { return * res_; } 2500const Response & operator * () const { return * res_; } 2501Response & operator * () { return * res_; } 2502const Response * operator -> () const { return res_ .get(); } 2503Response * operator -> () { return res_ .get(); } 2504 2505// Error 2506Error error () const { return err_ ; } 2507 2508// Request Headers 2509bool has_request_header ( const std :: string & key ) const ; 2510std :: string get_request_header_value ( const std :: string & key , 2511const char * def = "" , 2512size_t id = 0 ) const ; 2513size_t get_request_header_value_u64 ( const std :: string & key , size_t def = 0 , 2514size_t id = 0 ) const ; 2515size_t get_request_header_value_count( const std :: string & key ) const ; 2516 2517private : 2518std :: unique_ptr < Response > res_ ; 2519Error err_ = Error :: Unknown ; 2520Headers request_headers_ ; 2521 2522#ifdef CPPHTTPLIB_SSL_ENABLED 2523public : 2524Result ( std :: unique_ptr < Response > && res , Error err , Headers && request_headers , 2525int ssl_error ) 2526: res_( std ::move( res )), err_( err ), 2527request_headers_( std ::move( request_headers )), ssl_error_( ssl_error ) {} 2528Result( std :: unique_ptr < Response > && res , Error err , Headers && request_headers , 2529int ssl_error , uint64_t ssl_backend_error ) 2530: res_ ( std :: move ( res )), err_( err ), 2531request_headers_( std ::move( request_headers )), ssl_error_( ssl_error ), 2532ssl_backend_error_( ssl_backend_error ) {} 2533 2534int ssl_error() const { return ssl_error_ ; } 2535uint64_t ssl_backend_error() const { return ssl_backend_error_ ; } 2536 2537private : 2538int ssl_error_ = 0 ; 2539uint64_t ssl_backend_error_ = 0 ; 2540#endif 2541}; 2542 2543struct ClientConnection { 2544socket_t sock = INVALID_SOCKET ; 2545 2546bool is_open () const { return sock != INVALID_SOCKET ; } 2547 2548ClientConnection() = default ; 2549 2550~ClientConnection(); 2551 2552ClientConnection ( const ClientConnection & ) = delete ; 2553ClientConnection & operator = ( const ClientConnection & ) = delete ; 2554 2555ClientConnection( ClientConnection && other ) noexcept 2556: sock( other . sock ) 2557#ifdef CPPHTTPLIB_SSL_ENABLED 2558, 2559session( other . session ) 2560#endif 2561{ 2562other . sock = INVALID_SOCKET ; 2563#ifdef CPPHTTPLIB_SSL_ENABLED 2564other . session = nullptr ; 2565#endif 2566} 2567 2568ClientConnection & operator = ( ClientConnection && other ) noexcept { 2569if ( this != & other ) { 2570sock = other . sock ; 2571other . sock = INVALID_SOCKET ; 2572#ifdef CPPHTTPLIB_SSL_ENABLED 2573session = other . session ; 2574other . session = nullptr ; 2575#endif 2576} 2577return * this ; 2578} 2579 2580#ifdef CPPHTTPLIB_SSL_ENABLED 2581tls :: session_t session = nullptr ; 2582#endif 2583}; 2584 2585namespace detail { 2586 2587struct ChunkedDecoder ; 2588 2589struct BodyReader { 2590Stream * stream = nullptr; 2591bool has_content_length = false; 2592size_t content_length = 0 ; 2593size_t payload_max_length = CPPHTTPLIB_PAYLOAD_MAX_LENGTH ; 2594size_t bytes_read = 0 ; 2595bool chunked = false; 2596bool eof = false; 2597std :: unique_ptr < ChunkedDecoder > chunked_decoder ; 2598Error last_error = Error :: Success ; 2599 2600ssize_t read ( char * buf , size_t len ); 2601bool has_error () const { return last_error != Error :: Success ; } 2602}; 2603 2604inline ssize_t read_body_content( Stream * stream , BodyReader & br , char * buf , 2605size_t len ) { 2606( void ) stream ; 2607return br .read( buf , len ); 2608} 2609 2610class decompressor ; 2611 2612enum class NoProxyKind { 2613Wildcard , // "*" 2614HostnameSuffix , // "example.com" or ".example.com" 2615IPv4Cidr , // "10.0.0.0/8" (or single IP, treated as /32) 2616IPv6Cidr , // "fe80::/10" (or single IP, treated as /128) 2617}; 2618 2619// Unified 16-byte buffer holding either a v4 (first 4 bytes) or v6 address. 2620// Lets one CIDR matcher cover both families. 2621using IPBytes = std :: array < uint8_t , 16 > ; 2622 2623struct NoProxyEntry { 2624NoProxyKind kind = NoProxyKind :: Wildcard ; 2625std :: string hostname_pattern ; // lowercased, leading/trailing dot stripped 2626IPBytes net {}; 2627int prefix_bits = 0 ; 2628}; 2629 2630struct NormalizedTarget { 2631std :: string hostname ; // lowercase; brackets and trailing dot removed 2632bool is_ipv4 = false; 2633bool is_ipv6 = false; 2634IPBytes ip {}; 2635}; 2636 2637} // namespace detail 2638 2639class ClientImpl { 2640public : 2641explicit ClientImpl( const std :: string & host ); 2642 2643explicit ClientImpl( const std :: string & host , int port ); 2644 2645explicit ClientImpl( const std :: string & host , int port , 2646const std :: string & client_cert_path , 2647const std :: string & client_key_path ); 2648 2649virtual ~ClientImpl(); 2650 2651virtual bool is_valid() const ; 2652 2653struct StreamHandle { 2654std :: unique_ptr < Response > response ; 2655Error error = Error :: Success ; 2656 2657StreamHandle () = default ; 2658StreamHandle ( const StreamHandle & ) = delete ; 2659StreamHandle & operator = ( const StreamHandle & ) = delete ; 2660StreamHandle ( StreamHandle && ) = default ; 2661StreamHandle & operator = ( StreamHandle && ) = default ; 2662~ StreamHandle () = default ; 2663 2664bool is_valid () const { 2665return response != nullptr && error == Error :: Success ; 2666} 2667 2668ssize_t read( char * buf , size_t len ); 2669void parse_trailers_if_needed(); 2670Error get_read_error() const { return body_reader_ . last_error ; } 2671bool has_read_error() const { return body_reader_ .has_error(); } 2672 2673bool trailers_parsed_ = false; 2674 2675private : 2676friend class ClientImpl ; 2677 2678ssize_t read_with_decompression( char * buf , size_t len ); 2679 2680std :: unique_ptr < ClientConnection > connection_ ; 2681std :: unique_ptr < Stream > socket_stream_ ; 2682Stream * stream_ = nullptr ; 2683detail :: BodyReader body_reader_ ; 2684 2685std :: unique_ptr < detail :: decompressor > decompressor_ ; 2686std :: string decompress_buffer_ ; 2687size_t decompress_offset_ = 0 ; 2688size_t decompressed_bytes_read_ = 0 ; 2689}; 2690 2691// clang-format off 2692Result Get( const std :: string & path , DownloadProgress progress = nullptr ); 2693Result Get( const std :: string & path , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2694Result Get( const std :: string & path , ResponseHandler response_handler , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2695Result Get( const std :: string & path , const Headers & headers , DownloadProgress progress = nullptr ); 2696Result Get( const std :: string & path , const Headers & headers , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2697Result Get( const std :: string & path , const Headers & headers , ResponseHandler response_handler , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2698Result Get( const std :: string & path , const Params & params , DownloadProgress progress = nullptr ); 2699Result Get( const std :: string & path , const Params & params , const Headers & headers , DownloadProgress progress = nullptr ); 2700Result Get( const std :: string & path , const Params & params , const Headers & headers , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2701Result Get( const std :: string & path , const Params & params , const Headers & headers , ResponseHandler response_handler , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2702 2703Result Head( const std :: string & path ); 2704Result Head( const std :: string & path , const Headers & headers ); 2705 2706Result Post( const std :: string & path ); 2707Result Post( const std :: string & path , const char * body , size_t content_length , const std :: string & content_type , UploadProgress progress = nullptr ); 2708Result Post( const std :: string & path , const std :: string & body , const std :: string & content_type , UploadProgress progress = nullptr ); 2709Result Post( const std :: string & path , size_t content_length , ContentProvider content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2710Result Post( const std :: string & path , size_t content_length , ContentProvider content_provider , const std :: string & content_type , ContentReceiver content_receiver , UploadProgress progress = nullptr ); 2711Result Post( const std :: string & path , ContentProviderWithoutLength content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2712Result Post( const std :: string & path , ContentProviderWithoutLength content_provider , const std :: string & content_type , ContentReceiver content_receiver , UploadProgress progress = nullptr ); 2713Result Post( const std :: string & path , const Params & params ); 2714Result Post( const std :: string & path , const UploadFormDataItems & items , UploadProgress progress = nullptr ); 2715Result Post( const std :: string & path , const Headers & headers ); 2716Result Post( const std :: string & path , const Headers & headers , const char * body , size_t content_length , const std :: string & content_type , UploadProgress progress = nullptr ); 2717Result Post( const std :: string & path , const Headers & headers , const std :: string & body , const std :: string & content_type , UploadProgress progress = nullptr ); 2718Result Post( const std :: string & path , const Headers & headers , size_t content_length , ContentProvider content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2719Result 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 ); 2720Result Post( const std :: string & path , const Headers & headers , ContentProviderWithoutLength content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2721Result Post( const std :: string & path , const Headers & headers , ContentProviderWithoutLength content_provider , const std :: string & content_type , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2722Result Post( const std :: string & path , const Headers & headers , const Params & params ); 2723Result Post( const std :: string & path , const Headers & headers , const UploadFormDataItems & items , UploadProgress progress = nullptr ); 2724Result Post( const std :: string & path , const Headers & headers , const UploadFormDataItems & items , const std :: string & boundary , UploadProgress progress = nullptr ); 2725Result Post( const std :: string & path , const Headers & headers , const UploadFormDataItems & items , const FormDataProviderItems & provider_items , UploadProgress progress = nullptr ); 2726Result Post( const std :: string & path , const Headers & headers , const std :: string & body , const std :: string & content_type , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2727 2728Result Put( const std :: string & path ); 2729Result Put( const std :: string & path , const char * body , size_t content_length , const std :: string & content_type , UploadProgress progress = nullptr ); 2730Result Put( const std :: string & path , const std :: string & body , const std :: string & content_type , UploadProgress progress = nullptr ); 2731Result Put( const std :: string & path , size_t content_length , ContentProvider content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2732Result Put( const std :: string & path , size_t content_length , ContentProvider content_provider , const std :: string & content_type , ContentReceiver content_receiver , UploadProgress progress = nullptr ); 2733Result Put( const std :: string & path , ContentProviderWithoutLength content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2734Result Put( const std :: string & path , ContentProviderWithoutLength content_provider , const std :: string & content_type , ContentReceiver content_receiver , UploadProgress progress = nullptr ); 2735Result Put( const std :: string & path , const Params & params ); 2736Result Put( const std :: string & path , const UploadFormDataItems & items , UploadProgress progress = nullptr ); 2737Result Put( const std :: string & path , const Headers & headers ); 2738Result Put( const std :: string & path , const Headers & headers , const char * body , size_t content_length , const std :: string & content_type , UploadProgress progress = nullptr ); 2739Result Put( const std :: string & path , const Headers & headers , const std :: string & body , const std :: string & content_type , UploadProgress progress = nullptr ); 2740Result Put( const std :: string & path , const Headers & headers , size_t content_length , ContentProvider content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2741Result 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 ); 2742Result Put( const std :: string & path , const Headers & headers , ContentProviderWithoutLength content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2743Result Put( const std :: string & path , const Headers & headers , ContentProviderWithoutLength content_provider , const std :: string & content_type , ContentReceiver content_receiver , UploadProgress progress = nullptr ); 2744Result Put( const std :: string & path , const Headers & headers , const Params & params ); 2745Result Put( const std :: string & path , const Headers & headers , const UploadFormDataItems & items , UploadProgress progress = nullptr ); 2746Result Put( const std :: string & path , const Headers & headers , const UploadFormDataItems & items , const std :: string & boundary , UploadProgress progress = nullptr ); 2747Result Put( const std :: string & path , const Headers & headers , const UploadFormDataItems & items , const FormDataProviderItems & provider_items , UploadProgress progress = nullptr ); 2748Result Put( const std :: string & path , const Headers & headers , const std :: string & body , const std :: string & content_type , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2749 2750Result Patch( const std :: string & path ); 2751Result Patch( const std :: string & path , const char * body , size_t content_length , const std :: string & content_type , UploadProgress progress = nullptr ); 2752Result Patch( const std :: string & path , const std :: string & body , const std :: string & content_type , UploadProgress progress = nullptr ); 2753Result Patch( const std :: string & path , size_t content_length , ContentProvider content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2754Result Patch( const std :: string & path , size_t content_length , ContentProvider content_provider , const std :: string & content_type , ContentReceiver content_receiver , UploadProgress progress = nullptr ); 2755Result Patch( const std :: string & path , ContentProviderWithoutLength content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2756Result Patch( const std :: string & path , ContentProviderWithoutLength content_provider , const std :: string & content_type , ContentReceiver content_receiver , UploadProgress progress = nullptr ); 2757Result Patch( const std :: string & path , const Params & params ); 2758Result Patch( const std :: string & path , const UploadFormDataItems & items , UploadProgress progress = nullptr ); 2759Result Patch( const std :: string & path , const Headers & headers , UploadProgress progress = nullptr ); 2760Result Patch( const std :: string & path , const Headers & headers , const char * body , size_t content_length , const std :: string & content_type , UploadProgress progress = nullptr ); 2761Result Patch( const std :: string & path , const Headers & headers , const std :: string & body , const std :: string & content_type , UploadProgress progress = nullptr ); 2762Result Patch( const std :: string & path , const Headers & headers , size_t content_length , ContentProvider content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2763Result 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 ); 2764Result Patch( const std :: string & path , const Headers & headers , ContentProviderWithoutLength content_provider , const std :: string & content_type , UploadProgress progress = nullptr ); 2765Result Patch( const std :: string & path , const Headers & headers , ContentProviderWithoutLength content_provider , const std :: string & content_type , ContentReceiver content_receiver , UploadProgress progress = nullptr ); 2766Result Patch( const std :: string & path , const Headers & headers , const Params & params ); 2767Result Patch( const std :: string & path , const Headers & headers , const UploadFormDataItems & items , UploadProgress progress = nullptr ); 2768Result Patch( const std :: string & path , const Headers & headers , const UploadFormDataItems & items , const std :: string & boundary , UploadProgress progress = nullptr ); 2769Result Patch( const std :: string & path , const Headers & headers , const UploadFormDataItems & items , const FormDataProviderItems & provider_items , UploadProgress progress = nullptr ); 2770Result Patch( const std :: string & path , const Headers & headers , const std :: string & body , const std :: string & content_type , ContentReceiver content_receiver , DownloadProgress progress = nullptr ); 2771 2772Result Delete( const std :: string & path , DownloadProgress progress = nullptr ); 2773Result Delete( const std :: string & path , const char * body , size_t content_length , const std :: string & content_type , DownloadProgress progress = nullptr ); 2774Result Delete( const std :: string & path , const std :: string & body , const std :: string & content_type , DownloadProgress progress = nullptr ); 2775Result Delete( const std :: string & path , const Params & params , DownloadProgress progress = nullptr ); 2776Result Delete( const std :: string & path , const Headers & headers , DownloadProgress progress = nullptr ); 2777Result Delete( const std :: string & path , const Headers & headers , const char * body , size_t content_length , const std :: string & content_type , DownloadProgress progress = nullptr ); 2778Result Delete( const std :: string & path , const Headers & headers , const std :: string & body , const std :: string & content_type , DownloadProgress progress = nullptr ); 2779Result Delete( const std :: string & path , const Headers & headers , const Params & params , DownloadProgress progress = nullptr ); 2780 2781Result Options( const std :: string & path ); 2782Result Options( const std :: string & path , const Headers & headers ); 2783// clang-format on 2784 2785// Streaming API: Open a stream for reading response body incrementally 2786// Socket ownership is transferred to StreamHandle for true streaming 2787// Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.) 2788StreamHandle open_stream( const std :: string & method , const std :: string & path , 2789const Params & params = {}, 2790const Headers & headers = {}, 2791const std :: string & body = {}, 2792const std :: string & content_type = {}); 2793 2794bool send( Request & req , Response & res , Error & error ); 2795Result send( const Request & req ); 2796 2797void stop(); 2798 2799std :: string host() const ; 2800int port() const ; 2801 2802size_t is_socket_open() const ; 2803socket_t socket() const ; 2804 2805void set_hostname_addr_map( std :: map < std :: string , std :: string > addr_map ); 2806 2807void set_default_headers( Headers headers ); 2808 2809void 2810set_header_writer( std :: function < ssize_t ( Stream & , Headers & ) > const & writer ); 2811 2812void set_address_family( int family ); 2813void set_tcp_nodelay( bool on ); 2814void set_ipv6_v6only( bool on ); 2815void set_socket_options( SocketOptions socket_options ); 2816 2817void set_connection_timeout( time_t sec , time_t usec = 0 ); 2818template < class Rep , class Period > 2819void 2820set_connection_timeout( const std :: chrono :: duration < Rep , Period > & duration ); 2821 2822void set_read_timeout( time_t sec , time_t usec = 0 ); 2823template < class Rep , class Period > 2824void set_read_timeout( const std :: chrono :: duration < Rep , Period > & duration ); 2825 2826void set_write_timeout( time_t sec , time_t usec = 0 ); 2827template < class Rep , class Period > 2828void set_write_timeout( const std :: chrono :: duration < Rep , Period > & duration ); 2829 2830void set_max_timeout( time_t msec ); 2831template < class Rep , class Period > 2832void set_max_timeout( const std :: chrono :: duration < Rep , Period > & duration ); 2833 2834void set_basic_auth( const std :: string & username , const std :: string & password ); 2835void set_bearer_token_auth( const std :: string & token ); 2836 2837void set_keep_alive( bool on ); 2838void set_follow_location( bool on ); 2839 2840void set_path_encode( bool on ); 2841 2842void set_compress( bool on ); 2843 2844void set_decompress( bool on ); 2845 2846void set_payload_max_length( size_t length ); 2847 2848void set_interface( const std :: string & intf ); 2849 2850void set_proxy( const std :: string & host , int port ); 2851void set_proxy_basic_auth( const std :: string & username , 2852const std :: string & password ); 2853void set_proxy_bearer_token_auth( const std :: string & token ); 2854void set_no_proxy( const std :: vector < std :: string > & patterns ); 2855 2856void set_logger( Logger logger ); 2857void set_error_logger( ErrorLogger error_logger ); 2858 2859protected : 2860struct Socket { 2861socket_t sock = INVALID_SOCKET ; 2862 2863// For Mbed TLS compatibility: start_time for request timeout tracking 2864std :: chrono :: time_point < std :: chrono :: steady_clock > start_time_ ; 2865 2866bool is_open () const { return sock != INVALID_SOCKET ; } 2867 2868#ifdef CPPHTTPLIB_SSL_ENABLED 2869tls :: session_t ssl = nullptr ; 2870#endif 2871}; 2872 2873virtual bool create_and_connect_socket( Socket & socket , Error & error ); 2874virtual bool ensure_socket_connection( Socket & socket , Error & error ); 2875virtual bool setup_proxy_connection( 2876Socket & socket , 2877std :: chrono :: time_point < std :: chrono :: steady_clock > start_time , 2878Response & res , bool & success , Error & error ); 2879 2880bool is_proxy_enabled_for_host( const std :: string & host ) const ; 2881 2882// All of: 2883// shutdown_ssl 2884// shutdown_socket 2885// close_socket 2886// disconnect 2887// should ONLY be called when socket_mutex_ is locked, and only when 2888// no other thread is using the socket. 2889virtual void shutdown_ssl( Socket & socket , bool shutdown_gracefully ); 2890void shutdown_socket( Socket & socket ) const ; 2891void close_socket( Socket & socket ); 2892void disconnect( bool gracefully ); 2893 2894bool process_request( Stream & strm , Request & req , Response & res , 2895bool close_connection , Error & error ); 2896 2897bool write_content_with_provider( Stream & strm , const Request & req , 2898Error & error ) const ; 2899 2900void copy_settings( const ClientImpl & rhs ); 2901 2902void output_log( const Request & req , const Response & res ) const ; 2903void output_error_log( const Error & err , const Request * req ) const ; 2904 2905// Socket endpoint information 2906const std :: string host_ ; 2907const int port_ ; 2908 2909// Current open socket 2910Socket socket_ ; 2911mutable std :: mutex socket_mutex_ ; 2912std :: recursive_mutex request_mutex_ ; 2913 2914// These are all protected under socket_mutex 2915size_t socket_requests_in_flight_ = 0 ; 2916std :: thread :: id socket_requests_are_from_thread_ = std :: thread ::id(); 2917bool socket_should_be_closed_when_request_is_done_ = false; 2918 2919// Hostname to connection target map. The value is an IP literal or another 2920// hostname; only the connection target changes, never the identity. 2921std :: map < std :: string , std :: string > addr_map_ ; 2922 2923// Default headers 2924Headers default_headers_ ; 2925 2926// Header writer 2927std :: function < ssize_t ( Stream & , Headers & ) > header_writer_ = 2928detail :: write_headers ; 2929 2930// Settings 2931std :: string client_cert_path_ ; 2932std :: string client_key_path_ ; 2933 2934time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND ; 2935time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND ; 2936time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND ; 2937time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND ; 2938time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND ; 2939time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND ; 2940time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND ; 2941 2942std :: string basic_auth_username_ ; 2943std :: string basic_auth_password_ ; 2944std :: string bearer_token_auth_token_ ; 2945 2946bool keep_alive_ = false; 2947bool follow_location_ = false; 2948 2949bool path_encode_ = true; 2950 2951int address_family_ = AF_UNSPEC ; 2952bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY ; 2953bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY ; 2954SocketOptions socket_options_ = nullptr ; 2955 2956bool compress_ = false; 2957bool decompress_ = true; 2958 2959size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH ; 2960bool has_payload_max_length_ = false; 2961 2962std :: string interface_ ; 2963 2964std :: string proxy_host_ ; 2965int proxy_port_ = -1 ; 2966 2967std :: string proxy_basic_auth_username_ ; 2968std :: string proxy_basic_auth_password_ ; 2969std :: string proxy_bearer_token_auth_token_ ; 2970 2971std :: vector < detail :: NoProxyEntry > no_proxy_entries_ ; 2972 2973mutable detail :: NormalizedTarget host_normalized_ ; 2974mutable bool host_normalized_valid_ = false; 2975 2976mutable std :: mutex logger_mutex_ ; 2977Logger logger_ ; 2978ErrorLogger error_logger_ ; 2979 2980private : 2981bool send_( Request & req , Response & res , Error & error ); 2982Result send_( Request && req ); 2983 2984socket_t create_client_socket( Error & error ) const ; 2985bool read_response_line( Stream & strm , const Request & req , Response & res , 2986bool skip_100_continue = true) const ; 2987bool write_request( Stream & strm , Request & req , bool close_connection , 2988Error & error , bool skip_body = false); 2989bool write_request_body( Stream & strm , Request & req , Error & error ); 2990void prepare_default_headers( Request & r , bool for_stream , 2991const std :: string & ct ); 2992bool redirect( Request & req , Response & res , Error & error ); 2993bool create_redirect_client( const std :: string & scheme , 2994const std :: string & host , int port , Request & req , 2995Response & res , const std :: string & path , 2996const std :: string & location , Error & error ); 2997template < typename ClientType > void setup_redirect_client( ClientType & client ); 2998bool handle_request( Stream & strm , Request & req , Response & res , 2999bool close_connection , Error & error ); 3000std :: unique_ptr < Response > send_with_content_provider_and_receiver( 3001Request & req , const char * body , size_t content_length , 3002ContentProvider content_provider , 3003ContentProviderWithoutLength content_provider_without_length , 3004const std :: string & content_type , ContentReceiver content_receiver , 3005Error & error ); 3006Result send_with_content_provider_and_receiver( 3007const std :: string & method , const std :: string & path , 3008const Headers & headers , const char * body , size_t content_length , 3009ContentProvider content_provider , 3010ContentProviderWithoutLength content_provider_without_length , 3011const std :: string & content_type , ContentReceiver content_receiver , 3012UploadProgress progress ); 3013ContentProviderWithoutLength get_multipart_content_provider( 3014const std :: string & boundary , const UploadFormDataItems & items , 3015const FormDataProviderItems & provider_items ) const ; 3016 3017virtual bool 3018process_socket( const Socket & socket , 3019std :: chrono :: time_point < std :: chrono :: steady_clock > start_time , 3020std :: function < bool ( Stream & strm ) > callback ); 3021virtual bool is_ssl() const ; 3022 3023void transfer_socket_ownership_to_handle( StreamHandle & handle ); 3024 3025#ifdef CPPHTTPLIB_SSL_ENABLED 3026public : 3027void set_digest_auth( const std :: string & username , 3028const std :: string & password ); 3029void set_proxy_digest_auth( const std :: string & username , 3030const std :: string & password ); 3031void set_ca_cert_path( const std :: string & ca_cert_file_path , 3032const std :: string & ca_cert_dir_path = std ::string()); 3033void enable_server_certificate_verification( bool enabled ); 3034void enable_server_hostname_verification( bool enabled ); 3035void enable_system_ca( bool enabled ); 3036 3037protected : 3038std :: string digest_auth_username_ ; 3039std :: string digest_auth_password_ ; 3040std :: string proxy_digest_auth_username_ ; 3041std :: string proxy_digest_auth_password_ ; 3042std :: string ca_cert_file_path_ ; 3043std :: string ca_cert_dir_path_ ; 3044bool server_certificate_verification_ = true; 3045bool server_hostname_verification_ = true; 3046SystemCAMode system_ca_mode_ = SystemCAMode :: Auto ; 3047std :: string ca_cert_pem_ ; // Store CA cert PEM for redirect transfer 3048int last_ssl_error_ = 0 ; 3049uint64_t last_backend_error_ = 0 ; 3050#endif 3051}; 3052 3053class Client { 3054public : 3055// Universal interface 3056explicit Client( const std :: string & scheme_host_port ); 3057 3058explicit Client( const std :: string & scheme_host_port , 3059const std :: string & client_cert_path , 3060const std :: string & client_key_path ); 3061 3062// HTTP only interface 3063explicit Client( const std :: string & host , int port ); 3064 3065explicit Client( const std :: string & host , int port , 3066const std :: string & client_cert_path , 3067const std :: string & client_key_path ); 3068 3069Client( Client && ) = default; 3070Client & operator = (Client && ) = default; 3071 3072~ Client (); 3073 3074bool is_valid () const ; 3075 3076// clang-format off 3077Result Get ( const std ::string & path, DownloadProgress progress = nullptr ); 3078Result Get ( const std ::string & path, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3079Result Get ( const std ::string & path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3080Result Get ( const std ::string & path, const Headers & headers, DownloadProgress progress = nullptr ); 3081Result Get ( const std ::string & path, const Headers & headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3082Result Get ( const std ::string & path, const Headers & headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3083Result Get ( const std ::string & path, const Params & params, DownloadProgress progress = nullptr ); 3084Result Get ( const std ::string & path, const Params & params, const Headers & headers, DownloadProgress progress = nullptr ); 3085Result Get ( const std ::string & path, const Params & params, const Headers & headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3086Result Get ( const std ::string & path, const Params & params, const Headers & headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3087 3088Result Head ( const std ::string & path); 3089Result Head ( const std ::string & path, const Headers & headers); 3090 3091Result Post ( const std ::string & path); 3092Result Post ( const std ::string & path, const char * body, size_t content_length, const std ::string & content_type, UploadProgress progress = nullptr ); 3093Result Post ( const std ::string & path, const std ::string & body, const std ::string & content_type, UploadProgress progress = nullptr ); 3094Result Post ( const std ::string & path, size_t content_length, ContentProvider content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3095Result Post ( const std ::string & path, size_t content_length, ContentProvider content_provider, const std ::string & content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr ); 3096Result Post ( const std ::string & path, ContentProviderWithoutLength content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3097Result Post ( const std ::string & path, ContentProviderWithoutLength content_provider, const std ::string & content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr ); 3098Result Post ( const std ::string & path, const Params & params); 3099Result Post ( const std ::string & path, const UploadFormDataItems & items, UploadProgress progress = nullptr ); 3100Result Post ( const std ::string & path, const Headers & headers); 3101Result Post ( const std ::string & path, const Headers & headers, const char * body, size_t content_length, const std ::string & content_type, UploadProgress progress = nullptr ); 3102Result Post ( const std ::string & path, const Headers & headers, const std ::string & body, const std ::string & content_type, UploadProgress progress = nullptr ); 3103Result Post ( const std ::string & path, const Headers & headers, size_t content_length, ContentProvider content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3104Result 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 ); 3105Result Post ( const std ::string & path, const Headers & headers, ContentProviderWithoutLength content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3106Result Post ( const std ::string & path, const Headers & headers, ContentProviderWithoutLength content_provider, const std ::string & content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3107Result Post ( const std ::string & path, const Headers & headers, const Params & params); 3108Result Post ( const std ::string & path, const Headers & headers, const UploadFormDataItems & items, UploadProgress progress = nullptr ); 3109Result Post ( const std ::string & path, const Headers & headers, const UploadFormDataItems & items, const std ::string & boundary, UploadProgress progress = nullptr ); 3110Result Post ( const std ::string & path, const Headers & headers, const UploadFormDataItems & items, const FormDataProviderItems & provider_items, UploadProgress progress = nullptr ); 3111Result Post ( const std ::string & path, const Headers & headers, const std ::string & body, const std ::string & content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3112 3113Result Put ( const std ::string & path); 3114Result Put ( const std ::string & path, const char * body, size_t content_length, const std ::string & content_type, UploadProgress progress = nullptr ); 3115Result Put ( const std ::string & path, const std ::string & body, const std ::string & content_type, UploadProgress progress = nullptr ); 3116Result Put ( const std ::string & path, size_t content_length, ContentProvider content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3117Result Put ( const std ::string & path, size_t content_length, ContentProvider content_provider, const std ::string & content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr ); 3118Result Put ( const std ::string & path, ContentProviderWithoutLength content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3119Result Put ( const std ::string & path, ContentProviderWithoutLength content_provider, const std ::string & content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr ); 3120Result Put ( const std ::string & path, const Params & params); 3121Result Put ( const std ::string & path, const UploadFormDataItems & items, UploadProgress progress = nullptr ); 3122Result Put ( const std ::string & path, const Headers & headers); 3123Result Put ( const std ::string & path, const Headers & headers, const char * body, size_t content_length, const std ::string & content_type, UploadProgress progress = nullptr ); 3124Result Put ( const std ::string & path, const Headers & headers, const std ::string & body, const std ::string & content_type, UploadProgress progress = nullptr ); 3125Result Put ( const std ::string & path, const Headers & headers, size_t content_length, ContentProvider content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3126Result 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 ); 3127Result Put ( const std ::string & path, const Headers & headers, ContentProviderWithoutLength content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3128Result Put ( const std ::string & path, const Headers & headers, ContentProviderWithoutLength content_provider, const std ::string & content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr ); 3129Result Put ( const std ::string & path, const Headers & headers, const Params & params); 3130Result Put ( const std ::string & path, const Headers & headers, const UploadFormDataItems & items, UploadProgress progress = nullptr ); 3131Result Put ( const std ::string & path, const Headers & headers, const UploadFormDataItems & items, const std ::string & boundary, UploadProgress progress = nullptr ); 3132Result Put ( const std ::string & path, const Headers & headers, const UploadFormDataItems & items, const FormDataProviderItems & provider_items, UploadProgress progress = nullptr ); 3133Result Put ( const std ::string & path, const Headers & headers, const std ::string & body, const std ::string & content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3134 3135Result Patch ( const std ::string & path); 3136Result Patch ( const std ::string & path, const char * body, size_t content_length, const std ::string & content_type, UploadProgress progress = nullptr ); 3137Result Patch ( const std ::string & path, const std ::string & body, const std ::string & content_type, UploadProgress progress = nullptr ); 3138Result Patch ( const std ::string & path, size_t content_length, ContentProvider content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3139Result Patch ( const std ::string & path, size_t content_length, ContentProvider content_provider, const std ::string & content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr ); 3140Result Patch ( const std ::string & path, ContentProviderWithoutLength content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3141Result Patch ( const std ::string & path, ContentProviderWithoutLength content_provider, const std ::string & content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr ); 3142Result Patch ( const std ::string & path, const Params & params); 3143Result Patch ( const std ::string & path, const UploadFormDataItems & items, UploadProgress progress = nullptr ); 3144Result Patch ( const std ::string & path, const Headers & headers); 3145Result Patch ( const std ::string & path, const Headers & headers, const char * body, size_t content_length, const std ::string & content_type, UploadProgress progress = nullptr ); 3146Result Patch ( const std ::string & path, const Headers & headers, const std ::string & body, const std ::string & content_type, UploadProgress progress = nullptr ); 3147Result Patch ( const std ::string & path, const Headers & headers, size_t content_length, ContentProvider content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3148Result 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 ); 3149Result Patch ( const std ::string & path, const Headers & headers, ContentProviderWithoutLength content_provider, const std ::string & content_type, UploadProgress progress = nullptr ); 3150Result Patch ( const std ::string & path, const Headers & headers, ContentProviderWithoutLength content_provider, const std ::string & content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr ); 3151Result Patch ( const std ::string & path, const Headers & headers, const Params & params); 3152Result Patch ( const std ::string & path, const Headers & headers, const UploadFormDataItems & items, UploadProgress progress = nullptr ); 3153Result Patch ( const std ::string & path, const Headers & headers, const UploadFormDataItems & items, const std ::string & boundary, UploadProgress progress = nullptr ); 3154Result Patch ( const std ::string & path, const Headers & headers, const UploadFormDataItems & items, const FormDataProviderItems & provider_items, UploadProgress progress = nullptr ); 3155Result Patch ( const std ::string & path, const Headers & headers, const std ::string & body, const std ::string & content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr ); 3156 3157Result Delete ( const std ::string & path, DownloadProgress progress = nullptr ); 3158Result Delete ( const std ::string & path, const char * body, size_t content_length, const std ::string & content_type, DownloadProgress progress = nullptr ); 3159Result Delete ( const std ::string & path, const std ::string & body, const std ::string & content_type, DownloadProgress progress = nullptr ); 3160Result Delete ( const std ::string & path, const Params & params, DownloadProgress progress = nullptr ); 3161Result Delete ( const std ::string & path, const Headers & headers, DownloadProgress progress = nullptr ); 3162Result Delete ( const std ::string & path, const Headers & headers, const char * body, size_t content_length, const std ::string & content_type, DownloadProgress progress = nullptr ); 3163Result Delete ( const std ::string & path, const Headers & headers, const std ::string & body, const std ::string & content_type, DownloadProgress progress = nullptr ); 3164Result Delete ( const std ::string & path, const Headers & headers, const Params & params, DownloadProgress progress = nullptr ); 3165 3166Result Options ( const std ::string & path); 3167Result Options ( const std ::string & path, const Headers & headers); 3168// clang-format on 3169 3170// Streaming API: Open a stream for reading response body incrementally 3171// Socket ownership is transferred to StreamHandle for true streaming 3172// Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.) 3173ClientImpl ::StreamHandle open_stream ( const std ::string & method, 3174const std ::string & path, 3175const Params & params = {}, 3176const Headers & headers = {}, 3177const std::string & body = {}, 3178const std::string & content_type = {}); 3179 3180bool send ( Request & req, Response & res, Error & error); 3181Result send ( const Request & req); 3182 3183void stop (); 3184 3185std ::string host () const; 3186int port () const; 3187 3188size_t is_socket_open () const; 3189socket_t socket () const; 3190 3191void set_hostname_addr_map ( std ::map < std::string, std ::string > addr_map); 3192 3193void set_default_headers ( Headers headers); 3194 3195void 3196set_header_writer ( std ::function < ssize_t ( Stream & , Headers & ) > const & writer ); 3197 3198void set_address_family ( int family); 3199void set_tcp_nodelay ( bool on); 3200void set_socket_options ( SocketOptions socket_options); 3201 3202void set_connection_timeout ( time_t sec, time_t usec = 0 ); 3203template < class Rep, class Period > 3204void 3205set_connection_timeout ( const std ::chrono::duration < Rep, Period > & duration); 3206 3207void set_read_timeout ( time_t sec, time_t usec = 0 ); 3208template < class Rep, class Period > 3209void set_read_timeout ( const std ::chrono::duration < Rep, Period > & duration); 3210 3211void set_write_timeout ( time_t sec, time_t usec = 0 ); 3212template < class Rep, class Period > 3213void set_write_timeout ( const std ::chrono::duration < Rep, Period > & duration); 3214 3215void set_max_timeout ( time_t msec); 3216template < class Rep, class Period > 3217void set_max_timeout ( const std ::chrono::duration < Rep, Period > & duration); 3218 3219void set_basic_auth ( const std ::string & username, const std ::string & password); 3220void set_bearer_token_auth ( const std ::string & token); 3221 3222void set_keep_alive ( bool on); 3223void set_follow_location ( bool on); 3224 3225void set_path_encode ( bool on); 3226 3227void set_compress ( bool on); 3228 3229void set_decompress ( bool on); 3230 3231void set_payload_max_length ( size_t length); 3232 3233void set_interface ( const std ::string & intf); 3234 3235void set_proxy ( const std ::string & host, int port); 3236void set_proxy_basic_auth ( const std ::string & username, 3237const std ::string & password); 3238void set_proxy_bearer_token_auth ( const std ::string & token); 3239void set_no_proxy ( const std ::vector < std::string > & patterns); 3240void set_logger ( Logger logger); 3241void set_error_logger ( ErrorLogger error_logger); 3242 3243private : 3244std::unique_ptr < ClientImpl > cli_; 3245 3246#ifdef CPPHTTPLIB_SSL_ENABLED 3247public: 3248void set_digest_auth ( const std ::string & username, 3249const std ::string & password); 3250void set_proxy_digest_auth ( const std ::string & username, 3251const std ::string & password); 3252void enable_server_certificate_verification ( bool enabled); 3253void enable_server_hostname_verification ( bool enabled); 3254void enable_system_ca ( bool enabled); 3255void set_ca_cert_path ( const std ::string & ca_cert_file_path, 3256const std ::string & ca_cert_dir_path = std:: string ()); 3257 3258void set_ca_cert_store ( tls ::ca_store_t ca_cert_store); 3259void load_ca_cert_store ( const char * ca_cert, std :: size_t size); 3260 3261void set_server_certificate_verifier ( tls ::VerifyCallback verifier); 3262 3263void set_session_verifier ( 3264std ::function < SSLVerifierResponse ( tls ::session_t) > verifier); 3265 3266tls ::ctx_t tls_context () const; 3267 3268#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE 3269void enable_windows_certificate_verification ( bool enabled); 3270#endif 3271 3272private : 3273bool is_ssl_ = false; 3274#endif 3275}; 3276 3277#ifdef CPPHTTPLIB_SSL_ENABLED 3278class SSLServer : public Server { 3279public : 3280SSLServer( const char * cert_path, const char * private_key_path, 3281const char * client_ca_cert_file_path = nullptr , 3282const char * client_ca_cert_dir_path = nullptr , 3283const char * private_key_password = nullptr ); 3284 3285struct PemMemory { 3286const char * cert_pem ; 3287size_t cert_pem_len ; 3288const char * key_pem ; 3289size_t key_pem_len ; 3290const char * client_ca_pem ; 3291size_t client_ca_pem_len ; 3292const char * private_key_password ; 3293}; 3294explicit SSLServer ( const PemMemory & pem); 3295 3296// The callback receives the ctx_t handle which can be cast to the 3297// appropriate backend type (SSL_CTX* for OpenSSL, 3298// tls::impl::MbedTlsContext* for Mbed TLS) 3299explicit SSLServer ( const tls ::ContextSetupCallback & setup_callback); 3300 3301~ SSLServer () override; 3302 3303bool is_valid () const override ; 3304 3305bool update_certs_pem ( const char * cert_pem, const char * key_pem, 3306const char * client_ca_pem = nullptr , 3307const char * password = nullptr ); 3308 3309tls ::ctx_t tls_context () const { return ctx_; } 3310 3311int ssl_last_error () const { return last_ssl_error_; } 3312 3313private : 3314bool process_and_close_socket ( socket_t sock) override; 3315 3316tls :: ctx_t ctx_ = nullptr ; 3317std :: mutex ctx_mutex_; 3318 3319int last_ssl_error_ = 0 ; 3320}; 3321 3322class SSLClient final : public ClientImpl { 3323public : 3324explicit SSLClient ( const std ::string & host); 3325 3326explicit SSLClient ( const std ::string & host, int port); 3327 3328explicit SSLClient ( const std ::string & host, int port, 3329const std ::string & client_cert_path, 3330const std ::string & client_key_path, 3331const std ::string & private_key_password = std:: string ()); 3332 3333struct PemMemory { 3334const char * cert_pem ; 3335size_t cert_pem_len ; 3336const char * key_pem ; 3337size_t key_pem_len ; 3338const char * private_key_password ; 3339}; 3340explicit SSLClient ( const std ::string & host, int port, const PemMemory & pem); 3341 3342~ SSLClient () override; 3343 3344bool is_valid () const override ; 3345 3346void set_ca_cert_store ( tls ::ca_store_t ca_cert_store); 3347void load_ca_cert_store ( const char * ca_cert, std :: size_t size); 3348 3349void set_server_certificate_verifier ( tls ::VerifyCallback verifier); 3350 3351// Post-handshake session verifier (backend-independent) 3352void set_session_verifier ( 3353std ::function < SSLVerifierResponse ( tls ::session_t) > verifier); 3354 3355tls ::ctx_t tls_context () const { return ctx_; } 3356 3357#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE 3358void enable_windows_certificate_verification ( bool enabled); 3359#endif 3360 3361private : 3362bool create_and_connect_socket ( Socket & socket, Error & error) override; 3363bool ensure_socket_connection ( Socket & socket, Error & error) override; 3364void shutdown_ssl ( Socket & socket, bool shutdown_gracefully) override; 3365void shutdown_ssl_impl ( Socket & socket, bool shutdown_gracefully); 3366 3367bool 3368process_socket ( const Socket & socket, 3369std ::chrono::time_point < std::chrono::steady_clock > start_time, 3370std ::function < bool (Stream & strm) > callback) override; 3371bool is_ssl () const override; 3372 3373bool setup_proxy_connection ( 3374Socket & socket, 3375std ::chrono::time_point < std::chrono::steady_clock > start_time, 3376Response & res, bool & success, Error & error) override; 3377bool connect_with_proxy ( 3378Socket & sock, 3379std ::chrono::time_point < std::chrono::steady_clock > start_time, 3380Response & res, bool & success, Error & error); 3381bool initialize_ssl ( Socket & socket, Error & error); 3382 3383void init_ctx (); 3384void reset_ctx_on_error (); 3385 3386bool load_certs (); 3387 3388tls ::ctx_t ctx_ = nullptr ; 3389std ::mutex ctx_mutex_; 3390std ::once_flag initialize_cert_; 3391 3392// Tracks whether a custom CA store was applied via set_ca_cert_store(), 3393// since the store handle itself is owned by ctx_ and leaves no other trace. 3394// Used to keep custom CA configuration exclusive with system CA loading. 3395bool ca_cert_store_set_ = false; 3396 3397std ::function < SSLVerifierResponse ( tls ::session_t) > session_verifier_; 3398 3399#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE 3400bool enable_windows_cert_verification_ = true; 3401#endif 3402 3403friend class ClientImpl; 3404}; 3405#endif // CPPHTTPLIB_SSL_ENABLED 3406 3407namespace detail { 3408 3409template < typename T , typename U > 3410inline void duration_to_sec_and_usec ( const T & duration, U callback) { 3411auto sec = std::chrono::duration_cast < std::chrono::seconds > (duration). count (); 3412auto usec = std::chrono::duration_cast < std::chrono::microseconds > ( 3413duration - std::chrono:: seconds (sec)) 3414.count(); 3415callback ( static_cast < time_t > (sec), static_cast < time_t > (usec)); 3416} 3417 3418template < size_t N > inline constexpr size_t str_len ( const char ( & )[ N ]) { 3419return N - 1 ; 3420} 3421 3422inline bool is_numeric ( const std ::string & str) { 3423return !str. empty () && std:: all_of (str. cbegin (), str. cend (), is_ascii_digit); 3424} 3425 3426inline size_t get_header_value_u64 ( const Headers & headers, 3427const std ::string & key, size_t def, 3428size_t id, bool & is_invalid_value) { 3429is_invalid_value = false; 3430auto rng = headers. equal_range (key); 3431auto it = rng. first ; 3432std :: advance ( it , static_cast < ssize_t > (id)); 3433if ( it != rng.second) { 3434if ( is_numeric (it -> second )) { 3435// Parse at size_t width so an out-of-range Content-Length is reported 3436// rather than silently saturated/truncated (a value above 2^32 would 3437// otherwise wrap to a small framing length on 32-bit builds). Flag it 3438// and return SIZE_MAX so the existing oversized-value guards reject it. 3439size_t val = 0 ; 3440const auto & s = it -> second ; 3441auto r = from_chars (s. data (), s. data () + s. size (), val); 3442if (r. ec == std::errc::result_out_of_range) { 3443is_invalid_value = true; 3444return (std::numeric_limits < size_t > ::max)(); 3445} 3446return val; 3447} else { 3448is_invalid_value = true; 3449} 3450} 3451return def; 3452} 3453 3454inline size_t get_header_value_u64 ( const Headers & headers, 3455const std ::string & key, size_t def, 3456size_t id) { 3457auto dummy = false; 3458return get_header_value_u64 (headers, key, def, id, dummy); 3459} 3460 3461} // namespace detail 3462 3463template < class Rep, class Period > 3464inline Server & 3465Server:: set_read_timeout ( const std :: chrono ::duration < Rep, Period > & duration) { 3466detail :: duration_to_sec_and_usec ( 3467duration, [ & ](time_t sec, time_t usec) { set_read_timeout (sec, usec); }); 3468return * this; 3469} 3470 3471template < class Rep, class Period > 3472inline Server & 3473Server:: set_write_timeout ( const std :: chrono ::duration < Rep, Period > & duration) { 3474detail :: duration_to_sec_and_usec ( 3475duration, [ & ](time_t sec, time_t usec) { set_write_timeout (sec, usec); }); 3476return * this; 3477} 3478 3479template < class Rep, class Period > 3480inline Server & 3481Server:: set_idle_interval ( const std :: chrono ::duration < Rep, Period > & duration) { 3482detail :: duration_to_sec_and_usec ( 3483duration, [ & ](time_t sec, time_t usec) { set_idle_interval (sec, usec); }); 3484return * this; 3485} 3486 3487template < class Rep, class Period > 3488inline void ClientImpl:: set_connection_timeout ( 3489const std ::chrono::duration < Rep, Period > & duration) { 3490detail :: duration_to_sec_and_usec (duration, [ & ](time_t sec, time_t usec) { 3491set_connection_timeout (sec, usec); 3492}); 3493} 3494 3495template < class Rep, class Period > 3496inline void ClientImpl:: set_read_timeout ( 3497const std ::chrono::duration < Rep, Period > & duration) { 3498detail :: duration_to_sec_and_usec ( 3499duration, [ & ](time_t sec, time_t usec) { set_read_timeout (sec, usec); }); 3500} 3501 3502template < class Rep, class Period > 3503inline void ClientImpl:: set_write_timeout ( 3504const std ::chrono::duration < Rep, Period > & duration) { 3505detail :: duration_to_sec_and_usec ( 3506duration, [ & ](time_t sec, time_t usec) { set_write_timeout (sec, usec); }); 3507} 3508 3509template < class Rep, class Period > 3510inline void ClientImpl:: set_max_timeout ( 3511const std ::chrono::duration < Rep, Period > & duration) { 3512auto msec = 3513std::chrono::duration_cast < std::chrono::milliseconds > (duration). count (); 3514set_max_timeout (msec); 3515} 3516 3517template < class Rep, class Period > 3518inline void Client:: set_connection_timeout ( 3519const std ::chrono::duration < Rep, Period > & duration) { 3520cli_ -> set_connection_timeout (duration); 3521} 3522 3523template < class Rep, class Period > 3524inline void 3525Client:: set_read_timeout ( const std ::chrono::duration < Rep, Period > & duration) { 3526cli_ -> set_read_timeout (duration); 3527} 3528 3529template < class Rep, class Period > 3530inline void 3531Client:: set_write_timeout ( const std ::chrono::duration < Rep, Period > & duration) { 3532cli_ -> set_write_timeout (duration); 3533} 3534 3535inline void Client:: set_max_timeout ( time_t msec) { 3536cli_ -> set_max_timeout (msec); 3537} 3538 3539template < class Rep, class Period > 3540inline void 3541Client:: set_max_timeout ( const std ::chrono::duration < Rep, Period > & duration) { 3542cli_ -> set_max_timeout (duration); 3543} 3544 3545/* 3546* Forward declarations and types that will be part of the .h file if split into 3547* .h + .cc. 3548*/ 3549 3550std :: string hosted_at( const std ::string & hostname); 3551 3552void hosted_at ( const std ::string & hostname, std ::vector < std::string > & addrs); 3553 3554// JavaScript-style URL encoding/decoding functions 3555std :: string encode_uri_component( const std ::string & value); 3556std :: string encode_uri( const std ::string & value); 3557std :: string decode_uri_component( const std ::string & value); 3558std :: string decode_uri( const std ::string & value); 3559 3560// RFC 3986 compliant URL component encoding/decoding functions 3561std :: string encode_path_component( const std ::string & component); 3562std :: string decode_path_component( const std ::string & component); 3563std :: string encode_query_component( const std ::string & component, 3564bool space_as_plus = true); 3565std :: string decode_query_component( const std ::string & component, 3566bool plus_as_space = true); 3567 3568std :: string sanitize_filename( const std ::string & filename); 3569 3570std :: string append_query_params( const std ::string & path, const Params & params); 3571 3572std ::pair < std::string, std::string > make_range_header (const Ranges & ranges); 3573 3574std ::pair < std::string, std::string > 3575make_basic_authentication_header (const std::string & username, 3576const std::string & password, 3577bool is_proxy = false); 3578 3579namespace detail { 3580 3581#if defined(_WIN32) 3582inline std ::wstring u8string_to_wstring ( const char * s) { 3583if (!s) { return std:: wstring (); } 3584 3585auto len = static_cast < int > ( strlen (s)); 3586if (!len) { return std:: wstring (); } 3587 3588auto wlen = :: MultiByteToWideChar ( CP_UTF8 , 0 , s, len, nullptr, 0 ); 3589if (!wlen) { return std :: wstring (); } 3590 3591std :: wstring ws; 3592ws. resize (wlen); 3593wlen = :: MultiByteToWideChar ( 3594CP_UTF8 , 0 , s, len, 3595const_cast < LPWSTR > (reinterpret_cast < LPCWSTR > (ws. data ())), wlen); 3596if (wlen != static_cast < int > (ws. size ())) { ws. clear (); } 3597return ws; 3598} 3599#endif 3600 3601struct FileStat { 3602FileStat( const std ::string & path); 3603bool is_file () const; 3604bool is_dir () const; 3605time_t mtime () const; 3606size_t size () const; 3607 3608private : 3609#if defined(_WIN32) 3610struct _stat st_ ; 3611#else 3612struct stat st_ ; 3613#endif 3614int ret_ = -1 ; 3615}; 3616 3617std :: string make_host_and_port_string( const std ::string & host, int port, 3618bool is_ssl); 3619 3620template < typename T > 3621bool check_and_write_headers (Stream & strm, Headers & headers, T header_writer, 3622Error & error); 3623 3624std :: string trim_copy( const std ::string & s); 3625 3626void divide ( 3627const char * data, std :: size_t size, char d, 3628std ::function < void ( const char * , std :: size_t , const char * , std :: size_t ) > 3629fn); 3630 3631void divide ( 3632const std ::string & str, char d, 3633std ::function < void ( const char * , std :: size_t , const char * , std :: size_t ) > 3634fn); 3635 3636void split ( const char * b, const char * e, char d, 3637std ::function < void ( const char * , const char * ) > fn); 3638 3639void split ( const char * b, const char * e, char d, size_t m, 3640std ::function < void ( const char * , const char * ) > fn); 3641 3642bool split_find ( const char * b, const char * e, char d, 3643std ::function < bool ( const char * , const char * ) > fn); 3644 3645bool has_header_token ( const Headers & headers, const std ::string & key, 3646const std ::string & token); 3647 3648std :: string websocket_accept_key( const std ::string & client_key); 3649 3650bool is_websocket_upgrade ( const Request & req); 3651 3652bool process_client_socket ( 3653socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, 3654time_t write_timeout_sec, time_t write_timeout_usec, 3655time_t max_timeout_msec, 3656std ::chrono::time_point < std::chrono::steady_clock > start_time, 3657std ::function < bool (Stream & ) > callback); 3658 3659socket_t create_client_socket ( const std ::string & host, const std ::string & ip, 3660int port, int address_family, bool tcp_nodelay, 3661bool ipv6_v6only, SocketOptions socket_options, 3662time_t connection_timeout_sec, 3663time_t connection_timeout_usec, 3664time_t read_timeout_sec, time_t read_timeout_usec, 3665time_t write_timeout_sec, 3666time_t write_timeout_usec, 3667const std ::string & intf, Error & error); 3668 3669const char * get_header_value ( const Headers & headers, const std ::string & key, 3670const char * def, size_t id); 3671 3672std :: string get_combined_header_value( const Headers & headers, 3673const std ::string & key); 3674 3675std :: string params_to_query_str ( const Params & params); 3676 3677void parse_query_text ( const char * data, std :: size_t size, Params & params); 3678 3679void parse_query_text ( const std ::string & s, Params & params); 3680 3681bool parse_multipart_boundary ( const std ::string & content_type, 3682std ::string & boundary); 3683 3684bool parse_range_header ( const std ::string & s, Ranges & ranges); 3685 3686bool parse_accept_header ( const std ::string & s, 3687std ::vector < std::string > & content_types); 3688 3689void parse_disposition_params ( const std ::string & s, Params & params); 3690 3691ssize_t send_socket ( socket_t sock, const void * ptr, size_t size, int flags); 3692 3693ssize_t read_socket ( socket_t sock, void * ptr, size_t size, int flags); 3694 3695EncodingType encoding_type ( const Request & req, const std ::string & content_type); 3696 3697EncodingType encoding_type ( const Request & req, const Response & res, 3698const std ::string & content_type); 3699 3700EncodingType encoding_type ( const Request & req, const Response & res); 3701 3702class BufferStream final : public Stream { 3703public : 3704BufferStream () = default; 3705~ BufferStream () override = default; 3706 3707bool is_readable () const override ; 3708bool wait_readable () const override; 3709bool wait_writable () const override; 3710ssize_t read ( char * ptr, size_t size) override; 3711ssize_t write ( const char * ptr, size_t size) override; 3712void get_remote_ip_and_port ( std ::string & ip, int & port) const override; 3713void get_local_ip_and_port ( std ::string & ip, int & port) const override; 3714socket_t socket () const override; 3715time_t duration () const override; 3716 3717const std ::string & get_buffer () const; 3718 3719private: 3720std::string buffer; 3721size_t position = 0 ; 3722}; 3723 3724class compressor { 3725public : 3726virtual ~ compressor () = default; 3727 3728typedef std :: function < bool ( const char * data, size_t data_len) > Callback ; 3729virtual bool compress ( const char * data, size_t data_length, bool last, 3730Callback callback) = 0 ; 3731}; 3732 3733class decompressor { 3734public : 3735virtual ~ decompressor () = default; 3736 3737virtual bool is_valid () const = 0 ; 3738 3739typedef std :: function < bool ( const char * data, size_t data_len) > Callback ; 3740virtual bool decompress ( const char * data, size_t data_length, 3741Callback callback) = 0 ; 3742}; 3743 3744class nocompressor final : public compressor { 3745public : 3746~ nocompressor () override = default; 3747 3748bool compress ( const char * data, size_t data_length, bool /*last*/ , 3749Callback callback) override; 3750}; 3751 3752#ifdef CPPHTTPLIB_ZLIB_SUPPORT 3753class gzip_compressor final : public compressor { 3754public : 3755gzip_compressor (); 3756~ gzip_compressor () override; 3757 3758bool compress ( const char * data, size_t data_length, bool last, 3759Callback callback) override; 3760 3761private : 3762bool is_valid_ = false; 3763z_stream strm_; 3764}; 3765 3766class gzip_decompressor final : public decompressor { 3767public : 3768gzip_decompressor (); 3769~ gzip_decompressor () override; 3770 3771bool is_valid () const override ; 3772 3773bool decompress ( const char * data, size_t data_length, 3774Callback callback) override; 3775 3776private : 3777bool is_valid_ = false; 3778z_stream strm_; 3779}; 3780#endif 3781 3782#ifdef CPPHTTPLIB_BROTLI_SUPPORT 3783class brotli_compressor final : public compressor { 3784public : 3785brotli_compressor (); 3786~ brotli_compressor (); 3787 3788bool compress ( const char * data, size_t data_length, bool last, 3789Callback callback) override; 3790 3791private : 3792BrotliEncoderState * state_ = nullptr ; 3793}; 3794 3795class brotli_decompressor final : public decompressor { 3796public : 3797brotli_decompressor (); 3798~ brotli_decompressor (); 3799 3800bool is_valid () const override ; 3801 3802bool decompress ( const char * data, size_t data_length, 3803Callback callback) override; 3804 3805private : 3806BrotliDecoderResult decoder_r; 3807BrotliDecoderState * decoder_s = nullptr ; 3808}; 3809#endif 3810 3811#ifdef CPPHTTPLIB_ZSTD_SUPPORT 3812class zstd_compressor : public compressor { 3813public : 3814zstd_compressor (); 3815~ zstd_compressor (); 3816 3817bool compress ( const char * data, size_t data_length, bool last, 3818Callback callback) override; 3819 3820private : 3821ZSTD_CCtx * ctx_ = nullptr ; 3822}; 3823 3824class zstd_decompressor : public decompressor { 3825public : 3826zstd_decompressor (); 3827~ zstd_decompressor (); 3828 3829bool is_valid () const override ; 3830 3831bool decompress ( const char * data, size_t data_length, 3832Callback callback) override; 3833 3834private : 3835ZSTD_DCtx * ctx_ = nullptr ; 3836}; 3837#endif 3838 3839// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` 3840// to store data. The call can set memory on stack for performance. 3841class stream_line_reader { 3842public : 3843stream_line_reader (Stream & strm, char * fixed_buffer, 3844size_t fixed_buffer_size); 3845const char * ptr () const; 3846size_t size () const ; 3847bool end_with_crlf () const; 3848bool getline (); 3849 3850private : 3851void append ( char c); 3852void append ( const char * data, size_t size); 3853 3854Stream & strm_; 3855char * fixed_buffer_; 3856const size_t fixed_buffer_size_; 3857size_t fixed_buffer_used_size_ = 0 ; 3858std ::string growable_buffer_; 3859}; 3860 3861bool parse_trailers ( stream_line_reader & line_reader, Headers & dest, 3862const Headers & src_headers); 3863 3864struct ChunkedDecoder { 3865Stream & strm ; 3866size_t chunk_remaining = 0 ; 3867bool finished = false; 3868char line_buf [ 64 ]; 3869size_t last_chunk_total = 0 ; 3870size_t last_chunk_offset = 0 ; 3871 3872explicit ChunkedDecoder ( Stream & s); 3873 3874ssize_t read_payload ( char * buf, size_t len, size_t & out_chunk_offset, 3875size_t & out_chunk_total); 3876 3877bool parse_trailers_into ( Headers & dest, const Headers & src_headers); 3878}; 3879 3880class mmap { 3881public : 3882mmap( const char * path); 3883~ mmap (); 3884 3885bool open ( const char * path); 3886void close (); 3887 3888bool is_open () const ; 3889size_t size () const; 3890const char * data () const; 3891 3892private : 3893#if defined(_WIN32) 3894HANDLE hFile_ = NULL ; 3895HANDLE hMapping_ = NULL ; 3896#else 3897int fd_ = -1 ; 3898#endif 3899size_t size_ = 0 ; 3900void * addr_ = nullptr ; 3901bool is_open_empty_file = false; 3902}; 3903 3904// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5 3905namespace fields { 3906 3907bool is_token_char ( char c); 3908bool is_token ( const std ::string & s); 3909bool is_field_name ( const std ::string & s); 3910bool is_vchar ( char c); 3911bool is_obs_text ( char c); 3912bool is_field_vchar ( char c); 3913bool is_field_content ( const std ::string & s); 3914bool is_field_value ( const std ::string & s); 3915bool is_field_valid ( const std ::string & name, const std ::string & value); 3916 3917} // namespace fields 3918} // namespace detail 3919 3920/* 3921* TLS Abstraction Layer Declarations 3922*/ 3923 3924#ifdef CPPHTTPLIB_SSL_ENABLED 3925// TLS abstraction layer - backend-specific type declarations 3926#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT 3927namespace tls { 3928namespace impl { 3929 3930// Mbed TLS context wrapper (holds config, entropy, DRBG, CA chain, own 3931// cert/key). This struct is accessible via tls::impl for use in SSL context 3932// setup callbacks (cast ctx_t to tls::impl::MbedTlsContext*). 3933struct MbedTlsContext { 3934mbedtls_ssl_config conf ; 3935#ifndef CPPHTTPLIB_MBEDTLS_V4 3936// Mbed TLS 4.x uses PSA Crypto's internal RNG; no explicit entropy/DRBG. 3937mbedtls_entropy_context entropy ; 3938mbedtls_ctr_drbg_context ctr_drbg ; 3939#endif 3940mbedtls_x509_crt ca_chain ; 3941mbedtls_x509_crt own_cert ; 3942mbedtls_pk_context own_key ; 3943bool is_server = false; 3944bool verify_client = false; 3945bool has_verify_callback = false; 3946 3947MbedTlsContext( ); 3948~ MbedTlsContext (); 3949 3950MbedTlsContext ( const MbedTlsContext & ) = delete ; 3951MbedTlsContext & operator = ( const MbedTlsContext & ) = delete ; 3952}; 3953 3954} // namespace impl 3955} // namespace tls 3956#endif 3957 3958#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT 3959namespace tls { 3960namespace impl { 3961 3962// wolfSSL context wrapper (holds WOLFSSL_CTX and related state). 3963// This struct is accessible via tls::impl for use in SSL context 3964// setup callbacks (cast ctx_t to tls::impl::WolfSSLContext*). 3965struct WolfSSLContext { 3966WOLFSSL_CTX * ctx = nullptr; 3967bool is_server = false; 3968bool verify_client = false; 3969bool has_verify_callback = false; 3970std:: string ca_pem_data_ ; // accumulated PEM for get_ca_names/get_ca_certs 3971 3972WolfSSLContext (); 3973~ WolfSSLContext (); 3974 3975WolfSSLContext ( const WolfSSLContext & ) = delete ; 3976WolfSSLContext & operator = ( const WolfSSLContext & ) = delete ; 3977}; 3978 3979// CA store for wolfSSL: holds raw PEM bytes to allow reloading into any ctx 3980struct WolfSSLCAStore { 3981std:: string pem_data ; 3982}; 3983 3984} // namespace impl 3985} // namespace tls 3986#endif 3987 3988#endif // CPPHTTPLIB_SSL_ENABLED 3989 3990namespace stream { 3991 3992class Result { 3993public : 3994Result (); 3995explicit Result (ClientImpl:: StreamHandle && handle , size_t chunk_size = 8192 ); 3996Result ( Result && other ) noexcept ; 3997Result & operator = ( Result && other ) noexcept ; 3998Result ( const Result & ) = delete ; 3999Result & operator = ( const Result & ) = delete ; 4000 4001// Response info 4002bool is_valid () const ; 4003explicit operator bool () const ; 4004int status () const ; 4005const Headers & headers () const ; 4006std :: string get_header_value ( const std:: string & key , 4007const char * def = "" ) const ; 4008bool has_header ( const std:: string & key ) const ; 4009Error error () const ; 4010Error read_error () const ; 4011bool has_read_error () const ; 4012 4013// Stream reading 4014bool next (); 4015const char * data () const ; 4016size_t size () const ; 4017std :: string read_all (); 4018 4019private: 4020ClientImpl :: StreamHandle handle_ ; 4021std:: string buffer_ ; 4022size_t current_size_ = 0 ; 4023size_t chunk_size_ ; 4024bool finished_ = false; 4025}; 4026 4027// GET 4028template < typename ClientType > 4029inline Result Get (ClientType & cli , const std:: string & path , 4030size_t chunk_size = 8192 ) { 4031return Result { cli . open_stream ( "GET" , path ), chunk_size }; 4032} 4033 4034template < typename ClientType > 4035inline Result Get (ClientType & cli , const std:: string & path , 4036const Headers & headers , size_t chunk_size = 8192 ) { 4037return Result { cli . open_stream ( "GET" , path , {}, headers ), chunk_size }; 4038} 4039 4040template < typename ClientType > 4041inline Result Get (ClientType & cli , const std:: string & path , 4042const Params & params , size_t chunk_size = 8192 ) { 4043return Result { cli . open_stream ( "GET" , path , params ), chunk_size }; 4044} 4045 4046template < typename ClientType > 4047inline Result Get (ClientType & cli , const std:: string & path , 4048const Params & params , const Headers & headers , 4049size_t chunk_size = 8192 ) { 4050return Result { cli . open_stream ( "GET" , path , params , headers ), chunk_size }; 4051} 4052 4053// POST 4054template < typename ClientType > 4055inline Result Post (ClientType & cli , const std:: string & path , 4056const std:: string & body , const std:: string & content_type , 4057size_t chunk_size = 8192 ) { 4058return Result { cli . open_stream ( "POST" , path , {}, {}, body , content_type ), 4059chunk_size }; 4060} 4061 4062template < typename ClientType > 4063inline Result Post (ClientType & cli , const std:: string & path , 4064const Headers & headers , const std:: string & body , 4065const std:: string & content_type , size_t chunk_size = 8192 ) { 4066return Result { cli . open_stream ( "POST" , path , {}, headers , body , content_type ), 4067chunk_size }; 4068} 4069 4070template < typename ClientType > 4071inline Result Post (ClientType & cli , const std:: string & path , 4072const Params & params , const std:: string & body , 4073const std:: string & content_type , size_t chunk_size = 8192 ) { 4074return Result { cli . open_stream ( "POST" , path , params , {}, body , content_type ), 4075chunk_size }; 4076} 4077 4078template < typename ClientType > 4079inline Result Post (ClientType & cli , const std:: string & path , 4080const Params & params , const Headers & headers , 4081const std:: string & body , const std:: string & content_type , 4082size_t chunk_size = 8192 ) { 4083return Result { 4084cli . open_stream ( "POST" , path , params , headers , body , content_type ), 4085chunk_size }; 4086} 4087 4088// PUT 4089template < typename ClientType > 4090inline Result Put (ClientType & cli , const std:: string & path , 4091const std:: string & body , const std:: string & content_type , 4092size_t chunk_size = 8192 ) { 4093return Result { cli . open_stream ( "PUT" , path , {}, {}, body , content_type ), 4094chunk_size }; 4095} 4096 4097template < typename ClientType > 4098inline Result Put (ClientType & cli , const std:: string & path , 4099const Headers & headers , const std:: string & body , 4100const std:: string & content_type , size_t chunk_size = 8192 ) { 4101return Result { cli . open_stream ( "PUT" , path , {}, headers , body , content_type ), 4102chunk_size }; 4103} 4104 4105template < typename ClientType > 4106inline Result Put (ClientType & cli , const std:: string & path , 4107const Params & params , const std:: string & body , 4108const std:: string & content_type , size_t chunk_size = 8192 ) { 4109return Result { cli . open_stream ( "PUT" , path , params , {}, body , content_type ), 4110chunk_size }; 4111} 4112 4113template < typename ClientType > 4114inline Result Put (ClientType & cli , const std:: string & path , 4115const Params & params , const Headers & headers , 4116const std:: string & body , const std:: string & content_type , 4117size_t chunk_size = 8192 ) { 4118return Result { 4119cli . open_stream ( "PUT" , path , params , headers , body , content_type ), 4120chunk_size }; 4121} 4122 4123// PATCH 4124template < typename ClientType > 4125inline Result Patch (ClientType & cli , const std:: string & path , 4126const std:: string & body , const std:: string & content_type , 4127size_t chunk_size = 8192 ) { 4128return Result { cli . open_stream ( "PATCH" , path , {}, {}, body , content_type ), 4129chunk_size }; 4130} 4131 4132template < typename ClientType > 4133inline Result Patch (ClientType & cli , const std:: string & path , 4134const Headers & headers , const std:: string & body , 4135const std:: string & content_type , size_t chunk_size = 8192 ) { 4136return Result { cli . open_stream ( "PATCH" , path , {}, headers , body , content_type ), 4137chunk_size }; 4138} 4139 4140template < typename ClientType > 4141inline Result Patch (ClientType & cli , const std:: string & path , 4142const Params & params , const std:: string & body , 4143const std:: string & content_type , size_t chunk_size = 8192 ) { 4144return Result { cli . open_stream ( "PATCH" , path , params , {}, body , content_type ), 4145chunk_size }; 4146} 4147 4148template < typename ClientType > 4149inline Result Patch (ClientType & cli , const std:: string & path , 4150const Params & params , const Headers & headers , 4151const std:: string & body , const std:: string & content_type , 4152size_t chunk_size = 8192 ) { 4153return Result { 4154cli . open_stream ( "PATCH" , path , params , headers , body , content_type ), 4155chunk_size }; 4156} 4157 4158// DELETE 4159template < typename ClientType > 4160inline Result Delete (ClientType & cli , const std:: string & path , 4161size_t chunk_size = 8192 ) { 4162return Result { cli . open_stream ( "DELETE" , path ), chunk_size }; 4163} 4164 4165template < typename ClientType > 4166inline Result Delete (ClientType & cli , const std:: string & path , 4167const Headers & headers , size_t chunk_size = 8192 ) { 4168return Result { cli . open_stream ( "DELETE" , path , {}, headers ), chunk_size }; 4169} 4170 4171template < typename ClientType > 4172inline Result Delete (ClientType & cli , const std:: string & path , 4173const std:: string & body , const std:: string & content_type , 4174size_t chunk_size = 8192 ) { 4175return Result { cli . open_stream ( "DELETE" , path , {}, {}, body , content_type ), 4176chunk_size }; 4177} 4178 4179template < typename ClientType > 4180inline Result Delete (ClientType & cli , const std:: string & path , 4181const Headers & headers , const std:: string & body , 4182const std:: string & content_type , 4183size_t chunk_size = 8192 ) { 4184return Result { 4185cli . open_stream ( "DELETE" , path , {}, headers , body , content_type ), 4186chunk_size }; 4187} 4188 4189template < typename ClientType > 4190inline Result Delete (ClientType & cli , const std:: string & path , 4191const Params & params , size_t chunk_size = 8192 ) { 4192return Result { cli . open_stream ( "DELETE" , path , params ), chunk_size }; 4193} 4194 4195template < typename ClientType > 4196inline Result Delete (ClientType & cli , const std:: string & path , 4197const Params & params , const Headers & headers , 4198size_t chunk_size = 8192 ) { 4199return Result { cli . open_stream ( "DELETE" , path , params , headers ), chunk_size }; 4200} 4201 4202template < typename ClientType > 4203inline Result Delete (ClientType & cli , const std:: string & path , 4204const Params & params , const std:: string & body , 4205const std:: string & content_type , 4206size_t chunk_size = 8192 ) { 4207return Result { cli . open_stream ( "DELETE" , path , params , {}, body , content_type ), 4208chunk_size }; 4209} 4210 4211template < typename ClientType > 4212inline Result Delete (ClientType & cli , const std:: string & path , 4213const Params & params , const Headers & headers , 4214const std:: string & body , const std:: string & content_type , 4215size_t chunk_size = 8192 ) { 4216return Result { 4217cli . open_stream ( "DELETE" , path , params , headers , body , content_type ), 4218chunk_size }; 4219} 4220 4221// HEAD 4222template < typename ClientType > 4223inline Result Head (ClientType & cli , const std:: string & path , 4224size_t chunk_size = 8192 ) { 4225return Result { cli . open_stream ( "HEAD" , path ), chunk_size }; 4226} 4227 4228template < typename ClientType > 4229inline Result Head (ClientType & cli , const std:: string & path , 4230const Headers & headers , size_t chunk_size = 8192 ) { 4231return Result { cli . open_stream ( "HEAD" , path , {}, headers ), chunk_size }; 4232} 4233 4234template < typename ClientType > 4235inline Result Head (ClientType & cli , const std:: string & path , 4236const Params & params , size_t chunk_size = 8192 ) { 4237return Result { cli . open_stream ( "HEAD" , path , params ), chunk_size }; 4238} 4239 4240template < typename ClientType > 4241inline Result Head (ClientType & cli , const std:: string & path , 4242const Params & params , const Headers & headers , 4243size_t chunk_size = 8192 ) { 4244return Result { cli . open_stream ( "HEAD" , path , params , headers ), chunk_size }; 4245} 4246 4247// OPTIONS 4248template < typename ClientType > 4249inline Result Options (ClientType & cli , const std:: string & path , 4250size_t chunk_size = 8192 ) { 4251return Result { cli . open_stream ( "OPTIONS" , path ), chunk_size }; 4252} 4253 4254template < typename ClientType > 4255inline Result Options (ClientType & cli , const std:: string & path , 4256const Headers & headers , size_t chunk_size = 8192 ) { 4257return Result { cli . open_stream ( "OPTIONS" , path , {}, headers ), chunk_size }; 4258} 4259 4260template < typename ClientType > 4261inline Result Options (ClientType & cli , const std:: string & path , 4262const Params & params , size_t chunk_size = 8192 ) { 4263return Result { cli . open_stream ( "OPTIONS" , path , params ), chunk_size }; 4264} 4265 4266template < typename ClientType > 4267inline Result Options (ClientType & cli , const std:: string & path , 4268const Params & params , const Headers & headers , 4269size_t chunk_size = 8192 ) { 4270return Result { cli . open_stream ( "OPTIONS" , path , params , headers ), chunk_size }; 4271} 4272 4273} // namespace stream 4274 4275namespace sse { 4276 4277struct SSEMessage { 4278std :: string event ; // Event type (default: "message") 4279std:: string data ; // Event payload 4280std:: string id ; // Event ID for Last-Event-ID header 4281 4282SSEMessage (); 4283void clear(); 4284}; 4285 4286class SSEClient { 4287public: 4288using MessageHandler = std :: function < void ( const SSEMessage & ) > ; 4289using ErrorHandler = std :: function < void ( Error ) > ; 4290using OpenHandler = std :: function < void () > ; 4291 4292SSEClient ( Client & client , const std :: string & path ); 4293SSEClient (Client & client , const std :: string & path , const Headers & headers ); 4294~ SSEClient (); 4295 4296SSEClient ( const SSEClient & ) = delete ; 4297SSEClient & operator = ( const SSEClient & ) = delete ; 4298 4299// Event handlers 4300SSEClient & on_message (MessageHandler handler ); 4301SSEClient & on_event ( const std:: string & type , MessageHandler handler ); 4302SSEClient & on_open (OpenHandler handler ); 4303SSEClient & on_error (ErrorHandler handler ); 4304SSEClient & set_reconnect_interval (int ms ); 4305SSEClient & set_max_reconnect_attempts (int n ); 4306 4307// Update headers (thread-safe) 4308SSEClient & set_headers ( const Headers & headers ); 4309 4310// State accessors 4311bool is_connected () const ; 4312const std:: string & last_event_id () const ; 4313 4314// Blocking start - runs event loop with auto-reconnect 4315void start (); 4316 4317// Non-blocking start - runs in background thread 4318void start_async (); 4319 4320// Stop the client (thread-safe) 4321void stop (); 4322 4323private: 4324bool parse_sse_line ( const std:: string & line , SSEMessage & msg , int & retry_ms ); 4325void run_event_loop (); 4326void dispatch_event ( const SSEMessage & msg ); 4327bool should_reconnect (int count ) const ; 4328void wait_for_reconnect (); 4329 4330// Client and path 4331Client & client_ ; 4332std:: string path_ ; 4333Headers headers_ ; 4334mutable std :: mutex headers_mutex_ ; 4335 4336// Callbacks 4337MessageHandler on_message_ ; 4338std:: map < std :: string , MessageHandler > event_handlers_ ; 4339OpenHandler on_open_ ; 4340ErrorHandler on_error_ ; 4341 4342// Configuration 4343int reconnect_interval_ms_ = 3000 ; 4344int max_reconnect_attempts_ = 0 ; // 0 = unlimited 4345 4346// State 4347std:: atomic < bool > running_ {false}; 4348std :: atomic < bool > connected_ {false}; 4349std :: string last_event_id_ ; 4350 4351// Async support 4352std:: thread async_thread_ ; 4353}; 4354 4355} // namespace sse 4356 4357namespace ws { 4358 4359enum class Opcode : uint8_t { 4360Continuation = 0x0 , 4361Text = 0x1 , 4362Binary = 0x2 , 4363Close = 0x8 , 4364Ping = 0x9 , 4365Pong = 0xA , 4366}; 4367 4368enum class CloseStatus : uint16_t { 4369Normal = 1000 , 4370GoingAway = 1001 , 4371ProtocolError = 1002 , 4372UnsupportedData = 1003 , 4373NoStatus = 1005 , 4374Abnormal = 1006 , 4375InvalidPayload = 1007 , 4376PolicyViolation = 1008 , 4377MessageTooBig = 1009 , 4378MandatoryExtension = 1010 , 4379InternalError = 1011 , 4380}; 4381 4382// Timeout is returned only when a read timeout was set and it elapsed before 4383// any byte of a frame arrived: nothing was consumed and the connection is 4384// still open, so the caller can send on it and read again. `msg` is left 4385// untouched, so a `while (ws.read(msg))` loop must not treat it as a message. 4386enum ReadResult : int { Fail = 0 , Text = 1 , Binary = 2 , Timeout = 3 }; 4387 4388// Result of WebSocketClient::connect(). Truthy only when the WebSocket 4389// upgrade handshake fully succeeded. On failure error() identifies the 4390// failing layer; status()/headers() expose the server's upgrade response 4391// when one was received (status() is -1 otherwise). 4392class Result { 4393public : 4394Result () = default ; 4395Result (Error err , int status , Headers && headers ) 4396: err_ (err), status_ (status), headers_ (std:: move (headers)) {} 4397 4398explicit operator bool() const { return err_ == Error :: Success ; } 4399Error error () const { return err_ ; } 4400 4401// Upgrade response info 4402int status () const { return status_ ; } 4403const Headers & headers () const { return headers_ ; } 4404std ::string get_header_value ( const std:: string & key , 4405const char * def = "" ) const { 4406return detail :: get_header_value ( headers_ , key , def , 0 ); 4407} 4408bool has_header ( const std:: string & key ) const { 4409return headers_ . find ( key ) != headers_ . end (); 4410} 4411 4412#ifdef CPPHTTPLIB_SSL_ENABLED 4413Result (Error err , int status , Headers && headers , int ssl_error , 4414uint64_t ssl_backend_error ) 4415: err_ (err), status_ (status), headers_ (std:: move (headers)), 4416ssl_error_ (ssl_error), ssl_backend_error_ (ssl_backend_error) {} 4417 4418int ssl_error () const { return ssl_error_ ; } 4419uint64_t ssl_backend_error () const { return ssl_backend_error_ ; } 4420#endif 4421 4422private : 4423Error err_ = Error :: Unknown ; // a default-constructed Result is falsy 4424int status_ = -1 ; 4425Headers headers_ ; 4426#ifdef CPPHTTPLIB_SSL_ENABLED 4427int ssl_error_ = 0 ; 4428uint64_t ssl_backend_error_ = 0 ; 4429#endif 4430}; 4431 4432class WebSocket { 4433public : 4434WebSocket ( const WebSocket & ) = delete ; 4435WebSocket & operator = ( const WebSocket & ) = delete ; 4436~ WebSocket (); 4437 4438ReadResult read (std:: string & msg ); 4439bool send ( const std:: string & data ); 4440bool send ( const char * data , size_t len ); 4441void close (CloseStatus status = CloseStatus :: Normal , 4442const std :: string & reason = "" ); 4443const Request & request () const ; 4444bool is_open () const ; 4445 4446// Bound how long read() waits before returning Timeout. 0 waits forever. 4447// A server handler owns its connection's timeout this way; a client sets it 4448// through WebSocketClient. Safe to call while another thread is in read(). 4449// 4450// Only a timeout set here is reported as Timeout. The compile-time default 4451// (CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND) is a backstop rather 4452// than a request for control, so when it elapses read() returns Fail and 4453// closes the connection, and `while (ws.read(msg))` ends as it always has. 4454void set_read_timeout (time_t sec , time_t usec = 0 ); 4455template < class Rep , class Period > 4456void set_read_timeout ( const std:: chrono :: duration < Rep , Period > & duration ); 4457 4458private : 4459friend class httplib :: Server ; 4460friend class WebSocketClient ; 4461 4462WebSocket ( 4463Stream & strm , const Request & req , bool is_server , 4464time_t ping_interval_sec = CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND , 4465int max_missed_pongs = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS ) 4466: strm_ ( strm ), req_ ( req ), is_server_ ( is_server ), 4467ping_interval_sec_ ( ping_interval_sec ), 4468max_missed_pongs_ ( max_missed_pongs ) { 4469start_heartbeat (); 4470} 4471 4472WebSocket ( 4473std:: unique_ptr < Stream > && owned_strm , const Request & req , bool is_server , 4474time_t ping_interval_sec = CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND , 4475int max_missed_pongs = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS ) 4476: strm_ ( * owned_strm ), owned_strm_ ( std :: move ( owned_strm )), req_ ( req ), 4477is_server_ ( is_server ), ping_interval_sec_ ( ping_interval_sec ), 4478max_missed_pongs_ ( max_missed_pongs ) { 4479start_heartbeat (); 4480} 4481 4482void start_heartbeat (); 4483bool send_frame (Opcode op , const char * data , size_t len , bool fin = true); 4484 4485Stream & strm_ ; 4486std :: unique_ptr < Stream > owned_strm_ ; 4487Request req_ ; 4488bool is_server_ ; 4489time_t ping_interval_sec_ ; 4490int max_missed_pongs_ ; 4491int unacked_pings_ = 0 ; 4492std :: atomic < bool > closed_ {false}; 4493// Set once the caller has bounded read() through set_read_timeout(). Until 4494// then the timeout in effect is the compile-time default, and elapsing it 4495// is a failure that closes the connection, not a Timeout. 4496std :: atomic < bool > read_timeout_set_ {false}; 4497std ::mutex write_mutex_ ; 4498// Owned by whichever thread is parsing frames off strm_. Only one thread 4499// may do so: read_websocket_frame() reads a payload until it has the whole 4500// declared length, so a second parser stealing bytes silently corrupts the 4501// message the first one is assembling. 4502std ::mutex read_mutex_ ; 4503std ::thread ping_thread_ ; 4504std ::mutex ping_mutex_ ; 4505std ::condition_variable ping_cv_ ; 4506}; 4507 4508class WebSocketClient { 4509public : 4510explicit WebSocketClient ( const std:: string & scheme_host_port_path , 4511const Headers & headers = {}); 4512 4513~ WebSocketClient (); 4514WebSocketClient ( const WebSocketClient & ) = delete ; 4515WebSocketClient & operator = ( const WebSocketClient & ) = delete ; 4516 4517bool is_valid () const ; 4518 4519Result connect (); 4520ReadResult read (std:: string & msg ); 4521bool send ( const std:: string & data ); 4522bool send ( const char * data , size_t len ); 4523void close (CloseStatus status = CloseStatus :: Normal , 4524const std :: string & reason = "" ); 4525bool is_open () const ; 4526const std:: string & subprotocol () const ; 4527void set_read_timeout (time_t sec , time_t usec = 0 ); 4528template < class Rep , class Period > 4529void set_read_timeout ( const std:: chrono :: duration < Rep , Period > & duration ); 4530 4531void set_write_timeout (time_t sec , time_t usec = 0 ); 4532template < class Rep , class Period > 4533void set_write_timeout ( const std:: chrono :: duration < Rep , Period > & duration ); 4534 4535void set_websocket_ping_interval (time_t sec ); 4536void set_websocket_max_missed_pongs (int count ); 4537void set_tcp_nodelay (bool on ); 4538void set_address_family (int family ); 4539void set_ipv6_v6only (bool on ); 4540void set_socket_options (SocketOptions socket_options ); 4541 4542void set_connection_timeout (time_t sec , time_t usec = 0 ); 4543template < class Rep , class Period > 4544void 4545set_connection_timeout ( const std:: chrono :: duration < Rep , Period > & duration ); 4546 4547void set_interface ( const std:: string & intf ); 4548void set_hostname_addr_map (std:: map < std :: string , std:: string > addr_map ); 4549 4550#ifdef CPPHTTPLIB_SSL_ENABLED 4551struct PemMemory { 4552const char * cert_pem ; 4553size_t cert_pem_len ; 4554const char * key_pem ; 4555size_t key_pem_len ; 4556const char * private_key_password ; 4557}; 4558explicit WebSocketClient ( const std:: string & scheme_host_port_path , 4559const PemMemory & pem , const Headers & headers = {}); 4560 4561void set_ca_cert_path ( const std:: string & ca_cert_file_path , 4562const std:: string & ca_cert_dir_path = std :: string ()); 4563void set_ca_cert_store (tls:: ca_store_t store ); 4564void load_ca_cert_store ( const char * ca_cert , std::size_t size ); 4565void enable_server_certificate_verification (bool enabled ); 4566void enable_server_hostname_verification (bool enabled ); 4567void enable_system_ca (bool enabled ); 4568#endif 4569 4570private : 4571void shutdown_and_close (); 4572bool create_stream (std:: unique_ptr < Stream > & strm , Error & error , 4573int & ssl_error , uint64_t & ssl_backend_error ); 4574void prepare_default_headers (Request & req ); 4575 4576std ::string host_ ; 4577int port_ ; 4578std ::string path_ ; 4579Headers headers_ ; 4580std ::string subprotocol_ ; 4581bool is_valid_ = false; 4582socket_t sock_ = INVALID_SOCKET ; 4583std :: unique_ptr < WebSocket > ws_ ; 4584time_t read_timeout_sec_ = CPPHTTPLIB_WEBSOCKET_CLIENT_READ_TIMEOUT_SECOND ; 4585time_t read_timeout_usec_ = 0 ; 4586bool read_timeout_set_ = false; // see WebSocket::read_timeout_set_ 4587time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND ; 4588time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND ; 4589time_t websocket_ping_interval_sec_ = 4590CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND ; 4591int websocket_max_missed_pongs_ = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS ; 4592int address_family_ = AF_UNSPEC ; 4593bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY ; 4594bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY ; 4595SocketOptions socket_options_ = nullptr ; 4596time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND ; 4597time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND ; 4598std ::string interface_ ; 4599 4600// Hostname to connection target map. The value is an IP literal or another 4601// hostname; only the connection target changes, never the identity. 4602std :: map < std :: string , std :: string > addr_map_ ; 4603 4604#ifdef CPPHTTPLIB_SSL_ENABLED 4605bool is_ssl_ = false; 4606tls ::ctx_t tls_ctx_ = nullptr ; 4607tls ::session_t tls_session_ = nullptr ; 4608std ::string ca_cert_file_path_ ; 4609std ::string ca_cert_dir_path_ ; 4610bool custom_ca_loaded_ = false; 4611bool certs_loaded_ = false; 4612SystemCAMode system_ca_mode_ = SystemCAMode :: Auto ; 4613bool server_certificate_verification_ = true; 4614bool server_hostname_verification_ = true; 4615#endif 4616}; 4617 4618template < class Rep , class Period > 4619inline void WebSocket :: set_read_timeout ( 4620const std:: chrono :: duration < Rep , Period > & duration ) { 4621detail :: duration_to_sec_and_usec ( 4622duration , [ & ](time_t sec, time_t usec) { set_read_timeout (sec, usec); }); 4623} 4624 4625template < class Rep, class Period > 4626inline void WebSocketClient:: set_read_timeout ( 4627const std ::chrono::duration < Rep, Period > & duration) { 4628detail :: duration_to_sec_and_usec ( 4629duration, [ & ](time_t sec, time_t usec) { set_read_timeout (sec, usec); }); 4630} 4631 4632template < class Rep, class Period > 4633inline void WebSocketClient:: set_write_timeout ( 4634const std ::chrono::duration < Rep, Period > & duration) { 4635detail :: duration_to_sec_and_usec ( 4636duration, [ & ](time_t sec, time_t usec) { set_write_timeout (sec, usec); }); 4637} 4638 4639template < class Rep, class Period > 4640inline void WebSocketClient:: set_connection_timeout ( 4641const std ::chrono::duration < Rep, Period > & duration) { 4642detail :: duration_to_sec_and_usec (duration, [ & ](time_t sec, time_t usec) { 4643set_connection_timeout (sec, usec); 4644}); 4645} 4646 4647namespace impl { 4648 4649bool is_valid_utf8 ( const std ::string & s); 4650 4651// Three states, because a failure that consumed bytes and one that consumed 4652// none are not the same thing: the first has left the stream in the middle of 4653// a frame and the connection cannot be reused, the second can just be retried. 4654enum class FrameRead { Ok, Fail, Timeout }; 4655 4656FrameRead read_websocket_frame ( Stream & strm, Opcode & opcode, 4657std ::string & payload, bool & fin, 4658bool expect_masked, size_t max_len); 4659 4660} // namespace impl 4661 4662} // namespace ws 4663 4664 4665} // namespace httplib 4666 4667#endif // CPPHTTPLIB_HTTPLIB_H