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