char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Alessandro de Oliveira Faria (A.K.A.CABELO)vendor : update cpp-httplib to 0.56.0 (#28787)718f7b417

char/aliceai-t5-moe
167.2 KiB4667 linesraw
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) {
597  return 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) {
603  typedef typename std::remove_extent<T>::type RT;
604  return 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) {
615  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
616}
617
618inline bool is_ascii_alnum(char c) {
619  return is_ascii_digit(c) || is_ascii_alpha(c);
620}
621
622namespace case_ignore {
623
624inline unsigned char to_lower(int c) {
625  const static unsigned char table[256] = {
626      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
627      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
628      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
629      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
630      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,
631      107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
632      122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
633      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
634      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
635      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
636      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
637      165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
638      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
639      227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
640      242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
641      225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
642      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
643      255,
644  };
645  return table[(unsigned char)(char)c];
646}
647
648inline std::string to_lower(const std::string &s) {
649  std::string result = s;
650  std::transform(
651      result.begin(), result.end(), result.begin(),
652      [](unsigned char c) { return static_cast<char>(to_lower(c)); });
653  return result;
654}
655
656inline bool equal(const std::string &a, const std::string &b) {
657  return a.size() == b.size() &&
658         std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
659           return to_lower(ca) == to_lower(cb);
660         });
661}
662
663struct equal_to {
664  bool operator()(const std::string &a, const std::string &b) const {
665    return equal(a, b);
666  }
667};
668
669struct hash {
670  size_t operator()(const std::string &key) const {
671    return hash_core(key.data(), key.size(), 0);
672  }
673
674  size_t hash_core(const char *s, size_t l, size_t h) const {
675    return (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) &
680                                 h * 33) ^
681                                    static_cast<unsigned char>(to_lower(*s)));
682  }
683};
684
685template <typename T>
686using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
687                                         detail::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 {
695  explicit scope_exit(std::function<void(void)> &&f)
696      : exit_function(std::move(f)), execute_on_destruction{true} {}
697
698  scope_exit(scope_exit &&rhs) noexcept
699      : exit_function(std::move(rhs.exit_function)),
700        execute_on_destruction{rhs.execute_on_destruction} {
701    rhs.release();
702  }
703
704  ~scope_exit() {
705    if (execute_on_destruction) { this->exit_function(); }
706  }
707
708  void release() { this->execute_on_destruction = false; }
709
710private:
711  scope_exit(const scope_exit &) = delete;
712  void operator=(const scope_exit &) = delete;
713  scope_exit &operator=(scope_exit &&) = delete;
714
715  std::function<void(void)> exit_function;
716  bool 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 {
722  const char *ptr;
723  std::errc ec;
724};
725
726template <typename T>
727inline from_chars_result<T> from_chars(const char *first, const char *last,
728                                       T &value, int base = 10) {
729  value = 0;
730  const char *p = first;
731  bool negative = false;
732
733  if (p != last && *p == '-') {
734    negative = true;
735    ++p;
736  }
737  if (p == last) { return {first, std::errc::invalid_argument}; }
738
739  T result = 0;
740  for (; p != last; ++p) {
741    char c = *p;
742    int digit = -1;
743    if (is_ascii_digit(c)) {
744      digit = c - '0';
745    } else if ('a' <= c && c <= 'z') {
746      digit = c - 'a' + 10;
747    } else if ('A' <= c && c <= 'Z') {
748      digit = c - 'A' + 10;
749    } else {
750      break;
751    }
752
753    if (digit < 0 || digit >= base) { break; }
754    if (result > ((std::numeric_limits<T>::max)() - digit) / base) {
755      return {p, std::errc::result_out_of_range};
756    }
757    result = result * base + digit;
758  }
759
760  if (p == first || (negative && p == first + 1)) {
761    return {first, std::errc::invalid_argument};
762  }
763
764  value = negative ? T(0) - result : result;
765  return {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,
782                                            double &value) {
783  value = 0.0;
784  const 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.
788  static const double powers_of_ten[] = {
789      1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,  1e8, 1e9,
790      1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18};
791  const int max_frac_digits =
792      static_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.
802  uint64_t mantissa = 0;
803  int frac_digits = 0;
804  bool seen_digit = false;
805
806  const uint64_t limit = ((std::numeric_limits<uint64_t>::max)() - 9) / 10;
807  auto accumulate = [&](char c) {
808    if (mantissa <= limit) {
809      mantissa = mantissa * 10 + static_cast<uint64_t>(c - '0');
810      return true;
811    }
812    return false;
813  };
814
815  for (; p != last && is_ascii_digit(*p); ++p) {
816    seen_digit = true;
817    accumulate(*p);
818  }
819
820  if (p != last && *p == '.') {
821    ++p;
822    for (; p != last && is_ascii_digit(*p); ++p) {
823      seen_digit = true;
824      if (frac_digits < max_frac_digits && accumulate(*p)) { ++frac_digits; }
825    }
826  }
827
828  if (!seen_digit) { return {first, std::errc::invalid_argument}; }
829
830  value = static_cast<double>(mantissa) / powers_of_ten[frac_digits];
831  return {p, std::errc{}};
832}
833
834inline bool parse_port(const char *s, size_t len, int &port) {
835  int val = 0;
836  auto r = from_chars(s, s + len, val);
837  if (r.ec != std::errc{} || val < 1 || val > 65535) { return false; }
838  port = val;
839  return true;
840}
841
842inline bool parse_port(const std::string &s, int &port) {
843  return parse_port(s.data(), s.size(), port);
844}
845
846struct UrlComponents {
847  std::string scheme;
848  std::string host;
849  std::string port;
850  std::string path;
851  std::string query;
852};
853
854inline bool parse_url(const std::string &url, UrlComponents &uc) {
855  uc = {};
856  size_t pos = 0;
857
858  auto sep = url.find("://");
859  if (sep != std::string::npos) {
860    uc.scheme = url.substr(0, sep);
861
862    // Scheme must be [a-z]+ only
863    if (uc.scheme.empty()) { return false; }
864    for (auto c : uc.scheme) {
865      if (c < 'a' || c > 'z') { return false; }
866    }
867
868    pos = sep + 3;
869  } else if (url.compare(0, 2, "//") == 0) {
870    pos = 2;
871  }
872
873  auto has_authority_prefix = pos > 0;
874  auto has_authority = has_authority_prefix || (!url.empty() && url[0] != '/' &&
875                                                url[0] != '?' && url[0] != '#');
876  if (has_authority) {
877    if (pos < url.size() && url[pos] == '[') {
878      auto close = url.find(']', pos);
879      if (close == std::string::npos) { return false; }
880      uc.host = url.substr(pos + 1, close - pos - 1);
881
882      // IPv6 host must be [a-fA-F0-9:]+ only
883      if (uc.host.empty()) { return false; }
884      for (auto c : uc.host) {
885        if (!(is_ascii_digit(c) || (c >= 'a' && c <= 'f') ||
886              (c >= 'A' && c <= 'F') || c == ':')) {
887          return false;
888        }
889      }
890
891      pos = 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.
897      if (pos < url.size()) {
898        auto c = url[pos];
899        if (c != ':' && c != '/' && c != '?' && c != '#') { return false; }
900      }
901    } else {
902      auto end = url.find_first_of(":/?#", pos);
903      if (end == std::string::npos) { end = url.size(); }
904      uc.host = url.substr(pos, end - pos);
905      pos = end;
906    }
907
908    if (pos < url.size() && url[pos] == ':') {
909      ++pos;
910      auto end = url.find_first_of("/?#", pos);
911      if (end == std::string::npos) { end = url.size(); }
912      uc.port = url.substr(pos, end - pos);
913      pos = 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.
919    if (!has_authority_prefix && pos < url.size()) {
920      uc.host.clear();
921      uc.port.clear();
922      pos = 0;
923    }
924  }
925
926  if (pos < url.size() && url[pos] != '?' && url[pos] != '#') {
927    auto end = url.find_first_of("?#", pos);
928    if (end == std::string::npos) { end = url.size(); }
929    uc.path = url.substr(pos, end - pos);
930    pos = end;
931  }
932
933  if (pos < url.size() && url[pos] == '?') {
934    auto end = url.find('#', pos);
935    if (end == std::string::npos) { end = url.size(); }
936    uc.query = url.substr(pos, end - pos);
937  }
938
939  return true;
940}
941
942} // namespace detail
943
944enum class SSLVerifierResponse {
945  // no decision has been made, use the built-in certificate verifier
946  NoDecisionMade,
947  // connection certificate is verified and accepted
948  CertificateAccepted,
949  // connection certificate was processed but is rejected
950  CertificateRejected
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
960  Continue_100 = 100,
961  SwitchingProtocol_101 = 101,
962  Processing_102 = 102,
963  EarlyHints_103 = 103,
964
965  // Successful responses
966  OK_200 = 200,
967  Created_201 = 201,
968  Accepted_202 = 202,
969  NonAuthoritativeInformation_203 = 203,
970  NoContent_204 = 204,
971  ResetContent_205 = 205,
972  PartialContent_206 = 206,
973  MultiStatus_207 = 207,
974  AlreadyReported_208 = 208,
975  IMUsed_226 = 226,
976
977  // Redirection messages
978  MultipleChoices_300 = 300,
979  MovedPermanently_301 = 301,
980  Found_302 = 302,
981  SeeOther_303 = 303,
982  NotModified_304 = 304,
983  UseProxy_305 = 305,
984  unused_306 = 306,
985  TemporaryRedirect_307 = 307,
986  PermanentRedirect_308 = 308,
987
988  // Client error responses
989  BadRequest_400 = 400,
990  Unauthorized_401 = 401,
991  PaymentRequired_402 = 402,
992  Forbidden_403 = 403,
993  NotFound_404 = 404,
994  MethodNotAllowed_405 = 405,
995  NotAcceptable_406 = 406,
996  ProxyAuthenticationRequired_407 = 407,
997  RequestTimeout_408 = 408,
998  Conflict_409 = 409,
999  Gone_410 = 410,
1000  LengthRequired_411 = 411,
1001  PreconditionFailed_412 = 412,
1002  PayloadTooLarge_413 = 413,
1003  UriTooLong_414 = 414,
1004  UnsupportedMediaType_415 = 415,
1005  RangeNotSatisfiable_416 = 416,
1006  ExpectationFailed_417 = 417,
1007  ImATeapot_418 = 418,
1008  MisdirectedRequest_421 = 421,
1009  UnprocessableContent_422 = 422,
1010  Locked_423 = 423,
1011  FailedDependency_424 = 424,
1012  TooEarly_425 = 425,
1013  UpgradeRequired_426 = 426,
1014  PreconditionRequired_428 = 428,
1015  TooManyRequests_429 = 429,
1016  RequestHeaderFieldsTooLarge_431 = 431,
1017  UnavailableForLegalReasons_451 = 451,
1018
1019  // Server error responses
1020  InternalServerError_500 = 500,
1021  NotImplemented_501 = 501,
1022  BadGateway_502 = 502,
1023  ServiceUnavailable_503 = 503,
1024  GatewayTimeout_504 = 504,
1025  HttpVersionNotSupported_505 = 505,
1026  VariantAlsoNegotiates_506 = 506,
1027  InsufficientStorage_507 = 507,
1028  LoopDetected_508 = 508,
1029  NotExtended_510 = 510,
1030  NetworkAuthenticationRequired_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:
1054  using key_type = std::string;
1055  using mapped_type = Mapped;
1056  using value_type = std::pair<std::string, Mapped>;
1057  using size_type = std::size_t;
1058  using difference_type = std::ptrdiff_t;
1059  using reference = value_type &;
1060  using const_reference = const value_type &;
1061
1062private:
1063  static size_type npos() { return static_cast<size_type>(-1); }
1064
1065  static bool keys_equal(const std::string &a, const std::string &b) {
1066    return 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().
1074  template <typename V> class iterator_t {
1075  public:
1076    using iterator_category = std::bidirectional_iterator_tag;
1077    using value_type = insertion_ordered_multimap::value_type;
1078    using difference_type = insertion_ordered_multimap::difference_type;
1079    using pointer = V *;
1080    using reference = V &;
1081
1082    iterator_t() : data_(nullptr), idx_(0), size_(0), key_idx_(npos()) {}
1083
1084    template <typename U,
1085              typename std::enable_if<std::is_convertible<U *, V *>::value,
1086                                      int>::type = 0>
1087    iterator_t(const iterator_t<U> &rhs)
1088        : data_(rhs.data_), idx_(rhs.idx_), size_(rhs.size_),
1089          key_idx_(rhs.key_idx_) {}
1090
1091    reference operator*() const { return data_[idx_]; }
1092    pointer operator->() const { return data_ + idx_; }
1093
1094    iterator_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.
1098      if (idx_ >= size_) { return *this; }
1099      ++idx_;
1100      if (key_idx_ != npos()) {
1101        while (idx_ < size_ && !matches(idx_)) {
1102          ++idx_;
1103        }
1104      }
1105      return *this;
1106    }
1107
1108    iterator_t operator++(int) {
1109      auto tmp = *this;
1110      ++*this;
1111      return tmp;
1112    }
1113
1114    iterator_t &operator--() {
1115      if (idx_ == 0) { return *this; }
1116      --idx_;
1117      if (key_idx_ != npos()) {
1118        while (idx_ > 0 && !matches(idx_)) {
1119          --idx_;
1120        }
1121      }
1122      return *this;
1123    }
1124
1125    iterator_t operator--(int) {
1126      auto tmp = *this;
1127      --*this;
1128      return tmp;
1129    }
1130
1131    template <typename U> bool operator==(const iterator_t<U> &rhs) const {
1132      return idx_ == rhs.idx_;
1133    }
1134
1135    template <typename U> bool operator!=(const iterator_t<U> &rhs) const {
1136      return idx_ != rhs.idx_;
1137    }
1138
1139  private:
1140    friend class insertion_ordered_multimap;
1141    template <typename> friend class iterator_t;
1142
1143    iterator_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
1146    bool matches(size_type i) const {
1147      return keys_equal(data_[i].first, data_[key_idx_].first);
1148    }
1149
1150    V *data_;
1151    size_type idx_;
1152    size_type size_;
1153    size_type key_idx_;
1154  };
1155
1156public:
1157  using iterator = iterator_t<value_type>;
1158  using const_iterator = iterator_t<const value_type>;
1159
1160  insertion_ordered_multimap() = default;
1161  insertion_ordered_multimap(std::initializer_list<value_type> il)
1162      : entries_(il) {}
1163  template <typename InputIt>
1164  insertion_ordered_multimap(InputIt first, InputIt last)
1165      : entries_(first, last) {}
1166
1167  iterator begin() { return make_iter(0, npos()); }
1168  iterator end() { return make_iter(entries_.size(), npos()); }
1169  const_iterator begin() const { return make_citer(0, npos()); }
1170  const_iterator end() const { return make_citer(entries_.size(), npos()); }
1171  const_iterator cbegin() const { return begin(); }
1172  const_iterator cend() const { return end(); }
1173
1174  bool empty() const { return entries_.empty(); }
1175  size_type size() const { return entries_.size(); }
1176  void clear() { entries_.clear(); }
1177  void swap(insertion_ordered_multimap &rhs) { entries_.swap(rhs.entries_); }
1178
1179  iterator insert(const value_type &val) {
1180    entries_.push_back(val);
1181    return make_iter(entries_.size() - 1, npos());
1182  }
1183
1184  iterator insert(value_type &&val) {
1185    entries_.push_back(std::move(val));
1186    return make_iter(entries_.size() - 1, npos());
1187  }
1188
1189  template <typename... Args> iterator emplace(Args &&...args) {
1190    entries_.emplace_back(std::forward<Args>(args)...);
1191    return 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).
1196  template <typename... Args> iterator emplace_front(Args &&...args) {
1197    entries_.emplace(entries_.begin(), std::forward<Args>(args)...);
1198    return make_iter(0, npos());
1199  }
1200
1201  iterator find(const std::string &key) {
1202    auto i = index_of(key);
1203    return i == npos() ? end() : make_iter(i, i);
1204  }
1205
1206  const_iterator find(const std::string &key) const {
1207    auto i = index_of(key);
1208    return i == npos() ? end() : make_citer(i, i);
1209  }
1210
1211  size_type count(const std::string &key) const {
1212    size_type n = 0;
1213    for (const auto &entry : entries_) {
1214      if (keys_equal(entry.first, key)) { n++; }
1215    }
1216    return n;
1217  }
1218
1219  std::pair<iterator, iterator> equal_range(const std::string &key) {
1220    auto i = index_of(key);
1221    return i == npos() ? std::make_pair(end(), end())
1222                       : std::make_pair(make_iter(i, i), end());
1223  }
1224
1225  std::pair<const_iterator, const_iterator>
1226  equal_range(const std::string &key) const {
1227    auto i = index_of(key);
1228    return i == npos() ? std::make_pair(end(), end())
1229                       : std::make_pair(make_citer(i, i), end());
1230  }
1231
1232  size_type erase(const std::string &key) {
1233    auto before = entries_.size();
1234    entries_.erase(std::remove_if(entries_.begin(), entries_.end(),
1235                                  [&](const value_type &entry) {
1236                                    return keys_equal(entry.first, key);
1237                                  }),
1238                   entries_.end());
1239    return before - entries_.size();
1240  }
1241
1242  iterator erase(const_iterator pos) {
1243    entries_.erase(entries_.begin() + static_cast<difference_type>(pos.idx_));
1244    return 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.
1250  iterator erase(const_iterator first, const_iterator last) {
1251    auto from = first.idx_;
1252    auto to = last.idx_;
1253    if (from >= to) { return make_iter(from, npos()); }
1254
1255    auto begin_it = entries_.begin();
1256    auto from_it = begin_it + static_cast<difference_type>(from);
1257    auto to_it = begin_it + static_cast<difference_type>(to);
1258
1259    if (first.key_idx_ == npos()) {
1260      entries_.erase(from_it, to_it);
1261    } else {
1262      auto key = entries_[first.key_idx_].first;
1263      auto keep = from_it;
1264      for (auto it = from_it; it != to_it; ++it) {
1265        if (!keys_equal(it->first, key)) {
1266          if (keep != it) { *keep = std::move(*it); }
1267          ++keep;
1268        }
1269      }
1270      if (keep != to_it) {
1271        keep = std::move(to_it, entries_.end(), keep);
1272      } else {
1273        keep = entries_.end();
1274      }
1275      entries_.erase(keep, entries_.end());
1276    }
1277    return make_iter(from, npos());
1278  }
1279
1280  friend bool operator==(const insertion_ordered_multimap &lhs,
1281                         const insertion_ordered_multimap &rhs) {
1282    return lhs.entries_ == rhs.entries_;
1283  }
1284
1285  friend bool operator!=(const insertion_ordered_multimap &lhs,
1286                         const insertion_ordered_multimap &rhs) {
1287    return !(lhs == rhs);
1288  }
1289
1290private:
1291  size_type index_of(const std::string &key) const {
1292    for (size_type i = 0; i < entries_.size(); i++) {
1293      if (keys_equal(entries_[i].first, key)) { return i; }
1294    }
1295    return npos();
1296  }
1297
1298  iterator make_iter(size_type idx, size_type key_idx) {
1299    return iterator(entries_.data(), idx, entries_.size(), key_idx);
1300  }
1301
1302  const_iterator make_citer(size_type idx, size_type key_idx) const {
1303    return const_iterator(entries_.data(), idx, entries_.size(), key_idx);
1304  }
1305
1306  std::vector<value_type> entries_;
1307};
1308
1309} // namespace detail
1310
1311using Headers =
1312    detail::insertion_ordered_multimap<std::string,
1313                                       detail::case_ignore::equal_to>;
1314
1315// Query parameter names are case-sensitive, unlike header field names.
1316using Params =
1317    detail::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 {
1333  static const char id = 0;
1334  return &id;
1335}
1336
1337struct any_storage {
1338  virtual ~any_storage() = default;
1339  virtual std::unique_ptr<any_storage> clone() const = 0;
1340  virtual any_type_id type_id() const noexcept = 0;
1341};
1342
1343template <typename T> struct any_value final : any_storage {
1344  T value;
1345  template <typename U> explicit any_value(U &&v) : value(std::forward<U>(v)) {}
1346  std::unique_ptr<any_storage> clone() const override {
1347    return std::unique_ptr<any_storage>(new any_value<T>(value));
1348  }
1349  any_type_id type_id() const noexcept override { return any_typeid<T>(); }
1350};
1351
1352} // namespace detail
1353
1354class UserData {
1355public:
1356  UserData() = default;
1357  UserData(UserData &&) noexcept = default;
1358  UserData &operator=(UserData &&) noexcept = default;
1359
1360  UserData(const UserData &o) {
1361    for (const auto &e : o.entries_) {
1362      if (e.second) { entries_[e.first] = e.second->clone(); }
1363    }
1364  }
1365
1366  UserData &operator=(const UserData &o) {
1367    if (this != &o) {
1368      entries_.clear();
1369      for (const auto &e : o.entries_) {
1370        if (e.second) { entries_[e.first] = e.second->clone(); }
1371      }
1372    }
1373    return *this;
1374  }
1375
1376  template <typename T> void set(const std::string &key, T &&value) {
1377    using D = typename std::decay<T>::type;
1378    entries_[key].reset(new detail::any_value<D>(std::forward<T>(value)));
1379  }
1380
1381  template <typename T> T *get(const std::string &key) noexcept {
1382    auto it = entries_.find(key);
1383    if (it == entries_.end() || !it->second) { return nullptr; }
1384    if (it->second->type_id() != detail::any_typeid<T>()) { return nullptr; }
1385    return &static_cast<detail::any_value<T> *>(it->second.get())->value;
1386  }
1387
1388  template <typename T> const T *get(const std::string &key) const noexcept {
1389    auto it = entries_.find(key);
1390    if (it == entries_.end() || !it->second) { return nullptr; }
1391    if (it->second->type_id() != detail::any_typeid<T>()) { return nullptr; }
1392    return &static_cast<const detail::any_value<T> *>(it->second.get())->value;
1393  }
1394
1395  bool has(const std::string &key) const noexcept {
1396    return entries_.find(key) != entries_.end();
1397  }
1398
1399  void erase(const std::string &key) { entries_.erase(key); }
1400
1401  void clear() noexcept { entries_.clear(); }
1402
1403private:
1404  std::unordered_map<std::string, std::unique_ptr<detail::any_storage>>
1405      entries_;
1406};
1407
1408struct Response;
1409using ResponseHandler = std::function<bool(const Response &response)>;
1410
1411struct FormData {
1412  std::string name;
1413  std::string content;
1414  std::string filename;
1415  std::string content_type;
1416  Headers headers;
1417};
1418
1419struct FormField {
1420  std::string name;
1421  std::string content;
1422  Headers 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 =
1430    detail::insertion_ordered_multimap<FormField, std::equal_to<std::string>>;
1431
1432using FormFiles =
1433    detail::insertion_ordered_multimap<FormData, std::equal_to<std::string>>;
1434
1435struct MultipartFormData {
1436  FormFields fields; // Text fields from multipart
1437  FormFiles files;   // Files from multipart
1438
1439  // Text field access
1440  std::string get_field(const std::string &key, size_t id = 0) const;
1441  std::vector<std::string> get_fields(const std::string &key) const;
1442  bool has_field(const std::string &key) const;
1443  size_t get_field_count(const std::string &key) const;
1444
1445  // File access
1446  FormData get_file(const std::string &key, size_t id = 0) const;
1447  std::vector<FormData> get_files(const std::string &key) const;
1448  bool has_file(const std::string &key) const;
1449  size_t get_file_count(const std::string &key) const;
1450};
1451
1452struct UploadFormData {
1453  std::string name;
1454  std::string content;
1455  std::string filename;
1456  std::string content_type;
1457};
1458using UploadFormDataItems = std::vector<UploadFormData>;
1459
1460class DataSink {
1461public:
1462  DataSink() : os(&sb_), sb_(*this) {}
1463
1464  DataSink(const DataSink &) = delete;
1465  DataSink &operator=(const DataSink &) = delete;
1466  DataSink(DataSink &&) = delete;
1467  DataSink &operator=(DataSink &&) = delete;
1468
1469  std::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.
1475  std::function<bool()> is_writable = []() { return true; };
1476  std::function<void()> done = []() {};
1477  std::function<void(const Headers &trailer)> done_with_trailer =
1478      [this](const Headers & /*trailer*/) { done(); };
1479
1480  std::ostream os;
1481
1482private:
1483  class data_sink_streambuf final : public std::streambuf {
1484  public:
1485    explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
1486
1487  protected:
1488    std::streamsize xsputn(const char *s, std::streamsize n) override {
1489      if (sink_.write(s, static_cast<size_t>(n))) { return n; }
1490      return 0;
1491    }
1492
1493  private:
1494    DataSink &sink_;
1495  };
1496
1497  data_sink_streambuf sb_;
1498};
1499
1500using ContentProvider =
1501    std::function<bool(size_t offset, size_t length, DataSink &sink)>;
1502
1503using ContentProviderWithoutLength =
1504    std::function<bool(size_t offset, DataSink &sink)>;
1505
1506using ContentProviderResourceReleaser = std::function<void(bool success)>;
1507
1508struct FormDataProvider {
1509  std::string name;
1510  ContentProviderWithoutLength provider;
1511  std::string filename;
1512  std::string content_type;
1513};
1514using FormDataProviderItems = std::vector<FormDataProvider>;
1515
1516inline FormDataProvider
1517make_file_provider(const std::string &name, const std::string &filepath,
1518                   const std::string &filename = std::string(),
1519                   const std::string &content_type = std::string()) {
1520  FormDataProvider fdp;
1521  fdp.name = name;
1522  fdp.filename = filename.empty() ? filepath : filename;
1523  fdp.content_type = content_type;
1524  fdp.provider = [filepath](size_t offset, DataSink &sink) -> bool {
1525    std::ifstream f(filepath, std::ios::binary);
1526    if (!f) { return false; }
1527    if (offset > 0) {
1528      f.seekg(static_cast<std::streamoff>(offset));
1529      if (!f.good()) {
1530        sink.done();
1531        return true;
1532      }
1533    }
1534    char buf[8192];
1535    f.read(buf, sizeof(buf));
1536    auto n = static_cast<size_t>(f.gcount());
1537    if (n > 0) { return sink.write(buf, n); }
1538    sink.done(); // EOF
1539    return true;
1540  };
1541  return fdp;
1542}
1543
1544inline std::pair<size_t, ContentProvider>
1545make_file_body(const std::string &filepath) {
1546  size_t size = 0;
1547  {
1548    std::ifstream f(filepath, std::ios::binary | std::ios::ate);
1549    if (!f) { return {0, ContentProvider{}}; }
1550    size = static_cast<size_t>(f.tellg());
1551  }
1552
1553  ContentProvider provider = [filepath](size_t offset, size_t length,
1554                                        DataSink &sink) -> bool {
1555    std::ifstream f(filepath, std::ios::binary);
1556    if (!f) { return false; }
1557    f.seekg(static_cast<std::streamoff>(offset));
1558    if (!f.good()) { return false; }
1559    char buf[8192];
1560    while (length > 0) {
1561      auto to_read = (std::min)(sizeof(buf), length);
1562      f.read(buf, static_cast<std::streamsize>(to_read));
1563      auto 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.
1567      if (n == 0) { return false; }
1568      if (!sink.write(buf, n)) { return false; }
1569      length -= n;
1570    }
1571    return true;
1572  };
1573  return {size, std::move(provider)};
1574}
1575
1576using ContentReceiverWithProgress = std::function<bool(
1577    const char *data, size_t data_length, size_t offset, size_t total_length)>;
1578
1579using ContentReceiver =
1580    std::function<bool(const char *data, size_t data_length)>;
1581
1582using FormDataHeader = std::function<bool(const FormData &file)>;
1583
1584class ContentReader {
1585public:
1586  using Reader = std::function<bool(ContentReceiver receiver)>;
1587  using FormDataReader =
1588      std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
1589
1590  ContentReader(Reader reader, FormDataReader multipart_reader)
1591      : reader_(std::move(reader)),
1592        formdata_reader_(std::move(multipart_reader)) {}
1593
1594  bool operator()(FormDataHeader header, ContentReceiver receiver) const {
1595    return formdata_reader_(std::move(header), std::move(receiver));
1596  }
1597
1598  bool operator()(ContentReceiver receiver) const {
1599    return reader_(std::move(receiver));
1600  }
1601
1602  Reader reader_;
1603  FormDataReader 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 {
1622  TLS1_2 = 0x0303,
1623  TLS1_3 = 0x0304,
1624};
1625
1626// Subject Alternative Names (SAN) entry types
1627enum class SanType { DNS, IP, EMAIL, URI, OTHER };
1628
1629// SAN entry structure
1630struct SanEntry {
1631  SanType type;
1632  std::string value;
1633};
1634
1635// Verification context for certificate verification callback
1636struct VerifyContext {
1637  session_t session;        // TLS session handle
1638  cert_t cert;              // Current certificate being verified
1639  int depth;                // Certificate chain depth (0 = leaf)
1640  bool preverify_ok;        // OpenSSL/Mbed TLS pre-verification result
1641  long error_code;          // Backend-specific error code (0 = no error)
1642  const char *error_string; // Human-readable error description
1643
1644  // Certificate introspection methods
1645  std::string subject_cn() const;
1646  std::string issuer_name() const;
1647  bool check_hostname(const char *hostname) const;
1648  std::vector<SanEntry> sans() const;
1649  bool validity(time_t &not_before, time_t &not_after) const;
1650  std::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 {
1657  Success = 0,
1658  WantRead,         // Non-blocking: need to wait for read
1659  WantWrite,        // Non-blocking: need to wait for write
1660  PeerClosed,       // Peer closed the connection
1661  Fatal,            // Unrecoverable error
1662  SyscallError,     // System call error (check sys_errno)
1663  CertVerifyFailed, // Certificate verification failed
1664  HostnameMismatch, // Hostname verification failed
1665};
1666
1667// TLS error information
1668struct TlsError {
1669  ErrorCode code = ErrorCode::Fatal;
1670  uint64_t backend_code = 0; // OpenSSL: ERR_get_error(), mbedTLS: return value
1671  int sys_errno = 0;         // errno when SyscallError
1672
1673  // Convert verification error code to human-readable string
1674  static std::string verify_error_to_string(long error_code);
1675};
1676
1677// RAII wrapper for peer certificate
1678class PeerCert {
1679public:
1680  PeerCert();
1681  PeerCert(PeerCert &&other) noexcept;
1682  PeerCert &operator=(PeerCert &&other) noexcept;
1683  ~PeerCert();
1684
1685  PeerCert(const PeerCert &) = delete;
1686  PeerCert &operator=(const PeerCert &) = delete;
1687
1688  explicit operator bool() const;
1689  std::string subject_cn() const;
1690  std::string issuer_name() const;
1691  bool check_hostname(const char *hostname) const;
1692  std::vector<SanEntry> sans() const;
1693  bool validity(time_t &not_before, time_t &not_after) const;
1694  std::string serial() const;
1695
1696private:
1697  explicit PeerCert(cert_t cert);
1698  cert_t cert_ = nullptr;
1699  friend 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 {
1709  std::string method;
1710  std::string path;
1711  std::string matched_route;
1712  Params params;
1713  Headers headers;
1714  Headers trailers;
1715  std::string body;
1716
1717  std::string remote_addr;
1718  int remote_port = -1;
1719  std::string local_addr;
1720  int local_port = -1;
1721
1722  // for server
1723  std::string version;
1724  std::string target;
1725  MultipartFormData form;
1726  Ranges ranges;
1727  Match matches;
1728  std::unordered_map<std::string, std::string> path_params;
1729  std::function<bool()> is_connection_closed = []() { return true; };
1730
1731  // for client
1732  std::vector<std::string> accept_content_types;
1733  ResponseHandler response_handler;
1734  ContentReceiverWithProgress content_receiver;
1735  DownloadProgress download_progress;
1736  UploadProgress upload_progress;
1737
1738  bool has_header(const std::string &key) const;
1739  std::string get_header_value(const std::string &key, const char *def = "",
1740                               size_t id = 0) const;
1741  size_t get_header_value_u64(const std::string &key, size_t def = 0,
1742                              size_t id = 0) const;
1743  size_t get_header_value_count(const std::string &key) const;
1744  void set_header(const std::string &key, const std::string &val);
1745
1746  bool has_trailer(const std::string &key) const;
1747  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
1748  size_t get_trailer_value_count(const std::string &key) const;
1749
1750  bool has_param(const std::string &key) const;
1751  std::string get_param_value(const std::string &key, size_t id = 0) const;
1752  std::vector<std::string> get_param_values(const std::string &key) const;
1753  size_t get_param_value_count(const std::string &key) const;
1754
1755  bool is_multipart_form_data() const;
1756
1757  // private members...
1758  bool body_consumed_ = false;
1759  size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
1760  size_t content_length_ = 0;
1761  ContentProvider content_provider_;
1762  bool is_chunked_content_provider_ = false;
1763  size_t authorization_count_ = 0;
1764  std::chrono::time_point<std::chrono::steady_clock> start_time_ =
1765      (std::chrono::steady_clock::time_point::min)();
1766
1767#ifdef CPPHTTPLIB_SSL_ENABLED
1768  tls::const_session_t ssl = nullptr;
1769  tls::PeerCert peer_cert() const;
1770  std::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 {
1783  std::string version;
1784  int status = -1;
1785  std::string reason;
1786  Headers headers;
1787  Headers trailers;
1788  std::string body;
1789  std::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).
1793  UserData user_data;
1794
1795  bool has_header(const std::string &key) const;
1796  std::string get_header_value(const std::string &key, const char *def = "",
1797                               size_t id = 0) const;
1798  size_t get_header_value_u64(const std::string &key, size_t def = 0,
1799                              size_t id = 0) const;
1800  size_t get_header_value_count(const std::string &key) const;
1801  void set_header(const std::string &key, const std::string &val);
1802
1803  bool has_trailer(const std::string &key) const;
1804  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
1805  size_t get_trailer_value_count(const std::string &key) const;
1806
1807  void set_redirect(const std::string &url, int status = StatusCode::Found_302);
1808  void set_content(const char *s, size_t n, const std::string &content_type);
1809  void set_content(const std::string &s, const std::string &content_type);
1810  void set_content(std::string &&s, const std::string &content_type);
1811
1812  void set_content_provider(
1813      size_t length, const std::string &content_type, ContentProvider provider,
1814      ContentProviderResourceReleaser resource_releaser = nullptr);
1815
1816  void set_content_provider(
1817      const std::string &content_type, ContentProviderWithoutLength provider,
1818      ContentProviderResourceReleaser resource_releaser = nullptr);
1819
1820  void set_chunked_content_provider(
1821      const std::string &content_type, ContentProviderWithoutLength provider,
1822      ContentProviderResourceReleaser resource_releaser = nullptr);
1823
1824  void set_file_content(const std::string &path,
1825                        const std::string &content_type);
1826  void set_file_content(const std::string &path);
1827
1828  Response() = default;
1829  Response(const Response &) = default;
1830  Response &operator=(const Response &) = default;
1831  Response(Response &&) = default;
1832  Response &operator=(Response &&) = default;
1833  ~Response() {
1834    if (content_provider_resource_releaser_) {
1835      content_provider_resource_releaser_(content_provider_success_);
1836    }
1837  }
1838
1839  // private members...
1840  size_t content_length_ = 0;
1841  ContentProvider content_provider_;
1842  ContentProviderResourceReleaser content_provider_resource_releaser_;
1843  bool is_chunked_content_provider_ = false;
1844  bool content_provider_success_ = false;
1845  std::string file_content_path_;
1846  std::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.
1853  detail::EncodingType content_coding_ = detail::EncodingType::None;
1854};
1855
1856enum class Error {
1857  Success = 0,
1858  Unknown,
1859  Connection,
1860  BindIPAddress,
1861  Read,
1862  Write,
1863  ExceedRedirectCount,
1864  Canceled,
1865  SSLConnection,
1866  SSLLoadingCerts,
1867  SSLServerVerification,
1868  SSLServerHostnameVerification,
1869  UnsupportedMultipartBoundaryChars,
1870  Compression,
1871  ConnectionTimeout,
1872  ProxyConnection,
1873  ConnectionClosed,
1874  Timeout,
1875  ResourceExhaustion,
1876  TooManyFormDataFiles,
1877  ExceedMaxPayloadSize,
1878  ExceedUriMaxLength,
1879  ExceedMaxSocketDescriptorCount,
1880  InvalidRequestLine,
1881  InvalidHTTPMethod,
1882  InvalidHTTPVersion,
1883  InvalidHeaders,
1884  MultipartParsing,
1885  OpenFile,
1886  Listen,
1887  GetSockName,
1888  UnsupportedAddressFamily,
1889  HTTPParsing,
1890  InvalidRangeHeader,
1891  UnsupportedContentEncoding,
1892  WebSocketHandshake,
1893  UserCallbackException,
1894
1895  // For internal use only
1896  SSLPeerCouldBeClosed_,
1897};
1898
1899std::string to_string(Error error);
1900
1901std::ostream &operator<<(std::ostream &os, const Error &obj);
1902
1903class Stream {
1904public:
1905  virtual ~Stream() = default;
1906
1907  virtual bool is_readable() const = 0;
1908  virtual bool wait_readable() const = 0;
1909  virtual bool wait_writable() const = 0;
1910  virtual bool is_peer_alive() const { return wait_writable(); }
1911
1912  virtual ssize_t read(char *ptr, size_t size) = 0;
1913  virtual ssize_t write(const char *ptr, size_t size) = 0;
1914  virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
1915  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
1916  virtual socket_t socket() const = 0;
1917
1918  virtual time_t duration() const = 0;
1919
1920  virtual 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().
1929  virtual const char *buffered_data(size_t &size) const {
1930    size = 0;
1931    return nullptr;
1932  }
1933
1934  // Discards `size` bytes previously returned by buffered_data().
1935  virtual void consume_buffered(size_t size) { (void)size; }
1936
1937  ssize_t write(const char *ptr);
1938  ssize_t write(const std::string &s);
1939
1940  Error get_error() const { return error_; }
1941
1942protected:
1943  Error error_ = Error::Success;
1944};
1945
1946class TaskQueue {
1947public:
1948  TaskQueue() = default;
1949  virtual ~TaskQueue() = default;
1950
1951  virtual bool enqueue(std::function<void()> fn) = 0;
1952  virtual void shutdown() = 0;
1953
1954  virtual void on_idle() {}
1955};
1956
1957class ThreadPool final : public TaskQueue {
1958public:
1959  explicit ThreadPool(
1960      size_t n, size_t max_n = 0, size_t mqr = 0,
1961      time_t idle_timeout_sec = CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT);
1962  ThreadPool(const ThreadPool &) = delete;
1963  ~ThreadPool() override = default;
1964
1965  bool enqueue(std::function<void()> fn) override;
1966  void shutdown() override;
1967
1968private:
1969  void worker(bool is_dynamic);
1970  void move_to_finished(std::thread::id id);
1971  void cleanup_finished_threads();
1972
1973  size_t base_thread_count_;
1974  size_t max_thread_count_;
1975  size_t max_queued_requests_;
1976  time_t idle_timeout_sec_;
1977  size_t idle_thread_count_;
1978
1979  bool shutdown_;
1980
1981  std::list<std::function<void()>> jobs_;
1982  std::vector<std::thread> threads_;       // base threads
1983  std::list<std::thread> dynamic_threads_; // dynamic threads
1984  std::vector<std::thread>
1985      finished_threads_; // exited dynamic threads awaiting join
1986
1987  std::condition_variable cond_;
1988  std::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:
2015  MatcherBase(std::string pattern) : pattern_(std::move(pattern)) {}
2016  virtual ~MatcherBase() = default;
2017
2018  const std::string &pattern() const { return pattern_; }
2019
2020  // Match request path and populate its matches and
2021  virtual bool match(Request &request) const = 0;
2022
2023private:
2024  std::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:
2047  PathParamsMatcher(const std::string &pattern);
2048
2049  bool 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
2055  static 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
2060  std::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
2063  std::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:
2076  RegexMatcher(const std::string &pattern)
2077      : MatcherBase(pattern), regex_(pattern) {}
2078
2079  bool match(Request &request) const override;
2080
2081private:
2082  std::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,
2094                         time_t usec);
2095
2096size_t get_multipart_content_length(const UploadFormDataItems &items,
2097                                    const std::string &boundary);
2098
2099ContentProvider
2100make_multipart_content_provider(const UploadFormDataItems &items,
2101                                const 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:
2114  MultipartFormDataWriter();
2115  // precondition: is_valid_multipart_boundary(boundary)
2116  explicit MultipartFormDataWriter(std::string boundary);
2117
2118  const std::string &boundary() const;
2119  std::string content_type() const;
2120
2121  // In-memory items -> whole body (known length)
2122  std::string serialize(const UploadFormDataItems &items) const;
2123  size_t content_length(const UploadFormDataItems &items) const;
2124
2125  // Per-part framing for streaming via a content provider
2126  std::string item_begin(const UploadFormData &item) const;
2127  static std::string item_end();
2128  std::string finish() const;
2129
2130private:
2131  std::string boundary_;
2132};
2133
2134class Server {
2135public:
2136  using Handler = std::function<void(const Request &, Response &)>;
2137
2138  using ExceptionHandler =
2139      std::function<void(const Request &, Response &, std::exception_ptr ep)>;
2140
2141  enum class HandlerResponse {
2142    Handled,
2143    Unhandled,
2144  };
2145  using HandlerWithResponse =
2146      std::function<HandlerResponse(const Request &, Response &)>;
2147
2148  using HandlerWithContentReader = std::function<void(
2149      const Request &, Response &, const ContentReader &content_reader)>;
2150
2151  using Expect100ContinueHandler =
2152      std::function<int(const Request &, Response &)>;
2153
2154  using StartHandler = std::function<void()>;
2155
2156  using WebSocketHandler =
2157      std::function<void(const Request &, ws::WebSocket &)>;
2158  using SubProtocolSelector =
2159      std::function<std::string(const std::vector<std::string> &protocols)>;
2160
2161  Server();
2162
2163  virtual ~Server();
2164
2165  virtual bool is_valid() const;
2166
2167  Server &Get(const std::string &pattern, Handler handler);
2168  Server &Post(const std::string &pattern, Handler handler);
2169  Server &Post(const std::string &pattern, HandlerWithContentReader handler);
2170  Server &Put(const std::string &pattern, Handler handler);
2171  Server &Put(const std::string &pattern, HandlerWithContentReader handler);
2172  Server &Patch(const std::string &pattern, Handler handler);
2173  Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
2174  Server &Delete(const std::string &pattern, Handler handler);
2175  Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
2176  Server &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.
2184  Server &CustomRoute(const std::string &method, const std::string &pattern,
2185                      Handler handler);
2186  Server &CustomRoute(const std::string &method, const std::string &pattern,
2187                      HandlerWithContentReader handler);
2188
2189  Server &WebSocket(const std::string &pattern, WebSocketHandler handler);
2190  Server &WebSocket(const std::string &pattern, WebSocketHandler handler,
2191                    SubProtocolSelector sub_protocol_selector);
2192
2193  bool set_base_dir(const std::string &dir,
2194                    const std::string &mount_point = std::string());
2195  bool set_mount_point(const std::string &mount_point, const std::string &dir,
2196                       Headers headers = Headers());
2197  bool remove_mount_point(const std::string &mount_point);
2198  Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
2199                                                  const std::string &mime);
2200  Server &set_default_file_mimetype(const std::string &mime);
2201  Server &set_file_request_handler(Handler handler);
2202
2203  template <class ErrorHandlerFunc>
2204  Server &set_error_handler(ErrorHandlerFunc &&handler) {
2205    return set_error_handler_core(
2206        std::forward<ErrorHandlerFunc>(handler),
2207        std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
2208  }
2209
2210  Server &set_exception_handler(ExceptionHandler handler);
2211
2212  Server &set_pre_routing_handler(HandlerWithResponse handler);
2213  Server &set_post_routing_handler(Handler handler);
2214
2215  Server &set_pre_request_handler(HandlerWithResponse handler);
2216
2217  Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
2218
2219  Server &set_start_handler(StartHandler handler);
2220
2221  Server &set_logger(Logger logger);
2222  Server &set_pre_compression_logger(Logger logger);
2223  Server &set_error_logger(ErrorLogger error_logger);
2224
2225  Server &set_address_family(int family);
2226  Server &set_tcp_nodelay(bool on);
2227  Server &set_ipv6_v6only(bool on);
2228  Server &set_socket_options(SocketOptions socket_options);
2229
2230  Server &set_default_headers(Headers headers);
2231  Server &
2232  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
2233
2234  Server &set_trusted_proxies(const std::vector<std::string> &proxies);
2235
2236  Server &set_keep_alive_max_count(size_t count);
2237  Server &set_keep_alive_timeout(time_t sec);
2238  template <class Rep, class Period>
2239  Server &
2240  set_keep_alive_timeout(const std::chrono::duration<Rep, Period> &duration);
2241
2242  Server &set_read_timeout(time_t sec, time_t usec = 0);
2243  template <class Rep, class Period>
2244  Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
2245
2246  Server &set_write_timeout(time_t sec, time_t usec = 0);
2247  template <class Rep, class Period>
2248  Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
2249
2250  Server &set_idle_interval(time_t sec, time_t usec = 0);
2251  template <class Rep, class Period>
2252  Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
2253
2254  Server &set_payload_max_length(size_t length);
2255
2256  Server &set_static_file_compression(bool on);
2257  Server &set_static_file_compression_min_length(size_t length);
2258  Server &set_static_file_compression_max_length(size_t length);
2259
2260  Server &set_websocket_ping_interval(time_t sec);
2261  template <class Rep, class Period>
2262  Server &set_websocket_ping_interval(
2263      const std::chrono::duration<Rep, Period> &duration);
2264
2265  Server &set_websocket_max_missed_pongs(int count);
2266
2267  bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
2268  int bind_to_any_port(const std::string &host, int socket_flags = 0);
2269  bool listen_after_bind();
2270
2271  bool listen(const std::string &host, int port, int socket_flags = 0);
2272
2273  bool is_running() const;
2274  void wait_until_ready() const;
2275  void stop() noexcept;
2276  void decommission();
2277
2278  std::function<TaskQueue *(void)> new_task_queue;
2279
2280protected:
2281  bool process_request(Stream &strm, const std::string &remote_addr,
2282                       int remote_port, const std::string &local_addr,
2283                       int local_port, bool close_connection,
2284                       bool &connection_closed,
2285                       const std::function<void(Request &)> &setup_request,
2286                       bool *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.
2300  template <typename Serve> bool serve_guarded(Serve &&serve) const {
2301#ifdef CPPHTTPLIB_NO_EXCEPTIONS
2302    return serve();
2303#else
2304    try {
2305      return 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.
2309      try {
2310        output_error_log(Error::UserCallbackException, nullptr);
2311      } catch (...) {}
2312      return false;
2313    }
2314#endif
2315  }
2316
2317  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
2318
2319  std::vector<std::string> trusted_proxies_;
2320
2321  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
2322  time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
2323  time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
2324  time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
2325  time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
2326  time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
2327  time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
2328  time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
2329  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
2330  bool static_file_compression_ = false;
2331  size_t static_file_compression_min_length_ =
2332      CPPHTTPLIB_STATIC_FILE_COMPRESSION_MIN_LENGTH;
2333  size_t static_file_compression_max_length_ =
2334      CPPHTTPLIB_STATIC_FILE_COMPRESSION_MAX_LENGTH;
2335  time_t websocket_ping_interval_sec_ =
2336      CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND;
2337  int websocket_max_missed_pongs_ = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS;
2338
2339private:
2340  using Handlers =
2341      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
2342  using HandlersForContentReader =
2343      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
2344                            HandlerWithContentReader>>;
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.
2348  struct CustomHandlerEntry {
2349    Handlers handlers;
2350    HandlersForContentReader handlers_for_content_reader;
2351  };
2352  using CustomHandlers = std::map<std::string, CustomHandlerEntry>;
2353
2354  static std::unique_ptr<detail::MatcherBase>
2355  make_matcher(const std::string &pattern);
2356
2357  static const std::set<std::string> &builtin_methods();
2358  CustomHandlerEntry *custom_entry_for_registration(const std::string &method);
2359  const CustomHandlerEntry *find_custom_entry(const std::string &method) const;
2360
2361  template <typename H>
2362  Server &add_handler(
2363      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, H>> &handlers,
2364      const std::string &pattern, H handler) {
2365    handlers.emplace_back(make_matcher(pattern), std::move(handler));
2366    return *this;
2367  }
2368
2369  Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
2370  Server &set_error_handler_core(Handler handler, std::false_type);
2371
2372  socket_t create_server_socket(const std::string &host, int port,
2373                                int socket_flags,
2374                                SocketOptions socket_options) const;
2375  int bind_internal(const std::string &host, int port, int socket_flags);
2376  bool listen_internal();
2377
2378  bool routing(Request &req, Response &res, Stream &strm);
2379  bool handle_file_request(Request &req, Response &res);
2380  bool check_if_not_modified(const Request &req, Response &res,
2381                             const std::string &etag, time_t mtime) const;
2382  bool check_if_range(Request &req, const std::string &etag,
2383                      time_t mtime) const;
2384  bool dispatch_request(Request &req, Response &res, const Handlers &handlers,
2385                        Stream &strm);
2386  bool dispatch_request_for_content_reader(
2387      Request &req, Response &res, ContentReader content_reader,
2388      const HandlersForContentReader &handlers) const;
2389
2390  bool parse_request_line(const char *s, Request &req) const;
2391  detail::EncodingType static_file_encoding(const Request &req,
2392                                            const Response &res,
2393                                            const std::string &content_type,
2394                                            size_t length) const;
2395  bool apply_static_file_compression(const Request &req, Response &res) const;
2396  void apply_ranges(const Request &req, Response &res,
2397                    std::string &content_type, std::string &boundary) const;
2398  bool write_response(Stream &strm, bool close_connection, Request &req,
2399                      Response &res);
2400  bool write_response_with_content(Stream &strm, bool close_connection,
2401                                   const Request &req, Response &res);
2402  bool write_response_core(Stream &strm, bool close_connection,
2403                           const Request &req, Response &res,
2404                           bool need_apply_ranges);
2405  bool write_content_with_provider(Stream &strm, const Request &req,
2406                                   Response &res, const std::string &boundary,
2407                                   const std::string &content_type);
2408  bool read_content(Stream &strm, Request &req, Response &res);
2409  bool read_content_with_content_receiver(Stream &strm, Request &req,
2410                                          Response &res,
2411                                          ContentReceiver receiver,
2412                                          FormDataHeader multipart_header,
2413                                          ContentReceiver multipart_receiver);
2414  bool read_content_core(Stream &strm, Request &req, Response &res,
2415                         ContentReceiver receiver,
2416                         FormDataHeader multipart_header,
2417                         ContentReceiver multipart_receiver) const;
2418
2419  virtual bool process_and_close_socket(socket_t sock);
2420
2421  void output_log(const Request &req, const Response &res) const;
2422  void output_pre_compression_log(const Request &req,
2423                                  const Response &res) const;
2424  void output_error_log(const Error &err, const Request *req) const;
2425
2426  std::atomic<bool> is_running_{false};
2427  std::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.
2431  bool has_invalid_registration_ = false;
2432
2433  struct MountPointEntry {
2434    std::string mount_point;
2435    std::string base_dir;
2436    std::string resolved_base_dir;
2437    Headers headers;
2438  };
2439  std::vector<MountPointEntry> base_dirs_;
2440  std::map<std::string, std::string> file_extension_and_mimetype_map_;
2441  std::string default_file_mimetype_ = "application/octet-stream";
2442  Handler file_request_handler_;
2443
2444  Handlers get_handlers_;
2445  Handlers post_handlers_;
2446  HandlersForContentReader post_handlers_for_content_reader_;
2447  Handlers put_handlers_;
2448  HandlersForContentReader put_handlers_for_content_reader_;
2449  Handlers patch_handlers_;
2450  HandlersForContentReader patch_handlers_for_content_reader_;
2451  Handlers delete_handlers_;
2452  HandlersForContentReader delete_handlers_for_content_reader_;
2453  Handlers options_handlers_;
2454  CustomHandlers custom_handlers_;
2455
2456  struct WebSocketHandlerEntry {
2457    std::unique_ptr<detail::MatcherBase> matcher;
2458    WebSocketHandler handler;
2459    SubProtocolSelector sub_protocol_selector;
2460  };
2461  using WebSocketHandlers = std::vector<WebSocketHandlerEntry>;
2462  WebSocketHandlers websocket_handlers_;
2463
2464  HandlerWithResponse error_handler_;
2465  ExceptionHandler exception_handler_;
2466  HandlerWithResponse pre_routing_handler_;
2467  Handler post_routing_handler_;
2468  HandlerWithResponse pre_request_handler_;
2469  Expect100ContinueHandler expect_100_continue_handler_;
2470  StartHandler start_handler_;
2471
2472  mutable std::mutex logger_mutex_;
2473  Logger logger_;
2474  Logger pre_compression_logger_;
2475  ErrorLogger error_logger_;
2476
2477  int address_family_ = AF_UNSPEC;
2478  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
2479  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
2480  SocketOptions socket_options_ = default_socket_options;
2481
2482  Headers default_headers_;
2483  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
2484      detail::write_headers;
2485};
2486
2487class Result {
2488public:
2489  Result() = default;
2490  Result(std::unique_ptr<Response> &&res, Error err,
2491         Headers &&request_headers = Headers{})
2492      : res_(std::move(res)), err_(err),
2493        request_headers_(std::move(request_headers)) {}
2494  // Response
2495  operator bool() const { return res_ != nullptr; }
2496  bool operator==(std::nullptr_t) const { return res_ == nullptr; }
2497  bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
2498  const Response &value() const { return *res_; }
2499  Response &value() { return *res_; }
2500  const Response &operator*() const { return *res_; }
2501  Response &operator*() { return *res_; }
2502  const Response *operator->() const { return res_.get(); }
2503  Response *operator->() { return res_.get(); }
2504
2505  // Error
2506  Error error() const { return err_; }
2507
2508  // Request Headers
2509  bool has_request_header(const std::string &key) const;
2510  std::string get_request_header_value(const std::string &key,
2511                                       const char *def = "",
2512                                       size_t id = 0) const;
2513  size_t get_request_header_value_u64(const std::string &key, size_t def = 0,
2514                                      size_t id = 0) const;
2515  size_t get_request_header_value_count(const std::string &key) const;
2516
2517private:
2518  std::unique_ptr<Response> res_;
2519  Error err_ = Error::Unknown;
2520  Headers request_headers_;
2521
2522#ifdef CPPHTTPLIB_SSL_ENABLED
2523public:
2524  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
2525         int ssl_error)
2526      : res_(std::move(res)), err_(err),
2527        request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
2528  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
2529         int ssl_error, uint64_t ssl_backend_error)
2530      : res_(std::move(res)), err_(err),
2531        request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
2532        ssl_backend_error_(ssl_backend_error) {}
2533
2534  int ssl_error() const { return ssl_error_; }
2535  uint64_t ssl_backend_error() const { return ssl_backend_error_; }
2536
2537private:
2538  int ssl_error_ = 0;
2539  uint64_t ssl_backend_error_ = 0;
2540#endif
2541};
2542
2543struct ClientConnection {
2544  socket_t sock = INVALID_SOCKET;
2545
2546  bool is_open() const { return sock != INVALID_SOCKET; }
2547
2548  ClientConnection() = default;
2549
2550  ~ClientConnection();
2551
2552  ClientConnection(const ClientConnection &) = delete;
2553  ClientConnection &operator=(const ClientConnection &) = delete;
2554
2555  ClientConnection(ClientConnection &&other) noexcept
2556      : sock(other.sock)
2557#ifdef CPPHTTPLIB_SSL_ENABLED
2558        ,
2559        session(other.session)
2560#endif
2561  {
2562    other.sock = INVALID_SOCKET;
2563#ifdef CPPHTTPLIB_SSL_ENABLED
2564    other.session = nullptr;
2565#endif
2566  }
2567
2568  ClientConnection &operator=(ClientConnection &&other) noexcept {
2569    if (this != &other) {
2570      sock = other.sock;
2571      other.sock = INVALID_SOCKET;
2572#ifdef CPPHTTPLIB_SSL_ENABLED
2573      session = other.session;
2574      other.session = nullptr;
2575#endif
2576    }
2577    return *this;
2578  }
2579
2580#ifdef CPPHTTPLIB_SSL_ENABLED
2581  tls::session_t session = nullptr;
2582#endif
2583};
2584
2585namespace detail {
2586
2587struct ChunkedDecoder;
2588
2589struct BodyReader {
2590  Stream *stream = nullptr;
2591  bool has_content_length = false;
2592  size_t content_length = 0;
2593  size_t payload_max_length = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
2594  size_t bytes_read = 0;
2595  bool chunked = false;
2596  bool eof = false;
2597  std::unique_ptr<ChunkedDecoder> chunked_decoder;
2598  Error last_error = Error::Success;
2599
2600  ssize_t read(char *buf, size_t len);
2601  bool has_error() const { return last_error != Error::Success; }
2602};
2603
2604inline ssize_t read_body_content(Stream *stream, BodyReader &br, char *buf,
2605                                 size_t len) {
2606  (void)stream;
2607  return br.read(buf, len);
2608}
2609
2610class decompressor;
2611
2612enum class NoProxyKind {
2613  Wildcard,       // "*"
2614  HostnameSuffix, // "example.com" or ".example.com"
2615  IPv4Cidr,       // "10.0.0.0/8" (or single IP, treated as /32)
2616  IPv6Cidr,       // "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 {
2624  NoProxyKind kind = NoProxyKind::Wildcard;
2625  std::string hostname_pattern; // lowercased, leading/trailing dot stripped
2626  IPBytes net{};
2627  int prefix_bits = 0;
2628};
2629
2630struct NormalizedTarget {
2631  std::string hostname; // lowercase; brackets and trailing dot removed
2632  bool is_ipv4 = false;
2633  bool is_ipv6 = false;
2634  IPBytes ip{};
2635};
2636
2637} // namespace detail
2638
2639class ClientImpl {
2640public:
2641  explicit ClientImpl(const std::string &host);
2642
2643  explicit ClientImpl(const std::string &host, int port);
2644
2645  explicit ClientImpl(const std::string &host, int port,
2646                      const std::string &client_cert_path,
2647                      const std::string &client_key_path);
2648
2649  virtual ~ClientImpl();
2650
2651  virtual bool is_valid() const;
2652
2653  struct StreamHandle {
2654    std::unique_ptr<Response> response;
2655    Error error = Error::Success;
2656
2657    StreamHandle() = default;
2658    StreamHandle(const StreamHandle &) = delete;
2659    StreamHandle &operator=(const StreamHandle &) = delete;
2660    StreamHandle(StreamHandle &&) = default;
2661    StreamHandle &operator=(StreamHandle &&) = default;
2662    ~StreamHandle() = default;
2663
2664    bool is_valid() const {
2665      return response != nullptr && error == Error::Success;
2666    }
2667
2668    ssize_t read(char *buf, size_t len);
2669    void parse_trailers_if_needed();
2670    Error get_read_error() const { return body_reader_.last_error; }
2671    bool has_read_error() const { return body_reader_.has_error(); }
2672
2673    bool trailers_parsed_ = false;
2674
2675  private:
2676    friend class ClientImpl;
2677
2678    ssize_t read_with_decompression(char *buf, size_t len);
2679
2680    std::unique_ptr<ClientConnection> connection_;
2681    std::unique_ptr<Stream> socket_stream_;
2682    Stream *stream_ = nullptr;
2683    detail::BodyReader body_reader_;
2684
2685    std::unique_ptr<detail::decompressor> decompressor_;
2686    std::string decompress_buffer_;
2687    size_t decompress_offset_ = 0;
2688    size_t decompressed_bytes_read_ = 0;
2689  };
2690
2691  // clang-format off
2692  Result Get(const std::string &path, DownloadProgress progress = nullptr);
2693  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2694  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2695  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2696  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2697  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2698  Result Get(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
2699  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
2700  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2701  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2702
2703  Result Head(const std::string &path);
2704  Result Head(const std::string &path, const Headers &headers);
2705
2706  Result Post(const std::string &path);
2707  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2708  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2709  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2710  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2711  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2712  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2713  Result Post(const std::string &path, const Params &params);
2714  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2715  Result Post(const std::string &path, const Headers &headers);
2716  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2717  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2718  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2719  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2720  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2721  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2722  Result Post(const std::string &path, const Headers &headers, const Params &params);
2723  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2724  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2725  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2726  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2727
2728  Result Put(const std::string &path);
2729  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2730  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2731  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2732  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2733  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2734  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2735  Result Put(const std::string &path, const Params &params);
2736  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2737  Result Put(const std::string &path, const Headers &headers);
2738  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2739  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2740  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2741  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2742  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2743  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2744  Result Put(const std::string &path, const Headers &headers, const Params &params);
2745  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2746  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2747  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2748  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2749
2750  Result Patch(const std::string &path);
2751  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2752  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2753  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2754  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2755  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2756  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2757  Result Patch(const std::string &path, const Params &params);
2758  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2759  Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
2760  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2761  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2762  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2763  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2764  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2765  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2766  Result Patch(const std::string &path, const Headers &headers, const Params &params);
2767  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2768  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2769  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2770  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2771
2772  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
2773  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2774  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2775  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
2776  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2777  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2778  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2779  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
2780
2781  Result Options(const std::string &path);
2782  Result 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.)
2788  StreamHandle open_stream(const std::string &method, const std::string &path,
2789                           const Params &params = {},
2790                           const Headers &headers = {},
2791                           const std::string &body = {},
2792                           const std::string &content_type = {});
2793
2794  bool send(Request &req, Response &res, Error &error);
2795  Result send(const Request &req);
2796
2797  void stop();
2798
2799  std::string host() const;
2800  int port() const;
2801
2802  size_t is_socket_open() const;
2803  socket_t socket() const;
2804
2805  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
2806
2807  void set_default_headers(Headers headers);
2808
2809  void
2810  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
2811
2812  void set_address_family(int family);
2813  void set_tcp_nodelay(bool on);
2814  void set_ipv6_v6only(bool on);
2815  void set_socket_options(SocketOptions socket_options);
2816
2817  void set_connection_timeout(time_t sec, time_t usec = 0);
2818  template <class Rep, class Period>
2819  void
2820  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
2821
2822  void set_read_timeout(time_t sec, time_t usec = 0);
2823  template <class Rep, class Period>
2824  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
2825
2826  void set_write_timeout(time_t sec, time_t usec = 0);
2827  template <class Rep, class Period>
2828  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
2829
2830  void set_max_timeout(time_t msec);
2831  template <class Rep, class Period>
2832  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
2833
2834  void set_basic_auth(const std::string &username, const std::string &password);
2835  void set_bearer_token_auth(const std::string &token);
2836
2837  void set_keep_alive(bool on);
2838  void set_follow_location(bool on);
2839
2840  void set_path_encode(bool on);
2841
2842  void set_compress(bool on);
2843
2844  void set_decompress(bool on);
2845
2846  void set_payload_max_length(size_t length);
2847
2848  void set_interface(const std::string &intf);
2849
2850  void set_proxy(const std::string &host, int port);
2851  void set_proxy_basic_auth(const std::string &username,
2852                            const std::string &password);
2853  void set_proxy_bearer_token_auth(const std::string &token);
2854  void set_no_proxy(const std::vector<std::string> &patterns);
2855
2856  void set_logger(Logger logger);
2857  void set_error_logger(ErrorLogger error_logger);
2858
2859protected:
2860  struct Socket {
2861    socket_t sock = INVALID_SOCKET;
2862
2863    // For Mbed TLS compatibility: start_time for request timeout tracking
2864    std::chrono::time_point<std::chrono::steady_clock> start_time_;
2865
2866    bool is_open() const { return sock != INVALID_SOCKET; }
2867
2868#ifdef CPPHTTPLIB_SSL_ENABLED
2869    tls::session_t ssl = nullptr;
2870#endif
2871  };
2872
2873  virtual bool create_and_connect_socket(Socket &socket, Error &error);
2874  virtual bool ensure_socket_connection(Socket &socket, Error &error);
2875  virtual bool setup_proxy_connection(
2876      Socket &socket,
2877      std::chrono::time_point<std::chrono::steady_clock> start_time,
2878      Response &res, bool &success, Error &error);
2879
2880  bool 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.
2889  virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
2890  void shutdown_socket(Socket &socket) const;
2891  void close_socket(Socket &socket);
2892  void disconnect(bool gracefully);
2893
2894  bool process_request(Stream &strm, Request &req, Response &res,
2895                       bool close_connection, Error &error);
2896
2897  bool write_content_with_provider(Stream &strm, const Request &req,
2898                                   Error &error) const;
2899
2900  void copy_settings(const ClientImpl &rhs);
2901
2902  void output_log(const Request &req, const Response &res) const;
2903  void output_error_log(const Error &err, const Request *req) const;
2904
2905  // Socket endpoint information
2906  const std::string host_;
2907  const int port_;
2908
2909  // Current open socket
2910  Socket socket_;
2911  mutable std::mutex socket_mutex_;
2912  std::recursive_mutex request_mutex_;
2913
2914  // These are all protected under socket_mutex
2915  size_t socket_requests_in_flight_ = 0;
2916  std::thread::id socket_requests_are_from_thread_ = std::thread::id();
2917  bool 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.
2921  std::map<std::string, std::string> addr_map_;
2922
2923  // Default headers
2924  Headers default_headers_;
2925
2926  // Header writer
2927  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
2928      detail::write_headers;
2929
2930  // Settings
2931  std::string client_cert_path_;
2932  std::string client_key_path_;
2933
2934  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
2935  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
2936  time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
2937  time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
2938  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
2939  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
2940  time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;
2941
2942  std::string basic_auth_username_;
2943  std::string basic_auth_password_;
2944  std::string bearer_token_auth_token_;
2945
2946  bool keep_alive_ = false;
2947  bool follow_location_ = false;
2948
2949  bool path_encode_ = true;
2950
2951  int address_family_ = AF_UNSPEC;
2952  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
2953  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
2954  SocketOptions socket_options_ = nullptr;
2955
2956  bool compress_ = false;
2957  bool decompress_ = true;
2958
2959  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
2960  bool has_payload_max_length_ = false;
2961
2962  std::string interface_;
2963
2964  std::string proxy_host_;
2965  int proxy_port_ = -1;
2966
2967  std::string proxy_basic_auth_username_;
2968  std::string proxy_basic_auth_password_;
2969  std::string proxy_bearer_token_auth_token_;
2970
2971  std::vector<detail::NoProxyEntry> no_proxy_entries_;
2972
2973  mutable detail::NormalizedTarget host_normalized_;
2974  mutable bool host_normalized_valid_ = false;
2975
2976  mutable std::mutex logger_mutex_;
2977  Logger logger_;
2978  ErrorLogger error_logger_;
2979
2980private:
2981  bool send_(Request &req, Response &res, Error &error);
2982  Result send_(Request &&req);
2983
2984  socket_t create_client_socket(Error &error) const;
2985  bool read_response_line(Stream &strm, const Request &req, Response &res,
2986                          bool skip_100_continue = true) const;
2987  bool write_request(Stream &strm, Request &req, bool close_connection,
2988                     Error &error, bool skip_body = false);
2989  bool write_request_body(Stream &strm, Request &req, Error &error);
2990  void prepare_default_headers(Request &r, bool for_stream,
2991                               const std::string &ct);
2992  bool redirect(Request &req, Response &res, Error &error);
2993  bool create_redirect_client(const std::string &scheme,
2994                              const std::string &host, int port, Request &req,
2995                              Response &res, const std::string &path,
2996                              const std::string &location, Error &error);
2997  template <typename ClientType> void setup_redirect_client(ClientType &client);
2998  bool handle_request(Stream &strm, Request &req, Response &res,
2999                      bool close_connection, Error &error);
3000  std::unique_ptr<Response> send_with_content_provider_and_receiver(
3001      Request &req, const char *body, size_t content_length,
3002      ContentProvider content_provider,
3003      ContentProviderWithoutLength content_provider_without_length,
3004      const std::string &content_type, ContentReceiver content_receiver,
3005      Error &error);
3006  Result send_with_content_provider_and_receiver(
3007      const std::string &method, const std::string &path,
3008      const Headers &headers, const char *body, size_t content_length,
3009      ContentProvider content_provider,
3010      ContentProviderWithoutLength content_provider_without_length,
3011      const std::string &content_type, ContentReceiver content_receiver,
3012      UploadProgress progress);
3013  ContentProviderWithoutLength get_multipart_content_provider(
3014      const std::string &boundary, const UploadFormDataItems &items,
3015      const FormDataProviderItems &provider_items) const;
3016
3017  virtual bool
3018  process_socket(const Socket &socket,
3019                 std::chrono::time_point<std::chrono::steady_clock> start_time,
3020                 std::function<bool(Stream &strm)> callback);
3021  virtual bool is_ssl() const;
3022
3023  void transfer_socket_ownership_to_handle(StreamHandle &handle);
3024
3025#ifdef CPPHTTPLIB_SSL_ENABLED
3026public:
3027  void set_digest_auth(const std::string &username,
3028                       const std::string &password);
3029  void set_proxy_digest_auth(const std::string &username,
3030                             const std::string &password);
3031  void set_ca_cert_path(const std::string &ca_cert_file_path,
3032                        const std::string &ca_cert_dir_path = std::string());
3033  void enable_server_certificate_verification(bool enabled);
3034  void enable_server_hostname_verification(bool enabled);
3035  void enable_system_ca(bool enabled);
3036
3037protected:
3038  std::string digest_auth_username_;
3039  std::string digest_auth_password_;
3040  std::string proxy_digest_auth_username_;
3041  std::string proxy_digest_auth_password_;
3042  std::string ca_cert_file_path_;
3043  std::string ca_cert_dir_path_;
3044  bool server_certificate_verification_ = true;
3045  bool server_hostname_verification_ = true;
3046  SystemCAMode system_ca_mode_ = SystemCAMode::Auto;
3047  std::string ca_cert_pem_; // Store CA cert PEM for redirect transfer
3048  int last_ssl_error_ = 0;
3049  uint64_t last_backend_error_ = 0;
3050#endif
3051};
3052
3053class Client {
3054public:
3055  // Universal interface
3056  explicit Client(const std::string &scheme_host_port);
3057
3058  explicit Client(const std::string &scheme_host_port,
3059                  const std::string &client_cert_path,
3060                  const std::string &client_key_path);
3061
3062  // HTTP only interface
3063  explicit Client(const std::string &host, int port);
3064
3065  explicit Client(const std::string &host, int port,
3066                  const std::string &client_cert_path,
3067                  const std::string &client_key_path);
3068
3069  Client(Client &&) = default;
3070  Client &operator=(Client &&) = default;
3071
3072  ~Client();
3073
3074  bool is_valid() const;
3075
3076  // clang-format off
3077  Result Get(const std::string &path, DownloadProgress progress = nullptr);
3078  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3079  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3080  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
3081  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3082  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3083  Result Get(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
3084  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
3085  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3086  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3087
3088  Result Head(const std::string &path);
3089  Result Head(const std::string &path, const Headers &headers);
3090
3091  Result Post(const std::string &path);
3092  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
3093  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
3094  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3095  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3096  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3097  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3098  Result Post(const std::string &path, const Params &params);
3099  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
3100  Result Post(const std::string &path, const Headers &headers);
3101  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
3102  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
3103  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3104  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3105  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3106  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3107  Result Post(const std::string &path, const Headers &headers, const Params &params);
3108  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
3109  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
3110  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
3111  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3112
3113  Result Put(const std::string &path);
3114  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
3115  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
3116  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3117  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3118  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3119  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3120  Result Put(const std::string &path, const Params &params);
3121  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
3122  Result Put(const std::string &path, const Headers &headers);
3123  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
3124  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
3125  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3126  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3127  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3128  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3129  Result Put(const std::string &path, const Headers &headers, const Params &params);
3130  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
3131  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
3132  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
3133  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3134
3135  Result Patch(const std::string &path);
3136  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
3137  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
3138  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3139  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3140  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3141  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3142  Result Patch(const std::string &path, const Params &params);
3143  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
3144  Result Patch(const std::string &path, const Headers &headers);
3145  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
3146  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
3147  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3148  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3149  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
3150  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
3151  Result Patch(const std::string &path, const Headers &headers, const Params &params);
3152  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
3153  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
3154  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
3155  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
3156
3157  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
3158  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
3159  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
3160  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
3161  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
3162  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
3163  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
3164  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
3165
3166  Result Options(const std::string &path);
3167  Result 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.)
3173  ClientImpl::StreamHandle open_stream(const std::string &method,
3174                                       const std::string &path,
3175                                       const Params &params = {},
3176                                       const Headers &headers = {},
3177                                       const std::string &body = {},
3178                                       const std::string &content_type = {});
3179
3180  bool send(Request &req, Response &res, Error &error);
3181  Result send(const Request &req);
3182
3183  void stop();
3184
3185  std::string host() const;
3186  int port() const;
3187
3188  size_t is_socket_open() const;
3189  socket_t socket() const;
3190
3191  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
3192
3193  void set_default_headers(Headers headers);
3194
3195  void
3196  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
3197
3198  void set_address_family(int family);
3199  void set_tcp_nodelay(bool on);
3200  void set_socket_options(SocketOptions socket_options);
3201
3202  void set_connection_timeout(time_t sec, time_t usec = 0);
3203  template <class Rep, class Period>
3204  void
3205  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
3206
3207  void set_read_timeout(time_t sec, time_t usec = 0);
3208  template <class Rep, class Period>
3209  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
3210
3211  void set_write_timeout(time_t sec, time_t usec = 0);
3212  template <class Rep, class Period>
3213  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
3214
3215  void set_max_timeout(time_t msec);
3216  template <class Rep, class Period>
3217  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
3218
3219  void set_basic_auth(const std::string &username, const std::string &password);
3220  void set_bearer_token_auth(const std::string &token);
3221
3222  void set_keep_alive(bool on);
3223  void set_follow_location(bool on);
3224
3225  void set_path_encode(bool on);
3226
3227  void set_compress(bool on);
3228
3229  void set_decompress(bool on);
3230
3231  void set_payload_max_length(size_t length);
3232
3233  void set_interface(const std::string &intf);
3234
3235  void set_proxy(const std::string &host, int port);
3236  void set_proxy_basic_auth(const std::string &username,
3237                            const std::string &password);
3238  void set_proxy_bearer_token_auth(const std::string &token);
3239  void set_no_proxy(const std::vector<std::string> &patterns);
3240  void set_logger(Logger logger);
3241  void set_error_logger(ErrorLogger error_logger);
3242
3243private:
3244  std::unique_ptr<ClientImpl> cli_;
3245
3246#ifdef CPPHTTPLIB_SSL_ENABLED
3247public:
3248  void set_digest_auth(const std::string &username,
3249                       const std::string &password);
3250  void set_proxy_digest_auth(const std::string &username,
3251                             const std::string &password);
3252  void enable_server_certificate_verification(bool enabled);
3253  void enable_server_hostname_verification(bool enabled);
3254  void enable_system_ca(bool enabled);
3255  void set_ca_cert_path(const std::string &ca_cert_file_path,
3256                        const std::string &ca_cert_dir_path = std::string());
3257
3258  void set_ca_cert_store(tls::ca_store_t ca_cert_store);
3259  void load_ca_cert_store(const char *ca_cert, std::size_t size);
3260
3261  void set_server_certificate_verifier(tls::VerifyCallback verifier);
3262
3263  void set_session_verifier(
3264      std::function<SSLVerifierResponse(tls::session_t)> verifier);
3265
3266  tls::ctx_t tls_context() const;
3267
3268#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
3269  void enable_windows_certificate_verification(bool enabled);
3270#endif
3271
3272private:
3273  bool is_ssl_ = false;
3274#endif
3275};
3276
3277#ifdef CPPHTTPLIB_SSL_ENABLED
3278class SSLServer : public Server {
3279public:
3280  SSLServer(const char *cert_path, const char *private_key_path,
3281            const char *client_ca_cert_file_path = nullptr,
3282            const char *client_ca_cert_dir_path = nullptr,
3283            const char *private_key_password = nullptr);
3284
3285  struct PemMemory {
3286    const char *cert_pem;
3287    size_t cert_pem_len;
3288    const char *key_pem;
3289    size_t key_pem_len;
3290    const char *client_ca_pem;
3291    size_t client_ca_pem_len;
3292    const char *private_key_password;
3293  };
3294  explicit 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)
3299  explicit SSLServer(const tls::ContextSetupCallback &setup_callback);
3300
3301  ~SSLServer() override;
3302
3303  bool is_valid() const override;
3304
3305  bool update_certs_pem(const char *cert_pem, const char *key_pem,
3306                        const char *client_ca_pem = nullptr,
3307                        const char *password = nullptr);
3308
3309  tls::ctx_t tls_context() const { return ctx_; }
3310
3311  int ssl_last_error() const { return last_ssl_error_; }
3312
3313private:
3314  bool process_and_close_socket(socket_t sock) override;
3315
3316  tls::ctx_t ctx_ = nullptr;
3317  std::mutex ctx_mutex_;
3318
3319  int last_ssl_error_ = 0;
3320};
3321
3322class SSLClient final : public ClientImpl {
3323public:
3324  explicit SSLClient(const std::string &host);
3325
3326  explicit SSLClient(const std::string &host, int port);
3327
3328  explicit SSLClient(const std::string &host, int port,
3329                     const std::string &client_cert_path,
3330                     const std::string &client_key_path,
3331                     const std::string &private_key_password = std::string());
3332
3333  struct PemMemory {
3334    const char *cert_pem;
3335    size_t cert_pem_len;
3336    const char *key_pem;
3337    size_t key_pem_len;
3338    const char *private_key_password;
3339  };
3340  explicit SSLClient(const std::string &host, int port, const PemMemory &pem);
3341
3342  ~SSLClient() override;
3343
3344  bool is_valid() const override;
3345
3346  void set_ca_cert_store(tls::ca_store_t ca_cert_store);
3347  void load_ca_cert_store(const char *ca_cert, std::size_t size);
3348
3349  void set_server_certificate_verifier(tls::VerifyCallback verifier);
3350
3351  // Post-handshake session verifier (backend-independent)
3352  void set_session_verifier(
3353      std::function<SSLVerifierResponse(tls::session_t)> verifier);
3354
3355  tls::ctx_t tls_context() const { return ctx_; }
3356
3357#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
3358  void enable_windows_certificate_verification(bool enabled);
3359#endif
3360
3361private:
3362  bool create_and_connect_socket(Socket &socket, Error &error) override;
3363  bool ensure_socket_connection(Socket &socket, Error &error) override;
3364  void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
3365  void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
3366
3367  bool
3368  process_socket(const Socket &socket,
3369                 std::chrono::time_point<std::chrono::steady_clock> start_time,
3370                 std::function<bool(Stream &strm)> callback) override;
3371  bool is_ssl() const override;
3372
3373  bool setup_proxy_connection(
3374      Socket &socket,
3375      std::chrono::time_point<std::chrono::steady_clock> start_time,
3376      Response &res, bool &success, Error &error) override;
3377  bool connect_with_proxy(
3378      Socket &sock,
3379      std::chrono::time_point<std::chrono::steady_clock> start_time,
3380      Response &res, bool &success, Error &error);
3381  bool initialize_ssl(Socket &socket, Error &error);
3382
3383  void init_ctx();
3384  void reset_ctx_on_error();
3385
3386  bool load_certs();
3387
3388  tls::ctx_t ctx_ = nullptr;
3389  std::mutex ctx_mutex_;
3390  std::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.
3395  bool ca_cert_store_set_ = false;
3396
3397  std::function<SSLVerifierResponse(tls::session_t)> session_verifier_;
3398
3399#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
3400  bool enable_windows_cert_verification_ = true;
3401#endif
3402
3403  friend 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) {
3411  auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
3412  auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
3413                  duration - std::chrono::seconds(sec))
3414                  .count();
3415  callback(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]) {
3419  return N - 1;
3420}
3421
3422inline bool is_numeric(const std::string &str) {
3423  return !str.empty() && std::all_of(str.cbegin(), str.cend(), is_ascii_digit);
3424}
3425
3426inline size_t get_header_value_u64(const Headers &headers,
3427                                   const std::string &key, size_t def,
3428                                   size_t id, bool &is_invalid_value) {
3429  is_invalid_value = false;
3430  auto rng = headers.equal_range(key);
3431  auto it = rng.first;
3432  std::advance(it, static_cast<ssize_t>(id));
3433  if (it != rng.second) {
3434    if (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.
3439      size_t val = 0;
3440      const auto &s = it->second;
3441      auto r = from_chars(s.data(), s.data() + s.size(), val);
3442      if (r.ec == std::errc::result_out_of_range) {
3443        is_invalid_value = true;
3444        return (std::numeric_limits<size_t>::max)();
3445      }
3446      return val;
3447    } else {
3448      is_invalid_value = true;
3449    }
3450  }
3451  return def;
3452}
3453
3454inline size_t get_header_value_u64(const Headers &headers,
3455                                   const std::string &key, size_t def,
3456                                   size_t id) {
3457  auto dummy = false;
3458  return 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) {
3466  detail::duration_to_sec_and_usec(
3467      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
3468  return *this;
3469}
3470
3471template <class Rep, class Period>
3472inline Server &
3473Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
3474  detail::duration_to_sec_and_usec(
3475      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
3476  return *this;
3477}
3478
3479template <class Rep, class Period>
3480inline Server &
3481Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
3482  detail::duration_to_sec_and_usec(
3483      duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
3484  return *this;
3485}
3486
3487template <class Rep, class Period>
3488inline void ClientImpl::set_connection_timeout(
3489    const std::chrono::duration<Rep, Period> &duration) {
3490  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
3491    set_connection_timeout(sec, usec);
3492  });
3493}
3494
3495template <class Rep, class Period>
3496inline void ClientImpl::set_read_timeout(
3497    const std::chrono::duration<Rep, Period> &duration) {
3498  detail::duration_to_sec_and_usec(
3499      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
3500}
3501
3502template <class Rep, class Period>
3503inline void ClientImpl::set_write_timeout(
3504    const std::chrono::duration<Rep, Period> &duration) {
3505  detail::duration_to_sec_and_usec(
3506      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
3507}
3508
3509template <class Rep, class Period>
3510inline void ClientImpl::set_max_timeout(
3511    const std::chrono::duration<Rep, Period> &duration) {
3512  auto msec =
3513      std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
3514  set_max_timeout(msec);
3515}
3516
3517template <class Rep, class Period>
3518inline void Client::set_connection_timeout(
3519    const std::chrono::duration<Rep, Period> &duration) {
3520  cli_->set_connection_timeout(duration);
3521}
3522
3523template <class Rep, class Period>
3524inline void
3525Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
3526  cli_->set_read_timeout(duration);
3527}
3528
3529template <class Rep, class Period>
3530inline void
3531Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
3532  cli_->set_write_timeout(duration);
3533}
3534
3535inline void Client::set_max_timeout(time_t msec) {
3536  cli_->set_max_timeout(msec);
3537}
3538
3539template <class Rep, class Period>
3540inline void
3541Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
3542  cli_->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,
3564                                   bool space_as_plus = true);
3565std::string decode_query_component(const std::string &component,
3566                                   bool 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,
3576                                 const std::string &password,
3577                                 bool is_proxy = false);
3578
3579namespace detail {
3580
3581#if defined(_WIN32)
3582inline std::wstring u8string_to_wstring(const char *s) {
3583  if (!s) { return std::wstring(); }
3584
3585  auto len = static_cast<int>(strlen(s));
3586  if (!len) { return std::wstring(); }
3587
3588  auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
3589  if (!wlen) { return std::wstring(); }
3590
3591  std::wstring ws;
3592  ws.resize(wlen);
3593  wlen = ::MultiByteToWideChar(
3594      CP_UTF8, 0, s, len,
3595      const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
3596  if (wlen != static_cast<int>(ws.size())) { ws.clear(); }
3597  return ws;
3598}
3599#endif
3600
3601struct FileStat {
3602  FileStat(const std::string &path);
3603  bool is_file() const;
3604  bool is_dir() const;
3605  time_t mtime() const;
3606  size_t size() const;
3607
3608private:
3609#if defined(_WIN32)
3610  struct _stat st_;
3611#else
3612  struct stat st_;
3613#endif
3614  int ret_ = -1;
3615};
3616
3617std::string make_host_and_port_string(const std::string &host, int port,
3618                                      bool is_ssl);
3619
3620template <typename T>
3621bool check_and_write_headers(Stream &strm, Headers &headers, T header_writer,
3622                             Error &error);
3623
3624std::string trim_copy(const std::string &s);
3625
3626void divide(
3627    const char *data, std::size_t size, char d,
3628    std::function<void(const char *, std::size_t, const char *, std::size_t)>
3629        fn);
3630
3631void divide(
3632    const std::string &str, char d,
3633    std::function<void(const char *, std::size_t, const char *, std::size_t)>
3634        fn);
3635
3636void split(const char *b, const char *e, char d,
3637           std::function<void(const char *, const char *)> fn);
3638
3639void split(const char *b, const char *e, char d, size_t m,
3640           std::function<void(const char *, const char *)> fn);
3641
3642bool split_find(const char *b, const char *e, char d,
3643                std::function<bool(const char *, const char *)> fn);
3644
3645bool has_header_token(const Headers &headers, const std::string &key,
3646                      const 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(
3653    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3654    time_t write_timeout_sec, time_t write_timeout_usec,
3655    time_t max_timeout_msec,
3656    std::chrono::time_point<std::chrono::steady_clock> start_time,
3657    std::function<bool(Stream &)> callback);
3658
3659socket_t create_client_socket(const std::string &host, const std::string &ip,
3660                              int port, int address_family, bool tcp_nodelay,
3661                              bool ipv6_v6only, SocketOptions socket_options,
3662                              time_t connection_timeout_sec,
3663                              time_t connection_timeout_usec,
3664                              time_t read_timeout_sec, time_t read_timeout_usec,
3665                              time_t write_timeout_sec,
3666                              time_t write_timeout_usec,
3667                              const std::string &intf, Error &error);
3668
3669const char *get_header_value(const Headers &headers, const std::string &key,
3670                             const char *def, size_t id);
3671
3672std::string get_combined_header_value(const Headers &headers,
3673                                      const 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,
3682                              std::string &boundary);
3683
3684bool parse_range_header(const std::string &s, Ranges &ranges);
3685
3686bool parse_accept_header(const std::string &s,
3687                         std::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,
3698                           const std::string &content_type);
3699
3700EncodingType encoding_type(const Request &req, const Response &res);
3701
3702class BufferStream final : public Stream {
3703public:
3704  BufferStream() = default;
3705  ~BufferStream() override = default;
3706
3707  bool is_readable() const override;
3708  bool wait_readable() const override;
3709  bool wait_writable() const override;
3710  ssize_t read(char *ptr, size_t size) override;
3711  ssize_t write(const char *ptr, size_t size) override;
3712  void get_remote_ip_and_port(std::string &ip, int &port) const override;
3713  void get_local_ip_and_port(std::string &ip, int &port) const override;
3714  socket_t socket() const override;
3715  time_t duration() const override;
3716
3717  const std::string &get_buffer() const;
3718
3719private:
3720  std::string buffer;
3721  size_t position = 0;
3722};
3723
3724class compressor {
3725public:
3726  virtual ~compressor() = default;
3727
3728  typedef std::function<bool(const char *data, size_t data_len)> Callback;
3729  virtual bool compress(const char *data, size_t data_length, bool last,
3730                        Callback callback) = 0;
3731};
3732
3733class decompressor {
3734public:
3735  virtual ~decompressor() = default;
3736
3737  virtual bool is_valid() const = 0;
3738
3739  typedef std::function<bool(const char *data, size_t data_len)> Callback;
3740  virtual bool decompress(const char *data, size_t data_length,
3741                          Callback callback) = 0;
3742};
3743
3744class nocompressor final : public compressor {
3745public:
3746  ~nocompressor() override = default;
3747
3748  bool compress(const char *data, size_t data_length, bool /*last*/,
3749                Callback callback) override;
3750};
3751
3752#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3753class gzip_compressor final : public compressor {
3754public:
3755  gzip_compressor();
3756  ~gzip_compressor() override;
3757
3758  bool compress(const char *data, size_t data_length, bool last,
3759                Callback callback) override;
3760
3761private:
3762  bool is_valid_ = false;
3763  z_stream strm_;
3764};
3765
3766class gzip_decompressor final : public decompressor {
3767public:
3768  gzip_decompressor();
3769  ~gzip_decompressor() override;
3770
3771  bool is_valid() const override;
3772
3773  bool decompress(const char *data, size_t data_length,
3774                  Callback callback) override;
3775
3776private:
3777  bool is_valid_ = false;
3778  z_stream strm_;
3779};
3780#endif
3781
3782#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3783class brotli_compressor final : public compressor {
3784public:
3785  brotli_compressor();
3786  ~brotli_compressor();
3787
3788  bool compress(const char *data, size_t data_length, bool last,
3789                Callback callback) override;
3790
3791private:
3792  BrotliEncoderState *state_ = nullptr;
3793};
3794
3795class brotli_decompressor final : public decompressor {
3796public:
3797  brotli_decompressor();
3798  ~brotli_decompressor();
3799
3800  bool is_valid() const override;
3801
3802  bool decompress(const char *data, size_t data_length,
3803                  Callback callback) override;
3804
3805private:
3806  BrotliDecoderResult decoder_r;
3807  BrotliDecoderState *decoder_s = nullptr;
3808};
3809#endif
3810
3811#ifdef CPPHTTPLIB_ZSTD_SUPPORT
3812class zstd_compressor : public compressor {
3813public:
3814  zstd_compressor();
3815  ~zstd_compressor();
3816
3817  bool compress(const char *data, size_t data_length, bool last,
3818                Callback callback) override;
3819
3820private:
3821  ZSTD_CCtx *ctx_ = nullptr;
3822};
3823
3824class zstd_decompressor : public decompressor {
3825public:
3826  zstd_decompressor();
3827  ~zstd_decompressor();
3828
3829  bool is_valid() const override;
3830
3831  bool decompress(const char *data, size_t data_length,
3832                  Callback callback) override;
3833
3834private:
3835  ZSTD_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:
3843  stream_line_reader(Stream &strm, char *fixed_buffer,
3844                     size_t fixed_buffer_size);
3845  const char *ptr() const;
3846  size_t size() const;
3847  bool end_with_crlf() const;
3848  bool getline();
3849
3850private:
3851  void append(char c);
3852  void append(const char *data, size_t size);
3853
3854  Stream &strm_;
3855  char *fixed_buffer_;
3856  const size_t fixed_buffer_size_;
3857  size_t fixed_buffer_used_size_ = 0;
3858  std::string growable_buffer_;
3859};
3860
3861bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
3862                    const Headers &src_headers);
3863
3864struct ChunkedDecoder {
3865  Stream &strm;
3866  size_t chunk_remaining = 0;
3867  bool finished = false;
3868  char line_buf[64];
3869  size_t last_chunk_total = 0;
3870  size_t last_chunk_offset = 0;
3871
3872  explicit ChunkedDecoder(Stream &s);
3873
3874  ssize_t read_payload(char *buf, size_t len, size_t &out_chunk_offset,
3875                       size_t &out_chunk_total);
3876
3877  bool parse_trailers_into(Headers &dest, const Headers &src_headers);
3878};
3879
3880class mmap {
3881public:
3882  mmap(const char *path);
3883  ~mmap();
3884
3885  bool open(const char *path);
3886  void close();
3887
3888  bool is_open() const;
3889  size_t size() const;
3890  const char *data() const;
3891
3892private:
3893#if defined(_WIN32)
3894  HANDLE hFile_ = NULL;
3895  HANDLE hMapping_ = NULL;
3896#else
3897  int fd_ = -1;
3898#endif
3899  size_t size_ = 0;
3900  void *addr_ = nullptr;
3901  bool 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 {
3934  mbedtls_ssl_config conf;
3935#ifndef CPPHTTPLIB_MBEDTLS_V4
3936  // Mbed TLS 4.x uses PSA Crypto's internal RNG; no explicit entropy/DRBG.
3937  mbedtls_entropy_context entropy;
3938  mbedtls_ctr_drbg_context ctr_drbg;
3939#endif
3940  mbedtls_x509_crt ca_chain;
3941  mbedtls_x509_crt own_cert;
3942  mbedtls_pk_context own_key;
3943  bool is_server = false;
3944  bool verify_client = false;
3945  bool has_verify_callback = false;
3946
3947  MbedTlsContext();
3948  ~MbedTlsContext();
3949
3950  MbedTlsContext(const MbedTlsContext &) = delete;
3951  MbedTlsContext &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 {
3966  WOLFSSL_CTX *ctx = nullptr;
3967  bool is_server = false;
3968  bool verify_client = false;
3969  bool has_verify_callback = false;
3970  std::string ca_pem_data_; // accumulated PEM for get_ca_names/get_ca_certs
3971
3972  WolfSSLContext();
3973  ~WolfSSLContext();
3974
3975  WolfSSLContext(const WolfSSLContext &) = delete;
3976  WolfSSLContext &operator=(const WolfSSLContext &) = delete;
3977};
3978
3979// CA store for wolfSSL: holds raw PEM bytes to allow reloading into any ctx
3980struct WolfSSLCAStore {
3981  std::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:
3994  Result();
3995  explicit Result(ClientImpl::StreamHandle &&handle, size_t chunk_size = 8192);
3996  Result(Result &&other) noexcept;
3997  Result &operator=(Result &&other) noexcept;
3998  Result(const Result &) = delete;
3999  Result &operator=(const Result &) = delete;
4000
4001  // Response info
4002  bool is_valid() const;
4003  explicit operator bool() const;
4004  int status() const;
4005  const Headers &headers() const;
4006  std::string get_header_value(const std::string &key,
4007                               const char *def = "") const;
4008  bool has_header(const std::string &key) const;
4009  Error error() const;
4010  Error read_error() const;
4011  bool has_read_error() const;
4012
4013  // Stream reading
4014  bool next();
4015  const char *data() const;
4016  size_t size() const;
4017  std::string read_all();
4018
4019private:
4020  ClientImpl::StreamHandle handle_;
4021  std::string buffer_;
4022  size_t current_size_ = 0;
4023  size_t chunk_size_;
4024  bool finished_ = false;
4025};
4026
4027// GET
4028template <typename ClientType>
4029inline Result Get(ClientType &cli, const std::string &path,
4030                  size_t chunk_size = 8192) {
4031  return Result{cli.open_stream("GET", path), chunk_size};
4032}
4033
4034template <typename ClientType>
4035inline Result Get(ClientType &cli, const std::string &path,
4036                  const Headers &headers, size_t chunk_size = 8192) {
4037  return Result{cli.open_stream("GET", path, {}, headers), chunk_size};
4038}
4039
4040template <typename ClientType>
4041inline Result Get(ClientType &cli, const std::string &path,
4042                  const Params &params, size_t chunk_size = 8192) {
4043  return Result{cli.open_stream("GET", path, params), chunk_size};
4044}
4045
4046template <typename ClientType>
4047inline Result Get(ClientType &cli, const std::string &path,
4048                  const Params &params, const Headers &headers,
4049                  size_t chunk_size = 8192) {
4050  return 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,
4056                   const std::string &body, const std::string &content_type,
4057                   size_t chunk_size = 8192) {
4058  return Result{cli.open_stream("POST", path, {}, {}, body, content_type),
4059                chunk_size};
4060}
4061
4062template <typename ClientType>
4063inline Result Post(ClientType &cli, const std::string &path,
4064                   const Headers &headers, const std::string &body,
4065                   const std::string &content_type, size_t chunk_size = 8192) {
4066  return Result{cli.open_stream("POST", path, {}, headers, body, content_type),
4067                chunk_size};
4068}
4069
4070template <typename ClientType>
4071inline Result Post(ClientType &cli, const std::string &path,
4072                   const Params &params, const std::string &body,
4073                   const std::string &content_type, size_t chunk_size = 8192) {
4074  return Result{cli.open_stream("POST", path, params, {}, body, content_type),
4075                chunk_size};
4076}
4077
4078template <typename ClientType>
4079inline Result Post(ClientType &cli, const std::string &path,
4080                   const Params &params, const Headers &headers,
4081                   const std::string &body, const std::string &content_type,
4082                   size_t chunk_size = 8192) {
4083  return Result{
4084      cli.open_stream("POST", path, params, headers, body, content_type),
4085      chunk_size};
4086}
4087
4088// PUT
4089template <typename ClientType>
4090inline Result Put(ClientType &cli, const std::string &path,
4091                  const std::string &body, const std::string &content_type,
4092                  size_t chunk_size = 8192) {
4093  return Result{cli.open_stream("PUT", path, {}, {}, body, content_type),
4094                chunk_size};
4095}
4096
4097template <typename ClientType>
4098inline Result Put(ClientType &cli, const std::string &path,
4099                  const Headers &headers, const std::string &body,
4100                  const std::string &content_type, size_t chunk_size = 8192) {
4101  return Result{cli.open_stream("PUT", path, {}, headers, body, content_type),
4102                chunk_size};
4103}
4104
4105template <typename ClientType>
4106inline Result Put(ClientType &cli, const std::string &path,
4107                  const Params &params, const std::string &body,
4108                  const std::string &content_type, size_t chunk_size = 8192) {
4109  return Result{cli.open_stream("PUT", path, params, {}, body, content_type),
4110                chunk_size};
4111}
4112
4113template <typename ClientType>
4114inline Result Put(ClientType &cli, const std::string &path,
4115                  const Params &params, const Headers &headers,
4116                  const std::string &body, const std::string &content_type,
4117                  size_t chunk_size = 8192) {
4118  return Result{
4119      cli.open_stream("PUT", path, params, headers, body, content_type),
4120      chunk_size};
4121}
4122
4123// PATCH
4124template <typename ClientType>
4125inline Result Patch(ClientType &cli, const std::string &path,
4126                    const std::string &body, const std::string &content_type,
4127                    size_t chunk_size = 8192) {
4128  return Result{cli.open_stream("PATCH", path, {}, {}, body, content_type),
4129                chunk_size};
4130}
4131
4132template <typename ClientType>
4133inline Result Patch(ClientType &cli, const std::string &path,
4134                    const Headers &headers, const std::string &body,
4135                    const std::string &content_type, size_t chunk_size = 8192) {
4136  return Result{cli.open_stream("PATCH", path, {}, headers, body, content_type),
4137                chunk_size};
4138}
4139
4140template <typename ClientType>
4141inline Result Patch(ClientType &cli, const std::string &path,
4142                    const Params &params, const std::string &body,
4143                    const std::string &content_type, size_t chunk_size = 8192) {
4144  return Result{cli.open_stream("PATCH", path, params, {}, body, content_type),
4145                chunk_size};
4146}
4147
4148template <typename ClientType>
4149inline Result Patch(ClientType &cli, const std::string &path,
4150                    const Params &params, const Headers &headers,
4151                    const std::string &body, const std::string &content_type,
4152                    size_t chunk_size = 8192) {
4153  return Result{
4154      cli.open_stream("PATCH", path, params, headers, body, content_type),
4155      chunk_size};
4156}
4157
4158// DELETE
4159template <typename ClientType>
4160inline Result Delete(ClientType &cli, const std::string &path,
4161                     size_t chunk_size = 8192) {
4162  return Result{cli.open_stream("DELETE", path), chunk_size};
4163}
4164
4165template <typename ClientType>
4166inline Result Delete(ClientType &cli, const std::string &path,
4167                     const Headers &headers, size_t chunk_size = 8192) {
4168  return Result{cli.open_stream("DELETE", path, {}, headers), chunk_size};
4169}
4170
4171template <typename ClientType>
4172inline Result Delete(ClientType &cli, const std::string &path,
4173                     const std::string &body, const std::string &content_type,
4174                     size_t chunk_size = 8192) {
4175  return Result{cli.open_stream("DELETE", path, {}, {}, body, content_type),
4176                chunk_size};
4177}
4178
4179template <typename ClientType>
4180inline Result Delete(ClientType &cli, const std::string &path,
4181                     const Headers &headers, const std::string &body,
4182                     const std::string &content_type,
4183                     size_t chunk_size = 8192) {
4184  return Result{
4185      cli.open_stream("DELETE", path, {}, headers, body, content_type),
4186      chunk_size};
4187}
4188
4189template <typename ClientType>
4190inline Result Delete(ClientType &cli, const std::string &path,
4191                     const Params &params, size_t chunk_size = 8192) {
4192  return Result{cli.open_stream("DELETE", path, params), chunk_size};
4193}
4194
4195template <typename ClientType>
4196inline Result Delete(ClientType &cli, const std::string &path,
4197                     const Params &params, const Headers &headers,
4198                     size_t chunk_size = 8192) {
4199  return Result{cli.open_stream("DELETE", path, params, headers), chunk_size};
4200}
4201
4202template <typename ClientType>
4203inline Result Delete(ClientType &cli, const std::string &path,
4204                     const Params &params, const std::string &body,
4205                     const std::string &content_type,
4206                     size_t chunk_size = 8192) {
4207  return Result{cli.open_stream("DELETE", path, params, {}, body, content_type),
4208                chunk_size};
4209}
4210
4211template <typename ClientType>
4212inline Result Delete(ClientType &cli, const std::string &path,
4213                     const Params &params, const Headers &headers,
4214                     const std::string &body, const std::string &content_type,
4215                     size_t chunk_size = 8192) {
4216  return Result{
4217      cli.open_stream("DELETE", path, params, headers, body, content_type),
4218      chunk_size};
4219}
4220
4221// HEAD
4222template <typename ClientType>
4223inline Result Head(ClientType &cli, const std::string &path,
4224                   size_t chunk_size = 8192) {
4225  return Result{cli.open_stream("HEAD", path), chunk_size};
4226}
4227
4228template <typename ClientType>
4229inline Result Head(ClientType &cli, const std::string &path,
4230                   const Headers &headers, size_t chunk_size = 8192) {
4231  return Result{cli.open_stream("HEAD", path, {}, headers), chunk_size};
4232}
4233
4234template <typename ClientType>
4235inline Result Head(ClientType &cli, const std::string &path,
4236                   const Params &params, size_t chunk_size = 8192) {
4237  return Result{cli.open_stream("HEAD", path, params), chunk_size};
4238}
4239
4240template <typename ClientType>
4241inline Result Head(ClientType &cli, const std::string &path,
4242                   const Params &params, const Headers &headers,
4243                   size_t chunk_size = 8192) {
4244  return 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,
4250                      size_t chunk_size = 8192) {
4251  return Result{cli.open_stream("OPTIONS", path), chunk_size};
4252}
4253
4254template <typename ClientType>
4255inline Result Options(ClientType &cli, const std::string &path,
4256                      const Headers &headers, size_t chunk_size = 8192) {
4257  return Result{cli.open_stream("OPTIONS", path, {}, headers), chunk_size};
4258}
4259
4260template <typename ClientType>
4261inline Result Options(ClientType &cli, const std::string &path,
4262                      const Params &params, size_t chunk_size = 8192) {
4263  return Result{cli.open_stream("OPTIONS", path, params), chunk_size};
4264}
4265
4266template <typename ClientType>
4267inline Result Options(ClientType &cli, const std::string &path,
4268                      const Params &params, const Headers &headers,
4269                      size_t chunk_size = 8192) {
4270  return Result{cli.open_stream("OPTIONS", path, params, headers), chunk_size};
4271}
4272
4273} // namespace stream
4274
4275namespace sse {
4276
4277struct SSEMessage {
4278  std::string event; // Event type (default: "message")
4279  std::string data;  // Event payload
4280  std::string id;    // Event ID for Last-Event-ID header
4281
4282  SSEMessage();
4283  void clear();
4284};
4285
4286class SSEClient {
4287public:
4288  using MessageHandler = std::function<void(const SSEMessage &)>;
4289  using ErrorHandler = std::function<void(Error)>;
4290  using OpenHandler = std::function<void()>;
4291
4292  SSEClient(Client &client, const std::string &path);
4293  SSEClient(Client &client, const std::string &path, const Headers &headers);
4294  ~SSEClient();
4295
4296  SSEClient(const SSEClient &) = delete;
4297  SSEClient &operator=(const SSEClient &) = delete;
4298
4299  // Event handlers
4300  SSEClient &on_message(MessageHandler handler);
4301  SSEClient &on_event(const std::string &type, MessageHandler handler);
4302  SSEClient &on_open(OpenHandler handler);
4303  SSEClient &on_error(ErrorHandler handler);
4304  SSEClient &set_reconnect_interval(int ms);
4305  SSEClient &set_max_reconnect_attempts(int n);
4306
4307  // Update headers (thread-safe)
4308  SSEClient &set_headers(const Headers &headers);
4309
4310  // State accessors
4311  bool is_connected() const;
4312  const std::string &last_event_id() const;
4313
4314  // Blocking start - runs event loop with auto-reconnect
4315  void start();
4316
4317  // Non-blocking start - runs in background thread
4318  void start_async();
4319
4320  // Stop the client (thread-safe)
4321  void stop();
4322
4323private:
4324  bool parse_sse_line(const std::string &line, SSEMessage &msg, int &retry_ms);
4325  void run_event_loop();
4326  void dispatch_event(const SSEMessage &msg);
4327  bool should_reconnect(int count) const;
4328  void wait_for_reconnect();
4329
4330  // Client and path
4331  Client &client_;
4332  std::string path_;
4333  Headers headers_;
4334  mutable std::mutex headers_mutex_;
4335
4336  // Callbacks
4337  MessageHandler on_message_;
4338  std::map<std::string, MessageHandler> event_handlers_;
4339  OpenHandler on_open_;
4340  ErrorHandler on_error_;
4341
4342  // Configuration
4343  int reconnect_interval_ms_ = 3000;
4344  int max_reconnect_attempts_ = 0; // 0 = unlimited
4345
4346  // State
4347  std::atomic<bool> running_{false};
4348  std::atomic<bool> connected_{false};
4349  std::string last_event_id_;
4350
4351  // Async support
4352  std::thread async_thread_;
4353};
4354
4355} // namespace sse
4356
4357namespace ws {
4358
4359enum class Opcode : uint8_t {
4360  Continuation = 0x0,
4361  Text = 0x1,
4362  Binary = 0x2,
4363  Close = 0x8,
4364  Ping = 0x9,
4365  Pong = 0xA,
4366};
4367
4368enum class CloseStatus : uint16_t {
4369  Normal = 1000,
4370  GoingAway = 1001,
4371  ProtocolError = 1002,
4372  UnsupportedData = 1003,
4373  NoStatus = 1005,
4374  Abnormal = 1006,
4375  InvalidPayload = 1007,
4376  PolicyViolation = 1008,
4377  MessageTooBig = 1009,
4378  MandatoryExtension = 1010,
4379  InternalError = 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:
4394  Result() = default;
4395  Result(Error err, int status, Headers &&headers)
4396      : err_(err), status_(status), headers_(std::move(headers)) {}
4397
4398  explicit operator bool() const { return err_ == Error::Success; }
4399  Error error() const { return err_; }
4400
4401  // Upgrade response info
4402  int status() const { return status_; }
4403  const Headers &headers() const { return headers_; }
4404  std::string get_header_value(const std::string &key,
4405                               const char *def = "") const {
4406    return detail::get_header_value(headers_, key, def, 0);
4407  }
4408  bool has_header(const std::string &key) const {
4409    return headers_.find(key) != headers_.end();
4410  }
4411
4412#ifdef CPPHTTPLIB_SSL_ENABLED
4413  Result(Error err, int status, Headers &&headers, int ssl_error,
4414         uint64_t ssl_backend_error)
4415      : err_(err), status_(status), headers_(std::move(headers)),
4416        ssl_error_(ssl_error), ssl_backend_error_(ssl_backend_error) {}
4417
4418  int ssl_error() const { return ssl_error_; }
4419  uint64_t ssl_backend_error() const { return ssl_backend_error_; }
4420#endif
4421
4422private:
4423  Error err_ = Error::Unknown; // a default-constructed Result is falsy
4424  int status_ = -1;
4425  Headers headers_;
4426#ifdef CPPHTTPLIB_SSL_ENABLED
4427  int ssl_error_ = 0;
4428  uint64_t ssl_backend_error_ = 0;
4429#endif
4430};
4431
4432class WebSocket {
4433public:
4434  WebSocket(const WebSocket &) = delete;
4435  WebSocket &operator=(const WebSocket &) = delete;
4436  ~WebSocket();
4437
4438  ReadResult read(std::string &msg);
4439  bool send(const std::string &data);
4440  bool send(const char *data, size_t len);
4441  void close(CloseStatus status = CloseStatus::Normal,
4442             const std::string &reason = "");
4443  const Request &request() const;
4444  bool 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.
4454  void set_read_timeout(time_t sec, time_t usec = 0);
4455  template <class Rep, class Period>
4456  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
4457
4458private:
4459  friend class httplib::Server;
4460  friend class WebSocketClient;
4461
4462  WebSocket(
4463      Stream &strm, const Request &req, bool is_server,
4464      time_t ping_interval_sec = CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND,
4465      int max_missed_pongs = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS)
4466      : strm_(strm), req_(req), is_server_(is_server),
4467        ping_interval_sec_(ping_interval_sec),
4468        max_missed_pongs_(max_missed_pongs) {
4469    start_heartbeat();
4470  }
4471
4472  WebSocket(
4473      std::unique_ptr<Stream> &&owned_strm, const Request &req, bool is_server,
4474      time_t ping_interval_sec = CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND,
4475      int max_missed_pongs = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS)
4476      : strm_(*owned_strm), owned_strm_(std::move(owned_strm)), req_(req),
4477        is_server_(is_server), ping_interval_sec_(ping_interval_sec),
4478        max_missed_pongs_(max_missed_pongs) {
4479    start_heartbeat();
4480  }
4481
4482  void start_heartbeat();
4483  bool send_frame(Opcode op, const char *data, size_t len, bool fin = true);
4484
4485  Stream &strm_;
4486  std::unique_ptr<Stream> owned_strm_;
4487  Request req_;
4488  bool is_server_;
4489  time_t ping_interval_sec_;
4490  int max_missed_pongs_;
4491  int unacked_pings_ = 0;
4492  std::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.
4496  std::atomic<bool> read_timeout_set_{false};
4497  std::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.
4502  std::mutex read_mutex_;
4503  std::thread ping_thread_;
4504  std::mutex ping_mutex_;
4505  std::condition_variable ping_cv_;
4506};
4507
4508class WebSocketClient {
4509public:
4510  explicit WebSocketClient(const std::string &scheme_host_port_path,
4511                           const Headers &headers = {});
4512
4513  ~WebSocketClient();
4514  WebSocketClient(const WebSocketClient &) = delete;
4515  WebSocketClient &operator=(const WebSocketClient &) = delete;
4516
4517  bool is_valid() const;
4518
4519  Result connect();
4520  ReadResult read(std::string &msg);
4521  bool send(const std::string &data);
4522  bool send(const char *data, size_t len);
4523  void close(CloseStatus status = CloseStatus::Normal,
4524             const std::string &reason = "");
4525  bool is_open() const;
4526  const std::string &subprotocol() const;
4527  void set_read_timeout(time_t sec, time_t usec = 0);
4528  template <class Rep, class Period>
4529  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
4530
4531  void set_write_timeout(time_t sec, time_t usec = 0);
4532  template <class Rep, class Period>
4533  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
4534
4535  void set_websocket_ping_interval(time_t sec);
4536  void set_websocket_max_missed_pongs(int count);
4537  void set_tcp_nodelay(bool on);
4538  void set_address_family(int family);
4539  void set_ipv6_v6only(bool on);
4540  void set_socket_options(SocketOptions socket_options);
4541
4542  void set_connection_timeout(time_t sec, time_t usec = 0);
4543  template <class Rep, class Period>
4544  void
4545  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
4546
4547  void set_interface(const std::string &intf);
4548  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
4549
4550#ifdef CPPHTTPLIB_SSL_ENABLED
4551  struct PemMemory {
4552    const char *cert_pem;
4553    size_t cert_pem_len;
4554    const char *key_pem;
4555    size_t key_pem_len;
4556    const char *private_key_password;
4557  };
4558  explicit WebSocketClient(const std::string &scheme_host_port_path,
4559                           const PemMemory &pem, const Headers &headers = {});
4560
4561  void set_ca_cert_path(const std::string &ca_cert_file_path,
4562                        const std::string &ca_cert_dir_path = std::string());
4563  void set_ca_cert_store(tls::ca_store_t store);
4564  void load_ca_cert_store(const char *ca_cert, std::size_t size);
4565  void enable_server_certificate_verification(bool enabled);
4566  void enable_server_hostname_verification(bool enabled);
4567  void enable_system_ca(bool enabled);
4568#endif
4569
4570private:
4571  void shutdown_and_close();
4572  bool create_stream(std::unique_ptr<Stream> &strm, Error &error,
4573                     int &ssl_error, uint64_t &ssl_backend_error);
4574  void prepare_default_headers(Request &req);
4575
4576  std::string host_;
4577  int port_;
4578  std::string path_;
4579  Headers headers_;
4580  std::string subprotocol_;
4581  bool is_valid_ = false;
4582  socket_t sock_ = INVALID_SOCKET;
4583  std::unique_ptr<WebSocket> ws_;
4584  time_t read_timeout_sec_ = CPPHTTPLIB_WEBSOCKET_CLIENT_READ_TIMEOUT_SECOND;
4585  time_t read_timeout_usec_ = 0;
4586  bool read_timeout_set_ = false; // see WebSocket::read_timeout_set_
4587  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
4588  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
4589  time_t websocket_ping_interval_sec_ =
4590      CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND;
4591  int websocket_max_missed_pongs_ = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS;
4592  int address_family_ = AF_UNSPEC;
4593  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
4594  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
4595  SocketOptions socket_options_ = nullptr;
4596  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
4597  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
4598  std::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.
4602  std::map<std::string, std::string> addr_map_;
4603
4604#ifdef CPPHTTPLIB_SSL_ENABLED
4605  bool is_ssl_ = false;
4606  tls::ctx_t tls_ctx_ = nullptr;
4607  tls::session_t tls_session_ = nullptr;
4608  std::string ca_cert_file_path_;
4609  std::string ca_cert_dir_path_;
4610  bool custom_ca_loaded_ = false;
4611  bool certs_loaded_ = false;
4612  SystemCAMode system_ca_mode_ = SystemCAMode::Auto;
4613  bool server_certificate_verification_ = true;
4614  bool server_hostname_verification_ = true;
4615#endif
4616};
4617
4618template <class Rep, class Period>
4619inline void WebSocket::set_read_timeout(
4620    const std::chrono::duration<Rep, Period> &duration) {
4621  detail::duration_to_sec_and_usec(
4622      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
4623}
4624
4625template <class Rep, class Period>
4626inline void WebSocketClient::set_read_timeout(
4627    const std::chrono::duration<Rep, Period> &duration) {
4628  detail::duration_to_sec_and_usec(
4629      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
4630}
4631
4632template <class Rep, class Period>
4633inline void WebSocketClient::set_write_timeout(
4634    const std::chrono::duration<Rep, Period> &duration) {
4635  detail::duration_to_sec_and_usec(
4636      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
4637}
4638
4639template <class Rep, class Period>
4640inline void WebSocketClient::set_connection_timeout(
4641    const std::chrono::duration<Rep, Period> &duration) {
4642  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
4643    set_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,
4657                               std::string &payload, bool &fin,
4658                               bool 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