8#ifndef CPPHTTPLIB_HTTPLIB_H
9#define CPPHTTPLIB_HTTPLIB_H
11#define CPPHTTPLIB_VERSION "0.48.0"
12#define CPPHTTPLIB_VERSION_NUM "0x003000"
15#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
17 "cpp-httplib doesn't support Windows 8 or lower. Please use Windows 10 or later."
25#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
26#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
29#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
30#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
33#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
34#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
37#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
38#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
41#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
42#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
45#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
46#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
49#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
50#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
53#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
54#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
57#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
58#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
61#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
62#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
65#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
66#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
69#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
70#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
73#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
74#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
77#ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
78#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0
81#ifndef CPPHTTPLIB_EXPECT_100_THRESHOLD
82#define CPPHTTPLIB_EXPECT_100_THRESHOLD 1024
85#ifndef CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND
86#define CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND 1000
89#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD
90#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD (1024 * 1024)
93#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND
94#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND 50
97#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
98#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
101#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
103#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
105#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
109#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
110#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
113#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
114#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
117#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
118#define CPPHTTPLIB_HEADER_MAX_COUNT 100
121#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
122#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
125#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
126#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
129#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
130#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (100 * 1024 * 1024)
133#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
134#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
137#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
138#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
141#ifndef CPPHTTPLIB_TCP_NODELAY
142#define CPPHTTPLIB_TCP_NODELAY false
145#ifndef CPPHTTPLIB_IPV6_V6ONLY
146#define CPPHTTPLIB_IPV6_V6ONLY false
149#ifndef CPPHTTPLIB_RECV_BUFSIZ
150#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
153#ifndef CPPHTTPLIB_SEND_BUFSIZ
154#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
157#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
158#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
161#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
162#define CPPHTTPLIB_THREAD_POOL_COUNT \
163 ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
164 ? std::thread::hardware_concurrency() - 1 \
168#ifndef CPPHTTPLIB_THREAD_POOL_MAX_COUNT
169#define CPPHTTPLIB_THREAD_POOL_MAX_COUNT (CPPHTTPLIB_THREAD_POOL_COUNT * 4)
172#ifndef CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT
173#define CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT 3
176#ifndef CPPHTTPLIB_RECV_FLAGS
177#define CPPHTTPLIB_RECV_FLAGS 0
180#ifndef CPPHTTPLIB_SEND_FLAGS
181#define CPPHTTPLIB_SEND_FLAGS 0
184#ifndef CPPHTTPLIB_LISTEN_BACKLOG
185#define CPPHTTPLIB_LISTEN_BACKLOG 5
188#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
189#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
192#ifndef CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH
193#define CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH 16777216
196#ifndef CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND
197#define CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND 300
200#ifndef CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND
201#define CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND 5
204#ifndef CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND
205#define CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND 30
208#ifndef CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS
209#define CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS 0
217#ifndef _CRT_SECURE_NO_WARNINGS
218#define _CRT_SECURE_NO_WARNINGS
221#ifndef _CRT_NONSTDC_NO_DEPRECATE
222#define _CRT_NONSTDC_NO_DEPRECATE
227#error Sorry, Visual Studio versions prior to 2015 are not supported
230#pragma comment(lib, "ws2_32.lib")
232#ifndef _SSIZE_T_DEFINED
233using ssize_t = __int64;
234#define _SSIZE_T_DEFINED
239#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
243#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
254#if defined(__has_include)
255#if __has_include(<afunix.h>)
258#define CPPHTTPLIB_HAVE_AFUNIX_H 1
262#ifndef WSA_FLAG_NO_HANDLE_INHERIT
263#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
266using nfds_t =
unsigned long;
268using socklen_t = int;
272#include <arpa/inet.h>
273#if !defined(_AIX) && !defined(__MVS__)
279#define NI_MAXHOST 1025
284#include <netinet/in.h>
290#include <netinet/tcp.h>
294#include <sys/socket.h>
299#ifndef INVALID_SOCKET
300#define INVALID_SOCKET (-1)
304#if defined(__APPLE__)
305#include <TargetConditionals.h>
315#include <condition_variable>
335#include <system_error>
337#include <unordered_map>
338#include <unordered_set>
345#if defined(__APPLE__) && defined(__clang__) && \
346 !defined(CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES) && \
347 (defined(CPPHTTPLIB_OPENSSL_SUPPORT) || \
348 defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || \
349 defined(CPPHTTPLIB_WOLFSSL_SUPPORT))
351#ifndef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
352#define CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
357#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && \
358 defined(__APPLE__) && !TARGET_OS_OSX
360 "CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN is only supported on macOS. On iOS/tvOS/watchOS, supply a CA bundle via set_ca_cert_path()."
365#if defined(_WIN32) && \
366 !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
367#define CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
370#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) || \
371 defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
372#if TARGET_OS_MAC && defined(__clang__)
373#include <CFNetwork/CFHost.h>
374#include <CoreFoundation/CoreFoundation.h>
378#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
386#undef X509_EXTENSIONS
387#undef PKCS7_SIGNER_INFO
390#pragma comment(lib, "crypt32.lib")
394#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
396#include <Security/Security.h>
400#include <openssl/err.h>
401#include <openssl/evp.h>
402#include <openssl/ssl.h>
403#include <openssl/x509v3.h>
405#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
406#include <openssl/applink.c>
412#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
413#if OPENSSL_VERSION_NUMBER < 0x1010107f
414#error Please use OpenSSL or a current version of BoringSSL
416#define SSL_get1_peer_certificate SSL_get_peer_certificate
417#elif OPENSSL_VERSION_NUMBER < 0x30000000L
418#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
423#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
424#include <mbedtls/ctr_drbg.h>
425#include <mbedtls/entropy.h>
426#include <mbedtls/error.h>
427#include <mbedtls/md5.h>
428#include <mbedtls/net_sockets.h>
429#include <mbedtls/oid.h>
430#include <mbedtls/pk.h>
431#include <mbedtls/sha1.h>
432#include <mbedtls/sha256.h>
433#include <mbedtls/sha512.h>
434#include <mbedtls/ssl.h>
435#include <mbedtls/x509_crt.h>
439#pragma comment(lib, "crypt32.lib")
442#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
444#include <Security/Security.h>
449#if MBEDTLS_VERSION_MAJOR >= 3
450#define CPPHTTPLIB_MBEDTLS_V3
455#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
456#include <wolfssl/options.h>
458#include <wolfssl/openssl/x509v3.h>
461#ifndef WOLFSSL_GEN_EMAIL
462#define WOLFSSL_GEN_EMAIL 1
464#ifndef WOLFSSL_GEN_DNS
465#define WOLFSSL_GEN_DNS 2
467#ifndef WOLFSSL_GEN_URI
468#define WOLFSSL_GEN_URI 6
470#ifndef WOLFSSL_GEN_IPADD
471#define WOLFSSL_GEN_IPADD 7
474#include <wolfssl/ssl.h>
475#include <wolfssl/wolfcrypt/hash.h>
476#include <wolfssl/wolfcrypt/md5.h>
477#include <wolfssl/wolfcrypt/sha256.h>
478#include <wolfssl/wolfcrypt/sha512.h>
482#pragma comment(lib, "crypt32.lib")
485#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
487#include <Security/Security.h>
493#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) || \
494 defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
495#define CPPHTTPLIB_SSL_ENABLED
498#ifdef CPPHTTPLIB_ZLIB_SUPPORT
502#ifdef CPPHTTPLIB_BROTLI_SUPPORT
503#include <brotli/decode.h>
504#include <brotli/encode.h>
507#ifdef CPPHTTPLIB_ZSTD_SUPPORT
530template <
class T,
class... Args>
531typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
532make_unique(Args &&...args) {
533 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
537typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
538make_unique(std::size_t n) {
539 typedef typename std::remove_extent<T>::type RT;
540 return std::unique_ptr<T>(
new RT[n]);
543namespace case_ignore {
545inline unsigned char to_lower(
int c) {
546 const static unsigned char table[256] = {
547 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
548 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
549 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
550 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
551 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
552 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
553 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
554 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
555 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
556 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
557 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
558 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
559 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
560 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
561 242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
562 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
563 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
566 return table[(
unsigned char)(
char)c];
569inline std::string to_lower(
const std::string &s) {
570 std::string result = s;
572 result.begin(), result.end(), result.begin(),
573 [](
unsigned char c) { return static_cast<char>(to_lower(c)); });
577inline bool equal(
const std::string &a,
const std::string &b) {
578 return a.size() == b.size() &&
579 std::equal(a.begin(), a.end(), b.begin(), [](
char ca,
char cb) {
580 return to_lower(ca) == to_lower(cb);
585 bool operator()(
const std::string &a,
const std::string &b)
const {
591 size_t operator()(
const std::string &key)
const {
592 return hash_core(key.data(), key.size(), 0);
595 size_t hash_core(
const char *s,
size_t l,
size_t h)
const {
597 : hash_core(s + 1, l - 1,
600 (((std::numeric_limits<size_t>::max)() >> 6) &
602 static_cast<unsigned char>(to_lower(*s)));
607using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
608 detail::case_ignore::equal_to>;
616 explicit scope_exit(std::function<
void(
void)> &&f)
617 : exit_function(std::move(f)), execute_on_destruction{
true} {}
619 scope_exit(scope_exit &&rhs) noexcept
620 : exit_function(std::move(rhs.exit_function)),
621 execute_on_destruction{rhs.execute_on_destruction} {
626 if (execute_on_destruction) { this->exit_function(); }
629 void release() { this->execute_on_destruction =
false; }
632 scope_exit(
const scope_exit &) =
delete;
633 void operator=(
const scope_exit &) =
delete;
634 scope_exit &operator=(scope_exit &&) =
delete;
636 std::function<void(
void)> exit_function;
637 bool execute_on_destruction;
642template <
typename T>
struct from_chars_result {
648inline from_chars_result<T> from_chars(
const char *first,
const char *last,
649 T &value,
int base = 10) {
651 const char *p = first;
652 bool negative =
false;
654 if (p != last && *p ==
'-') {
658 if (p == last) {
return {first, std::errc::invalid_argument}; }
661 for (; p != last; ++p) {
664 if (
'0' <= c && c <=
'9') {
666 }
else if (
'a' <= c && c <=
'z') {
667 digit = c -
'a' + 10;
668 }
else if (
'A' <= c && c <=
'Z') {
669 digit = c -
'A' + 10;
674 if (digit < 0 || digit >= base) {
break; }
675 if (result > ((std::numeric_limits<T>::max)() - digit) / base) {
676 return {p, std::errc::result_out_of_range};
678 result = result * base + digit;
681 if (p == first || (negative && p == first + 1)) {
682 return {first, std::errc::invalid_argument};
685 value = negative ? -result : result;
686 return {p, std::errc{}};
702inline from_chars_result<double> from_chars(
const char *first,
const char *last,
705 const char *p = first;
709 static const double powers_of_ten[] = {
710 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
711 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18};
712 const int max_frac_digits =
713 static_cast<int>(
sizeof(powers_of_ten) /
sizeof(powers_of_ten[0])) - 1;
723 uint64_t mantissa = 0;
725 bool seen_digit =
false;
727 const uint64_t limit = ((std::numeric_limits<uint64_t>::max)() - 9) / 10;
728 auto accumulate = [&](
char c) {
729 if (mantissa <= limit) {
730 mantissa = mantissa * 10 +
static_cast<uint64_t
>(c -
'0');
736 for (; p != last &&
'0' <= *p && *p <=
'9'; ++p) {
741 if (p != last && *p ==
'.') {
743 for (; p != last &&
'0' <= *p && *p <=
'9'; ++p) {
745 if (frac_digits < max_frac_digits && accumulate(*p)) { ++frac_digits; }
749 if (!seen_digit) {
return {first, std::errc::invalid_argument}; }
751 value =
static_cast<double>(mantissa) / powers_of_ten[frac_digits];
752 return {p, std::errc{}};
755inline bool parse_port(
const char *s,
size_t len,
int &port) {
757 auto r = from_chars(s, s + len, val);
758 if (r.ec != std::errc{} || val < 1 || val > 65535) {
return false; }
763inline bool parse_port(
const std::string &s,
int &port) {
764 return parse_port(s.data(), s.size(), port);
767struct UrlComponents {
775inline bool parse_url(
const std::string &url, UrlComponents &uc) {
779 auto sep = url.find(
"://");
780 if (sep != std::string::npos) {
781 uc.scheme = url.substr(0, sep);
784 if (uc.scheme.empty()) {
return false; }
785 for (
auto c : uc.scheme) {
786 if (c <
'a' || c >
'z') {
return false; }
790 }
else if (url.compare(0, 2,
"//") == 0) {
794 auto has_authority_prefix = pos > 0;
795 auto has_authority = has_authority_prefix || (!url.empty() && url[0] !=
'/' &&
796 url[0] !=
'?' && url[0] !=
'#');
798 if (pos < url.size() && url[pos] ==
'[') {
799 auto close = url.find(
']', pos);
800 if (close == std::string::npos) {
return false; }
801 uc.host = url.substr(pos + 1, close - pos - 1);
804 if (uc.host.empty()) {
return false; }
805 for (
auto c : uc.host) {
806 if (!((c >=
'a' && c <=
'f') || (c >=
'A' && c <=
'F') ||
807 (c >=
'0' && c <=
'9') || c ==
':')) {
814 auto end = url.find_first_of(
":/?#", pos);
815 if (end == std::string::npos) { end = url.size(); }
816 uc.host = url.substr(pos, end - pos);
820 if (pos < url.size() && url[pos] ==
':') {
822 auto end = url.find_first_of(
"/?#", pos);
823 if (end == std::string::npos) { end = url.size(); }
824 uc.port = url.substr(pos, end - pos);
831 if (!has_authority_prefix && pos < url.size()) {
838 if (pos < url.size() && url[pos] !=
'?' && url[pos] !=
'#') {
839 auto end = url.find_first_of(
"?#", pos);
840 if (end == std::string::npos) { end = url.size(); }
841 uc.path = url.substr(pos, end - pos);
845 if (pos < url.size() && url[pos] ==
'?') {
846 auto end = url.find(
'#', pos);
847 if (end == std::string::npos) { end = url.size(); }
848 uc.query = url.substr(pos, end - pos);
856enum class SSLVerifierResponse {
868enum class SystemCAMode { Auto, Enabled, Disabled };
873 SwitchingProtocol_101 = 101,
874 Processing_102 = 102,
875 EarlyHints_103 = 103,
881 NonAuthoritativeInformation_203 = 203,
883 ResetContent_205 = 205,
884 PartialContent_206 = 206,
885 MultiStatus_207 = 207,
886 AlreadyReported_208 = 208,
890 MultipleChoices_300 = 300,
891 MovedPermanently_301 = 301,
894 NotModified_304 = 304,
897 TemporaryRedirect_307 = 307,
898 PermanentRedirect_308 = 308,
901 BadRequest_400 = 400,
902 Unauthorized_401 = 401,
903 PaymentRequired_402 = 402,
906 MethodNotAllowed_405 = 405,
907 NotAcceptable_406 = 406,
908 ProxyAuthenticationRequired_407 = 407,
909 RequestTimeout_408 = 408,
912 LengthRequired_411 = 411,
913 PreconditionFailed_412 = 412,
914 PayloadTooLarge_413 = 413,
915 UriTooLong_414 = 414,
916 UnsupportedMediaType_415 = 415,
917 RangeNotSatisfiable_416 = 416,
918 ExpectationFailed_417 = 417,
920 MisdirectedRequest_421 = 421,
921 UnprocessableContent_422 = 422,
923 FailedDependency_424 = 424,
925 UpgradeRequired_426 = 426,
926 PreconditionRequired_428 = 428,
927 TooManyRequests_429 = 429,
928 RequestHeaderFieldsTooLarge_431 = 431,
929 UnavailableForLegalReasons_451 = 451,
932 InternalServerError_500 = 500,
933 NotImplemented_501 = 501,
934 BadGateway_502 = 502,
935 ServiceUnavailable_503 = 503,
936 GatewayTimeout_504 = 504,
937 HttpVersionNotSupported_505 = 505,
938 VariantAlsoNegotiates_506 = 506,
939 InsufficientStorage_507 = 507,
940 LoopDetected_508 = 508,
941 NotExtended_510 = 510,
942 NetworkAuthenticationRequired_511 = 511,
946 std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
947 detail::case_ignore::equal_to>;
949using Params = std::multimap<std::string, std::string>;
950using Match = std::smatch;
952using DownloadProgress = std::function<bool(
size_t current,
size_t total)>;
953using UploadProgress = std::function<bool(
size_t current,
size_t total)>;
962using any_type_id =
const void *;
964template <
typename T> any_type_id any_typeid()
noexcept {
965 static const char id = 0;
970 virtual ~any_storage() =
default;
971 virtual std::unique_ptr<any_storage> clone()
const = 0;
972 virtual any_type_id type_id()
const noexcept = 0;
975template <
typename T>
struct any_value final : any_storage {
977 template <
typename U>
explicit any_value(U &&v) : value(std::forward<U>(v)) {}
978 std::unique_ptr<any_storage> clone()
const override {
979 return std::unique_ptr<any_storage>(
new any_value<T>(value));
981 any_type_id type_id()
const noexcept override {
return any_typeid<T>(); }
988 UserData() =
default;
989 UserData(UserData &&)
noexcept =
default;
990 UserData &operator=(UserData &&)
noexcept =
default;
992 UserData(
const UserData &o) {
993 for (
const auto &e : o.entries_) {
994 if (e.second) { entries_[e.first] = e.second->clone(); }
998 UserData &operator=(
const UserData &o) {
1001 for (
const auto &e : o.entries_) {
1002 if (e.second) { entries_[e.first] = e.second->clone(); }
1008 template <
typename T>
void set(
const std::string &key, T &&value) {
1009 using D =
typename std::decay<T>::type;
1010 entries_[key].reset(
new detail::any_value<D>(std::forward<T>(value)));
1013 template <
typename T> T *get(
const std::string &key)
noexcept {
1014 auto it = entries_.find(key);
1015 if (it == entries_.end() || !it->second) {
return nullptr; }
1016 if (it->second->type_id() != detail::any_typeid<T>()) {
return nullptr; }
1017 return &
static_cast<detail::any_value<T> *
>(it->second.get())->value;
1020 template <
typename T>
const T *get(
const std::string &key)
const noexcept {
1021 auto it = entries_.find(key);
1022 if (it == entries_.end() || !it->second) {
return nullptr; }
1023 if (it->second->type_id() != detail::any_typeid<T>()) {
return nullptr; }
1024 return &
static_cast<const detail::any_value<T> *
>(it->second.get())->value;
1027 bool has(
const std::string &key)
const noexcept {
1028 return entries_.find(key) != entries_.end();
1031 void erase(
const std::string &key) { entries_.erase(key); }
1033 void clear()
noexcept { entries_.clear(); }
1036 std::unordered_map<std::string, std::unique_ptr<detail::any_storage>>
1041using ResponseHandler = std::function<bool(
const Response &response)>;
1045 std::string content;
1046 std::string filename;
1047 std::string content_type;
1053 std::string content;
1056using FormFields = std::multimap<std::string, FormField>;
1058using FormFiles = std::multimap<std::string, FormData>;
1060struct MultipartFormData {
1065 std::string get_field(
const std::string &key,
size_t id = 0)
const;
1066 std::vector<std::string> get_fields(
const std::string &key)
const;
1067 bool has_field(
const std::string &key)
const;
1068 size_t get_field_count(
const std::string &key)
const;
1071 FormData get_file(
const std::string &key,
size_t id = 0)
const;
1072 std::vector<FormData> get_files(
const std::string &key)
const;
1073 bool has_file(
const std::string &key)
const;
1074 size_t get_file_count(
const std::string &key)
const;
1077struct UploadFormData {
1079 std::string content;
1080 std::string filename;
1081 std::string content_type;
1083using UploadFormDataItems = std::vector<UploadFormData>;
1087 DataSink() : os(&sb_), sb_(*
this) {}
1089 DataSink(
const DataSink &) =
delete;
1090 DataSink &operator=(
const DataSink &) =
delete;
1091 DataSink(DataSink &&) =
delete;
1092 DataSink &operator=(DataSink &&) =
delete;
1094 std::function<bool(
const char *data,
size_t data_len)> write;
1095 std::function<bool()> is_writable;
1096 std::function<void()> done;
1097 std::function<void(
const Headers &trailer)> done_with_trailer;
1101 class data_sink_streambuf final :
public std::streambuf {
1103 explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
1106 std::streamsize xsputn(
const char *s, std::streamsize n)
override {
1107 if (sink_.write(s,
static_cast<size_t>(n))) {
return n; }
1115 data_sink_streambuf sb_;
1118using ContentProvider =
1119 std::function<bool(
size_t offset,
size_t length, DataSink &sink)>;
1121using ContentProviderWithoutLength =
1122 std::function<bool(
size_t offset, DataSink &sink)>;
1124using ContentProviderResourceReleaser = std::function<void(
bool success)>;
1126struct FormDataProvider {
1128 ContentProviderWithoutLength provider;
1129 std::string filename;
1130 std::string content_type;
1132using FormDataProviderItems = std::vector<FormDataProvider>;
1134inline FormDataProvider
1135make_file_provider(
const std::string &name,
const std::string &filepath,
1136 const std::string &filename = std::string(),
1137 const std::string &content_type = std::string()) {
1138 FormDataProvider fdp;
1140 fdp.filename = filename.empty() ? filepath : filename;
1141 fdp.content_type = content_type;
1142 fdp.provider = [filepath](
size_t offset, DataSink &sink) ->
bool {
1143 std::ifstream f(filepath, std::ios::binary);
1144 if (!f) {
return false; }
1146 f.seekg(
static_cast<std::streamoff
>(offset));
1153 f.read(buf,
sizeof(buf));
1154 auto n =
static_cast<size_t>(f.gcount());
1155 if (n > 0) {
return sink.write(buf, n); }
1162inline std::pair<size_t, ContentProvider>
1163make_file_body(
const std::string &filepath) {
1166 std::ifstream f(filepath, std::ios::binary | std::ios::ate);
1167 if (!f) {
return {0, ContentProvider{}}; }
1168 size =
static_cast<size_t>(f.tellg());
1171 ContentProvider provider = [filepath](
size_t offset,
size_t length,
1172 DataSink &sink) ->
bool {
1173 std::ifstream f(filepath, std::ios::binary);
1174 if (!f) {
return false; }
1175 f.seekg(
static_cast<std::streamoff
>(offset));
1176 if (!f.good()) {
return false; }
1178 while (length > 0) {
1179 auto to_read = (std::min)(
sizeof(buf), length);
1180 f.read(buf,
static_cast<std::streamsize
>(to_read));
1181 auto n =
static_cast<size_t>(f.gcount());
1182 if (n == 0) {
break; }
1183 if (!sink.write(buf, n)) {
return false; }
1188 return {size, std::move(provider)};
1191using ContentReceiverWithProgress = std::function<bool(
1192 const char *data,
size_t data_length,
size_t offset,
size_t total_length)>;
1194using ContentReceiver =
1195 std::function<bool(
const char *data,
size_t data_length)>;
1197using FormDataHeader = std::function<bool(
const FormData &file)>;
1199class ContentReader {
1201 using Reader = std::function<bool(ContentReceiver receiver)>;
1202 using FormDataReader =
1203 std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
1205 ContentReader(Reader reader, FormDataReader multipart_reader)
1206 : reader_(std::move(reader)),
1207 formdata_reader_(std::move(multipart_reader)) {}
1209 bool operator()(FormDataHeader header, ContentReceiver receiver)
const {
1210 return formdata_reader_(std::move(header), std::move(receiver));
1213 bool operator()(ContentReceiver receiver)
const {
1214 return reader_(std::move(receiver));
1218 FormDataReader formdata_reader_;
1221using Range = std::pair<ssize_t, ssize_t>;
1222using Ranges = std::vector<Range>;
1224#ifdef CPPHTTPLIB_SSL_ENABLED
1229using ctx_t =
void *;
1230using session_t =
void *;
1231using const_session_t =
const void *;
1232using cert_t =
void *;
1233using ca_store_t =
void *;
1242enum class SanType { DNS, IP, EMAIL, URI, OTHER };
1251struct VerifyContext {
1257 const char *error_string;
1260 std::string subject_cn()
const;
1261 std::string issuer_name()
const;
1262 bool check_hostname(
const char *hostname)
const;
1263 std::vector<SanEntry> sans()
const;
1264 bool validity(time_t ¬_before, time_t ¬_after)
const;
1265 std::string serial()
const;
1268using VerifyCallback = std::function<bool(
const VerifyContext &ctx)>;
1271enum class ErrorCode :
int {
1284 ErrorCode code = ErrorCode::Fatal;
1285 uint64_t backend_code = 0;
1289 static std::string verify_error_to_string(
long error_code);
1296 PeerCert(PeerCert &&other)
noexcept;
1297 PeerCert &operator=(PeerCert &&other)
noexcept;
1300 PeerCert(
const PeerCert &) =
delete;
1301 PeerCert &operator=(
const PeerCert &) =
delete;
1303 explicit operator bool()
const;
1304 std::string subject_cn()
const;
1305 std::string issuer_name()
const;
1306 bool check_hostname(
const char *hostname)
const;
1307 std::vector<SanEntry> sans()
const;
1308 bool validity(time_t ¬_before, time_t ¬_after)
const;
1309 std::string serial()
const;
1312 explicit PeerCert(cert_t cert);
1313 cert_t cert_ =
nullptr;
1314 friend PeerCert get_peer_cert_from_session(const_session_t session);
1318using ContextSetupCallback = std::function<bool(ctx_t ctx)>;
1326 std::string matched_route;
1332 std::string remote_addr;
1333 int remote_port = -1;
1334 std::string local_addr;
1335 int local_port = -1;
1338 std::string version;
1340 MultipartFormData form;
1343 std::unordered_map<std::string, std::string> path_params;
1344 std::function<bool()> is_connection_closed = []() {
return true; };
1347 std::vector<std::string> accept_content_types;
1348 ResponseHandler response_handler;
1349 ContentReceiverWithProgress content_receiver;
1350 DownloadProgress download_progress;
1351 UploadProgress upload_progress;
1353 bool has_header(
const std::string &key)
const;
1354 std::string get_header_value(
const std::string &key,
const char *def =
"",
1355 size_t id = 0)
const;
1356 size_t get_header_value_u64(
const std::string &key,
size_t def = 0,
1357 size_t id = 0)
const;
1358 size_t get_header_value_count(
const std::string &key)
const;
1359 void set_header(
const std::string &key,
const std::string &val);
1361 bool has_trailer(
const std::string &key)
const;
1362 std::string get_trailer_value(
const std::string &key,
size_t id = 0)
const;
1363 size_t get_trailer_value_count(
const std::string &key)
const;
1365 bool has_param(
const std::string &key)
const;
1366 std::string get_param_value(
const std::string &key,
size_t id = 0)
const;
1367 std::vector<std::string> get_param_values(
const std::string &key)
const;
1368 size_t get_param_value_count(
const std::string &key)
const;
1370 bool is_multipart_form_data()
const;
1373 bool body_consumed_ =
false;
1375 size_t content_length_ = 0;
1376 ContentProvider content_provider_;
1377 bool is_chunked_content_provider_ =
false;
1378 size_t authorization_count_ = 0;
1379 std::chrono::time_point<std::chrono::steady_clock> start_time_ =
1380 (std::chrono::steady_clock::time_point::min)();
1382#ifdef CPPHTTPLIB_SSL_ENABLED
1383 tls::const_session_t ssl =
nullptr;
1384 tls::PeerCert peer_cert()
const;
1385 std::string sni()
const;
1390 std::string version;
1396 std::string location;
1402 bool has_header(
const std::string &key)
const;
1403 std::string get_header_value(
const std::string &key,
const char *def =
"",
1404 size_t id = 0)
const;
1405 size_t get_header_value_u64(
const std::string &key,
size_t def = 0,
1406 size_t id = 0)
const;
1407 size_t get_header_value_count(
const std::string &key)
const;
1408 void set_header(
const std::string &key,
const std::string &val);
1410 bool has_trailer(
const std::string &key)
const;
1411 std::string get_trailer_value(
const std::string &key,
size_t id = 0)
const;
1412 size_t get_trailer_value_count(
const std::string &key)
const;
1414 void set_redirect(
const std::string &url,
int status = StatusCode::Found_302);
1415 void set_content(
const char *s,
size_t n,
const std::string &content_type);
1416 void set_content(
const std::string &s,
const std::string &content_type);
1417 void set_content(std::string &&s,
const std::string &content_type);
1419 void set_content_provider(
1420 size_t length,
const std::string &content_type, ContentProvider provider,
1421 ContentProviderResourceReleaser resource_releaser =
nullptr);
1423 void set_content_provider(
1424 const std::string &content_type, ContentProviderWithoutLength provider,
1425 ContentProviderResourceReleaser resource_releaser =
nullptr);
1427 void set_chunked_content_provider(
1428 const std::string &content_type, ContentProviderWithoutLength provider,
1429 ContentProviderResourceReleaser resource_releaser =
nullptr);
1431 void set_file_content(
const std::string &path,
1432 const std::string &content_type);
1433 void set_file_content(
const std::string &path);
1435 Response() =
default;
1436 Response(
const Response &) =
default;
1437 Response &operator=(
const Response &) =
default;
1438 Response(Response &&) =
default;
1439 Response &operator=(Response &&) =
default;
1441 if (content_provider_resource_releaser_) {
1442 content_provider_resource_releaser_(content_provider_success_);
1447 size_t content_length_ = 0;
1448 ContentProvider content_provider_;
1449 ContentProviderResourceReleaser content_provider_resource_releaser_;
1450 bool is_chunked_content_provider_ =
false;
1451 bool content_provider_success_ =
false;
1452 std::string file_content_path_;
1453 std::string file_content_content_type_;
1463 ExceedRedirectCount,
1467 SSLServerVerification,
1468 SSLServerHostnameVerification,
1469 UnsupportedMultipartBoundaryChars,
1476 TooManyFormDataFiles,
1477 ExceedMaxPayloadSize,
1479 ExceedMaxSocketDescriptorCount,
1488 UnsupportedAddressFamily,
1493 SSLPeerCouldBeClosed_,
1496std::string to_string(Error error);
1498std::ostream &operator<<(std::ostream &os,
const Error &obj);
1502 virtual ~Stream() =
default;
1504 virtual bool is_readable()
const = 0;
1505 virtual bool wait_readable()
const = 0;
1506 virtual bool wait_writable()
const = 0;
1507 virtual bool is_peer_alive()
const {
return wait_writable(); }
1509 virtual ssize_t read(
char *ptr,
size_t size) = 0;
1510 virtual ssize_t write(
const char *ptr,
size_t size) = 0;
1511 virtual void get_remote_ip_and_port(std::string &ip,
int &port)
const = 0;
1512 virtual void get_local_ip_and_port(std::string &ip,
int &port)
const = 0;
1513 virtual socket_t socket()
const = 0;
1515 virtual time_t duration()
const = 0;
1517 virtual void set_read_timeout(time_t sec, time_t usec = 0) {
1522 ssize_t write(
const char *ptr);
1523 ssize_t write(
const std::string &s);
1525 Error get_error()
const {
return error_; }
1528 Error error_ = Error::Success;
1533 TaskQueue() =
default;
1534 virtual ~TaskQueue() =
default;
1536 virtual bool enqueue(std::function<
void()> fn) = 0;
1537 virtual void shutdown() = 0;
1539 virtual void on_idle() {}
1542class ThreadPool final :
public TaskQueue {
1544 explicit ThreadPool(
size_t n,
size_t max_n = 0,
size_t mqr = 0);
1545 ThreadPool(
const ThreadPool &) =
delete;
1546 ~ThreadPool()
override =
default;
1548 bool enqueue(std::function<
void()> fn)
override;
1549 void shutdown()
override;
1552 void worker(
bool is_dynamic);
1553 void move_to_finished(std::thread::id
id);
1554 void cleanup_finished_threads();
1556 size_t base_thread_count_;
1557 size_t max_thread_count_;
1558 size_t max_queued_requests_;
1559 size_t idle_thread_count_;
1563 std::list<std::function<void()>> jobs_;
1564 std::vector<std::thread> threads_;
1565 std::list<std::thread> dynamic_threads_;
1566 std::vector<std::thread>
1569 std::condition_variable cond_;
1573using Logger = std::function<void(
const Request &,
const Response &)>;
1577using ErrorLogger = std::function<void(
const Error &,
const Request *)>;
1579using SocketOptions = std::function<void(
socket_t sock)>;
1581void default_socket_options(
socket_t sock);
1583bool set_socket_opt(
socket_t sock,
int level,
int optname,
int optval);
1585const char *status_message(
int status);
1587std::string to_string(Error error);
1589std::ostream &operator<<(std::ostream &os,
const Error &obj);
1591std::string get_bearer_token_auth(
const Request &req);
1597 MatcherBase(std::string pattern) : pattern_(std::move(pattern)) {}
1598 virtual ~MatcherBase() =
default;
1600 const std::string &pattern()
const {
return pattern_; }
1603 virtual bool match(Request &request)
const = 0;
1606 std::string pattern_;
1627class PathParamsMatcher final :
public MatcherBase {
1629 PathParamsMatcher(
const std::string &pattern);
1631 bool match(Request &request)
const override;
1637 static constexpr char separator =
'/';
1642 std::vector<std::string> static_fragments_;
1645 std::vector<std::string> param_names_;
1656class RegexMatcher final :
public MatcherBase {
1658 RegexMatcher(
const std::string &pattern)
1659 : MatcherBase(pattern), regex_(pattern) {}
1661 bool match(Request &request)
const override;
1667int close_socket(
socket_t sock)
noexcept;
1669ssize_t write_headers(Stream &strm,
const Headers &headers);
1671bool set_socket_opt_time(
socket_t sock,
int level,
int optname, time_t sec,
1674size_t get_multipart_content_length(
const UploadFormDataItems &items,
1675 const std::string &boundary);
1678make_multipart_content_provider(
const UploadFormDataItems &items,
1679 const std::string &boundary);
1685 using Handler = std::function<void(
const Request &, Response &)>;
1687 using ExceptionHandler =
1688 std::function<void(
const Request &, Response &, std::exception_ptr ep)>;
1690 enum class HandlerResponse {
1694 using HandlerWithResponse =
1695 std::function<HandlerResponse(
const Request &, Response &)>;
1697 using HandlerWithContentReader = std::function<void(
1698 const Request &, Response &,
const ContentReader &content_reader)>;
1700 using Expect100ContinueHandler =
1701 std::function<int(
const Request &, Response &)>;
1703 using StartHandler = std::function<void()>;
1705 using WebSocketHandler =
1706 std::function<void(
const Request &, ws::WebSocket &)>;
1707 using SubProtocolSelector =
1708 std::function<std::string(
const std::vector<std::string> &protocols)>;
1714 virtual bool is_valid()
const;
1716 Server &Get(
const std::string &pattern, Handler handler);
1717 Server &Post(
const std::string &pattern, Handler handler);
1718 Server &Post(
const std::string &pattern, HandlerWithContentReader handler);
1719 Server &Put(
const std::string &pattern, Handler handler);
1720 Server &Put(
const std::string &pattern, HandlerWithContentReader handler);
1721 Server &Patch(
const std::string &pattern, Handler handler);
1722 Server &Patch(
const std::string &pattern, HandlerWithContentReader handler);
1723 Server &Delete(
const std::string &pattern, Handler handler);
1724 Server &Delete(
const std::string &pattern, HandlerWithContentReader handler);
1725 Server &Options(
const std::string &pattern, Handler handler);
1727 Server &WebSocket(
const std::string &pattern, WebSocketHandler handler);
1728 Server &WebSocket(
const std::string &pattern, WebSocketHandler handler,
1729 SubProtocolSelector sub_protocol_selector);
1731 bool set_base_dir(
const std::string &dir,
1732 const std::string &mount_point = std::string());
1733 bool set_mount_point(
const std::string &mount_point,
const std::string &dir,
1734 Headers headers = Headers());
1735 bool remove_mount_point(
const std::string &mount_point);
1736 Server &set_file_extension_and_mimetype_mapping(
const std::string &ext,
1737 const std::string &mime);
1738 Server &set_default_file_mimetype(
const std::string &mime);
1739 Server &set_file_request_handler(Handler handler);
1741 template <
class ErrorHandlerFunc>
1742 Server &set_error_handler(ErrorHandlerFunc &&handler) {
1743 return set_error_handler_core(
1744 std::forward<ErrorHandlerFunc>(handler),
1745 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
1748 Server &set_exception_handler(ExceptionHandler handler);
1750 Server &set_pre_routing_handler(HandlerWithResponse handler);
1751 Server &set_post_routing_handler(Handler handler);
1753 Server &set_pre_request_handler(HandlerWithResponse handler);
1755 Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
1757 Server &set_start_handler(StartHandler handler);
1759 Server &set_logger(Logger logger);
1760 Server &set_pre_compression_logger(Logger logger);
1761 Server &set_error_logger(ErrorLogger error_logger);
1763 Server &set_address_family(
int family);
1764 Server &set_tcp_nodelay(
bool on);
1765 Server &set_ipv6_v6only(
bool on);
1766 Server &set_socket_options(SocketOptions socket_options);
1768 Server &set_default_headers(Headers headers);
1770 set_header_writer(std::function<ssize_t(Stream &, Headers &)>
const &writer);
1772 Server &set_trusted_proxies(
const std::vector<std::string> &proxies);
1774 Server &set_keep_alive_max_count(
size_t count);
1775 Server &set_keep_alive_timeout(time_t sec);
1776 template <
class Rep,
class Period>
1778 set_keep_alive_timeout(
const std::chrono::duration<Rep, Period> &duration);
1780 Server &set_read_timeout(time_t sec, time_t usec = 0);
1781 template <
class Rep,
class Period>
1782 Server &set_read_timeout(
const std::chrono::duration<Rep, Period> &duration);
1784 Server &set_write_timeout(time_t sec, time_t usec = 0);
1785 template <
class Rep,
class Period>
1786 Server &set_write_timeout(
const std::chrono::duration<Rep, Period> &duration);
1788 Server &set_idle_interval(time_t sec, time_t usec = 0);
1789 template <
class Rep,
class Period>
1790 Server &set_idle_interval(
const std::chrono::duration<Rep, Period> &duration);
1792 Server &set_payload_max_length(
size_t length);
1794 Server &set_websocket_ping_interval(time_t sec);
1795 template <
class Rep,
class Period>
1796 Server &set_websocket_ping_interval(
1797 const std::chrono::duration<Rep, Period> &duration);
1799 Server &set_websocket_max_missed_pongs(
int count);
1801 bool bind_to_port(
const std::string &host,
int port,
int socket_flags = 0);
1802 int bind_to_any_port(
const std::string &host,
int socket_flags = 0);
1803 bool listen_after_bind();
1805 bool listen(
const std::string &host,
int port,
int socket_flags = 0);
1807 bool is_running()
const;
1808 void wait_until_ready()
const;
1809 void stop()
noexcept;
1810 void decommission();
1812 std::function<TaskQueue *(void)> new_task_queue;
1815 bool process_request(Stream &strm,
const std::string &remote_addr,
1816 int remote_port,
const std::string &local_addr,
1817 int local_port,
bool close_connection,
1818 bool &connection_closed,
1819 const std::function<
void(Request &)> &setup_request,
1820 bool *websocket_upgraded =
nullptr);
1824 std::vector<std::string> trusted_proxies_;
1835 time_t websocket_ping_interval_sec_ =
1841 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
1842 using HandlersForContentReader =
1843 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1844 HandlerWithContentReader>>;
1846 static std::unique_ptr<detail::MatcherBase>
1847 make_matcher(
const std::string &pattern);
1849 template <
typename H>
1850 Server &add_handler(
1851 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, H>> &handlers,
1852 const std::string &pattern, H handler) {
1853 handlers.emplace_back(make_matcher(pattern), std::move(handler));
1857 Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1858 Server &set_error_handler_core(Handler handler, std::false_type);
1860 socket_t create_server_socket(
const std::string &host,
int port,
1862 SocketOptions socket_options)
const;
1863 int bind_internal(
const std::string &host,
int port,
int socket_flags);
1864 bool listen_internal();
1866 bool routing(Request &req, Response &res, Stream &strm);
1867 bool handle_file_request(Request &req, Response &res);
1868 bool check_if_not_modified(
const Request &req, Response &res,
1869 const std::string &etag, time_t mtime)
const;
1870 bool check_if_range(Request &req,
const std::string &etag,
1871 time_t mtime)
const;
1872 bool dispatch_request(Request &req, Response &res,
const Handlers &handlers,
1874 bool dispatch_request_for_content_reader(
1875 Request &req, Response &res, ContentReader content_reader,
1876 const HandlersForContentReader &handlers)
const;
1878 bool parse_request_line(
const char *s, Request &req)
const;
1879 void apply_ranges(
const Request &req, Response &res,
1880 std::string &content_type, std::string &boundary)
const;
1881 bool write_response(Stream &strm,
bool close_connection, Request &req,
1883 bool write_response_with_content(Stream &strm,
bool close_connection,
1884 const Request &req, Response &res);
1885 bool write_response_core(Stream &strm,
bool close_connection,
1886 const Request &req, Response &res,
1887 bool need_apply_ranges);
1888 bool write_content_with_provider(Stream &strm,
const Request &req,
1889 Response &res,
const std::string &boundary,
1890 const std::string &content_type);
1891 bool read_content(Stream &strm, Request &req, Response &res);
1892 bool read_content_with_content_receiver(Stream &strm, Request &req,
1894 ContentReceiver receiver,
1895 FormDataHeader multipart_header,
1896 ContentReceiver multipart_receiver);
1897 bool read_content_core(Stream &strm, Request &req, Response &res,
1898 ContentReceiver receiver,
1899 FormDataHeader multipart_header,
1900 ContentReceiver multipart_receiver)
const;
1902 virtual bool process_and_close_socket(
socket_t sock);
1904 void output_log(
const Request &req,
const Response &res)
const;
1905 void output_pre_compression_log(
const Request &req,
1906 const Response &res)
const;
1907 void output_error_log(
const Error &err,
const Request *req)
const;
1909 std::atomic<bool> is_running_{
false};
1910 std::atomic<bool> is_decommissioned{
false};
1912 struct MountPointEntry {
1913 std::string mount_point;
1914 std::string base_dir;
1915 std::string resolved_base_dir;
1918 std::vector<MountPointEntry> base_dirs_;
1919 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1920 std::string default_file_mimetype_ =
"application/octet-stream";
1921 Handler file_request_handler_;
1923 Handlers get_handlers_;
1924 Handlers post_handlers_;
1925 HandlersForContentReader post_handlers_for_content_reader_;
1926 Handlers put_handlers_;
1927 HandlersForContentReader put_handlers_for_content_reader_;
1928 Handlers patch_handlers_;
1929 HandlersForContentReader patch_handlers_for_content_reader_;
1930 Handlers delete_handlers_;
1931 HandlersForContentReader delete_handlers_for_content_reader_;
1932 Handlers options_handlers_;
1934 struct WebSocketHandlerEntry {
1935 std::unique_ptr<detail::MatcherBase> matcher;
1936 WebSocketHandler handler;
1937 SubProtocolSelector sub_protocol_selector;
1939 using WebSocketHandlers = std::vector<WebSocketHandlerEntry>;
1940 WebSocketHandlers websocket_handlers_;
1942 HandlerWithResponse error_handler_;
1943 ExceptionHandler exception_handler_;
1944 HandlerWithResponse pre_routing_handler_;
1945 Handler post_routing_handler_;
1946 HandlerWithResponse pre_request_handler_;
1947 Expect100ContinueHandler expect_100_continue_handler_;
1948 StartHandler start_handler_;
1950 mutable std::mutex logger_mutex_;
1952 Logger pre_compression_logger_;
1953 ErrorLogger error_logger_;
1955 int address_family_ = AF_UNSPEC;
1958 SocketOptions socket_options_ = default_socket_options;
1960 Headers default_headers_;
1961 std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1962 detail::write_headers;
1968 Result(std::unique_ptr<Response> &&res, Error err,
1969 Headers &&request_headers = Headers{})
1970 : res_(std::move(res)), err_(err),
1971 request_headers_(std::move(request_headers)) {}
1973 operator bool()
const {
return res_ !=
nullptr; }
1974 bool operator==(std::nullptr_t)
const {
return res_ ==
nullptr; }
1975 bool operator!=(std::nullptr_t)
const {
return res_ !=
nullptr; }
1976 const Response &value()
const {
return *res_; }
1977 Response &value() {
return *res_; }
1978 const Response &operator*()
const {
return *res_; }
1979 Response &operator*() {
return *res_; }
1980 const Response *operator->()
const {
return res_.get(); }
1981 Response *operator->() {
return res_.get(); }
1984 Error error()
const {
return err_; }
1987 bool has_request_header(
const std::string &key)
const;
1988 std::string get_request_header_value(
const std::string &key,
1989 const char *def =
"",
1990 size_t id = 0)
const;
1991 size_t get_request_header_value_u64(
const std::string &key,
size_t def = 0,
1992 size_t id = 0)
const;
1993 size_t get_request_header_value_count(
const std::string &key)
const;
1996 std::unique_ptr<Response> res_;
1997 Error err_ = Error::Unknown;
1998 Headers request_headers_;
2000#ifdef CPPHTTPLIB_SSL_ENABLED
2002 Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
2004 : res_(std::move(res)), err_(err),
2005 request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
2006 Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
2007 int ssl_error, uint64_t ssl_backend_error)
2008 : res_(std::move(res)), err_(err),
2009 request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
2010 ssl_backend_error_(ssl_backend_error) {}
2012 int ssl_error()
const {
return ssl_error_; }
2013 uint64_t ssl_backend_error()
const {
return ssl_backend_error_; }
2017 uint64_t ssl_backend_error_ = 0;
2021struct ClientConnection {
2026 ClientConnection() =
default;
2028 ~ClientConnection();
2030 ClientConnection(
const ClientConnection &) =
delete;
2031 ClientConnection &operator=(
const ClientConnection &) =
delete;
2033 ClientConnection(ClientConnection &&other) noexcept
2035#ifdef CPPHTTPLIB_SSL_ENABLED
2037 session(other.session)
2041#ifdef CPPHTTPLIB_SSL_ENABLED
2042 other.session =
nullptr;
2046 ClientConnection &operator=(ClientConnection &&other)
noexcept {
2047 if (
this != &other) {
2050#ifdef CPPHTTPLIB_SSL_ENABLED
2051 session = other.session;
2052 other.session =
nullptr;
2058#ifdef CPPHTTPLIB_SSL_ENABLED
2059 tls::session_t session =
nullptr;
2065struct ChunkedDecoder;
2068 Stream *stream =
nullptr;
2069 bool has_content_length =
false;
2070 size_t content_length = 0;
2072 size_t bytes_read = 0;
2073 bool chunked =
false;
2075 std::unique_ptr<ChunkedDecoder> chunked_decoder;
2076 Error last_error = Error::Success;
2078 ssize_t read(
char *buf,
size_t len);
2079 bool has_error()
const {
return last_error != Error::Success; }
2082inline ssize_t read_body_content(Stream *stream, BodyReader &br,
char *buf,
2085 return br.read(buf, len);
2090enum class NoProxyKind {
2099using IPBytes = std::array<uint8_t, 16>;
2101struct NoProxyEntry {
2102 NoProxyKind kind = NoProxyKind::Wildcard;
2103 std::string hostname_pattern;
2105 int prefix_bits = 0;
2108struct NormalizedTarget {
2109 std::string hostname;
2110 bool is_ipv4 =
false;
2111 bool is_ipv6 =
false;
2119 explicit ClientImpl(
const std::string &host);
2121 explicit ClientImpl(
const std::string &host,
int port);
2123 explicit ClientImpl(
const std::string &host,
int port,
2124 const std::string &client_cert_path,
2125 const std::string &client_key_path);
2127 virtual ~ClientImpl();
2129 virtual bool is_valid()
const;
2131 struct StreamHandle {
2132 std::unique_ptr<Response> response;
2133 Error error = Error::Success;
2135 StreamHandle() =
default;
2136 StreamHandle(
const StreamHandle &) =
delete;
2137 StreamHandle &operator=(
const StreamHandle &) =
delete;
2138 StreamHandle(StreamHandle &&) =
default;
2139 StreamHandle &operator=(StreamHandle &&) =
default;
2140 ~StreamHandle() =
default;
2142 bool is_valid()
const {
2143 return response !=
nullptr && error == Error::Success;
2146 ssize_t read(
char *buf,
size_t len);
2147 void parse_trailers_if_needed();
2148 Error get_read_error()
const {
return body_reader_.last_error; }
2149 bool has_read_error()
const {
return body_reader_.has_error(); }
2151 bool trailers_parsed_ =
false;
2154 friend class ClientImpl;
2156 ssize_t read_with_decompression(
char *buf,
size_t len);
2158 std::unique_ptr<ClientConnection> connection_;
2159 std::unique_ptr<Stream> socket_stream_;
2160 Stream *stream_ =
nullptr;
2161 detail::BodyReader body_reader_;
2163 std::unique_ptr<detail::decompressor> decompressor_;
2164 std::string decompress_buffer_;
2165 size_t decompress_offset_ = 0;
2166 size_t decompressed_bytes_read_ = 0;
2170 Result Get(
const std::string &path, DownloadProgress progress =
nullptr);
2171 Result Get(
const std::string &path, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2172 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2173 Result Get(
const std::string &path,
const Headers &headers, DownloadProgress progress =
nullptr);
2174 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2175 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2176 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, DownloadProgress progress =
nullptr);
2177 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2178 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2180 Result Head(
const std::string &path);
2181 Result Head(
const std::string &path,
const Headers &headers);
2183 Result Post(
const std::string &path);
2184 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2185 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2186 Result Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2187 Result Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2188 Result Post(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2189 Result Post(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2190 Result Post(
const std::string &path,
const Params ¶ms);
2191 Result Post(
const std::string &path,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2192 Result Post(
const std::string &path,
const Headers &headers);
2193 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2194 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2195 Result Post(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2196 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);
2197 Result Post(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2198 Result Post(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2199 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms);
2200 Result Post(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2201 Result Post(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const std::string &boundary, UploadProgress progress =
nullptr);
2202 Result Post(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const FormDataProviderItems &provider_items, UploadProgress progress =
nullptr);
2203 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2205 Result Put(
const std::string &path);
2206 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2207 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2208 Result Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2209 Result Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2210 Result Put(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2211 Result Put(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2212 Result Put(
const std::string &path,
const Params ¶ms);
2213 Result Put(
const std::string &path,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2214 Result Put(
const std::string &path,
const Headers &headers);
2215 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2216 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2217 Result Put(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2218 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);
2219 Result Put(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2220 Result Put(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2221 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms);
2222 Result Put(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2223 Result Put(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const std::string &boundary, UploadProgress progress =
nullptr);
2224 Result Put(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const FormDataProviderItems &provider_items, UploadProgress progress =
nullptr);
2225 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2227 Result Patch(
const std::string &path);
2228 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2229 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2230 Result Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2231 Result Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2232 Result Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2233 Result Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2234 Result Patch(
const std::string &path,
const Params ¶ms);
2235 Result Patch(
const std::string &path,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2236 Result Patch(
const std::string &path,
const Headers &headers, UploadProgress progress =
nullptr);
2237 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2238 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2239 Result Patch(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2240 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);
2241 Result Patch(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2242 Result Patch(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2243 Result Patch(
const std::string &path,
const Headers &headers,
const Params ¶ms);
2244 Result Patch(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2245 Result Patch(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const std::string &boundary, UploadProgress progress =
nullptr);
2246 Result Patch(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const FormDataProviderItems &provider_items, UploadProgress progress =
nullptr);
2247 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2249 Result Delete(
const std::string &path, DownloadProgress progress =
nullptr);
2250 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type, DownloadProgress progress =
nullptr);
2251 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type, DownloadProgress progress =
nullptr);
2252 Result Delete(
const std::string &path,
const Params ¶ms, DownloadProgress progress =
nullptr);
2253 Result Delete(
const std::string &path,
const Headers &headers, DownloadProgress progress =
nullptr);
2254 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type, DownloadProgress progress =
nullptr);
2255 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, DownloadProgress progress =
nullptr);
2256 Result Delete(
const std::string &path,
const Headers &headers,
const Params ¶ms, DownloadProgress progress =
nullptr);
2258 Result Options(
const std::string &path);
2259 Result Options(
const std::string &path,
const Headers &headers);
2265 StreamHandle open_stream(
const std::string &method,
const std::string &path,
2266 const Params ¶ms = {},
2267 const Headers &headers = {},
2268 const std::string &body = {},
2269 const std::string &content_type = {});
2271 bool send(Request &req, Response &res, Error &error);
2272 Result send(
const Request &req);
2276 std::string host()
const;
2279 size_t is_socket_open()
const;
2282 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
2284 void set_default_headers(Headers headers);
2287 set_header_writer(std::function<ssize_t(Stream &, Headers &)>
const &writer);
2289 void set_address_family(
int family);
2290 void set_tcp_nodelay(
bool on);
2291 void set_ipv6_v6only(
bool on);
2292 void set_socket_options(SocketOptions socket_options);
2294 void set_connection_timeout(time_t sec, time_t usec = 0);
2295 template <
class Rep,
class Period>
2297 set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration);
2299 void set_read_timeout(time_t sec, time_t usec = 0);
2300 template <
class Rep,
class Period>
2301 void set_read_timeout(
const std::chrono::duration<Rep, Period> &duration);
2303 void set_write_timeout(time_t sec, time_t usec = 0);
2304 template <
class Rep,
class Period>
2305 void set_write_timeout(
const std::chrono::duration<Rep, Period> &duration);
2307 void set_max_timeout(time_t msec);
2308 template <
class Rep,
class Period>
2309 void set_max_timeout(
const std::chrono::duration<Rep, Period> &duration);
2311 void set_basic_auth(
const std::string &username,
const std::string &password);
2312 void set_bearer_token_auth(
const std::string &token);
2314 void set_keep_alive(
bool on);
2315 void set_follow_location(
bool on);
2317 void set_path_encode(
bool on);
2319 void set_compress(
bool on);
2321 void set_decompress(
bool on);
2323 void set_payload_max_length(
size_t length);
2325 void set_interface(
const std::string &intf);
2327 void set_proxy(
const std::string &host,
int port);
2328 void set_proxy_basic_auth(
const std::string &username,
2329 const std::string &password);
2330 void set_proxy_bearer_token_auth(
const std::string &token);
2331 void set_no_proxy(
const std::vector<std::string> &patterns);
2333 void set_logger(Logger logger);
2334 void set_error_logger(ErrorLogger error_logger);
2341 std::chrono::time_point<std::chrono::steady_clock> start_time_;
2345#ifdef CPPHTTPLIB_SSL_ENABLED
2346 tls::session_t ssl =
nullptr;
2350 virtual bool create_and_connect_socket(Socket &socket, Error &error);
2351 virtual bool ensure_socket_connection(Socket &socket, Error &error);
2352 virtual bool setup_proxy_connection(
2354 std::chrono::time_point<std::chrono::steady_clock> start_time,
2355 Response &res,
bool &success, Error &error);
2357 bool is_proxy_enabled_for_host(
const std::string &host)
const;
2366 virtual void shutdown_ssl(Socket &socket,
bool shutdown_gracefully);
2367 void shutdown_socket(Socket &socket)
const;
2368 void close_socket(Socket &socket);
2369 void disconnect(
bool gracefully);
2371 bool process_request(Stream &strm, Request &req, Response &res,
2372 bool close_connection, Error &error);
2374 bool write_content_with_provider(Stream &strm,
const Request &req,
2375 Error &error)
const;
2377 void copy_settings(
const ClientImpl &rhs);
2379 void output_log(
const Request &req,
const Response &res)
const;
2380 void output_error_log(
const Error &err,
const Request *req)
const;
2383 const std::string host_;
2388 mutable std::mutex socket_mutex_;
2389 std::recursive_mutex request_mutex_;
2392 size_t socket_requests_in_flight_ = 0;
2393 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
2394 bool socket_should_be_closed_when_request_is_done_ =
false;
2397 std::map<std::string, std::string> addr_map_;
2400 Headers default_headers_;
2403 std::function<ssize_t(Stream &, Headers &)> header_writer_ =
2404 detail::write_headers;
2407 std::string client_cert_path_;
2408 std::string client_key_path_;
2418 std::string basic_auth_username_;
2419 std::string basic_auth_password_;
2420 std::string bearer_token_auth_token_;
2422 bool keep_alive_ =
false;
2423 bool follow_location_ =
false;
2425 bool path_encode_ =
true;
2427 int address_family_ = AF_UNSPEC;
2430 SocketOptions socket_options_ =
nullptr;
2432 bool compress_ =
false;
2433 bool decompress_ =
true;
2436 bool has_payload_max_length_ =
false;
2438 std::string interface_;
2440 std::string proxy_host_;
2441 int proxy_port_ = -1;
2443 std::string proxy_basic_auth_username_;
2444 std::string proxy_basic_auth_password_;
2445 std::string proxy_bearer_token_auth_token_;
2447 std::vector<detail::NoProxyEntry> no_proxy_entries_;
2449 mutable detail::NormalizedTarget host_normalized_;
2450 mutable bool host_normalized_valid_ =
false;
2452 mutable std::mutex logger_mutex_;
2454 ErrorLogger error_logger_;
2457 bool send_(Request &req, Response &res, Error &error);
2458 Result send_(Request &&req);
2460 socket_t create_client_socket(Error &error)
const;
2461 bool read_response_line(Stream &strm,
const Request &req, Response &res,
2462 bool skip_100_continue =
true)
const;
2463 bool write_request(Stream &strm, Request &req,
bool close_connection,
2464 Error &error,
bool skip_body =
false);
2465 bool write_request_body(Stream &strm, Request &req, Error &error);
2466 void prepare_default_headers(Request &r,
bool for_stream,
2467 const std::string &ct);
2468 bool redirect(Request &req, Response &res, Error &error);
2469 bool create_redirect_client(
const std::string &scheme,
2470 const std::string &host,
int port, Request &req,
2471 Response &res,
const std::string &path,
2472 const std::string &location, Error &error);
2473 template <
typename ClientType>
void setup_redirect_client(ClientType &client);
2474 bool handle_request(Stream &strm, Request &req, Response &res,
2475 bool close_connection, Error &error);
2476 std::unique_ptr<Response> send_with_content_provider_and_receiver(
2477 Request &req,
const char *body,
size_t content_length,
2478 ContentProvider content_provider,
2479 ContentProviderWithoutLength content_provider_without_length,
2480 const std::string &content_type, ContentReceiver content_receiver,
2482 Result send_with_content_provider_and_receiver(
2483 const std::string &method,
const std::string &path,
2484 const Headers &headers,
const char *body,
size_t content_length,
2485 ContentProvider content_provider,
2486 ContentProviderWithoutLength content_provider_without_length,
2487 const std::string &content_type, ContentReceiver content_receiver,
2488 UploadProgress progress);
2489 ContentProviderWithoutLength get_multipart_content_provider(
2490 const std::string &boundary,
const UploadFormDataItems &items,
2491 const FormDataProviderItems &provider_items)
const;
2494 process_socket(
const Socket &socket,
2495 std::chrono::time_point<std::chrono::steady_clock> start_time,
2496 std::function<
bool(Stream &strm)> callback);
2497 virtual bool is_ssl()
const;
2499 void transfer_socket_ownership_to_handle(StreamHandle &handle);
2501#ifdef CPPHTTPLIB_SSL_ENABLED
2503 void set_digest_auth(
const std::string &username,
2504 const std::string &password);
2505 void set_proxy_digest_auth(
const std::string &username,
2506 const std::string &password);
2507 void set_ca_cert_path(
const std::string &ca_cert_file_path,
2508 const std::string &ca_cert_dir_path = std::string());
2509 void enable_server_certificate_verification(
bool enabled);
2510 void enable_server_hostname_verification(
bool enabled);
2511 void enable_system_ca(
bool enabled);
2514 std::string digest_auth_username_;
2515 std::string digest_auth_password_;
2516 std::string proxy_digest_auth_username_;
2517 std::string proxy_digest_auth_password_;
2518 std::string ca_cert_file_path_;
2519 std::string ca_cert_dir_path_;
2520 bool server_certificate_verification_ =
true;
2521 bool server_hostname_verification_ =
true;
2522 SystemCAMode system_ca_mode_ = SystemCAMode::Auto;
2523 std::string ca_cert_pem_;
2524 int last_ssl_error_ = 0;
2525 uint64_t last_backend_error_ = 0;
2532 explicit Client(
const std::string &scheme_host_port);
2534 explicit Client(
const std::string &scheme_host_port,
2535 const std::string &client_cert_path,
2536 const std::string &client_key_path);
2539 explicit Client(
const std::string &host,
int port);
2541 explicit Client(
const std::string &host,
int port,
2542 const std::string &client_cert_path,
2543 const std::string &client_key_path);
2545 Client(Client &&) =
default;
2546 Client &operator=(Client &&) =
default;
2550 bool is_valid()
const;
2553 Result Get(
const std::string &path, DownloadProgress progress =
nullptr);
2554 Result Get(
const std::string &path, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2555 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2556 Result Get(
const std::string &path,
const Headers &headers, DownloadProgress progress =
nullptr);
2557 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2558 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2559 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, DownloadProgress progress =
nullptr);
2560 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2561 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2563 Result Head(
const std::string &path);
2564 Result Head(
const std::string &path,
const Headers &headers);
2566 Result Post(
const std::string &path);
2567 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2568 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2569 Result Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2570 Result Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2571 Result Post(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2572 Result Post(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2573 Result Post(
const std::string &path,
const Params ¶ms);
2574 Result Post(
const std::string &path,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2575 Result Post(
const std::string &path,
const Headers &headers);
2576 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2577 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2578 Result Post(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2579 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);
2580 Result Post(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2581 Result Post(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2582 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms);
2583 Result Post(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2584 Result Post(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const std::string &boundary, UploadProgress progress =
nullptr);
2585 Result Post(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const FormDataProviderItems &provider_items, UploadProgress progress =
nullptr);
2586 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2588 Result Put(
const std::string &path);
2589 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2590 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2591 Result Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2592 Result Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2593 Result Put(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2594 Result Put(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2595 Result Put(
const std::string &path,
const Params ¶ms);
2596 Result Put(
const std::string &path,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2597 Result Put(
const std::string &path,
const Headers &headers);
2598 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2599 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2600 Result Put(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2601 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);
2602 Result Put(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2603 Result Put(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2604 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms);
2605 Result Put(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2606 Result Put(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const std::string &boundary, UploadProgress progress =
nullptr);
2607 Result Put(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const FormDataProviderItems &provider_items, UploadProgress progress =
nullptr);
2608 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2610 Result Patch(
const std::string &path);
2611 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2612 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2613 Result Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2614 Result Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2615 Result Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2616 Result Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2617 Result Patch(
const std::string &path,
const Params ¶ms);
2618 Result Patch(
const std::string &path,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2619 Result Patch(
const std::string &path,
const Headers &headers);
2620 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type, UploadProgress progress =
nullptr);
2621 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, UploadProgress progress =
nullptr);
2622 Result Patch(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2623 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);
2624 Result Patch(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, UploadProgress progress =
nullptr);
2625 Result Patch(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress =
nullptr);
2626 Result Patch(
const std::string &path,
const Headers &headers,
const Params ¶ms);
2627 Result Patch(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items, UploadProgress progress =
nullptr);
2628 Result Patch(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const std::string &boundary, UploadProgress progress =
nullptr);
2629 Result Patch(
const std::string &path,
const Headers &headers,
const UploadFormDataItems &items,
const FormDataProviderItems &provider_items, UploadProgress progress =
nullptr);
2630 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress =
nullptr);
2632 Result Delete(
const std::string &path, DownloadProgress progress =
nullptr);
2633 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type, DownloadProgress progress =
nullptr);
2634 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type, DownloadProgress progress =
nullptr);
2635 Result Delete(
const std::string &path,
const Params ¶ms, DownloadProgress progress =
nullptr);
2636 Result Delete(
const std::string &path,
const Headers &headers, DownloadProgress progress =
nullptr);
2637 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type, DownloadProgress progress =
nullptr);
2638 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type, DownloadProgress progress =
nullptr);
2639 Result Delete(
const std::string &path,
const Headers &headers,
const Params ¶ms, DownloadProgress progress =
nullptr);
2641 Result Options(
const std::string &path);
2642 Result Options(
const std::string &path,
const Headers &headers);
2648 ClientImpl::StreamHandle open_stream(
const std::string &method,
2649 const std::string &path,
2650 const Params ¶ms = {},
2651 const Headers &headers = {},
2652 const std::string &body = {},
2653 const std::string &content_type = {});
2655 bool send(Request &req, Response &res, Error &error);
2656 Result send(
const Request &req);
2660 std::string host()
const;
2663 size_t is_socket_open()
const;
2666 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
2668 void set_default_headers(Headers headers);
2671 set_header_writer(std::function<ssize_t(Stream &, Headers &)>
const &writer);
2673 void set_address_family(
int family);
2674 void set_tcp_nodelay(
bool on);
2675 void set_socket_options(SocketOptions socket_options);
2677 void set_connection_timeout(time_t sec, time_t usec = 0);
2678 template <
class Rep,
class Period>
2680 set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration);
2682 void set_read_timeout(time_t sec, time_t usec = 0);
2683 template <
class Rep,
class Period>
2684 void set_read_timeout(
const std::chrono::duration<Rep, Period> &duration);
2686 void set_write_timeout(time_t sec, time_t usec = 0);
2687 template <
class Rep,
class Period>
2688 void set_write_timeout(
const std::chrono::duration<Rep, Period> &duration);
2690 void set_max_timeout(time_t msec);
2691 template <
class Rep,
class Period>
2692 void set_max_timeout(
const std::chrono::duration<Rep, Period> &duration);
2694 void set_basic_auth(
const std::string &username,
const std::string &password);
2695 void set_bearer_token_auth(
const std::string &token);
2697 void set_keep_alive(
bool on);
2698 void set_follow_location(
bool on);
2700 void set_path_encode(
bool on);
2702 void set_compress(
bool on);
2704 void set_decompress(
bool on);
2706 void set_payload_max_length(
size_t length);
2708 void set_interface(
const std::string &intf);
2710 void set_proxy(
const std::string &host,
int port);
2711 void set_proxy_basic_auth(
const std::string &username,
2712 const std::string &password);
2713 void set_proxy_bearer_token_auth(
const std::string &token);
2714 void set_no_proxy(
const std::vector<std::string> &patterns);
2715 void set_logger(Logger logger);
2716 void set_error_logger(ErrorLogger error_logger);
2719 std::unique_ptr<ClientImpl> cli_;
2721#ifdef CPPHTTPLIB_SSL_ENABLED
2723 void set_digest_auth(
const std::string &username,
2724 const std::string &password);
2725 void set_proxy_digest_auth(
const std::string &username,
2726 const std::string &password);
2727 void enable_server_certificate_verification(
bool enabled);
2728 void enable_server_hostname_verification(
bool enabled);
2729 void enable_system_ca(
bool enabled);
2730 void set_ca_cert_path(
const std::string &ca_cert_file_path,
2731 const std::string &ca_cert_dir_path = std::string());
2733 void set_ca_cert_store(tls::ca_store_t ca_cert_store);
2734 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
2736 void set_server_certificate_verifier(tls::VerifyCallback verifier);
2738 void set_session_verifier(
2739 std::function<SSLVerifierResponse(tls::session_t)> verifier);
2741 tls::ctx_t tls_context()
const;
2743#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
2744 void enable_windows_certificate_verification(
bool enabled);
2748 bool is_ssl_ =
false;
2752#ifdef CPPHTTPLIB_SSL_ENABLED
2753class SSLServer :
public Server {
2755 SSLServer(
const char *cert_path,
const char *private_key_path,
2756 const char *client_ca_cert_file_path =
nullptr,
2757 const char *client_ca_cert_dir_path =
nullptr,
2758 const char *private_key_password =
nullptr);
2761 const char *cert_pem;
2762 size_t cert_pem_len;
2763 const char *key_pem;
2765 const char *client_ca_pem;
2766 size_t client_ca_pem_len;
2767 const char *private_key_password;
2769 explicit SSLServer(
const PemMemory &pem);
2774 explicit SSLServer(
const tls::ContextSetupCallback &setup_callback);
2776 ~SSLServer()
override;
2778 bool is_valid()
const override;
2780 bool update_certs_pem(
const char *cert_pem,
const char *key_pem,
2781 const char *client_ca_pem =
nullptr,
2782 const char *password =
nullptr);
2784 tls::ctx_t tls_context()
const {
return ctx_; }
2786 int ssl_last_error()
const {
return last_ssl_error_; }
2789 bool process_and_close_socket(
socket_t sock)
override;
2791 tls::ctx_t ctx_ =
nullptr;
2792 std::mutex ctx_mutex_;
2794 int last_ssl_error_ = 0;
2797class SSLClient final :
public ClientImpl {
2799 explicit SSLClient(
const std::string &host);
2801 explicit SSLClient(
const std::string &host,
int port);
2803 explicit SSLClient(
const std::string &host,
int port,
2804 const std::string &client_cert_path,
2805 const std::string &client_key_path,
2806 const std::string &private_key_password = std::string());
2809 const char *cert_pem;
2810 size_t cert_pem_len;
2811 const char *key_pem;
2813 const char *private_key_password;
2815 explicit SSLClient(
const std::string &host,
int port,
const PemMemory &pem);
2817 ~SSLClient()
override;
2819 bool is_valid()
const override;
2821 void set_ca_cert_store(tls::ca_store_t ca_cert_store);
2822 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
2824 void set_server_certificate_verifier(tls::VerifyCallback verifier);
2827 void set_session_verifier(
2828 std::function<SSLVerifierResponse(tls::session_t)> verifier);
2830 tls::ctx_t tls_context()
const {
return ctx_; }
2832#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
2833 void enable_windows_certificate_verification(
bool enabled);
2837 bool create_and_connect_socket(Socket &socket, Error &error)
override;
2838 bool ensure_socket_connection(Socket &socket, Error &error)
override;
2839 void shutdown_ssl(Socket &socket,
bool shutdown_gracefully)
override;
2840 void shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully);
2843 process_socket(
const Socket &socket,
2844 std::chrono::time_point<std::chrono::steady_clock> start_time,
2845 std::function<
bool(Stream &strm)> callback)
override;
2846 bool is_ssl()
const override;
2848 bool setup_proxy_connection(
2850 std::chrono::time_point<std::chrono::steady_clock> start_time,
2851 Response &res,
bool &success, Error &error)
override;
2852 bool connect_with_proxy(
2854 std::chrono::time_point<std::chrono::steady_clock> start_time,
2855 Response &res,
bool &success, Error &error);
2856 bool initialize_ssl(Socket &socket, Error &error);
2859 void reset_ctx_on_error();
2863 tls::ctx_t ctx_ =
nullptr;
2864 std::mutex ctx_mutex_;
2865 std::once_flag initialize_cert_;
2870 bool ca_cert_store_set_ =
false;
2872 long verify_result_ = 0;
2874 std::function<SSLVerifierResponse(tls::session_t)> session_verifier_;
2876#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
2877 bool enable_windows_cert_verification_ =
true;
2880 friend class ClientImpl;
2886template <
typename T,
typename U>
2887inline void duration_to_sec_and_usec(
const T &duration, U callback) {
2888 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2889 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2890 duration - std::chrono::seconds(sec))
2892 callback(
static_cast<time_t
>(sec),
static_cast<time_t
>(usec));
2895template <
size_t N>
inline constexpr size_t str_len(
const char (&)[N]) {
2899inline bool is_numeric(
const std::string &str) {
2900 return !str.empty() &&
2901 std::all_of(str.cbegin(), str.cend(),
2902 [](
unsigned char c) { return std::isdigit(c); });
2905inline size_t get_header_value_u64(
const Headers &headers,
2906 const std::string &key,
size_t def,
2907 size_t id,
bool &is_invalid_value) {
2908 is_invalid_value =
false;
2909 auto rng = headers.equal_range(key);
2910 auto it = rng.first;
2911 std::advance(it,
static_cast<ssize_t
>(
id));
2912 if (it != rng.second) {
2913 if (is_numeric(it->second)) {
2914 return static_cast<size_t>(std::strtoull(it->second.data(),
nullptr, 10));
2916 is_invalid_value =
true;
2922inline size_t get_header_value_u64(
const Headers &headers,
2923 const std::string &key,
size_t def,
2926 return get_header_value_u64(headers, key, def,
id, dummy);
2931template <
class Rep,
class Period>
2933Server::set_read_timeout(
const std::chrono::duration<Rep, Period> &duration) {
2934 detail::duration_to_sec_and_usec(
2935 duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2939template <
class Rep,
class Period>
2941Server::set_write_timeout(
const std::chrono::duration<Rep, Period> &duration) {
2942 detail::duration_to_sec_and_usec(
2943 duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2947template <
class Rep,
class Period>
2949Server::set_idle_interval(
const std::chrono::duration<Rep, Period> &duration) {
2950 detail::duration_to_sec_and_usec(
2951 duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
2955template <
class Rep,
class Period>
2956inline void ClientImpl::set_connection_timeout(
2957 const std::chrono::duration<Rep, Period> &duration) {
2958 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
2959 set_connection_timeout(sec, usec);
2963template <
class Rep,
class Period>
2964inline void ClientImpl::set_read_timeout(
2965 const std::chrono::duration<Rep, Period> &duration) {
2966 detail::duration_to_sec_and_usec(
2967 duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2970template <
class Rep,
class Period>
2971inline void ClientImpl::set_write_timeout(
2972 const std::chrono::duration<Rep, Period> &duration) {
2973 detail::duration_to_sec_and_usec(
2974 duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2977template <
class Rep,
class Period>
2978inline void ClientImpl::set_max_timeout(
2979 const std::chrono::duration<Rep, Period> &duration) {
2981 std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2982 set_max_timeout(msec);
2985template <
class Rep,
class Period>
2986inline void Client::set_connection_timeout(
2987 const std::chrono::duration<Rep, Period> &duration) {
2988 cli_->set_connection_timeout(duration);
2991template <
class Rep,
class Period>
2993Client::set_read_timeout(
const std::chrono::duration<Rep, Period> &duration) {
2994 cli_->set_read_timeout(duration);
2997template <
class Rep,
class Period>
2999Client::set_write_timeout(
const std::chrono::duration<Rep, Period> &duration) {
3000 cli_->set_write_timeout(duration);
3003inline void Client::set_max_timeout(time_t msec) {
3004 cli_->set_max_timeout(msec);
3007template <
class Rep,
class Period>
3009Client::set_max_timeout(
const std::chrono::duration<Rep, Period> &duration) {
3010 cli_->set_max_timeout(duration);
3018std::string hosted_at(
const std::string &hostname);
3020void hosted_at(
const std::string &hostname, std::vector<std::string> &addrs);
3023std::string encode_uri_component(
const std::string &value);
3024std::string encode_uri(
const std::string &value);
3025std::string decode_uri_component(
const std::string &value);
3026std::string decode_uri(
const std::string &value);
3029std::string encode_path_component(
const std::string &component);
3030std::string decode_path_component(
const std::string &component);
3031std::string encode_query_component(
const std::string &component,
3032 bool space_as_plus =
true);
3033std::string decode_query_component(
const std::string &component,
3034 bool plus_as_space =
true);
3036std::string sanitize_filename(
const std::string &filename);
3038std::string append_query_params(
const std::string &path,
const Params ¶ms);
3040std::pair<std::string, std::string> make_range_header(
const Ranges &ranges);
3042std::pair<std::string, std::string>
3043make_basic_authentication_header(
const std::string &username,
3044 const std::string &password,
3045 bool is_proxy =
false);
3050inline std::wstring u8string_to_wstring(
const char *s) {
3051 if (!s) {
return std::wstring(); }
3053 auto len =
static_cast<int>(strlen(s));
3054 if (!len) {
return std::wstring(); }
3056 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len,
nullptr, 0);
3057 if (!wlen) {
return std::wstring(); }
3061 wlen = ::MultiByteToWideChar(
3063 const_cast<LPWSTR
>(
reinterpret_cast<LPCWSTR
>(ws.data())), wlen);
3064 if (wlen !=
static_cast<int>(ws.size())) { ws.clear(); }
3070 FileStat(
const std::string &path);
3071 bool is_file()
const;
3072 bool is_dir()
const;
3073 time_t mtime()
const;
3074 size_t size()
const;
3085std::string make_host_and_port_string(
const std::string &host,
int port,
3088std::string trim_copy(
const std::string &s);
3091 const char *data, std::size_t size,
char d,
3092 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
3096 const std::string &str,
char d,
3097 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
3100void split(
const char *b,
const char *e,
char d,
3101 std::function<
void(
const char *,
const char *)> fn);
3103void split(
const char *b,
const char *e,
char d,
size_t m,
3104 std::function<
void(
const char *,
const char *)> fn);
3106bool process_client_socket(
3107 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3108 time_t write_timeout_sec, time_t write_timeout_usec,
3109 time_t max_timeout_msec,
3110 std::chrono::time_point<std::chrono::steady_clock> start_time,
3111 std::function<
bool(Stream &)> callback);
3113socket_t create_client_socket(
const std::string &host,
const std::string &ip,
3114 int port,
int address_family,
bool tcp_nodelay,
3115 bool ipv6_v6only, SocketOptions socket_options,
3116 time_t connection_timeout_sec,
3117 time_t connection_timeout_usec,
3118 time_t read_timeout_sec, time_t read_timeout_usec,
3119 time_t write_timeout_sec,
3120 time_t write_timeout_usec,
3121 const std::string &intf, Error &error);
3123const char *get_header_value(
const Headers &headers,
const std::string &key,
3124 const char *def,
size_t id);
3126std::string params_to_query_str(
const Params ¶ms);
3128void parse_query_text(
const char *data, std::size_t size, Params ¶ms);
3130void parse_query_text(
const std::string &s, Params ¶ms);
3132bool parse_multipart_boundary(
const std::string &content_type,
3133 std::string &boundary);
3135bool parse_range_header(
const std::string &s, Ranges &ranges);
3137bool parse_accept_header(
const std::string &s,
3138 std::vector<std::string> &content_types);
3140ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
int flags);
3142ssize_t read_socket(
socket_t sock,
void *ptr,
size_t size,
int flags);
3144enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
3146EncodingType encoding_type(
const Request &req,
const Response &res);
3148class BufferStream final :
public Stream {
3150 BufferStream() =
default;
3151 ~BufferStream()
override =
default;
3153 bool is_readable()
const override;
3154 bool wait_readable()
const override;
3155 bool wait_writable()
const override;
3156 ssize_t read(
char *ptr,
size_t size)
override;
3157 ssize_t write(
const char *ptr,
size_t size)
override;
3158 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
3159 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
3161 time_t duration()
const override;
3163 const std::string &get_buffer()
const;
3167 size_t position = 0;
3172 virtual ~compressor() =
default;
3174 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
3175 virtual bool compress(
const char *data,
size_t data_length,
bool last,
3176 Callback callback) = 0;
3181 virtual ~decompressor() =
default;
3183 virtual bool is_valid()
const = 0;
3185 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
3186 virtual bool decompress(
const char *data,
size_t data_length,
3187 Callback callback) = 0;
3190class nocompressor final :
public compressor {
3192 ~nocompressor()
override =
default;
3194 bool compress(
const char *data,
size_t data_length,
bool ,
3195 Callback callback)
override;
3198#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3199class gzip_compressor final :
public compressor {
3202 ~gzip_compressor()
override;
3204 bool compress(
const char *data,
size_t data_length,
bool last,
3205 Callback callback)
override;
3208 bool is_valid_ =
false;
3212class gzip_decompressor final :
public decompressor {
3214 gzip_decompressor();
3215 ~gzip_decompressor()
override;
3217 bool is_valid()
const override;
3219 bool decompress(
const char *data,
size_t data_length,
3220 Callback callback)
override;
3223 bool is_valid_ =
false;
3228#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3229class brotli_compressor final :
public compressor {
3231 brotli_compressor();
3232 ~brotli_compressor();
3234 bool compress(
const char *data,
size_t data_length,
bool last,
3235 Callback callback)
override;
3238 BrotliEncoderState *state_ =
nullptr;
3241class brotli_decompressor final :
public decompressor {
3243 brotli_decompressor();
3244 ~brotli_decompressor();
3246 bool is_valid()
const override;
3248 bool decompress(
const char *data,
size_t data_length,
3249 Callback callback)
override;
3252 BrotliDecoderResult decoder_r;
3253 BrotliDecoderState *decoder_s =
nullptr;
3257#ifdef CPPHTTPLIB_ZSTD_SUPPORT
3258class zstd_compressor :
public compressor {
3263 bool compress(
const char *data,
size_t data_length,
bool last,
3264 Callback callback)
override;
3267 ZSTD_CCtx *ctx_ =
nullptr;
3270class zstd_decompressor :
public decompressor {
3272 zstd_decompressor();
3273 ~zstd_decompressor();
3275 bool is_valid()
const override;
3277 bool decompress(
const char *data,
size_t data_length,
3278 Callback callback)
override;
3281 ZSTD_DCtx *ctx_ =
nullptr;
3287class stream_line_reader {
3289 stream_line_reader(Stream &strm,
char *fixed_buffer,
3290 size_t fixed_buffer_size);
3291 const char *ptr()
const;
3292 size_t size()
const;
3293 bool end_with_crlf()
const;
3297 void append(
char c);
3300 char *fixed_buffer_;
3301 const size_t fixed_buffer_size_;
3302 size_t fixed_buffer_used_size_ = 0;
3303 std::string growable_buffer_;
3306bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
3307 const Headers &src_headers);
3309struct ChunkedDecoder {
3311 size_t chunk_remaining = 0;
3312 bool finished =
false;
3314 size_t last_chunk_total = 0;
3315 size_t last_chunk_offset = 0;
3317 explicit ChunkedDecoder(Stream &s);
3319 ssize_t read_payload(
char *buf,
size_t len,
size_t &out_chunk_offset,
3320 size_t &out_chunk_total);
3322 bool parse_trailers_into(Headers &dest,
const Headers &src_headers);
3327 mmap(
const char *path);
3330 bool open(
const char *path);
3333 bool is_open()
const;
3334 size_t size()
const;
3335 const char *data()
const;
3339 HANDLE hFile_ = NULL;
3340 HANDLE hMapping_ = NULL;
3345 void *addr_ =
nullptr;
3346 bool is_open_empty_file =
false;
3352bool is_token_char(
char c);
3353bool is_token(
const std::string &s);
3354bool is_field_name(
const std::string &s);
3355bool is_vchar(
char c);
3356bool is_obs_text(
char c);
3357bool is_field_vchar(
char c);
3358bool is_field_content(
const std::string &s);
3359bool is_field_value(
const std::string &s);
3368#ifdef CPPHTTPLIB_SSL_ENABLED
3370#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
3377struct MbedTlsContext {
3378 mbedtls_ssl_config conf;
3379 mbedtls_entropy_context entropy;
3380 mbedtls_ctr_drbg_context ctr_drbg;
3381 mbedtls_x509_crt ca_chain;
3382 mbedtls_x509_crt own_cert;
3383 mbedtls_pk_context own_key;
3384 bool is_server =
false;
3385 bool verify_client =
false;
3386 bool has_verify_callback =
false;
3391 MbedTlsContext(
const MbedTlsContext &) =
delete;
3392 MbedTlsContext &operator=(
const MbedTlsContext &) =
delete;
3399#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
3406struct WolfSSLContext {
3407 WOLFSSL_CTX *ctx =
nullptr;
3408 bool is_server =
false;
3409 bool verify_client =
false;
3410 bool has_verify_callback =
false;
3411 std::string ca_pem_data_;
3416 WolfSSLContext(
const WolfSSLContext &) =
delete;
3417 WolfSSLContext &operator=(
const WolfSSLContext &) =
delete;
3421struct WolfSSLCAStore {
3422 std::string pem_data;
3436 explicit Result(ClientImpl::StreamHandle &&handle,
size_t chunk_size = 8192);
3437 Result(Result &&other)
noexcept;
3438 Result &operator=(Result &&other)
noexcept;
3439 Result(
const Result &) =
delete;
3440 Result &operator=(
const Result &) =
delete;
3443 bool is_valid()
const;
3444 explicit operator bool()
const;
3446 const Headers &headers()
const;
3447 std::string get_header_value(
const std::string &key,
3448 const char *def =
"")
const;
3449 bool has_header(
const std::string &key)
const;
3450 Error error()
const;
3451 Error read_error()
const;
3452 bool has_read_error()
const;
3456 const char *data()
const;
3457 size_t size()
const;
3458 std::string read_all();
3461 ClientImpl::StreamHandle handle_;
3462 std::string buffer_;
3463 size_t current_size_ = 0;
3465 bool finished_ =
false;
3469template <
typename ClientType>
3470inline Result Get(ClientType &cli,
const std::string &path,
3471 size_t chunk_size = 8192) {
3472 return Result{cli.open_stream(
"GET", path), chunk_size};
3475template <
typename ClientType>
3476inline Result Get(ClientType &cli,
const std::string &path,
3477 const Headers &headers,
size_t chunk_size = 8192) {
3478 return Result{cli.open_stream(
"GET", path, {}, headers), chunk_size};
3481template <
typename ClientType>
3482inline Result Get(ClientType &cli,
const std::string &path,
3483 const Params ¶ms,
size_t chunk_size = 8192) {
3484 return Result{cli.open_stream(
"GET", path, params), chunk_size};
3487template <
typename ClientType>
3488inline Result Get(ClientType &cli,
const std::string &path,
3489 const Params ¶ms,
const Headers &headers,
3490 size_t chunk_size = 8192) {
3491 return Result{cli.open_stream(
"GET", path, params, headers), chunk_size};
3495template <
typename ClientType>
3496inline Result Post(ClientType &cli,
const std::string &path,
3497 const std::string &body,
const std::string &content_type,
3498 size_t chunk_size = 8192) {
3499 return Result{cli.open_stream(
"POST", path, {}, {}, body, content_type),
3503template <
typename ClientType>
3504inline Result Post(ClientType &cli,
const std::string &path,
3505 const Headers &headers,
const std::string &body,
3506 const std::string &content_type,
size_t chunk_size = 8192) {
3507 return Result{cli.open_stream(
"POST", path, {}, headers, body, content_type),
3511template <
typename ClientType>
3512inline Result Post(ClientType &cli,
const std::string &path,
3513 const Params ¶ms,
const std::string &body,
3514 const std::string &content_type,
size_t chunk_size = 8192) {
3515 return Result{cli.open_stream(
"POST", path, params, {}, body, content_type),
3519template <
typename ClientType>
3520inline Result Post(ClientType &cli,
const std::string &path,
3521 const Params ¶ms,
const Headers &headers,
3522 const std::string &body,
const std::string &content_type,
3523 size_t chunk_size = 8192) {
3525 cli.open_stream(
"POST", path, params, headers, body, content_type),
3530template <
typename ClientType>
3531inline Result Put(ClientType &cli,
const std::string &path,
3532 const std::string &body,
const std::string &content_type,
3533 size_t chunk_size = 8192) {
3534 return Result{cli.open_stream(
"PUT", path, {}, {}, body, content_type),
3538template <
typename ClientType>
3539inline Result Put(ClientType &cli,
const std::string &path,
3540 const Headers &headers,
const std::string &body,
3541 const std::string &content_type,
size_t chunk_size = 8192) {
3542 return Result{cli.open_stream(
"PUT", path, {}, headers, body, content_type),
3546template <
typename ClientType>
3547inline Result Put(ClientType &cli,
const std::string &path,
3548 const Params ¶ms,
const std::string &body,
3549 const std::string &content_type,
size_t chunk_size = 8192) {
3550 return Result{cli.open_stream(
"PUT", path, params, {}, body, content_type),
3554template <
typename ClientType>
3555inline Result Put(ClientType &cli,
const std::string &path,
3556 const Params ¶ms,
const Headers &headers,
3557 const std::string &body,
const std::string &content_type,
3558 size_t chunk_size = 8192) {
3560 cli.open_stream(
"PUT", path, params, headers, body, content_type),
3565template <
typename ClientType>
3566inline Result Patch(ClientType &cli,
const std::string &path,
3567 const std::string &body,
const std::string &content_type,
3568 size_t chunk_size = 8192) {
3569 return Result{cli.open_stream(
"PATCH", path, {}, {}, body, content_type),
3573template <
typename ClientType>
3574inline Result Patch(ClientType &cli,
const std::string &path,
3575 const Headers &headers,
const std::string &body,
3576 const std::string &content_type,
size_t chunk_size = 8192) {
3577 return Result{cli.open_stream(
"PATCH", path, {}, headers, body, content_type),
3581template <
typename ClientType>
3582inline Result Patch(ClientType &cli,
const std::string &path,
3583 const Params ¶ms,
const std::string &body,
3584 const std::string &content_type,
size_t chunk_size = 8192) {
3585 return Result{cli.open_stream(
"PATCH", path, params, {}, body, content_type),
3589template <
typename ClientType>
3590inline Result Patch(ClientType &cli,
const std::string &path,
3591 const Params ¶ms,
const Headers &headers,
3592 const std::string &body,
const std::string &content_type,
3593 size_t chunk_size = 8192) {
3595 cli.open_stream(
"PATCH", path, params, headers, body, content_type),
3600template <
typename ClientType>
3601inline Result Delete(ClientType &cli,
const std::string &path,
3602 size_t chunk_size = 8192) {
3603 return Result{cli.open_stream(
"DELETE", path), chunk_size};
3606template <
typename ClientType>
3607inline Result Delete(ClientType &cli,
const std::string &path,
3608 const Headers &headers,
size_t chunk_size = 8192) {
3609 return Result{cli.open_stream(
"DELETE", path, {}, headers), chunk_size};
3612template <
typename ClientType>
3613inline Result Delete(ClientType &cli,
const std::string &path,
3614 const std::string &body,
const std::string &content_type,
3615 size_t chunk_size = 8192) {
3616 return Result{cli.open_stream(
"DELETE", path, {}, {}, body, content_type),
3620template <
typename ClientType>
3621inline Result Delete(ClientType &cli,
const std::string &path,
3622 const Headers &headers,
const std::string &body,
3623 const std::string &content_type,
3624 size_t chunk_size = 8192) {
3626 cli.open_stream(
"DELETE", path, {}, headers, body, content_type),
3630template <
typename ClientType>
3631inline Result Delete(ClientType &cli,
const std::string &path,
3632 const Params ¶ms,
size_t chunk_size = 8192) {
3633 return Result{cli.open_stream(
"DELETE", path, params), chunk_size};
3636template <
typename ClientType>
3637inline Result Delete(ClientType &cli,
const std::string &path,
3638 const Params ¶ms,
const Headers &headers,
3639 size_t chunk_size = 8192) {
3640 return Result{cli.open_stream(
"DELETE", path, params, headers), chunk_size};
3643template <
typename ClientType>
3644inline Result Delete(ClientType &cli,
const std::string &path,
3645 const Params ¶ms,
const std::string &body,
3646 const std::string &content_type,
3647 size_t chunk_size = 8192) {
3648 return Result{cli.open_stream(
"DELETE", path, params, {}, body, content_type),
3652template <
typename ClientType>
3653inline Result Delete(ClientType &cli,
const std::string &path,
3654 const Params ¶ms,
const Headers &headers,
3655 const std::string &body,
const std::string &content_type,
3656 size_t chunk_size = 8192) {
3658 cli.open_stream(
"DELETE", path, params, headers, body, content_type),
3663template <
typename ClientType>
3664inline Result Head(ClientType &cli,
const std::string &path,
3665 size_t chunk_size = 8192) {
3666 return Result{cli.open_stream(
"HEAD", path), chunk_size};
3669template <
typename ClientType>
3670inline Result Head(ClientType &cli,
const std::string &path,
3671 const Headers &headers,
size_t chunk_size = 8192) {
3672 return Result{cli.open_stream(
"HEAD", path, {}, headers), chunk_size};
3675template <
typename ClientType>
3676inline Result Head(ClientType &cli,
const std::string &path,
3677 const Params ¶ms,
size_t chunk_size = 8192) {
3678 return Result{cli.open_stream(
"HEAD", path, params), chunk_size};
3681template <
typename ClientType>
3682inline Result Head(ClientType &cli,
const std::string &path,
3683 const Params ¶ms,
const Headers &headers,
3684 size_t chunk_size = 8192) {
3685 return Result{cli.open_stream(
"HEAD", path, params, headers), chunk_size};
3689template <
typename ClientType>
3690inline Result Options(ClientType &cli,
const std::string &path,
3691 size_t chunk_size = 8192) {
3692 return Result{cli.open_stream(
"OPTIONS", path), chunk_size};
3695template <
typename ClientType>
3696inline Result Options(ClientType &cli,
const std::string &path,
3697 const Headers &headers,
size_t chunk_size = 8192) {
3698 return Result{cli.open_stream(
"OPTIONS", path, {}, headers), chunk_size};
3701template <
typename ClientType>
3702inline Result Options(ClientType &cli,
const std::string &path,
3703 const Params ¶ms,
size_t chunk_size = 8192) {
3704 return Result{cli.open_stream(
"OPTIONS", path, params), chunk_size};
3707template <
typename ClientType>
3708inline Result Options(ClientType &cli,
const std::string &path,
3709 const Params ¶ms,
const Headers &headers,
3710 size_t chunk_size = 8192) {
3711 return Result{cli.open_stream(
"OPTIONS", path, params, headers), chunk_size};
3729 using MessageHandler = std::function<void(
const SSEMessage &)>;
3730 using ErrorHandler = std::function<void(Error)>;
3731 using OpenHandler = std::function<void()>;
3733 SSEClient(Client &client,
const std::string &path);
3734 SSEClient(Client &client,
const std::string &path,
const Headers &headers);
3737 SSEClient(
const SSEClient &) =
delete;
3738 SSEClient &operator=(
const SSEClient &) =
delete;
3741 SSEClient &on_message(MessageHandler handler);
3742 SSEClient &on_event(
const std::string &type, MessageHandler handler);
3743 SSEClient &on_open(OpenHandler handler);
3744 SSEClient &on_error(ErrorHandler handler);
3745 SSEClient &set_reconnect_interval(
int ms);
3746 SSEClient &set_max_reconnect_attempts(
int n);
3749 SSEClient &set_headers(
const Headers &headers);
3752 bool is_connected()
const;
3753 const std::string &last_event_id()
const;
3765 bool parse_sse_line(
const std::string &line, SSEMessage &msg,
int &retry_ms);
3766 void run_event_loop();
3767 void dispatch_event(
const SSEMessage &msg);
3768 bool should_reconnect(
int count)
const;
3769 void wait_for_reconnect();
3775 mutable std::mutex headers_mutex_;
3778 MessageHandler on_message_;
3779 std::map<std::string, MessageHandler> event_handlers_;
3780 OpenHandler on_open_;
3781 ErrorHandler on_error_;
3784 int reconnect_interval_ms_ = 3000;
3785 int max_reconnect_attempts_ = 0;
3788 std::atomic<bool> running_{
false};
3789 std::atomic<bool> connected_{
false};
3790 std::string last_event_id_;
3793 std::thread async_thread_;
3800enum class Opcode : uint8_t {
3809enum class CloseStatus : uint16_t {
3812 ProtocolError = 1002,
3813 UnsupportedData = 1003,
3816 InvalidPayload = 1007,
3817 PolicyViolation = 1008,
3818 MessageTooBig = 1009,
3819 MandatoryExtension = 1010,
3820 InternalError = 1011,
3823enum ReadResult :
int { Fail = 0, Text = 1, Binary = 2 };
3827 WebSocket(
const WebSocket &) =
delete;
3828 WebSocket &operator=(
const WebSocket &) =
delete;
3831 ReadResult read(std::string &msg);
3832 bool send(
const std::string &data);
3833 bool send(
const char *data,
size_t len);
3834 void close(CloseStatus status = CloseStatus::Normal,
3835 const std::string &reason =
"");
3836 const Request &request()
const;
3837 bool is_open()
const;
3840 friend class httplib::Server;
3841 friend class WebSocketClient;
3844 Stream &strm,
const Request &req,
bool is_server,
3847 : strm_(strm), req_(req), is_server_(is_server),
3848 ping_interval_sec_(ping_interval_sec),
3849 max_missed_pongs_(max_missed_pongs) {
3854 std::unique_ptr<Stream> &&owned_strm,
const Request &req,
bool is_server,
3857 : strm_(*owned_strm), owned_strm_(std::move(owned_strm)), req_(req),
3858 is_server_(is_server), ping_interval_sec_(ping_interval_sec),
3859 max_missed_pongs_(max_missed_pongs) {
3863 void start_heartbeat();
3864 bool send_frame(Opcode op,
const char *data,
size_t len,
bool fin =
true);
3867 std::unique_ptr<Stream> owned_strm_;
3870 time_t ping_interval_sec_;
3871 int max_missed_pongs_;
3872 int unacked_pings_ = 0;
3873 std::atomic<bool> closed_{
false};
3874 std::mutex write_mutex_;
3875 std::thread ping_thread_;
3876 std::mutex ping_mutex_;
3877 std::condition_variable ping_cv_;
3880class WebSocketClient {
3882 explicit WebSocketClient(
const std::string &scheme_host_port_path,
3883 const Headers &headers = {});
3886 WebSocketClient(
const WebSocketClient &) =
delete;
3887 WebSocketClient &operator=(
const WebSocketClient &) =
delete;
3889 bool is_valid()
const;
3892 ReadResult read(std::string &msg);
3893 bool send(
const std::string &data);
3894 bool send(
const char *data,
size_t len);
3895 void close(CloseStatus status = CloseStatus::Normal,
3896 const std::string &reason =
"");
3897 bool is_open()
const;
3898 const std::string &subprotocol()
const;
3899 void set_read_timeout(time_t sec, time_t usec = 0);
3900 void set_write_timeout(time_t sec, time_t usec = 0);
3901 void set_websocket_ping_interval(time_t sec);
3902 void set_websocket_max_missed_pongs(
int count);
3903 void set_tcp_nodelay(
bool on);
3904 void set_address_family(
int family);
3905 void set_ipv6_v6only(
bool on);
3906 void set_socket_options(SocketOptions socket_options);
3907 void set_connection_timeout(time_t sec, time_t usec = 0);
3908 void set_interface(
const std::string &intf);
3909 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
3911#ifdef CPPHTTPLIB_SSL_ENABLED
3912 void set_ca_cert_path(
const std::string &path);
3913 void set_ca_cert_store(tls::ca_store_t store);
3914 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
3915 void enable_server_certificate_verification(
bool enabled);
3916 void enable_system_ca(
bool enabled);
3920 void shutdown_and_close();
3921 bool create_stream(std::unique_ptr<Stream> &strm);
3927 std::string subprotocol_;
3928 bool is_valid_ =
false;
3930 std::unique_ptr<WebSocket> ws_;
3932 time_t read_timeout_usec_ = 0;
3935 time_t websocket_ping_interval_sec_ =
3938 int address_family_ = AF_UNSPEC;
3941 SocketOptions socket_options_ =
nullptr;
3944 std::string interface_;
3947 std::map<std::string, std::string> addr_map_;
3949#ifdef CPPHTTPLIB_SSL_ENABLED
3950 bool is_ssl_ =
false;
3951 tls::ctx_t tls_ctx_ =
nullptr;
3952 tls::session_t tls_session_ =
nullptr;
3953 std::string ca_cert_file_path_;
3954 bool custom_ca_loaded_ =
false;
3955 bool certs_loaded_ =
false;
3956 SystemCAMode system_ca_mode_ = SystemCAMode::Auto;
3957 bool server_certificate_verification_ =
true;
3963bool is_valid_utf8(
const std::string &s);
3965bool read_websocket_frame(Stream &strm, Opcode &opcode, std::string &payload,
3966 bool &fin,
bool expect_masked,
size_t max_len);
3981inline Result::Result() : chunk_size_(8192) {}
3983inline Result::Result(ClientImpl::StreamHandle &&handle,
size_t chunk_size)
3984 : handle_(std::move(handle)), chunk_size_(chunk_size) {}
3986inline Result::Result(Result &&other) noexcept
3987 : handle_(std::move(other.handle_)), buffer_(std::move(other.buffer_)),
3988 current_size_(other.current_size_), chunk_size_(other.chunk_size_),
3989 finished_(other.finished_) {
3990 other.current_size_ = 0;
3991 other.finished_ =
true;
3994inline Result &Result::operator=(Result &&other)
noexcept {
3995 if (
this != &other) {
3996 handle_ = std::move(other.handle_);
3997 buffer_ = std::move(other.buffer_);
3998 current_size_ = other.current_size_;
3999 chunk_size_ = other.chunk_size_;
4000 finished_ = other.finished_;
4001 other.current_size_ = 0;
4002 other.finished_ =
true;
4007inline bool Result::is_valid()
const {
return handle_.is_valid(); }
4008inline Result::operator bool()
const {
return is_valid(); }
4010inline int Result::status()
const {
4011 return handle_.response ? handle_.response->status : -1;
4014inline const Headers &Result::headers()
const {
4015 static const Headers empty_headers;
4016 return handle_.response ? handle_.response->headers : empty_headers;
4019inline std::string Result::get_header_value(
const std::string &key,
4020 const char *def)
const {
4021 return handle_.response ? handle_.response->get_header_value(key, def) : def;
4024inline bool Result::has_header(
const std::string &key)
const {
4025 return handle_.response ? handle_.response->has_header(key) :
false;
4028inline Error Result::error()
const {
return handle_.error; }
4029inline Error Result::read_error()
const {
return handle_.get_read_error(); }
4030inline bool Result::has_read_error()
const {
return handle_.has_read_error(); }
4032inline bool Result::next() {
4033 if (!handle_.is_valid() || finished_) {
return false; }
4035 if (buffer_.size() < chunk_size_) { buffer_.resize(chunk_size_); }
4037 ssize_t n = handle_.read(&buffer_[0], chunk_size_);
4039 current_size_ =
static_cast<size_t>(n);
4048inline const char *Result::data()
const {
return buffer_.data(); }
4049inline size_t Result::size()
const {
return current_size_; }
4051inline std::string Result::read_all() {
4054 result.append(data(), size());
4064inline SSEMessage::SSEMessage() : event(
"message") {}
4066inline void SSEMessage::clear() {
4073inline SSEClient::SSEClient(Client &client,
const std::string &path)
4074 : client_(client), path_(path) {}
4076inline SSEClient::SSEClient(Client &client,
const std::string &path,
4077 const Headers &headers)
4078 : client_(client), path_(path), headers_(headers) {}
4080inline SSEClient::~SSEClient() { stop(); }
4082inline SSEClient &SSEClient::on_message(MessageHandler handler) {
4083 on_message_ = std::move(handler);
4087inline SSEClient &SSEClient::on_event(
const std::string &type,
4088 MessageHandler handler) {
4089 event_handlers_[type] = std::move(handler);
4093inline SSEClient &SSEClient::on_open(OpenHandler handler) {
4094 on_open_ = std::move(handler);
4098inline SSEClient &SSEClient::on_error(ErrorHandler handler) {
4099 on_error_ = std::move(handler);
4103inline SSEClient &SSEClient::set_reconnect_interval(
int ms) {
4104 reconnect_interval_ms_ = ms;
4108inline SSEClient &SSEClient::set_max_reconnect_attempts(
int n) {
4109 max_reconnect_attempts_ = n;
4113inline SSEClient &SSEClient::set_headers(
const Headers &headers) {
4114 std::lock_guard<std::mutex> lock(headers_mutex_);
4119inline bool SSEClient::is_connected()
const {
return connected_.load(); }
4121inline const std::string &SSEClient::last_event_id()
const {
4122 return last_event_id_;
4125inline void SSEClient::start() {
4126 running_.store(
true);
4130inline void SSEClient::start_async() {
4131 running_.store(
true);
4132 async_thread_ = std::thread([
this]() { run_event_loop(); });
4135inline void SSEClient::stop() {
4136 running_.store(
false);
4138 if (async_thread_.joinable()) { async_thread_.join(); }
4141inline bool SSEClient::parse_sse_line(
const std::string &line, SSEMessage &msg,
4144 if (line.empty() || line ==
"\r") {
return true; }
4147 if (!line.empty() && line[0] ==
':') {
return false; }
4150 auto colon_pos = line.find(
':');
4151 if (colon_pos == std::string::npos) {
4156 auto field = line.substr(0, colon_pos);
4160 if (colon_pos + 1 < line.size()) {
4161 auto value_start = colon_pos + 1;
4162 if (line[value_start] ==
' ') { value_start++; }
4163 value = line.substr(value_start);
4165 if (!value.empty() && value.back() ==
'\r') { value.pop_back(); }
4169 if (field ==
"event") {
4171 }
else if (field ==
"data") {
4173 if (!msg.data.empty()) { msg.data +=
"\n"; }
4175 }
else if (field ==
"id") {
4178 }
else if (field ==
"retry") {
4183 detail::from_chars(value.data(), value.data() + value.size(), v);
4184 if (res.ec == std::errc{}) { retry_ms = v; }
4192inline void SSEClient::run_event_loop() {
4193 auto reconnect_count = 0;
4195 while (running_.load()) {
4197 Headers request_headers;
4199 std::lock_guard<std::mutex> lock(headers_mutex_);
4200 request_headers = headers_;
4202 if (!last_event_id_.empty()) {
4203 request_headers.emplace(
"Last-Event-ID", last_event_id_);
4207 auto result = stream::Get(client_, path_, request_headers);
4211 connected_.store(
false);
4212 if (on_error_) { on_error_(result.error()); }
4214 if (!should_reconnect(reconnect_count)) {
break; }
4215 wait_for_reconnect();
4220 if (result.status() != StatusCode::OK_200) {
4221 connected_.store(
false);
4222 if (on_error_) { on_error_(Error::Connection); }
4227 if (result.status() == StatusCode::NoContent_204 ||
4228 result.status() == StatusCode::NotFound_404 ||
4229 result.status() == StatusCode::Forbidden_403) {
4233 if (!should_reconnect(reconnect_count)) {
break; }
4234 wait_for_reconnect();
4240 connected_.store(
true);
4241 reconnect_count = 0;
4242 if (on_open_) { on_open_(); }
4246 SSEMessage current_msg;
4248 while (running_.load() && result.next()) {
4249 buffer.append(result.data(), result.size());
4252 size_t line_start = 0;
4255 while ((newline_pos = buffer.find(
'\n', line_start)) !=
4256 std::string::npos) {
4257 auto line = buffer.substr(line_start, newline_pos - line_start);
4258 line_start = newline_pos + 1;
4261 auto event_complete =
4262 parse_sse_line(line, current_msg, reconnect_interval_ms_);
4264 if (event_complete && !current_msg.data.empty()) {
4266 if (!current_msg.id.empty()) { last_event_id_ = current_msg.id; }
4269 dispatch_event(current_msg);
4271 current_msg.clear();
4276 buffer.erase(0, line_start);
4280 connected_.store(
false);
4282 if (!running_.load()) {
break; }
4285 if (result.has_read_error()) {
4286 if (on_error_) { on_error_(result.read_error()); }
4289 if (!should_reconnect(reconnect_count)) {
break; }
4290 wait_for_reconnect();
4294 connected_.store(
false);
4297inline void SSEClient::dispatch_event(
const SSEMessage &msg) {
4299 auto it = event_handlers_.find(msg.event);
4300 if (it != event_handlers_.end()) {
4306 if (on_message_) { on_message_(msg); }
4309inline bool SSEClient::should_reconnect(
int count)
const {
4310 if (!running_.load()) {
return false; }
4311 if (max_reconnect_attempts_ == 0) {
return true; }
4312 return count < max_reconnect_attempts_;
4315inline void SSEClient::wait_for_reconnect() {
4318 while (running_.load() && waited < reconnect_interval_ms_) {
4319 std::this_thread::sleep_for(std::chrono::milliseconds(100));
4326#ifdef CPPHTTPLIB_SSL_ENABLED
4334ctx_t create_client_context();
4335void free_context(ctx_t ctx);
4336bool set_min_version(ctx_t ctx, Version version);
4337bool load_ca_pem(ctx_t ctx,
const char *pem,
size_t len);
4338bool load_ca_file(ctx_t ctx,
const char *file_path);
4339bool load_ca_dir(ctx_t ctx,
const char *dir_path);
4340bool load_system_certs(ctx_t ctx);
4341bool set_client_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
4342 const char *password);
4343bool set_client_cert_file(ctx_t ctx,
const char *cert_path,
4344 const char *key_path,
const char *password);
4347ctx_t create_server_context();
4348bool set_server_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
4349 const char *password);
4350bool set_server_cert_file(ctx_t ctx,
const char *cert_path,
4351 const char *key_path,
const char *password);
4352bool set_client_ca_file(ctx_t ctx,
const char *ca_file,
const char *ca_dir);
4353void set_verify_client(ctx_t ctx,
bool require);
4356session_t create_session(ctx_t ctx,
socket_t sock);
4357void free_session(session_t session);
4358bool set_sni(session_t session,
const char *hostname);
4359bool set_hostname(session_t session,
const char *hostname);
4362TlsError connect(session_t session);
4363TlsError accept(session_t session);
4366bool connect_nonblocking(session_t session,
socket_t sock, time_t timeout_sec,
4367 time_t timeout_usec, TlsError *err);
4368bool accept_nonblocking(session_t session,
socket_t sock, time_t timeout_sec,
4369 time_t timeout_usec, TlsError *err);
4372ssize_t read(session_t session,
void *buf,
size_t len, TlsError &err);
4373ssize_t write(session_t session,
const void *buf,
size_t len, TlsError &err);
4374int pending(const_session_t session);
4375void shutdown(session_t session,
bool graceful);
4378bool is_peer_closed(session_t session,
socket_t sock);
4381cert_t get_peer_cert(const_session_t session);
4382void free_cert(cert_t cert);
4383bool verify_hostname(cert_t cert,
const char *hostname);
4384uint64_t hostname_mismatch_code();
4385long get_verify_result(const_session_t session);
4388std::string get_cert_subject_cn(cert_t cert);
4389std::string get_cert_issuer_name(cert_t cert);
4390bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans);
4391bool get_cert_validity(cert_t cert, time_t ¬_before, time_t ¬_after);
4392std::string get_cert_serial(cert_t cert);
4393bool get_cert_der(cert_t cert, std::vector<unsigned char> &der);
4394const char *get_sni(const_session_t session);
4397ca_store_t create_ca_store(
const char *pem,
size_t len);
4398void free_ca_store(ca_store_t store);
4399bool set_ca_store(ctx_t ctx, ca_store_t store);
4400size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs);
4401std::vector<std::string> get_ca_names(ctx_t ctx);
4404bool update_server_cert(ctx_t ctx,
const char *cert_pem,
const char *key_pem,
4405 const char *password);
4406bool update_server_client_ca(ctx_t ctx,
const char *ca_pem);
4409bool set_verify_callback(ctx_t ctx, VerifyCallback callback);
4410long get_verify_error(const_session_t session);
4411std::string verify_error_string(
long error_code);
4414uint64_t peek_error();
4415uint64_t get_error();
4416std::string error_string(uint64_t code);
4427inline bool set_socket_opt_impl(
socket_t sock,
int level,
int optname,
4428 const void *optval, socklen_t optlen) {
4429 return setsockopt(sock, level, optname,
4431 reinterpret_cast<const char *
>(optval),
4438inline bool set_socket_opt_time(
socket_t sock,
int level,
int optname,
4439 time_t sec, time_t usec) {
4441 auto timeout =
static_cast<uint32_t
>(sec * 1000 + usec / 1000);
4444 timeout.tv_sec =
static_cast<long>(sec);
4445 timeout.tv_usec =
static_cast<decltype(timeout.tv_usec)
>(usec);
4447 return set_socket_opt_impl(sock, level, optname, &timeout,
sizeof(timeout));
4450inline bool is_hex(
char c,
int &v) {
4451 if (isdigit(
static_cast<unsigned char>(c))) {
4454 }
else if (
'A' <= c && c <=
'F') {
4457 }
else if (
'a' <= c && c <=
'f') {
4464inline bool from_hex_to_i(
const std::string &s,
size_t i,
size_t cnt,
4466 if (i >= s.size()) {
return false; }
4469 for (; cnt; i++, cnt--) {
4470 if (!s[i]) {
return false; }
4472 if (is_hex(s[i], v)) {
4481inline std::string from_i_to_hex(
size_t n) {
4482 static const auto charset =
"0123456789abcdef";
4485 ret = charset[n & 15] + ret;
4491inline std::string compute_etag(
const FileStat &fs) {
4492 if (!fs.is_file()) {
return std::string(); }
4498 auto mtime_raw = fs.mtime();
4499 if (mtime_raw < 0) {
return std::string(); }
4501 auto mtime =
static_cast<size_t>(mtime_raw);
4502 auto size = fs.size();
4504 return std::string(
"W/\"") + from_i_to_hex(mtime) +
"-" +
4505 from_i_to_hex(size) +
"\"";
4511inline std::string file_mtime_to_http_date(time_t mtime) {
4512 if (mtime < 0) {
return std::string(); }
4516 if (gmtime_s(&tm_buf, &mtime) != 0) {
return std::string(); }
4518 if (gmtime_r(&mtime, &tm_buf) ==
nullptr) {
return std::string(); }
4521 if (strftime(buf,
sizeof(buf),
"%a, %d %b %Y %H:%M:%S GMT", &tm_buf) == 0) {
4522 return std::string();
4525 return std::string(buf);
4529inline time_t parse_http_date(
const std::string &date_str) {
4533 const std::locale classic_locale = std::locale::classic();
4536 auto try_parse = [&](
const char *fmt) ->
bool {
4537 std::istringstream ss(date_str);
4538 ss.imbue(classic_locale);
4540 memset(&tm_buf, 0,
sizeof(tm_buf));
4541 ss >> std::get_time(&tm_buf, fmt);
4547 if (!try_parse(
"%a, %d %b %Y %H:%M:%S")) {
4549 if (!try_parse(
"%A, %d-%b-%y %H:%M:%S")) {
4551 if (!try_parse(
"%a %b %d %H:%M:%S %Y")) {
4552 return static_cast<time_t
>(-1);
4558 return _mkgmtime(&tm_buf);
4560 return mktime(&tm_buf);
4562 return timegm(&tm_buf);
4566inline bool is_weak_etag(
const std::string &s) {
4568 return s.size() > 3 && s[0] ==
'W' && s[1] ==
'/' && s[2] ==
'"';
4571inline bool is_strong_etag(
const std::string &s) {
4574 return s.size() >= 2 && s[0] ==
'"' && s.back() ==
'"';
4577inline size_t to_utf8(
int code,
char *buff) {
4578 if (code < 0x0080) {
4579 buff[0] =
static_cast<char>(code & 0x7F);
4581 }
else if (code < 0x0800) {
4582 buff[0] =
static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
4583 buff[1] =
static_cast<char>(0x80 | (code & 0x3F));
4585 }
else if (code < 0xD800) {
4586 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
4587 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4588 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
4590 }
else if (code < 0xE000) {
4592 }
else if (code < 0x10000) {
4593 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
4594 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4595 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
4597 }
else if (code < 0x110000) {
4598 buff[0] =
static_cast<char>(0xF0 | ((code >> 18) & 0x7));
4599 buff[1] =
static_cast<char>(0x80 | ((code >> 12) & 0x3F));
4600 buff[2] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4601 buff[3] =
static_cast<char>(0x80 | (code & 0x3F));
4614inline bool is_valid_utf8(
const std::string &s) {
4618 auto c =
static_cast<unsigned char>(s[i]);
4624 }
else if ((c & 0xE0) == 0xC0) {
4627 }
else if ((c & 0xF0) == 0xE0) {
4630 }
else if ((c & 0xF8) == 0xF0) {
4636 if (i + len > n) {
return false; }
4637 for (
size_t j = 1; j < len; j++) {
4638 auto b =
static_cast<unsigned char>(s[i + j]);
4639 if ((b & 0xC0) != 0x80) {
return false; }
4640 cp = (cp << 6) | (b & 0x3F);
4643 if (len == 2 && cp < 0x80) {
return false; }
4644 if (len == 3 && cp < 0x800) {
return false; }
4645 if (len == 4 && cp < 0x10000) {
return false; }
4647 if (cp >= 0xD800 && cp <= 0xDFFF) {
return false; }
4648 if (cp > 0x10FFFF) {
return false; }
4661inline std::string base64_encode(
const std::string &in) {
4662 static const auto lookup =
4663 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4666 out.reserve(in.size());
4672 val = (val << 8) + static_cast<uint8_t>(c);
4675 out.push_back(lookup[(val >> valb) & 0x3F]);
4680 if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
4682 while (out.size() % 4) {
4689inline std::string sha1(
const std::string &input) {
4691 auto left_rotate = [](uint32_t x, uint32_t n) -> uint32_t {
4692 return (x << n) | (x >> (32 - n));
4695 uint32_t h0 = 0x67452301;
4696 uint32_t h1 = 0xEFCDAB89;
4697 uint32_t h2 = 0x98BADCFE;
4698 uint32_t h3 = 0x10325476;
4699 uint32_t h4 = 0xC3D2E1F0;
4702 std::string msg = input;
4703 uint64_t original_bit_len =
static_cast<uint64_t
>(msg.size()) * 8;
4704 msg.push_back(
static_cast<char>(0x80u));
4705 while (msg.size() % 64 != 56) {
4710 for (
int i = 56; i >= 0; i -= 8) {
4711 msg.push_back(
static_cast<char>((original_bit_len >> i) & 0xFF));
4715 for (
size_t offset = 0; offset < msg.size(); offset += 64) {
4718 for (
size_t i = 0; i < 16; i++) {
4720 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(msg[offset + i * 4]))
4722 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(msg[offset + i * 4 + 1]))
4724 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(msg[offset + i * 4 + 2]))
4726 (
static_cast<uint32_t
>(
4727 static_cast<uint8_t
>(msg[offset + i * 4 + 3])));
4730 for (
int i = 16; i < 80; i++) {
4731 w[i] = left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
4734 uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;
4736 for (
int i = 0; i < 80; i++) {
4739 f = (b & c) | ((~b) & d);
4741 }
else if (i < 40) {
4744 }
else if (i < 60) {
4745 f = (b & c) | (b & d) | (c & d);
4752 uint32_t temp = left_rotate(a, 5) + f + e + k + w[i];
4755 c = left_rotate(b, 30);
4768 std::string hash(20,
'\0');
4769 for (
size_t i = 0; i < 4; i++) {
4770 hash[i] =
static_cast<char>((h0 >> (24 - i * 8)) & 0xFF);
4771 hash[4 + i] =
static_cast<char>((h1 >> (24 - i * 8)) & 0xFF);
4772 hash[8 + i] =
static_cast<char>((h2 >> (24 - i * 8)) & 0xFF);
4773 hash[12 + i] =
static_cast<char>((h3 >> (24 - i * 8)) & 0xFF);
4774 hash[16 + i] =
static_cast<char>((h4 >> (24 - i * 8)) & 0xFF);
4779inline std::string websocket_accept_key(
const std::string &client_key) {
4780 const std::string magic =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
4781 return base64_encode(sha1(client_key + magic));
4784inline bool is_websocket_upgrade(
const Request &req) {
4785 if (req.method !=
"GET") {
return false; }
4788 auto upgrade_it = req.headers.find(
"Upgrade");
4789 if (upgrade_it == req.headers.end()) {
return false; }
4790 auto upgrade_val = case_ignore::to_lower(upgrade_it->second);
4791 if (upgrade_val !=
"websocket") {
return false; }
4794 auto connection_it = req.headers.find(
"Connection");
4795 if (connection_it == req.headers.end()) {
return false; }
4796 auto connection_val = case_ignore::to_lower(connection_it->second);
4797 if (connection_val.find(
"upgrade") == std::string::npos) {
return false; }
4801 auto ws_key = req.get_header_value(
"Sec-WebSocket-Key");
4802 if (ws_key.size() != 24 || ws_key[22] !=
'=' || ws_key[23] !=
'=') {
4805 static const std::string b64chars =
4806 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4807 for (
size_t i = 0; i < 22; i++) {
4808 if (b64chars.find(ws_key[i]) == std::string::npos) {
return false; }
4812 auto version = req.get_header_value(
"Sec-WebSocket-Version");
4813 if (version !=
"13") {
return false; }
4818inline bool write_websocket_frame(Stream &strm, ws::Opcode opcode,
4819 const char *data,
size_t len,
bool fin,
4823 header[0] =
static_cast<uint8_t
>((fin ? 0x80 : 0x00) |
4824 (
static_cast<uint8_t
>(opcode) & 0x0F));
4828 header[1] =
static_cast<uint8_t
>(len);
4829 if (mask) { header[1] |= 0x80; }
4830 if (strm.write(
reinterpret_cast<char *
>(header), 2) < 0) {
return false; }
4831 }
else if (len <= 0xFFFF) {
4833 if (mask) { header[1] |= 0x80; }
4834 if (strm.write(
reinterpret_cast<char *
>(header), 2) < 0) {
return false; }
4836 ext[0] =
static_cast<uint8_t
>((len >> 8) & 0xFF);
4837 ext[1] =
static_cast<uint8_t
>(len & 0xFF);
4838 if (strm.write(
reinterpret_cast<char *
>(ext), 2) < 0) {
return false; }
4841 if (mask) { header[1] |= 0x80; }
4842 if (strm.write(
reinterpret_cast<char *
>(header), 2) < 0) {
return false; }
4844 for (
int i = 7; i >= 0; i--) {
4846 static_cast<uint8_t
>((
static_cast<uint64_t
>(len) >> (i * 8)) & 0xFF);
4848 if (strm.write(
reinterpret_cast<char *
>(ext), 8) < 0) {
return false; }
4853 thread_local std::mt19937 rng(std::random_device{}());
4854 uint8_t mask_key[4];
4856 std::memcpy(mask_key, &r, 4);
4857 if (strm.write(
reinterpret_cast<char *
>(mask_key), 4) < 0) {
return false; }
4860 const size_t chunk_size = 4096;
4861 std::vector<char> buf((std::min)(len, chunk_size));
4862 for (
size_t offset = 0; offset < len; offset += chunk_size) {
4863 size_t n = (std::min)(chunk_size, len - offset);
4864 for (
size_t i = 0; i < n; i++) {
4866 data[offset + i] ^
static_cast<char>(mask_key[(offset + i) % 4]);
4868 if (strm.write(buf.data(), n) < 0) {
return false; }
4872 if (strm.write(data, len) < 0) {
return false; }
4884inline bool read_websocket_frame(Stream &strm, Opcode &opcode,
4885 std::string &payload,
bool &fin,
4886 bool expect_masked,
size_t max_len) {
4889 if (strm.read(
reinterpret_cast<char *
>(header), 2) != 2) {
return false; }
4891 fin = (header[0] & 0x80) != 0;
4894 if (header[0] & 0x70) {
return false; }
4896 opcode =
static_cast<Opcode
>(header[0] & 0x0F);
4897 bool masked = (header[1] & 0x80) != 0;
4898 uint64_t payload_len = header[1] & 0x7F;
4902 bool is_control = (
static_cast<uint8_t
>(opcode) & 0x08) != 0;
4904 if (!fin) {
return false; }
4905 if (payload_len > 125) {
return false; }
4908 if (masked != expect_masked) {
return false; }
4911 if (payload_len == 126) {
4913 if (strm.read(
reinterpret_cast<char *
>(ext), 2) != 2) {
return false; }
4914 payload_len = (
static_cast<uint64_t
>(ext[0]) << 8) | ext[1];
4915 }
else if (payload_len == 127) {
4917 if (strm.read(
reinterpret_cast<char *
>(ext), 8) != 8) {
return false; }
4919 if (ext[0] & 0x80) {
return false; }
4921 for (
int i = 0; i < 8; i++) {
4922 payload_len = (payload_len << 8) | ext[i];
4926 if (payload_len > max_len) {
return false; }
4929 uint8_t mask_key[4] = {0};
4931 if (strm.read(
reinterpret_cast<char *
>(mask_key), 4) != 4) {
return false; }
4935 payload.resize(
static_cast<size_t>(payload_len));
4936 if (payload_len > 0) {
4937 size_t total_read = 0;
4938 while (total_read < payload_len) {
4939 auto n = strm.read(&payload[total_read],
4940 static_cast<size_t>(payload_len - total_read));
4941 if (n <= 0) {
return false; }
4942 total_read +=
static_cast<size_t>(n);
4948 for (
size_t i = 0; i < payload.size(); i++) {
4949 payload[i] ^=
static_cast<char>(mask_key[i % 4]);
4961inline bool is_valid_path(
const std::string &path) {
4966 while (i < path.size() && path[i] ==
'/') {
4970 while (i < path.size()) {
4973 while (i < path.size() && path[i] !=
'/') {
4974 if (path[i] ==
'\0') {
4976 }
else if (path[i] ==
'\\') {
4985 if (!path.compare(beg, len,
".")) {
4987 }
else if (!path.compare(beg, len,
"..")) {
4988 if (level == 0) {
return false; }
4995 while (i < path.size() && path[i] ==
'/') {
5003inline bool canonicalize_path(
const char *path, std::string &resolved) {
5005 char buf[_MAX_PATH];
5006 if (_fullpath(buf, path, _MAX_PATH) ==
nullptr) {
return false; }
5008#elif defined(PATH_MAX)
5010 if (realpath(path, buf) ==
nullptr) {
return false; }
5013 auto buf = realpath(path,
nullptr);
5014 auto guard = scope_exit([&]() { std::free(buf); });
5015 if (buf ==
nullptr) {
return false; }
5021inline bool is_path_within_base(
const std::string &resolved_path,
5022 const std::string &resolved_base) {
5024 return _strnicmp(resolved_path.c_str(), resolved_base.c_str(),
5025 resolved_base.size()) == 0;
5027 return strncmp(resolved_path.c_str(), resolved_base.c_str(),
5028 resolved_base.size()) == 0;
5032inline FileStat::FileStat(
const std::string &path) {
5034 auto wpath = u8string_to_wstring(path.c_str());
5035 ret_ = _wstat(wpath.c_str(), &st_);
5037 ret_ = stat(path.c_str(), &st_);
5040inline bool FileStat::is_file()
const {
5041 return ret_ >= 0 && S_ISREG(st_.st_mode);
5043inline bool FileStat::is_dir()
const {
5044 return ret_ >= 0 && S_ISDIR(st_.st_mode);
5047inline time_t FileStat::mtime()
const {
5048 return ret_ >= 0 ?
static_cast<time_t
>(st_.st_mtime)
5049 :
static_cast<time_t
>(-1);
5052inline size_t FileStat::size()
const {
5053 return ret_ >= 0 ?
static_cast<size_t>(st_.st_size) : 0;
5056inline std::string encode_path(
const std::string &s) {
5058 result.reserve(s.size());
5060 for (
size_t i = 0; s[i]; i++) {
5062 case ' ': result +=
"%20";
break;
5063 case '+': result +=
"%2B";
break;
5064 case '\r': result +=
"%0D";
break;
5065 case '\n': result +=
"%0A";
break;
5066 case '\'': result +=
"%27";
break;
5067 case ',': result +=
"%2C";
break;
5069 case ';': result +=
"%3B";
break;
5071 auto c =
static_cast<uint8_t
>(s[i]);
5075 auto len = snprintf(hex,
sizeof(hex) - 1,
"%02X", c);
5077 result.append(hex,
static_cast<size_t>(len));
5088inline std::string file_extension(
const std::string &path) {
5090 thread_local auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
5091 if (std::regex_search(path, m, re)) {
return m[1].str(); }
5092 return std::string();
5095inline bool is_space_or_tab(
char c) {
return c ==
' ' || c ==
'\t'; }
5097template <
typename T>
5098inline bool parse_header(
const char *beg,
const char *end, T fn);
5100template <
typename T>
5101inline bool parse_header(
const char *beg,
const char *end, T fn) {
5103 while (beg < end && is_space_or_tab(end[-1])) {
5108 while (p < end && *p !=
':') {
5112 auto name = std::string(beg, p);
5113 if (!detail::fields::is_field_name(name)) {
return false; }
5115 if (p == end) {
return false; }
5119 if (*p++ !=
':') {
return false; }
5121 while (p < end && is_space_or_tab(*p)) {
5126 auto key_len = key_end - beg;
5127 if (!key_len) {
return false; }
5129 auto key = std::string(beg, key_end);
5130 auto val = std::string(p, end);
5132 if (!detail::fields::is_field_value(val)) {
return false; }
5146inline bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
5147 const Headers &src_headers) {
5162 thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {
5163 "transfer-encoding",
5168 "proxy-authenticate",
5169 "proxy-authorization",
5190 case_ignore::unordered_set<std::string> declared_trailers;
5191 auto trailer_header = get_header_value(src_headers,
"Trailer",
"", 0);
5192 if (trailer_header && std::strlen(trailer_header)) {
5193 auto len = std::strlen(trailer_header);
5194 split(trailer_header, trailer_header + len,
',',
5195 [&](
const char *b,
const char *e) {
5196 const char *kbeg = b;
5197 const char *kend = e;
5198 while (kbeg < kend && (*kbeg ==
' ' || *kbeg ==
'\t')) {
5201 while (kend > kbeg && (kend[-1] ==
' ' || kend[-1] ==
'\t')) {
5204 std::string key(kbeg,
static_cast<size_t>(kend - kbeg));
5206 prohibited_trailers.find(key) == prohibited_trailers.end()) {
5207 declared_trailers.insert(key);
5212 size_t trailer_header_count = 0;
5213 while (strcmp(line_reader.ptr(),
"\r\n") != 0) {
5217 constexpr auto line_terminator_len = 2;
5218 auto line_beg = line_reader.ptr();
5220 line_reader.ptr() + line_reader.size() - line_terminator_len;
5222 if (!parse_header(line_beg, line_end,
5223 [&](
const std::string &key,
const std::string &val) {
5224 if (declared_trailers.find(key) !=
5225 declared_trailers.end()) {
5226 dest.emplace(key, val);
5227 trailer_header_count++;
5233 if (!line_reader.getline()) {
return false; }
5239inline std::pair<size_t, size_t> trim(
const char *b,
const char *e,
size_t left,
5241 while (b + left < e && is_space_or_tab(b[left])) {
5244 while (right > 0 && is_space_or_tab(b[right - 1])) {
5247 return std::make_pair(left, right);
5250inline std::string trim_copy(
const std::string &s) {
5251 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
5252 return s.substr(r.first, r.second - r.first);
5255inline std::string trim_double_quotes_copy(
const std::string &s) {
5256 if (s.length() >= 2 && s.front() ==
'"' && s.back() ==
'"') {
5257 return s.substr(1, s.size() - 2);
5263divide(
const char *data, std::size_t size,
char d,
5264 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
5266 const auto it = std::find(data, data + size, d);
5267 const auto found =
static_cast<std::size_t
>(it != data + size);
5268 const auto lhs_data = data;
5269 const auto lhs_size =
static_cast<std::size_t
>(it - data);
5270 const auto rhs_data = it + found;
5271 const auto rhs_size = size - lhs_size - found;
5273 fn(lhs_data, lhs_size, rhs_data, rhs_size);
5277divide(
const std::string &str,
char d,
5278 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
5280 divide(str.data(), str.size(), d, std::move(fn));
5283inline void split(
const char *b,
const char *e,
char d,
5284 std::function<
void(
const char *,
const char *)> fn) {
5285 return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
5288inline void split(
const char *b,
const char *e,
char d,
size_t m,
5289 std::function<
void(
const char *,
const char *)> fn) {
5294 while (e ? (b + i < e) : (b[i] !=
'\0')) {
5295 if (b[i] == d && count < m) {
5296 auto r = trim(b, e, beg, i);
5297 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
5305 auto r = trim(b, e, beg, i);
5306 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
5310inline bool split_find(
const char *b,
const char *e,
char d,
size_t m,
5311 std::function<
bool(
const char *,
const char *)> fn) {
5316 while (e ? (b + i < e) : (b[i] !=
'\0')) {
5317 if (b[i] == d && count < m) {
5318 auto r = trim(b, e, beg, i);
5319 if (r.first < r.second) {
5320 auto found = fn(&b[r.first], &b[r.second]);
5321 if (found) {
return true; }
5330 auto r = trim(b, e, beg, i);
5331 if (r.first < r.second) {
5332 auto found = fn(&b[r.first], &b[r.second]);
5333 if (found) {
return true; }
5340inline bool split_find(
const char *b,
const char *e,
char d,
5341 std::function<
bool(
const char *,
const char *)> fn) {
5342 return split_find(b, e, d, (std::numeric_limits<size_t>::max)(),
5346inline stream_line_reader::stream_line_reader(Stream &strm,
char *fixed_buffer,
5347 size_t fixed_buffer_size)
5348 : strm_(strm), fixed_buffer_(fixed_buffer),
5349 fixed_buffer_size_(fixed_buffer_size) {}
5351inline const char *stream_line_reader::ptr()
const {
5352 if (growable_buffer_.empty()) {
5353 return fixed_buffer_;
5355 return growable_buffer_.data();
5359inline size_t stream_line_reader::size()
const {
5360 if (growable_buffer_.empty()) {
5361 return fixed_buffer_used_size_;
5363 return growable_buffer_.size();
5367inline bool stream_line_reader::end_with_crlf()
const {
5368 auto end = ptr() + size();
5369 return size() >= 2 && end[-2] ==
'\r' && end[-1] ==
'\n';
5372inline bool stream_line_reader::getline() {
5373 fixed_buffer_used_size_ = 0;
5374 growable_buffer_.clear();
5376#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
5380 for (
size_t i = 0;; i++) {
5387 auto n = strm_.read(&
byte, 1);
5391 }
else if (n == 0) {
5401#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
5402 if (
byte ==
'\n') {
break; }
5404 if (prev_byte ==
'\r' &&
byte ==
'\n') {
break; }
5412inline void stream_line_reader::append(
char c) {
5413 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
5414 fixed_buffer_[fixed_buffer_used_size_++] = c;
5415 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
5417 if (growable_buffer_.empty()) {
5418 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
5419 growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
5421 growable_buffer_ += c;
5425inline mmap::mmap(
const char *path) { open(path); }
5427inline mmap::~mmap() { close(); }
5429inline bool mmap::open(
const char *path) {
5433 auto wpath = u8string_to_wstring(path);
5434 if (wpath.empty()) {
return false; }
5437 ::CreateFile2(wpath.c_str(), GENERIC_READ,
5438 FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING, NULL);
5440 if (hFile_ == INVALID_HANDLE_VALUE) {
return false; }
5442 LARGE_INTEGER size{};
5443 if (!::GetFileSizeEx(hFile_, &size)) {
return false; }
5447 if (
static_cast<ULONGLONG
>(size.QuadPart) >
5448 (std::numeric_limits<
decltype(size_)>::max)()) {
5452 size_ =
static_cast<size_t>(size.QuadPart);
5455 ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
5458 if (hMapping_ == NULL && size_ == 0) {
5460 is_open_empty_file =
true;
5464 if (hMapping_ == NULL) {
5469 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
5471 if (addr_ ==
nullptr) {
5476 fd_ = ::open(path, O_RDONLY);
5477 if (fd_ == -1) {
return false; }
5480 if (fstat(fd_, &sb) == -1) {
5484 size_ =
static_cast<size_t>(sb.st_size);
5486 addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
5489 if (addr_ == MAP_FAILED && size_ == 0) {
5491 is_open_empty_file =
true;
5499inline bool mmap::is_open()
const {
5500 return is_open_empty_file ? true : addr_ !=
nullptr;
5503inline size_t mmap::size()
const {
return size_; }
5505inline const char *mmap::data()
const {
5506 return is_open_empty_file ?
"" :
static_cast<const char *
>(addr_);
5509inline void mmap::close() {
5512 ::UnmapViewOfFile(addr_);
5517 ::CloseHandle(hMapping_);
5521 if (hFile_ != INVALID_HANDLE_VALUE) {
5522 ::CloseHandle(hFile_);
5523 hFile_ = INVALID_HANDLE_VALUE;
5526 is_open_empty_file =
false;
5528 if (addr_ !=
nullptr) {
5529 munmap(addr_, size_);
5540inline int close_socket(
socket_t sock)
noexcept {
5542 return closesocket(sock);
5548template <
typename T>
inline ssize_t handle_EINTR(T fn) {
5552 if (res < 0 && errno == EINTR) {
5553 std::this_thread::sleep_for(std::chrono::microseconds{1});
5561inline ssize_t read_socket(
socket_t sock,
void *ptr,
size_t size,
int flags) {
5562 return handle_EINTR([&]() {
5565 static_cast<char *
>(ptr),
static_cast<int>(size),
5573inline ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
5575 return handle_EINTR([&]() {
5578 static_cast<const char *
>(ptr),
static_cast<int>(size),
5586inline int poll_wrapper(
struct pollfd *fds, nfds_t nfds,
int timeout) {
5588 return ::WSAPoll(fds, nfds, timeout);
5590 return ::poll(fds, nfds, timeout);
5594inline ssize_t select_impl(
socket_t sock,
short events, time_t sec,
5598 pfd.events = events;
5601 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
5603 return handle_EINTR([&]() {
return poll_wrapper(&pfd, 1, timeout); });
5606inline ssize_t select_read(
socket_t sock, time_t sec, time_t usec) {
5607 return select_impl(sock, POLLIN, sec, usec);
5610inline ssize_t select_write(
socket_t sock, time_t sec, time_t usec) {
5611 return select_impl(sock, POLLOUT, sec, usec);
5614inline Error wait_until_socket_is_ready(
socket_t sock, time_t sec,
5616 struct pollfd pfd_read;
5618 pfd_read.events = POLLIN | POLLOUT;
5619 pfd_read.revents = 0;
5621 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
5624 handle_EINTR([&]() {
return poll_wrapper(&pfd_read, 1, timeout); });
5626 if (poll_res == 0) {
return Error::ConnectionTimeout; }
5628 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
5630 socklen_t len =
sizeof(error);
5631 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
5632 reinterpret_cast<char *
>(&error), &len);
5633 auto successful = res >= 0 && !error;
5634 return successful ? Error::Success : Error::Connection;
5637 return Error::Connection;
5640inline bool is_socket_alive(
socket_t sock) {
5641 const auto val = detail::select_read(sock, 0, 0);
5644 }
else if (val < 0 && errno == EBADF) {
5648 return detail::read_socket(sock, &buf[0],
sizeof(buf), MSG_PEEK) > 0;
5651class SocketStream final :
public Stream {
5653 SocketStream(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5654 time_t write_timeout_sec, time_t write_timeout_usec,
5655 time_t max_timeout_msec = 0,
5656 std::chrono::time_point<std::chrono::steady_clock> start_time =
5657 (std::chrono::steady_clock::time_point::min)());
5658 ~SocketStream()
override;
5660 bool is_readable()
const override;
5661 bool wait_readable()
const override;
5662 bool wait_writable()
const override;
5663 bool is_peer_alive()
const override;
5664 ssize_t read(
char *ptr,
size_t size)
override;
5665 ssize_t write(
const char *ptr,
size_t size)
override;
5666 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
5667 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
5669 time_t duration()
const override;
5670 void set_read_timeout(time_t sec, time_t usec = 0)
override;
5674 time_t read_timeout_sec_;
5675 time_t read_timeout_usec_;
5676 time_t write_timeout_sec_;
5677 time_t write_timeout_usec_;
5678 time_t max_timeout_msec_;
5679 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
5681 std::vector<char> read_buff_;
5682 size_t read_buff_off_ = 0;
5683 size_t read_buff_content_size_ = 0;
5685 static const size_t read_buff_size_ = 1024l * 4;
5688inline bool keep_alive(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
5689 time_t keep_alive_timeout_sec) {
5690 using namespace std::chrono;
5692 const auto interval_usec =
5696 if (select_read(sock, 0, interval_usec) > 0) {
return true; }
5698 const auto start = steady_clock::now() - microseconds{interval_usec};
5699 const auto timeout = seconds{keep_alive_timeout_sec};
5706 auto val = select_read(sock, 0, interval_usec);
5709 }
else if (val == 0) {
5710 if (steady_clock::now() - start > timeout) {
5721template <
typename T>
5723process_server_socket_core(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
5724 size_t keep_alive_max_count,
5725 time_t keep_alive_timeout_sec, T callback) {
5726 assert(keep_alive_max_count > 0);
5728 auto count = keep_alive_max_count;
5729 while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
5730 auto close_connection = count == 1;
5731 auto connection_closed =
false;
5732 ret = callback(close_connection, connection_closed);
5733 if (!ret || connection_closed) {
break; }
5739template <
typename T>
5741process_server_socket(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
5742 size_t keep_alive_max_count,
5743 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
5744 time_t read_timeout_usec, time_t write_timeout_sec,
5745 time_t write_timeout_usec, T callback) {
5746 return process_server_socket_core(
5747 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
5748 [&](
bool close_connection,
bool &connection_closed) {
5749 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5750 write_timeout_sec, write_timeout_usec);
5751 return callback(strm, close_connection, connection_closed);
5755inline bool process_client_socket(
5756 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5757 time_t write_timeout_sec, time_t write_timeout_usec,
5758 time_t max_timeout_msec,
5759 std::chrono::time_point<std::chrono::steady_clock> start_time,
5760 std::function<
bool(Stream &)> callback) {
5761 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5762 write_timeout_sec, write_timeout_usec, max_timeout_msec,
5764 return callback(strm);
5767inline int shutdown_socket(
socket_t sock)
noexcept {
5769 return shutdown(sock, SD_BOTH);
5771 return shutdown(sock, SHUT_RDWR);
5775inline std::string escape_abstract_namespace_unix_domain(
const std::string &s) {
5776 if (s.size() > 1 && s[0] ==
'\0') {
5785unescape_abstract_namespace_unix_domain(
const std::string &s) {
5786 if (s.size() > 1 && s[0] ==
'@') {
5794inline int getaddrinfo_with_timeout(
const char *node,
const char *service,
5795 const struct addrinfo *hints,
5796 struct addrinfo **res, time_t timeout_sec) {
5797#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
5798 if (timeout_sec <= 0) {
5800 return getaddrinfo(node, service, hints, res);
5805 OVERLAPPED overlapped = {};
5806 HANDLE
event = CreateEventW(
nullptr, TRUE, FALSE,
nullptr);
5807 if (!event) {
return EAI_FAIL; }
5809 overlapped.hEvent = event;
5811 PADDRINFOEXW result_addrinfo =
nullptr;
5812 HANDLE cancel_handle =
nullptr;
5814 ADDRINFOEXW hints_ex = {};
5816 hints_ex.ai_flags = hints->ai_flags;
5817 hints_ex.ai_family = hints->ai_family;
5818 hints_ex.ai_socktype = hints->ai_socktype;
5819 hints_ex.ai_protocol = hints->ai_protocol;
5822 auto wnode = u8string_to_wstring(node);
5823 auto wservice = u8string_to_wstring(service);
5825 auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS,
nullptr,
5826 hints ? &hints_ex :
nullptr, &result_addrinfo,
5827 nullptr, &overlapped,
nullptr, &cancel_handle);
5829 if (ret == WSA_IO_PENDING) {
5831 ::WaitForSingleObject(event,
static_cast<DWORD
>(timeout_sec * 1000));
5832 if (wait_result == WAIT_TIMEOUT) {
5833 if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
5834 ::CloseHandle(event);
5838 DWORD bytes_returned;
5840 &bytes_returned, FALSE)) {
5841 ::CloseHandle(event);
5842 return ::WSAGetLastError();
5846 ::CloseHandle(event);
5848 if (ret == NO_ERROR || ret == WSA_IO_PENDING) {
5849 *res =
reinterpret_cast<struct addrinfo *
>(result_addrinfo);
5854#elif TARGET_OS_MAC && defined(__clang__)
5855 if (!node) {
return EAI_NONAME; }
5857 CFStringRef hostname_ref = CFStringCreateWithCString(
5858 kCFAllocatorDefault, node, kCFStringEncodingUTF8);
5859 if (!hostname_ref) {
return EAI_MEMORY; }
5861 CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
5862 CFRelease(hostname_ref);
5863 if (!host_ref) {
return EAI_MEMORY; }
5866 struct CFHostContext {
5867 bool completed =
false;
5868 bool success =
false;
5869 CFArrayRef addresses =
nullptr;
5871 std::condition_variable cv;
5874 CFHostClientContext client_context;
5875 memset(&client_context, 0,
sizeof(client_context));
5876 client_context.info = &context;
5879 auto callback = [](CFHostRef theHost, CFHostInfoType ,
5880 const CFStreamError *error,
void *info) {
5881 auto ctx =
static_cast<CFHostContext *
>(info);
5882 std::lock_guard<std::mutex> lock(ctx->mutex);
5884 if (error && error->error != 0) {
5885 ctx->success =
false;
5887 Boolean hasBeenResolved;
5888 ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
5889 if (ctx->addresses && hasBeenResolved) {
5890 CFRetain(ctx->addresses);
5891 ctx->success =
true;
5893 ctx->success =
false;
5896 ctx->completed =
true;
5897 ctx->cv.notify_one();
5900 if (!CFHostSetClient(host_ref, callback, &client_context)) {
5901 CFRelease(host_ref);
5906 CFRunLoopRef run_loop = CFRunLoopGetCurrent();
5907 CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5910 CFStreamError stream_error;
5911 if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
5912 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5913 CFRelease(host_ref);
5919 std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
5920 bool timed_out =
false;
5923 std::unique_lock<std::mutex> lock(context.mutex);
5925 while (!context.completed) {
5926 auto now = std::chrono::steady_clock::now();
5927 if (now >= timeout_time) {
5934 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1,
true);
5940 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5941 CFHostSetClient(host_ref,
nullptr,
nullptr);
5943 if (timed_out || !context.completed) {
5944 CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
5945 CFRelease(host_ref);
5949 if (!context.success || !context.addresses) {
5950 CFRelease(host_ref);
5955 CFIndex count = CFArrayGetCount(context.addresses);
5957 CFRelease(context.addresses);
5958 CFRelease(host_ref);
5962 struct addrinfo *result_addrinfo =
nullptr;
5963 struct addrinfo **current = &result_addrinfo;
5965 for (CFIndex i = 0; i < count; i++) {
5966 CFDataRef addr_data =
5967 static_cast<CFDataRef
>(CFArrayGetValueAtIndex(context.addresses, i));
5968 if (!addr_data)
continue;
5970 const struct sockaddr *sockaddr_ptr =
5971 reinterpret_cast<const struct sockaddr *
>(CFDataGetBytePtr(addr_data));
5972 socklen_t sockaddr_len =
static_cast<socklen_t
>(CFDataGetLength(addr_data));
5975 *current =
static_cast<struct addrinfo *
>(malloc(
sizeof(
struct addrinfo)));
5977 freeaddrinfo(result_addrinfo);
5978 CFRelease(context.addresses);
5979 CFRelease(host_ref);
5983 memset(*current, 0,
sizeof(
struct addrinfo));
5986 (*current)->ai_family = sockaddr_ptr->sa_family;
5987 (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
5988 (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
5989 (*current)->ai_addrlen = sockaddr_len;
5992 (*current)->ai_addr =
static_cast<struct sockaddr *
>(malloc(sockaddr_len));
5993 if (!(*current)->ai_addr) {
5994 freeaddrinfo(result_addrinfo);
5995 CFRelease(context.addresses);
5996 CFRelease(host_ref);
5999 memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
6002 if (service && *service) {
6004 if (parse_port(service, strlen(service), port)) {
6005 if (sockaddr_ptr->sa_family == AF_INET) {
6006 reinterpret_cast<struct sockaddr_in *
>((*current)->ai_addr)
6007 ->sin_port = htons(
static_cast<uint16_t
>(port));
6008 }
else if (sockaddr_ptr->sa_family == AF_INET6) {
6009 reinterpret_cast<struct sockaddr_in6 *
>((*current)->ai_addr)
6010 ->sin6_port = htons(
static_cast<uint16_t
>(port));
6015 current = &((*current)->ai_next);
6018 CFRelease(context.addresses);
6019 CFRelease(host_ref);
6021 *res = result_addrinfo;
6023#elif defined(_GNU_SOURCE) && defined(__GLIBC__) && \
6024 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
6031 struct gaicb request {};
6032 struct gaicb *requests[1] = {&request};
6033 struct sigevent sevp {};
6034 struct timespec timeout {
6038 request.ar_name = node;
6039 request.ar_service = service;
6040 request.ar_request = hints;
6041 sevp.sigev_notify = SIGEV_NONE;
6043 int rc = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
6044 if (rc != 0) {
return rc; }
6046 auto cleanup = scope_exit([&] {
6047 if (request.ar_result) { freeaddrinfo(request.ar_result); }
6050 int wait_result = gai_suspend(requests, 1, &timeout);
6052 if (wait_result == 0 || wait_result == EAI_ALLDONE) {
6053 int gai_result = gai_error(&request);
6054 if (gai_result == 0) {
6055 *res = request.ar_result;
6056 request.ar_result =
nullptr;
6062 gai_cancel(&request);
6063 while (gai_error(&request) == EAI_INPROGRESS) {
6064 gai_suspend(requests, 1,
nullptr);
6070 struct GetAddrInfoState {
6071 ~GetAddrInfoState() {
6072 if (info) { freeaddrinfo(info); }
6076 std::condition_variable result_cv;
6077 bool completed =
false;
6078 int result = EAI_SYSTEM;
6080 std::string service;
6081 struct addrinfo hints;
6082 struct addrinfo *info =
nullptr;
6086 auto state = std::make_shared<GetAddrInfoState>();
6087 if (node) { state->node = node; }
6088 state->service = service;
6089 state->hints = *hints;
6091 std::thread resolve_thread([state]() {
6092 auto thread_result =
6093 getaddrinfo(state->node.c_str(), state->service.c_str(), &state->hints,
6096 std::lock_guard<std::mutex> lock(state->mutex);
6097 state->result = thread_result;
6098 state->completed =
true;
6099 state->result_cv.notify_one();
6103 std::unique_lock<std::mutex> lock(state->mutex);
6105 state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
6106 [&] {
return state->completed; });
6110 resolve_thread.join();
6112 state->info =
nullptr;
6113 return state->result;
6116 resolve_thread.detach();
6121 (void)(timeout_sec);
6122 return getaddrinfo(node, service, hints, res);
6126template <
typename BindOrConnect>
6127socket_t create_socket(
const std::string &host,
const std::string &ip,
int port,
6128 int address_family,
int socket_flags,
bool tcp_nodelay,
6129 bool ipv6_v6only, SocketOptions socket_options,
6130 BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
6132 const char *node =
nullptr;
6133 struct addrinfo hints;
6134 struct addrinfo *result;
6136 memset(&hints, 0,
sizeof(
struct addrinfo));
6137 hints.ai_socktype = SOCK_STREAM;
6138 hints.ai_protocol = IPPROTO_IP;
6143 hints.ai_family = AF_UNSPEC;
6144 hints.ai_flags = AI_NUMERICHOST;
6146 if (!host.empty()) { node = host.c_str(); }
6147 hints.ai_family = address_family;
6148 hints.ai_flags = socket_flags;
6151#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
6152 if (hints.ai_family == AF_UNIX) {
6153 const auto addrlen = host.length();
6154 if (addrlen >
sizeof(sockaddr_un::sun_path)) {
return INVALID_SOCKET; }
6157 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
6160 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
6165 addr.sun_family = AF_UNIX;
6167 auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
6168 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
6170 hints.ai_addr =
reinterpret_cast<sockaddr *
>(&addr);
6171 hints.ai_addrlen =
static_cast<socklen_t
>(
6172 sizeof(addr) -
sizeof(addr.sun_path) + addrlen);
6176 fcntl(sock, F_SETFD, FD_CLOEXEC);
6180 if (socket_options) { socket_options(sock); }
6185 set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
6189 if (!bind_or_connect(sock, hints, dummy)) {
6198 auto service = std::to_string(port);
6200 if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
6202#if defined __linux__ && !defined __ANDROID__
6207 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
6209 for (
auto rp = result; rp; rp = rp->ai_next) {
6213 WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0,
6214 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
6230 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
6236 socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
6238 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
6244#if !defined _WIN32 && !defined SOCK_CLOEXEC
6245 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
6251 if (tcp_nodelay) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
6253 if (rp->ai_family == AF_INET6) {
6254 set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
6257 if (socket_options) { socket_options(sock); }
6261 if (bind_or_connect(sock, *rp, quit)) {
return sock; }
6265 if (quit) {
break; }
6271inline void set_nonblocking(
socket_t sock,
bool nonblocking) {
6273 auto flags = nonblocking ? 1UL : 0UL;
6274 ioctlsocket(sock, FIONBIO, &flags);
6276 auto flags = fcntl(sock, F_GETFL, 0);
6277 fcntl(sock, F_SETFL,
6278 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
6282inline bool is_connection_error() {
6284 return WSAGetLastError() != WSAEWOULDBLOCK;
6286 return errno != EINPROGRESS;
6290inline bool bind_ip_address(
socket_t sock,
const std::string &host) {
6291 struct addrinfo hints;
6292 struct addrinfo *result;
6294 memset(&hints, 0,
sizeof(
struct addrinfo));
6295 hints.ai_family = AF_UNSPEC;
6296 hints.ai_socktype = SOCK_STREAM;
6297 hints.ai_protocol = 0;
6299 if (getaddrinfo_with_timeout(host.c_str(),
"0", &hints, &result, 0)) {
6303 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
6306 for (
auto rp = result; rp; rp = rp->ai_next) {
6307 const auto &ai = *rp;
6308 if (!::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen))) {
6317#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
6322inline std::string if2ip(
int address_family,
const std::string &ifn) {
6323 struct ifaddrs *ifap;
6325 auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
6327 std::string addr_candidate;
6328 for (
auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
6329 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
6330 (AF_UNSPEC == address_family ||
6331 ifa->ifa_addr->sa_family == address_family)) {
6332 if (ifa->ifa_addr->sa_family == AF_INET) {
6333 auto sa =
reinterpret_cast<struct sockaddr_in *
>(ifa->ifa_addr);
6334 char buf[INET_ADDRSTRLEN];
6335 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
6336 return std::string(buf, INET_ADDRSTRLEN);
6338 }
else if (ifa->ifa_addr->sa_family == AF_INET6) {
6339 auto sa =
reinterpret_cast<struct sockaddr_in6 *
>(ifa->ifa_addr);
6340 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
6341 char buf[INET6_ADDRSTRLEN] = {};
6342 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
6344 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
6345 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
6346 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
6348 return std::string(buf, INET6_ADDRSTRLEN);
6355 return addr_candidate;
6359inline socket_t create_client_socket(
6360 const std::string &host,
const std::string &ip,
int port,
6361 int address_family,
bool tcp_nodelay,
bool ipv6_v6only,
6362 SocketOptions socket_options, time_t connection_timeout_sec,
6363 time_t connection_timeout_usec, time_t read_timeout_sec,
6364 time_t read_timeout_usec, time_t write_timeout_sec,
6365 time_t write_timeout_usec,
const std::string &intf, Error &error) {
6366 auto sock = create_socket(
6367 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
6368 std::move(socket_options),
6369 [&](
socket_t sock2,
struct addrinfo &ai,
bool &quit) ->
bool {
6370 if (!intf.empty()) {
6372 auto ip_from_if = if2ip(address_family, intf);
6373 if (ip_from_if.empty()) { ip_from_if = intf; }
6374 if (!bind_ip_address(sock2, ip_from_if)) {
6375 error = Error::BindIPAddress;
6381 set_nonblocking(sock2,
true);
6384 ::connect(sock2, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen));
6387 if (is_connection_error()) {
6388 error = Error::Connection;
6391 error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
6392 connection_timeout_usec);
6393 if (error != Error::Success) {
6394 if (error == Error::ConnectionTimeout) { quit =
true; }
6399 set_nonblocking(sock2,
false);
6400 set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
6402 set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
6403 write_timeout_usec);
6405 error = Error::Success;
6408 connection_timeout_sec);
6411 error = Error::Success;
6413 if (error == Error::Success) { error = Error::Connection; }
6419inline bool get_ip_and_port(
const struct sockaddr_storage &addr,
6420 socklen_t addr_len, std::string &ip,
int &port) {
6421 if (addr.ss_family == AF_INET) {
6422 port = ntohs(
reinterpret_cast<const struct sockaddr_in *
>(&addr)->sin_port);
6423 }
else if (addr.ss_family == AF_INET6) {
6425 ntohs(
reinterpret_cast<const struct sockaddr_in6 *
>(&addr)->sin6_port);
6430 std::array<char, NI_MAXHOST> ipstr{};
6431 if (getnameinfo(
reinterpret_cast<const struct sockaddr *
>(&addr), addr_len,
6432 ipstr.data(),
static_cast<socklen_t
>(ipstr.size()),
nullptr,
6433 0, NI_NUMERICHOST)) {
6441inline void get_local_ip_and_port(
socket_t sock, std::string &ip,
int &port) {
6442 struct sockaddr_storage addr;
6443 socklen_t addr_len =
sizeof(addr);
6444 if (!getsockname(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
6446 get_ip_and_port(addr, addr_len, ip, port);
6450inline void get_remote_ip_and_port(
socket_t sock, std::string &ip,
int &port) {
6451 struct sockaddr_storage addr;
6452 socklen_t addr_len =
sizeof(addr);
6454 if (!getpeername(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
6457 if (addr.ss_family == AF_UNIX) {
6458#if defined(__linux__)
6460 socklen_t len =
sizeof(ucred);
6461 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
6464#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
6466 socklen_t len =
sizeof(pid);
6467 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
6474 get_ip_and_port(addr, addr_len, ip, port);
6482inline constexpr unsigned int str2tag_core(
const char *s,
size_t l,
6489 (((std::numeric_limits<unsigned int>::max)() >> 6) &
6491 static_cast<unsigned char>(*s));
6494inline unsigned int str2tag(
const std::string &s) {
6501 h = (((std::numeric_limits<unsigned int>::max)() >> 6) & h * 33) ^
6502 static_cast<unsigned char>(c);
6509inline constexpr unsigned int operator""_t(
const char *s,
size_t l) {
6510 return str2tag_core(s, l, 0);
6516find_content_type(
const std::string &path,
6517 const std::map<std::string, std::string> &user_data,
6518 const std::string &default_content_type) {
6519 auto ext = file_extension(path);
6521 auto it = user_data.find(ext);
6522 if (it != user_data.end()) {
return it->second; }
6524 using udl::operator
""_t;
6526 switch (str2tag(ext)) {
6527 default:
return default_content_type;
6529 case "css"_t:
return "text/css";
6530 case "csv"_t:
return "text/csv";
6532 case "html"_t:
return "text/html";
6534 case "mjs"_t:
return "text/javascript";
6535 case "txt"_t:
return "text/plain";
6536 case "vtt"_t:
return "text/vtt";
6538 case "apng"_t:
return "image/apng";
6539 case "avif"_t:
return "image/avif";
6540 case "bmp"_t:
return "image/bmp";
6541 case "gif"_t:
return "image/gif";
6542 case "png"_t:
return "image/png";
6543 case "svg"_t:
return "image/svg+xml";
6544 case "webp"_t:
return "image/webp";
6545 case "ico"_t:
return "image/x-icon";
6546 case "tif"_t:
return "image/tiff";
6547 case "tiff"_t:
return "image/tiff";
6549 case "jpeg"_t:
return "image/jpeg";
6551 case "mp4"_t:
return "video/mp4";
6552 case "mpeg"_t:
return "video/mpeg";
6553 case "webm"_t:
return "video/webm";
6555 case "mp3"_t:
return "audio/mp3";
6556 case "mpga"_t:
return "audio/mpeg";
6557 case "weba"_t:
return "audio/webm";
6558 case "wav"_t:
return "audio/wave";
6560 case "otf"_t:
return "font/otf";
6561 case "ttf"_t:
return "font/ttf";
6562 case "woff"_t:
return "font/woff";
6563 case "woff2"_t:
return "font/woff2";
6565 case "7z"_t:
return "application/x-7z-compressed";
6566 case "atom"_t:
return "application/atom+xml";
6567 case "pdf"_t:
return "application/pdf";
6568 case "json"_t:
return "application/json";
6569 case "rss"_t:
return "application/rss+xml";
6570 case "tar"_t:
return "application/x-tar";
6572 case "xhtml"_t:
return "application/xhtml+xml";
6573 case "xslt"_t:
return "application/xslt+xml";
6574 case "xml"_t:
return "application/xml";
6575 case "gz"_t:
return "application/gzip";
6576 case "zip"_t:
return "application/zip";
6577 case "wasm"_t:
return "application/wasm";
6582extract_media_type(
const std::string &content_type,
6583 std::map<std::string, std::string> *params =
nullptr) {
6586 auto media_type = content_type;
6587 auto semicolon_pos = media_type.find(
';');
6588 if (semicolon_pos != std::string::npos) {
6589 auto param_str = media_type.substr(semicolon_pos + 1);
6590 media_type = media_type.substr(0, semicolon_pos);
6594 split(param_str.data(), param_str.data() + param_str.size(),
';',
6595 [&](
const char *b,
const char *e) {
6598 split(b, e,
'=', [&](const char *b2, const char *e2) {
6606 params->emplace(trim_copy(key), trim_double_quotes_copy(val));
6613 return trim_copy(media_type);
6616inline bool can_compress_content_type(
const std::string &content_type) {
6617 using udl::operator
""_t;
6619 auto mime_type = extract_media_type(content_type);
6620 auto tag = str2tag(mime_type);
6623 case "image/svg+xml"_t:
6624 case "application/javascript"_t:
6625 case "application/x-javascript"_t:
6626 case "application/json"_t:
6627 case "application/ld+json"_t:
6628 case "application/xml"_t:
6629 case "application/xhtml+xml"_t:
6630 case "application/rss+xml"_t:
6631 case "application/atom+xml"_t:
6632 case "application/xslt+xml"_t:
6633 case "application/protobuf"_t:
return true;
6635 case "text/event-stream"_t:
return false;
6637 default:
return !mime_type.rfind(
"text/", 0);
6641inline bool parse_quality(
const char *b,
const char *e, std::string &token,
6647 const char *params_b =
nullptr;
6648 std::size_t params_len = 0;
6651 b,
static_cast<std::size_t
>(e - b),
';',
6652 [&](
const char *lb, std::size_t llen,
const char *rb, std::size_t rlen) {
6653 auto r = trim(lb, lb + llen, 0, llen);
6654 if (r.first < r.second) { token.assign(lb + r.first, lb + r.second); }
6659 if (token.empty()) {
return false; }
6660 if (params_len == 0) {
return true; }
6663 bool invalid =
false;
6664 split_find(params_b, params_b + params_len,
';',
6665 (std::numeric_limits<size_t>::max)(),
6666 [&](
const char *pb,
const char *pe) ->
bool {
6668 auto len =
static_cast<size_t>(pe - pb);
6669 if (len < 2) {
return false; }
6670 if ((pb[0] !=
'q' && pb[0] !=
'Q') || pb[1] !=
'=') {
6675 auto r = trim(pb, pe, 2, len);
6676 if (r.first >= r.second) {
6682 auto res = from_chars(pb + r.first, pb + r.second, v);
6683 if (res.ec != std::errc{} || v < 0.0 || v > 1.0) {
6694inline EncodingType encoding_type(
const Request &req,
const Response &res) {
6695 if (!can_compress_content_type(res.get_header_value(
"Content-Type"))) {
6696 return EncodingType::None;
6699 const auto &s = req.get_header_value(
"Accept-Encoding");
6700 if (s.empty()) {
return EncodingType::None; }
6704 EncodingType best = EncodingType::None;
6705 double best_q = 0.0;
6708 auto priority = [](EncodingType t) ->
int {
6710 case EncodingType::Brotli:
return 0;
6711 case EncodingType::Gzip:
return 1;
6712 case EncodingType::Zstd:
return 2;
6718 split(s.data(), s.data() + s.size(),
',', [&](
const char *b,
const char *e) {
6719 double quality = 1.0;
6720 if (!parse_quality(b, e, name, quality)) { return; }
6721 if (quality <= 0.0) {
return; }
6723 EncodingType type = EncodingType::None;
6724#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6725 if (case_ignore::equal(name,
"br")) { type = EncodingType::Brotli; }
6727#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6728 if (type == EncodingType::None && case_ignore::equal(name,
"gzip")) {
6729 type = EncodingType::Gzip;
6732#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6733 if (type == EncodingType::None && case_ignore::equal(name,
"zstd")) {
6734 type = EncodingType::Zstd;
6738 if (type == EncodingType::None) {
return; }
6741 if (quality > best_q ||
6742 (quality == best_q && priority(type) < priority(best))) {
6751inline std::unique_ptr<compressor> make_compressor(EncodingType type) {
6752#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6753 if (type == EncodingType::Gzip) {
6754 return detail::make_unique<gzip_compressor>();
6757#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6758 if (type == EncodingType::Brotli) {
6759 return detail::make_unique<brotli_compressor>();
6762#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6763 if (type == EncodingType::Zstd) {
6764 return detail::make_unique<zstd_compressor>();
6771inline const char *encoding_name(EncodingType type) {
6773 case EncodingType::Gzip:
return "gzip";
6774 case EncodingType::Brotli:
return "br";
6775 case EncodingType::Zstd:
return "zstd";
6780inline bool nocompressor::compress(
const char *data,
size_t data_length,
6781 bool , Callback callback) {
6782 if (!data_length) {
return true; }
6783 return callback(data, data_length);
6786#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6787inline gzip_compressor::gzip_compressor() {
6788 std::memset(&strm_, 0,
sizeof(strm_));
6789 strm_.zalloc = Z_NULL;
6790 strm_.zfree = Z_NULL;
6791 strm_.opaque = Z_NULL;
6793 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
6794 Z_DEFAULT_STRATEGY) == Z_OK;
6797inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
6799inline bool gzip_compressor::compress(
const char *data,
size_t data_length,
6800 bool last, Callback callback) {
6804 constexpr size_t max_avail_in =
6805 (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
6807 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
6808 (std::min)(data_length, max_avail_in));
6809 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
6811 data_length -= strm_.avail_in;
6812 data += strm_.avail_in;
6814 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
6817 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6819 strm_.avail_out =
static_cast<uInt
>(buff.size());
6820 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
6822 ret = deflate(&strm_, flush);
6823 if (ret == Z_STREAM_ERROR) {
return false; }
6825 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
6828 }
while (strm_.avail_out == 0);
6830 assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
6831 (flush == Z_NO_FLUSH && ret == Z_OK));
6832 assert(strm_.avail_in == 0);
6833 }
while (data_length > 0);
6838inline gzip_decompressor::gzip_decompressor() {
6839 std::memset(&strm_, 0,
sizeof(strm_));
6840 strm_.zalloc = Z_NULL;
6841 strm_.zfree = Z_NULL;
6842 strm_.opaque = Z_NULL;
6848 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
6851inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
6853inline bool gzip_decompressor::is_valid()
const {
return is_valid_; }
6855inline bool gzip_decompressor::decompress(
const char *data,
size_t data_length,
6856 Callback callback) {
6862 constexpr size_t max_avail_in =
6863 (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
6865 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
6866 (std::min)(data_length, max_avail_in));
6867 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
6869 data_length -= strm_.avail_in;
6870 data += strm_.avail_in;
6872 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6873 while (strm_.avail_in > 0 && ret == Z_OK) {
6874 strm_.avail_out = static_cast<uInt>(buff.size());
6875 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
6877 ret = inflate(&strm_, Z_NO_FLUSH);
6879 assert(ret != Z_STREAM_ERROR);
6883 case Z_MEM_ERROR: inflateEnd(&strm_); return false;
6886 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
6891 if (ret != Z_OK && ret != Z_STREAM_END) {
return false; }
6893 }
while (data_length > 0);
6899#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6900inline brotli_compressor::brotli_compressor() {
6901 state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr);
6904inline brotli_compressor::~brotli_compressor() {
6905 BrotliEncoderDestroyInstance(state_);
6908inline bool brotli_compressor::compress(
const char *data,
size_t data_length,
6909 bool last, Callback callback) {
6910 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6912 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
6913 auto available_in = data_length;
6914 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
6918 if (BrotliEncoderIsFinished(state_)) {
break; }
6920 if (!available_in) {
break; }
6923 auto available_out = buff.size();
6924 auto next_out = buff.data();
6926 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
6927 &available_out, &next_out,
nullptr)) {
6931 auto output_bytes = buff.size() - available_out;
6933 callback(
reinterpret_cast<const char *
>(buff.data()), output_bytes);
6940inline brotli_decompressor::brotli_decompressor() {
6941 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
6942 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
6943 : BROTLI_DECODER_RESULT_ERROR;
6946inline brotli_decompressor::~brotli_decompressor() {
6947 if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
6950inline bool brotli_decompressor::is_valid()
const {
return decoder_s; }
6952inline bool brotli_decompressor::decompress(
const char *data,
6954 Callback callback) {
6955 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6956 decoder_r == BROTLI_DECODER_RESULT_ERROR) {
6960 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
6961 size_t avail_in = data_length;
6964 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
6966 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6967 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
6968 char *next_out = buff.data();
6969 size_t avail_out = buff.size();
6971 decoder_r = BrotliDecoderDecompressStream(
6972 decoder_s, &avail_in, &next_in, &avail_out,
6973 reinterpret_cast<uint8_t **
>(&next_out), &total_out);
6975 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) {
return false; }
6977 if (!callback(buff.data(), buff.size() - avail_out)) {
return false; }
6980 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6981 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
6985#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6986inline zstd_compressor::zstd_compressor() {
6987 ctx_ = ZSTD_createCCtx();
6988 ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
6991inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
6993inline bool zstd_compressor::compress(
const char *data,
size_t data_length,
6994 bool last, Callback callback) {
6995 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6997 ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
6998 ZSTD_inBuffer input = {data, data_length, 0};
7003 size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
7005 if (ZSTD_isError(remaining)) {
return false; }
7007 if (!callback(buff.data(), output.pos)) {
return false; }
7009 finished = last ? (remaining == 0) : (input.pos == input.size);
7011 }
while (!finished);
7016inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
7018inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
7020inline bool zstd_decompressor::is_valid()
const {
return ctx_ !=
nullptr; }
7022inline bool zstd_decompressor::decompress(
const char *data,
size_t data_length,
7023 Callback callback) {
7024 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
7025 ZSTD_inBuffer input = {data, data_length, 0};
7027 while (input.pos < input.size) {
7029 size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
7031 if (ZSTD_isError(remaining)) {
return false; }
7033 if (!callback(buff.data(), output.pos)) {
return false; }
7040inline std::unique_ptr<decompressor>
7041create_decompressor(
const std::string &encoding) {
7042 std::unique_ptr<decompressor> decompressor;
7044 if (encoding ==
"gzip" || encoding ==
"deflate") {
7045#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7046 decompressor = detail::make_unique<gzip_decompressor>();
7048 }
else if (encoding.find(
"br") != std::string::npos) {
7049#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7050 decompressor = detail::make_unique<brotli_decompressor>();
7052 }
else if (encoding ==
"zstd" || encoding.find(
"zstd") != std::string::npos) {
7053#ifdef CPPHTTPLIB_ZSTD_SUPPORT
7054 decompressor = detail::make_unique<zstd_decompressor>();
7058 return decompressor;
7063inline std::pair<std::unique_ptr<compressor>,
const char *>
7064create_compressor() {
7065#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7066 return {detail::make_unique<brotli_compressor>(),
"br"};
7067#elif defined(CPPHTTPLIB_ZLIB_SUPPORT)
7068 return {detail::make_unique<gzip_compressor>(),
"gzip"};
7069#elif defined(CPPHTTPLIB_ZSTD_SUPPORT)
7070 return {detail::make_unique<zstd_compressor>(),
"zstd"};
7072 return {
nullptr,
nullptr};
7076inline bool is_prohibited_header_name(
const std::string &name) {
7077 using udl::operator
""_t;
7079 switch (str2tag(name)) {
7080 case "REMOTE_ADDR"_t:
7081 case "REMOTE_PORT"_t:
7082 case "LOCAL_ADDR"_t:
7083 case "LOCAL_PORT"_t:
return true;
7084 default:
return false;
7088inline bool has_header(
const Headers &headers,
const std::string &key) {
7089 if (is_prohibited_header_name(key)) {
return false; }
7090 return headers.find(key) != headers.end();
7093inline const char *get_header_value(
const Headers &headers,
7094 const std::string &key,
const char *def,
7096 if (is_prohibited_header_name(key)) {
7097#ifndef CPPHTTPLIB_NO_EXCEPTIONS
7098 std::string msg =
"Prohibited header name '" + key +
"' is specified.";
7099 throw std::invalid_argument(msg);
7105 auto rng = headers.equal_range(key);
7106 auto it = rng.first;
7107 std::advance(it,
static_cast<ssize_t
>(
id));
7108 if (it != rng.second) {
return it->second.c_str(); }
7112inline size_t get_header_value_count(
const Headers &headers,
7113 const std::string &key) {
7114 auto r = headers.equal_range(key);
7115 return static_cast<size_t>(std::distance(r.first, r.second));
7118template <
typename Map>
7119inline typename Map::mapped_type
7120get_multimap_value(
const Map &m,
const std::string &key,
size_t id) {
7121 auto rng = m.equal_range(key);
7122 auto it = rng.first;
7123 std::advance(it,
static_cast<ssize_t
>(
id));
7124 if (it != rng.second) {
return it->second; }
7125 return typename Map::mapped_type();
7128inline void set_header(Headers &headers,
const std::string &key,
7129 const std::string &val) {
7130 if (fields::is_field_name(key) && fields::is_field_value(val)) {
7131 headers.emplace(key, val);
7135inline bool read_headers(Stream &strm, Headers &headers) {
7136 const auto bufsiz = 2048;
7138 stream_line_reader line_reader(strm, buf, bufsiz);
7140 size_t header_count = 0;
7143 if (!line_reader.getline()) {
return false; }
7146 auto line_terminator_len = 2;
7147 if (line_reader.end_with_crlf()) {
7149 if (line_reader.size() == 2) {
break; }
7151#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7153 if (line_reader.size() == 1) {
break; }
7154 line_terminator_len = 1;
7166 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
7168 if (!parse_header(line_reader.ptr(), end,
7169 [&](
const std::string &key,
const std::string &val) {
7170 headers.emplace(key, val);
7180 auto cl_range = headers.equal_range(
"Content-Length");
7181 if (cl_range.first != cl_range.second) {
7182 const auto &first_val = cl_range.first->second;
7183 for (
auto it = std::next(cl_range.first); it != cl_range.second; ++it) {
7184 if (it->second != first_val) {
return false; }
7191inline bool read_websocket_upgrade_response(Stream &strm,
7192 const std::string &expected_accept,
7193 std::string &selected_subprotocol) {
7195 const auto bufsiz = 2048;
7197 stream_line_reader line_reader(strm, buf, bufsiz);
7198 if (!line_reader.getline()) {
return false; }
7201 auto line = std::string(line_reader.ptr(), line_reader.size());
7202 if (line.find(
"HTTP/1.1 101") == std::string::npos) {
return false; }
7206 if (!read_headers(strm, headers)) {
return false; }
7209 auto upgrade_it = headers.find(
"Upgrade");
7210 if (upgrade_it == headers.end()) {
return false; }
7211 auto upgrade_val = case_ignore::to_lower(upgrade_it->second);
7212 if (upgrade_val !=
"websocket") {
return false; }
7215 auto connection_it = headers.find(
"Connection");
7216 if (connection_it == headers.end()) {
return false; }
7217 auto connection_val = case_ignore::to_lower(connection_it->second);
7218 if (connection_val.find(
"upgrade") == std::string::npos) {
return false; }
7221 auto it = headers.find(
"Sec-WebSocket-Accept");
7222 if (it == headers.end() || it->second != expected_accept) {
return false; }
7225 auto proto_it = headers.find(
"Sec-WebSocket-Protocol");
7226 if (proto_it != headers.end()) { selected_subprotocol = proto_it->second; }
7231enum class ReadContentResult {
7237inline ReadContentResult read_content_with_length(
7238 Stream &strm,
size_t len, DownloadProgress progress,
7239 ContentReceiverWithProgress out,
7240 size_t payload_max_length = (std::numeric_limits<size_t>::max)()) {
7243 detail::BodyReader br;
7245 br.has_content_length =
true;
7246 br.content_length = len;
7247 br.payload_max_length = payload_max_length;
7250 br.last_error = Error::Success;
7254 auto read_len =
static_cast<size_t>(len - r);
7256 auto n = detail::read_body_content(&strm, br, buf, to_read);
7259 if (br.last_error == Error::ExceedMaxPayloadSize) {
7260 return ReadContentResult::PayloadTooLarge;
7262 return ReadContentResult::Error;
7265 if (!out(buf,
static_cast<size_t>(n), r, len)) {
7266 return ReadContentResult::Error;
7268 r +=
static_cast<size_t>(n);
7271 if (!progress(r, len)) {
return ReadContentResult::Error; }
7275 return ReadContentResult::Success;
7278inline ReadContentResult
7279read_content_without_length(Stream &strm,
size_t payload_max_length,
7280 ContentReceiverWithProgress out) {
7285 if (n == 0) {
return ReadContentResult::Success; }
7286 if (n < 0) {
return ReadContentResult::Error; }
7289 if (r > payload_max_length ||
7290 payload_max_length - r <
static_cast<size_t>(n)) {
7291 return ReadContentResult::PayloadTooLarge;
7294 if (!out(buf,
static_cast<size_t>(n), r, 0)) {
7295 return ReadContentResult::Error;
7297 r +=
static_cast<size_t>(n);
7300 return ReadContentResult::Success;
7303template <
typename T>
7304inline ReadContentResult read_content_chunked(Stream &strm, T &x,
7305 size_t payload_max_length,
7306 ContentReceiverWithProgress out) {
7307 detail::ChunkedDecoder dec(strm);
7310 size_t total_len = 0;
7313 size_t chunk_offset = 0;
7314 size_t chunk_total = 0;
7315 auto n = dec.read_payload(buf,
sizeof(buf), chunk_offset, chunk_total);
7316 if (n < 0) {
return ReadContentResult::Error; }
7319 if (!dec.parse_trailers_into(x.trailers, x.headers)) {
7320 return ReadContentResult::Error;
7322 return ReadContentResult::Success;
7325 if (total_len > payload_max_length ||
7326 payload_max_length - total_len <
static_cast<size_t>(n)) {
7327 return ReadContentResult::PayloadTooLarge;
7330 if (!out(buf,
static_cast<size_t>(n), chunk_offset, chunk_total)) {
7331 return ReadContentResult::Error;
7334 total_len +=
static_cast<size_t>(n);
7338inline bool is_chunked_transfer_encoding(
const Headers &headers) {
7339 return case_ignore::equal(
7340 get_header_value(headers,
"Transfer-Encoding",
"", 0),
"chunked");
7343template <
typename T,
typename U>
7344bool prepare_content_receiver(T &x,
int &status,
7345 ContentReceiverWithProgress receiver,
7346 bool decompress,
size_t payload_max_length,
7347 bool &exceed_payload_max_length, U callback) {
7349 std::string encoding = x.get_header_value(
"Content-Encoding");
7350 std::unique_ptr<decompressor> decompressor;
7352 if (!encoding.empty()) {
7353 decompressor = detail::create_decompressor(encoding);
7354 if (!decompressor) {
7356 status = StatusCode::UnsupportedMediaType_415;
7362 if (decompressor->is_valid()) {
7363 size_t decompressed_size = 0;
7364 ContentReceiverWithProgress out = [&](
const char *buf,
size_t n,
7365 size_t off,
size_t len) {
7366 return decompressor->decompress(
7367 buf, n, [&](
const char *buf2,
size_t n2) {
7370 if (payload_max_length > 0 &&
7371 (decompressed_size >= payload_max_length ||
7372 n2 > payload_max_length - decompressed_size)) {
7373 exceed_payload_max_length =
true;
7376 decompressed_size += n2;
7377 return receiver(buf2, n2, off, len);
7380 return callback(std::move(out));
7382 status = StatusCode::InternalServerError_500;
7388 ContentReceiverWithProgress out = [&](
const char *buf,
size_t n,
size_t off,
7390 return receiver(buf, n, off, len);
7392 return callback(std::move(out));
7395template <
typename T>
7396bool read_content(Stream &strm, T &x,
size_t payload_max_length,
int &status,
7397 DownloadProgress progress,
7398 ContentReceiverWithProgress receiver,
bool decompress) {
7399 bool exceed_payload_max_length =
false;
7400 return prepare_content_receiver(
7401 x, status, std::move(receiver), decompress, payload_max_length,
7402 exceed_payload_max_length, [&](
const ContentReceiverWithProgress &out) {
7408 if (is_chunked_transfer_encoding(x.headers)) {
7409 auto result = read_content_chunked(strm, x, payload_max_length, out);
7410 if (result == ReadContentResult::Success) {
7412 }
else if (result == ReadContentResult::PayloadTooLarge) {
7413 exceed_payload_max_length =
true;
7418 }
else if (!has_header(x.headers,
"Content-Length")) {
7420 read_content_without_length(strm, payload_max_length, out);
7421 if (result == ReadContentResult::Success) {
7423 }
else if (result == ReadContentResult::PayloadTooLarge) {
7424 exceed_payload_max_length =
true;
7430 auto is_invalid_value =
false;
7431 auto len = get_header_value_u64(x.headers,
"Content-Length",
7432 (std::numeric_limits<size_t>::max)(),
7433 0, is_invalid_value);
7435 if (is_invalid_value) {
7437 }
else if (len > 0) {
7438 auto result = read_content_with_length(
7439 strm, len, std::move(progress), out, payload_max_length);
7440 ret = (result == ReadContentResult::Success);
7441 if (result == ReadContentResult::PayloadTooLarge) {
7442 exceed_payload_max_length =
true;
7448 status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
7449 : StatusCode::BadRequest_400;
7455inline ssize_t write_request_line(Stream &strm,
const std::string &method,
7456 const std::string &path) {
7457 std::string s = method;
7460 s +=
" HTTP/1.1\r\n";
7461 return strm.write(s.data(), s.size());
7464inline ssize_t write_response_line(Stream &strm,
int status) {
7465 std::string s =
"HTTP/1.1 ";
7466 s += std::to_string(status);
7468 s += httplib::status_message(status);
7470 return strm.write(s.data(), s.size());
7473inline ssize_t write_headers(Stream &strm,
const Headers &headers) {
7474 ssize_t write_len = 0;
7475 for (
const auto &x : headers) {
7482 auto len = strm.write(s.data(), s.size());
7483 if (len < 0) {
return len; }
7486 auto len = strm.write(
"\r\n");
7487 if (len < 0) {
return len; }
7492inline bool write_data(Stream &strm,
const char *d,
size_t l) {
7494 while (offset < l) {
7495 auto length = strm.write(d + offset, l - offset);
7496 if (length < 0) {
return false; }
7497 offset +=
static_cast<size_t>(length);
7502template <
typename T>
7503inline bool write_content_with_progress(Stream &strm,
7504 const ContentProvider &content_provider,
7505 size_t offset,
size_t length,
7507 const UploadProgress &upload_progress,
7509 size_t end_offset = offset + length;
7510 size_t start_offset = offset;
7514 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
7516 if (write_data(strm, d, l)) {
7519 if (upload_progress && length > 0) {
7520 size_t current_written = offset - start_offset;
7521 if (!upload_progress(current_written, length)) {
7533 data_sink.is_writable = [&]() ->
bool {
return strm.is_peer_alive(); };
7535 while (offset < end_offset && !is_shutting_down()) {
7536 if (!strm.wait_writable() || !strm.is_peer_alive()) {
7537 error = Error::Write;
7539 }
else if (!content_provider(offset, end_offset - offset, data_sink)) {
7540 error = Error::Canceled;
7543 error = Error::Write;
7548 if (offset < end_offset) {
7549 error = Error::Write;
7553 error = Error::Success;
7557template <
typename T>
7558inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
7559 size_t offset,
size_t length, T is_shutting_down,
7561 return write_content_with_progress<T>(strm, content_provider, offset, length,
7562 is_shutting_down,
nullptr, error);
7565template <
typename T>
7566inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
7567 size_t offset,
size_t length,
7568 const T &is_shutting_down) {
7569 auto error = Error::Success;
7570 return write_content(strm, content_provider, offset, length, is_shutting_down,
7574template <
typename T>
7576write_content_without_length(Stream &strm,
7577 const ContentProvider &content_provider,
7578 const T &is_shutting_down) {
7580 auto data_available =
true;
7584 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
7587 if (!write_data(strm, d, l)) { ok =
false; }
7592 data_sink.is_writable = [&]() ->
bool {
return strm.is_peer_alive(); };
7594 data_sink.done = [&](void) { data_available =
false; };
7596 while (data_available && !is_shutting_down()) {
7597 if (!strm.wait_writable() || !strm.is_peer_alive()) {
7599 }
else if (!content_provider(offset, 0, data_sink)) {
7605 return !data_available;
7609template <
typename T,
typename U>
7611write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
7612 const T &is_shutting_down, U &compressor, Error &error) {
7614 auto data_available =
true;
7618 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
7620 data_available = l > 0;
7623 std::string payload;
7624 if (compressor.compress(d, l,
false,
7625 [&](
const char *data,
size_t data_len) {
7626 payload.append(data, data_len);
7629 if (!payload.empty()) {
7632 from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
7633 if (!write_data(strm, chunk.data(), chunk.size())) { ok =
false; }
7642 data_sink.is_writable = [&]() ->
bool {
return strm.is_peer_alive(); };
7644 auto done_with_trailer = [&](
const Headers *trailer) {
7645 if (!ok) {
return; }
7647 data_available =
false;
7649 std::string payload;
7650 if (!compressor.compress(
nullptr, 0,
true,
7651 [&](
const char *data,
size_t data_len) {
7652 payload.append(data, data_len);
7659 if (!payload.empty()) {
7661 auto chunk = from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
7662 if (!write_data(strm, chunk.data(), chunk.size())) {
7668 constexpr const char done_marker[] =
"0\r\n";
7669 if (!write_data(strm, done_marker, str_len(done_marker))) { ok =
false; }
7673 for (
const auto &kv : *trailer) {
7674 std::string field_line = kv.first +
": " + kv.second +
"\r\n";
7675 if (!write_data(strm, field_line.data(), field_line.size())) {
7681 constexpr const char crlf[] =
"\r\n";
7682 if (!write_data(strm, crlf, str_len(crlf))) { ok =
false; }
7685 data_sink.done = [&](void) { done_with_trailer(
nullptr); };
7687 data_sink.done_with_trailer = [&](
const Headers &trailer) {
7688 done_with_trailer(&trailer);
7691 while (data_available && !is_shutting_down()) {
7692 if (!strm.wait_writable() || !strm.is_peer_alive()) {
7693 error = Error::Write;
7695 }
else if (!content_provider(offset, 0, data_sink)) {
7696 error = Error::Canceled;
7699 error = Error::Write;
7704 if (data_available) {
7705 error = Error::Write;
7709 error = Error::Success;
7713template <
typename T,
typename U>
7714inline bool write_content_chunked(Stream &strm,
7715 const ContentProvider &content_provider,
7716 const T &is_shutting_down, U &compressor) {
7717 auto error = Error::Success;
7718 return write_content_chunked(strm, content_provider, is_shutting_down,
7722template <
typename T>
7723inline bool redirect(T &cli, Request &req, Response &res,
7724 const std::string &path,
const std::string &location,
7726 Request new_req = req;
7727 new_req.path = path;
7728 new_req.redirect_count_ -= 1;
7730 if (res.status == StatusCode::SeeOther_303 &&
7731 (req.method !=
"GET" && req.method !=
"HEAD")) {
7732 new_req.method =
"GET";
7733 new_req.body.clear();
7734 new_req.headers.clear();
7739 auto ret = cli.send(new_req, new_res, error);
7741 req = std::move(new_req);
7742 res = std::move(new_res);
7744 if (res.location.empty()) { res.location = location; }
7749inline std::string params_to_query_str(
const Params ¶ms) {
7752 for (
auto it = params.begin(); it != params.end(); ++it) {
7753 if (it != params.begin()) { query +=
'&'; }
7754 query += encode_query_component(it->first);
7756 query += encode_query_component(it->second);
7761inline void parse_query_text(
const char *data, std::size_t size,
7763 std::set<std::string> cache;
7764 split(data, data + size,
'&', [&](
const char *b,
const char *e) {
7765 std::string kv(b, e);
7766 if (cache.find(kv) != cache.end()) {
return; }
7767 cache.insert(std::move(kv));
7771 divide(b,
static_cast<std::size_t
>(e - b),
'=',
7772 [&](
const char *lhs_data, std::size_t lhs_size,
const char *rhs_data,
7773 std::size_t rhs_size) {
7774 key.assign(lhs_data, lhs_size);
7775 val.assign(rhs_data, rhs_size);
7779 params.emplace(decode_query_component(key), decode_query_component(val));
7784inline void parse_query_text(
const std::string &s, Params ¶ms) {
7785 parse_query_text(s.data(), s.size(), params);
7792inline std::string normalize_query_string(
const std::string &query) {
7794 split(query.data(), query.data() + query.size(),
'&',
7795 [&](
const char *b,
const char *e) {
7798 divide(b, static_cast<std::size_t>(e - b),
'=',
7799 [&](const char *lhs_data, std::size_t lhs_size,
7800 const char *rhs_data, std::size_t rhs_size) {
7801 key.assign(lhs_data, lhs_size);
7802 val.assign(rhs_data, rhs_size);
7806 auto dec_key = decode_query_component(key);
7807 auto dec_val = decode_query_component(val);
7809 if (!result.empty()) { result +=
'&'; }
7810 result += encode_query_component(dec_key);
7811 if (!val.empty() || std::find(b, e,
'=') != e) {
7813 result += encode_query_component(dec_val);
7820inline bool parse_multipart_boundary(
const std::string &content_type,
7821 std::string &boundary) {
7822 std::map<std::string, std::string> params;
7823 extract_media_type(content_type, ¶ms);
7824 auto it = params.find(
"boundary");
7825 if (it == params.end()) {
return false; }
7826 boundary = it->second;
7827 return !boundary.empty();
7830inline void parse_disposition_params(
const std::string &s, Params ¶ms) {
7831 std::set<std::string> cache;
7832 split(s.data(), s.data() + s.size(),
';', [&](
const char *b,
const char *e) {
7833 std::string kv(b, e);
7834 if (cache.find(kv) != cache.end()) { return; }
7839 split(b, e,
'=', [&](
const char *b2,
const char *e2) {
7848 params.emplace(trim_double_quotes_copy((key)),
7849 trim_double_quotes_copy((val)));
7854#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7855inline bool parse_range_header(
const std::string &s, Ranges &ranges) {
7857inline bool parse_range_header(
const std::string &s, Ranges &ranges)
try {
7859 auto is_valid = [](
const std::string &str) {
7860 return std::all_of(str.cbegin(), str.cend(),
7861 [](
unsigned char c) { return std::isdigit(c); });
7864 if (s.size() > 7 && s.compare(0, 6,
"bytes=") == 0) {
7865 const auto pos =
static_cast<size_t>(6);
7866 const auto len =
static_cast<size_t>(s.size() - 6);
7867 auto all_valid_ranges =
true;
7868 split(&s[pos], &s[pos + len],
',', [&](
const char *b,
const char *e) {
7869 if (!all_valid_ranges) {
return; }
7871 const auto it = std::find(b, e,
'-');
7873 all_valid_ranges =
false;
7877 const auto lhs = std::string(b, it);
7878 const auto rhs = std::string(it + 1, e);
7879 if (!is_valid(lhs) || !is_valid(rhs)) {
7880 all_valid_ranges =
false;
7887 auto res = detail::from_chars(lhs.data(), lhs.data() + lhs.size(), v);
7888 if (res.ec == std::errc{}) { first = v; }
7894 auto res = detail::from_chars(rhs.data(), rhs.data() + rhs.size(), v);
7895 if (res.ec == std::errc{}) { last = v; }
7898 if ((first == -1 && last == -1) ||
7899 (first != -1 && last != -1 && first > last)) {
7900 all_valid_ranges =
false;
7904 ranges.emplace_back(first, last);
7906 return all_valid_ranges && !ranges.empty();
7909#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7912}
catch (...) {
return false; }
7915inline bool parse_accept_header(
const std::string &s,
7916 std::vector<std::string> &content_types) {
7917 content_types.clear();
7920 if (s.empty()) {
return true; }
7923 if (s.front() ==
',' || s.back() ==
',' ||
7924 s.find(
",,") != std::string::npos) {
7928 struct AcceptEntry {
7929 std::string media_type;
7934 std::vector<AcceptEntry> entries;
7936 bool has_invalid_entry =
false;
7939 split(s.data(), s.data() + s.size(),
',', [&](
const char *b,
const char *e) {
7940 std::string entry(b, e);
7941 entry = trim_copy(entry);
7943 if (entry.empty()) {
7944 has_invalid_entry = true;
7948 AcceptEntry accept_entry;
7949 accept_entry.order = order++;
7951 if (!parse_quality(entry.data(), entry.data() + entry.size(),
7952 accept_entry.media_type, accept_entry.quality)) {
7953 has_invalid_entry = true;
7958 accept_entry.media_type = extract_media_type(accept_entry.media_type);
7961 if (accept_entry.media_type.empty()) {
7962 has_invalid_entry = true;
7967 if (accept_entry.media_type !=
"*" &&
7968 accept_entry.media_type.find(
'/') == std::string::npos) {
7969 has_invalid_entry = true;
7973 entries.push_back(std::move(accept_entry));
7977 if (has_invalid_entry) {
return false; }
7980 std::sort(entries.begin(), entries.end(),
7981 [](
const AcceptEntry &a,
const AcceptEntry &b) {
7982 if (a.quality != b.quality) {
7983 return a.quality > b.quality;
7985 return a.order < b.order;
7989 content_types.reserve(entries.size());
7990 for (
auto &entry : entries) {
7991 content_types.push_back(std::move(entry.media_type));
7997class FormDataParser {
7999 FormDataParser() =
default;
8001 void set_boundary(std::string &&boundary) {
8002 boundary_ = std::move(boundary);
8003 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
8004 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
8007 bool is_valid()
const {
return is_valid_; }
8009 bool parse(
const char *buf,
size_t n,
const FormDataHeader &header_callback,
8010 const ContentReceiver &content_callback) {
8014 while (buf_size() > 0) {
8017 auto pos = buf_find(dash_boundary_crlf_);
8018 if (pos == buf_size()) {
return true; }
8019 buf_erase(pos + dash_boundary_crlf_.size());
8029 auto pos = buf_find(crlf_);
8031 while (pos < buf_size()) {
8034 if (!header_callback(file_)) {
8038 buf_erase(crlf_.size());
8043 const auto header = buf_head(pos);
8045 if (!parse_header(header.data(), header.data() + header.size(),
8046 [&](
const std::string &,
const std::string &) {})) {
8053 header.data(), header.data() + header.size(),
8054 [&](
const std::string &key,
const std::string &val) {
8055 file_.headers.emplace(key, val);
8061 constexpr const char header_content_type[] =
"Content-Type:";
8063 if (start_with_case_ignore(header, header_content_type)) {
8064 file_.content_type =
8065 trim_copy(header.substr(str_len(header_content_type)));
8067 std::string disposition_params;
8068 if (parse_content_disposition(header, disposition_params)) {
8070 parse_disposition_params(disposition_params, params);
8072 auto it = params.find(
"name");
8073 if (it != params.end()) {
8074 file_.name = it->second;
8080 it = params.find(
"filename");
8081 if (it != params.end()) { file_.filename = it->second; }
8083 it = params.find(
"filename*");
8084 if (it != params.end()) {
8086 const auto &val = it->second;
8087 constexpr const char utf8_prefix[] =
"UTF-8''";
8088 constexpr size_t prefix_len = str_len(utf8_prefix);
8089 if (val.size() > prefix_len &&
8090 start_with_case_ignore(val, utf8_prefix)) {
8091 file_.filename = decode_path_component(
8092 val.substr(prefix_len));
8100 buf_erase(pos + crlf_.size());
8101 pos = buf_find(crlf_);
8103 if (state_ != 3) {
return true; }
8107 if (crlf_dash_boundary_.size() > buf_size()) {
return true; }
8108 auto pos = buf_find(crlf_dash_boundary_);
8109 if (pos < buf_size()) {
8110 if (!content_callback(buf_data(), pos)) {
8114 buf_erase(pos + crlf_dash_boundary_.size());
8117 auto len = buf_size() - crlf_dash_boundary_.size();
8119 if (!content_callback(buf_data(), len)) {
8130 if (crlf_.size() > buf_size()) {
return true; }
8131 if (buf_start_with(crlf_)) {
8132 buf_erase(crlf_.size());
8135 if (dash_.size() > buf_size()) {
return true; }
8136 if (buf_start_with(dash_)) {
8137 buf_erase(dash_.size());
8139 buf_erase(buf_size());
8153 void clear_file_info() {
8155 file_.filename.clear();
8156 file_.content_type.clear();
8157 file_.headers.clear();
8160 bool start_with_case_ignore(
const std::string &a,
const char *b,
8161 size_t offset = 0)
const {
8162 const auto b_len = strlen(b);
8163 if (a.size() < offset + b_len) {
return false; }
8164 for (
size_t i = 0; i < b_len; i++) {
8165 if (case_ignore::to_lower(a[offset + i]) != case_ignore::to_lower(b[i])) {
8174 bool parse_content_disposition(
const std::string &header,
8175 std::string ¶ms_out)
const {
8176 constexpr const char prefix[] =
"Content-Disposition:";
8177 constexpr size_t prefix_len = str_len(prefix);
8179 if (!start_with_case_ignore(header, prefix)) {
return false; }
8182 auto pos = prefix_len;
8183 while (pos < header.size() && (header[pos] ==
' ' || header[pos] ==
'\t')) {
8188 constexpr const char form_data[] =
"form-data;";
8189 constexpr size_t form_data_len = str_len(form_data);
8190 if (!start_with_case_ignore(header, form_data, pos)) {
return false; }
8191 pos += form_data_len;
8194 while (pos < header.size() && (header[pos] ==
' ' || header[pos] ==
'\t')) {
8198 params_out = header.substr(pos);
8202 const std::string dash_ =
"--";
8203 const std::string crlf_ =
"\r\n";
8204 std::string boundary_;
8205 std::string dash_boundary_crlf_;
8206 std::string crlf_dash_boundary_;
8209 bool is_valid_ =
false;
8213 bool start_with(
const std::string &a,
size_t spos,
size_t epos,
8214 const std::string &b)
const {
8215 if (epos - spos < b.size()) {
return false; }
8216 for (
size_t i = 0; i < b.size(); i++) {
8217 if (a[i + spos] != b[i]) {
return false; }
8222 size_t buf_size()
const {
return buf_epos_ - buf_spos_; }
8224 const char *buf_data()
const {
return &buf_[buf_spos_]; }
8226 std::string buf_head(
size_t l)
const {
return buf_.substr(buf_spos_, l); }
8228 bool buf_start_with(
const std::string &s)
const {
8229 return start_with(buf_, buf_spos_, buf_epos_, s);
8232 size_t buf_find(
const std::string &s)
const {
8235 size_t off = buf_spos_;
8236 while (off < buf_epos_) {
8239 if (pos == buf_epos_) {
return buf_size(); }
8240 if (buf_[pos] == c) {
break; }
8244 auto remaining_size = buf_epos_ - pos;
8245 if (s.size() > remaining_size) {
return buf_size(); }
8247 if (start_with(buf_, pos, buf_epos_, s)) {
return pos - buf_spos_; }
8255 void buf_append(
const char *data,
size_t n) {
8256 auto remaining_size = buf_size();
8257 if (remaining_size > 0 && buf_spos_ > 0) {
8258 for (
size_t i = 0; i < remaining_size; i++) {
8259 buf_[i] = buf_[buf_spos_ + i];
8263 buf_epos_ = remaining_size;
8265 if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
8267 for (
size_t i = 0; i < n; i++) {
8268 buf_[buf_epos_ + i] = data[i];
8273 void buf_erase(
size_t size) { buf_spos_ += size; }
8276 size_t buf_spos_ = 0;
8277 size_t buf_epos_ = 0;
8280inline std::string random_string(
size_t length) {
8281 constexpr const char data[] =
8282 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
8284 thread_local auto engine([]() {
8288 std::random_device seed_gen;
8290 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
8291 return std::mt19937(seed_sequence);
8295 for (
size_t i = 0; i < length; i++) {
8296 result += data[engine() % (
sizeof(data) - 1)];
8301inline std::string make_multipart_data_boundary() {
8302 return "--cpp-httplib-multipart-data-" + detail::random_string(16);
8305inline bool is_multipart_boundary_chars_valid(
const std::string &boundary) {
8307 for (
size_t i = 0; i < boundary.size(); i++) {
8308 auto c = boundary[i];
8309 if (!std::isalnum(
static_cast<unsigned char>(c)) && c !=
'-' && c !=
'_') {
8317template <
typename T>
8319serialize_multipart_formdata_item_begin(
const T &item,
8320 const std::string &boundary) {
8321 std::string body =
"--" + boundary +
"\r\n";
8322 body +=
"Content-Disposition: form-data; name=\"" + item.name +
"\"";
8323 if (!item.filename.empty()) {
8324 body +=
"; filename=\"" + item.filename +
"\"";
8327 if (!item.content_type.empty()) {
8328 body +=
"Content-Type: " + item.content_type +
"\r\n";
8335inline std::string serialize_multipart_formdata_item_end() {
return "\r\n"; }
8338serialize_multipart_formdata_finish(
const std::string &boundary) {
8339 return "--" + boundary +
"--\r\n";
8343serialize_multipart_formdata_get_content_type(
const std::string &boundary) {
8344 return "multipart/form-data; boundary=" + boundary;
8348serialize_multipart_formdata(
const UploadFormDataItems &items,
8349 const std::string &boundary,
bool finish =
true) {
8352 for (
const auto &item : items) {
8353 body += serialize_multipart_formdata_item_begin(item, boundary);
8354 body += item.content + serialize_multipart_formdata_item_end();
8357 if (finish) { body += serialize_multipart_formdata_finish(boundary); }
8362inline size_t get_multipart_content_length(
const UploadFormDataItems &items,
8363 const std::string &boundary) {
8365 for (
const auto &item : items) {
8366 total += serialize_multipart_formdata_item_begin(item, boundary).size();
8367 total += item.content.size();
8368 total += serialize_multipart_formdata_item_end().size();
8370 total += serialize_multipart_formdata_finish(boundary).size();
8374struct MultipartSegment {
8381inline ContentProvider
8382make_multipart_content_provider(
const UploadFormDataItems &items,
8383 const std::string &boundary) {
8385 std::vector<std::string> owned;
8386 owned.reserve(items.size() + 1);
8387 for (
const auto &item : items)
8388 owned.push_back(serialize_multipart_formdata_item_begin(item, boundary));
8389 owned.push_back(serialize_multipart_formdata_finish(boundary));
8392 std::vector<MultipartSegment> segs;
8393 segs.reserve(items.size() * 3 + 1);
8394 static const char crlf[] =
"\r\n";
8395 for (
size_t i = 0; i < items.size(); i++) {
8396 segs.push_back({owned[i].data(), owned[i].size()});
8397 segs.push_back({items[i].content.data(), items[i].content.size()});
8398 segs.push_back({crlf, 2});
8400 segs.push_back({owned.back().data(), owned.back().size()});
8402 struct MultipartState {
8403 std::vector<std::string> owned;
8404 std::vector<MultipartSegment> segs;
8407 auto state = std::make_shared<MultipartState>();
8408 state->owned = std::move(owned);
8411 state->segs = std::move(segs);
8413 return [state](
size_t offset,
size_t length, DataSink &sink) ->
bool {
8416 auto &buf = state->buf;
8417 auto buf_size = buf.size();
8419 size_t remaining = length;
8424 for (; seg_idx < state->segs.size(); seg_idx++) {
8425 const auto &seg = state->segs[seg_idx];
8426 if (seg.size > 0 && offset - pos < seg.size) {
break; }
8430 size_t seg_offset = (seg_idx < state->segs.size()) ? offset - pos : 0;
8432 for (; seg_idx < state->segs.size() && remaining > 0; seg_idx++) {
8433 const auto &seg = state->segs[seg_idx];
8434 size_t available = seg.size - seg_offset;
8435 size_t to_copy = (std::min)(available, remaining);
8436 const char *src = seg.data + seg_offset;
8439 while (to_copy > 0) {
8440 size_t space = buf_size - buf_len;
8441 size_t chunk = (std::min)(to_copy, space);
8442 std::memcpy(buf.data() + buf_len, src, chunk);
8448 if (buf_len == buf_size) {
8449 if (!sink.write(buf.data(), buf_len)) {
return false; }
8455 if (buf_len > 0) {
return sink.write(buf.data(), buf_len); }
8460inline void coalesce_ranges(Ranges &ranges,
size_t content_length) {
8461 if (ranges.size() <= 1)
return;
8464 std::sort(ranges.begin(), ranges.end(),
8465 [](
const Range &a,
const Range &b) { return a.first < b.first; });
8468 coalesced.reserve(ranges.size());
8470 for (
auto &r : ranges) {
8471 auto first_pos = r.first;
8472 auto last_pos = r.second;
8475 if (first_pos == -1 && last_pos == -1) {
8477 last_pos =
static_cast<ssize_t
>(content_length);
8480 if (first_pos == -1) {
8481 first_pos =
static_cast<ssize_t
>(content_length) - last_pos;
8482 last_pos =
static_cast<ssize_t
>(content_length) - 1;
8485 if (last_pos == -1 || last_pos >=
static_cast<ssize_t
>(content_length)) {
8486 last_pos =
static_cast<ssize_t
>(content_length) - 1;
8490 if (!(0 <= first_pos && first_pos <= last_pos &&
8491 last_pos <
static_cast<ssize_t
>(content_length))) {
8497 if (!coalesced.empty()) {
8498 auto &prev = coalesced.back();
8501 if (first_pos <= prev.second + 1 &&
8502 !(first_pos == prev.first && last_pos == prev.second)) {
8504 prev.second = (std::max)(prev.second, last_pos);
8510 coalesced.emplace_back(first_pos, last_pos);
8513 ranges = std::move(coalesced);
8516inline bool range_error(Request &req, Response &res) {
8517 if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
8518 if (res.body.empty() && res.content_provider_ && res.content_length_ == 0) {
8520 if (res.status == StatusCode::PartialContent_206) {
8521 res.status = StatusCode::OK_200;
8526 ssize_t content_len =
static_cast<ssize_t
>(
8527 res.content_length_ ? res.content_length_ : res.body.size());
8529 std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
8530 size_t overwrapping_count = 0;
8539 for (
auto &r : req.ranges) {
8540 auto &first_pos = r.first;
8541 auto &last_pos = r.second;
8543 if (first_pos == -1 && last_pos == -1) {
8545 last_pos = content_len;
8548 if (first_pos == -1) {
8549 first_pos = content_len - last_pos;
8550 last_pos = content_len - 1;
8562 if (last_pos == -1 || last_pos >= content_len) {
8563 last_pos = content_len - 1;
8567 if (!(0 <= first_pos && first_pos <= last_pos &&
8568 last_pos <= content_len - 1)) {
8573 for (
const auto &processed_range : processed_ranges) {
8574 if (!(last_pos < processed_range.first ||
8575 first_pos > processed_range.second)) {
8576 overwrapping_count++;
8577 if (overwrapping_count > 2) {
return true; }
8582 processed_ranges.emplace_back(first_pos, last_pos);
8586 coalesce_ranges(req.ranges,
static_cast<size_t>(content_len));
8592inline std::pair<size_t, size_t>
8593get_range_offset_and_length(Range r,
size_t content_length) {
8594 assert(r.first != -1 && r.second != -1);
8595 assert(0 <= r.first && r.first <
static_cast<ssize_t
>(content_length));
8596 assert(r.first <= r.second &&
8597 r.second <
static_cast<ssize_t
>(content_length));
8598 (void)(content_length);
8599 return std::make_pair(
static_cast<size_t>(r.first),
8600 static_cast<size_t>(r.second - r.first) + 1);
8603inline std::string make_content_range_header_field(
8604 const std::pair<size_t, size_t> &offset_and_length,
size_t content_length) {
8605 auto st = offset_and_length.first;
8606 auto ed = st + offset_and_length.second - 1;
8608 std::string field =
"bytes ";
8609 field += std::to_string(st);
8611 field += std::to_string(ed);
8613 field += std::to_string(content_length);
8617template <
typename SToken,
typename CToken,
typename Content>
8618bool process_multipart_ranges_data(
const Request &req,
8619 const std::string &boundary,
8620 const std::string &content_type,
8621 size_t content_length, SToken stoken,
8622 CToken ctoken, Content content) {
8623 for (
size_t i = 0; i < req.ranges.size(); i++) {
8627 if (!content_type.empty()) {
8628 ctoken(
"Content-Type: ");
8629 stoken(content_type);
8633 auto offset_and_length =
8634 get_range_offset_and_length(req.ranges[i], content_length);
8636 ctoken(
"Content-Range: ");
8637 stoken(make_content_range_header_field(offset_and_length, content_length));
8641 if (!content(offset_and_length.first, offset_and_length.second)) {
8654inline void make_multipart_ranges_data(
const Request &req, Response &res,
8655 const std::string &boundary,
8656 const std::string &content_type,
8657 size_t content_length,
8658 std::string &data) {
8659 process_multipart_ranges_data(
8660 req, boundary, content_type, content_length,
8661 [&](
const std::string &token) { data += token; },
8662 [&](
const std::string &token) { data += token; },
8663 [&](
size_t offset,
size_t length) {
8664 assert(offset + length <= content_length);
8665 data += res.body.substr(offset, length);
8670inline size_t get_multipart_ranges_data_length(
const Request &req,
8671 const std::string &boundary,
8672 const std::string &content_type,
8673 size_t content_length) {
8674 size_t data_length = 0;
8676 process_multipart_ranges_data(
8677 req, boundary, content_type, content_length,
8678 [&](
const std::string &token) { data_length += token.size(); },
8679 [&](
const std::string &token) { data_length += token.size(); },
8680 [&](
size_t ,
size_t length) {
8681 data_length += length;
8688template <
typename T>
8690write_multipart_ranges_data(Stream &strm,
const Request &req, Response &res,
8691 const std::string &boundary,
8692 const std::string &content_type,
8693 size_t content_length,
const T &is_shutting_down) {
8694 return process_multipart_ranges_data(
8695 req, boundary, content_type, content_length,
8696 [&](
const std::string &token) { strm.write(token); },
8697 [&](
const std::string &token) { strm.write(token); },
8698 [&](
size_t offset,
size_t length) {
8699 return write_content(strm, res.content_provider_, offset, length,
8704inline bool has_framed_body(
const Request &req) {
8705 return is_chunked_transfer_encoding(req.headers) ||
8706 req.get_header_value_u64(
"Content-Length") > 0;
8709inline bool is_connection_persistent(
const Request &req) {
8710 auto conn = req.get_header_value(
"Connection");
8711 if (conn ==
"close") {
return false; }
8712 if (req.version ==
"HTTP/1.0" && conn !=
"Keep-Alive") {
return false; }
8716inline bool expect_content(
const Request &req) {
8717 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH" ||
8718 req.method ==
"DELETE") {
8721 return has_framed_body(req);
8729 if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ =
true;
8733 if (is_valid_) WSACleanup();
8736 bool is_valid_ =
false;
8739static WSInit wsinit_;
8742inline bool parse_www_authenticate(
const Response &res,
8743 std::map<std::string, std::string> &auth,
8745 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
8746 if (res.has_header(auth_key)) {
8747 thread_local auto re =
8748 std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
8749 auto s = res.get_header_value(auth_key);
8750 auto pos = s.find(
' ');
8751 if (pos != std::string::npos) {
8752 auto type = s.substr(0, pos);
8753 if (type ==
"Basic") {
8755 }
else if (type ==
"Digest") {
8756 s = s.substr(pos + 1);
8757 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
8758 for (
auto i = beg; i != std::sregex_iterator(); ++i) {
8760 auto key = s.substr(
static_cast<size_t>(m.position(1)),
8761 static_cast<size_t>(m.length(1)));
8762 auto val = m.length(2) > 0
8763 ? s.substr(
static_cast<size_t>(m.position(2)),
8764 static_cast<size_t>(m.length(2)))
8765 : s.substr(
static_cast<size_t>(m.position(3)),
8766 static_cast<size_t>(m.length(3)));
8767 auth[std::move(key)] = std::move(val);
8776class ContentProviderAdapter {
8778 explicit ContentProviderAdapter(
8779 ContentProviderWithoutLength &&content_provider)
8780 : content_provider_(std::move(content_provider)) {}
8782 bool operator()(
size_t offset,
size_t, DataSink &sink) {
8783 return content_provider_(offset, sink);
8787 ContentProviderWithoutLength content_provider_;
8793inline bool is_token_char(
char c) {
8794 return std::isalnum(
static_cast<unsigned char>(c)) || c ==
'!' || c ==
'#' ||
8795 c ==
'$' || c ==
'%' || c ==
'&' || c ==
'\'' || c ==
'*' ||
8796 c ==
'+' || c ==
'-' || c ==
'.' || c ==
'^' || c ==
'_' || c ==
'`' ||
8797 c ==
'|' || c ==
'~';
8800inline bool is_token(
const std::string &s) {
8801 if (s.empty()) {
return false; }
8803 if (!is_token_char(c)) {
return false; }
8808inline bool is_field_name(
const std::string &s) {
return is_token(s); }
8810inline bool is_vchar(
char c) {
return c >= 33 && c <= 126; }
8812inline bool is_obs_text(
char c) {
return 128 <=
static_cast<unsigned char>(c); }
8814inline bool is_field_vchar(
char c) {
return is_vchar(c) || is_obs_text(c); }
8816inline bool is_field_content(
const std::string &s) {
8817 if (s.empty()) {
return true; }
8819 if (s.size() == 1) {
8820 return is_field_vchar(s[0]);
8821 }
else if (s.size() == 2) {
8822 return is_field_vchar(s[0]) && is_field_vchar(s[1]);
8826 if (!is_field_vchar(s[i])) {
return false; }
8829 while (i < s.size() - 1) {
8831 if (c ==
' ' || c ==
'\t' || is_field_vchar(c)) {
8837 return is_field_vchar(s[i]);
8841inline bool is_field_value(
const std::string &s) {
return is_field_content(s); }
8845inline bool perform_websocket_handshake(Stream &strm,
const std::string &host,
8846 int port,
const std::string &path,
8847 const Headers &headers,
8848 std::string &selected_subprotocol) {
8850 if (!fields::is_field_value(path) || !fields::is_field_value(host)) {
8855 for (
const auto &h : headers) {
8856 if (!fields::is_field_name(h.first) || !fields::is_field_value(h.second)) {
8862 thread_local std::mt19937 rng(std::random_device{}());
8863 std::string key_bytes(16,
'\0');
8864 for (
size_t i = 0; i < 16; i += 4) {
8866 std::memcpy(&key_bytes[i], &r, (std::min)(
size_t(4),
size_t(16 - i)));
8868 auto client_key = base64_encode(key_bytes);
8871 std::string req_str =
"GET " + path +
" HTTP/1.1\r\n";
8872 req_str +=
"Host: " + host +
":" + std::to_string(port) +
"\r\n";
8873 req_str +=
"Upgrade: websocket\r\n";
8874 req_str +=
"Connection: Upgrade\r\n";
8875 req_str +=
"Sec-WebSocket-Key: " + client_key +
"\r\n";
8876 req_str +=
"Sec-WebSocket-Version: 13\r\n";
8877 for (
const auto &h : headers) {
8878 req_str += h.first +
": " + h.second +
"\r\n";
8882 if (strm.write(req_str.data(), req_str.size()) < 0) {
return false; }
8885 auto expected_accept = websocket_accept_key(client_key);
8886 return read_websocket_upgrade_response(strm, expected_accept,
8887 selected_subprotocol);
8896#ifdef CPPHTTPLIB_SSL_ENABLED
8899class SSLSocketStream final :
public Stream {
8902 socket_t sock, tls::session_t session, time_t read_timeout_sec,
8903 time_t read_timeout_usec, time_t write_timeout_sec,
8904 time_t write_timeout_usec, time_t max_timeout_msec = 0,
8905 std::chrono::time_point<std::chrono::steady_clock> start_time =
8906 (std::chrono::steady_clock::time_point::min)());
8907 ~SSLSocketStream()
override;
8909 bool is_readable()
const override;
8910 bool wait_readable()
const override;
8911 bool wait_writable()
const override;
8912 bool is_peer_alive()
const override;
8913 ssize_t read(
char *ptr,
size_t size)
override;
8914 ssize_t write(
const char *ptr,
size_t size)
override;
8915 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
8916 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
8918 time_t duration()
const override;
8919 void set_read_timeout(time_t sec, time_t usec = 0)
override;
8923 tls::session_t session_;
8924 time_t read_timeout_sec_;
8925 time_t read_timeout_usec_;
8926 time_t write_timeout_sec_;
8927 time_t write_timeout_usec_;
8928 time_t max_timeout_msec_;
8929 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
8932#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8933inline std::string message_digest(
const std::string &s,
const EVP_MD *algo) {
8934 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
8935 EVP_MD_CTX_new(), EVP_MD_CTX_free);
8937 unsigned int hash_length = 0;
8938 unsigned char hash[EVP_MAX_MD_SIZE];
8940 EVP_DigestInit_ex(context.get(), algo,
nullptr);
8941 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
8942 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
8944 std::stringstream ss;
8945 for (
auto i = 0u; i < hash_length; ++i) {
8946 ss << std::hex << std::setw(2) << std::setfill(
'0')
8947 <<
static_cast<unsigned int>(hash[i]);
8953inline std::string MD5(
const std::string &s) {
8954 return message_digest(s, EVP_md5());
8957inline std::string SHA_256(
const std::string &s) {
8958 return message_digest(s, EVP_sha256());
8961inline std::string SHA_512(
const std::string &s) {
8962 return message_digest(s, EVP_sha512());
8964#elif defined(CPPHTTPLIB_MBEDTLS_SUPPORT)
8967inline std::string hash_to_hex(
const unsigned char (&hash)[N]) {
8968 std::stringstream ss;
8969 for (
size_t i = 0; i < N; ++i) {
8970 ss << std::hex << std::setw(2) << std::setfill(
'0')
8971 <<
static_cast<unsigned int>(hash[i]);
8977inline std::string MD5(
const std::string &s) {
8978 unsigned char hash[16];
8979#ifdef CPPHTTPLIB_MBEDTLS_V3
8980 mbedtls_md5(
reinterpret_cast<const unsigned char *
>(s.c_str()), s.size(),
8983 mbedtls_md5_ret(
reinterpret_cast<const unsigned char *
>(s.c_str()), s.size(),
8986 return hash_to_hex(hash);
8989inline std::string SHA_256(
const std::string &s) {
8990 unsigned char hash[32];
8991#ifdef CPPHTTPLIB_MBEDTLS_V3
8992 mbedtls_sha256(
reinterpret_cast<const unsigned char *
>(s.c_str()), s.size(),
8995 mbedtls_sha256_ret(
reinterpret_cast<const unsigned char *
>(s.c_str()),
8998 return hash_to_hex(hash);
9001inline std::string SHA_512(
const std::string &s) {
9002 unsigned char hash[64];
9003#ifdef CPPHTTPLIB_MBEDTLS_V3
9004 mbedtls_sha512(
reinterpret_cast<const unsigned char *
>(s.c_str()), s.size(),
9007 mbedtls_sha512_ret(
reinterpret_cast<const unsigned char *
>(s.c_str()),
9010 return hash_to_hex(hash);
9012#elif defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
9015inline std::string hash_to_hex(
const unsigned char (&hash)[N]) {
9016 std::stringstream ss;
9017 for (
size_t i = 0; i < N; ++i) {
9018 ss << std::hex << std::setw(2) << std::setfill(
'0')
9019 <<
static_cast<unsigned int>(hash[i]);
9025inline std::string MD5(
const std::string &s) {
9026 unsigned char hash[WC_MD5_DIGEST_SIZE];
9027 wc_Md5Hash(
reinterpret_cast<const unsigned char *
>(s.c_str()),
9028 static_cast<word32
>(s.size()), hash);
9029 return hash_to_hex(hash);
9032inline std::string SHA_256(
const std::string &s) {
9033 unsigned char hash[WC_SHA256_DIGEST_SIZE];
9034 wc_Sha256Hash(
reinterpret_cast<const unsigned char *
>(s.c_str()),
9035 static_cast<word32
>(s.size()), hash);
9036 return hash_to_hex(hash);
9039inline std::string SHA_512(
const std::string &s) {
9040 unsigned char hash[WC_SHA512_DIGEST_SIZE];
9041 wc_Sha512Hash(
reinterpret_cast<const unsigned char *
>(s.c_str()),
9042 static_cast<word32
>(s.size()), hash);
9043 return hash_to_hex(hash);
9047inline bool is_ip_address(
const std::string &host) {
9048 struct in_addr addr4;
9049 struct in6_addr addr6;
9050 return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
9051 inet_pton(AF_INET6, host.c_str(), &addr6) == 1;
9054template <
typename T>
9055inline bool process_server_socket_ssl(
9056 const std::atomic<socket_t> &svr_sock, tls::session_t session,
9057 socket_t sock,
size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
9058 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
9059 time_t write_timeout_usec, T callback) {
9060 return process_server_socket_core(
9061 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
9062 [&](
bool close_connection,
bool &connection_closed) {
9063 SSLSocketStream strm(sock, session, read_timeout_sec, read_timeout_usec,
9064 write_timeout_sec, write_timeout_usec);
9065 return callback(strm, close_connection, connection_closed);
9069template <
typename T>
9070inline bool process_client_socket_ssl(
9071 tls::session_t session,
socket_t sock, time_t read_timeout_sec,
9072 time_t read_timeout_usec, time_t write_timeout_sec,
9073 time_t write_timeout_usec, time_t max_timeout_msec,
9074 std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
9075 SSLSocketStream strm(sock, session, read_timeout_sec, read_timeout_usec,
9076 write_timeout_sec, write_timeout_usec, max_timeout_msec,
9078 return callback(strm);
9081inline std::pair<std::string, std::string> make_digest_authentication_header(
9082 const Request &req,
const std::map<std::string, std::string> &auth,
9083 size_t cnonce_count,
const std::string &cnonce,
const std::string &username,
9084 const std::string &password,
bool is_proxy =
false) {
9087 std::stringstream ss;
9088 ss << std::setfill(
'0') << std::setw(8) << std::hex << cnonce_count;
9093 if (auth.find(
"qop") != auth.end()) {
9094 qop = auth.at(
"qop");
9095 if (qop.find(
"auth-int") != std::string::npos) {
9097 }
else if (qop.find(
"auth") != std::string::npos) {
9104 std::string algo =
"MD5";
9105 if (auth.find(
"algorithm") != auth.end()) { algo = auth.at(
"algorithm"); }
9107 std::string response;
9109 auto H = algo ==
"SHA-256" ? detail::SHA_256
9110 : algo ==
"SHA-512" ? detail::SHA_512
9113 auto A1 = username +
":" + auth.at(
"realm") +
":" + password;
9115 auto A2 = req.method +
":" + req.path;
9116 if (qop ==
"auth-int") { A2 +=
":" + H(req.body); }
9119 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + H(A2));
9121 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + nc +
":" + cnonce +
9122 ":" + qop +
":" + H(A2));
9126 auto opaque = (auth.find(
"opaque") != auth.end()) ? auth.at(
"opaque") :
"";
9128 auto field =
"Digest username=\"" + username +
"\", realm=\"" +
9129 auth.at(
"realm") +
"\", nonce=\"" + auth.at(
"nonce") +
9130 "\", uri=\"" + req.path +
"\", algorithm=" + algo +
9131 (qop.empty() ?
", response=\""
9132 :
", qop=" + qop +
", nc=" + nc +
", cnonce=\"" +
9133 cnonce +
"\", response=\"") +
9135 (opaque.empty() ?
"" :
", opaque=\"" + opaque +
"\"");
9137 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
9138 return std::make_pair(key, field);
9141inline bool match_hostname(
const std::string &pattern,
9142 const std::string &hostname) {
9144 if (detail::case_ignore::equal(hostname, pattern)) {
return true; }
9147 std::vector<std::string> pattern_components;
9148 if (!pattern.empty()) {
9149 split(pattern.data(), pattern.data() + pattern.size(),
'.',
9150 [&](
const char *b,
const char *e) {
9151 pattern_components.emplace_back(b, e);
9155 std::vector<std::string> host_components;
9156 if (!hostname.empty()) {
9157 split(hostname.data(), hostname.data() + hostname.size(),
'.',
9158 [&](
const char *b,
const char *e) {
9159 host_components.emplace_back(b, e);
9164 if (host_components.size() != pattern_components.size()) {
return false; }
9169 auto itr = pattern_components.begin();
9170 for (
const auto &h : host_components) {
9172 if (!detail::case_ignore::equal(p, h) && p !=
"*") {
9173 bool partial_match =
false;
9174 if (!p.empty() && p[p.size() - 1] ==
'*') {
9175 const auto prefix_length = p.size() - 1;
9176 if (prefix_length == 0) {
9177 partial_match =
true;
9178 }
else if (h.size() >= prefix_length) {
9180 std::equal(p.begin(),
9181 p.begin() +
static_cast<std::string::difference_type
>(
9183 h.begin(), [](
const char ca,
const char cb) {
9184 return detail::case_ignore::to_lower(ca) ==
9185 detail::case_ignore::to_lower(cb);
9189 if (!partial_match) {
return false; }
9202verify_cert_with_windows_schannel(
const std::vector<unsigned char> &der_cert,
9203 const std::string &hostname,
9204 bool verify_hostname, uint64_t &out_error) {
9205 if (der_cert.empty()) {
return false; }
9210 auto cert_context = CertCreateCertificateContext(
9211 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data(),
9212 static_cast<DWORD
>(der_cert.size()));
9214 if (!cert_context) {
9215 out_error = GetLastError();
9220 scope_exit([&] { CertFreeCertificateContext(cert_context); });
9223 CERT_CHAIN_PARA chain_para = {};
9224 chain_para.cbSize =
sizeof(chain_para);
9227 PCCERT_CHAIN_CONTEXT chain_context =
nullptr;
9228 auto chain_result = CertGetCertificateChain(
9229 nullptr, cert_context,
nullptr, cert_context->hCertStore, &chain_para,
9230 CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_END_CERT |
9231 CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
9232 nullptr, &chain_context);
9234 if (!chain_result || !chain_context) {
9235 out_error = GetLastError();
9240 scope_exit([&] { CertFreeCertificateChain(chain_context); });
9243 if (chain_context->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR) {
9244 out_error = chain_context->TrustStatus.dwErrorStatus;
9249 SSL_EXTRA_CERT_CHAIN_POLICY_PARA extra_policy_para = {};
9250 extra_policy_para.cbSize =
sizeof(extra_policy_para);
9251#ifdef AUTHTYPE_SERVER
9252 extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
9256 if (verify_hostname) {
9257 whost = u8string_to_wstring(hostname.c_str());
9258 extra_policy_para.pwszServerName =
const_cast<wchar_t *
>(whost.c_str());
9261 CERT_CHAIN_POLICY_PARA policy_para = {};
9262 policy_para.cbSize =
sizeof(policy_para);
9263#ifdef CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS
9264 policy_para.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
9266 policy_para.dwFlags = 0;
9268 policy_para.pvExtraPolicyPara = &extra_policy_para;
9270 CERT_CHAIN_POLICY_STATUS policy_status = {};
9271 policy_status.cbSize =
sizeof(policy_status);
9273 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chain_context,
9274 &policy_para, &policy_status)) {
9275 out_error = GetLastError();
9279 if (policy_status.dwError != 0) {
9280 out_error = policy_status.dwError;
9292inline bool load_client_ca_config(tls::ctx_t ctx,
9293 const std::string &ca_cert_file_path,
9294 const std::string &ca_cert_dir_path,
9295 bool has_custom_store, SystemCAMode mode,
9296 uint64_t &backend_error) {
9299 if (!ca_cert_file_path.empty()) {
9300 if (!tls::load_ca_file(ctx, ca_cert_file_path.c_str())) {
9301 backend_error = tls::get_error();
9304 }
else if (!ca_cert_dir_path.empty()) {
9305 if (!tls::load_ca_dir(ctx, ca_cert_dir_path.c_str())) {
9306 backend_error = tls::get_error();
9311 auto has_custom_ca = !ca_cert_file_path.empty() ||
9312 !ca_cert_dir_path.empty() || has_custom_store;
9313 if (mode == SystemCAMode::Enabled ||
9314 (mode == SystemCAMode::Auto && !has_custom_ca)) {
9315 if (!tls::load_system_certs(ctx)) { backend_error = tls::get_error(); }
9321inline bool setup_client_tls_session(
const std::string &host, tls::ctx_t ctx,
9322 tls::session_t &session,
socket_t sock,
9323 bool server_certificate_verification,
9324 time_t timeout_sec, time_t timeout_usec) {
9325 using namespace tls;
9327 if (!ctx) {
return false; }
9329 bool is_ip = is_ip_address(host);
9331#if defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
9334 set_verify_client(ctx, server_certificate_verification);
9337 session = create_session(ctx, sock);
9338 if (!session) {
return false; }
9344 if (server_certificate_verification) {
9345 set_hostname(session, host.c_str());
9347 set_sni(session, host.c_str());
9351 if (!connect_nonblocking(session, sock, timeout_sec, timeout_usec,
nullptr)) {
9355 if (server_certificate_verification) {
9356 if (get_verify_result(session) != 0) {
return false; }
9361 auto server_cert = get_peer_cert(session);
9362 if (!server_cert) {
return false; }
9363 auto cert_guard = detail::scope_exit([&] { free_cert(server_cert); });
9364 if (!verify_hostname(server_cert, host.c_str())) {
return false; }
9377inline void default_socket_options(
socket_t sock) {
9378 set_socket_opt(sock, SOL_SOCKET,
9387inline bool set_socket_opt(
socket_t sock,
int level,
int optname,
int optval) {
9388 return detail::set_socket_opt_impl(sock, level, optname, &optval,
9392inline std::string get_bearer_token_auth(
const Request &req) {
9393 if (req.has_header(
"Authorization")) {
9394 constexpr auto bearer_header_prefix_len = detail::str_len(
"Bearer ");
9395 return req.get_header_value(
"Authorization")
9396 .substr(bearer_header_prefix_len);
9401inline const char *status_message(
int status) {
9403 case StatusCode::Continue_100:
return "Continue";
9404 case StatusCode::SwitchingProtocol_101:
return "Switching Protocol";
9405 case StatusCode::Processing_102:
return "Processing";
9406 case StatusCode::EarlyHints_103:
return "Early Hints";
9407 case StatusCode::OK_200:
return "OK";
9408 case StatusCode::Created_201:
return "Created";
9409 case StatusCode::Accepted_202:
return "Accepted";
9410 case StatusCode::NonAuthoritativeInformation_203:
9411 return "Non-Authoritative Information";
9412 case StatusCode::NoContent_204:
return "No Content";
9413 case StatusCode::ResetContent_205:
return "Reset Content";
9414 case StatusCode::PartialContent_206:
return "Partial Content";
9415 case StatusCode::MultiStatus_207:
return "Multi-Status";
9416 case StatusCode::AlreadyReported_208:
return "Already Reported";
9417 case StatusCode::IMUsed_226:
return "IM Used";
9418 case StatusCode::MultipleChoices_300:
return "Multiple Choices";
9419 case StatusCode::MovedPermanently_301:
return "Moved Permanently";
9420 case StatusCode::Found_302:
return "Found";
9421 case StatusCode::SeeOther_303:
return "See Other";
9422 case StatusCode::NotModified_304:
return "Not Modified";
9423 case StatusCode::UseProxy_305:
return "Use Proxy";
9424 case StatusCode::unused_306:
return "unused";
9425 case StatusCode::TemporaryRedirect_307:
return "Temporary Redirect";
9426 case StatusCode::PermanentRedirect_308:
return "Permanent Redirect";
9427 case StatusCode::BadRequest_400:
return "Bad Request";
9428 case StatusCode::Unauthorized_401:
return "Unauthorized";
9429 case StatusCode::PaymentRequired_402:
return "Payment Required";
9430 case StatusCode::Forbidden_403:
return "Forbidden";
9431 case StatusCode::NotFound_404:
return "Not Found";
9432 case StatusCode::MethodNotAllowed_405:
return "Method Not Allowed";
9433 case StatusCode::NotAcceptable_406:
return "Not Acceptable";
9434 case StatusCode::ProxyAuthenticationRequired_407:
9435 return "Proxy Authentication Required";
9436 case StatusCode::RequestTimeout_408:
return "Request Timeout";
9437 case StatusCode::Conflict_409:
return "Conflict";
9438 case StatusCode::Gone_410:
return "Gone";
9439 case StatusCode::LengthRequired_411:
return "Length Required";
9440 case StatusCode::PreconditionFailed_412:
return "Precondition Failed";
9441 case StatusCode::PayloadTooLarge_413:
return "Payload Too Large";
9442 case StatusCode::UriTooLong_414:
return "URI Too Long";
9443 case StatusCode::UnsupportedMediaType_415:
return "Unsupported Media Type";
9444 case StatusCode::RangeNotSatisfiable_416:
return "Range Not Satisfiable";
9445 case StatusCode::ExpectationFailed_417:
return "Expectation Failed";
9446 case StatusCode::ImATeapot_418:
return "I'm a teapot";
9447 case StatusCode::MisdirectedRequest_421:
return "Misdirected Request";
9448 case StatusCode::UnprocessableContent_422:
return "Unprocessable Content";
9449 case StatusCode::Locked_423:
return "Locked";
9450 case StatusCode::FailedDependency_424:
return "Failed Dependency";
9451 case StatusCode::TooEarly_425:
return "Too Early";
9452 case StatusCode::UpgradeRequired_426:
return "Upgrade Required";
9453 case StatusCode::PreconditionRequired_428:
return "Precondition Required";
9454 case StatusCode::TooManyRequests_429:
return "Too Many Requests";
9455 case StatusCode::RequestHeaderFieldsTooLarge_431:
9456 return "Request Header Fields Too Large";
9457 case StatusCode::UnavailableForLegalReasons_451:
9458 return "Unavailable For Legal Reasons";
9459 case StatusCode::NotImplemented_501:
return "Not Implemented";
9460 case StatusCode::BadGateway_502:
return "Bad Gateway";
9461 case StatusCode::ServiceUnavailable_503:
return "Service Unavailable";
9462 case StatusCode::GatewayTimeout_504:
return "Gateway Timeout";
9463 case StatusCode::HttpVersionNotSupported_505:
9464 return "HTTP Version Not Supported";
9465 case StatusCode::VariantAlsoNegotiates_506:
return "Variant Also Negotiates";
9466 case StatusCode::InsufficientStorage_507:
return "Insufficient Storage";
9467 case StatusCode::LoopDetected_508:
return "Loop Detected";
9468 case StatusCode::NotExtended_510:
return "Not Extended";
9469 case StatusCode::NetworkAuthenticationRequired_511:
9470 return "Network Authentication Required";
9473 case StatusCode::InternalServerError_500:
return "Internal Server Error";
9477inline std::string to_string(
const Error error) {
9479 case Error::Success:
return "Success (no error)";
9480 case Error::Unknown:
return "Unknown";
9481 case Error::Connection:
return "Could not establish connection";
9482 case Error::BindIPAddress:
return "Failed to bind IP address";
9483 case Error::Read:
return "Failed to read connection";
9484 case Error::Write:
return "Failed to write connection";
9485 case Error::ExceedRedirectCount:
return "Maximum redirect count exceeded";
9486 case Error::Canceled:
return "Connection handling canceled";
9487 case Error::SSLConnection:
return "SSL connection failed";
9488 case Error::SSLLoadingCerts:
return "SSL certificate loading failed";
9489 case Error::SSLServerVerification:
return "SSL server verification failed";
9490 case Error::SSLServerHostnameVerification:
9491 return "SSL server hostname verification failed";
9492 case Error::UnsupportedMultipartBoundaryChars:
9493 return "Unsupported HTTP multipart boundary characters";
9494 case Error::Compression:
return "Compression failed";
9495 case Error::ConnectionTimeout:
return "Connection timed out";
9496 case Error::ProxyConnection:
return "Proxy connection failed";
9497 case Error::ConnectionClosed:
return "Connection closed by server";
9498 case Error::Timeout:
return "Read timeout";
9499 case Error::ResourceExhaustion:
return "Resource exhaustion";
9500 case Error::TooManyFormDataFiles:
return "Too many form data files";
9501 case Error::ExceedMaxPayloadSize:
return "Exceeded maximum payload size";
9502 case Error::ExceedUriMaxLength:
return "Exceeded maximum URI length";
9503 case Error::ExceedMaxSocketDescriptorCount:
9504 return "Exceeded maximum socket descriptor count";
9505 case Error::InvalidRequestLine:
return "Invalid request line";
9506 case Error::InvalidHTTPMethod:
return "Invalid HTTP method";
9507 case Error::InvalidHTTPVersion:
return "Invalid HTTP version";
9508 case Error::InvalidHeaders:
return "Invalid headers";
9509 case Error::MultipartParsing:
return "Multipart parsing failed";
9510 case Error::OpenFile:
return "Failed to open file";
9511 case Error::Listen:
return "Failed to listen on socket";
9512 case Error::GetSockName:
return "Failed to get socket name";
9513 case Error::UnsupportedAddressFamily:
return "Unsupported address family";
9514 case Error::HTTPParsing:
return "HTTP parsing failed";
9515 case Error::InvalidRangeHeader:
return "Invalid Range header";
9522inline std::ostream &operator<<(std::ostream &os,
const Error &obj) {
9523 os << to_string(obj);
9524 os <<
" (" <<
static_cast<std::underlying_type<Error>::type
>(obj) <<
')';
9528inline std::string hosted_at(
const std::string &hostname) {
9529 std::vector<std::string> addrs;
9530 hosted_at(hostname, addrs);
9531 if (addrs.empty()) {
return std::string(); }
9535inline void hosted_at(
const std::string &hostname,
9536 std::vector<std::string> &addrs) {
9537 struct addrinfo hints;
9538 struct addrinfo *result;
9540 memset(&hints, 0,
sizeof(
struct addrinfo));
9541 hints.ai_family = AF_UNSPEC;
9542 hints.ai_socktype = SOCK_STREAM;
9543 hints.ai_protocol = 0;
9545 if (detail::getaddrinfo_with_timeout(hostname.c_str(),
nullptr, &hints,
9547#if defined __linux__ && !defined __ANDROID__
9552 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
9554 for (
auto rp = result; rp; rp = rp->ai_next) {
9556 *
reinterpret_cast<struct sockaddr_storage *
>(rp->ai_addr);
9559 if (detail::get_ip_and_port(addr,
sizeof(
struct sockaddr_storage), ip,
9561 addrs.emplace_back(std::move(ip));
9566inline std::string encode_uri_component(
const std::string &value) {
9567 std::ostringstream escaped;
9569 escaped << std::hex;
9571 for (
auto c : value) {
9572 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' ||
9573 c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' || c ==
'\'' || c ==
'(' ||
9577 escaped << std::uppercase;
9578 escaped <<
'%' << std::setw(2)
9579 <<
static_cast<int>(
static_cast<unsigned char>(c));
9580 escaped << std::nouppercase;
9584 return escaped.str();
9587inline std::string encode_uri(
const std::string &value) {
9588 std::ostringstream escaped;
9590 escaped << std::hex;
9592 for (
auto c : value) {
9593 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' ||
9594 c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' || c ==
'\'' || c ==
'(' ||
9595 c ==
')' || c ==
';' || c ==
'/' || c ==
'?' || c ==
':' || c ==
'@' ||
9596 c ==
'&' || c ==
'=' || c ==
'+' || c ==
'$' || c ==
',' || c ==
'#') {
9599 escaped << std::uppercase;
9600 escaped <<
'%' << std::setw(2)
9601 <<
static_cast<int>(
static_cast<unsigned char>(c));
9602 escaped << std::nouppercase;
9606 return escaped.str();
9609inline std::string decode_uri_component(
const std::string &value) {
9612 for (
size_t i = 0; i < value.size(); i++) {
9613 if (value[i] ==
'%' && i + 2 < value.size()) {
9615 if (detail::from_hex_to_i(value, i + 1, 2, val)) {
9616 result +=
static_cast<char>(val);
9629inline std::string decode_uri(
const std::string &value) {
9632 for (
size_t i = 0; i < value.size(); i++) {
9633 if (value[i] ==
'%' && i + 2 < value.size()) {
9635 if (detail::from_hex_to_i(value, i + 1, 2, val)) {
9636 result +=
static_cast<char>(val);
9649inline std::string encode_path_component(
const std::string &component) {
9651 result.reserve(component.size() * 3);
9653 for (
size_t i = 0; i < component.size(); i++) {
9654 auto c =
static_cast<unsigned char>(component[i]);
9657 if (std::isalnum(c) || c ==
'-' || c ==
'.' || c ==
'_' || c ==
'~') {
9658 result +=
static_cast<char>(c);
9662 else if (c ==
'!' || c ==
'$' || c ==
'&' || c ==
'\'' || c ==
'(' ||
9663 c ==
')' || c ==
'*' || c ==
'+' || c ==
',' || c ==
';' ||
9665 result +=
static_cast<char>(c);
9668 else if (c ==
':') {
9669 result +=
static_cast<char>(c);
9672 else if (c ==
'@') {
9673 result +=
static_cast<char>(c);
9677 snprintf(hex,
sizeof(hex),
"%02X", c);
9678 result.append(hex, 2);
9684inline std::string decode_path_component(
const std::string &component) {
9686 result.reserve(component.size());
9688 for (
size_t i = 0; i < component.size(); i++) {
9689 if (component[i] ==
'%' && i + 1 < component.size()) {
9690 if (component[i + 1] ==
'u') {
9693 if (detail::from_hex_to_i(component, i + 2, 4, val)) {
9697 size_t len = detail::to_utf8(val, buff);
9698 if (len > 0) { result.append(buff, len); }
9701 result += component[i];
9706 if (detail::from_hex_to_i(component, i + 1, 2, val)) {
9708 result +=
static_cast<char>(val);
9711 result += component[i];
9715 result += component[i];
9721inline std::string encode_query_component(
const std::string &component,
9722 bool space_as_plus) {
9724 result.reserve(component.size() * 3);
9726 for (
size_t i = 0; i < component.size(); i++) {
9727 auto c =
static_cast<unsigned char>(component[i]);
9730 if (std::isalnum(c) || c ==
'-' || c ==
'.' || c ==
'_' || c ==
'~') {
9731 result +=
static_cast<char>(c);
9734 else if (c ==
' ') {
9735 if (space_as_plus) {
9742 else if (c ==
'+') {
9743 if (space_as_plus) {
9746 result +=
static_cast<char>(c);
9750 else if (c ==
'!' || c ==
'$' || c ==
'\'' || c ==
'(' || c ==
')' ||
9751 c ==
'*' || c ==
',' || c ==
';') {
9752 result +=
static_cast<char>(c);
9755 else if (c ==
':' || c ==
'@') {
9756 result +=
static_cast<char>(c);
9759 else if (c ==
'/') {
9760 result +=
static_cast<char>(c);
9763 else if (c ==
'?') {
9764 result +=
static_cast<char>(c);
9768 snprintf(hex,
sizeof(hex),
"%02X", c);
9769 result.append(hex, 2);
9775inline std::string decode_query_component(
const std::string &component,
9776 bool plus_as_space) {
9778 result.reserve(component.size());
9780 for (
size_t i = 0; i < component.size(); i++) {
9781 if (component[i] ==
'%' && i + 2 < component.size()) {
9783 if (detail::from_hex_to_i(component, i + 1, 2, val)) {
9784 result +=
static_cast<char>(val);
9787 result += component[i];
9789 }
else if (component[i] ==
'+' && plus_as_space) {
9792 result += component[i];
9798inline std::string sanitize_filename(
const std::string &filename) {
9800 auto pos = filename.find_last_of(
"/\\");
9802 (pos != std::string::npos) ? filename.substr(pos + 1) : filename;
9805 result.erase(std::remove(result.begin(), result.end(),
'\0'), result.end());
9809 auto start = result.find_first_not_of(
" \t");
9810 auto end = result.find_last_not_of(
" \t");
9811 result = (start == std::string::npos)
9813 : result.substr(start, end - start + 1);
9817 if (result ==
"." || result ==
"..") {
return ""; }
9822inline std::string append_query_params(
const std::string &path,
9823 const Params ¶ms) {
9824 std::string path_with_query = path;
9825 thread_local const std::regex re(
"[^?]+\\?.*");
9826 auto delm = std::regex_match(path, re) ?
'&' :
'?';
9827 path_with_query += delm + detail::params_to_query_str(params);
9828 return path_with_query;
9832inline std::pair<std::string, std::string>
9833make_range_header(
const Ranges &ranges) {
9834 std::string field =
"bytes=";
9836 for (
const auto &r : ranges) {
9837 if (i != 0) { field +=
", "; }
9838 if (r.first != -1) { field += std::to_string(r.first); }
9840 if (r.second != -1) { field += std::to_string(r.second); }
9843 return std::make_pair(
"Range", std::move(field));
9846inline std::pair<std::string, std::string>
9847make_basic_authentication_header(
const std::string &username,
9848 const std::string &password,
bool is_proxy) {
9849 auto field =
"Basic " + detail::base64_encode(username +
":" + password);
9850 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
9851 return std::make_pair(key, std::move(field));
9854inline std::pair<std::string, std::string>
9855make_bearer_token_authentication_header(
const std::string &token,
9856 bool is_proxy =
false) {
9857 auto field =
"Bearer " + token;
9858 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
9859 return std::make_pair(key, std::move(field));
9863inline size_t Request::get_header_value_u64(
const std::string &key,
size_t def,
9865 return detail::get_header_value_u64(headers, key, def,
id);
9868inline bool Request::has_header(
const std::string &key)
const {
9869 return detail::has_header(headers, key);
9872inline std::string Request::get_header_value(
const std::string &key,
9873 const char *def,
size_t id)
const {
9874 return detail::get_header_value(headers, key, def,
id);
9877inline size_t Request::get_header_value_count(
const std::string &key)
const {
9878 return detail::get_header_value_count(headers, key);
9881inline void Request::set_header(
const std::string &key,
9882 const std::string &val) {
9883 detail::set_header(headers, key, val);
9886inline bool Request::has_trailer(
const std::string &key)
const {
9887 return trailers.find(key) != trailers.end();
9890inline std::string Request::get_trailer_value(
const std::string &key,
9892 return detail::get_multimap_value(trailers, key,
id);
9895inline size_t Request::get_trailer_value_count(
const std::string &key)
const {
9896 auto r = trailers.equal_range(key);
9897 return static_cast<size_t>(std::distance(r.first, r.second));
9900inline bool Request::has_param(
const std::string &key)
const {
9901 return params.find(key) != params.end();
9904inline std::string Request::get_param_value(
const std::string &key,
9906 return detail::get_multimap_value(params, key,
id);
9909inline std::vector<std::string>
9910Request::get_param_values(
const std::string &key)
const {
9911 auto rng = params.equal_range(key);
9912 std::vector<std::string> values;
9913 values.reserve(
static_cast<size_t>(std::distance(rng.first, rng.second)));
9914 for (
auto it = rng.first; it != rng.second; ++it) {
9915 values.push_back(it->second);
9920inline size_t Request::get_param_value_count(
const std::string &key)
const {
9921 auto r = params.equal_range(key);
9922 return static_cast<size_t>(std::distance(r.first, r.second));
9925inline bool Request::is_multipart_form_data()
const {
9926 const auto &content_type = get_header_value(
"Content-Type");
9927 return detail::extract_media_type(content_type) ==
"multipart/form-data";
9931inline std::string MultipartFormData::get_field(
const std::string &key,
9933 auto rng = fields.equal_range(key);
9934 auto it = rng.first;
9935 std::advance(it,
static_cast<ssize_t
>(
id));
9936 if (it != rng.second) {
return it->second.content; }
9937 return std::string();
9940inline std::vector<std::string>
9941MultipartFormData::get_fields(
const std::string &key)
const {
9942 std::vector<std::string> values;
9943 auto rng = fields.equal_range(key);
9944 for (
auto it = rng.first; it != rng.second; it++) {
9945 values.push_back(it->second.content);
9950inline bool MultipartFormData::has_field(
const std::string &key)
const {
9951 return fields.find(key) != fields.end();
9954inline size_t MultipartFormData::get_field_count(
const std::string &key)
const {
9955 auto r = fields.equal_range(key);
9956 return static_cast<size_t>(std::distance(r.first, r.second));
9959inline FormData MultipartFormData::get_file(
const std::string &key,
9961 return detail::get_multimap_value(files, key,
id);
9964inline std::vector<FormData>
9965MultipartFormData::get_files(
const std::string &key)
const {
9966 std::vector<FormData> values;
9967 auto rng = files.equal_range(key);
9968 for (
auto it = rng.first; it != rng.second; it++) {
9969 values.push_back(it->second);
9974inline bool MultipartFormData::has_file(
const std::string &key)
const {
9975 return files.find(key) != files.end();
9978inline size_t MultipartFormData::get_file_count(
const std::string &key)
const {
9979 auto r = files.equal_range(key);
9980 return static_cast<size_t>(std::distance(r.first, r.second));
9984inline size_t Response::get_header_value_u64(
const std::string &key,
size_t def,
9986 return detail::get_header_value_u64(headers, key, def,
id);
9989inline bool Response::has_header(
const std::string &key)
const {
9990 return headers.find(key) != headers.end();
9993inline std::string Response::get_header_value(
const std::string &key,
9996 return detail::get_header_value(headers, key, def,
id);
9999inline size_t Response::get_header_value_count(
const std::string &key)
const {
10000 return detail::get_header_value_count(headers, key);
10003inline void Response::set_header(
const std::string &key,
10004 const std::string &val) {
10005 detail::set_header(headers, key, val);
10007inline bool Response::has_trailer(
const std::string &key)
const {
10008 return trailers.find(key) != trailers.end();
10011inline std::string Response::get_trailer_value(
const std::string &key,
10013 return detail::get_multimap_value(trailers, key,
id);
10016inline size_t Response::get_trailer_value_count(
const std::string &key)
const {
10017 auto r = trailers.equal_range(key);
10018 return static_cast<size_t>(std::distance(r.first, r.second));
10021inline void Response::set_redirect(
const std::string &url,
int stat) {
10022 if (detail::fields::is_field_value(url)) {
10023 set_header(
"Location", url);
10024 if (300 <= stat && stat < 400) {
10025 this->status = stat;
10027 this->status = StatusCode::Found_302;
10032inline void Response::set_content(
const char *s,
size_t n,
10033 const std::string &content_type) {
10036 auto rng = headers.equal_range(
"Content-Type");
10037 headers.erase(rng.first, rng.second);
10038 set_header(
"Content-Type", content_type);
10041inline void Response::set_content(
const std::string &s,
10042 const std::string &content_type) {
10043 set_content(s.data(), s.size(), content_type);
10046inline void Response::set_content(std::string &&s,
10047 const std::string &content_type) {
10048 body = std::move(s);
10050 auto rng = headers.equal_range(
"Content-Type");
10051 headers.erase(rng.first, rng.second);
10052 set_header(
"Content-Type", content_type);
10055inline void Response::set_content_provider(
10056 size_t in_length,
const std::string &content_type, ContentProvider provider,
10057 ContentProviderResourceReleaser resource_releaser) {
10058 set_header(
"Content-Type", content_type);
10059 content_length_ = in_length;
10060 if (in_length > 0) { content_provider_ = std::move(provider); }
10061 content_provider_resource_releaser_ = std::move(resource_releaser);
10062 is_chunked_content_provider_ =
false;
10065inline void Response::set_content_provider(
10066 const std::string &content_type, ContentProviderWithoutLength provider,
10067 ContentProviderResourceReleaser resource_releaser) {
10068 set_header(
"Content-Type", content_type);
10069 content_length_ = 0;
10070 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
10071 content_provider_resource_releaser_ = std::move(resource_releaser);
10072 is_chunked_content_provider_ =
false;
10075inline void Response::set_chunked_content_provider(
10076 const std::string &content_type, ContentProviderWithoutLength provider,
10077 ContentProviderResourceReleaser resource_releaser) {
10078 set_header(
"Content-Type", content_type);
10079 content_length_ = 0;
10080 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
10081 content_provider_resource_releaser_ = std::move(resource_releaser);
10082 is_chunked_content_provider_ =
true;
10085inline void Response::set_file_content(
const std::string &path,
10086 const std::string &content_type) {
10087 file_content_path_ = path;
10088 file_content_content_type_ = content_type;
10091inline void Response::set_file_content(
const std::string &path) {
10092 file_content_path_ = path;
10096inline size_t Result::get_request_header_value_u64(
const std::string &key,
10099 return detail::get_header_value_u64(request_headers_, key, def,
id);
10102inline bool Result::has_request_header(
const std::string &key)
const {
10103 return request_headers_.find(key) != request_headers_.end();
10106inline std::string Result::get_request_header_value(
const std::string &key,
10109 return detail::get_header_value(request_headers_, key, def,
id);
10113Result::get_request_header_value_count(
const std::string &key)
const {
10114 auto r = request_headers_.equal_range(key);
10115 return static_cast<size_t>(std::distance(r.first, r.second));
10119inline ssize_t Stream::write(
const char *ptr) {
10120 return write(ptr, strlen(ptr));
10123inline ssize_t Stream::write(
const std::string &s) {
10124 return write(s.data(), s.size());
10128inline ssize_t detail::BodyReader::read(
char *buf,
size_t len) {
10130 last_error = Error::Connection;
10133 if (eof) {
return 0; }
10137 if (has_content_length && bytes_read >= content_length) {
10142 auto to_read = len;
10143 if (has_content_length) {
10144 auto remaining = content_length - bytes_read;
10145 to_read = (std::min)(len, remaining);
10147 auto n = stream->read(buf, to_read);
10150 last_error = stream->get_error();
10151 if (last_error == Error::Success) { last_error = Error::Read; }
10157 last_error = stream->get_error();
10158 if (last_error == Error::Success) { last_error = Error::Read; }
10163 bytes_read +=
static_cast<size_t>(n);
10164 if (has_content_length && bytes_read >= content_length) { eof =
true; }
10165 if (payload_max_length > 0 && bytes_read > payload_max_length) {
10166 last_error = Error::ExceedMaxPayloadSize;
10174 if (!chunked_decoder) { chunked_decoder.reset(
new ChunkedDecoder(*stream)); }
10176 size_t chunk_offset = 0;
10177 size_t chunk_total = 0;
10178 auto n = chunked_decoder->read_payload(buf, len, chunk_offset, chunk_total);
10180 last_error = stream->get_error();
10181 if (last_error == Error::Success) { last_error = Error::Read; }
10192 bytes_read +=
static_cast<size_t>(n);
10193 if (payload_max_length > 0 && bytes_read > payload_max_length) {
10194 last_error = Error::ExceedMaxPayloadSize;
10202inline ThreadPool::ThreadPool(
size_t n,
size_t max_n,
size_t mqr)
10203 : base_thread_count_(n), max_queued_requests_(mqr), idle_thread_count_(0),
10205#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10206 if (max_n != 0 && max_n < n) {
10207 std::string msg =
"max_threads must be >= base_threads";
10208 throw std::invalid_argument(msg);
10211 max_thread_count_ = max_n == 0 ? n : max_n;
10212 threads_.reserve(base_thread_count_);
10213#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10216 for (
size_t i = 0; i < base_thread_count_; i++) {
10217 threads_.emplace_back(std::thread([
this]() { worker(
false); }));
10219#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10226 std::unique_lock<std::mutex> lock(mutex_);
10229 cond_.notify_all();
10230 for (
auto &t : threads_) {
10231 if (t.joinable()) { t.join(); }
10238inline bool ThreadPool::enqueue(std::function<
void()> fn) {
10240 std::unique_lock<std::mutex> lock(mutex_);
10241 if (shutdown_) {
return false; }
10242 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
10245 jobs_.push_back(std::move(fn));
10248 if (idle_thread_count_ == 0 &&
10249 threads_.size() + dynamic_threads_.size() < max_thread_count_) {
10250 cleanup_finished_threads();
10251 dynamic_threads_.emplace_back(std::thread([
this]() { worker(
true); }));
10255 cond_.notify_one();
10259inline void ThreadPool::shutdown() {
10261 std::unique_lock<std::mutex> lock(mutex_);
10265 cond_.notify_all();
10267 for (
auto &t : threads_) {
10268 if (t.joinable()) { t.join(); }
10273 std::list<std::thread> remaining_dynamic;
10275 std::unique_lock<std::mutex> lock(mutex_);
10276 remaining_dynamic = std::move(dynamic_threads_);
10278 for (
auto &t : remaining_dynamic) {
10279 if (t.joinable()) { t.join(); }
10282 std::unique_lock<std::mutex> lock(mutex_);
10283 cleanup_finished_threads();
10286inline void ThreadPool::move_to_finished(std::thread::id
id) {
10288 for (
auto it = dynamic_threads_.begin(); it != dynamic_threads_.end(); ++it) {
10289 if (it->get_id() ==
id) {
10290 finished_threads_.push_back(std::move(*it));
10291 dynamic_threads_.erase(it);
10297inline void ThreadPool::cleanup_finished_threads() {
10299 for (
auto &t : finished_threads_) {
10300 if (t.joinable()) { t.join(); }
10302 finished_threads_.clear();
10305inline void ThreadPool::worker(
bool is_dynamic) {
10307 std::function<void()> fn;
10309 std::unique_lock<std::mutex> lock(mutex_);
10310 idle_thread_count_++;
10313 auto has_work = cond_.wait_for(
10315 [&] {
return !jobs_.empty() || shutdown_; });
10318 idle_thread_count_--;
10319 move_to_finished(std::this_thread::get_id());
10323 cond_.wait(lock, [&] {
return !jobs_.empty() || shutdown_; });
10326 idle_thread_count_--;
10328 if (shutdown_ && jobs_.empty()) {
break; }
10330 fn = std::move(jobs_.front());
10334 assert(
true ==
static_cast<bool>(fn));
10338#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && \
10339 !defined(LIBRESSL_VERSION_NUMBER)
10340 OPENSSL_thread_stop();
10350inline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
10351 time_t timeout_sec, time_t timeout_usec,
10352 time_t &actual_timeout_sec,
10353 time_t &actual_timeout_usec) {
10354 auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
10356 auto actual_timeout_msec =
10357 (std::min)(max_timeout_msec - duration_msec, timeout_msec);
10359 if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }
10361 actual_timeout_sec = actual_timeout_msec / 1000;
10362 actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
10366inline SocketStream::SocketStream(
10367 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
10368 time_t write_timeout_sec, time_t write_timeout_usec,
10369 time_t max_timeout_msec,
10370 std::chrono::time_point<std::chrono::steady_clock> start_time)
10371 : sock_(sock), read_timeout_sec_(read_timeout_sec),
10372 read_timeout_usec_(read_timeout_usec),
10373 write_timeout_sec_(write_timeout_sec),
10374 write_timeout_usec_(write_timeout_usec),
10375 max_timeout_msec_(max_timeout_msec), start_time_(start_time),
10376 read_buff_(read_buff_size_, 0) {}
10378inline SocketStream::~SocketStream() =
default;
10380inline bool SocketStream::is_readable()
const {
10381 return read_buff_off_ < read_buff_content_size_;
10384inline bool SocketStream::wait_readable()
const {
10385 if (max_timeout_msec_ <= 0) {
10386 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
10389 time_t read_timeout_sec;
10390 time_t read_timeout_usec;
10391 calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
10392 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
10394 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
10397inline bool SocketStream::wait_writable()
const {
10398 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
10401inline bool SocketStream::is_peer_alive()
const {
10402 return detail::is_socket_alive(sock_);
10405inline ssize_t SocketStream::read(
char *ptr,
size_t size) {
10408 (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
10410 size = (std::min)(size,
10411 static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
10414 if (read_buff_off_ < read_buff_content_size_) {
10415 auto remaining_size = read_buff_content_size_ - read_buff_off_;
10416 if (size <= remaining_size) {
10417 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
10418 read_buff_off_ += size;
10419 return static_cast<ssize_t
>(size);
10421 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
10422 read_buff_off_ += remaining_size;
10423 return static_cast<ssize_t
>(remaining_size);
10427 if (!wait_readable()) {
10428 error_ = Error::Timeout;
10432 read_buff_off_ = 0;
10433 read_buff_content_size_ = 0;
10435 if (size < read_buff_size_) {
10436 auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
10440 error_ = Error::ConnectionClosed;
10442 error_ = Error::Read;
10445 }
else if (n <=
static_cast<ssize_t
>(size)) {
10446 memcpy(ptr, read_buff_.data(),
static_cast<size_t>(n));
10449 memcpy(ptr, read_buff_.data(), size);
10450 read_buff_off_ = size;
10451 read_buff_content_size_ =
static_cast<size_t>(n);
10452 return static_cast<ssize_t
>(size);
10458 error_ = Error::ConnectionClosed;
10460 error_ = Error::Read;
10467inline ssize_t SocketStream::write(
const char *ptr,
size_t size) {
10468 if (!wait_writable()) {
return -1; }
10470#if defined(_WIN32) && !defined(_WIN64)
10472 (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
10478inline void SocketStream::get_remote_ip_and_port(std::string &ip,
10480 return detail::get_remote_ip_and_port(sock_, ip, port);
10483inline void SocketStream::get_local_ip_and_port(std::string &ip,
10485 return detail::get_local_ip_and_port(sock_, ip, port);
10488inline socket_t SocketStream::socket()
const {
return sock_; }
10490inline time_t SocketStream::duration()
const {
10491 return std::chrono::duration_cast<std::chrono::milliseconds>(
10492 std::chrono::steady_clock::now() - start_time_)
10496inline void SocketStream::set_read_timeout(time_t sec, time_t usec) {
10497 read_timeout_sec_ = sec;
10498 read_timeout_usec_ = usec;
10502inline bool BufferStream::is_readable()
const {
return true; }
10504inline bool BufferStream::wait_readable()
const {
return true; }
10506inline bool BufferStream::wait_writable()
const {
return true; }
10508inline ssize_t BufferStream::read(
char *ptr,
size_t size) {
10509#if defined(_MSC_VER) && _MSC_VER < 1910
10510 auto len_read = buffer._Copy_s(ptr, size, size, position);
10512 auto len_read = buffer.copy(ptr, size, position);
10514 position +=
static_cast<size_t>(len_read);
10515 return static_cast<ssize_t
>(len_read);
10518inline ssize_t BufferStream::write(
const char *ptr,
size_t size) {
10519 buffer.append(ptr, size);
10520 return static_cast<ssize_t
>(size);
10523inline void BufferStream::get_remote_ip_and_port(std::string & ,
10526inline void BufferStream::get_local_ip_and_port(std::string & ,
10529inline socket_t BufferStream::socket()
const {
return 0; }
10531inline time_t BufferStream::duration()
const {
return 0; }
10533inline const std::string &BufferStream::get_buffer()
const {
return buffer; }
10535inline PathParamsMatcher::PathParamsMatcher(
const std::string &pattern)
10536 : MatcherBase(pattern) {
10537 constexpr const char marker[] =
"/:";
10540 std::size_t last_param_end = 0;
10542#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10547 std::unordered_set<std::string> param_name_set;
10551 const auto marker_pos = pattern.find(
10552 marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
10553 if (marker_pos == std::string::npos) {
break; }
10555 static_fragments_.push_back(
10556 pattern.substr(last_param_end, marker_pos - last_param_end + 1));
10558 const auto param_name_start = marker_pos + str_len(marker);
10560 auto sep_pos = pattern.find(separator, param_name_start);
10561 if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
10564 pattern.substr(param_name_start, sep_pos - param_name_start);
10566#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10567 if (param_name_set.find(param_name) != param_name_set.cend()) {
10568 std::string msg =
"Encountered path parameter '" + param_name +
10569 "' multiple times in route pattern '" + pattern +
"'.";
10570 throw std::invalid_argument(msg);
10574 param_names_.push_back(std::move(param_name));
10576 last_param_end = sep_pos + 1;
10579 if (last_param_end < pattern.length()) {
10580 static_fragments_.push_back(pattern.substr(last_param_end));
10584inline bool PathParamsMatcher::match(Request &request)
const {
10585 request.matches = std::smatch();
10586 request.path_params.clear();
10587 request.path_params.reserve(param_names_.size());
10590 std::size_t starting_pos = 0;
10591 for (
size_t i = 0; i < static_fragments_.size(); ++i) {
10592 const auto &fragment = static_fragments_[i];
10594 if (starting_pos + fragment.length() > request.path.length()) {
10600 if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),
10601 fragment.length()) != 0) {
10605 starting_pos += fragment.length();
10610 if (i >= param_names_.size()) {
continue; }
10612 auto sep_pos = request.path.find(separator, starting_pos);
10613 if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }
10615 const auto ¶m_name = param_names_[i];
10617 request.path_params.emplace(
10618 param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
10621 starting_pos = sep_pos + 1;
10624 return starting_pos >= request.path.length();
10627inline bool RegexMatcher::match(Request &request)
const {
10628 request.path_params.clear();
10629 return std::regex_match(request.path, request.matches, regex_);
10633inline std::string prepare_host_string(
const std::string &host) {
10635 if (host.find(
':') == std::string::npos ||
10636 (!host.empty() && host[0] ==
'[')) {
10641 return "[" + host +
"]";
10645inline std::string make_host_and_port_string(
const std::string &host,
int port,
10647 auto result = prepare_host_string(host);
10650 if ((!is_ssl && port == 80) || (is_ssl && port == 443)) {
10653 result +=
":" + std::to_string(port);
10661make_host_and_port_string_always_port(
const std::string &host,
int port) {
10662 return prepare_host_string(host) +
":" + std::to_string(port);
10665bool parse_no_proxy_entry(
const std::string &token, NoProxyEntry &out);
10666NormalizedTarget normalize_target(
const std::string &host);
10667bool ip_in_cidr(
const IPBytes &ip,
const IPBytes &net,
int prefix_bits);
10668bool host_matches_no_proxy(
const NormalizedTarget &target,
10669 const std::vector<NoProxyEntry> &entries);
10671inline bool ip_in_cidr(
const IPBytes &ip,
const IPBytes &net,
int prefix_bits) {
10672 if (prefix_bits < 0 || prefix_bits > 128) {
return false; }
10673 if (prefix_bits == 0) {
return true; }
10674 int full_bytes = prefix_bits / 8;
10675 int rem_bits = prefix_bits % 8;
10676 if (full_bytes > 0 && std::memcmp(ip.data(), net.data(),
10677 static_cast<size_t>(full_bytes)) != 0) {
10680 if (rem_bits == 0) {
return true; }
10681 auto i =
static_cast<size_t>(full_bytes);
10682 auto mask =
static_cast<uint8_t
>(0xFFu << (8 - rem_bits));
10683 return (ip[i] & mask) == (net[i] & mask);
10686inline bool parse_no_proxy_entry(
const std::string &token, NoProxyEntry &out) {
10687 if (token.empty()) {
return false; }
10689 if (token ==
"*") {
10690 out.kind = NoProxyKind::Wildcard;
10694 auto slash = token.find(
'/');
10695 std::string addr_part =
10696 (slash == std::string::npos) ? token : token.substr(0, slash);
10697 std::string prefix_part =
10698 (slash == std::string::npos) ? std::string() : token.substr(slash + 1);
10702 if (slash != std::string::npos && prefix_part.empty()) { return false; }
10707 bool bracketed = addr_part.size() >= 2 && addr_part.front() ==
'[' &&
10708 addr_part.back() ==
']';
10709 if (bracketed) { addr_part = addr_part.substr(1, addr_part.size() - 2); }
10713 if (inet_pton(AF_INET, addr_part.c_str(), &v4) == 1) {
10715 if (!prefix_part.empty()) {
10716 auto r = from_chars(prefix_part.data(),
10717 prefix_part.data() + prefix_part.size(), prefix);
10718 if (r.ec != std::errc{} ||
10719 r.ptr != prefix_part.data() + prefix_part.size()) {
10722 if (prefix < 0 || prefix > 32) {
return false; }
10724 out.kind = NoProxyKind::IPv4Cidr;
10725 std::memcpy(out.net.data(), &v4,
sizeof(v4));
10726 out.prefix_bits = prefix;
10731 struct in6_addr v6;
10732 if (inet_pton(AF_INET6, addr_part.c_str(), &v6) == 1) {
10734 if (!prefix_part.empty()) {
10735 auto r = from_chars(prefix_part.data(),
10736 prefix_part.data() + prefix_part.size(), prefix);
10737 if (r.ec != std::errc{} ||
10738 r.ptr != prefix_part.data() + prefix_part.size()) {
10741 if (prefix < 0 || prefix > 128) { return false; }
10743 out.kind = NoProxyKind::IPv6Cidr;
10744 std::memcpy(out.net.data(), &v6,
sizeof(v6));
10745 out.prefix_bits = prefix;
10751 if (bracketed) {
return false; }
10754 if (slash != std::string::npos) {
return false; }
10756 if (token.find(
':') != std::string::npos) { return false; }
10758 std::string hostname = case_ignore::to_lower(token);
10759 while (!hostname.empty() && hostname.front() ==
'.') {
10760 hostname.erase(hostname.begin());
10762 while (!hostname.empty() && hostname.back() ==
'.') {
10763 hostname.pop_back();
10765 if (hostname.empty()) { return false; }
10767 out.kind = NoProxyKind::HostnameSuffix;
10768 out.hostname_pattern = std::move(hostname);
10772inline NormalizedTarget normalize_target(
const std::string &host) {
10773 NormalizedTarget t;
10774 std::string h = host;
10776 if (h.size() >= 2 && h.front() ==
'[' && h.back() ==
']') {
10777 h = h.substr(1, h.size() - 2);
10782 if (!h.empty() && h.back() ==
'.') { h.pop_back(); }
10784 t.hostname = case_ignore::to_lower(h);
10786 if (!t.hostname.empty()) {
10788 struct in6_addr v6;
10789 if (inet_pton(AF_INET, t.hostname.c_str(), &v4) == 1) {
10791 std::memcpy(t.ip.data(), &v4,
sizeof(v4));
10792 }
else if (inet_pton(AF_INET6, t.hostname.c_str(), &v6) == 1) {
10794 std::memcpy(t.ip.data(), &v6,
sizeof(v6));
10800inline bool host_matches_no_proxy(
const NormalizedTarget &target,
10801 const std::vector<NoProxyEntry> &entries) {
10802 if (target.hostname.empty()) {
return false; }
10803 for (
const auto &e : entries) {
10805 case NoProxyKind::Wildcard:
return true;
10806 case NoProxyKind::IPv4Cidr:
10807 if (target.is_ipv4 && ip_in_cidr(target.ip, e.net, e.prefix_bits)) {
10811 case NoProxyKind::IPv6Cidr:
10812 if (target.is_ipv6 && ip_in_cidr(target.ip, e.net, e.prefix_bits)) {
10816 case NoProxyKind::HostnameSuffix:
10817 if (target.is_ipv4 || target.is_ipv6) {
break; }
10818 if (target.hostname == e.hostname_pattern) {
return true; }
10821 if (target.hostname.size() > e.hostname_pattern.size() + 1) {
10822 auto offset = target.hostname.size() - e.hostname_pattern.size();
10823 if (target.hostname[offset - 1] ==
'.' &&
10824 target.hostname.compare(offset, e.hostname_pattern.size(),
10825 e.hostname_pattern) == 0) {
10835template <
typename T>
10836inline bool check_and_write_headers(Stream &strm, Headers &headers,
10837 T header_writer, Error &error) {
10838 for (
const auto &h : headers) {
10839 if (!detail::fields::is_field_name(h.first) ||
10840 !detail::fields::is_field_value(h.second)) {
10841 error = Error::InvalidHeaders;
10845 if (header_writer(strm, headers) <= 0) {
10846 error = Error::Write;
10858#ifdef CPPHTTPLIB_SSL_ENABLED
10862inline SSLSocketStream::SSLSocketStream(
10863 socket_t sock, tls::session_t session, time_t read_timeout_sec,
10864 time_t read_timeout_usec, time_t write_timeout_sec,
10865 time_t write_timeout_usec, time_t max_timeout_msec,
10866 std::chrono::time_point<std::chrono::steady_clock> start_time)
10867 : sock_(sock), session_(session), read_timeout_sec_(read_timeout_sec),
10868 read_timeout_usec_(read_timeout_usec),
10869 write_timeout_sec_(write_timeout_sec),
10870 write_timeout_usec_(write_timeout_usec),
10871 max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
10872#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10878 SSL_clear_mode(
static_cast<SSL *
>(session), SSL_MODE_AUTO_RETRY);
10882inline SSLSocketStream::~SSLSocketStream() =
default;
10884inline bool SSLSocketStream::is_readable()
const {
10885 return tls::pending(session_) > 0;
10888inline bool SSLSocketStream::wait_readable()
const {
10889 if (max_timeout_msec_ <= 0) {
10890 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
10893 time_t read_timeout_sec;
10894 time_t read_timeout_usec;
10895 calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
10896 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
10898 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
10901inline bool SSLSocketStream::wait_writable()
const {
10902 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
10903 !tls::is_peer_closed(session_, sock_);
10906inline bool SSLSocketStream::is_peer_alive()
const {
10907 return !tls::is_peer_closed(session_, sock_);
10910inline ssize_t SSLSocketStream::read(
char *ptr,
size_t size) {
10911 if (tls::pending(session_) > 0) {
10913 auto ret = tls::read(session_, ptr, size, err);
10914 if (ret == 0 || err.code == tls::ErrorCode::PeerClosed) {
10915 error_ = Error::ConnectionClosed;
10918 }
else if (wait_readable()) {
10920 auto ret = tls::read(session_, ptr, size, err);
10924 while (--n >= 0 && (err.code == tls::ErrorCode::WantRead ||
10925 (err.code == tls::ErrorCode::SyscallError &&
10926 WSAGetLastError() == WSAETIMEDOUT))) {
10928 while (--n >= 0 && err.code == tls::ErrorCode::WantRead) {
10930 if (tls::pending(session_) > 0) {
10931 return tls::read(session_, ptr, size, err);
10932 }
else if (wait_readable()) {
10933 std::this_thread::sleep_for(std::chrono::microseconds{10});
10934 ret = tls::read(session_, ptr, size, err);
10935 if (ret >= 0) {
return ret; }
10941 }
else if (ret == 0 || err.code == tls::ErrorCode::PeerClosed) {
10942 error_ = Error::ConnectionClosed;
10946 error_ = Error::Timeout;
10951inline ssize_t SSLSocketStream::write(
const char *ptr,
size_t size) {
10952 if (wait_writable()) {
10954 std::min<size_t>(size, (std::numeric_limits<int>::max)());
10957 auto ret = tls::write(session_, ptr, handle_size, err);
10961 while (--n >= 0 && (err.code == tls::ErrorCode::WantWrite ||
10962 (err.code == tls::ErrorCode::SyscallError &&
10963 WSAGetLastError() == WSAETIMEDOUT))) {
10965 while (--n >= 0 && err.code == tls::ErrorCode::WantWrite) {
10967 if (wait_writable()) {
10968 std::this_thread::sleep_for(std::chrono::microseconds{10});
10969 ret = tls::write(session_, ptr, handle_size, err);
10970 if (ret >= 0) {
return ret; }
10982inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
10984 detail::get_remote_ip_and_port(sock_, ip, port);
10987inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
10989 detail::get_local_ip_and_port(sock_, ip, port);
10992inline socket_t SSLSocketStream::socket()
const {
return sock_; }
10994inline time_t SSLSocketStream::duration()
const {
10995 return std::chrono::duration_cast<std::chrono::milliseconds>(
10996 std::chrono::steady_clock::now() - start_time_)
11000inline void SSLSocketStream::set_read_timeout(time_t sec, time_t usec) {
11001 read_timeout_sec_ = sec;
11002 read_timeout_usec_ = usec;
11013inline Server::Server()
11014 : new_task_queue([] {
11019 signal(SIGPIPE, SIG_IGN);
11023inline Server::~Server() =
default;
11025inline std::unique_ptr<detail::MatcherBase>
11026Server::make_matcher(
const std::string &pattern) {
11027 if (pattern.find(
"/:") != std::string::npos) {
11028 return detail::make_unique<detail::PathParamsMatcher>(pattern);
11030 return detail::make_unique<detail::RegexMatcher>(pattern);
11034inline Server &Server::Get(
const std::string &pattern, Handler handler) {
11035 return add_handler(get_handlers_, pattern, std::move(handler));
11038inline Server &Server::Post(
const std::string &pattern, Handler handler) {
11039 return add_handler(post_handlers_, pattern, std::move(handler));
11042inline Server &Server::Post(
const std::string &pattern,
11043 HandlerWithContentReader handler) {
11044 return add_handler(post_handlers_for_content_reader_, pattern,
11045 std::move(handler));
11048inline Server &Server::Put(
const std::string &pattern, Handler handler) {
11049 return add_handler(put_handlers_, pattern, std::move(handler));
11052inline Server &Server::Put(
const std::string &pattern,
11053 HandlerWithContentReader handler) {
11054 return add_handler(put_handlers_for_content_reader_, pattern,
11055 std::move(handler));
11058inline Server &Server::Patch(
const std::string &pattern, Handler handler) {
11059 return add_handler(patch_handlers_, pattern, std::move(handler));
11062inline Server &Server::Patch(
const std::string &pattern,
11063 HandlerWithContentReader handler) {
11064 return add_handler(patch_handlers_for_content_reader_, pattern,
11065 std::move(handler));
11068inline Server &Server::Delete(
const std::string &pattern, Handler handler) {
11069 return add_handler(delete_handlers_, pattern, std::move(handler));
11072inline Server &Server::Delete(
const std::string &pattern,
11073 HandlerWithContentReader handler) {
11074 return add_handler(delete_handlers_for_content_reader_, pattern,
11075 std::move(handler));
11078inline Server &Server::Options(
const std::string &pattern, Handler handler) {
11079 return add_handler(options_handlers_, pattern, std::move(handler));
11082inline Server &Server::WebSocket(
const std::string &pattern,
11083 WebSocketHandler handler) {
11084 websocket_handlers_.push_back(
11085 {make_matcher(pattern), std::move(handler),
nullptr});
11089inline Server &Server::WebSocket(
const std::string &pattern,
11090 WebSocketHandler handler,
11091 SubProtocolSelector sub_protocol_selector) {
11092 websocket_handlers_.push_back({make_matcher(pattern), std::move(handler),
11093 std::move(sub_protocol_selector)});
11097inline bool Server::set_base_dir(
const std::string &dir,
11098 const std::string &mount_point) {
11099 return set_mount_point(mount_point, dir);
11102inline bool Server::set_mount_point(
const std::string &mount_point,
11103 const std::string &dir, Headers headers) {
11104 detail::FileStat stat(dir);
11105 if (stat.is_dir()) {
11106 std::string mnt = !mount_point.empty() ? mount_point :
"/";
11107 if (!mnt.empty() && mnt[0] ==
'/') {
11108 std::string resolved_base;
11109 if (detail::canonicalize_path(dir.c_str(), resolved_base)) {
11111 if (resolved_base.back() !=
'\\' && resolved_base.back() !=
'/') {
11112 resolved_base +=
'\\';
11115 if (resolved_base.back() !=
'/') { resolved_base +=
'/'; }
11118 base_dirs_.push_back(
11119 {std::move(mnt), dir, std::move(resolved_base), std::move(headers)});
11126inline bool Server::remove_mount_point(
const std::string &mount_point) {
11127 for (
auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
11128 if (it->mount_point == mount_point) {
11129 base_dirs_.erase(it);
11137Server::set_file_extension_and_mimetype_mapping(
const std::string &ext,
11138 const std::string &mime) {
11139 file_extension_and_mimetype_map_[ext] = mime;
11143inline Server &Server::set_default_file_mimetype(
const std::string &mime) {
11144 default_file_mimetype_ = mime;
11148inline Server &Server::set_file_request_handler(Handler handler) {
11149 file_request_handler_ = std::move(handler);
11153inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
11155 error_handler_ = std::move(handler);
11159inline Server &Server::set_error_handler_core(Handler handler,
11161 error_handler_ = [handler](
const Request &req, Response &res) {
11163 return HandlerResponse::Handled;
11168inline Server &Server::set_exception_handler(ExceptionHandler handler) {
11169 exception_handler_ = std::move(handler);
11173inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
11174 pre_routing_handler_ = std::move(handler);
11178inline Server &Server::set_post_routing_handler(Handler handler) {
11179 post_routing_handler_ = std::move(handler);
11183inline Server &Server::set_pre_request_handler(HandlerWithResponse handler) {
11184 pre_request_handler_ = std::move(handler);
11188inline Server &Server::set_logger(Logger logger) {
11189 logger_ = std::move(logger);
11193inline Server &Server::set_error_logger(ErrorLogger error_logger) {
11194 error_logger_ = std::move(error_logger);
11198inline Server &Server::set_pre_compression_logger(Logger logger) {
11199 pre_compression_logger_ = std::move(logger);
11204Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
11205 expect_100_continue_handler_ = std::move(handler);
11209inline Server &Server::set_start_handler(StartHandler handler) {
11210 start_handler_ = std::move(handler);
11214inline Server &Server::set_address_family(
int family) {
11215 address_family_ = family;
11219inline Server &Server::set_tcp_nodelay(
bool on) {
11224inline Server &Server::set_ipv6_v6only(
bool on) {
11229inline Server &Server::set_socket_options(SocketOptions socket_options) {
11230 socket_options_ = std::move(socket_options);
11234inline Server &Server::set_default_headers(Headers headers) {
11235 default_headers_ = std::move(headers);
11239inline Server &Server::set_header_writer(
11240 std::function<ssize_t(Stream &, Headers &)>
const &writer) {
11241 header_writer_ = writer;
11246Server::set_trusted_proxies(
const std::vector<std::string> &proxies) {
11247 trusted_proxies_ = proxies;
11251inline Server &Server::set_keep_alive_max_count(
size_t count) {
11252 keep_alive_max_count_ = count;
11256inline Server &Server::set_keep_alive_timeout(time_t sec) {
11257 keep_alive_timeout_sec_ = sec;
11261template <
class Rep,
class Period>
11262inline Server &Server::set_keep_alive_timeout(
11263 const std::chrono::duration<Rep, Period> &duration) {
11264 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t ) {
11265 set_keep_alive_timeout(sec);
11270inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
11271 read_timeout_sec_ = sec;
11272 read_timeout_usec_ = usec;
11276inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
11277 write_timeout_sec_ = sec;
11278 write_timeout_usec_ = usec;
11282inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
11283 idle_interval_sec_ = sec;
11284 idle_interval_usec_ = usec;
11288inline Server &Server::set_payload_max_length(
size_t length) {
11289 payload_max_length_ = length;
11293inline Server &Server::set_websocket_max_missed_pongs(
int count) {
11294 websocket_max_missed_pongs_ = count;
11298inline Server &Server::set_websocket_ping_interval(time_t sec) {
11299 websocket_ping_interval_sec_ = sec;
11303template <
class Rep,
class Period>
11304inline Server &Server::set_websocket_ping_interval(
11305 const std::chrono::duration<Rep, Period> &duration) {
11306 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t ) {
11307 set_websocket_ping_interval(sec);
11312inline bool Server::bind_to_port(
const std::string &host,
int port,
11313 int socket_flags) {
11314 auto ret = bind_internal(host, port, socket_flags);
11315 if (ret == -1) { is_decommissioned =
true; }
11318inline int Server::bind_to_any_port(
const std::string &host,
int socket_flags) {
11319 auto ret = bind_internal(host, 0, socket_flags);
11320 if (ret == -1) { is_decommissioned =
true; }
11324inline bool Server::listen_after_bind() {
return listen_internal(); }
11326inline bool Server::listen(
const std::string &host,
int port,
11327 int socket_flags) {
11328 return bind_to_port(host, port, socket_flags) && listen_internal();
11331inline bool Server::is_running()
const {
return is_running_; }
11333inline void Server::wait_until_ready()
const {
11334 while (!is_running_ && !is_decommissioned) {
11335 std::this_thread::sleep_for(std::chrono::milliseconds{1});
11339inline void Server::stop() noexcept {
11343 detail::shutdown_socket(sock);
11344 detail::close_socket(sock);
11346 is_decommissioned =
false;
11349inline void Server::decommission() { is_decommissioned =
true; }
11351inline bool Server::parse_request_line(
const char *s, Request &req)
const {
11352 auto len = strlen(s);
11353 if (len < 2 || s[len - 2] !=
'\r' || s[len - 1] !=
'\n') {
return false; }
11359 detail::split(s, s + len,
' ', [&](
const char *b,
const char *e) {
11361 case 0: req.method = std::string(b, e);
break;
11362 case 1: req.target = std::string(b, e);
break;
11363 case 2: req.version = std::string(b, e);
break;
11369 if (count != 3) {
return false; }
11372 thread_local const std::set<std::string> methods{
11373 "GET",
"HEAD",
"POST",
"PUT",
"DELETE",
11374 "CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PRI"};
11376 if (methods.find(req.method) == methods.end()) {
11377 output_error_log(Error::InvalidHTTPMethod, &req);
11381 if (req.version !=
"HTTP/1.1" && req.version !=
"HTTP/1.0") {
11382 output_error_log(Error::InvalidHTTPVersion, &req);
11388 for (
size_t i = 0; i < req.target.size(); i++) {
11389 if (req.target[i] ==
'#') {
11390 req.target.erase(i);
11395 detail::divide(req.target,
'?',
11396 [&](
const char *lhs_data, std::size_t lhs_size,
11397 const char *rhs_data, std::size_t rhs_size) {
11399 decode_path_component(std::string(lhs_data, lhs_size));
11400 detail::parse_query_text(rhs_data, rhs_size, req.params);
11407inline bool Server::write_response(Stream &strm,
bool close_connection,
11408 Request &req, Response &res) {
11411 req.ranges.clear();
11412 return write_response_core(strm, close_connection, req, res,
false);
11415inline bool Server::write_response_with_content(Stream &strm,
11416 bool close_connection,
11417 const Request &req,
11419 return write_response_core(strm, close_connection, req, res,
true);
11422inline bool Server::write_response_core(Stream &strm,
bool close_connection,
11423 const Request &req, Response &res,
11424 bool need_apply_ranges) {
11425 assert(res.status != -1);
11427 if (400 <= res.status && error_handler_ &&
11428 error_handler_(req, res) == HandlerResponse::Handled) {
11429 need_apply_ranges =
true;
11432 std::string content_type;
11433 std::string boundary;
11434 if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
11437 if (close_connection || req.get_header_value(
"Connection") ==
"close" ||
11438 400 <= res.status) {
11439 res.set_header(
"Connection",
"close");
11441 std::string s =
"timeout=";
11442 s += std::to_string(keep_alive_timeout_sec_);
11444 s += std::to_string(keep_alive_max_count_);
11445 res.set_header(
"Keep-Alive", s);
11448 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
11449 !res.has_header(
"Content-Type")) {
11450 res.set_header(
"Content-Type",
"text/plain");
11453 if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
11454 !res.has_header(
"Content-Length")) {
11455 res.set_header(
"Content-Length",
"0");
11458 if (req.method ==
"HEAD" && !res.has_header(
"Accept-Ranges")) {
11459 res.set_header(
"Accept-Ranges",
"bytes");
11462 if (post_routing_handler_) { post_routing_handler_(req, res); }
11465 detail::BufferStream bstrm;
11466 if (!detail::write_response_line(bstrm, res.status)) {
return false; }
11467 if (header_writer_(bstrm, res.headers) <= 0) {
return false; }
11470 if (req.method !=
"HEAD" && !res.body.empty() && !res.content_provider_) {
11471 bstrm.write(res.body.data(), res.body.size());
11476 output_log(req, res);
11479 auto &data = bstrm.get_buffer();
11480 if (!detail::write_data(strm, data.data(), data.size())) {
return false; }
11484 if (req.method !=
"HEAD" && res.content_provider_) {
11485 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
11486 res.content_provider_success_ =
true;
11496Server::write_content_with_provider(Stream &strm,
const Request &req,
11497 Response &res,
const std::string &boundary,
11498 const std::string &content_type) {
11499 auto is_shutting_down = [
this]() {
11503 if (res.content_length_ > 0) {
11504 if (req.ranges.empty()) {
11505 return detail::write_content(strm, res.content_provider_, 0,
11506 res.content_length_, is_shutting_down);
11507 }
else if (req.ranges.size() == 1) {
11508 auto offset_and_length = detail::get_range_offset_and_length(
11509 req.ranges[0], res.content_length_);
11511 return detail::write_content(strm, res.content_provider_,
11512 offset_and_length.first,
11513 offset_and_length.second, is_shutting_down);
11515 return detail::write_multipart_ranges_data(
11516 strm, req, res, boundary, content_type, res.content_length_,
11520 if (res.is_chunked_content_provider_) {
11521 auto type = detail::encoding_type(req, res);
11523 auto compressor = detail::make_compressor(type);
11525 compressor = detail::make_unique<detail::nocompressor>();
11528 return detail::write_content_chunked(strm, res.content_provider_,
11529 is_shutting_down, *compressor);
11531 return detail::write_content_without_length(strm, res.content_provider_,
11537inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
11538 FormFields::iterator cur_field;
11539 FormFiles::iterator cur_file;
11540 auto is_text_field =
false;
11542 if (read_content_core(
11545 [&](
const char *buf,
size_t n) {
11552 if (req.body.size() >= req.body.max_size() ||
11553 n > req.body.max_size() - req.body.size()) {
11560 if (payload_max_length_ > 0 &&
11561 (req.body.size() >= payload_max_length_ ||
11562 n > payload_max_length_ - req.body.size())) {
11566 req.body.append(buf, n);
11570 [&](
const FormData &file) {
11572 output_error_log(Error::TooManyFormDataFiles, &req);
11576 if (file.filename.empty()) {
11577 cur_field = req.form.fields.emplace(
11578 file.name, FormField{file.name, file.content, file.headers});
11579 is_text_field =
true;
11581 cur_file = req.form.files.emplace(file.name, file);
11582 is_text_field =
false;
11586 [&](
const char *buf,
size_t n) {
11587 if (is_text_field) {
11588 auto &content = cur_field->second.content;
11589 if (content.size() + n > content.max_size()) {
return false; }
11590 content.append(buf, n);
11592 auto &content = cur_file->second.content;
11593 if (content.size() + n > content.max_size()) {
return false; }
11594 content.append(buf, n);
11598 const auto &content_type = req.get_header_value(
"Content-Type");
11599 if (detail::extract_media_type(content_type) ==
11600 "application/x-www-form-urlencoded") {
11602 res.status = StatusCode::PayloadTooLarge_413;
11603 output_error_log(Error::ExceedMaxPayloadSize, &req);
11606 detail::parse_query_text(req.body, req.params);
11613inline bool Server::read_content_with_content_receiver(
11614 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
11615 FormDataHeader multipart_header, ContentReceiver multipart_receiver) {
11616 return read_content_core(strm, req, res, std::move(receiver),
11617 std::move(multipart_header),
11618 std::move(multipart_receiver));
11621inline bool Server::read_content_core(
11622 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
11623 FormDataHeader multipart_header, ContentReceiver multipart_receiver)
const {
11624 detail::FormDataParser multipart_form_data_parser;
11625 ContentReceiverWithProgress out;
11627 if (req.is_multipart_form_data()) {
11628 const auto &content_type = req.get_header_value(
"Content-Type");
11629 std::string boundary;
11630 if (!detail::parse_multipart_boundary(content_type, boundary)) {
11631 res.status = StatusCode::BadRequest_400;
11632 output_error_log(Error::MultipartParsing, &req);
11636 multipart_form_data_parser.set_boundary(std::move(boundary));
11637 out = [&](
const char *buf,
size_t n,
size_t ,
size_t ) {
11638 return multipart_form_data_parser.parse(buf, n, multipart_header,
11639 multipart_receiver);
11642 out = [receiver](
const char *buf,
size_t n,
size_t ,
11643 size_t ) {
return receiver(buf, n); };
11650#if !defined(CPPHTTPLIB_SSL_ENABLED)
11651 if (!req.has_header(
"Content-Length") &&
11652 !detail::is_chunked_transfer_encoding(req.headers)) {
11653 if (!detail::is_connection_persistent(req) && payload_max_length_ > 0 &&
11654 payload_max_length_ < (std::numeric_limits<size_t>::max)()) {
11655 auto has_data = strm.is_readable();
11657 auto s = strm.socket();
11659 has_data = detail::select_read(s, 0, 0) > 0;
11664 detail::read_content_without_length(strm, payload_max_length_, out);
11665 if (result == detail::ReadContentResult::PayloadTooLarge) {
11666 res.status = StatusCode::PayloadTooLarge_413;
11668 }
else if (result != detail::ReadContentResult::Success) {
11677 if (!req.has_header(
"Content-Length") &&
11678 !detail::is_chunked_transfer_encoding(req.headers)) {
11683 if (!detail::read_content(strm, req, payload_max_length_, res.status,
nullptr,
11688 req.body_consumed_ =
true;
11690 if (req.is_multipart_form_data()) {
11691 if (!multipart_form_data_parser.is_valid()) {
11692 res.status = StatusCode::BadRequest_400;
11693 output_error_log(Error::MultipartParsing, &req);
11701inline bool Server::handle_file_request(Request &req, Response &res) {
11702 for (
const auto &entry : base_dirs_) {
11704 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
11705 std::string sub_path =
"/" + req.path.substr(entry.mount_point.size());
11706 if (detail::is_valid_path(sub_path)) {
11707 auto path = entry.base_dir + sub_path;
11708 if (path.back() ==
'/') { path +=
"index.html"; }
11712 if (!entry.resolved_base_dir.empty()) {
11713 std::string resolved_path;
11714 if (detail::canonicalize_path(path.c_str(), resolved_path) &&
11715 !detail::is_path_within_base(resolved_path,
11716 entry.resolved_base_dir)) {
11717 res.status = StatusCode::Forbidden_403;
11722 detail::FileStat stat(path);
11724 if (stat.is_dir()) {
11725 res.set_redirect(sub_path +
"/", StatusCode::MovedPermanently_301);
11729 if (stat.is_file()) {
11730 for (
const auto &kv : entry.headers) {
11731 res.set_header(kv.first, kv.second);
11734 auto etag = detail::compute_etag(stat);
11735 if (!etag.empty()) { res.set_header(
"ETag", etag); }
11737 auto mtime = stat.mtime();
11739 auto last_modified = detail::file_mtime_to_http_date(mtime);
11740 if (!last_modified.empty()) {
11741 res.set_header(
"Last-Modified", last_modified);
11744 if (check_if_not_modified(req, res, etag, mtime)) {
return true; }
11746 check_if_range(req, etag, mtime);
11748 auto mm = std::make_shared<detail::mmap>(path.c_str());
11749 if (!mm->is_open()) {
11750 output_error_log(Error::OpenFile, &req);
11754 res.set_content_provider(
11756 detail::find_content_type(path, file_extension_and_mimetype_map_,
11757 default_file_mimetype_),
11758 [mm](
size_t offset,
size_t length, DataSink &sink) ->
bool {
11759 sink.write(mm->data() + offset, length);
11763 if (req.method !=
"HEAD" && file_request_handler_) {
11764 file_request_handler_(req, res);
11769 output_error_log(Error::OpenFile, &req);
11777inline bool Server::check_if_not_modified(
const Request &req, Response &res,
11778 const std::string &etag,
11779 time_t mtime)
const {
11783 if (req.has_header(
"If-None-Match")) {
11784 if (!etag.empty()) {
11785 auto val = req.get_header_value(
"If-None-Match");
11793 auto ret = detail::split_find(val.data(), val.data() + val.size(),
',',
11794 [&](
const char *b,
const char *e) {
11795 auto seg_len = static_cast<size_t>(e - b);
11796 return (seg_len == 1 && *b ==
'*') ||
11797 (seg_len == etag.size() &&
11798 std::equal(b, e, etag.begin()));
11802 res.status = StatusCode::NotModified_304;
11806 }
else if (req.has_header(
"If-Modified-Since")) {
11807 auto val = req.get_header_value(
"If-Modified-Since");
11808 auto t = detail::parse_http_date(val);
11810 if (t !=
static_cast<time_t
>(-1) && mtime <= t) {
11811 res.status = StatusCode::NotModified_304;
11818inline bool Server::check_if_range(Request &req,
const std::string &etag,
11819 time_t mtime)
const {
11824 if (!req.ranges.empty() && req.has_header(
"If-Range")) {
11825 auto val = req.get_header_value(
"If-Range");
11827 auto is_valid_range = [&]() {
11828 if (detail::is_strong_etag(val)) {
11831 return (!etag.empty() && val == etag);
11832 }
else if (detail::is_weak_etag(val)) {
11837 auto t = detail::parse_http_date(val);
11838 return (t !=
static_cast<time_t
>(-1) && mtime <= t);
11842 if (!is_valid_range()) {
11844 req.ranges.clear();
11853Server::create_server_socket(
const std::string &host,
int port,
11855 SocketOptions socket_options)
const {
11856 return detail::create_socket(
11857 host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
11858 ipv6_v6only_, std::move(socket_options),
11859 [&](
socket_t sock,
struct addrinfo &ai,
bool & ) ->
bool {
11860 if (::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen))) {
11861 output_error_log(Error::BindIPAddress,
nullptr);
11865 output_error_log(Error::Listen,
nullptr);
11872inline int Server::bind_internal(
const std::string &host,
int port,
11873 int socket_flags) {
11874 if (is_decommissioned) {
return -1; }
11876 if (!is_valid()) {
return -1; }
11878 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
11882 struct sockaddr_storage addr;
11883 socklen_t addr_len =
sizeof(addr);
11884 if (getsockname(svr_sock_,
reinterpret_cast<struct sockaddr *
>(&addr),
11885 &addr_len) == -1) {
11886 output_error_log(Error::GetSockName,
nullptr);
11889 if (addr.ss_family == AF_INET) {
11890 return ntohs(
reinterpret_cast<struct sockaddr_in *
>(&addr)->sin_port);
11891 }
else if (addr.ss_family == AF_INET6) {
11892 return ntohs(
reinterpret_cast<struct sockaddr_in6 *
>(&addr)->sin6_port);
11894 output_error_log(Error::UnsupportedAddressFamily,
nullptr);
11902inline bool Server::listen_internal() {
11903 if (is_decommissioned) {
return false; }
11906 is_running_ =
true;
11907 auto se = detail::scope_exit([&]() { is_running_ =
false; });
11909 if (start_handler_) { start_handler_(); }
11912 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
11916 if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
11918 auto val = detail::select_read(svr_sock_, idle_interval_sec_,
11919 idle_interval_usec_);
11921 task_queue->on_idle();
11931 socket_t sock = WSAAccept(svr_sock_,
nullptr,
nullptr,
nullptr, 0);
11932#elif defined SOCK_CLOEXEC
11933 socket_t sock = accept4(svr_sock_,
nullptr,
nullptr, SOCK_CLOEXEC);
11935 socket_t sock = accept(svr_sock_,
nullptr,
nullptr);
11939 if (errno == EMFILE) {
11942 std::this_thread::sleep_for(std::chrono::microseconds{1});
11944 }
else if (errno == EINTR || errno == EAGAIN) {
11948 detail::close_socket(svr_sock_);
11950 output_error_log(Error::Connection,
nullptr);
11957 detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
11958 read_timeout_sec_, read_timeout_usec_);
11959 detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
11960 write_timeout_sec_, write_timeout_usec_);
11962 if (tcp_nodelay_) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
11964 if (!task_queue->enqueue(
11965 [
this, sock]() { process_and_close_socket(sock); })) {
11966 output_error_log(Error::ResourceExhaustion,
nullptr);
11967 detail::shutdown_socket(sock);
11968 detail::close_socket(sock);
11972 task_queue->shutdown();
11975 is_decommissioned = !ret;
11979inline bool Server::routing(Request &req, Response &res, Stream &strm) {
11980 if (pre_routing_handler_ &&
11981 pre_routing_handler_(req, res) == HandlerResponse::Handled) {
11986 if ((req.method ==
"GET" || req.method ==
"HEAD") &&
11987 handle_file_request(req, res)) {
11991 if (detail::expect_content(req)) {
11998 bool content_reader_payload_too_large =
false;
12000 ContentReader reader(
12001 [&](ContentReceiver receiver) {
12002 auto result = read_content_with_content_receiver(
12003 strm, req, res, std::move(receiver),
nullptr,
nullptr);
12005 output_error_log(Error::Read, &req);
12006 if (res.status == StatusCode::PayloadTooLarge_413) {
12007 content_reader_payload_too_large =
true;
12012 [&](FormDataHeader header, ContentReceiver receiver) {
12013 auto result = read_content_with_content_receiver(
12014 strm, req, res,
nullptr, std::move(header),
12015 std::move(receiver));
12017 output_error_log(Error::Read, &req);
12018 if (res.status == StatusCode::PayloadTooLarge_413) {
12019 content_reader_payload_too_large =
true;
12025 bool dispatched =
false;
12026 if (req.method ==
"POST") {
12027 dispatched = dispatch_request_for_content_reader(
12028 req, res, std::move(reader), post_handlers_for_content_reader_);
12029 }
else if (req.method ==
"PUT") {
12030 dispatched = dispatch_request_for_content_reader(
12031 req, res, std::move(reader), put_handlers_for_content_reader_);
12032 }
else if (req.method ==
"PATCH") {
12033 dispatched = dispatch_request_for_content_reader(
12034 req, res, std::move(reader), patch_handlers_for_content_reader_);
12035 }
else if (req.method ==
"DELETE") {
12036 dispatched = dispatch_request_for_content_reader(
12037 req, res, std::move(reader), delete_handlers_for_content_reader_);
12041 if (content_reader_payload_too_large) {
12044 res.status = StatusCode::PayloadTooLarge_413;
12046 res.content_length_ = 0;
12047 res.content_provider_ =
nullptr;
12062 if (req.method ==
"GET" || req.method ==
"HEAD") {
12063 return dispatch_request(req, res, get_handlers_, strm);
12064 }
else if (req.method ==
"POST") {
12065 return dispatch_request(req, res, post_handlers_, strm);
12066 }
else if (req.method ==
"PUT") {
12067 return dispatch_request(req, res, put_handlers_, strm);
12068 }
else if (req.method ==
"DELETE") {
12069 return dispatch_request(req, res, delete_handlers_, strm);
12070 }
else if (req.method ==
"OPTIONS") {
12071 return dispatch_request(req, res, options_handlers_, strm);
12072 }
else if (req.method ==
"PATCH") {
12073 return dispatch_request(req, res, patch_handlers_, strm);
12076 res.status = StatusCode::BadRequest_400;
12080inline bool Server::dispatch_request(Request &req, Response &res,
12081 const Handlers &handlers, Stream &strm) {
12082 for (
const auto &x : handlers) {
12083 const auto &matcher = x.first;
12084 const auto &handler = x.second;
12086 if (matcher->match(req)) {
12087 req.matched_route = matcher->pattern();
12092 if (pre_request_handler_ &&
12093 pre_request_handler_(req, res) == HandlerResponse::Handled) {
12098 if (detail::expect_content(req) && !read_content(strm, req, res)) {
12099 output_error_log(Error::Read, &req);
12110inline void Server::apply_ranges(
const Request &req, Response &res,
12111 std::string &content_type,
12112 std::string &boundary)
const {
12113 if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
12114 auto it = res.headers.find(
"Content-Type");
12115 if (it != res.headers.end()) {
12116 content_type = it->second;
12117 res.headers.erase(it);
12120 boundary = detail::make_multipart_data_boundary();
12122 res.set_header(
"Content-Type",
12123 "multipart/byteranges; boundary=" + boundary);
12126 auto type = detail::encoding_type(req, res);
12128 if (res.body.empty()) {
12129 if (res.content_length_ > 0) {
12131 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
12132 length = res.content_length_;
12133 }
else if (req.ranges.size() == 1) {
12134 auto offset_and_length = detail::get_range_offset_and_length(
12135 req.ranges[0], res.content_length_);
12137 length = offset_and_length.second;
12139 auto content_range = detail::make_content_range_header_field(
12140 offset_and_length, res.content_length_);
12141 res.set_header(
"Content-Range", content_range);
12143 length = detail::get_multipart_ranges_data_length(
12144 req, boundary, content_type, res.content_length_);
12146 res.set_header(
"Content-Length", std::to_string(length));
12148 if (res.content_provider_) {
12149 if (res.is_chunked_content_provider_) {
12150 res.set_header(
"Transfer-Encoding",
"chunked");
12151 if (type != detail::EncodingType::None) {
12152 res.set_header(
"Content-Encoding", detail::encoding_name(type));
12153 res.set_header(
"Vary",
"Accept-Encoding");
12159 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
12161 }
else if (req.ranges.size() == 1) {
12162 auto offset_and_length =
12163 detail::get_range_offset_and_length(req.ranges[0], res.body.size());
12164 auto offset = offset_and_length.first;
12165 auto length = offset_and_length.second;
12167 auto content_range = detail::make_content_range_header_field(
12168 offset_and_length, res.body.size());
12169 res.set_header(
"Content-Range", content_range);
12171 assert(offset + length <= res.body.size());
12172 res.body = res.body.substr(offset, length);
12175 detail::make_multipart_ranges_data(req, res, boundary, content_type,
12176 res.body.size(), data);
12177 res.body.swap(data);
12180 if (type != detail::EncodingType::None) {
12181 output_pre_compression_log(req, res);
12183 if (
auto compressor = detail::make_compressor(type)) {
12184 std::string compressed;
12185 if (compressor->compress(res.body.data(), res.body.size(),
true,
12186 [&](
const char *data,
size_t data_len) {
12187 compressed.append(data, data_len);
12190 res.body.swap(compressed);
12191 res.set_header(
"Content-Encoding", detail::encoding_name(type));
12192 res.set_header(
"Vary",
"Accept-Encoding");
12197 auto length = std::to_string(res.body.size());
12198 res.set_header(
"Content-Length", length);
12202inline bool Server::dispatch_request_for_content_reader(
12203 Request &req, Response &res, ContentReader content_reader,
12204 const HandlersForContentReader &handlers)
const {
12205 for (
const auto &x : handlers) {
12206 const auto &matcher = x.first;
12207 const auto &handler = x.second;
12209 if (matcher->match(req)) {
12210 req.matched_route = matcher->pattern();
12211 if (!pre_request_handler_ ||
12212 pre_request_handler_(req, res) != HandlerResponse::Handled) {
12213 handler(req, res, content_reader);
12222get_client_ip(
const std::string &x_forwarded_for,
12223 const std::vector<std::string> &trusted_proxies) {
12225 std::vector<std::string> ip_list;
12226 detail::split(x_forwarded_for.data(),
12227 x_forwarded_for.data() + x_forwarded_for.size(),
',',
12228 [&](
const char *b,
const char *e) {
12229 auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));
12230 ip_list.emplace_back(std::string(b + r.first, b + r.second));
12236 if (ip_list.empty()) {
return std::string(); }
12238 for (
size_t i = 0; i < ip_list.size(); ++i) {
12239 auto ip = ip_list[i];
12241 auto is_trusted_proxy =
12242 std::any_of(trusted_proxies.begin(), trusted_proxies.end(),
12243 [&](
const std::string &proxy) { return ip == proxy; });
12245 if (is_trusted_proxy) {
12251 return ip_list[i - 1];
12257 return ip_list.front();
12261Server::process_request(Stream &strm,
const std::string &remote_addr,
12262 int remote_port,
const std::string &local_addr,
12263 int local_port,
bool close_connection,
12264 bool &connection_closed,
12265 const std::function<
void(Request &)> &setup_request,
12266 bool *websocket_upgraded) {
12267 std::array<char, 2048> buf{};
12269 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
12272 if (!line_reader.getline()) {
return false; }
12275 req.start_time_ = std::chrono::steady_clock::now();
12276 req.remote_addr = remote_addr;
12277 req.remote_port = remote_port;
12278 req.local_addr = local_addr;
12279 req.local_port = local_port;
12282 res.version =
"HTTP/1.1";
12283 res.headers = default_headers_;
12286 if (!parse_request_line(line_reader.ptr(), req)) {
12287 res.status = StatusCode::BadRequest_400;
12288 output_error_log(Error::InvalidRequestLine, &req);
12289 return write_response(strm, close_connection, req, res);
12293 if (!detail::read_headers(strm, req.headers)) {
12294 res.status = StatusCode::BadRequest_400;
12295 output_error_log(Error::InvalidHeaders, &req);
12296 return write_response(strm, close_connection, req, res);
12302 if (req.get_header_value_u64(
"Content-Length") > 0 &&
12303 req.has_header(
"Transfer-Encoding")) {
12304 connection_closed =
true;
12305 res.status = StatusCode::BadRequest_400;
12306 return write_response(strm, close_connection, req, res);
12311 connection_closed =
true;
12312 res.status = StatusCode::UriTooLong_414;
12313 output_error_log(Error::ExceedUriMaxLength, &req);
12314 return write_response(strm, close_connection, req, res);
12317 if (req.get_header_value(
"Connection") ==
"close") {
12318 connection_closed =
true;
12321 if (req.version ==
"HTTP/1.0" &&
12322 req.get_header_value(
"Connection") !=
"Keep-Alive") {
12323 connection_closed =
true;
12326 if (!trusted_proxies_.empty() && req.has_header(
"X-Forwarded-For")) {
12327 auto x_forwarded_for = req.get_header_value(
"X-Forwarded-For");
12328 auto derived = get_client_ip(x_forwarded_for, trusted_proxies_);
12329 req.remote_addr = derived.empty() ? remote_addr : derived;
12331 req.remote_addr = remote_addr;
12333 req.remote_port = remote_port;
12335 req.local_addr = local_addr;
12336 req.local_port = local_port;
12338 if (req.has_header(
"Accept")) {
12339 const auto &accept_header = req.get_header_value(
"Accept");
12340 if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
12341 connection_closed =
true;
12342 res.status = StatusCode::BadRequest_400;
12343 output_error_log(Error::HTTPParsing, &req);
12344 return write_response(strm, close_connection, req, res);
12348 if (req.has_header(
"Range")) {
12349 const auto &range_header_value = req.get_header_value(
"Range");
12350 if (!detail::parse_range_header(range_header_value, req.ranges)) {
12351 connection_closed =
true;
12352 res.status = StatusCode::RangeNotSatisfiable_416;
12353 output_error_log(Error::InvalidRangeHeader, &req);
12354 return write_response(strm, close_connection, req, res);
12358 if (setup_request) { setup_request(req); }
12360 if (req.get_header_value(
"Expect") ==
"100-continue") {
12361 int status = StatusCode::Continue_100;
12362 if (expect_100_continue_handler_) {
12363 status = expect_100_continue_handler_(req, res);
12366 case StatusCode::Continue_100:
12367 case StatusCode::ExpectationFailed_417:
12368 detail::write_response_line(strm, status);
12369 strm.write(
"\r\n");
12372 connection_closed =
true;
12373 return write_response(strm,
true, req, res);
12378 auto sock = strm.socket();
12379 req.is_connection_closed = [sock]() {
12380 return !detail::is_socket_alive(sock);
12387 if (detail::is_websocket_upgrade(req)) {
12388 if (pre_routing_handler_ &&
12389 pre_routing_handler_(req, res) == HandlerResponse::Handled) {
12390 if (res.status == -1) { res.status = StatusCode::OK_200; }
12391 return write_response(strm, close_connection, req, res);
12394 for (
const auto &entry : websocket_handlers_) {
12395 if (entry.matcher->match(req)) {
12397 auto client_key = req.get_header_value(
"Sec-WebSocket-Key");
12398 auto accept_key = detail::websocket_accept_key(client_key);
12401 std::string selected_subprotocol;
12402 if (entry.sub_protocol_selector) {
12403 auto protocol_header = req.get_header_value(
"Sec-WebSocket-Protocol");
12404 if (!protocol_header.empty()) {
12405 std::vector<std::string> protocols;
12406 std::istringstream iss(protocol_header);
12408 while (std::getline(iss, token,
',')) {
12410 auto start = token.find_first_not_of(
' ');
12411 auto end = token.find_last_not_of(
' ');
12412 if (start != std::string::npos) {
12413 protocols.push_back(token.substr(start, end - start + 1));
12416 selected_subprotocol = entry.sub_protocol_selector(protocols);
12421 std::string handshake_response =
"HTTP/1.1 101 Switching Protocols\r\n"
12422 "Upgrade: websocket\r\n"
12423 "Connection: Upgrade\r\n"
12424 "Sec-WebSocket-Accept: " +
12425 accept_key +
"\r\n";
12426 if (!selected_subprotocol.empty()) {
12427 if (!detail::fields::is_field_value(selected_subprotocol)) {
12430 handshake_response +=
12431 "Sec-WebSocket-Protocol: " + selected_subprotocol +
"\r\n";
12433 handshake_response +=
"\r\n";
12434 if (strm.write(handshake_response.data(), handshake_response.size()) <
12439 connection_closed =
true;
12440 if (websocket_upgraded) { *websocket_upgraded =
true; }
12445 ws::WebSocket ws(strm, req,
true, websocket_ping_interval_sec_,
12446 websocket_max_missed_pongs_);
12447 entry.handler(req, ws);
12456 auto routed =
false;
12457#ifdef CPPHTTPLIB_NO_EXCEPTIONS
12458 routed = routing(req, res, strm);
12461 routed = routing(req, res, strm);
12462 }
catch (std::exception &) {
12463 if (exception_handler_) {
12464 auto ep = std::current_exception();
12465 exception_handler_(req, res, ep);
12468 res.status = StatusCode::InternalServerError_500;
12471 if (exception_handler_) {
12472 auto ep = std::current_exception();
12473 exception_handler_(req, res, ep);
12476 res.status = StatusCode::InternalServerError_500;
12482 if (res.status == -1) {
12483 res.status = req.ranges.empty() ? StatusCode::OK_200
12484 : StatusCode::PartialContent_206;
12488 auto file_open_error =
false;
12489 if (!res.file_content_path_.empty()) {
12490 const auto &path = res.file_content_path_;
12491 auto mm = std::make_shared<detail::mmap>(path.c_str());
12492 if (!mm->is_open()) {
12494 res.content_length_ = 0;
12495 res.content_provider_ =
nullptr;
12496 res.status = StatusCode::NotFound_404;
12497 output_error_log(Error::OpenFile, &req);
12498 file_open_error =
true;
12500 auto content_type = res.file_content_content_type_;
12501 if (content_type.empty()) {
12502 content_type = detail::find_content_type(
12503 path, file_extension_and_mimetype_map_, default_file_mimetype_);
12506 res.set_content_provider(
12507 mm->size(), content_type,
12508 [mm](
size_t offset,
size_t length, DataSink &sink) ->
bool {
12509 sink.write(mm->data() + offset, length);
12515 if (file_open_error) {
12516 ret = write_response(strm, close_connection, req, res);
12517 }
else if (detail::range_error(req, res)) {
12519 res.content_length_ = 0;
12520 res.content_provider_ =
nullptr;
12521 res.status = StatusCode::RangeNotSatisfiable_416;
12522 ret = write_response(strm, close_connection, req, res);
12524 ret = write_response_with_content(strm, close_connection, req, res);
12527 if (res.status == -1) { res.status = StatusCode::NotFound_404; }
12528 ret = write_response(strm, close_connection, req, res);
12534 if (!req.body_consumed_ && detail::has_framed_body(req)) {
12536 if (!detail::read_content(
12537 strm, req, payload_max_length_, dummy_status,
nullptr,
12538 [](
const char *,
size_t,
size_t,
size_t) {
return true; },
false)) {
12539 connection_closed =
true;
12546inline bool Server::is_valid()
const {
return true; }
12548inline bool Server::process_and_close_socket(
socket_t sock) {
12549 std::string remote_addr;
12550 int remote_port = 0;
12551 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
12553 std::string local_addr;
12554 int local_port = 0;
12555 detail::get_local_ip_and_port(sock, local_addr, local_port);
12557 bool websocket_upgraded =
false;
12558 auto ret = detail::process_server_socket(
12559 svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
12560 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
12561 write_timeout_usec_,
12562 [&](Stream &strm,
bool close_connection,
bool &connection_closed) {
12563 return process_request(strm, remote_addr, remote_port, local_addr,
12564 local_port, close_connection, connection_closed,
12565 nullptr, &websocket_upgraded);
12568 detail::shutdown_socket(sock);
12569 detail::close_socket(sock);
12573inline void Server::output_log(
const Request &req,
const Response &res)
const {
12575 std::lock_guard<std::mutex> guard(logger_mutex_);
12580inline void Server::output_pre_compression_log(
const Request &req,
12581 const Response &res)
const {
12582 if (pre_compression_logger_) {
12583 std::lock_guard<std::mutex> guard(logger_mutex_);
12584 pre_compression_logger_(req, res);
12588inline void Server::output_error_log(
const Error &err,
12589 const Request *req)
const {
12590 if (error_logger_) {
12591 std::lock_guard<std::mutex> guard(logger_mutex_);
12592 error_logger_(err, req);
12600inline ClientImpl::ClientImpl(
const std::string &host)
12601 : ClientImpl(host, 80, std::string(), std::string()) {}
12603inline ClientImpl::ClientImpl(
const std::string &host,
int port)
12604 : ClientImpl(host, port, std::string(), std::string()) {}
12606inline ClientImpl::ClientImpl(
const std::string &host,
int port,
12607 const std::string &client_cert_path,
12608 const std::string &client_key_path)
12609 : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
12610 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
12612inline ClientImpl::~ClientImpl() {
12614 size_t retry_count = 10;
12615 while (retry_count-- > 0) {
12617 std::lock_guard<std::mutex> guard(socket_mutex_);
12618 if (socket_requests_in_flight_ == 0) {
break; }
12620 std::this_thread::sleep_for(std::chrono::milliseconds{1});
12623 std::lock_guard<std::mutex> guard(socket_mutex_);
12624 shutdown_socket(socket_);
12625 close_socket(socket_);
12628inline bool ClientImpl::is_valid()
const {
return true; }
12630inline void ClientImpl::copy_settings(
const ClientImpl &rhs) {
12631 client_cert_path_ = rhs.client_cert_path_;
12632 client_key_path_ = rhs.client_key_path_;
12633 connection_timeout_sec_ = rhs.connection_timeout_sec_;
12634 read_timeout_sec_ = rhs.read_timeout_sec_;
12635 read_timeout_usec_ = rhs.read_timeout_usec_;
12636 write_timeout_sec_ = rhs.write_timeout_sec_;
12637 write_timeout_usec_ = rhs.write_timeout_usec_;
12638 max_timeout_msec_ = rhs.max_timeout_msec_;
12639 basic_auth_username_ = rhs.basic_auth_username_;
12640 basic_auth_password_ = rhs.basic_auth_password_;
12641 bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
12642 keep_alive_ = rhs.keep_alive_;
12643 follow_location_ = rhs.follow_location_;
12644 path_encode_ = rhs.path_encode_;
12645 address_family_ = rhs.address_family_;
12646 tcp_nodelay_ = rhs.tcp_nodelay_;
12647 ipv6_v6only_ = rhs.ipv6_v6only_;
12648 socket_options_ = rhs.socket_options_;
12649 compress_ = rhs.compress_;
12650 decompress_ = rhs.decompress_;
12651 payload_max_length_ = rhs.payload_max_length_;
12652 has_payload_max_length_ = rhs.has_payload_max_length_;
12653 interface_ = rhs.interface_;
12654 proxy_host_ = rhs.proxy_host_;
12655 proxy_port_ = rhs.proxy_port_;
12656 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
12657 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
12658 proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
12659 no_proxy_entries_ = rhs.no_proxy_entries_;
12660 logger_ = rhs.logger_;
12661 error_logger_ = rhs.error_logger_;
12663#ifdef CPPHTTPLIB_SSL_ENABLED
12664 digest_auth_username_ = rhs.digest_auth_username_;
12665 digest_auth_password_ = rhs.digest_auth_password_;
12666 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
12667 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
12668 ca_cert_file_path_ = rhs.ca_cert_file_path_;
12669 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
12670 server_certificate_verification_ = rhs.server_certificate_verification_;
12671 server_hostname_verification_ = rhs.server_hostname_verification_;
12672 system_ca_mode_ = rhs.system_ca_mode_;
12677ClientImpl::is_proxy_enabled_for_host(
const std::string &host)
const {
12678 if (proxy_host_.empty() || proxy_port_ == -1) {
return false; }
12679 if (no_proxy_entries_.empty()) {
return true; }
12682 if (host == host_) {
12683 if (!host_normalized_valid_) {
12684 host_normalized_ = detail::normalize_target(host_);
12685 host_normalized_valid_ =
true;
12687 return !detail::host_matches_no_proxy(host_normalized_, no_proxy_entries_);
12689 auto target = detail::normalize_target(host);
12690 return !detail::host_matches_no_proxy(target, no_proxy_entries_);
12693inline socket_t ClientImpl::create_client_socket(Error &error)
const {
12694 if (is_proxy_enabled_for_host(host_)) {
12695 return detail::create_client_socket(
12696 proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
12697 ipv6_v6only_, socket_options_, connection_timeout_sec_,
12698 connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
12699 write_timeout_sec_, write_timeout_usec_, interface_, error);
12704 auto it = addr_map_.find(host_);
12705 if (it != addr_map_.end()) { ip = it->second; }
12707 return detail::create_client_socket(
12708 host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
12709 socket_options_, connection_timeout_sec_, connection_timeout_usec_,
12710 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
12711 write_timeout_usec_, interface_, error);
12714inline bool ClientImpl::create_and_connect_socket(Socket &socket,
12716 auto sock = create_client_socket(error);
12718 socket.sock = sock;
12722inline bool ClientImpl::ensure_socket_connection(Socket &socket, Error &error) {
12723 return create_and_connect_socket(socket, error);
12726inline bool ClientImpl::setup_proxy_connection(
12728 std::chrono::time_point<std::chrono::steady_clock> ,
12729 Response & ,
bool & , Error & ) {
12733inline void ClientImpl::shutdown_ssl(Socket & ,
12737 assert(socket_requests_in_flight_ == 0 ||
12738 socket_requests_are_from_thread_ == std::this_thread::get_id());
12741inline void ClientImpl::shutdown_socket(Socket &socket)
const {
12743 detail::shutdown_socket(socket.sock);
12746inline void ClientImpl::close_socket(Socket &socket) {
12753 assert(socket_requests_in_flight_ == 0 ||
12754 socket_requests_are_from_thread_ == std::this_thread::get_id());
12757#ifdef CPPHTTPLIB_SSL_ENABLED
12758 assert(socket.ssl ==
nullptr);
12762 detail::close_socket(socket.sock);
12766inline void ClientImpl::disconnect(
bool gracefully) {
12767 shutdown_ssl(socket_, gracefully);
12768 shutdown_socket(socket_);
12769 close_socket(socket_);
12772inline bool ClientImpl::read_response_line(Stream &strm,
const Request &req,
12774 bool skip_100_continue)
const {
12775 std::array<char, 2048> buf{};
12777 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
12779 if (!line_reader.getline()) {
return false; }
12781#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
12782 thread_local const std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
12784 thread_local const std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
12788 if (!std::regex_match(line_reader.ptr(), m, re)) {
12789 return req.method ==
"CONNECT";
12791 res.version = std::string(m[1]);
12792 res.status = std::stoi(std::string(m[2]));
12793 res.reason = std::string(m[3]);
12796 while (skip_100_continue && res.status == StatusCode::Continue_100) {
12797 if (!line_reader.getline()) {
return false; }
12798 if (!line_reader.getline()) {
return false; }
12800 if (!std::regex_match(line_reader.ptr(), m, re)) {
return false; }
12801 res.version = std::string(m[1]);
12802 res.status = std::stoi(std::string(m[2]));
12803 res.reason = std::string(m[3]);
12809inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
12810 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
12811 auto ret = send_(req, res, error);
12812 if (error == Error::SSLPeerCouldBeClosed_) {
12814 ret = send_(req, res, error);
12816 if (error == Error::SSLPeerCouldBeClosed_) { error = Error::Read; }
12821inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
12823 std::lock_guard<std::mutex> guard(socket_mutex_);
12828 socket_should_be_closed_when_request_is_done_ =
false;
12830 auto is_alive =
false;
12831 if (socket_.is_open()) {
12832 is_alive = detail::is_socket_alive(socket_.sock);
12834#ifdef CPPHTTPLIB_SSL_ENABLED
12835 if (is_alive && is_ssl()) {
12836 if (tls::is_peer_closed(socket_.ssl, socket_.sock)) {
12849 if (!ensure_socket_connection(socket_, error)) {
12850 output_error_log(error, &req);
12855 auto success =
true;
12856 if (!setup_proxy_connection(socket_, req.start_time_, res, success,
12858 if (!success) { output_error_log(error, &req); }
12867 if (socket_requests_in_flight_ > 1) {
12868 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
12870 socket_requests_in_flight_ += 1;
12871 socket_requests_are_from_thread_ = std::this_thread::get_id();
12874 for (
const auto &header : default_headers_) {
12875 if (req.headers.find(header.first) == req.headers.end()) {
12876 req.headers.insert(header);
12881 auto close_connection = !keep_alive_;
12883 auto se = detail::scope_exit([&]() {
12885 std::lock_guard<std::mutex> guard(socket_mutex_);
12886 socket_requests_in_flight_ -= 1;
12887 if (socket_requests_in_flight_ <= 0) {
12888 assert(socket_requests_in_flight_ == 0);
12889 socket_requests_are_from_thread_ = std::thread::id();
12892 if (socket_should_be_closed_when_request_is_done_ || close_connection ||
12898 ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {
12899 return handle_request(strm, req, res, close_connection, error);
12903 if (error == Error::Success) {
12904 error = Error::Unknown;
12905 output_error_log(error, &req);
12912inline Result ClientImpl::send(
const Request &req) {
12914 return send_(std::move(req2));
12917inline Result ClientImpl::send_(Request &&req) {
12918 auto res = detail::make_unique<Response>();
12919 auto error = Error::Success;
12920 auto ret = send(req, *res, error);
12921#ifdef CPPHTTPLIB_SSL_ENABLED
12922 return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers),
12923 last_ssl_error_, last_backend_error_};
12925 return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
12929inline void ClientImpl::prepare_default_headers(Request &r,
bool for_stream,
12930 const std::string &ct) {
12932 for (
const auto &header : default_headers_) {
12933 if (!r.has_header(header.first)) { r.headers.insert(header); }
12936 if (!r.has_header(
"Host")) {
12937 if (address_family_ == AF_UNIX) {
12938 r.headers.emplace(
"Host",
"localhost");
12941 "Host", detail::make_host_and_port_string(host_, port_, is_ssl()));
12945 if (!r.has_header(
"Accept")) { r.headers.emplace(
"Accept",
"*/*"); }
12947 if (!r.content_receiver) {
12948 if (!r.has_header(
"Accept-Encoding")) {
12949 std::string accept_encoding;
12950#ifdef CPPHTTPLIB_BROTLI_SUPPORT
12951 accept_encoding =
"br";
12953#ifdef CPPHTTPLIB_ZLIB_SUPPORT
12954 if (!accept_encoding.empty()) { accept_encoding +=
", "; }
12955 accept_encoding +=
"gzip, deflate";
12957#ifdef CPPHTTPLIB_ZSTD_SUPPORT
12958 if (!accept_encoding.empty()) { accept_encoding +=
", "; }
12959 accept_encoding +=
"zstd";
12961 r.set_header(
"Accept-Encoding", accept_encoding);
12964#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
12965 if (!r.has_header(
"User-Agent")) {
12967 r.set_header(
"User-Agent", agent);
12972 if (!r.body.empty()) {
12973 if (!ct.empty() && !r.has_header(
"Content-Type")) {
12974 r.headers.emplace(
"Content-Type", ct);
12976 if (!r.has_header(
"Content-Length")) {
12977 r.headers.emplace(
"Content-Length", std::to_string(r.body.size()));
12982inline ClientImpl::StreamHandle
12983ClientImpl::open_stream(
const std::string &method,
const std::string &path,
12984 const Params ¶ms,
const Headers &headers,
12985 const std::string &body,
12986 const std::string &content_type) {
12987 StreamHandle handle;
12988 handle.response = detail::make_unique<Response>();
12989 handle.error = Error::Success;
12991 auto query_path = params.empty() ? path : append_query_params(path, params);
12992 handle.connection_ = detail::make_unique<ClientConnection>();
12995 std::lock_guard<std::mutex> guard(socket_mutex_);
12997 auto is_alive =
false;
12998 if (socket_.is_open()) {
12999 is_alive = detail::is_socket_alive(socket_.sock);
13000#ifdef CPPHTTPLIB_SSL_ENABLED
13001 if (is_alive && is_ssl()) {
13002 if (tls::is_peer_closed(socket_.ssl, socket_.sock)) {
13007 if (!is_alive) { disconnect(
false); }
13011 if (!ensure_socket_connection(socket_, handle.error)) {
13012 handle.response.reset();
13017 auto success =
true;
13018 auto start_time = std::chrono::steady_clock::now();
13019 if (!setup_proxy_connection(socket_, start_time, *handle.response,
13020 success, handle.error)) {
13021 if (!success) { handle.response.reset(); }
13027 transfer_socket_ownership_to_handle(handle);
13030#ifdef CPPHTTPLIB_SSL_ENABLED
13031 if (is_ssl() && handle.connection_->session) {
13032 handle.socket_stream_ = detail::make_unique<detail::SSLSocketStream>(
13033 handle.connection_->sock, handle.connection_->session,
13034 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
13035 write_timeout_usec_);
13037 handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
13038 handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
13039 write_timeout_sec_, write_timeout_usec_);
13042 handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
13043 handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
13044 write_timeout_sec_, write_timeout_usec_);
13046 handle.stream_ = handle.socket_stream_.get();
13049 req.method = method;
13050 req.path = query_path;
13051 req.headers = headers;
13054 prepare_default_headers(req,
true, content_type);
13056 auto &strm = *handle.stream_;
13057 if (detail::write_request_line(strm, req.method, req.path) < 0) {
13058 handle.error = Error::Write;
13059 handle.response.reset();
13063 if (!detail::check_and_write_headers(strm, req.headers, header_writer_,
13065 handle.response.reset();
13069 if (!body.empty()) {
13070 if (strm.write(body.data(), body.size()) < 0) {
13071 handle.error = Error::Write;
13072 handle.response.reset();
13077 if (!read_response_line(strm, req, *handle.response) ||
13078 !detail::read_headers(strm, handle.response->headers)) {
13079 handle.error = Error::Read;
13080 handle.response.reset();
13084 handle.body_reader_.stream = handle.stream_;
13085 handle.body_reader_.payload_max_length = payload_max_length_;
13087 if (handle.response->has_header(
"Content-Length")) {
13088 bool is_invalid =
false;
13089 auto content_length = detail::get_header_value_u64(
13090 handle.response->headers,
"Content-Length", 0, 0, is_invalid);
13092 handle.error = Error::Read;
13093 handle.response.reset();
13096 handle.body_reader_.has_content_length =
true;
13097 handle.body_reader_.content_length = content_length;
13100 auto transfer_encoding =
13101 handle.response->get_header_value(
"Transfer-Encoding");
13102 handle.body_reader_.chunked = (transfer_encoding ==
"chunked");
13104 auto content_encoding = handle.response->get_header_value(
"Content-Encoding");
13105 if (!content_encoding.empty()) {
13106 handle.decompressor_ = detail::create_decompressor(content_encoding);
13112inline ssize_t ClientImpl::StreamHandle::read(
char *buf,
size_t len) {
13113 if (!is_valid() || !response) {
return -1; }
13115 if (decompressor_) {
return read_with_decompression(buf, len); }
13116 auto n = detail::read_body_content(stream_, body_reader_, buf, len);
13118 if (n <= 0 && body_reader_.chunked && !trailers_parsed_ && stream_) {
13119 trailers_parsed_ =
true;
13120 if (body_reader_.chunked_decoder) {
13121 if (!body_reader_.chunked_decoder->parse_trailers_into(
13122 response->trailers, response->headers)) {
13126 detail::ChunkedDecoder dec(*stream_);
13127 if (!dec.parse_trailers_into(response->trailers, response->headers)) {
13136inline ssize_t ClientImpl::StreamHandle::read_with_decompression(
char *buf,
13138 if (decompress_offset_ < decompress_buffer_.size()) {
13139 auto available = decompress_buffer_.size() - decompress_offset_;
13140 auto to_copy = (std::min)(len, available);
13141 std::memcpy(buf, decompress_buffer_.data() + decompress_offset_, to_copy);
13142 decompress_offset_ += to_copy;
13143 decompressed_bytes_read_ += to_copy;
13144 return static_cast<ssize_t
>(to_copy);
13147 decompress_buffer_.clear();
13148 decompress_offset_ = 0;
13150 constexpr size_t kDecompressionBufferSize = 8192;
13151 char compressed_buf[kDecompressionBufferSize];
13154 auto n = detail::read_body_content(stream_, body_reader_, compressed_buf,
13155 sizeof(compressed_buf));
13157 if (n <= 0) {
return n; }
13159 bool decompress_ok = decompressor_->decompress(
13160 compressed_buf,
static_cast<size_t>(n),
13161 [
this](
const char *data,
size_t data_len) {
13162 decompress_buffer_.append(data, data_len);
13163 auto limit = body_reader_.payload_max_length;
13164 if (decompressed_bytes_read_ + decompress_buffer_.size() > limit) {
13170 if (!decompress_ok) {
13171 body_reader_.last_error = Error::Read;
13175 if (!decompress_buffer_.empty()) {
break; }
13178 auto to_copy = (std::min)(len, decompress_buffer_.size());
13179 std::memcpy(buf, decompress_buffer_.data(), to_copy);
13180 decompress_offset_ = to_copy;
13181 decompressed_bytes_read_ += to_copy;
13182 return static_cast<ssize_t
>(to_copy);
13185inline void ClientImpl::StreamHandle::parse_trailers_if_needed() {
13186 if (!response || !stream_ || !body_reader_.chunked || trailers_parsed_) {
13190 trailers_parsed_ =
true;
13192 const auto bufsiz = 128;
13193 char line_buf[bufsiz];
13194 detail::stream_line_reader line_reader(*stream_, line_buf, bufsiz);
13196 if (!line_reader.getline()) {
return; }
13198 if (!detail::parse_trailers(line_reader, response->trailers,
13199 response->headers)) {
13206inline ChunkedDecoder::ChunkedDecoder(Stream &s) : strm(s) {}
13208inline ssize_t ChunkedDecoder::read_payload(
char *buf,
size_t len,
13209 size_t &out_chunk_offset,
13210 size_t &out_chunk_total) {
13211 if (finished) {
return 0; }
13213 if (chunk_remaining == 0) {
13214 stream_line_reader lr(strm, line_buf,
sizeof(line_buf));
13215 if (!lr.getline()) {
return -1; }
13218 const char *p = lr.ptr();
13220 if (!is_hex(*p, v)) {
return -1; }
13222 size_t chunk_len = 0;
13223 constexpr size_t chunk_len_max = (std::numeric_limits<size_t>::max)();
13224 for (; is_hex(*p, v); ++p) {
13225 if (chunk_len > (chunk_len_max >> 4)) {
return -1; }
13226 chunk_len = (chunk_len << 4) | static_cast<size_t>(v);
13229 while (is_space_or_tab(*p)) {
13232 if (*p !=
'\0' && *p !=
';' && *p !=
'\r' && *p !=
'\n') {
return -1; }
13234 if (chunk_len == 0) {
13235 chunk_remaining = 0;
13237 out_chunk_offset = 0;
13238 out_chunk_total = 0;
13242 chunk_remaining = chunk_len;
13243 last_chunk_total = chunk_remaining;
13244 last_chunk_offset = 0;
13247 auto to_read = (std::min)(chunk_remaining, len);
13248 auto n = strm.read(buf, to_read);
13249 if (n <= 0) {
return -1; }
13251 auto offset_before = last_chunk_offset;
13252 last_chunk_offset +=
static_cast<size_t>(n);
13253 chunk_remaining -=
static_cast<size_t>(n);
13255 out_chunk_offset = offset_before;
13256 out_chunk_total = last_chunk_total;
13258 if (chunk_remaining == 0) {
13259 stream_line_reader lr(strm, line_buf,
sizeof(line_buf));
13260 if (!lr.getline()) {
return -1; }
13261 if (std::strcmp(lr.ptr(),
"\r\n") != 0) {
return -1; }
13267inline bool ChunkedDecoder::parse_trailers_into(Headers &dest,
13268 const Headers &src_headers) {
13269 stream_line_reader lr(strm, line_buf,
sizeof(line_buf));
13270 if (!lr.getline()) {
return false; }
13271 return parse_trailers(lr, dest, src_headers);
13277ClientImpl::transfer_socket_ownership_to_handle(StreamHandle &handle) {
13278 handle.connection_->sock = socket_.sock;
13279#ifdef CPPHTTPLIB_SSL_ENABLED
13280 handle.connection_->session = socket_.ssl;
13281 socket_.ssl =
nullptr;
13286inline bool ClientImpl::handle_request(Stream &strm, Request &req,
13287 Response &res,
bool close_connection,
13289 if (req.path.empty()) {
13290 error = Error::Connection;
13291 output_error_log(error, &req);
13295 auto req_save = req;
13299 if (!is_ssl() && is_proxy_enabled_for_host(host_)) {
13301 req2.path =
"http://" +
13302 detail::make_host_and_port_string(host_, port_,
false) +
13304 ret = process_request(strm, req2, res, close_connection, error);
13305 req = std::move(req2);
13306 req.path = req_save.path;
13308 ret = process_request(strm, req, res, close_connection, error);
13311 if (!ret) {
return false; }
13313 if (res.get_header_value(
"Connection") ==
"close" ||
13314 (res.version ==
"HTTP/1.0" && res.reason !=
"Connection established")) {
13322 std::lock_guard<std::mutex> guard(socket_mutex_);
13326 if (300 < res.status && res.status < 400 && follow_location_) {
13327 req = std::move(req_save);
13328 ret = redirect(req, res, error);
13331#ifdef CPPHTTPLIB_SSL_ENABLED
13332 if ((res.status == StatusCode::Unauthorized_401 ||
13333 res.status == StatusCode::ProxyAuthenticationRequired_407) &&
13334 req.authorization_count_ < 5) {
13335 auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
13340 if (is_proxy && !(!is_ssl() && is_proxy_enabled_for_host(host_))) {
13344 const auto &username =
13345 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
13346 const auto &password =
13347 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
13349 if (!username.empty() && !password.empty()) {
13350 std::map<std::string, std::string> auth;
13351 if (detail::parse_www_authenticate(res, auth, is_proxy)) {
13352 Request new_req = req;
13353 new_req.authorization_count_ += 1;
13354 new_req.headers.erase(is_proxy ?
"Proxy-Authorization"
13355 :
"Authorization");
13356 new_req.headers.insert(detail::make_digest_authentication_header(
13357 req, auth, new_req.authorization_count_, detail::random_string(10),
13358 username, password, is_proxy));
13362 ret = send(new_req, new_res, error);
13363 if (ret) { res = std::move(new_res); }
13372inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
13373 if (req.redirect_count_ == 0) {
13374 error = Error::ExceedRedirectCount;
13375 output_error_log(error, &req);
13379 auto location = res.get_header_value(
"location");
13380 if (location.empty()) {
return false; }
13382 detail::UrlComponents uc;
13383 if (!detail::parse_url(location, uc)) {
return false; }
13386 if (!uc.scheme.empty() && uc.scheme !=
"http" && uc.scheme !=
"https") {
13390 auto scheme = is_ssl() ?
"https" :
"http";
13392 auto next_scheme = std::move(uc.scheme);
13393 auto next_host = std::move(uc.host);
13394 auto port_str = std::move(uc.port);
13395 auto next_path = std::move(uc.path);
13396 auto next_query = std::move(uc.query);
13398 auto next_port = port_;
13399 if (!port_str.empty()) {
13400 if (!detail::parse_port(port_str, next_port)) {
return false; }
13401 }
else if (!next_scheme.empty()) {
13402 next_port = next_scheme ==
"https" ? 443 : 80;
13405 if (next_scheme.empty()) { next_scheme = scheme; }
13406 if (next_host.empty()) { next_host = host_; }
13407 if (next_path.empty()) { next_path =
"/"; }
13409 auto path = decode_path_component(next_path) + next_query;
13412 if (next_scheme == scheme && next_host == host_ && next_port == port_) {
13413 return detail::redirect(*
this, req, res, path, location, error);
13417 return create_redirect_client(next_scheme, next_host, next_port, req, res,
13418 path, location, error);
13422inline bool ClientImpl::create_redirect_client(
13423 const std::string &scheme,
const std::string &host,
int port, Request &req,
13424 Response &res,
const std::string &path,
const std::string &location,
13427 auto need_ssl = (scheme ==
"https");
13431 auto headers_to_remove =
13432 std::vector<std::string>{
"Host",
"Proxy-Authorization",
"Authorization"};
13434 for (
const auto &header_name : headers_to_remove) {
13435 auto it = req.headers.find(header_name);
13436 while (it != req.headers.end()) {
13437 it = req.headers.erase(it);
13438 it = req.headers.find(header_name);
13444#ifdef CPPHTTPLIB_SSL_ENABLED
13446 SSLClient redirect_client(host, port);
13449 setup_redirect_client(redirect_client);
13451 redirect_client.enable_server_certificate_verification(
13452 server_certificate_verification_);
13453 redirect_client.enable_server_hostname_verification(
13454 server_hostname_verification_);
13455 redirect_client.system_ca_mode_ = system_ca_mode_;
13458 if (!ca_cert_pem_.empty()) {
13459 redirect_client.load_ca_cert_store(ca_cert_pem_.c_str(),
13460 ca_cert_pem_.size());
13462 if (!ca_cert_file_path_.empty()) {
13463 redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
13472 return detail::redirect(redirect_client, req, res, path, location, error);
13475 error = Error::SSLConnection;
13476 output_error_log(error, &req);
13481 ClientImpl redirect_client(host, port);
13484 setup_redirect_client(redirect_client);
13487 return detail::redirect(redirect_client, req, res, path, location, error);
13493template <
typename ClientType>
13494inline void ClientImpl::setup_redirect_client(ClientType &client) {
13496 client.set_connection_timeout(connection_timeout_sec_);
13497 client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
13498 client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
13499 client.set_keep_alive(keep_alive_);
13500 client.set_follow_location(
13502 client.set_path_encode(path_encode_);
13503 client.set_compress(compress_);
13504 client.set_decompress(decompress_);
13515 client.no_proxy_entries_ = no_proxy_entries_;
13516 if (!proxy_host_.empty() && proxy_port_ != -1) {
13517 client.set_proxy(proxy_host_, proxy_port_);
13519 if (!proxy_basic_auth_username_.empty()) {
13520 client.set_proxy_basic_auth(proxy_basic_auth_username_,
13521 proxy_basic_auth_password_);
13523 if (!proxy_bearer_token_auth_token_.empty()) {
13524 client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
13526#ifdef CPPHTTPLIB_SSL_ENABLED
13527 if (!proxy_digest_auth_username_.empty()) {
13528 client.set_proxy_digest_auth(proxy_digest_auth_username_,
13529 proxy_digest_auth_password_);
13535 client.set_address_family(address_family_);
13536 client.set_tcp_nodelay(tcp_nodelay_);
13537 client.set_ipv6_v6only(ipv6_v6only_);
13538 if (socket_options_) { client.set_socket_options(socket_options_); }
13539 if (!interface_.empty()) { client.set_interface(interface_); }
13542 if (logger_) { client.set_logger(logger_); }
13543 if (error_logger_) { client.set_error_logger(error_logger_); }
13549inline bool ClientImpl::write_content_with_provider(Stream &strm,
13550 const Request &req,
13551 Error &error)
const {
13552 auto is_shutting_down = []() {
return false; };
13554 if (req.is_chunked_content_provider_) {
13555 auto compressor = compress_ ? detail::create_compressor().first
13556 : std::unique_ptr<detail::compressor>();
13558 compressor = detail::make_unique<detail::nocompressor>();
13561 return detail::write_content_chunked(strm, req.content_provider_,
13562 is_shutting_down, *compressor, error);
13564 return detail::write_content_with_progress(
13565 strm, req.content_provider_, 0, req.content_length_, is_shutting_down,
13566 req.upload_progress, error);
13570inline bool ClientImpl::write_request(Stream &strm, Request &req,
13571 bool close_connection, Error &error,
13574 if (close_connection) {
13575 if (!req.has_header(
"Connection")) {
13576 req.set_header(
"Connection",
"close");
13580 std::string ct_for_defaults;
13581 if (!req.has_header(
"Content-Type") && !req.body.empty()) {
13582 ct_for_defaults =
"text/plain";
13584 prepare_default_headers(req,
false, ct_for_defaults);
13586 if (req.body.empty()) {
13587 if (req.content_provider_) {
13588 if (!req.is_chunked_content_provider_) {
13589 if (!req.has_header(
"Content-Length")) {
13590 auto length = std::to_string(req.content_length_);
13591 req.set_header(
"Content-Length", length);
13595 if (req.method ==
"POST" || req.method ==
"PUT" ||
13596 req.method ==
"PATCH") {
13597 req.set_header(
"Content-Length",
"0");
13602 if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
13603 if (!req.has_header(
"Authorization")) {
13604 req.headers.insert(make_basic_authentication_header(
13605 basic_auth_username_, basic_auth_password_,
false));
13609 if (!bearer_token_auth_token_.empty()) {
13610 if (!req.has_header(
"Authorization")) {
13611 req.headers.insert(make_bearer_token_authentication_header(
13612 bearer_token_auth_token_,
false));
13619 if (is_proxy_enabled_for_host(host_)) {
13620 if (!proxy_basic_auth_username_.empty() &&
13621 !proxy_basic_auth_password_.empty() &&
13622 !req.has_header(
"Proxy-Authorization")) {
13623 req.headers.insert(make_basic_authentication_header(
13624 proxy_basic_auth_username_, proxy_basic_auth_password_,
true));
13626 if (!proxy_bearer_token_auth_token_.empty() &&
13627 !req.has_header(
"Proxy-Authorization")) {
13628 req.headers.insert(make_bearer_token_authentication_header(
13629 proxy_bearer_token_auth_token_,
true));
13635 detail::BufferStream bstrm;
13638 std::string path_part, query_part;
13639 auto query_pos = req.path.find(
'?');
13640 if (query_pos != std::string::npos) {
13641 path_part = req.path.substr(0, query_pos);
13642 query_part = req.path.substr(query_pos + 1);
13644 path_part = req.path;
13655 auto path_with_query =
13656 path_encode_ ? detail::encode_path(path_part) : path_part;
13658 if (!query_part.empty()) {
13661 auto normalized = detail::normalize_query_string(query_part);
13662 if (!normalized.empty()) { path_with_query +=
'?' + normalized; }
13665 detail::parse_query_text(query_part, req.params);
13670 detail::parse_query_text(query_part, req.params);
13671 if (!req.params.empty()) {
13672 path_with_query = append_query_params(path_with_query, req.params);
13677 detail::write_request_line(bstrm, req.method, path_with_query);
13678 if (!detail::check_and_write_headers(bstrm, req.headers, header_writer_,
13680 output_error_log(error, &req);
13685 auto &data = bstrm.get_buffer();
13686 if (!detail::write_data(strm, data.data(), data.size())) {
13687 error = Error::Write;
13688 output_error_log(error, &req);
13708 auto start = std::chrono::high_resolution_clock::now();
13714 auto sock = strm.socket();
13715 if (sock !=
INVALID_SOCKET && detail::select_read(sock, 0, 0) > 0) {
13723 if (!is_ssl() && strm.is_readable()) {
return false; }
13725 auto now = std::chrono::high_resolution_clock::now();
13727 std::chrono::duration_cast<std::chrono::milliseconds>(now - start)
13733 std::this_thread::sleep_for(std::chrono::milliseconds(1));
13739 if (skip_body) {
return true; }
13741 return write_request_body(strm, req, error);
13744inline bool ClientImpl::write_request_body(Stream &strm, Request &req,
13746 if (req.body.empty()) {
13747 return write_content_with_provider(strm, req, error);
13750 if (req.upload_progress) {
13751 auto body_size = req.body.size();
13752 size_t written = 0;
13753 auto data = req.body.data();
13755 while (written < body_size) {
13757 if (!detail::write_data(strm, data + written, to_write)) {
13758 error = Error::Write;
13759 output_error_log(error, &req);
13762 written += to_write;
13764 if (!req.upload_progress(written, body_size)) {
13765 error = Error::Canceled;
13766 output_error_log(error, &req);
13771 if (!detail::write_data(strm, req.body.data(), req.body.size())) {
13772 error = Error::Write;
13773 output_error_log(error, &req);
13781inline std::unique_ptr<Response>
13782ClientImpl::send_with_content_provider_and_receiver(
13783 Request &req,
const char *body,
size_t content_length,
13784 ContentProvider content_provider,
13785 ContentProviderWithoutLength content_provider_without_length,
13786 const std::string &content_type, ContentReceiver content_receiver,
13788 if (!content_type.empty()) { req.set_header(
"Content-Type", content_type); }
13790 auto enc = compress_
13791 ? detail::create_compressor()
13792 : std::pair<std::unique_ptr<detail::compressor>, const char *>(
13795 if (enc.second) { req.set_header(
"Content-Encoding", enc.second); }
13797 if (enc.first && !content_provider_without_length) {
13798 auto &compressor = enc.first;
13800 if (content_provider) {
13803 DataSink data_sink;
13805 data_sink.write = [&](const char *data, size_t data_len) -> bool {
13807 auto last = offset + data_len == content_length;
13809 auto ret = compressor->compress(
13810 data, data_len, last,
13811 [&](const char *compressed_data, size_t compressed_data_len) {
13812 req.body.append(compressed_data, compressed_data_len);
13817 offset += data_len;
13825 while (ok && offset < content_length) {
13826 if (!content_provider(offset, content_length - offset, data_sink)) {
13827 error = Error::Canceled;
13828 output_error_log(error, &req);
13833 if (!compressor->compress(body, content_length, true,
13834 [&](const char *data, size_t data_len) {
13835 req.body.append(data, data_len);
13838 error = Error::Compression;
13839 output_error_log(error, &req);
13844 if (content_provider) {
13845 req.content_length_ = content_length;
13846 req.content_provider_ = std::move(content_provider);
13847 req.is_chunked_content_provider_ =
false;
13848 }
else if (content_provider_without_length) {
13849 req.content_length_ = 0;
13850 req.content_provider_ = detail::ContentProviderAdapter(
13851 std::move(content_provider_without_length));
13852 req.is_chunked_content_provider_ =
true;
13853 req.set_header(
"Transfer-Encoding",
"chunked");
13855 req.body.assign(body, content_length);
13859 if (content_receiver) {
13860 req.content_receiver =
13861 [content_receiver](
const char *data,
size_t data_length,
13862 size_t ,
size_t ) {
13863 return content_receiver(data, data_length);
13867 auto res = detail::make_unique<Response>();
13868 return send(req, *res, error) ? std::move(res) : nullptr;
13871inline Result ClientImpl::send_with_content_provider_and_receiver(
13872 const std::string &method,
const std::string &path,
const Headers &headers,
13873 const char *body,
size_t content_length, ContentProvider content_provider,
13874 ContentProviderWithoutLength content_provider_without_length,
13875 const std::string &content_type, ContentReceiver content_receiver,
13876 UploadProgress progress) {
13878 req.method = method;
13879 req.headers = headers;
13881 req.upload_progress = std::move(progress);
13882 if (max_timeout_msec_ > 0) {
13883 req.start_time_ = std::chrono::steady_clock::now();
13886 auto error = Error::Success;
13888 auto res = send_with_content_provider_and_receiver(
13889 req, body, content_length, std::move(content_provider),
13890 std::move(content_provider_without_length), content_type,
13891 std::move(content_receiver), error);
13893#ifdef CPPHTTPLIB_SSL_ENABLED
13894 return Result{std::move(res), error, std::move(req.headers), last_ssl_error_,
13895 last_backend_error_};
13897 return Result{std::move(res), error, std::move(req.headers)};
13901inline void ClientImpl::output_log(
const Request &req,
13902 const Response &res)
const {
13904 std::lock_guard<std::mutex> guard(logger_mutex_);
13909inline void ClientImpl::output_error_log(
const Error &err,
13910 const Request *req)
const {
13911 if (error_logger_) {
13912 std::lock_guard<std::mutex> guard(logger_mutex_);
13913 error_logger_(err, req);
13917inline bool ClientImpl::process_request(Stream &strm, Request &req,
13918 Response &res,
bool close_connection,
13922 auto body_size = req.body.empty() ? req.content_length_ : req.body.size();
13924 req.set_header(
"Expect",
"100-continue");
13929 auto expect_100_continue = req.get_header_value(
"Expect") ==
"100-continue";
13932 auto write_request_success =
13933 write_request(strm, req, close_connection, error, expect_100_continue);
13935#ifdef CPPHTTPLIB_SSL_ENABLED
13936 if (is_ssl() && !expect_100_continue) {
13937 auto is_proxy_enabled = is_proxy_enabled_for_host(host_);
13938 if (!is_proxy_enabled) {
13939 if (tls::is_peer_closed(socket_.ssl, socket_.sock)) {
13940 error = Error::SSLPeerCouldBeClosed_;
13941 output_error_log(error, &req);
13957 auto status_line_read =
false;
13958 if (expect_100_continue && write_request_success) {
13962 strm.set_read_timeout(sec, usec);
13963 status_line_read = read_response_line(strm, req, res,
false);
13964 strm.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
13967 if (!status_line_read) {
13970 if (!write_request_body(strm, req, error)) {
return false; }
13971 expect_100_continue =
false;
13977 if ((!status_line_read &&
13978 !read_response_line(strm, req, res, !expect_100_continue)) ||
13979 !detail::read_headers(strm, res.headers)) {
13980 if (write_request_success) { error = Error::Read; }
13981 output_error_log(error, &req);
13985 if (!write_request_success) {
return false; }
13988 if (expect_100_continue) {
13989 if (res.status == StatusCode::Continue_100) {
13991 if (!write_request_body(strm, req, error)) {
return false; }
13994 res.headers.clear();
13996 if (!read_response_line(strm, req, res) ||
13997 !detail::read_headers(strm, res.headers)) {
13998 error = Error::Read;
13999 output_error_log(error, &req);
14007 if ((res.status != StatusCode::NoContent_204) && req.method !=
"HEAD" &&
14008 req.method !=
"CONNECT") {
14009 auto redirect = 300 < res.status && res.status < 400 &&
14010 res.status != StatusCode::NotModified_304 &&
14013 if (req.response_handler && !redirect) {
14014 if (!req.response_handler(res)) {
14015 error = Error::Canceled;
14016 output_error_log(error, &req);
14022 req.content_receiver
14023 ?
static_cast<ContentReceiverWithProgress
>(
14024 [&](
const char *buf,
size_t n,
size_t off,
size_t len) {
14025 if (redirect) {
return true; }
14026 auto ret = req.content_receiver(buf, n, off, len);
14028 error = Error::Canceled;
14029 output_error_log(error, &req);
14033 : static_cast<ContentReceiverWithProgress>(
14034 [&](const char *buf, size_t n, size_t ,
14036 assert(res.body.size() + n <= res.body.max_size());
14037 if (payload_max_length_ > 0 &&
14038 (res.body.size() >= payload_max_length_ ||
14039 n > payload_max_length_ - res.body.size())) {
14042 res.body.append(buf, n);
14046 auto progress = [&](
size_t current,
size_t total) {
14047 if (!req.download_progress || redirect) {
return true; }
14048 auto ret = req.download_progress(current, total);
14050 error = Error::Canceled;
14051 output_error_log(error, &req);
14056 if (res.has_header(
"Content-Length")) {
14057 if (!req.content_receiver) {
14058 auto len = res.get_header_value_u64(
"Content-Length");
14059 if (len > res.body.max_size()) {
14060 error = Error::Read;
14061 output_error_log(error, &req);
14068 auto reserve_len =
static_cast<size_t>(len);
14069 if (payload_max_length_ > 0 && reserve_len > payload_max_length_) {
14070 reserve_len = payload_max_length_;
14072 res.body.reserve(reserve_len);
14076 if (res.status != StatusCode::NotModified_304) {
14078 auto max_length = (!has_payload_max_length_ && req.content_receiver)
14079 ? (std::numeric_limits<size_t>::max)()
14080 : payload_max_length_;
14081 if (!detail::read_content(strm, res, max_length, dummy_status,
14082 std::move(progress), std::move(out),
14084 if (error != Error::Canceled) { error = Error::Read; }
14085 output_error_log(error, &req);
14092 output_log(req, res);
14097inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
14098 const std::string &boundary,
const UploadFormDataItems &items,
14099 const FormDataProviderItems &provider_items)
const {
14100 size_t cur_item = 0;
14101 size_t cur_start = 0;
14104 return [&, cur_item, cur_start](
size_t offset,
14105 DataSink &sink)
mutable ->
bool {
14106 if (!offset && !items.empty()) {
14107 sink.os << detail::serialize_multipart_formdata(items, boundary,
false);
14109 }
else if (cur_item < provider_items.size()) {
14111 const auto &begin = detail::serialize_multipart_formdata_item_begin(
14112 provider_items[cur_item], boundary);
14113 offset += begin.size();
14114 cur_start = offset;
14119 auto has_data =
true;
14120 cur_sink.write = sink.write;
14121 cur_sink.done = [&]() { has_data =
false; };
14123 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
14128 sink.os << detail::serialize_multipart_formdata_item_end();
14134 sink.os << detail::serialize_multipart_formdata_finish(boundary);
14141inline bool ClientImpl::process_socket(
14142 const Socket &socket,
14143 std::chrono::time_point<std::chrono::steady_clock> start_time,
14144 std::function<
bool(Stream &strm)> callback) {
14145 return detail::process_client_socket(
14146 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
14147 write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
14150inline bool ClientImpl::is_ssl()
const {
return false; }
14152inline Result ClientImpl::Get(
const std::string &path,
14153 DownloadProgress progress) {
14154 return Get(path, Headers(), std::move(progress));
14157inline Result ClientImpl::Get(
const std::string &path,
const Params ¶ms,
14158 const Headers &headers,
14159 DownloadProgress progress) {
14160 if (params.empty()) {
return Get(path, headers); }
14162 std::string path_with_query = append_query_params(path, params);
14163 return Get(path_with_query, headers, std::move(progress));
14166inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers,
14167 DownloadProgress progress) {
14169 req.method =
"GET";
14171 req.headers = headers;
14172 req.download_progress = std::move(progress);
14173 if (max_timeout_msec_ > 0) {
14174 req.start_time_ = std::chrono::steady_clock::now();
14177 return send_(std::move(req));
14180inline Result ClientImpl::Get(
const std::string &path,
14181 ContentReceiver content_receiver,
14182 DownloadProgress progress) {
14183 return Get(path, Headers(),
nullptr, std::move(content_receiver),
14184 std::move(progress));
14187inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers,
14188 ContentReceiver content_receiver,
14189 DownloadProgress progress) {
14190 return Get(path, headers,
nullptr, std::move(content_receiver),
14191 std::move(progress));
14194inline Result ClientImpl::Get(
const std::string &path,
14195 ResponseHandler response_handler,
14196 ContentReceiver content_receiver,
14197 DownloadProgress progress) {
14198 return Get(path, Headers(), std::move(response_handler),
14199 std::move(content_receiver), std::move(progress));
14202inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers,
14203 ResponseHandler response_handler,
14204 ContentReceiver content_receiver,
14205 DownloadProgress progress) {
14207 req.method =
"GET";
14209 req.headers = headers;
14210 req.response_handler = std::move(response_handler);
14211 req.content_receiver =
14212 [content_receiver](
const char *data,
size_t data_length,
14213 size_t ,
size_t ) {
14214 return content_receiver(data, data_length);
14216 req.download_progress = std::move(progress);
14217 if (max_timeout_msec_ > 0) {
14218 req.start_time_ = std::chrono::steady_clock::now();
14221 return send_(std::move(req));
14224inline Result ClientImpl::Get(
const std::string &path,
const Params ¶ms,
14225 const Headers &headers,
14226 ContentReceiver content_receiver,
14227 DownloadProgress progress) {
14228 return Get(path, params, headers,
nullptr, std::move(content_receiver),
14229 std::move(progress));
14232inline Result ClientImpl::Get(
const std::string &path,
const Params ¶ms,
14233 const Headers &headers,
14234 ResponseHandler response_handler,
14235 ContentReceiver content_receiver,
14236 DownloadProgress progress) {
14237 if (params.empty()) {
14238 return Get(path, headers, std::move(response_handler),
14239 std::move(content_receiver), std::move(progress));
14242 std::string path_with_query = append_query_params(path, params);
14243 return Get(path_with_query, headers, std::move(response_handler),
14244 std::move(content_receiver), std::move(progress));
14247inline Result ClientImpl::Head(
const std::string &path) {
14248 return Head(path, Headers());
14251inline Result ClientImpl::Head(
const std::string &path,
14252 const Headers &headers) {
14254 req.method =
"HEAD";
14255 req.headers = headers;
14257 if (max_timeout_msec_ > 0) {
14258 req.start_time_ = std::chrono::steady_clock::now();
14261 return send_(std::move(req));
14264inline Result ClientImpl::Post(
const std::string &path) {
14265 return Post(path, std::string(), std::string());
14268inline Result ClientImpl::Post(
const std::string &path,
14269 const Headers &headers) {
14270 return Post(path, headers,
nullptr, 0, std::string());
14273inline Result ClientImpl::Post(
const std::string &path,
const char *body,
14274 size_t content_length,
14275 const std::string &content_type,
14276 UploadProgress progress) {
14277 return Post(path, Headers(), body, content_length, content_type, progress);
14280inline Result ClientImpl::Post(
const std::string &path,
const std::string &body,
14281 const std::string &content_type,
14282 UploadProgress progress) {
14283 return Post(path, Headers(), body, content_type, progress);
14286inline Result ClientImpl::Post(
const std::string &path,
const Params ¶ms) {
14287 return Post(path, Headers(), params);
14290inline Result ClientImpl::Post(
const std::string &path,
size_t content_length,
14291 ContentProvider content_provider,
14292 const std::string &content_type,
14293 UploadProgress progress) {
14294 return Post(path, Headers(), content_length, std::move(content_provider),
14295 content_type, progress);
14298inline Result ClientImpl::Post(
const std::string &path,
size_t content_length,
14299 ContentProvider content_provider,
14300 const std::string &content_type,
14301 ContentReceiver content_receiver,
14302 UploadProgress progress) {
14303 return Post(path, Headers(), content_length, std::move(content_provider),
14304 content_type, std::move(content_receiver), progress);
14307inline Result ClientImpl::Post(
const std::string &path,
14308 ContentProviderWithoutLength content_provider,
14309 const std::string &content_type,
14310 UploadProgress progress) {
14311 return Post(path, Headers(), std::move(content_provider), content_type,
14315inline Result ClientImpl::Post(
const std::string &path,
14316 ContentProviderWithoutLength content_provider,
14317 const std::string &content_type,
14318 ContentReceiver content_receiver,
14319 UploadProgress progress) {
14320 return Post(path, Headers(), std::move(content_provider), content_type,
14321 std::move(content_receiver), progress);
14324inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14325 const Params ¶ms) {
14326 auto query = detail::params_to_query_str(params);
14327 return Post(path, headers, query,
"application/x-www-form-urlencoded");
14330inline Result ClientImpl::Post(
const std::string &path,
14331 const UploadFormDataItems &items,
14332 UploadProgress progress) {
14333 return Post(path, Headers(), items, progress);
14336inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14337 const UploadFormDataItems &items,
14338 UploadProgress progress) {
14339 const auto &boundary = detail::make_multipart_data_boundary();
14340 const auto &content_type =
14341 detail::serialize_multipart_formdata_get_content_type(boundary);
14342 auto content_length = detail::get_multipart_content_length(items, boundary);
14343 return Post(path, headers, content_length,
14344 detail::make_multipart_content_provider(items, boundary),
14345 content_type, progress);
14348inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14349 const UploadFormDataItems &items,
14350 const std::string &boundary,
14351 UploadProgress progress) {
14352 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
14353 return Result{
nullptr, Error::UnsupportedMultipartBoundaryChars};
14356 const auto &content_type =
14357 detail::serialize_multipart_formdata_get_content_type(boundary);
14358 auto content_length = detail::get_multipart_content_length(items, boundary);
14359 return Post(path, headers, content_length,
14360 detail::make_multipart_content_provider(items, boundary),
14361 content_type, progress);
14364inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14365 const char *body,
size_t content_length,
14366 const std::string &content_type,
14367 UploadProgress progress) {
14368 return send_with_content_provider_and_receiver(
14369 "POST", path, headers, body, content_length,
nullptr,
nullptr,
14370 content_type,
nullptr, progress);
14373inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14374 const std::string &body,
14375 const std::string &content_type,
14376 UploadProgress progress) {
14377 return send_with_content_provider_and_receiver(
14378 "POST", path, headers, body.data(), body.size(),
nullptr,
nullptr,
14379 content_type,
nullptr, progress);
14382inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14383 size_t content_length,
14384 ContentProvider content_provider,
14385 const std::string &content_type,
14386 UploadProgress progress) {
14387 return send_with_content_provider_and_receiver(
14388 "POST", path, headers,
nullptr, content_length,
14389 std::move(content_provider),
nullptr, content_type,
nullptr, progress);
14392inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14393 size_t content_length,
14394 ContentProvider content_provider,
14395 const std::string &content_type,
14396 ContentReceiver content_receiver,
14397 DownloadProgress progress) {
14398 return send_with_content_provider_and_receiver(
14399 "POST", path, headers,
nullptr, content_length,
14400 std::move(content_provider),
nullptr, content_type,
14401 std::move(content_receiver), std::move(progress));
14404inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14405 ContentProviderWithoutLength content_provider,
14406 const std::string &content_type,
14407 UploadProgress progress) {
14408 return send_with_content_provider_and_receiver(
14409 "POST", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14410 content_type,
nullptr, progress);
14413inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14414 ContentProviderWithoutLength content_provider,
14415 const std::string &content_type,
14416 ContentReceiver content_receiver,
14417 DownloadProgress progress) {
14418 return send_with_content_provider_and_receiver(
14419 "POST", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14420 content_type, std::move(content_receiver), std::move(progress));
14423inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14424 const UploadFormDataItems &items,
14425 const FormDataProviderItems &provider_items,
14426 UploadProgress progress) {
14427 const auto &boundary = detail::make_multipart_data_boundary();
14428 const auto &content_type =
14429 detail::serialize_multipart_formdata_get_content_type(boundary);
14430 return send_with_content_provider_and_receiver(
14431 "POST", path, headers,
nullptr, 0,
nullptr,
14432 get_multipart_content_provider(boundary, items, provider_items),
14433 content_type,
nullptr, progress);
14436inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
14437 const std::string &body,
14438 const std::string &content_type,
14439 ContentReceiver content_receiver,
14440 DownloadProgress progress) {
14442 req.method =
"POST";
14444 req.headers = headers;
14446 req.content_receiver =
14447 [content_receiver](
const char *data,
size_t data_length,
14448 size_t ,
size_t ) {
14449 return content_receiver(data, data_length);
14451 req.download_progress = std::move(progress);
14453 if (max_timeout_msec_ > 0) {
14454 req.start_time_ = std::chrono::steady_clock::now();
14457 if (!content_type.empty()) { req.set_header(
"Content-Type", content_type); }
14459 return send_(std::move(req));
14462inline Result ClientImpl::Put(
const std::string &path) {
14463 return Put(path, std::string(), std::string());
14466inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers) {
14467 return Put(path, headers,
nullptr, 0, std::string());
14470inline Result ClientImpl::Put(
const std::string &path,
const char *body,
14471 size_t content_length,
14472 const std::string &content_type,
14473 UploadProgress progress) {
14474 return Put(path, Headers(), body, content_length, content_type, progress);
14477inline Result ClientImpl::Put(
const std::string &path,
const std::string &body,
14478 const std::string &content_type,
14479 UploadProgress progress) {
14480 return Put(path, Headers(), body, content_type, progress);
14483inline Result ClientImpl::Put(
const std::string &path,
const Params ¶ms) {
14484 return Put(path, Headers(), params);
14487inline Result ClientImpl::Put(
const std::string &path,
size_t content_length,
14488 ContentProvider content_provider,
14489 const std::string &content_type,
14490 UploadProgress progress) {
14491 return Put(path, Headers(), content_length, std::move(content_provider),
14492 content_type, progress);
14495inline Result ClientImpl::Put(
const std::string &path,
size_t content_length,
14496 ContentProvider content_provider,
14497 const std::string &content_type,
14498 ContentReceiver content_receiver,
14499 UploadProgress progress) {
14500 return Put(path, Headers(), content_length, std::move(content_provider),
14501 content_type, std::move(content_receiver), progress);
14504inline Result ClientImpl::Put(
const std::string &path,
14505 ContentProviderWithoutLength content_provider,
14506 const std::string &content_type,
14507 UploadProgress progress) {
14508 return Put(path, Headers(), std::move(content_provider), content_type,
14512inline Result ClientImpl::Put(
const std::string &path,
14513 ContentProviderWithoutLength content_provider,
14514 const std::string &content_type,
14515 ContentReceiver content_receiver,
14516 UploadProgress progress) {
14517 return Put(path, Headers(), std::move(content_provider), content_type,
14518 std::move(content_receiver), progress);
14521inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14522 const Params ¶ms) {
14523 auto query = detail::params_to_query_str(params);
14524 return Put(path, headers, query,
"application/x-www-form-urlencoded");
14527inline Result ClientImpl::Put(
const std::string &path,
14528 const UploadFormDataItems &items,
14529 UploadProgress progress) {
14530 return Put(path, Headers(), items, progress);
14533inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14534 const UploadFormDataItems &items,
14535 UploadProgress progress) {
14536 const auto &boundary = detail::make_multipart_data_boundary();
14537 const auto &content_type =
14538 detail::serialize_multipart_formdata_get_content_type(boundary);
14539 auto content_length = detail::get_multipart_content_length(items, boundary);
14540 return Put(path, headers, content_length,
14541 detail::make_multipart_content_provider(items, boundary),
14542 content_type, progress);
14545inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14546 const UploadFormDataItems &items,
14547 const std::string &boundary,
14548 UploadProgress progress) {
14549 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
14550 return Result{
nullptr, Error::UnsupportedMultipartBoundaryChars};
14553 const auto &content_type =
14554 detail::serialize_multipart_formdata_get_content_type(boundary);
14555 auto content_length = detail::get_multipart_content_length(items, boundary);
14556 return Put(path, headers, content_length,
14557 detail::make_multipart_content_provider(items, boundary),
14558 content_type, progress);
14561inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14562 const char *body,
size_t content_length,
14563 const std::string &content_type,
14564 UploadProgress progress) {
14565 return send_with_content_provider_and_receiver(
14566 "PUT", path, headers, body, content_length,
nullptr,
nullptr,
14567 content_type,
nullptr, progress);
14570inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14571 const std::string &body,
14572 const std::string &content_type,
14573 UploadProgress progress) {
14574 return send_with_content_provider_and_receiver(
14575 "PUT", path, headers, body.data(), body.size(),
nullptr,
nullptr,
14576 content_type,
nullptr, progress);
14579inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14580 size_t content_length,
14581 ContentProvider content_provider,
14582 const std::string &content_type,
14583 UploadProgress progress) {
14584 return send_with_content_provider_and_receiver(
14585 "PUT", path, headers,
nullptr, content_length,
14586 std::move(content_provider),
nullptr, content_type,
nullptr, progress);
14589inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14590 size_t content_length,
14591 ContentProvider content_provider,
14592 const std::string &content_type,
14593 ContentReceiver content_receiver,
14594 UploadProgress progress) {
14595 return send_with_content_provider_and_receiver(
14596 "PUT", path, headers,
nullptr, content_length,
14597 std::move(content_provider),
nullptr, content_type,
14598 std::move(content_receiver), progress);
14601inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14602 ContentProviderWithoutLength content_provider,
14603 const std::string &content_type,
14604 UploadProgress progress) {
14605 return send_with_content_provider_and_receiver(
14606 "PUT", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14607 content_type,
nullptr, progress);
14610inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14611 ContentProviderWithoutLength content_provider,
14612 const std::string &content_type,
14613 ContentReceiver content_receiver,
14614 UploadProgress progress) {
14615 return send_with_content_provider_and_receiver(
14616 "PUT", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14617 content_type, std::move(content_receiver), progress);
14620inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14621 const UploadFormDataItems &items,
14622 const FormDataProviderItems &provider_items,
14623 UploadProgress progress) {
14624 const auto &boundary = detail::make_multipart_data_boundary();
14625 const auto &content_type =
14626 detail::serialize_multipart_formdata_get_content_type(boundary);
14627 return send_with_content_provider_and_receiver(
14628 "PUT", path, headers,
nullptr, 0,
nullptr,
14629 get_multipart_content_provider(boundary, items, provider_items),
14630 content_type,
nullptr, progress);
14633inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
14634 const std::string &body,
14635 const std::string &content_type,
14636 ContentReceiver content_receiver,
14637 DownloadProgress progress) {
14639 req.method =
"PUT";
14641 req.headers = headers;
14643 req.content_receiver =
14644 [content_receiver](
const char *data,
size_t data_length,
14645 size_t ,
size_t ) {
14646 return content_receiver(data, data_length);
14648 req.download_progress = std::move(progress);
14650 if (max_timeout_msec_ > 0) {
14651 req.start_time_ = std::chrono::steady_clock::now();
14654 if (!content_type.empty()) { req.set_header(
"Content-Type", content_type); }
14656 return send_(std::move(req));
14659inline Result ClientImpl::Patch(
const std::string &path) {
14660 return Patch(path, std::string(), std::string());
14663inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14664 UploadProgress progress) {
14665 return Patch(path, headers,
nullptr, 0, std::string(), progress);
14668inline Result ClientImpl::Patch(
const std::string &path,
const char *body,
14669 size_t content_length,
14670 const std::string &content_type,
14671 UploadProgress progress) {
14672 return Patch(path, Headers(), body, content_length, content_type, progress);
14675inline Result ClientImpl::Patch(
const std::string &path,
14676 const std::string &body,
14677 const std::string &content_type,
14678 UploadProgress progress) {
14679 return Patch(path, Headers(), body, content_type, progress);
14682inline Result ClientImpl::Patch(
const std::string &path,
const Params ¶ms) {
14683 return Patch(path, Headers(), params);
14686inline Result ClientImpl::Patch(
const std::string &path,
size_t content_length,
14687 ContentProvider content_provider,
14688 const std::string &content_type,
14689 UploadProgress progress) {
14690 return Patch(path, Headers(), content_length, std::move(content_provider),
14691 content_type, progress);
14694inline Result ClientImpl::Patch(
const std::string &path,
size_t content_length,
14695 ContentProvider content_provider,
14696 const std::string &content_type,
14697 ContentReceiver content_receiver,
14698 UploadProgress progress) {
14699 return Patch(path, Headers(), content_length, std::move(content_provider),
14700 content_type, std::move(content_receiver), progress);
14703inline Result ClientImpl::Patch(
const std::string &path,
14704 ContentProviderWithoutLength content_provider,
14705 const std::string &content_type,
14706 UploadProgress progress) {
14707 return Patch(path, Headers(), std::move(content_provider), content_type,
14711inline Result ClientImpl::Patch(
const std::string &path,
14712 ContentProviderWithoutLength content_provider,
14713 const std::string &content_type,
14714 ContentReceiver content_receiver,
14715 UploadProgress progress) {
14716 return Patch(path, Headers(), std::move(content_provider), content_type,
14717 std::move(content_receiver), progress);
14720inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14721 const Params ¶ms) {
14722 auto query = detail::params_to_query_str(params);
14723 return Patch(path, headers, query,
"application/x-www-form-urlencoded");
14726inline Result ClientImpl::Patch(
const std::string &path,
14727 const UploadFormDataItems &items,
14728 UploadProgress progress) {
14729 return Patch(path, Headers(), items, progress);
14732inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14733 const UploadFormDataItems &items,
14734 UploadProgress progress) {
14735 const auto &boundary = detail::make_multipart_data_boundary();
14736 const auto &content_type =
14737 detail::serialize_multipart_formdata_get_content_type(boundary);
14738 auto content_length = detail::get_multipart_content_length(items, boundary);
14739 return Patch(path, headers, content_length,
14740 detail::make_multipart_content_provider(items, boundary),
14741 content_type, progress);
14744inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14745 const UploadFormDataItems &items,
14746 const std::string &boundary,
14747 UploadProgress progress) {
14748 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
14749 return Result{
nullptr, Error::UnsupportedMultipartBoundaryChars};
14752 const auto &content_type =
14753 detail::serialize_multipart_formdata_get_content_type(boundary);
14754 auto content_length = detail::get_multipart_content_length(items, boundary);
14755 return Patch(path, headers, content_length,
14756 detail::make_multipart_content_provider(items, boundary),
14757 content_type, progress);
14760inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14761 const char *body,
size_t content_length,
14762 const std::string &content_type,
14763 UploadProgress progress) {
14764 return send_with_content_provider_and_receiver(
14765 "PATCH", path, headers, body, content_length,
nullptr,
nullptr,
14766 content_type,
nullptr, progress);
14769inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14770 const std::string &body,
14771 const std::string &content_type,
14772 UploadProgress progress) {
14773 return send_with_content_provider_and_receiver(
14774 "PATCH", path, headers, body.data(), body.size(),
nullptr,
nullptr,
14775 content_type,
nullptr, progress);
14778inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14779 size_t content_length,
14780 ContentProvider content_provider,
14781 const std::string &content_type,
14782 UploadProgress progress) {
14783 return send_with_content_provider_and_receiver(
14784 "PATCH", path, headers,
nullptr, content_length,
14785 std::move(content_provider),
nullptr, content_type,
nullptr, progress);
14788inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14789 size_t content_length,
14790 ContentProvider content_provider,
14791 const std::string &content_type,
14792 ContentReceiver content_receiver,
14793 UploadProgress progress) {
14794 return send_with_content_provider_and_receiver(
14795 "PATCH", path, headers,
nullptr, content_length,
14796 std::move(content_provider),
nullptr, content_type,
14797 std::move(content_receiver), progress);
14800inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14801 ContentProviderWithoutLength content_provider,
14802 const std::string &content_type,
14803 UploadProgress progress) {
14804 return send_with_content_provider_and_receiver(
14805 "PATCH", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14806 content_type,
nullptr, progress);
14809inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14810 ContentProviderWithoutLength content_provider,
14811 const std::string &content_type,
14812 ContentReceiver content_receiver,
14813 UploadProgress progress) {
14814 return send_with_content_provider_and_receiver(
14815 "PATCH", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14816 content_type, std::move(content_receiver), progress);
14819inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14820 const UploadFormDataItems &items,
14821 const FormDataProviderItems &provider_items,
14822 UploadProgress progress) {
14823 const auto &boundary = detail::make_multipart_data_boundary();
14824 const auto &content_type =
14825 detail::serialize_multipart_formdata_get_content_type(boundary);
14826 return send_with_content_provider_and_receiver(
14827 "PATCH", path, headers,
nullptr, 0,
nullptr,
14828 get_multipart_content_provider(boundary, items, provider_items),
14829 content_type,
nullptr, progress);
14832inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
14833 const std::string &body,
14834 const std::string &content_type,
14835 ContentReceiver content_receiver,
14836 DownloadProgress progress) {
14838 req.method =
"PATCH";
14840 req.headers = headers;
14842 req.content_receiver =
14843 [content_receiver](
const char *data,
size_t data_length,
14844 size_t ,
size_t ) {
14845 return content_receiver(data, data_length);
14847 req.download_progress = std::move(progress);
14849 if (max_timeout_msec_ > 0) {
14850 req.start_time_ = std::chrono::steady_clock::now();
14853 if (!content_type.empty()) { req.set_header(
"Content-Type", content_type); }
14855 return send_(std::move(req));
14858inline Result ClientImpl::Delete(
const std::string &path,
14859 DownloadProgress progress) {
14860 return Delete(path, Headers(), std::string(), std::string(), progress);
14863inline Result ClientImpl::Delete(
const std::string &path,
14864 const Headers &headers,
14865 DownloadProgress progress) {
14866 return Delete(path, headers, std::string(), std::string(), progress);
14869inline Result ClientImpl::Delete(
const std::string &path,
const char *body,
14870 size_t content_length,
14871 const std::string &content_type,
14872 DownloadProgress progress) {
14873 return Delete(path, Headers(), body, content_length, content_type, progress);
14876inline Result ClientImpl::Delete(
const std::string &path,
14877 const std::string &body,
14878 const std::string &content_type,
14879 DownloadProgress progress) {
14880 return Delete(path, Headers(), body.data(), body.size(), content_type,
14884inline Result ClientImpl::Delete(
const std::string &path,
14885 const Headers &headers,
14886 const std::string &body,
14887 const std::string &content_type,
14888 DownloadProgress progress) {
14889 return Delete(path, headers, body.data(), body.size(), content_type,
14893inline Result ClientImpl::Delete(
const std::string &path,
const Params ¶ms,
14894 DownloadProgress progress) {
14895 return Delete(path, Headers(), params, progress);
14898inline Result ClientImpl::Delete(
const std::string &path,
14899 const Headers &headers,
const Params ¶ms,
14900 DownloadProgress progress) {
14901 auto query = detail::params_to_query_str(params);
14902 return Delete(path, headers, query,
"application/x-www-form-urlencoded",
14906inline Result ClientImpl::Delete(
const std::string &path,
14907 const Headers &headers,
const char *body,
14908 size_t content_length,
14909 const std::string &content_type,
14910 DownloadProgress progress) {
14912 req.method =
"DELETE";
14913 req.headers = headers;
14915 req.download_progress = std::move(progress);
14916 if (max_timeout_msec_ > 0) {
14917 req.start_time_ = std::chrono::steady_clock::now();
14920 if (!content_type.empty()) { req.set_header(
"Content-Type", content_type); }
14921 req.body.assign(body, content_length);
14923 return send_(std::move(req));
14926inline Result ClientImpl::Options(
const std::string &path) {
14927 return Options(path, Headers());
14930inline Result ClientImpl::Options(
const std::string &path,
14931 const Headers &headers) {
14933 req.method =
"OPTIONS";
14934 req.headers = headers;
14936 if (max_timeout_msec_ > 0) {
14937 req.start_time_ = std::chrono::steady_clock::now();
14940 return send_(std::move(req));
14943inline void ClientImpl::stop() {
14944 std::lock_guard<std::mutex> guard(socket_mutex_);
14951 if (socket_requests_in_flight_ > 0) {
14952 shutdown_socket(socket_);
14956 socket_should_be_closed_when_request_is_done_ =
true;
14963inline std::string ClientImpl::host()
const {
return host_; }
14965inline int ClientImpl::port()
const {
return port_; }
14967inline size_t ClientImpl::is_socket_open()
const {
14968 std::lock_guard<std::mutex> guard(socket_mutex_);
14969 return socket_.is_open();
14972inline socket_t ClientImpl::socket()
const {
return socket_.sock; }
14974inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
14975 connection_timeout_sec_ = sec;
14976 connection_timeout_usec_ = usec;
14979inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
14980 read_timeout_sec_ = sec;
14981 read_timeout_usec_ = usec;
14984inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
14985 write_timeout_sec_ = sec;
14986 write_timeout_usec_ = usec;
14989inline void ClientImpl::set_max_timeout(time_t msec) {
14990 max_timeout_msec_ = msec;
14993inline void ClientImpl::set_basic_auth(
const std::string &username,
14994 const std::string &password) {
14995 basic_auth_username_ = username;
14996 basic_auth_password_ = password;
14999inline void ClientImpl::set_bearer_token_auth(
const std::string &token) {
15000 bearer_token_auth_token_ = token;
15003inline void ClientImpl::set_keep_alive(
bool on) { keep_alive_ = on; }
15005inline void ClientImpl::set_follow_location(
bool on) { follow_location_ = on; }
15007inline void ClientImpl::set_path_encode(
bool on) { path_encode_ = on; }
15010ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
15011 addr_map_ = std::move(addr_map);
15014inline void ClientImpl::set_default_headers(Headers headers) {
15015 default_headers_ = std::move(headers);
15018inline void ClientImpl::set_header_writer(
15019 std::function<ssize_t(Stream &, Headers &)>
const &writer) {
15020 header_writer_ = writer;
15023inline void ClientImpl::set_address_family(
int family) {
15024 address_family_ = family;
15027inline void ClientImpl::set_tcp_nodelay(
bool on) { tcp_nodelay_ = on; }
15029inline void ClientImpl::set_ipv6_v6only(
bool on) { ipv6_v6only_ = on; }
15031inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
15032 socket_options_ = std::move(socket_options);
15035inline void ClientImpl::set_compress(
bool on) { compress_ = on; }
15037inline void ClientImpl::set_decompress(
bool on) { decompress_ = on; }
15039inline void ClientImpl::set_payload_max_length(
size_t length) {
15040 payload_max_length_ = length;
15041 has_payload_max_length_ =
true;
15044inline void ClientImpl::set_interface(
const std::string &intf) {
15048inline void ClientImpl::set_proxy(
const std::string &host,
int port) {
15049 proxy_host_ = host;
15050 proxy_port_ = port;
15051 std::lock_guard<std::mutex> guard(socket_mutex_);
15055inline void ClientImpl::set_proxy_basic_auth(
const std::string &username,
15056 const std::string &password) {
15057 proxy_basic_auth_username_ = username;
15058 proxy_basic_auth_password_ = password;
15061inline void ClientImpl::set_proxy_bearer_token_auth(
const std::string &token) {
15062 proxy_bearer_token_auth_token_ = token;
15065inline void ClientImpl::set_no_proxy(
const std::vector<std::string> &patterns) {
15066 std::vector<detail::NoProxyEntry> parsed;
15067 parsed.reserve(patterns.size());
15068 for (
const auto &p : patterns) {
15069 auto trimmed = detail::trim_copy(p);
15070 if (trimmed.empty()) {
continue; }
15071 detail::NoProxyEntry entry;
15072 if (detail::parse_no_proxy_entry(trimmed, entry)) {
15073 parsed.push_back(std::move(entry));
15076 no_proxy_entries_ = std::move(parsed);
15077 std::lock_guard<std::mutex> guard(socket_mutex_);
15081#ifdef CPPHTTPLIB_SSL_ENABLED
15082inline void ClientImpl::set_digest_auth(
const std::string &username,
15083 const std::string &password) {
15084 digest_auth_username_ = username;
15085 digest_auth_password_ = password;
15088inline void ClientImpl::set_ca_cert_path(
const std::string &ca_cert_file_path,
15089 const std::string &ca_cert_dir_path) {
15090 ca_cert_file_path_ = ca_cert_file_path;
15091 ca_cert_dir_path_ = ca_cert_dir_path;
15094inline void ClientImpl::set_proxy_digest_auth(
const std::string &username,
15095 const std::string &password) {
15096 proxy_digest_auth_username_ = username;
15097 proxy_digest_auth_password_ = password;
15100inline void ClientImpl::enable_server_certificate_verification(
bool enabled) {
15101 server_certificate_verification_ = enabled;
15104inline void ClientImpl::enable_server_hostname_verification(
bool enabled) {
15105 server_hostname_verification_ = enabled;
15108inline void ClientImpl::enable_system_ca(
bool enabled) {
15109 system_ca_mode_ = enabled ? SystemCAMode::Enabled : SystemCAMode::Disabled;
15113inline void ClientImpl::set_logger(Logger logger) {
15114 logger_ = std::move(logger);
15117inline void ClientImpl::set_error_logger(ErrorLogger error_logger) {
15118 error_logger_ = std::move(error_logger);
15125inline ClientConnection::~ClientConnection() {
15126#ifdef CPPHTTPLIB_SSL_ENABLED
15128 tls::shutdown(session,
true);
15129 tls::free_session(session);
15135 detail::close_socket(sock);
15141inline Client::Client(
const std::string &scheme_host_port)
15142 : Client(scheme_host_port, std::string(), std::string()) {}
15144inline Client::Client(
const std::string &scheme_host_port,
15145 const std::string &client_cert_path,
15146 const std::string &client_key_path) {
15147 detail::UrlComponents uc;
15148 if (detail::parse_url(scheme_host_port, uc) && !uc.host.empty()) {
15149 auto &scheme = uc.scheme;
15151#ifdef CPPHTTPLIB_SSL_ENABLED
15152 if (!scheme.empty() && (scheme !=
"http" && scheme !=
"https")) {
15154 if (!scheme.empty() && scheme !=
"http") {
15156#ifndef CPPHTTPLIB_NO_EXCEPTIONS
15157 std::string msg =
"'" + scheme +
"' scheme is not supported.";
15158 throw std::invalid_argument(msg);
15163 auto is_ssl = scheme ==
"https";
15165 auto host = std::move(uc.host);
15167 auto port = is_ssl ? 443 : 80;
15168 if (!uc.port.empty() && !detail::parse_port(uc.port, port)) {
return; }
15171#ifdef CPPHTTPLIB_SSL_ENABLED
15172 cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
15177 cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
15183 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
15184 client_cert_path, client_key_path);
15188inline Client::Client(
const std::string &host,
int port)
15189 : Client(host, port, std::string(), std::string()) {}
15191inline Client::Client(
const std::string &host,
int port,
15192 const std::string &client_cert_path,
15193 const std::string &client_key_path)
15194 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
15195 client_key_path)) {}
15197inline Client::~Client() =
default;
15199inline bool Client::is_valid()
const {
15200 return cli_ !=
nullptr && cli_->is_valid();
15203inline Result Client::Get(
const std::string &path, DownloadProgress progress) {
15204 return cli_->Get(path, std::move(progress));
15206inline Result Client::Get(
const std::string &path,
const Headers &headers,
15207 DownloadProgress progress) {
15208 return cli_->Get(path, headers, std::move(progress));
15210inline Result Client::Get(
const std::string &path,
15211 ContentReceiver content_receiver,
15212 DownloadProgress progress) {
15213 return cli_->Get(path, std::move(content_receiver), std::move(progress));
15215inline Result Client::Get(
const std::string &path,
const Headers &headers,
15216 ContentReceiver content_receiver,
15217 DownloadProgress progress) {
15218 return cli_->Get(path, headers, std::move(content_receiver),
15219 std::move(progress));
15221inline Result Client::Get(
const std::string &path,
15222 ResponseHandler response_handler,
15223 ContentReceiver content_receiver,
15224 DownloadProgress progress) {
15225 return cli_->Get(path, std::move(response_handler),
15226 std::move(content_receiver), std::move(progress));
15228inline Result Client::Get(
const std::string &path,
const Headers &headers,
15229 ResponseHandler response_handler,
15230 ContentReceiver content_receiver,
15231 DownloadProgress progress) {
15232 return cli_->Get(path, headers, std::move(response_handler),
15233 std::move(content_receiver), std::move(progress));
15235inline Result Client::Get(
const std::string &path,
const Params ¶ms,
15236 const Headers &headers, DownloadProgress progress) {
15237 return cli_->Get(path, params, headers, std::move(progress));
15239inline Result Client::Get(
const std::string &path,
const Params ¶ms,
15240 const Headers &headers,
15241 ContentReceiver content_receiver,
15242 DownloadProgress progress) {
15243 return cli_->Get(path, params, headers, std::move(content_receiver),
15244 std::move(progress));
15246inline Result Client::Get(
const std::string &path,
const Params ¶ms,
15247 const Headers &headers,
15248 ResponseHandler response_handler,
15249 ContentReceiver content_receiver,
15250 DownloadProgress progress) {
15251 return cli_->Get(path, params, headers, std::move(response_handler),
15252 std::move(content_receiver), std::move(progress));
15255inline Result Client::Head(
const std::string &path) {
return cli_->Head(path); }
15256inline Result Client::Head(
const std::string &path,
const Headers &headers) {
15257 return cli_->Head(path, headers);
15260inline Result Client::Post(
const std::string &path) {
return cli_->Post(path); }
15261inline Result Client::Post(
const std::string &path,
const Headers &headers) {
15262 return cli_->Post(path, headers);
15264inline Result Client::Post(
const std::string &path,
const char *body,
15265 size_t content_length,
15266 const std::string &content_type,
15267 UploadProgress progress) {
15268 return cli_->Post(path, body, content_length, content_type, progress);
15270inline Result Client::Post(
const std::string &path,
const Headers &headers,
15271 const char *body,
size_t content_length,
15272 const std::string &content_type,
15273 UploadProgress progress) {
15274 return cli_->Post(path, headers, body, content_length, content_type,
15277inline Result Client::Post(
const std::string &path,
const std::string &body,
15278 const std::string &content_type,
15279 UploadProgress progress) {
15280 return cli_->Post(path, body, content_type, progress);
15282inline Result Client::Post(
const std::string &path,
const Headers &headers,
15283 const std::string &body,
15284 const std::string &content_type,
15285 UploadProgress progress) {
15286 return cli_->Post(path, headers, body, content_type, progress);
15288inline Result Client::Post(
const std::string &path,
size_t content_length,
15289 ContentProvider content_provider,
15290 const std::string &content_type,
15291 UploadProgress progress) {
15292 return cli_->Post(path, content_length, std::move(content_provider),
15293 content_type, progress);
15295inline Result Client::Post(
const std::string &path,
size_t content_length,
15296 ContentProvider content_provider,
15297 const std::string &content_type,
15298 ContentReceiver content_receiver,
15299 UploadProgress progress) {
15300 return cli_->Post(path, content_length, std::move(content_provider),
15301 content_type, std::move(content_receiver), progress);
15303inline Result Client::Post(
const std::string &path,
15304 ContentProviderWithoutLength content_provider,
15305 const std::string &content_type,
15306 UploadProgress progress) {
15307 return cli_->Post(path, std::move(content_provider), content_type, progress);
15309inline Result Client::Post(
const std::string &path,
15310 ContentProviderWithoutLength content_provider,
15311 const std::string &content_type,
15312 ContentReceiver content_receiver,
15313 UploadProgress progress) {
15314 return cli_->Post(path, std::move(content_provider), content_type,
15315 std::move(content_receiver), progress);
15317inline Result Client::Post(
const std::string &path,
const Headers &headers,
15318 size_t content_length,
15319 ContentProvider content_provider,
15320 const std::string &content_type,
15321 UploadProgress progress) {
15322 return cli_->Post(path, headers, content_length, std::move(content_provider),
15323 content_type, progress);
15325inline Result Client::Post(
const std::string &path,
const Headers &headers,
15326 size_t content_length,
15327 ContentProvider content_provider,
15328 const std::string &content_type,
15329 ContentReceiver content_receiver,
15330 DownloadProgress progress) {
15331 return cli_->Post(path, headers, content_length, std::move(content_provider),
15332 content_type, std::move(content_receiver), progress);
15334inline Result Client::Post(
const std::string &path,
const Headers &headers,
15335 ContentProviderWithoutLength content_provider,
15336 const std::string &content_type,
15337 UploadProgress progress) {
15338 return cli_->Post(path, headers, std::move(content_provider), content_type,
15341inline Result Client::Post(
const std::string &path,
const Headers &headers,
15342 ContentProviderWithoutLength content_provider,
15343 const std::string &content_type,
15344 ContentReceiver content_receiver,
15345 DownloadProgress progress) {
15346 return cli_->Post(path, headers, std::move(content_provider), content_type,
15347 std::move(content_receiver), progress);
15349inline Result Client::Post(
const std::string &path,
const Params ¶ms) {
15350 return cli_->Post(path, params);
15352inline Result Client::Post(
const std::string &path,
const Headers &headers,
15353 const Params ¶ms) {
15354 return cli_->Post(path, headers, params);
15356inline Result Client::Post(
const std::string &path,
15357 const UploadFormDataItems &items,
15358 UploadProgress progress) {
15359 return cli_->Post(path, items, progress);
15361inline Result Client::Post(
const std::string &path,
const Headers &headers,
15362 const UploadFormDataItems &items,
15363 UploadProgress progress) {
15364 return cli_->Post(path, headers, items, progress);
15366inline Result Client::Post(
const std::string &path,
const Headers &headers,
15367 const UploadFormDataItems &items,
15368 const std::string &boundary,
15369 UploadProgress progress) {
15370 return cli_->Post(path, headers, items, boundary, progress);
15372inline Result Client::Post(
const std::string &path,
const Headers &headers,
15373 const UploadFormDataItems &items,
15374 const FormDataProviderItems &provider_items,
15375 UploadProgress progress) {
15376 return cli_->Post(path, headers, items, provider_items, progress);
15378inline Result Client::Post(
const std::string &path,
const Headers &headers,
15379 const std::string &body,
15380 const std::string &content_type,
15381 ContentReceiver content_receiver,
15382 DownloadProgress progress) {
15383 return cli_->Post(path, headers, body, content_type,
15384 std::move(content_receiver), progress);
15387inline Result Client::Put(
const std::string &path) {
return cli_->Put(path); }
15388inline Result Client::Put(
const std::string &path,
const Headers &headers) {
15389 return cli_->Put(path, headers);
15391inline Result Client::Put(
const std::string &path,
const char *body,
15392 size_t content_length,
15393 const std::string &content_type,
15394 UploadProgress progress) {
15395 return cli_->Put(path, body, content_length, content_type, progress);
15397inline Result Client::Put(
const std::string &path,
const Headers &headers,
15398 const char *body,
size_t content_length,
15399 const std::string &content_type,
15400 UploadProgress progress) {
15401 return cli_->Put(path, headers, body, content_length, content_type, progress);
15403inline Result Client::Put(
const std::string &path,
const std::string &body,
15404 const std::string &content_type,
15405 UploadProgress progress) {
15406 return cli_->Put(path, body, content_type, progress);
15408inline Result Client::Put(
const std::string &path,
const Headers &headers,
15409 const std::string &body,
15410 const std::string &content_type,
15411 UploadProgress progress) {
15412 return cli_->Put(path, headers, body, content_type, progress);
15414inline Result Client::Put(
const std::string &path,
size_t content_length,
15415 ContentProvider content_provider,
15416 const std::string &content_type,
15417 UploadProgress progress) {
15418 return cli_->Put(path, content_length, std::move(content_provider),
15419 content_type, progress);
15421inline Result Client::Put(
const std::string &path,
size_t content_length,
15422 ContentProvider content_provider,
15423 const std::string &content_type,
15424 ContentReceiver content_receiver,
15425 UploadProgress progress) {
15426 return cli_->Put(path, content_length, std::move(content_provider),
15427 content_type, std::move(content_receiver), progress);
15429inline Result Client::Put(
const std::string &path,
15430 ContentProviderWithoutLength content_provider,
15431 const std::string &content_type,
15432 UploadProgress progress) {
15433 return cli_->Put(path, std::move(content_provider), content_type, progress);
15435inline Result Client::Put(
const std::string &path,
15436 ContentProviderWithoutLength content_provider,
15437 const std::string &content_type,
15438 ContentReceiver content_receiver,
15439 UploadProgress progress) {
15440 return cli_->Put(path, std::move(content_provider), content_type,
15441 std::move(content_receiver), progress);
15443inline Result Client::Put(
const std::string &path,
const Headers &headers,
15444 size_t content_length,
15445 ContentProvider content_provider,
15446 const std::string &content_type,
15447 UploadProgress progress) {
15448 return cli_->Put(path, headers, content_length, std::move(content_provider),
15449 content_type, progress);
15451inline Result Client::Put(
const std::string &path,
const Headers &headers,
15452 size_t content_length,
15453 ContentProvider content_provider,
15454 const std::string &content_type,
15455 ContentReceiver content_receiver,
15456 UploadProgress progress) {
15457 return cli_->Put(path, headers, content_length, std::move(content_provider),
15458 content_type, std::move(content_receiver), progress);
15460inline Result Client::Put(
const std::string &path,
const Headers &headers,
15461 ContentProviderWithoutLength content_provider,
15462 const std::string &content_type,
15463 UploadProgress progress) {
15464 return cli_->Put(path, headers, std::move(content_provider), content_type,
15467inline Result Client::Put(
const std::string &path,
const Headers &headers,
15468 ContentProviderWithoutLength content_provider,
15469 const std::string &content_type,
15470 ContentReceiver content_receiver,
15471 UploadProgress progress) {
15472 return cli_->Put(path, headers, std::move(content_provider), content_type,
15473 std::move(content_receiver), progress);
15475inline Result Client::Put(
const std::string &path,
const Params ¶ms) {
15476 return cli_->Put(path, params);
15478inline Result Client::Put(
const std::string &path,
const Headers &headers,
15479 const Params ¶ms) {
15480 return cli_->Put(path, headers, params);
15482inline Result Client::Put(
const std::string &path,
15483 const UploadFormDataItems &items,
15484 UploadProgress progress) {
15485 return cli_->Put(path, items, progress);
15487inline Result Client::Put(
const std::string &path,
const Headers &headers,
15488 const UploadFormDataItems &items,
15489 UploadProgress progress) {
15490 return cli_->Put(path, headers, items, progress);
15492inline Result Client::Put(
const std::string &path,
const Headers &headers,
15493 const UploadFormDataItems &items,
15494 const std::string &boundary,
15495 UploadProgress progress) {
15496 return cli_->Put(path, headers, items, boundary, progress);
15498inline Result Client::Put(
const std::string &path,
const Headers &headers,
15499 const UploadFormDataItems &items,
15500 const FormDataProviderItems &provider_items,
15501 UploadProgress progress) {
15502 return cli_->Put(path, headers, items, provider_items, progress);
15504inline Result Client::Put(
const std::string &path,
const Headers &headers,
15505 const std::string &body,
15506 const std::string &content_type,
15507 ContentReceiver content_receiver,
15508 DownloadProgress progress) {
15509 return cli_->Put(path, headers, body, content_type, content_receiver,
15513inline Result Client::Patch(
const std::string &path) {
15514 return cli_->Patch(path);
15516inline Result Client::Patch(
const std::string &path,
const Headers &headers) {
15517 return cli_->Patch(path, headers);
15519inline Result Client::Patch(
const std::string &path,
const char *body,
15520 size_t content_length,
15521 const std::string &content_type,
15522 UploadProgress progress) {
15523 return cli_->Patch(path, body, content_length, content_type, progress);
15525inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15526 const char *body,
size_t content_length,
15527 const std::string &content_type,
15528 UploadProgress progress) {
15529 return cli_->Patch(path, headers, body, content_length, content_type,
15532inline Result Client::Patch(
const std::string &path,
const std::string &body,
15533 const std::string &content_type,
15534 UploadProgress progress) {
15535 return cli_->Patch(path, body, content_type, progress);
15537inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15538 const std::string &body,
15539 const std::string &content_type,
15540 UploadProgress progress) {
15541 return cli_->Patch(path, headers, body, content_type, progress);
15543inline Result Client::Patch(
const std::string &path,
size_t content_length,
15544 ContentProvider content_provider,
15545 const std::string &content_type,
15546 UploadProgress progress) {
15547 return cli_->Patch(path, content_length, std::move(content_provider),
15548 content_type, progress);
15550inline Result Client::Patch(
const std::string &path,
size_t content_length,
15551 ContentProvider content_provider,
15552 const std::string &content_type,
15553 ContentReceiver content_receiver,
15554 UploadProgress progress) {
15555 return cli_->Patch(path, content_length, std::move(content_provider),
15556 content_type, std::move(content_receiver), progress);
15558inline Result Client::Patch(
const std::string &path,
15559 ContentProviderWithoutLength content_provider,
15560 const std::string &content_type,
15561 UploadProgress progress) {
15562 return cli_->Patch(path, std::move(content_provider), content_type, progress);
15564inline Result Client::Patch(
const std::string &path,
15565 ContentProviderWithoutLength content_provider,
15566 const std::string &content_type,
15567 ContentReceiver content_receiver,
15568 UploadProgress progress) {
15569 return cli_->Patch(path, std::move(content_provider), content_type,
15570 std::move(content_receiver), progress);
15572inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15573 size_t content_length,
15574 ContentProvider content_provider,
15575 const std::string &content_type,
15576 UploadProgress progress) {
15577 return cli_->Patch(path, headers, content_length, std::move(content_provider),
15578 content_type, progress);
15580inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15581 size_t content_length,
15582 ContentProvider content_provider,
15583 const std::string &content_type,
15584 ContentReceiver content_receiver,
15585 UploadProgress progress) {
15586 return cli_->Patch(path, headers, content_length, std::move(content_provider),
15587 content_type, std::move(content_receiver), progress);
15589inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15590 ContentProviderWithoutLength content_provider,
15591 const std::string &content_type,
15592 UploadProgress progress) {
15593 return cli_->Patch(path, headers, std::move(content_provider), content_type,
15596inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15597 ContentProviderWithoutLength content_provider,
15598 const std::string &content_type,
15599 ContentReceiver content_receiver,
15600 UploadProgress progress) {
15601 return cli_->Patch(path, headers, std::move(content_provider), content_type,
15602 std::move(content_receiver), progress);
15604inline Result Client::Patch(
const std::string &path,
const Params ¶ms) {
15605 return cli_->Patch(path, params);
15607inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15608 const Params ¶ms) {
15609 return cli_->Patch(path, headers, params);
15611inline Result Client::Patch(
const std::string &path,
15612 const UploadFormDataItems &items,
15613 UploadProgress progress) {
15614 return cli_->Patch(path, items, progress);
15616inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15617 const UploadFormDataItems &items,
15618 UploadProgress progress) {
15619 return cli_->Patch(path, headers, items, progress);
15621inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15622 const UploadFormDataItems &items,
15623 const std::string &boundary,
15624 UploadProgress progress) {
15625 return cli_->Patch(path, headers, items, boundary, progress);
15627inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15628 const UploadFormDataItems &items,
15629 const FormDataProviderItems &provider_items,
15630 UploadProgress progress) {
15631 return cli_->Patch(path, headers, items, provider_items, progress);
15633inline Result Client::Patch(
const std::string &path,
const Headers &headers,
15634 const std::string &body,
15635 const std::string &content_type,
15636 ContentReceiver content_receiver,
15637 DownloadProgress progress) {
15638 return cli_->Patch(path, headers, body, content_type, content_receiver,
15642inline Result Client::Delete(
const std::string &path,
15643 DownloadProgress progress) {
15644 return cli_->Delete(path, progress);
15646inline Result Client::Delete(
const std::string &path,
const Headers &headers,
15647 DownloadProgress progress) {
15648 return cli_->Delete(path, headers, progress);
15650inline Result Client::Delete(
const std::string &path,
const char *body,
15651 size_t content_length,
15652 const std::string &content_type,
15653 DownloadProgress progress) {
15654 return cli_->Delete(path, body, content_length, content_type, progress);
15656inline Result Client::Delete(
const std::string &path,
const Headers &headers,
15657 const char *body,
size_t content_length,
15658 const std::string &content_type,
15659 DownloadProgress progress) {
15660 return cli_->Delete(path, headers, body, content_length, content_type,
15663inline Result Client::Delete(
const std::string &path,
const std::string &body,
15664 const std::string &content_type,
15665 DownloadProgress progress) {
15666 return cli_->Delete(path, body, content_type, progress);
15668inline Result Client::Delete(
const std::string &path,
const Headers &headers,
15669 const std::string &body,
15670 const std::string &content_type,
15671 DownloadProgress progress) {
15672 return cli_->Delete(path, headers, body, content_type, progress);
15674inline Result Client::Delete(
const std::string &path,
const Params ¶ms,
15675 DownloadProgress progress) {
15676 return cli_->Delete(path, params, progress);
15678inline Result Client::Delete(
const std::string &path,
const Headers &headers,
15679 const Params ¶ms, DownloadProgress progress) {
15680 return cli_->Delete(path, headers, params, progress);
15683inline Result Client::Options(
const std::string &path) {
15684 return cli_->Options(path);
15686inline Result Client::Options(
const std::string &path,
const Headers &headers) {
15687 return cli_->Options(path, headers);
15690inline ClientImpl::StreamHandle
15691Client::open_stream(
const std::string &method,
const std::string &path,
15692 const Params ¶ms,
const Headers &headers,
15693 const std::string &body,
const std::string &content_type) {
15694 return cli_->open_stream(method, path, params, headers, body, content_type);
15697inline bool Client::send(Request &req, Response &res, Error &error) {
15698 return cli_->send(req, res, error);
15701inline Result Client::send(
const Request &req) {
return cli_->send(req); }
15703inline void Client::stop() { cli_->stop(); }
15705inline std::string Client::host()
const {
return cli_->host(); }
15707inline int Client::port()
const {
return cli_->port(); }
15709inline size_t Client::is_socket_open()
const {
return cli_->is_socket_open(); }
15711inline socket_t Client::socket()
const {
return cli_->socket(); }
15714Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
15715 cli_->set_hostname_addr_map(std::move(addr_map));
15718inline void Client::set_default_headers(Headers headers) {
15719 cli_->set_default_headers(std::move(headers));
15722inline void Client::set_header_writer(
15723 std::function<ssize_t(Stream &, Headers &)>
const &writer) {
15724 cli_->set_header_writer(writer);
15727inline void Client::set_address_family(
int family) {
15728 cli_->set_address_family(family);
15731inline void Client::set_tcp_nodelay(
bool on) { cli_->set_tcp_nodelay(on); }
15733inline void Client::set_socket_options(SocketOptions socket_options) {
15734 cli_->set_socket_options(std::move(socket_options));
15737inline void Client::set_connection_timeout(time_t sec, time_t usec) {
15738 cli_->set_connection_timeout(sec, usec);
15741inline void Client::set_read_timeout(time_t sec, time_t usec) {
15742 cli_->set_read_timeout(sec, usec);
15745inline void Client::set_write_timeout(time_t sec, time_t usec) {
15746 cli_->set_write_timeout(sec, usec);
15749inline void Client::set_basic_auth(
const std::string &username,
15750 const std::string &password) {
15751 cli_->set_basic_auth(username, password);
15753inline void Client::set_bearer_token_auth(
const std::string &token) {
15754 cli_->set_bearer_token_auth(token);
15757inline void Client::set_keep_alive(
bool on) { cli_->set_keep_alive(on); }
15758inline void Client::set_follow_location(
bool on) {
15759 cli_->set_follow_location(on);
15762inline void Client::set_path_encode(
bool on) { cli_->set_path_encode(on); }
15764inline void Client::set_compress(
bool on) { cli_->set_compress(on); }
15766inline void Client::set_decompress(
bool on) { cli_->set_decompress(on); }
15768inline void Client::set_payload_max_length(
size_t length) {
15769 cli_->set_payload_max_length(length);
15772inline void Client::set_interface(
const std::string &intf) {
15773 cli_->set_interface(intf);
15776inline void Client::set_proxy(
const std::string &host,
int port) {
15777 cli_->set_proxy(host, port);
15779inline void Client::set_proxy_basic_auth(
const std::string &username,
15780 const std::string &password) {
15781 cli_->set_proxy_basic_auth(username, password);
15783inline void Client::set_proxy_bearer_token_auth(
const std::string &token) {
15784 cli_->set_proxy_bearer_token_auth(token);
15786inline void Client::set_no_proxy(
const std::vector<std::string> &patterns) {
15787 cli_->set_no_proxy(patterns);
15790inline void Client::set_logger(Logger logger) {
15791 cli_->set_logger(std::move(logger));
15794inline void Client::set_error_logger(ErrorLogger error_logger) {
15795 cli_->set_error_logger(std::move(error_logger));
15802#ifdef CPPHTTPLIB_SSL_ENABLED
15805inline SSLServer::SSLServer(
const char *cert_path,
const char *private_key_path,
15806 const char *client_ca_cert_file_path,
15807 const char *client_ca_cert_dir_path,
15808 const char *private_key_password) {
15809 using namespace tls;
15811 ctx_ = create_server_context();
15812 if (!ctx_) {
return; }
15815 if (!set_server_cert_file(ctx_, cert_path, private_key_path,
15816 private_key_password)) {
15817 last_ssl_error_ =
static_cast<int>(get_error());
15818 free_context(ctx_);
15824 if (client_ca_cert_file_path || client_ca_cert_dir_path) {
15825 if (!set_client_ca_file(ctx_, client_ca_cert_file_path,
15826 client_ca_cert_dir_path)) {
15827 last_ssl_error_ =
static_cast<int>(get_error());
15828 free_context(ctx_);
15833 set_verify_client(ctx_,
true);
15837inline SSLServer::SSLServer(
const PemMemory &pem) {
15838 using namespace tls;
15839 ctx_ = create_server_context();
15841 if (!set_server_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
15842 pem.private_key_password)) {
15843 last_ssl_error_ =
static_cast<int>(get_error());
15844 free_context(ctx_);
15846 }
else if (pem.client_ca_pem && pem.client_ca_pem_len > 0) {
15847 if (!load_ca_pem(ctx_, pem.client_ca_pem, pem.client_ca_pem_len)) {
15848 last_ssl_error_ =
static_cast<int>(get_error());
15849 free_context(ctx_);
15852 set_verify_client(ctx_,
true);
15858inline SSLServer::SSLServer(
const tls::ContextSetupCallback &setup_callback) {
15859 using namespace tls;
15860 ctx_ = create_server_context();
15862 if (!setup_callback(ctx_)) {
15863 free_context(ctx_);
15869inline SSLServer::~SSLServer() {
15870 if (ctx_) { tls::free_context(ctx_); }
15873inline bool SSLServer::is_valid()
const {
return ctx_ !=
nullptr; }
15875inline bool SSLServer::process_and_close_socket(
socket_t sock) {
15876 using namespace tls;
15879 session_t session =
nullptr;
15881 std::lock_guard<std::mutex> guard(ctx_mutex_);
15882 session = create_session(
static_cast<ctx_t
>(ctx_), sock);
15886 last_ssl_error_ =
static_cast<int>(get_error());
15887 detail::shutdown_socket(sock);
15888 detail::close_socket(sock);
15893 bool handshake_done =
false;
15895 bool websocket_upgraded =
false;
15896 auto cleanup = detail::scope_exit([&] {
15897 if (handshake_done) { shutdown(session, !websocket_upgraded && ret); }
15898 free_session(session);
15899 detail::shutdown_socket(sock);
15900 detail::close_socket(sock);
15905 if (!accept_nonblocking(session, sock, read_timeout_sec_, read_timeout_usec_,
15907#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
15909 if (tls_err.code == ErrorCode::WantRead) {
15910 last_ssl_error_ = SSL_ERROR_WANT_READ;
15911 }
else if (tls_err.code == ErrorCode::WantWrite) {
15912 last_ssl_error_ = SSL_ERROR_WANT_WRITE;
15914 last_ssl_error_ = SSL_ERROR_SSL;
15917 last_ssl_error_ =
static_cast<int>(get_error());
15922 handshake_done =
true;
15924 std::string remote_addr;
15925 int remote_port = 0;
15926 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
15928 std::string local_addr;
15929 int local_port = 0;
15930 detail::get_local_ip_and_port(sock, local_addr, local_port);
15932 ret = detail::process_server_socket_ssl(
15933 svr_sock_, session, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
15934 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
15935 write_timeout_usec_,
15936 [&](Stream &strm,
bool close_connection,
bool &connection_closed) {
15937 return process_request(
15938 strm, remote_addr, remote_port, local_addr, local_port,
15939 close_connection, connection_closed,
15940 [&](Request &req) { req.ssl = session; }, &websocket_upgraded);
15946inline bool SSLServer::update_certs_pem(
const char *cert_pem,
15947 const char *key_pem,
15948 const char *client_ca_pem,
15949 const char *password) {
15950 if (!ctx_) {
return false; }
15951 std::lock_guard<std::mutex> guard(ctx_mutex_);
15952 if (!tls::update_server_cert(ctx_, cert_pem, key_pem, password)) {
15955 if (client_ca_pem) {
15956 return tls::update_server_client_ca(ctx_, client_ca_pem);
15962inline SSLClient::~SSLClient() {
15963 if (ctx_) { tls::free_context(ctx_); }
15967 shutdown_ssl_impl(socket_,
true);
15970inline bool SSLClient::is_valid()
const {
return ctx_ !=
nullptr; }
15972inline void SSLClient::shutdown_ssl(Socket &socket,
bool shutdown_gracefully) {
15973 shutdown_ssl_impl(socket, shutdown_gracefully);
15976inline void SSLClient::shutdown_ssl_impl(Socket &socket,
15977 bool shutdown_gracefully) {
15979 assert(socket.ssl ==
nullptr);
15983 tls::shutdown(socket.ssl, shutdown_gracefully);
15985 std::lock_guard<std::mutex> guard(ctx_mutex_);
15986 tls::free_session(socket.ssl);
15988 socket.ssl =
nullptr;
15990 assert(socket.ssl ==
nullptr);
15993inline bool SSLClient::process_socket(
15994 const Socket &socket,
15995 std::chrono::time_point<std::chrono::steady_clock> start_time,
15996 std::function<
bool(Stream &strm)> callback) {
15997 assert(socket.ssl);
15998 return detail::process_client_socket_ssl(
15999 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
16000 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
16001 std::move(callback));
16004inline bool SSLClient::is_ssl()
const {
return true; }
16006inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
16008 error = Error::SSLConnection;
16011 return ClientImpl::create_and_connect_socket(socket, error);
16014inline bool SSLClient::setup_proxy_connection(
16016 std::chrono::time_point<std::chrono::steady_clock> start_time,
16017 Response &res,
bool &success, Error &error) {
16018 if (!is_proxy_enabled_for_host(host_)) {
return true; }
16020 if (!connect_with_proxy(socket, start_time, res, success, error)) {
16024 if (!initialize_ssl(socket, error)) {
16034inline bool SSLClient::connect_with_proxy(
16036 std::chrono::time_point<std::chrono::steady_clock> start_time,
16037 Response &res,
bool &success, Error &error) {
16039 Response proxy_res;
16040 if (!detail::process_client_socket(
16041 socket.sock, read_timeout_sec_, read_timeout_usec_,
16042 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
16043 start_time, [&](Stream &strm) {
16045 req2.method =
"CONNECT";
16047 detail::make_host_and_port_string_always_port(host_, port_);
16048 if (max_timeout_msec_ > 0) {
16049 req2.start_time_ = std::chrono::steady_clock::now();
16051 return process_request(strm, req2, proxy_res,
false, error);
16055 shutdown_ssl(socket,
true);
16056 shutdown_socket(socket);
16057 close_socket(socket);
16062 if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
16063 if (!proxy_digest_auth_username_.empty() &&
16064 !proxy_digest_auth_password_.empty()) {
16065 std::map<std::string, std::string> auth;
16066 if (detail::parse_www_authenticate(proxy_res, auth,
true)) {
16069 shutdown_ssl(socket,
true);
16070 shutdown_socket(socket);
16071 close_socket(socket);
16074 if (!ensure_socket_connection(socket, error)) {
16076 output_error_log(error,
nullptr);
16080 proxy_res = Response();
16081 if (!detail::process_client_socket(
16082 socket.sock, read_timeout_sec_, read_timeout_usec_,
16083 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
16084 start_time, [&](Stream &strm) {
16086 req3.method =
"CONNECT";
16087 req3.path = detail::make_host_and_port_string_always_port(
16089 req3.headers.insert(detail::make_digest_authentication_header(
16090 req3, auth, 1, detail::random_string(10),
16091 proxy_digest_auth_username_, proxy_digest_auth_password_,
16093 if (max_timeout_msec_ > 0) {
16094 req3.start_time_ = std::chrono::steady_clock::now();
16096 return process_request(strm, req3, proxy_res,
false, error);
16100 shutdown_ssl(socket,
true);
16101 shutdown_socket(socket);
16102 close_socket(socket);
16113 if (proxy_res.status != StatusCode::OK_200) {
16114 error = Error::ProxyConnection;
16115 output_error_log(error,
nullptr);
16116 res = std::move(proxy_res);
16119 shutdown_ssl(socket,
true);
16120 shutdown_socket(socket);
16121 close_socket(socket);
16128inline bool SSLClient::ensure_socket_connection(Socket &socket, Error &error) {
16129 if (!ClientImpl::ensure_socket_connection(socket, error)) {
return false; }
16131 if (is_proxy_enabled_for_host(host_)) {
return true; }
16133 if (!initialize_ssl(socket, error)) {
16134 shutdown_socket(socket);
16135 close_socket(socket);
16143inline SSLClient::SSLClient(
const std::string &host)
16144 : SSLClient(host, 443, std::string(), std::string()) {}
16146inline SSLClient::SSLClient(
const std::string &host,
int port)
16147 : SSLClient(host, port, std::string(), std::string()) {}
16149inline void SSLClient::init_ctx() {
16150 ctx_ = tls::create_client_context();
16151 if (ctx_) { tls::set_min_version(ctx_, tls::Version::TLS1_2); }
16154inline void SSLClient::reset_ctx_on_error() {
16155 last_backend_error_ = tls::get_error();
16156 tls::free_context(ctx_);
16160inline SSLClient::SSLClient(
const std::string &host,
int port,
16161 const std::string &client_cert_path,
16162 const std::string &client_key_path,
16163 const std::string &private_key_password)
16164 : ClientImpl(host, port, client_cert_path, client_key_path) {
16166 if (!ctx_) {
return; }
16168 if (!client_cert_path.empty() && !client_key_path.empty()) {
16169 const char *password =
16170 private_key_password.empty() ? nullptr : private_key_password.c_str();
16171 if (!tls::set_client_cert_file(ctx_, client_cert_path.c_str(),
16172 client_key_path.c_str(), password)) {
16173 reset_ctx_on_error();
16178inline SSLClient::SSLClient(
const std::string &host,
int port,
16179 const PemMemory &pem)
16180 : ClientImpl(host, port) {
16182 if (!ctx_) {
return; }
16184 if (pem.cert_pem && pem.key_pem) {
16185 if (!tls::set_client_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
16186 pem.private_key_password)) {
16187 reset_ctx_on_error();
16192inline void SSLClient::set_ca_cert_store(tls::ca_store_t ca_cert_store) {
16193 if (ca_cert_store && ctx_) {
16195 tls::set_ca_store(ctx_, ca_cert_store);
16196 ca_cert_store_set_ =
true;
16197 }
else if (ca_cert_store) {
16198 tls::free_ca_store(ca_cert_store);
16203SSLClient::set_server_certificate_verifier(tls::VerifyCallback verifier) {
16204 if (!ctx_) {
return; }
16205 tls::set_verify_callback(ctx_, verifier);
16208inline void SSLClient::set_session_verifier(
16209 std::function<SSLVerifierResponse(tls::session_t)> verifier) {
16210 session_verifier_ = std::move(verifier);
16213#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
16214inline void SSLClient::enable_windows_certificate_verification(
bool enabled) {
16215 enable_windows_cert_verification_ = enabled;
16219inline void SSLClient::load_ca_cert_store(
const char *ca_cert,
16220 std::size_t size) {
16221 if (ctx_ && ca_cert && size > 0) {
16222 ca_cert_pem_.assign(ca_cert, size);
16223 tls::load_ca_pem(ctx_, ca_cert, size);
16227inline bool SSLClient::load_certs() {
16230 std::call_once(initialize_cert_, [&]() {
16231 std::lock_guard<std::mutex> guard(ctx_mutex_);
16233 ret = detail::load_client_ca_config(
16234 ctx_, ca_cert_file_path_, ca_cert_dir_path_,
16235 !ca_cert_pem_.empty() || ca_cert_store_set_, system_ca_mode_,
16236 last_backend_error_);
16242inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
16243 using namespace tls;
16246 if (server_certificate_verification_) {
16247 if (!load_certs()) {
16248 error = Error::SSLLoadingCerts;
16249 output_error_log(error,
nullptr);
16254 bool is_ip = detail::is_ip_address(host_);
16256#if defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
16261 set_verify_client(ctx_, server_certificate_verification_);
16265 session_t session =
nullptr;
16267 std::lock_guard<std::mutex> guard(ctx_mutex_);
16268 session = create_session(ctx_, socket.sock);
16272 error = Error::SSLConnection;
16273 last_backend_error_ = get_error();
16278 bool success =
false;
16279 auto session_guard = detail::scope_exit([&] {
16280 if (!success) { free_session(session); }
16287 if (!set_sni(session, host_.c_str())) {
16288 error = Error::SSLConnection;
16289 last_backend_error_ = get_error();
16296 if (!connect_nonblocking(session, socket.sock, connection_timeout_sec_,
16297 connection_timeout_usec_, &tls_err)) {
16298 last_ssl_error_ =
static_cast<int>(tls_err.code);
16299 last_backend_error_ = tls_err.backend_code;
16300 if (tls_err.code == ErrorCode::CertVerifyFailed) {
16301 error = Error::SSLServerVerification;
16302 }
else if (tls_err.code == ErrorCode::HostnameMismatch) {
16303 error = Error::SSLServerHostnameVerification;
16305 error = Error::SSLConnection;
16307 output_error_log(error,
nullptr);
16312 auto verification_status = SSLVerifierResponse::NoDecisionMade;
16313 if (session_verifier_) { verification_status = session_verifier_(session); }
16315 if (verification_status == SSLVerifierResponse::CertificateRejected) {
16316 last_backend_error_ = get_error();
16317 error = Error::SSLServerVerification;
16318 output_error_log(error,
nullptr);
16323 if (verification_status == SSLVerifierResponse::NoDecisionMade &&
16324 server_certificate_verification_) {
16325 verify_result_ = tls::get_verify_result(session);
16326 if (verify_result_ != 0) {
16327 last_backend_error_ =
static_cast<uint64_t
>(verify_result_);
16328 error = Error::SSLServerVerification;
16329 output_error_log(error,
nullptr);
16333 auto server_cert = get_peer_cert(session);
16334 if (!server_cert) {
16335 last_backend_error_ = get_error();
16336 error = Error::SSLServerVerification;
16337 output_error_log(error,
nullptr);
16340 auto cert_guard = detail::scope_exit([&] { free_cert(server_cert); });
16347 if (server_hostname_verification_) {
16348 if (!verify_hostname(server_cert, host_.c_str())) {
16349 last_backend_error_ = hostname_mismatch_code();
16350 error = Error::SSLServerHostnameVerification;
16351 output_error_log(error,
nullptr);
16356#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
16363 if (enable_windows_cert_verification_ &&
16364 system_ca_mode_ != SystemCAMode::Disabled &&
16365 ca_cert_file_path_.empty() && ca_cert_dir_path_.empty() &&
16366 ca_cert_pem_.empty() && !ca_cert_store_set_) {
16367 std::vector<unsigned char> der;
16368 if (get_cert_der(server_cert, der)) {
16369 uint64_t wincrypt_error = 0;
16370 if (!detail::verify_cert_with_windows_schannel(
16371 der, host_, server_hostname_verification_, wincrypt_error)) {
16372 last_backend_error_ = wincrypt_error;
16373 error = Error::SSLServerVerification;
16374 output_error_log(error,
nullptr);
16383 socket.ssl = session;
16387inline void Client::set_digest_auth(
const std::string &username,
16388 const std::string &password) {
16389 cli_->set_digest_auth(username, password);
16392inline void Client::set_proxy_digest_auth(
const std::string &username,
16393 const std::string &password) {
16394 cli_->set_proxy_digest_auth(username, password);
16397inline void Client::enable_server_certificate_verification(
bool enabled) {
16398 cli_->enable_server_certificate_verification(enabled);
16401inline void Client::enable_server_hostname_verification(
bool enabled) {
16402 cli_->enable_server_hostname_verification(enabled);
16405inline void Client::enable_system_ca(
bool enabled) {
16406 cli_->enable_system_ca(enabled);
16409#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
16410inline void Client::enable_windows_certificate_verification(
bool enabled) {
16412 static_cast<SSLClient &
>(*cli_).enable_windows_certificate_verification(
16418inline void Client::set_ca_cert_path(
const std::string &ca_cert_file_path,
16419 const std::string &ca_cert_dir_path) {
16420 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
16423inline void Client::set_ca_cert_store(tls::ca_store_t ca_cert_store) {
16425 static_cast<SSLClient &
>(*cli_).set_ca_cert_store(ca_cert_store);
16426 }
else if (ca_cert_store) {
16427 tls::free_ca_store(ca_cert_store);
16431inline void Client::load_ca_cert_store(
const char *ca_cert, std::size_t size) {
16434 static_cast<SSLClient &
>(*cli_).load_ca_cert_store(ca_cert, size);
16439Client::set_server_certificate_verifier(tls::VerifyCallback verifier) {
16441 static_cast<SSLClient &
>(*cli_).set_server_certificate_verifier(
16442 std::move(verifier));
16446inline void Client::set_session_verifier(
16447 std::function<SSLVerifierResponse(tls::session_t)> verifier) {
16449 static_cast<SSLClient &
>(*cli_).set_session_verifier(std::move(verifier));
16453inline tls::ctx_t Client::tls_context()
const {
16454 if (is_ssl_) {
return static_cast<SSLClient &
>(*cli_).tls_context(); }
16464#ifdef CPPHTTPLIB_SSL_ENABLED
16469inline PeerCert get_peer_cert_from_session(const_session_t session) {
16470 return PeerCert(get_peer_cert(session));
16475inline VerifyCallback &get_verify_callback() {
16476 static thread_local VerifyCallback callback;
16480inline VerifyCallback &get_mbedtls_verify_callback() {
16481 static thread_local VerifyCallback callback;
16486inline bool is_ipv4_address(
const std::string &str) {
16488 for (
char c : str) {
16491 }
else if (!isdigit(
static_cast<unsigned char>(c))) {
16499inline bool parse_ipv4(
const std::string &str,
unsigned char *out) {
16500 const char *p = str.c_str();
16501 for (
int i = 0; i < 4; i++) {
16503 if (*p !=
'.') {
return false; }
16508 while (*p >=
'0' && *p <=
'9') {
16509 val = val * 10 + (*p -
'0');
16510 if (val > 255) {
return false; }
16514 if (digits == 0) {
return false; }
16516 if (digits > 1 && *(p - digits) ==
'0') {
return false; }
16517 out[i] =
static_cast<unsigned char>(val);
16527inline size_t parse_ip_address(
const std::string &str,
unsigned char *out) {
16528 if (is_ipv4_address(str)) {
return parse_ipv4(str, out) ? 4 : 0; }
16529 struct in6_addr addr6 = {};
16530 if (inet_pton(AF_INET6, str.c_str(), &addr6) == 1) {
16531 memcpy(out, &addr6, 16);
16539template <
typename Callback>
16540inline bool enumerate_windows_system_certs(Callback cb) {
16541 bool loaded =
false;
16542 static const wchar_t *store_names[] = {L
"ROOT", L
"CA"};
16543 for (
auto store_name : store_names) {
16544 HCERTSTORE hStore = CertOpenSystemStoreW(0, store_name);
16546 PCCERT_CONTEXT pContext =
nullptr;
16547 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
16549 if (cb(pContext->pbCertEncoded, pContext->cbCertEncoded)) {
16553 CertCloseStore(hStore, 0);
16560#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
16562template <
typename Callback>
16563inline bool enumerate_macos_keychain_certs(Callback cb) {
16564 bool loaded =
false;
16565 const SecTrustSettingsDomain domains[] = {
16566 kSecTrustSettingsDomainSystem,
16567 kSecTrustSettingsDomainAdmin,
16568 kSecTrustSettingsDomainUser,
16570 for (
auto domain : domains) {
16571 CFArrayRef certs =
nullptr;
16572 OSStatus status = SecTrustSettingsCopyCertificates(domain, &certs);
16573 if (status != errSecSuccess || !certs) {
16574 if (certs) CFRelease(certs);
16577 CFIndex count = CFArrayGetCount(certs);
16578 for (CFIndex i = 0; i < count; i++) {
16579 SecCertificateRef cert =
16580 (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
16581 CFDataRef data = SecCertificateCopyData(cert);
16583 if (cb(CFDataGetBytePtr(data),
16584 static_cast<size_t>(CFDataGetLength(data)))) {
16596#if !defined(_WIN32) && !(defined(__APPLE__) && \
16597 defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN))
16599inline const char **system_ca_paths() {
16600 static const char *paths[] = {
16601 "/etc/ssl/certs/ca-certificates.crt",
16602 "/etc/pki/tls/certs/ca-bundle.crt",
16603 "/etc/ssl/ca-bundle.pem",
16604 "/etc/pki/tls/cacert.pem",
16605 "/etc/ssl/cert.pem",
16611inline const char **system_ca_dirs() {
16612 static const char *dirs[] = {
"/etc/ssl/certs",
16613 "/etc/pki/tls/certs",
16614 "/usr/share/ca-certificates",
16622inline bool set_client_ca_file(ctx_t ctx,
const char *ca_file,
16623 const char *ca_dir) {
16624 if (!ctx) {
return false; }
16626 bool success =
true;
16627 if (ca_file && *ca_file) {
16628 if (!load_ca_file(ctx, ca_file)) { success =
false; }
16630 if (ca_dir && *ca_dir) {
16631 if (!load_ca_dir(ctx, ca_dir)) { success =
false; }
16634#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16636 if (ca_file && *ca_file) {
16637 auto list = SSL_load_client_CA_file(ca_file);
16638 if (list) { SSL_CTX_set_client_CA_list(
static_cast<SSL_CTX *
>(ctx), list); }
16645inline bool set_server_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
16646 const char *password) {
16647 return set_client_cert_pem(ctx, cert, key, password);
16650inline bool set_server_cert_file(ctx_t ctx,
const char *cert_path,
16651 const char *key_path,
const char *password) {
16652 return set_client_cert_file(ctx, cert_path, key_path, password);
16656inline PeerCert::PeerCert() =
default;
16658inline PeerCert::PeerCert(cert_t cert) : cert_(cert) {}
16660inline PeerCert::PeerCert(PeerCert &&other) noexcept : cert_(other.cert_) {
16661 other.cert_ =
nullptr;
16664inline PeerCert &PeerCert::operator=(PeerCert &&other)
noexcept {
16665 if (
this != &other) {
16666 if (cert_) { free_cert(cert_); }
16667 cert_ = other.cert_;
16668 other.cert_ =
nullptr;
16673inline PeerCert::~PeerCert() {
16674 if (cert_) { free_cert(cert_); }
16677inline PeerCert::operator bool()
const {
return cert_ !=
nullptr; }
16679inline std::string PeerCert::subject_cn()
const {
16680 return cert_ ? get_cert_subject_cn(cert_) : std::string();
16683inline std::string PeerCert::issuer_name()
const {
16684 return cert_ ? get_cert_issuer_name(cert_) : std::string();
16687inline bool PeerCert::check_hostname(
const char *hostname)
const {
16688 return cert_ ? verify_hostname(cert_, hostname) : false;
16691inline std::vector<SanEntry> PeerCert::sans()
const {
16692 std::vector<SanEntry> result;
16693 if (cert_) { get_cert_sans(cert_, result); }
16697inline bool PeerCert::validity(time_t ¬_before, time_t ¬_after)
const {
16698 return cert_ ? get_cert_validity(cert_, not_before, not_after) : false;
16701inline std::string PeerCert::serial()
const {
16702 return cert_ ? get_cert_serial(cert_) : std::string();
16706inline std::string VerifyContext::subject_cn()
const {
16707 return cert ? get_cert_subject_cn(cert) : std::string();
16710inline std::string VerifyContext::issuer_name()
const {
16711 return cert ? get_cert_issuer_name(cert) : std::string();
16714inline bool VerifyContext::check_hostname(
const char *hostname)
const {
16715 return cert ? verify_hostname(cert, hostname) : false;
16718inline std::vector<SanEntry> VerifyContext::sans()
const {
16719 std::vector<SanEntry> result;
16720 if (cert) { get_cert_sans(cert, result); }
16724inline bool VerifyContext::validity(time_t ¬_before,
16725 time_t ¬_after)
const {
16726 return cert ? get_cert_validity(cert, not_before, not_after) : false;
16729inline std::string VerifyContext::serial()
const {
16730 return cert ? get_cert_serial(cert) : std::string();
16734inline std::string TlsError::verify_error_to_string(
long error_code) {
16735 return verify_error_string(error_code);
16741inline tls::PeerCert Request::peer_cert()
const {
16742 return tls::get_peer_cert_from_session(ssl);
16746inline std::string Request::sni()
const {
16747 if (!ssl) {
return std::string(); }
16748 const char *s = tls::get_sni(ssl);
16749 return s ? std::string(s) : std::string();
16762#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16768inline ErrorCode map_ssl_error(
int ssl_error,
int &out_errno) {
16769 switch (ssl_error) {
16770 case SSL_ERROR_NONE:
return ErrorCode::Success;
16771 case SSL_ERROR_WANT_READ:
return ErrorCode::WantRead;
16772 case SSL_ERROR_WANT_WRITE:
return ErrorCode::WantWrite;
16773 case SSL_ERROR_ZERO_RETURN:
return ErrorCode::PeerClosed;
16774 case SSL_ERROR_SYSCALL: out_errno = errno;
return ErrorCode::SyscallError;
16775 case SSL_ERROR_SSL:
16776 default:
return ErrorCode::Fatal;
16783inline STACK_OF(X509_NAME) *
16784 create_client_ca_list_from_pem(
const char *ca_pem) {
16785 if (!ca_pem) {
return nullptr; }
16787 auto ca_list = sk_X509_NAME_new_null();
16788 if (!ca_list) {
return nullptr; }
16790 BIO *bio = BIO_new_mem_buf(ca_pem, -1);
16792 sk_X509_NAME_pop_free(ca_list, X509_NAME_free);
16796 X509 *cert =
nullptr;
16797 while ((cert = PEM_read_bio_X509(bio,
nullptr,
nullptr,
nullptr)) !=
16799 const X509_NAME *name = X509_get_subject_name(cert);
16801 sk_X509_NAME_push(ca_list, X509_NAME_dup(
const_cast<X509_NAME *
>(name)));
16811inline int openssl_verify_callback(
int preverify_ok, X509_STORE_CTX *ctx) {
16812 auto &callback = get_verify_callback();
16813 if (!callback) {
return preverify_ok; }
16816 auto ssl =
static_cast<SSL *
>(
16817 X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
16818 if (!ssl) {
return preverify_ok; }
16821 auto cert = X509_STORE_CTX_get_current_cert(ctx);
16822 int depth = X509_STORE_CTX_get_error_depth(ctx);
16823 int error = X509_STORE_CTX_get_error(ctx);
16826 VerifyContext verify_ctx;
16827 verify_ctx.session =
static_cast<session_t
>(ssl);
16828 verify_ctx.cert =
static_cast<cert_t
>(cert);
16829 verify_ctx.depth = depth;
16830 verify_ctx.preverify_ok = (preverify_ok != 0);
16831 verify_ctx.error_code = error;
16832 verify_ctx.error_string =
16833 (error != X509_V_OK) ? X509_verify_cert_error_string(error) :
nullptr;
16835 return callback(verify_ctx) ? 1 : 0;
16841#if !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER) && \
16842 OPENSSL_VERSION_NUMBER >= 0x30300000L
16843#define CPPHTTPLIB_HAS_X509_STORE_GET1_OBJECTS
16846inline STACK_OF(X509_OBJECT) * get_store_objects(X509_STORE *store) {
16847#ifdef CPPHTTPLIB_HAS_X509_STORE_GET1_OBJECTS
16848 return X509_STORE_get1_objects(store);
16850 return X509_STORE_get0_objects(store);
16854inline void release_store_objects(STACK_OF(X509_OBJECT) * objs) {
16855#ifdef CPPHTTPLIB_HAS_X509_STORE_GET1_OBJECTS
16856 sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
16864inline ctx_t create_client_context() {
16865 SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
16868 SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
16870 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
16872 return static_cast<ctx_t
>(ctx);
16875inline void free_context(ctx_t ctx) {
16876 if (ctx) { SSL_CTX_free(
static_cast<SSL_CTX *
>(ctx)); }
16879inline bool set_min_version(ctx_t ctx, Version version) {
16880 if (!ctx)
return false;
16881 return SSL_CTX_set_min_proto_version(
static_cast<SSL_CTX *
>(ctx),
16882 static_cast<int>(version)) == 1;
16885inline bool load_ca_pem(ctx_t ctx,
const char *pem,
size_t len) {
16886 if (!ctx || !pem || len == 0)
return false;
16888 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
16889 auto store = SSL_CTX_get_cert_store(ssl_ctx);
16890 if (!store)
return false;
16892 auto bio = BIO_new_mem_buf(pem,
static_cast<int>(len));
16893 if (!bio)
return false;
16896 X509 *cert =
nullptr;
16897 while ((cert = PEM_read_bio_X509(bio,
nullptr,
nullptr,
nullptr)) !=
16899 if (X509_STORE_add_cert(store, cert) != 1) {
16901 auto err = ERR_peek_last_error();
16902 if (ERR_GET_REASON(err) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
16916inline bool load_ca_file(ctx_t ctx,
const char *file_path) {
16917 if (!ctx || !file_path)
return false;
16918 return SSL_CTX_load_verify_locations(
static_cast<SSL_CTX *
>(ctx), file_path,
16922inline bool load_ca_dir(ctx_t ctx,
const char *dir_path) {
16923 if (!ctx || !dir_path)
return false;
16924 return SSL_CTX_load_verify_locations(
static_cast<SSL_CTX *
>(ctx),
nullptr,
16928inline bool load_system_certs(ctx_t ctx) {
16929 if (!ctx)
return false;
16930 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
16934 auto store = SSL_CTX_get_cert_store(ssl_ctx);
16935 if (!store)
return false;
16937 bool loaded_any =
false;
16938 static const wchar_t *store_names[] = {L
"ROOT", L
"CA"};
16939 for (
auto store_name : store_names) {
16940 auto hStore = CertOpenSystemStoreW(NULL, store_name);
16941 if (!hStore)
continue;
16943 PCCERT_CONTEXT pContext =
nullptr;
16944 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
16946 const unsigned char *data = pContext->pbCertEncoded;
16947 auto x509 = d2i_X509(
nullptr, &data, pContext->cbCertEncoded);
16949 if (X509_STORE_add_cert(store, x509) == 1) { loaded_any =
true; }
16953 CertCloseStore(hStore, 0);
16957#elif defined(__APPLE__)
16958#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
16960 auto store = SSL_CTX_get_cert_store(ssl_ctx);
16961 if (!store)
return false;
16963 bool loaded_any =
false;
16964 const SecTrustSettingsDomain domains[] = {
16965 kSecTrustSettingsDomainSystem,
16966 kSecTrustSettingsDomainAdmin,
16967 kSecTrustSettingsDomainUser,
16969 for (
auto domain : domains) {
16970 CFArrayRef certs =
nullptr;
16971 if (SecTrustSettingsCopyCertificates(domain, &certs) != errSecSuccess ||
16973 if (certs) CFRelease(certs);
16976 auto count = CFArrayGetCount(certs);
16977 for (CFIndex i = 0; i < count; i++) {
16978 auto cert =
reinterpret_cast<SecCertificateRef
>(
16979 const_cast<void *
>(CFArrayGetValueAtIndex(certs, i)));
16980 CFDataRef der = SecCertificateCopyData(cert);
16982 const unsigned char *data = CFDataGetBytePtr(der);
16983 auto x509 = d2i_X509(
nullptr, &data, CFDataGetLength(der));
16985 if (X509_STORE_add_cert(store, x509) == 1) { loaded_any =
true; }
16993 return loaded_any || SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
16995 return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
17000 return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
17004inline bool set_client_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
17005 const char *password) {
17006 if (!ctx || !cert || !key)
return false;
17008 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17011 auto cert_bio = BIO_new_mem_buf(cert, -1);
17012 if (!cert_bio)
return false;
17014 auto x509 = PEM_read_bio_X509(cert_bio,
nullptr,
nullptr,
nullptr);
17015 BIO_free(cert_bio);
17016 if (!x509)
return false;
17018 auto cert_ok = SSL_CTX_use_certificate(ssl_ctx, x509) == 1;
17020 if (!cert_ok)
return false;
17023 auto key_bio = BIO_new_mem_buf(key, -1);
17024 if (!key_bio)
return false;
17026 auto pkey = PEM_read_bio_PrivateKey(key_bio,
nullptr,
nullptr,
17027 password ?
const_cast<char *
>(password)
17030 if (!pkey)
return false;
17032 auto key_ok = SSL_CTX_use_PrivateKey(ssl_ctx, pkey) == 1;
17033 EVP_PKEY_free(pkey);
17035 return key_ok && SSL_CTX_check_private_key(ssl_ctx) == 1;
17038inline bool set_client_cert_file(ctx_t ctx,
const char *cert_path,
17039 const char *key_path,
const char *password) {
17040 if (!ctx || !cert_path || !key_path)
return false;
17042 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17044 if (password && password[0] !=
'\0') {
17045 SSL_CTX_set_default_passwd_cb_userdata(
17046 ssl_ctx,
reinterpret_cast<void *
>(
const_cast<char *
>(password)));
17049 return SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_path) == 1 &&
17050 SSL_CTX_use_PrivateKey_file(ssl_ctx, key_path, SSL_FILETYPE_PEM) == 1;
17053inline ctx_t create_server_context() {
17054 SSL_CTX *ctx = SSL_CTX_new(TLS_server_method());
17056 SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION |
17057 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
17058 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
17060 return static_cast<ctx_t
>(ctx);
17063inline void set_verify_client(ctx_t ctx,
bool require) {
17065 SSL_CTX_set_verify(
static_cast<SSL_CTX *
>(ctx),
17067 ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
17072inline session_t create_session(ctx_t ctx,
socket_t sock) {
17075 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17076 SSL *ssl = SSL_new(ssl_ctx);
17077 if (!ssl)
return nullptr;
17080 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
17082 auto bio = BIO_new_socket(
static_cast<int>(sock), BIO_NOCLOSE);
17088 SSL_set_bio(ssl, bio, bio);
17089 return static_cast<session_t
>(ssl);
17092inline void free_session(session_t session) {
17093 if (session) { SSL_free(
static_cast<SSL *
>(session)); }
17096inline bool set_sni(session_t session,
const char *hostname) {
17097 if (!session || !hostname)
return false;
17099 auto ssl =
static_cast<SSL *
>(session);
17102#if defined(OPENSSL_IS_BORINGSSL)
17103 return SSL_set_tlsext_host_name(ssl, hostname) == 1;
17106 return SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
17107 static_cast<void *
>(
const_cast<char *
>(hostname))) == 1;
17111inline bool set_hostname(session_t session,
const char *hostname) {
17112 if (!session || !hostname)
return false;
17114 auto ssl =
static_cast<SSL *
>(session);
17117 auto param = SSL_get0_param(ssl);
17118 if (!param)
return false;
17120 if (detail::is_ip_address(hostname)) {
17123 if (X509_VERIFY_PARAM_set1_ip_asc(param, hostname) != 1) {
return false; }
17126 if (!set_sni(session, hostname)) {
return false; }
17128 X509_VERIFY_PARAM_set_hostflags(param,
17129 X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
17130 if (X509_VERIFY_PARAM_set1_host(param, hostname, 0) != 1) {
return false; }
17133 SSL_set_verify(ssl, SSL_VERIFY_PEER,
nullptr);
17137inline TlsError connect(session_t session) {
17138 if (!session) {
return TlsError(); }
17140 auto ssl =
static_cast<SSL *
>(session);
17141 auto ret = SSL_connect(ssl);
17145 err.code = ErrorCode::Success;
17147 auto ssl_err = SSL_get_error(ssl, ret);
17148 err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
17149 err.backend_code = ERR_get_error();
17154inline TlsError accept(session_t session) {
17155 if (!session) {
return TlsError(); }
17157 auto ssl =
static_cast<SSL *
>(session);
17158 auto ret = SSL_accept(ssl);
17162 err.code = ErrorCode::Success;
17164 auto ssl_err = SSL_get_error(ssl, ret);
17165 err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
17166 err.backend_code = ERR_get_error();
17171inline bool connect_nonblocking(session_t session,
socket_t sock,
17172 time_t timeout_sec, time_t timeout_usec,
17175 if (err) { err->code = ErrorCode::Fatal; }
17179 auto ssl =
static_cast<SSL *
>(session);
17180 auto bio = SSL_get_rbio(ssl);
17183 detail::set_nonblocking(sock,
true);
17184 if (bio) { BIO_set_nbio(bio, 1); }
17186 auto cleanup = detail::scope_exit([&]() {
17188 if (bio) { BIO_set_nbio(bio, 0); }
17189 detail::set_nonblocking(sock,
false);
17193 while ((res = SSL_connect(ssl)) != 1) {
17194 auto ssl_err = SSL_get_error(ssl, res);
17196 case SSL_ERROR_WANT_READ:
17197 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
17201 case SSL_ERROR_WANT_WRITE:
17202 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
17209 err->code = impl::map_ssl_error(ssl_err, err->sys_errno);
17210 err->backend_code = ERR_get_error();
17214 if (err) { err->code = ErrorCode::Success; }
17218inline bool accept_nonblocking(session_t session,
socket_t sock,
17219 time_t timeout_sec, time_t timeout_usec,
17222 if (err) { err->code = ErrorCode::Fatal; }
17226 auto ssl =
static_cast<SSL *
>(session);
17227 auto bio = SSL_get_rbio(ssl);
17230 detail::set_nonblocking(sock,
true);
17231 if (bio) { BIO_set_nbio(bio, 1); }
17233 auto cleanup = detail::scope_exit([&]() {
17235 if (bio) { BIO_set_nbio(bio, 0); }
17236 detail::set_nonblocking(sock,
false);
17240 while ((res = SSL_accept(ssl)) != 1) {
17241 auto ssl_err = SSL_get_error(ssl, res);
17243 case SSL_ERROR_WANT_READ:
17244 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
17248 case SSL_ERROR_WANT_WRITE:
17249 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
17256 err->code = impl::map_ssl_error(ssl_err, err->sys_errno);
17257 err->backend_code = ERR_get_error();
17261 if (err) { err->code = ErrorCode::Success; }
17265inline ssize_t read(session_t session,
void *buf,
size_t len, TlsError &err) {
17266 if (!session || !buf) {
17267 err.code = ErrorCode::Fatal;
17271 auto ssl =
static_cast<SSL *
>(session);
17272 constexpr auto max_len =
17273 static_cast<size_t>((std::numeric_limits<int>::max)());
17274 if (len > max_len) { len = max_len; }
17275 auto ret = SSL_read(ssl, buf,
static_cast<int>(len));
17278 err.code = ErrorCode::Success;
17282 auto ssl_err = SSL_get_error(ssl, ret);
17283 err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
17284 if (err.code == ErrorCode::PeerClosed) {
17287 if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
17291inline ssize_t write(session_t session,
const void *buf,
size_t len,
17293 if (!session || !buf) {
17294 err.code = ErrorCode::Fatal;
17298 auto ssl =
static_cast<SSL *
>(session);
17299 auto ret = SSL_write(ssl, buf,
static_cast<int>(len));
17302 err.code = ErrorCode::Success;
17306 auto ssl_err = SSL_get_error(ssl, ret);
17307 err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
17308 if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
17312inline int pending(const_session_t session) {
17313 if (!session)
return 0;
17314 return SSL_pending(
static_cast<SSL *
>(
const_cast<void *
>(session)));
17317inline void shutdown(session_t session,
bool graceful) {
17318 if (!session)
return;
17320 auto ssl =
static_cast<SSL *
>(session);
17323 if (SSL_shutdown(ssl) == 0) {
17330inline bool is_peer_closed(session_t session,
socket_t sock) {
17331 if (!session)
return true;
17334 detail::set_nonblocking(sock,
true);
17335 auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
17337 auto ssl =
static_cast<SSL *
>(session);
17339 auto ret = SSL_peek(ssl, &buf, 1);
17340 if (ret > 0)
return false;
17342 auto err = SSL_get_error(ssl, ret);
17343 return err == SSL_ERROR_ZERO_RETURN;
17346inline cert_t get_peer_cert(const_session_t session) {
17347 if (!session)
return nullptr;
17348 return static_cast<cert_t
>(SSL_get1_peer_certificate(
17349 static_cast<SSL *
>(
const_cast<void *
>(session))));
17352inline void free_cert(cert_t cert) {
17353 if (cert) { X509_free(
static_cast<X509 *
>(cert)); }
17356inline bool verify_hostname(cert_t cert,
const char *hostname) {
17357 if (!cert || !hostname)
return false;
17359 auto x509 =
static_cast<X509 *
>(cert);
17362 if (detail::is_ip_address(hostname)) {
17363 return X509_check_ip_asc(x509, hostname, 0) == 1;
17365 return X509_check_host(x509, hostname, strlen(hostname), 0,
nullptr) == 1;
17368inline uint64_t hostname_mismatch_code() {
17369 return static_cast<uint64_t
>(X509_V_ERR_HOSTNAME_MISMATCH);
17372inline long get_verify_result(const_session_t session) {
17373 if (!session)
return X509_V_ERR_UNSPECIFIED;
17374 return SSL_get_verify_result(
static_cast<SSL *
>(
const_cast<void *
>(session)));
17377inline std::string get_cert_subject_cn(cert_t cert) {
17378 if (!cert)
return "";
17379 auto x509 =
static_cast<X509 *
>(cert);
17380 auto subject_name = X509_get_subject_name(x509);
17381 if (!subject_name)
return "";
17384 auto idx = X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1);
17385 if (idx < 0)
return "";
17387 auto entry = X509_NAME_get_entry(subject_name, idx);
17388 if (!entry)
return "";
17390 auto data = X509_NAME_ENTRY_get_data(entry);
17391 if (!data)
return "";
17393 return std::string(
17394 reinterpret_cast<const char *
>(ASN1_STRING_get0_data(data)),
17395 static_cast<size_t>(ASN1_STRING_length(data)));
17398inline std::string get_cert_issuer_name(cert_t cert) {
17399 if (!cert)
return "";
17400 auto x509 =
static_cast<X509 *
>(cert);
17401 auto issuer_name = X509_get_issuer_name(x509);
17402 if (!issuer_name)
return "";
17405 X509_NAME_oneline(issuer_name, buf,
sizeof(buf));
17406 return std::string(buf);
17409inline bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans) {
17411 if (!cert)
return false;
17412 auto x509 =
static_cast<X509 *
>(cert);
17414 auto names =
static_cast<GENERAL_NAMES *
>(
17415 X509_get_ext_d2i(x509, NID_subject_alt_name,
nullptr,
nullptr));
17416 if (!names)
return true;
17418 auto count = sk_GENERAL_NAME_num(names);
17419 for (
decltype(count) i = 0; i < count; i++) {
17420 auto gen = sk_GENERAL_NAME_value(names, i);
17421 if (!gen)
continue;
17424 switch (gen->type) {
17426 entry.type = SanType::DNS;
17427 if (gen->d.dNSName) {
17428 entry.value = std::string(
17429 reinterpret_cast<const char *
>(
17430 ASN1_STRING_get0_data(gen->d.dNSName)),
17431 static_cast<size_t>(ASN1_STRING_length(gen->d.dNSName)));
17435 entry.type = SanType::IP;
17436 if (gen->d.iPAddress) {
17437 auto data = ASN1_STRING_get0_data(gen->d.iPAddress);
17438 auto len = ASN1_STRING_length(gen->d.iPAddress);
17441 char buf[INET_ADDRSTRLEN];
17442 inet_ntop(AF_INET, data, buf,
sizeof(buf));
17444 }
else if (len == 16) {
17446 char buf[INET6_ADDRSTRLEN];
17447 inet_ntop(AF_INET6, data, buf,
sizeof(buf));
17453 entry.type = SanType::EMAIL;
17454 if (gen->d.rfc822Name) {
17455 entry.value = std::string(
17456 reinterpret_cast<const char *
>(
17457 ASN1_STRING_get0_data(gen->d.rfc822Name)),
17458 static_cast<size_t>(ASN1_STRING_length(gen->d.rfc822Name)));
17462 entry.type = SanType::URI;
17463 if (gen->d.uniformResourceIdentifier) {
17464 entry.value = std::string(
17465 reinterpret_cast<const char *
>(
17466 ASN1_STRING_get0_data(gen->d.uniformResourceIdentifier)),
17467 static_cast<size_t>(
17468 ASN1_STRING_length(gen->d.uniformResourceIdentifier)));
17471 default: entry.type = SanType::OTHER;
break;
17474 if (!entry.value.empty()) { sans.push_back(std::move(entry)); }
17477 GENERAL_NAMES_free(names);
17481inline bool get_cert_validity(cert_t cert, time_t ¬_before,
17482 time_t ¬_after) {
17483 if (!cert)
return false;
17484 auto x509 =
static_cast<X509 *
>(cert);
17486 auto nb = X509_get0_notBefore(x509);
17487 auto na = X509_get0_notAfter(x509);
17488 if (!nb || !na)
return false;
17490 ASN1_TIME *epoch = ASN1_TIME_new();
17491 if (!epoch)
return false;
17492 auto se = detail::scope_exit([&] { ASN1_TIME_free(epoch); });
17494 if (!ASN1_TIME_set(epoch, 0))
return false;
17498 if (!ASN1_TIME_diff(&pday, &psec, epoch, nb))
return false;
17499 not_before = 86400 * (time_t)pday + psec;
17501 if (!ASN1_TIME_diff(&pday, &psec, epoch, na))
return false;
17502 not_after = 86400 * (time_t)pday + psec;
17507inline std::string get_cert_serial(cert_t cert) {
17508 if (!cert)
return "";
17509 auto x509 =
static_cast<X509 *
>(cert);
17511 auto serial = X509_get_serialNumber(x509);
17512 if (!serial)
return "";
17514 auto bn = ASN1_INTEGER_to_BN(serial,
nullptr);
17515 if (!bn)
return "";
17517 auto hex = BN_bn2hex(bn);
17519 if (!hex)
return "";
17521 std::string result(hex);
17526inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
17527 if (!cert)
return false;
17528 auto x509 =
static_cast<X509 *
>(cert);
17529 auto len = i2d_X509(x509,
nullptr);
17530 if (len < 0)
return false;
17531 der.resize(
static_cast<size_t>(len));
17532 auto p = der.data();
17533 i2d_X509(x509, &p);
17537inline const char *get_sni(const_session_t session) {
17538 if (!session)
return nullptr;
17539 auto ssl =
static_cast<SSL *
>(
const_cast<void *
>(session));
17540 return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
17543inline uint64_t peek_error() {
return ERR_peek_last_error(); }
17545inline uint64_t get_error() {
return ERR_get_error(); }
17547inline std::string error_string(uint64_t code) {
17549 ERR_error_string_n(
static_cast<unsigned long>(code), buf,
sizeof(buf));
17550 return std::string(buf);
17553inline ca_store_t create_ca_store(
const char *pem,
size_t len) {
17554 auto mem = BIO_new_mem_buf(pem,
static_cast<int>(len));
17555 if (!mem) {
return nullptr; }
17556 auto mem_guard = detail::scope_exit([&] { BIO_free_all(mem); });
17558 auto inf = PEM_X509_INFO_read_bio(mem,
nullptr,
nullptr,
nullptr);
17559 if (!inf) {
return nullptr; }
17561 auto store = X509_STORE_new();
17563 for (
auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
17564 auto itmp = sk_X509_INFO_value(inf, i);
17565 if (!itmp) {
continue; }
17566 if (itmp->x509) { X509_STORE_add_cert(store, itmp->x509); }
17567 if (itmp->crl) { X509_STORE_add_crl(store, itmp->crl); }
17571 sk_X509_INFO_pop_free(inf, X509_INFO_free);
17572 return static_cast<ca_store_t
>(store);
17575inline void free_ca_store(ca_store_t store) {
17576 if (store) { X509_STORE_free(
static_cast<X509_STORE *
>(store)); }
17579inline bool set_ca_store(ctx_t ctx, ca_store_t store) {
17580 if (!ctx || !store) {
return false; }
17581 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17582 auto x509_store =
static_cast<X509_STORE *
>(store);
17585 if (SSL_CTX_get_cert_store(ssl_ctx) == x509_store) {
return true; }
17588 SSL_CTX_set_cert_store(ssl_ctx, x509_store);
17592inline size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs) {
17594 if (!ctx) {
return 0; }
17595 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17597 auto store = SSL_CTX_get_cert_store(ssl_ctx);
17598 if (!store) {
return 0; }
17600 auto objs = impl::get_store_objects(store);
17601 if (!objs) {
return 0; }
17602 auto se = detail::scope_exit([&] { impl::release_store_objects(objs); });
17604 auto count = sk_X509_OBJECT_num(objs);
17605 for (
decltype(count) i = 0; i < count; i++) {
17606 auto obj = sk_X509_OBJECT_value(objs, i);
17607 if (!obj) {
continue; }
17608 if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
17609 auto x509 = X509_OBJECT_get0_X509(obj);
17613 certs.push_back(
static_cast<cert_t
>(x509));
17617 return certs.size();
17620inline std::vector<std::string> get_ca_names(ctx_t ctx) {
17621 std::vector<std::string> names;
17622 if (!ctx) {
return names; }
17623 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17625 auto store = SSL_CTX_get_cert_store(ssl_ctx);
17626 if (!store) {
return names; }
17628 auto objs = impl::get_store_objects(store);
17629 if (!objs) {
return names; }
17630 auto se = detail::scope_exit([&] { impl::release_store_objects(objs); });
17632 auto count = sk_X509_OBJECT_num(objs);
17633 for (
decltype(count) i = 0; i < count; i++) {
17634 auto obj = sk_X509_OBJECT_value(objs, i);
17635 if (!obj) {
continue; }
17636 if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
17637 auto x509 = X509_OBJECT_get0_X509(obj);
17639 auto subject = X509_get_subject_name(x509);
17642 X509_NAME_oneline(subject, buf,
sizeof(buf));
17643 names.push_back(buf);
17651inline bool update_server_cert(ctx_t ctx,
const char *cert_pem,
17652 const char *key_pem,
const char *password) {
17653 if (!ctx || !cert_pem || !key_pem) {
return false; }
17654 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17657 auto cert_bio = BIO_new_mem_buf(cert_pem, -1);
17658 if (!cert_bio) {
return false; }
17659 auto cert = PEM_read_bio_X509(cert_bio,
nullptr,
nullptr,
nullptr);
17660 BIO_free(cert_bio);
17661 if (!cert) {
return false; }
17664 auto key_bio = BIO_new_mem_buf(key_pem, -1);
17669 auto key = PEM_read_bio_PrivateKey(key_bio,
nullptr,
nullptr,
17670 password ?
const_cast<char *
>(password)
17679 auto ret = SSL_CTX_use_certificate(ssl_ctx, cert) == 1 &&
17680 SSL_CTX_use_PrivateKey(ssl_ctx, key) == 1;
17683 EVP_PKEY_free(key);
17687inline bool update_server_client_ca(ctx_t ctx,
const char *ca_pem) {
17688 if (!ctx || !ca_pem) {
return false; }
17689 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17692 auto store = create_ca_store(ca_pem, strlen(ca_pem));
17693 if (!store) {
return false; }
17696 SSL_CTX_set_cert_store(ssl_ctx,
static_cast<X509_STORE *
>(store));
17699 auto ca_list = impl::create_client_ca_list_from_pem(ca_pem);
17702 SSL_CTX_set_client_CA_list(ssl_ctx, ca_list);
17708inline bool set_verify_callback(ctx_t ctx, VerifyCallback callback) {
17709 if (!ctx) {
return false; }
17710 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17712 impl::get_verify_callback() = std::move(callback);
17714 if (impl::get_verify_callback()) {
17715 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, impl::openssl_verify_callback);
17717 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER,
nullptr);
17722inline long get_verify_error(const_session_t session) {
17723 if (!session) {
return -1; }
17724 auto ssl =
static_cast<SSL *
>(
const_cast<void *
>(session));
17725 return SSL_get_verify_result(ssl);
17728inline std::string verify_error_string(
long error_code) {
17729 if (error_code == X509_V_OK) {
return ""; }
17730 const char *str = X509_verify_cert_error_string(
static_cast<int>(error_code));
17731 return str ? str :
"unknown error";
17746#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
17752struct MbedTlsSession {
17753 mbedtls_ssl_context ssl;
17755 std::string hostname;
17756 std::string sni_hostname;
17758 MbedTlsSession() { mbedtls_ssl_init(&ssl); }
17760 ~MbedTlsSession() { mbedtls_ssl_free(&ssl); }
17762 MbedTlsSession(
const MbedTlsSession &) =
delete;
17763 MbedTlsSession &operator=(
const MbedTlsSession &) =
delete;
17768inline int &mbedtls_last_error() {
17769 static thread_local int err = 0;
17774inline ErrorCode map_mbedtls_error(
int ret,
int &out_errno) {
17775 if (ret == 0) {
return ErrorCode::Success; }
17776 if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
return ErrorCode::WantRead; }
17777 if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
return ErrorCode::WantWrite; }
17778 if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
17779 return ErrorCode::PeerClosed;
17781 if (ret == MBEDTLS_ERR_NET_CONN_RESET || ret == MBEDTLS_ERR_NET_SEND_FAILED ||
17782 ret == MBEDTLS_ERR_NET_RECV_FAILED) {
17784 return ErrorCode::SyscallError;
17786 if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
17787 return ErrorCode::CertVerifyFailed;
17789 return ErrorCode::Fatal;
17793inline int mbedtls_net_send_cb(
void *ctx,
const unsigned char *buf,
17795 auto sock = *
static_cast<socket_t *
>(ctx);
17798 send(sock,
reinterpret_cast<const char *
>(buf),
static_cast<int>(len), 0);
17799 if (ret == SOCKET_ERROR) {
17800 int err = WSAGetLastError();
17801 if (err == WSAEWOULDBLOCK) {
return MBEDTLS_ERR_SSL_WANT_WRITE; }
17802 return MBEDTLS_ERR_NET_SEND_FAILED;
17805 auto ret = send(sock, buf, len, 0);
17807 if (errno == EAGAIN || errno == EWOULDBLOCK) {
17808 return MBEDTLS_ERR_SSL_WANT_WRITE;
17810 return MBEDTLS_ERR_NET_SEND_FAILED;
17813 return static_cast<int>(ret);
17817inline int mbedtls_net_recv_cb(
void *ctx,
unsigned char *buf,
size_t len) {
17818 auto sock = *
static_cast<socket_t *
>(ctx);
17821 recv(sock,
reinterpret_cast<char *
>(buf),
static_cast<int>(len), 0);
17822 if (ret == SOCKET_ERROR) {
17823 int err = WSAGetLastError();
17824 if (err == WSAEWOULDBLOCK) {
return MBEDTLS_ERR_SSL_WANT_READ; }
17825 return MBEDTLS_ERR_NET_RECV_FAILED;
17828 auto ret = recv(sock, buf, len, 0);
17830 if (errno == EAGAIN || errno == EWOULDBLOCK) {
17831 return MBEDTLS_ERR_SSL_WANT_READ;
17833 return MBEDTLS_ERR_NET_RECV_FAILED;
17836 if (ret == 0) {
return MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY; }
17837 return static_cast<int>(ret);
17841inline MbedTlsContext::MbedTlsContext() {
17842 mbedtls_ssl_config_init(&conf);
17843 mbedtls_entropy_init(&entropy);
17844 mbedtls_ctr_drbg_init(&ctr_drbg);
17845 mbedtls_x509_crt_init(&ca_chain);
17846 mbedtls_x509_crt_init(&own_cert);
17847 mbedtls_pk_init(&own_key);
17850inline MbedTlsContext::~MbedTlsContext() {
17851 mbedtls_pk_free(&own_key);
17852 mbedtls_x509_crt_free(&own_cert);
17853 mbedtls_x509_crt_free(&ca_chain);
17854 mbedtls_ctr_drbg_free(&ctr_drbg);
17855 mbedtls_entropy_free(&entropy);
17856 mbedtls_ssl_config_free(&conf);
17862inline std::string &mbedpending_sni() {
17863 static thread_local std::string sni;
17868inline int mbedtls_sni_callback(
void *p_ctx, mbedtls_ssl_context *ssl,
17869 const unsigned char *name,
size_t name_len) {
17875 if (name && name_len > 0) {
17876 mbedpending_sni().assign(
reinterpret_cast<const char *
>(name), name_len);
17878 mbedpending_sni().clear();
17883inline int mbedtls_verify_callback(
void *data, mbedtls_x509_crt *crt,
17884 int cert_depth, uint32_t *flags);
17887inline int mbedtls_verify_callback(
void *data, mbedtls_x509_crt *crt,
17888 int cert_depth, uint32_t *flags) {
17889 auto &callback = get_verify_callback();
17890 if (!callback) {
return 0; }
17893 auto *session =
static_cast<MbedTlsSession *
>(data);
17896 VerifyContext verify_ctx;
17897 verify_ctx.session =
static_cast<session_t
>(session);
17898 verify_ctx.cert =
static_cast<cert_t
>(crt);
17899 verify_ctx.depth = cert_depth;
17900 verify_ctx.preverify_ok = (*flags == 0);
17901 verify_ctx.error_code =
static_cast<long>(*flags);
17904 static thread_local char error_buf[256];
17906 mbedtls_x509_crt_verify_info(error_buf,
sizeof(error_buf),
"", *flags);
17907 verify_ctx.error_string = error_buf;
17909 verify_ctx.error_string =
nullptr;
17912 bool accepted = callback(verify_ctx);
17918 return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
17923inline ctx_t create_client_context() {
17924 auto ctx =
new (std::nothrow) impl::MbedTlsContext();
17925 if (!ctx) {
return nullptr; }
17927 ctx->is_server =
false;
17930 const char *pers =
"httplib_client";
17931 int ret = mbedtls_ctr_drbg_seed(
17932 &ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy,
17933 reinterpret_cast<const unsigned char *
>(pers), strlen(pers));
17935 impl::mbedtls_last_error() = ret;
17941 ret = mbedtls_ssl_config_defaults(&ctx->conf, MBEDTLS_SSL_IS_CLIENT,
17942 MBEDTLS_SSL_TRANSPORT_STREAM,
17943 MBEDTLS_SSL_PRESET_DEFAULT);
17945 impl::mbedtls_last_error() = ret;
17951 mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
17954 mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
17957#ifdef CPPHTTPLIB_MBEDTLS_V3
17958 mbedtls_ssl_conf_min_tls_version(&ctx->conf, MBEDTLS_SSL_VERSION_TLS1_2);
17960 mbedtls_ssl_conf_min_version(&ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3,
17961 MBEDTLS_SSL_MINOR_VERSION_3);
17964 return static_cast<ctx_t
>(ctx);
17967inline ctx_t create_server_context() {
17968 auto ctx =
new (std::nothrow) impl::MbedTlsContext();
17969 if (!ctx) {
return nullptr; }
17971 ctx->is_server =
true;
17974 const char *pers =
"httplib_server";
17975 int ret = mbedtls_ctr_drbg_seed(
17976 &ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy,
17977 reinterpret_cast<const unsigned char *
>(pers), strlen(pers));
17979 impl::mbedtls_last_error() = ret;
17985 ret = mbedtls_ssl_config_defaults(&ctx->conf, MBEDTLS_SSL_IS_SERVER,
17986 MBEDTLS_SSL_TRANSPORT_STREAM,
17987 MBEDTLS_SSL_PRESET_DEFAULT);
17989 impl::mbedtls_last_error() = ret;
17995 mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
17998 mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_NONE);
18001#ifdef CPPHTTPLIB_MBEDTLS_V3
18002 mbedtls_ssl_conf_min_tls_version(&ctx->conf, MBEDTLS_SSL_VERSION_TLS1_2);
18004 mbedtls_ssl_conf_min_version(&ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3,
18005 MBEDTLS_SSL_MINOR_VERSION_3);
18009 mbedtls_ssl_conf_sni(&ctx->conf, impl::mbedtls_sni_callback,
nullptr);
18011 return static_cast<ctx_t
>(ctx);
18014inline void free_context(ctx_t ctx) {
18015 if (ctx) {
delete static_cast<impl::MbedTlsContext *
>(ctx); }
18018inline bool set_min_version(ctx_t ctx, Version version) {
18019 if (!ctx) {
return false; }
18020 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18022#ifdef CPPHTTPLIB_MBEDTLS_V3
18024 mbedtls_ssl_protocol_version min_ver = MBEDTLS_SSL_VERSION_TLS1_2;
18025 if (version >= Version::TLS1_3) {
18026#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
18027 min_ver = MBEDTLS_SSL_VERSION_TLS1_3;
18030 mbedtls_ssl_conf_min_tls_version(&mctx->conf, min_ver);
18033 int major = MBEDTLS_SSL_MAJOR_VERSION_3;
18034 int minor = MBEDTLS_SSL_MINOR_VERSION_3;
18035 if (version >= Version::TLS1_3) {
18036#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
18037 minor = MBEDTLS_SSL_MINOR_VERSION_4;
18039 minor = MBEDTLS_SSL_MINOR_VERSION_3;
18042 mbedtls_ssl_conf_min_version(&mctx->conf, major, minor);
18047inline bool load_ca_pem(ctx_t ctx,
const char *pem,
size_t len) {
18048 if (!ctx || !pem) {
return false; }
18049 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18053 std::string pem_str(pem, len);
18054 int ret = mbedtls_x509_crt_parse(
18055 &mctx->ca_chain,
reinterpret_cast<const unsigned char *
>(pem_str.c_str()),
18056 pem_str.size() + 1);
18058 impl::mbedtls_last_error() = ret;
18062 mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain,
nullptr);
18066inline bool load_ca_file(ctx_t ctx,
const char *file_path) {
18067 if (!ctx || !file_path) {
return false; }
18068 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18070 int ret = mbedtls_x509_crt_parse_file(&mctx->ca_chain, file_path);
18072 impl::mbedtls_last_error() = ret;
18076 mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain,
nullptr);
18080inline bool load_ca_dir(ctx_t ctx,
const char *dir_path) {
18081 if (!ctx || !dir_path) {
return false; }
18082 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18084 int ret = mbedtls_x509_crt_parse_path(&mctx->ca_chain, dir_path);
18086 impl::mbedtls_last_error() = ret;
18090 mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain,
nullptr);
18094inline bool load_system_certs(ctx_t ctx) {
18095 if (!ctx) {
return false; }
18096 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18097 bool loaded =
false;
18100 loaded = impl::enumerate_windows_system_certs(
18101 [&](
const unsigned char *data,
size_t len) {
18102 return mbedtls_x509_crt_parse_der(&mctx->ca_chain, data, len) == 0;
18104#elif defined(__APPLE__) && defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
18105 loaded = impl::enumerate_macos_keychain_certs(
18106 [&](
const unsigned char *data,
size_t len) {
18107 return mbedtls_x509_crt_parse_der(&mctx->ca_chain, data, len) == 0;
18110 for (
auto path = impl::system_ca_paths(); *path; ++path) {
18111 if (mbedtls_x509_crt_parse_file(&mctx->ca_chain, *path) >= 0) {
18118 for (
auto dir = impl::system_ca_dirs(); *dir; ++dir) {
18119 if (mbedtls_x509_crt_parse_path(&mctx->ca_chain, *dir) >= 0) {
18128 mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain,
nullptr);
18133inline bool set_client_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
18134 const char *password) {
18135 if (!ctx || !cert || !key) {
return false; }
18136 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18139 std::string cert_str(cert);
18140 int ret = mbedtls_x509_crt_parse(
18142 reinterpret_cast<const unsigned char *
>(cert_str.c_str()),
18143 cert_str.size() + 1);
18145 impl::mbedtls_last_error() = ret;
18150 std::string key_str(key);
18151 const unsigned char *pwd =
18152 password ?
reinterpret_cast<const unsigned char *
>(password) : nullptr;
18153 size_t pwd_len = password ? strlen(password) : 0;
18155#ifdef CPPHTTPLIB_MBEDTLS_V3
18156 ret = mbedtls_pk_parse_key(
18157 &mctx->own_key,
reinterpret_cast<const unsigned char *
>(key_str.c_str()),
18158 key_str.size() + 1, pwd, pwd_len, mbedtls_ctr_drbg_random,
18161 ret = mbedtls_pk_parse_key(
18162 &mctx->own_key,
reinterpret_cast<const unsigned char *
>(key_str.c_str()),
18163 key_str.size() + 1, pwd, pwd_len);
18166 impl::mbedtls_last_error() = ret;
18171#ifdef CPPHTTPLIB_MBEDTLS_V3
18172 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key,
18173 mbedtls_ctr_drbg_random, &mctx->ctr_drbg);
18175 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key);
18178 impl::mbedtls_last_error() = ret;
18182 ret = mbedtls_ssl_conf_own_cert(&mctx->conf, &mctx->own_cert, &mctx->own_key);
18184 impl::mbedtls_last_error() = ret;
18191inline bool set_client_cert_file(ctx_t ctx,
const char *cert_path,
18192 const char *key_path,
const char *password) {
18193 if (!ctx || !cert_path || !key_path) {
return false; }
18194 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18197 int ret = mbedtls_x509_crt_parse_file(&mctx->own_cert, cert_path);
18199 impl::mbedtls_last_error() = ret;
18204#ifdef CPPHTTPLIB_MBEDTLS_V3
18205 ret = mbedtls_pk_parse_keyfile(&mctx->own_key, key_path, password,
18206 mbedtls_ctr_drbg_random, &mctx->ctr_drbg);
18208 ret = mbedtls_pk_parse_keyfile(&mctx->own_key, key_path, password);
18211 impl::mbedtls_last_error() = ret;
18216#ifdef CPPHTTPLIB_MBEDTLS_V3
18217 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key,
18218 mbedtls_ctr_drbg_random, &mctx->ctr_drbg);
18220 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key);
18223 impl::mbedtls_last_error() = ret;
18227 ret = mbedtls_ssl_conf_own_cert(&mctx->conf, &mctx->own_cert, &mctx->own_key);
18229 impl::mbedtls_last_error() = ret;
18236inline void set_verify_client(ctx_t ctx,
bool require) {
18237 if (!ctx) {
return; }
18238 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18239 mctx->verify_client = require;
18241 mbedtls_ssl_conf_authmode(&mctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
18245 mbedtls_ssl_conf_authmode(&mctx->conf, mctx->has_verify_callback
18246 ? MBEDTLS_SSL_VERIFY_OPTIONAL
18247 : MBEDTLS_SSL_VERIFY_NONE);
18251inline session_t create_session(ctx_t ctx,
socket_t sock) {
18253 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18255 auto session =
new (std::nothrow) impl::MbedTlsSession();
18256 if (!session) {
return nullptr; }
18258 session->sock = sock;
18260 int ret = mbedtls_ssl_setup(&session->ssl, &mctx->conf);
18262 impl::mbedtls_last_error() = ret;
18273 mbedtls_ssl_set_hostname(&session->ssl,
nullptr);
18276 mbedtls_ssl_set_bio(&session->ssl, &session->sock, impl::mbedtls_net_send_cb,
18277 impl::mbedtls_net_recv_cb,
nullptr);
18281 if (mctx->has_verify_callback) {
18282 mbedtls_ssl_set_verify(&session->ssl, impl::mbedtls_verify_callback,
18286 return static_cast<session_t
>(session);
18289inline void free_session(session_t session) {
18290 if (session) {
delete static_cast<impl::MbedTlsSession *
>(session); }
18293inline bool set_sni(session_t session,
const char *hostname) {
18294 if (!session || !hostname) {
return false; }
18295 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18297 int ret = mbedtls_ssl_set_hostname(&msession->ssl, hostname);
18299 impl::mbedtls_last_error() = ret;
18303 msession->hostname = hostname;
18307inline bool set_hostname(session_t session,
const char *hostname) {
18309 return set_sni(session, hostname);
18312inline TlsError connect(session_t session) {
18315 err.code = ErrorCode::Fatal;
18319 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18320 int ret = mbedtls_ssl_handshake(&msession->ssl);
18323 err.code = ErrorCode::Success;
18325 err.code = impl::map_mbedtls_error(ret, err.sys_errno);
18326 err.backend_code =
static_cast<uint64_t
>(-ret);
18327 impl::mbedtls_last_error() = ret;
18333inline TlsError accept(session_t session) {
18335 auto result = connect(session);
18338 if (result.code == ErrorCode::Success && session) {
18339 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18340 msession->sni_hostname = std::move(impl::mbedpending_sni());
18341 impl::mbedpending_sni().clear();
18347inline bool connect_nonblocking(session_t session,
socket_t sock,
18348 time_t timeout_sec, time_t timeout_usec,
18351 if (err) { err->code = ErrorCode::Fatal; }
18355 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18358 detail::set_nonblocking(sock,
true);
18360 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
18363 while ((ret = mbedtls_ssl_handshake(&msession->ssl)) != 0) {
18364 if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
18365 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
18368 }
else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
18369 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
18376 err->code = impl::map_mbedtls_error(ret, err->sys_errno);
18377 err->backend_code =
static_cast<uint64_t
>(-ret);
18379 impl::mbedtls_last_error() = ret;
18383 if (err) { err->code = ErrorCode::Success; }
18387inline bool accept_nonblocking(session_t session,
socket_t sock,
18388 time_t timeout_sec, time_t timeout_usec,
18392 connect_nonblocking(session, sock, timeout_sec, timeout_usec, err);
18395 if (result && session) {
18396 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18397 msession->sni_hostname = std::move(impl::mbedpending_sni());
18398 impl::mbedpending_sni().clear();
18404inline ssize_t read(session_t session,
void *buf,
size_t len, TlsError &err) {
18405 if (!session || !buf) {
18406 err.code = ErrorCode::Fatal;
18410 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18412 mbedtls_ssl_read(&msession->ssl,
static_cast<unsigned char *
>(buf), len);
18415 err.code = ErrorCode::Success;
18416 return static_cast<ssize_t
>(ret);
18420 err.code = ErrorCode::PeerClosed;
18424 err.code = impl::map_mbedtls_error(ret, err.sys_errno);
18425 err.backend_code =
static_cast<uint64_t
>(-ret);
18426 impl::mbedtls_last_error() = ret;
18429 if (err.code == ErrorCode::PeerClosed) {
return 0; }
18433inline ssize_t write(session_t session,
const void *buf,
size_t len,
18435 if (!session || !buf) {
18436 err.code = ErrorCode::Fatal;
18440 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18441 int ret = mbedtls_ssl_write(&msession->ssl,
18442 static_cast<const unsigned char *
>(buf), len);
18445 err.code = ErrorCode::Success;
18446 return static_cast<ssize_t
>(ret);
18450 err.code = ErrorCode::PeerClosed;
18454 err.code = impl::map_mbedtls_error(ret, err.sys_errno);
18455 err.backend_code =
static_cast<uint64_t
>(-ret);
18456 impl::mbedtls_last_error() = ret;
18460inline int pending(const_session_t session) {
18461 if (!session) {
return 0; }
18463 static_cast<impl::MbedTlsSession *
>(
const_cast<void *
>(session));
18464 return static_cast<int>(mbedtls_ssl_get_bytes_avail(&msession->ssl));
18467inline void shutdown(session_t session,
bool graceful) {
18468 if (!session) {
return; }
18469 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18475 while ((ret = mbedtls_ssl_close_notify(&msession->ssl)) != 0 &&
18477 if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
18478 ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
18486inline bool is_peer_closed(session_t session,
socket_t sock) {
18488 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18492 if (mbedtls_ssl_get_bytes_avail(&msession->ssl) > 0) {
return false; }
18495 detail::set_nonblocking(sock,
true);
18497 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
18504 int ret = mbedtls_ssl_read(&msession->ssl, &buf, 1);
18507 if (ret > 0 || ret == MBEDTLS_ERR_SSL_WANT_READ) {
return false; }
18510 return ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY ||
18511 ret == MBEDTLS_ERR_NET_CONN_RESET || ret == 0;
18514inline cert_t get_peer_cert(const_session_t session) {
18515 if (!session) {
return nullptr; }
18517 static_cast<impl::MbedTlsSession *
>(
const_cast<void *
>(session));
18522 const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&msession->ssl);
18523 return const_cast<mbedtls_x509_crt *
>(cert);
18526inline void free_cert(cert_t cert) {
18533inline bool verify_hostname(cert_t cert,
const char *hostname) {
18534 if (!cert || !hostname) {
return false; }
18535 auto mcert =
static_cast<const mbedtls_x509_crt *
>(cert);
18536 std::string host_str(hostname);
18539 unsigned char ip_bytes[16];
18540 auto ip_len = impl::parse_ip_address(host_str, ip_bytes);
18541 auto is_ip = ip_len > 0;
18547 const mbedtls_x509_sequence *san = &mcert->subject_alt_names;
18548 while (san !=
nullptr && san->buf.p !=
nullptr && san->buf.len > 0) {
18549 const unsigned char *p = san->buf.p;
18550 size_t len = san->buf.len;
18555 if (len == ip_len && memcmp(p, ip_bytes, ip_len) == 0) {
return true; }
18558 bool is_dns = len > 0;
18559 for (
size_t i = 0; i < len && is_dns; i++) {
18560 if (p[i] < 32 || p[i] > 126) { is_dns =
false; }
18563 std::string san_name(
reinterpret_cast<const char *
>(p), len);
18564 if (detail::match_hostname(san_name, host_str)) {
return true; }
18575 int ret = mbedtls_x509_dn_gets(cn,
sizeof(cn), &mcert->subject);
18577 std::string cn_str(cn);
18580 size_t cn_pos = cn_str.find(
"CN=");
18581 if (cn_pos != std::string::npos) {
18582 size_t start = cn_pos + 3;
18583 size_t end = cn_str.find(
',', start);
18584 std::string cn_value =
18585 cn_str.substr(start, end == std::string::npos ? end : end - start);
18587 if (detail::match_hostname(cn_value, host_str)) {
return true; }
18595inline uint64_t hostname_mismatch_code() {
18596 return static_cast<uint64_t
>(MBEDTLS_X509_BADCERT_CN_MISMATCH);
18599inline long get_verify_result(const_session_t session) {
18600 if (!session) {
return -1; }
18602 static_cast<impl::MbedTlsSession *
>(
const_cast<void *
>(session));
18603 uint32_t flags = mbedtls_ssl_get_verify_result(&msession->ssl);
18605 return flags == 0 ? 0 :
static_cast<long>(flags);
18608inline std::string get_cert_subject_cn(cert_t cert) {
18609 if (!cert)
return "";
18610 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18613 const mbedtls_x509_name *name = &x509->subject;
18614 while (name !=
nullptr) {
18615 if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0) {
18616 return std::string(
reinterpret_cast<const char *
>(name->val.p),
18624inline std::string get_cert_issuer_name(cert_t cert) {
18625 if (!cert)
return "";
18626 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18630 int ret = mbedtls_x509_dn_gets(buf,
sizeof(buf), &x509->issuer);
18631 if (ret < 0)
return "";
18632 return std::string(buf);
18635inline bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans) {
18637 if (!cert)
return false;
18638 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18641 const mbedtls_x509_sequence *cur = &x509->subject_alt_names;
18642 while (cur !=
nullptr) {
18643 if (cur->buf.len > 0) {
18646 const unsigned char *p = cur->buf.p;
18647 size_t len = cur->buf.len;
18650 unsigned char tag = *p;
18655 if (len > 0 && *p < 0x80) {
18656 size_t value_len = *p;
18660 if (value_len <= len) {
18663 switch (tag & 0x1F) {
18665 entry.type = SanType::DNS;
18667 std::string(
reinterpret_cast<const char *
>(p), value_len);
18670 entry.type = SanType::IP;
18671 if (value_len == 4) {
18674 snprintf(buf,
sizeof(buf),
"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
18676 }
else if (value_len == 16) {
18679 snprintf(buf,
sizeof(buf),
18680 "%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
18681 "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
18682 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8],
18683 p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
18688 entry.type = SanType::EMAIL;
18690 std::string(
reinterpret_cast<const char *
>(p), value_len);
18693 entry.type = SanType::URI;
18695 std::string(
reinterpret_cast<const char *
>(p), value_len);
18697 default: entry.type = SanType::OTHER;
break;
18700 if (!entry.value.empty()) { sans.push_back(std::move(entry)); }
18709inline bool get_cert_validity(cert_t cert, time_t ¬_before,
18710 time_t ¬_after) {
18711 if (!cert)
return false;
18712 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18715 auto to_time_t = [](
const mbedtls_x509_time &t) -> time_t {
18716 struct tm tm_time = {};
18717 tm_time.tm_year = t.year - 1900;
18718 tm_time.tm_mon = t.mon - 1;
18719 tm_time.tm_mday = t.day;
18720 tm_time.tm_hour = t.hour;
18721 tm_time.tm_min = t.min;
18722 tm_time.tm_sec = t.sec;
18724 return _mkgmtime(&tm_time);
18726 return timegm(&tm_time);
18730 not_before = to_time_t(x509->valid_from);
18731 not_after = to_time_t(x509->valid_to);
18735inline std::string get_cert_serial(cert_t cert) {
18736 if (!cert)
return "";
18737 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18740 std::string result;
18741 result.reserve(x509->serial.len * 2);
18742 for (
size_t i = 0; i < x509->serial.len; i++) {
18744 snprintf(hex,
sizeof(hex),
"%02X", x509->serial.p[i]);
18750inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
18751 if (!cert)
return false;
18752 auto crt =
static_cast<mbedtls_x509_crt *
>(cert);
18753 if (!crt->raw.p || crt->raw.len == 0)
return false;
18754 der.assign(crt->raw.p, crt->raw.p + crt->raw.len);
18758inline const char *get_sni(const_session_t session) {
18759 if (!session)
return nullptr;
18760 auto msession =
static_cast<const impl::MbedTlsSession *
>(session);
18763 if (!msession->sni_hostname.empty()) {
18764 return msession->sni_hostname.c_str();
18768 if (!msession->hostname.empty()) {
return msession->hostname.c_str(); }
18773inline uint64_t peek_error() {
18775 return static_cast<uint64_t
>(-impl::mbedtls_last_error());
18778inline uint64_t get_error() {
18780 uint64_t err =
static_cast<uint64_t
>(-impl::mbedtls_last_error());
18781 impl::mbedtls_last_error() = 0;
18785inline std::string error_string(uint64_t code) {
18787 mbedtls_strerror(-
static_cast<int>(code), buf,
sizeof(buf));
18788 return std::string(buf);
18791inline ca_store_t create_ca_store(
const char *pem,
size_t len) {
18792 auto *ca_chain =
new (std::nothrow) mbedtls_x509_crt;
18793 if (!ca_chain) {
return nullptr; }
18795 mbedtls_x509_crt_init(ca_chain);
18798 int ret = mbedtls_x509_crt_parse(ca_chain,
18799 reinterpret_cast<const unsigned char *
>(pem),
18803 ret = mbedtls_x509_crt_parse(
18804 ca_chain,
reinterpret_cast<const unsigned char *
>(pem), len);
18806 mbedtls_x509_crt_free(ca_chain);
18812 return static_cast<ca_store_t
>(ca_chain);
18815inline void free_ca_store(ca_store_t store) {
18817 auto *ca_chain =
static_cast<mbedtls_x509_crt *
>(store);
18818 mbedtls_x509_crt_free(ca_chain);
18823inline bool set_ca_store(ctx_t ctx, ca_store_t store) {
18824 if (!ctx || !store) {
return false; }
18825 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18826 auto *ca_chain =
static_cast<mbedtls_x509_crt *
>(store);
18829 mbedtls_x509_crt_free(&mbed_ctx->ca_chain);
18830 mbedtls_x509_crt_init(&mbed_ctx->ca_chain);
18834 mbedtls_x509_crt *src = ca_chain;
18835 while (src !=
nullptr) {
18836 int ret = mbedtls_x509_crt_parse_der(&mbed_ctx->ca_chain, src->raw.p,
18839 free_ca_store(store);
18847 free_ca_store(store);
18850 mbedtls_ssl_conf_ca_chain(&mbed_ctx->conf, &mbed_ctx->ca_chain,
nullptr);
18854inline size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs) {
18856 if (!ctx) {
return 0; }
18857 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18860 mbedtls_x509_crt *cert = &mbed_ctx->ca_chain;
18861 while (cert !=
nullptr && cert->raw.len > 0) {
18863 auto *copy =
new mbedtls_x509_crt;
18864 mbedtls_x509_crt_init(copy);
18865 int ret = mbedtls_x509_crt_parse_der(copy, cert->raw.p, cert->raw.len);
18867 certs.push_back(
static_cast<cert_t
>(copy));
18869 mbedtls_x509_crt_free(copy);
18874 return certs.size();
18877inline std::vector<std::string> get_ca_names(ctx_t ctx) {
18878 std::vector<std::string> names;
18879 if (!ctx) {
return names; }
18880 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18883 mbedtls_x509_crt *cert = &mbed_ctx->ca_chain;
18884 while (cert !=
nullptr && cert->raw.len > 0) {
18886 int ret = mbedtls_x509_dn_gets(buf,
sizeof(buf), &cert->subject);
18887 if (ret > 0) { names.push_back(buf); }
18893inline bool update_server_cert(ctx_t ctx,
const char *cert_pem,
18894 const char *key_pem,
const char *password) {
18895 if (!ctx || !cert_pem || !key_pem) {
return false; }
18896 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18899 mbedtls_x509_crt_free(&mbed_ctx->own_cert);
18900 mbedtls_pk_free(&mbed_ctx->own_key);
18901 mbedtls_x509_crt_init(&mbed_ctx->own_cert);
18902 mbedtls_pk_init(&mbed_ctx->own_key);
18905 int ret = mbedtls_x509_crt_parse(
18906 &mbed_ctx->own_cert,
reinterpret_cast<const unsigned char *
>(cert_pem),
18907 strlen(cert_pem) + 1);
18909 impl::mbedtls_last_error() = ret;
18914#ifdef CPPHTTPLIB_MBEDTLS_V3
18915 ret = mbedtls_pk_parse_key(
18916 &mbed_ctx->own_key,
reinterpret_cast<const unsigned char *
>(key_pem),
18917 strlen(key_pem) + 1,
18918 password ?
reinterpret_cast<const unsigned char *
>(password) :
nullptr,
18919 password ? strlen(password) : 0, mbedtls_ctr_drbg_random,
18920 &mbed_ctx->ctr_drbg);
18922 ret = mbedtls_pk_parse_key(
18923 &mbed_ctx->own_key,
reinterpret_cast<const unsigned char *
>(key_pem),
18924 strlen(key_pem) + 1,
18925 password ?
reinterpret_cast<const unsigned char *
>(password) :
nullptr,
18926 password ? strlen(password) : 0);
18929 impl::mbedtls_last_error() = ret;
18934 ret = mbedtls_ssl_conf_own_cert(&mbed_ctx->conf, &mbed_ctx->own_cert,
18935 &mbed_ctx->own_key);
18937 impl::mbedtls_last_error() = ret;
18944inline bool update_server_client_ca(ctx_t ctx,
const char *ca_pem) {
18945 if (!ctx || !ca_pem) {
return false; }
18946 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18949 mbedtls_x509_crt_free(&mbed_ctx->ca_chain);
18950 mbedtls_x509_crt_init(&mbed_ctx->ca_chain);
18953 int ret = mbedtls_x509_crt_parse(
18954 &mbed_ctx->ca_chain,
reinterpret_cast<const unsigned char *
>(ca_pem),
18955 strlen(ca_pem) + 1);
18957 impl::mbedtls_last_error() = ret;
18962 mbedtls_ssl_conf_ca_chain(&mbed_ctx->conf, &mbed_ctx->ca_chain,
nullptr);
18966inline bool set_verify_callback(ctx_t ctx, VerifyCallback callback) {
18967 if (!ctx) {
return false; }
18968 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18970 impl::get_verify_callback() = std::move(callback);
18971 mbed_ctx->has_verify_callback =
18972 static_cast<bool>(impl::get_verify_callback());
18974 if (mbed_ctx->has_verify_callback) {
18977 mbedtls_ssl_conf_authmode(&mbed_ctx->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
18978 mbedtls_ssl_conf_verify(&mbed_ctx->conf, impl::mbedtls_verify_callback,
18981 mbedtls_ssl_conf_verify(&mbed_ctx->conf,
nullptr,
nullptr);
18986inline long get_verify_error(const_session_t session) {
18987 if (!session) {
return -1; }
18989 static_cast<impl::MbedTlsSession *
>(
const_cast<void *
>(session));
18990 return static_cast<long>(mbedtls_ssl_get_verify_result(&msession->ssl));
18993inline std::string verify_error_string(
long error_code) {
18994 if (error_code == 0) {
return ""; }
18996 mbedtls_x509_crt_verify_info(buf,
sizeof(buf),
"",
18997 static_cast<uint32_t
>(error_code));
18999 std::string result(buf);
19000 while (!result.empty() && (result.back() ==
'\n' || result.back() ==
' ')) {
19018#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
19024struct WolfSSLSession {
19025 WOLFSSL *ssl =
nullptr;
19027 std::string hostname;
19028 std::string sni_hostname;
19030 WolfSSLSession() =
default;
19032 ~WolfSSLSession() {
19033 if (ssl) { wolfSSL_free(ssl); }
19036 WolfSSLSession(
const WolfSSLSession &) =
delete;
19037 WolfSSLSession &operator=(
const WolfSSLSession &) =
delete;
19041inline uint64_t &wolfssl_last_error() {
19042 static thread_local uint64_t err = 0;
19049inline ErrorCode map_wolfssl_error(WOLFSSL *ssl,
int ssl_error,
19051 switch (ssl_error) {
19052 case SSL_ERROR_NONE:
return ErrorCode::Success;
19053 case SSL_ERROR_WANT_READ:
return ErrorCode::WantRead;
19054 case SSL_ERROR_WANT_WRITE:
return ErrorCode::WantWrite;
19055 case SSL_ERROR_ZERO_RETURN:
return ErrorCode::PeerClosed;
19056 case SSL_ERROR_SYSCALL: out_errno = errno;
return ErrorCode::SyscallError;
19061 int low_err = ssl_error;
19062 if (low_err == DOMAIN_NAME_MISMATCH) {
19063 return ErrorCode::HostnameMismatch;
19067 long vr = wolfSSL_get_verify_result(ssl);
19068 if (vr != 0) {
return ErrorCode::CertVerifyFailed; }
19070 return ErrorCode::Fatal;
19075inline WolfSSLContext::WolfSSLContext() { wolfSSL_Init(); }
19077inline WolfSSLContext::~WolfSSLContext() {
19078 if (ctx) { wolfSSL_CTX_free(ctx); }
19082inline std::string &wolfssl_pending_sni() {
19083 static thread_local std::string sni;
19088inline int wolfssl_sni_callback(WOLFSSL *ssl,
int *ret,
void *exArg) {
19092 void *name_data =
nullptr;
19093 unsigned short name_len =
19094 wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, &name_data);
19096 if (name_data && name_len > 0) {
19097 wolfssl_pending_sni().assign(
static_cast<const char *
>(name_data),
19100 wolfssl_pending_sni().clear();
19106inline int wolfssl_verify_callback(
int preverify_ok,
19107 WOLFSSL_X509_STORE_CTX *x509_ctx) {
19108 auto &callback = get_verify_callback();
19109 if (!callback) {
return preverify_ok; }
19111 WOLFSSL_X509 *cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
19112 int depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx);
19113 int err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx);
19116 WOLFSSL *ssl =
static_cast<WOLFSSL *
>(wolfSSL_X509_STORE_CTX_get_ex_data(
19117 x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx()));
19119 VerifyContext verify_ctx;
19120 verify_ctx.session =
static_cast<session_t
>(ssl);
19121 verify_ctx.cert =
static_cast<cert_t
>(cert);
19122 verify_ctx.depth = depth;
19123 verify_ctx.preverify_ok = (preverify_ok != 0);
19124 verify_ctx.error_code =
static_cast<long>(err);
19127 verify_ctx.error_string = wolfSSL_X509_verify_cert_error_string(err);
19129 verify_ctx.error_string =
nullptr;
19132 bool accepted = callback(verify_ctx);
19133 return accepted ? 1 : 0;
19136inline void set_wolfssl_password_cb(WOLFSSL_CTX *ctx,
const char *password) {
19137 wolfSSL_CTX_set_default_passwd_cb_userdata(ctx,
const_cast<char *
>(password));
19138 wolfSSL_CTX_set_default_passwd_cb(
19139 ctx, [](
char *buf,
int size,
int ,
void *userdata) ->
int {
19140 auto *pwd =
static_cast<const char *
>(userdata);
19141 if (!pwd)
return 0;
19142 auto len =
static_cast<int>(strlen(pwd));
19143 if (len > size) len = size;
19144 memcpy(buf, pwd,
static_cast<size_t>(len));
19151inline ctx_t create_client_context() {
19152 auto ctx =
new (std::nothrow) impl::WolfSSLContext();
19153 if (!ctx) {
return nullptr; }
19155 ctx->is_server =
false;
19157 WOLFSSL_METHOD *method = wolfTLSv1_2_client_method();
19163 ctx->ctx = wolfSSL_CTX_new(method);
19170 wolfSSL_CTX_set_verify(ctx->ctx, SSL_VERIFY_PEER,
nullptr);
19172 return static_cast<ctx_t
>(ctx);
19175inline ctx_t create_server_context() {
19176 auto ctx =
new (std::nothrow) impl::WolfSSLContext();
19177 if (!ctx) {
return nullptr; }
19179 ctx->is_server =
true;
19181 WOLFSSL_METHOD *method = wolfTLSv1_2_server_method();
19187 ctx->ctx = wolfSSL_CTX_new(method);
19194 wolfSSL_CTX_set_verify(ctx->ctx, SSL_VERIFY_NONE,
nullptr);
19197 wolfSSL_CTX_SNI_SetOptions(ctx->ctx, WOLFSSL_SNI_HOST_NAME,
19198 WOLFSSL_SNI_CONTINUE_ON_MISMATCH);
19199 wolfSSL_CTX_set_servername_callback(ctx->ctx, impl::wolfssl_sni_callback);
19201 return static_cast<ctx_t
>(ctx);
19204inline void free_context(ctx_t ctx) {
19205 if (ctx) {
delete static_cast<impl::WolfSSLContext *
>(ctx); }
19208inline bool set_min_version(ctx_t ctx, Version version) {
19209 if (!ctx) {
return false; }
19210 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19212 int min_ver = WOLFSSL_TLSV1_2;
19213 if (version >= Version::TLS1_3) { min_ver = WOLFSSL_TLSV1_3; }
19215 return wolfSSL_CTX_SetMinVersion(wctx->ctx, min_ver) == WOLFSSL_SUCCESS;
19218inline bool load_ca_pem(ctx_t ctx,
const char *pem,
size_t len) {
19219 if (!ctx || !pem) {
return false; }
19220 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19222 int ret = wolfSSL_CTX_load_verify_buffer(
19223 wctx->ctx,
reinterpret_cast<const unsigned char *
>(pem),
19224 static_cast<long>(len), SSL_FILETYPE_PEM);
19225 if (ret != SSL_SUCCESS) {
19226 impl::wolfssl_last_error() =
19227 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19230 wctx->ca_pem_data_.append(pem, len);
19234inline bool load_ca_file(ctx_t ctx,
const char *file_path) {
19235 if (!ctx || !file_path) {
return false; }
19236 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19238 int ret = wolfSSL_CTX_load_verify_locations(wctx->ctx, file_path,
nullptr);
19239 if (ret != SSL_SUCCESS) {
19240 impl::wolfssl_last_error() =
19241 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19247inline bool load_ca_dir(ctx_t ctx,
const char *dir_path) {
19248 if (!ctx || !dir_path) {
return false; }
19249 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19251 int ret = wolfSSL_CTX_load_verify_locations(wctx->ctx,
nullptr, dir_path);
19260inline bool load_system_certs(ctx_t ctx) {
19261 if (!ctx) {
return false; }
19262 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19263 bool loaded =
false;
19266 loaded = impl::enumerate_windows_system_certs(
19267 [&](
const unsigned char *data,
size_t len) {
19268 return wolfSSL_CTX_load_verify_buffer(wctx->ctx, data,
19269 static_cast<long>(len),
19270 SSL_FILETYPE_ASN1) == SSL_SUCCESS;
19272#elif defined(__APPLE__) && defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
19273 loaded = impl::enumerate_macos_keychain_certs(
19274 [&](
const unsigned char *data,
size_t len) {
19275 return wolfSSL_CTX_load_verify_buffer(wctx->ctx, data,
19276 static_cast<long>(len),
19277 SSL_FILETYPE_ASN1) == SSL_SUCCESS;
19280 for (
auto path = impl::system_ca_paths(); *path; ++path) {
19281 if (wolfSSL_CTX_load_verify_locations(wctx->ctx, *path,
nullptr) ==
19289 for (
auto dir = impl::system_ca_dirs(); *dir; ++dir) {
19290 if (wolfSSL_CTX_load_verify_locations(wctx->ctx,
nullptr, *dir) ==
19302inline bool set_client_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
19303 const char *password) {
19304 if (!ctx || !cert || !key) {
return false; }
19305 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19308 int ret = wolfSSL_CTX_use_certificate_buffer(
19309 wctx->ctx,
reinterpret_cast<const unsigned char *
>(cert),
19310 static_cast<long>(strlen(cert)), SSL_FILETYPE_PEM);
19311 if (ret != SSL_SUCCESS) {
19312 impl::wolfssl_last_error() =
19313 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19318 if (password) { impl::set_wolfssl_password_cb(wctx->ctx, password); }
19321 ret = wolfSSL_CTX_use_PrivateKey_buffer(
19322 wctx->ctx,
reinterpret_cast<const unsigned char *
>(key),
19323 static_cast<long>(strlen(key)), SSL_FILETYPE_PEM);
19324 if (ret != SSL_SUCCESS) {
19325 impl::wolfssl_last_error() =
19326 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19331 return wolfSSL_CTX_check_private_key(wctx->ctx) == SSL_SUCCESS;
19334inline bool set_client_cert_file(ctx_t ctx,
const char *cert_path,
19335 const char *key_path,
const char *password) {
19336 if (!ctx || !cert_path || !key_path) {
return false; }
19337 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19341 wolfSSL_CTX_use_certificate_file(wctx->ctx, cert_path, SSL_FILETYPE_PEM);
19342 if (ret != SSL_SUCCESS) {
19343 impl::wolfssl_last_error() =
19344 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19349 if (password) { impl::set_wolfssl_password_cb(wctx->ctx, password); }
19352 ret = wolfSSL_CTX_use_PrivateKey_file(wctx->ctx, key_path, SSL_FILETYPE_PEM);
19353 if (ret != SSL_SUCCESS) {
19354 impl::wolfssl_last_error() =
19355 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19360 return wolfSSL_CTX_check_private_key(wctx->ctx) == SSL_SUCCESS;
19363inline void set_verify_client(ctx_t ctx,
bool require) {
19364 if (!ctx) {
return; }
19365 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19366 wctx->verify_client = require;
19368 wolfSSL_CTX_set_verify(
19369 wctx->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
19370 wctx->has_verify_callback ? impl::wolfssl_verify_callback :
nullptr);
19372 if (wctx->has_verify_callback) {
19373 wolfSSL_CTX_set_verify(wctx->ctx, SSL_VERIFY_PEER,
19374 impl::wolfssl_verify_callback);
19376 wolfSSL_CTX_set_verify(wctx->ctx, SSL_VERIFY_NONE,
nullptr);
19381inline session_t create_session(ctx_t ctx,
socket_t sock) {
19383 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19385 auto session =
new (std::nothrow) impl::WolfSSLSession();
19386 if (!session) {
return nullptr; }
19388 session->sock = sock;
19389 session->ssl = wolfSSL_new(wctx->ctx);
19390 if (!session->ssl) {
19391 impl::wolfssl_last_error() =
19392 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19397 wolfSSL_set_fd(session->ssl,
static_cast<int>(sock));
19399 return static_cast<session_t
>(session);
19402inline void free_session(session_t session) {
19403 if (session) {
delete static_cast<impl::WolfSSLSession *
>(session); }
19406inline bool set_sni(session_t session,
const char *hostname) {
19407 if (!session || !hostname) {
return false; }
19408 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19410 int ret = wolfSSL_UseSNI(wsession->ssl, WOLFSSL_SNI_HOST_NAME, hostname,
19411 static_cast<word16
>(strlen(hostname)));
19412 if (ret != WOLFSSL_SUCCESS) {
19413 impl::wolfssl_last_error() =
19414 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19419 wolfSSL_check_domain_name(wsession->ssl, hostname);
19421 wsession->hostname = hostname;
19425inline bool set_hostname(session_t session,
const char *hostname) {
19427 return set_sni(session, hostname);
19430inline TlsError connect(session_t session) {
19433 err.code = ErrorCode::Fatal;
19437 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19438 int ret = wolfSSL_connect(wsession->ssl);
19440 if (ret == SSL_SUCCESS) {
19441 err.code = ErrorCode::Success;
19443 int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19444 err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19445 err.backend_code =
static_cast<uint64_t
>(ssl_error);
19446 impl::wolfssl_last_error() = err.backend_code;
19452inline TlsError accept(session_t session) {
19455 err.code = ErrorCode::Fatal;
19459 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19460 int ret = wolfSSL_accept(wsession->ssl);
19462 if (ret == SSL_SUCCESS) {
19463 err.code = ErrorCode::Success;
19465 wsession->sni_hostname = std::move(impl::wolfssl_pending_sni());
19466 impl::wolfssl_pending_sni().clear();
19468 int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19469 err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19470 err.backend_code =
static_cast<uint64_t
>(ssl_error);
19471 impl::wolfssl_last_error() = err.backend_code;
19477inline bool connect_nonblocking(session_t session,
socket_t sock,
19478 time_t timeout_sec, time_t timeout_usec,
19481 if (err) { err->code = ErrorCode::Fatal; }
19485 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19488 detail::set_nonblocking(sock,
true);
19490 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
19493 while ((ret = wolfSSL_connect(wsession->ssl)) != SSL_SUCCESS) {
19494 int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19495 if (ssl_error == SSL_ERROR_WANT_READ) {
19496 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
19499 }
else if (ssl_error == SSL_ERROR_WANT_WRITE) {
19500 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
19508 impl::map_wolfssl_error(wsession->ssl, ssl_error, err->sys_errno);
19509 err->backend_code =
static_cast<uint64_t
>(ssl_error);
19511 impl::wolfssl_last_error() =
static_cast<uint64_t
>(ssl_error);
19515 if (err) { err->code = ErrorCode::Success; }
19519inline bool accept_nonblocking(session_t session,
socket_t sock,
19520 time_t timeout_sec, time_t timeout_usec,
19523 if (err) { err->code = ErrorCode::Fatal; }
19527 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19530 detail::set_nonblocking(sock,
true);
19532 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
19535 while ((ret = wolfSSL_accept(wsession->ssl)) != SSL_SUCCESS) {
19536 int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19537 if (ssl_error == SSL_ERROR_WANT_READ) {
19538 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
19541 }
else if (ssl_error == SSL_ERROR_WANT_WRITE) {
19542 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
19550 impl::map_wolfssl_error(wsession->ssl, ssl_error, err->sys_errno);
19551 err->backend_code =
static_cast<uint64_t
>(ssl_error);
19553 impl::wolfssl_last_error() =
static_cast<uint64_t
>(ssl_error);
19557 if (err) { err->code = ErrorCode::Success; }
19560 wsession->sni_hostname = std::move(impl::wolfssl_pending_sni());
19561 impl::wolfssl_pending_sni().clear();
19566inline ssize_t read(session_t session,
void *buf,
size_t len, TlsError &err) {
19567 if (!session || !buf) {
19568 err.code = ErrorCode::Fatal;
19572 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19573 int ret = wolfSSL_read(wsession->ssl, buf,
static_cast<int>(len));
19576 err.code = ErrorCode::Success;
19577 return static_cast<ssize_t
>(ret);
19581 err.code = ErrorCode::PeerClosed;
19585 int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19586 err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19587 err.backend_code =
static_cast<uint64_t
>(ssl_error);
19588 impl::wolfssl_last_error() = err.backend_code;
19592inline ssize_t write(session_t session,
const void *buf,
size_t len,
19594 if (!session || !buf) {
19595 err.code = ErrorCode::Fatal;
19599 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19600 int ret = wolfSSL_write(wsession->ssl, buf,
static_cast<int>(len));
19603 err.code = ErrorCode::Success;
19604 return static_cast<ssize_t
>(ret);
19611 err.code = ErrorCode::PeerClosed;
19615 int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19616 err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19617 err.backend_code =
static_cast<uint64_t
>(ssl_error);
19618 impl::wolfssl_last_error() = err.backend_code;
19622inline int pending(const_session_t session) {
19623 if (!session) {
return 0; }
19625 static_cast<impl::WolfSSLSession *
>(
const_cast<void *
>(session));
19626 return wolfSSL_pending(wsession->ssl);
19629inline void shutdown(session_t session,
bool graceful) {
19630 if (!session) {
return; }
19631 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19636 while ((ret = wolfSSL_shutdown(wsession->ssl)) != SSL_SUCCESS &&
19638 int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19639 if (ssl_error != SSL_ERROR_WANT_READ &&
19640 ssl_error != SSL_ERROR_WANT_WRITE) {
19646 wolfSSL_shutdown(wsession->ssl);
19650inline bool is_peer_closed(session_t session,
socket_t sock) {
19652 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19655 if (wolfSSL_pending(wsession->ssl) > 0) {
return false; }
19658 detail::set_nonblocking(sock,
true);
19660 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
19664 int ret = wolfSSL_peek(wsession->ssl, &buf, 1);
19667 if (ret > 0) {
return false; }
19669 int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19670 if (ssl_error == SSL_ERROR_WANT_READ) {
return false; }
19672 return ssl_error == SSL_ERROR_ZERO_RETURN || ssl_error == SSL_ERROR_SYSCALL ||
19676inline cert_t get_peer_cert(const_session_t session) {
19677 if (!session) {
return nullptr; }
19679 static_cast<impl::WolfSSLSession *
>(
const_cast<void *
>(session));
19681 WOLFSSL_X509 *cert = wolfSSL_get_peer_certificate(wsession->ssl);
19682 return static_cast<cert_t
>(cert);
19685inline void free_cert(cert_t cert) {
19686 if (cert) { wolfSSL_X509_free(
static_cast<WOLFSSL_X509 *
>(cert)); }
19689inline bool verify_hostname(cert_t cert,
const char *hostname) {
19690 if (!cert || !hostname) {
return false; }
19691 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19692 std::string host_str(hostname);
19695 unsigned char ip_bytes[16];
19696 auto ip_len = impl::parse_ip_address(host_str, ip_bytes);
19697 auto is_ip = ip_len > 0;
19700 auto *san_names =
static_cast<WOLF_STACK_OF(WOLFSSL_GENERAL_NAME) *
>(
19701 wolfSSL_X509_get_ext_d2i(x509, NID_subject_alt_name,
nullptr,
nullptr));
19704 int san_count = wolfSSL_sk_num(san_names);
19705 for (
int i = 0; i < san_count; i++) {
19707 static_cast<WOLFSSL_GENERAL_NAME *
>(wolfSSL_sk_value(san_names, i));
19708 if (!names)
continue;
19710 if (!is_ip && names->type == WOLFSSL_GEN_DNS) {
19712 unsigned char *dns_name =
nullptr;
19713 int dns_len = wolfSSL_ASN1_STRING_to_UTF8(&dns_name, names->d.dNSName);
19714 if (dns_name && dns_len > 0) {
19715 std::string san_name(
reinterpret_cast<char *
>(dns_name),
19716 static_cast<size_t>(dns_len));
19717 XFREE(dns_name,
nullptr, DYNAMIC_TYPE_OPENSSL);
19718 if (detail::match_hostname(san_name, host_str)) {
19719 wolfSSL_sk_free(san_names);
19723 }
else if (is_ip && names->type == WOLFSSL_GEN_IPADD) {
19726 unsigned char *ip_data = wolfSSL_ASN1_STRING_data(names->d.iPAddress);
19727 auto san_ip_len = wolfSSL_ASN1_STRING_length(names->d.iPAddress);
19728 if (ip_data && san_ip_len ==
static_cast<int>(ip_len) &&
19729 memcmp(ip_data, ip_bytes, ip_len) == 0) {
19730 wolfSSL_sk_free(san_names);
19735 wolfSSL_sk_free(san_names);
19741 auto subject = is_ip ? nullptr : wolfSSL_X509_get_subject_name(x509);
19744 int cn_len = wolfSSL_X509_NAME_get_text_by_NID(subject, NID_commonName, cn,
19747 std::string cn_str(cn,
static_cast<size_t>(cn_len));
19748 if (detail::match_hostname(cn_str, host_str)) {
return true; }
19755inline uint64_t hostname_mismatch_code() {
19756 return static_cast<uint64_t
>(DOMAIN_NAME_MISMATCH);
19759inline long get_verify_result(const_session_t session) {
19760 if (!session) {
return -1; }
19762 static_cast<impl::WolfSSLSession *
>(
const_cast<void *
>(session));
19763 long result = wolfSSL_get_verify_result(wsession->ssl);
19767inline std::string get_cert_subject_cn(cert_t cert) {
19768 if (!cert)
return "";
19769 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19771 WOLFSSL_X509_NAME *subject = wolfSSL_X509_get_subject_name(x509);
19772 if (!subject)
return "";
19775 int cn_len = wolfSSL_X509_NAME_get_text_by_NID(subject, NID_commonName, cn,
19777 if (cn_len <= 0)
return "";
19778 return std::string(cn,
static_cast<size_t>(cn_len));
19781inline std::string get_cert_issuer_name(cert_t cert) {
19782 if (!cert)
return "";
19783 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19785 WOLFSSL_X509_NAME *issuer = wolfSSL_X509_get_issuer_name(x509);
19786 if (!issuer)
return "";
19788 char *name_str = wolfSSL_X509_NAME_oneline(issuer,
nullptr, 0);
19789 if (!name_str)
return "";
19791 std::string result(name_str);
19792 XFREE(name_str,
nullptr, DYNAMIC_TYPE_OPENSSL);
19796inline bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans) {
19798 if (!cert)
return false;
19799 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19801 auto *san_names =
static_cast<WOLF_STACK_OF(WOLFSSL_GENERAL_NAME) *
>(
19802 wolfSSL_X509_get_ext_d2i(x509, NID_subject_alt_name,
nullptr,
nullptr));
19803 if (!san_names)
return true;
19805 int count = wolfSSL_sk_num(san_names);
19806 for (
int i = 0; i < count; i++) {
19808 static_cast<WOLFSSL_GENERAL_NAME *
>(wolfSSL_sk_value(san_names, i));
19809 if (!name)
continue;
19812 switch (name->type) {
19813 case WOLFSSL_GEN_DNS: {
19814 entry.type = SanType::DNS;
19815 unsigned char *dns_name =
nullptr;
19816 int dns_len = wolfSSL_ASN1_STRING_to_UTF8(&dns_name, name->d.dNSName);
19817 if (dns_name && dns_len > 0) {
19818 entry.value = std::string(
reinterpret_cast<char *
>(dns_name),
19819 static_cast<size_t>(dns_len));
19820 XFREE(dns_name,
nullptr, DYNAMIC_TYPE_OPENSSL);
19824 case WOLFSSL_GEN_IPADD: {
19825 entry.type = SanType::IP;
19826 unsigned char *ip_data = wolfSSL_ASN1_STRING_data(name->d.iPAddress);
19827 int ip_len = wolfSSL_ASN1_STRING_length(name->d.iPAddress);
19828 if (ip_data && ip_len == 4) {
19830 snprintf(buf,
sizeof(buf),
"%d.%d.%d.%d", ip_data[0], ip_data[1],
19831 ip_data[2], ip_data[3]);
19833 }
else if (ip_data && ip_len == 16) {
19835 snprintf(buf,
sizeof(buf),
19836 "%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
19837 "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
19838 ip_data[0], ip_data[1], ip_data[2], ip_data[3], ip_data[4],
19839 ip_data[5], ip_data[6], ip_data[7], ip_data[8], ip_data[9],
19840 ip_data[10], ip_data[11], ip_data[12], ip_data[13],
19841 ip_data[14], ip_data[15]);
19846 case WOLFSSL_GEN_EMAIL:
19847 entry.type = SanType::EMAIL;
19849 unsigned char *email =
nullptr;
19850 int email_len = wolfSSL_ASN1_STRING_to_UTF8(&email, name->d.rfc822Name);
19851 if (email && email_len > 0) {
19852 entry.value = std::string(
reinterpret_cast<char *
>(email),
19853 static_cast<size_t>(email_len));
19854 XFREE(email,
nullptr, DYNAMIC_TYPE_OPENSSL);
19858 case WOLFSSL_GEN_URI:
19859 entry.type = SanType::URI;
19861 unsigned char *uri =
nullptr;
19862 int uri_len = wolfSSL_ASN1_STRING_to_UTF8(
19863 &uri, name->d.uniformResourceIdentifier);
19864 if (uri && uri_len > 0) {
19865 entry.value = std::string(
reinterpret_cast<char *
>(uri),
19866 static_cast<size_t>(uri_len));
19867 XFREE(uri,
nullptr, DYNAMIC_TYPE_OPENSSL);
19871 default: entry.type = SanType::OTHER;
break;
19874 if (!entry.value.empty()) { sans.push_back(std::move(entry)); }
19876 wolfSSL_sk_free(san_names);
19880inline bool get_cert_validity(cert_t cert, time_t ¬_before,
19881 time_t ¬_after) {
19882 if (!cert)
return false;
19883 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19885 const WOLFSSL_ASN1_TIME *nb = wolfSSL_X509_get_notBefore(x509);
19886 const WOLFSSL_ASN1_TIME *na = wolfSSL_X509_get_notAfter(x509);
19888 if (!nb || !na)
return false;
19891 struct tm tm_nb = {}, tm_na = {};
19892 if (wolfSSL_ASN1_TIME_to_tm(nb, &tm_nb) != WOLFSSL_SUCCESS)
return false;
19893 if (wolfSSL_ASN1_TIME_to_tm(na, &tm_na) != WOLFSSL_SUCCESS)
return false;
19896 not_before = _mkgmtime(&tm_nb);
19897 not_after = _mkgmtime(&tm_na);
19899 not_before = timegm(&tm_nb);
19900 not_after = timegm(&tm_na);
19905inline std::string get_cert_serial(cert_t cert) {
19906 if (!cert)
return "";
19907 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19909 WOLFSSL_ASN1_INTEGER *serial_asn1 = wolfSSL_X509_get_serialNumber(x509);
19910 if (!serial_asn1)
return "";
19913 int len = serial_asn1->length;
19914 unsigned char *data = serial_asn1->data;
19915 if (!data || len <= 0)
return "";
19917 std::string result;
19918 result.reserve(
static_cast<size_t>(len) * 2);
19919 for (
int i = 0; i < len; i++) {
19921 snprintf(hex,
sizeof(hex),
"%02X", data[i]);
19927inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
19928 if (!cert)
return false;
19929 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19932 const unsigned char *der_data = wolfSSL_X509_get_der(x509, &der_len);
19933 if (!der_data || der_len <= 0)
return false;
19935 der.assign(der_data, der_data + der_len);
19939inline const char *get_sni(const_session_t session) {
19940 if (!session)
return nullptr;
19941 auto wsession =
static_cast<const impl::WolfSSLSession *
>(session);
19944 if (!wsession->sni_hostname.empty()) {
19945 return wsession->sni_hostname.c_str();
19949 if (!wsession->hostname.empty()) {
return wsession->hostname.c_str(); }
19954inline uint64_t peek_error() {
19955 return static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19958inline uint64_t get_error() {
19959 uint64_t err = impl::wolfssl_last_error();
19960 impl::wolfssl_last_error() = 0;
19964inline std::string error_string(uint64_t code) {
19966 wolfSSL_ERR_error_string(
static_cast<unsigned long>(code), buf);
19967 return std::string(buf);
19970inline ca_store_t create_ca_store(
const char *pem,
size_t len) {
19971 if (!pem || len == 0) {
return nullptr; }
19973 WOLFSSL_CTX *tmp_ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method());
19974 if (!tmp_ctx) {
return nullptr; }
19975 int ret = wolfSSL_CTX_load_verify_buffer(
19976 tmp_ctx,
reinterpret_cast<const unsigned char *
>(pem),
19977 static_cast<long>(len), SSL_FILETYPE_PEM);
19978 wolfSSL_CTX_free(tmp_ctx);
19979 if (ret != SSL_SUCCESS) {
return nullptr; }
19980 return static_cast<ca_store_t
>(
19981 new impl::WolfSSLCAStore{std::string(pem, len)});
19984inline void free_ca_store(ca_store_t store) {
19985 delete static_cast<impl::WolfSSLCAStore *
>(store);
19988inline bool set_ca_store(ctx_t ctx, ca_store_t store) {
19989 if (!ctx || !store) {
return false; }
19990 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19991 auto *ca =
static_cast<impl::WolfSSLCAStore *
>(store);
19992 int ret = wolfSSL_CTX_load_verify_buffer(
19993 wctx->ctx,
reinterpret_cast<const unsigned char *
>(ca->pem_data.data()),
19994 static_cast<long>(ca->pem_data.size()), SSL_FILETYPE_PEM);
19995 if (ret == SSL_SUCCESS) { wctx->ca_pem_data_ += ca->pem_data; }
19998 free_ca_store(store);
19999 return ret == SSL_SUCCESS;
20002inline size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs) {
20004 if (!ctx) {
return 0; }
20005 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
20006 if (wctx->ca_pem_data_.empty()) {
return 0; }
20008 const std::string &pem = wctx->ca_pem_data_;
20009 const std::string begin_marker =
"-----BEGIN CERTIFICATE-----";
20010 const std::string end_marker =
"-----END CERTIFICATE-----";
20012 while ((pos = pem.find(begin_marker, pos)) != std::string::npos) {
20013 size_t end_pos = pem.find(end_marker, pos);
20014 if (end_pos == std::string::npos) {
break; }
20015 end_pos += end_marker.size();
20016 std::string cert_pem = pem.substr(pos, end_pos - pos);
20017 WOLFSSL_X509 *x509 = wolfSSL_X509_load_certificate_buffer(
20018 reinterpret_cast<const unsigned char *
>(cert_pem.data()),
20019 static_cast<int>(cert_pem.size()), WOLFSSL_FILETYPE_PEM);
20020 if (x509) { certs.push_back(
static_cast<cert_t
>(x509)); }
20023 return certs.size();
20026inline std::vector<std::string> get_ca_names(ctx_t ctx) {
20027 std::vector<std::string> names;
20028 if (!ctx) {
return names; }
20029 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
20030 if (wctx->ca_pem_data_.empty()) {
return names; }
20032 const std::string &pem = wctx->ca_pem_data_;
20033 const std::string begin_marker =
"-----BEGIN CERTIFICATE-----";
20034 const std::string end_marker =
"-----END CERTIFICATE-----";
20036 while ((pos = pem.find(begin_marker, pos)) != std::string::npos) {
20037 size_t end_pos = pem.find(end_marker, pos);
20038 if (end_pos == std::string::npos) {
break; }
20039 end_pos += end_marker.size();
20040 std::string cert_pem = pem.substr(pos, end_pos - pos);
20041 WOLFSSL_X509 *x509 = wolfSSL_X509_load_certificate_buffer(
20042 reinterpret_cast<const unsigned char *
>(cert_pem.data()),
20043 static_cast<int>(cert_pem.size()), WOLFSSL_FILETYPE_PEM);
20045 WOLFSSL_X509_NAME *subject = wolfSSL_X509_get_subject_name(x509);
20047 char *name_str = wolfSSL_X509_NAME_oneline(subject,
nullptr, 0);
20049 names.push_back(name_str);
20050 XFREE(name_str,
nullptr, DYNAMIC_TYPE_OPENSSL);
20053 wolfSSL_X509_free(x509);
20060inline bool update_server_cert(ctx_t ctx,
const char *cert_pem,
20061 const char *key_pem,
const char *password) {
20062 if (!ctx || !cert_pem || !key_pem) {
return false; }
20063 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
20066 int ret = wolfSSL_CTX_use_certificate_buffer(
20067 wctx->ctx,
reinterpret_cast<const unsigned char *
>(cert_pem),
20068 static_cast<long>(strlen(cert_pem)), SSL_FILETYPE_PEM);
20069 if (ret != SSL_SUCCESS) {
20070 impl::wolfssl_last_error() =
20071 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
20076 if (password) { impl::set_wolfssl_password_cb(wctx->ctx, password); }
20079 ret = wolfSSL_CTX_use_PrivateKey_buffer(
20080 wctx->ctx,
reinterpret_cast<const unsigned char *
>(key_pem),
20081 static_cast<long>(strlen(key_pem)), SSL_FILETYPE_PEM);
20082 if (ret != SSL_SUCCESS) {
20083 impl::wolfssl_last_error() =
20084 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
20091inline bool update_server_client_ca(ctx_t ctx,
const char *ca_pem) {
20092 if (!ctx || !ca_pem) {
return false; }
20093 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
20095 int ret = wolfSSL_CTX_load_verify_buffer(
20096 wctx->ctx,
reinterpret_cast<const unsigned char *
>(ca_pem),
20097 static_cast<long>(strlen(ca_pem)), SSL_FILETYPE_PEM);
20098 if (ret != SSL_SUCCESS) {
20099 impl::wolfssl_last_error() =
20100 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
20106inline bool set_verify_callback(ctx_t ctx, VerifyCallback callback) {
20107 if (!ctx) {
return false; }
20108 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
20110 impl::get_verify_callback() = std::move(callback);
20111 wctx->has_verify_callback =
static_cast<bool>(impl::get_verify_callback());
20113 if (wctx->has_verify_callback) {
20114 wolfSSL_CTX_set_verify(wctx->ctx, SSL_VERIFY_PEER,
20115 impl::wolfssl_verify_callback);
20117 wolfSSL_CTX_set_verify(
20119 wctx->verify_client
20120 ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
20127inline long get_verify_error(const_session_t session) {
20128 if (!session) {
return -1; }
20130 static_cast<impl::WolfSSLSession *
>(
const_cast<void *
>(session));
20131 return wolfSSL_get_verify_result(wsession->ssl);
20134inline std::string verify_error_string(
long error_code) {
20135 if (error_code == 0) {
return ""; }
20137 wolfSSL_X509_verify_cert_error_string(
static_cast<int>(error_code));
20138 return str ? std::string(str) : std::string();
20148inline bool WebSocket::send_frame(Opcode op,
const char *data,
size_t len,
20150 std::lock_guard<std::mutex> lock(write_mutex_);
20151 if (closed_) {
return false; }
20152 return detail::write_websocket_frame(strm_, op, data, len, fin, !is_server_);
20155inline ReadResult WebSocket::read(std::string &msg) {
20158 std::string payload;
20161 if (!impl::read_websocket_frame(strm_, opcode, payload, fin, is_server_,
20168 case Opcode::Ping: {
20169 std::lock_guard<std::mutex> lock(write_mutex_);
20170 detail::write_websocket_frame(strm_, Opcode::Pong, payload.data(),
20171 payload.size(),
true, !is_server_);
20174 case Opcode::Pong: {
20175 std::lock_guard<std::mutex> lock(ping_mutex_);
20176 unacked_pings_ = 0;
20179 case Opcode::Close: {
20180 if (!closed_.exchange(
true)) {
20182 std::lock_guard<std::mutex> lock(write_mutex_);
20183 detail::write_websocket_frame(strm_, Opcode::Close, payload.data(),
20184 payload.size(),
true, !is_server_);
20189 case Opcode::Binary: {
20190 auto result = opcode == Opcode::Text ? Text : Binary;
20191 msg = std::move(payload);
20196 Opcode cont_opcode;
20197 std::string cont_payload;
20199 if (!impl::read_websocket_frame(
20200 strm_, cont_opcode, cont_payload, cont_fin, is_server_,
20205 if (cont_opcode == Opcode::Ping) {
20206 std::lock_guard<std::mutex> lock(write_mutex_);
20207 detail::write_websocket_frame(
20208 strm_, Opcode::Pong, cont_payload.data(), cont_payload.size(),
20209 true, !is_server_);
20212 if (cont_opcode == Opcode::Pong) {
20213 std::lock_guard<std::mutex> lock(ping_mutex_);
20214 unacked_pings_ = 0;
20217 if (cont_opcode == Opcode::Close) {
20218 if (!closed_.exchange(
true)) {
20219 std::lock_guard<std::mutex> lock(write_mutex_);
20220 detail::write_websocket_frame(
20221 strm_, Opcode::Close, cont_payload.data(),
20222 cont_payload.size(),
true, !is_server_);
20227 if (cont_opcode != Opcode::Continuation) {
20231 msg += cont_payload;
20236 if (cont_fin) {
break; }
20240 if (result == Text && !impl::is_valid_utf8(msg)) {
20241 close(CloseStatus::InvalidPayload,
"invalid UTF-8");
20246 default: closed_ =
true;
return Fail;
20252inline bool WebSocket::send(
const std::string &data) {
20253 return send_frame(Opcode::Text, data.data(), data.size());
20256inline bool WebSocket::send(
const char *data,
size_t len) {
20257 return send_frame(Opcode::Binary, data, len);
20260inline void WebSocket::close(CloseStatus status,
const std::string &reason) {
20261 if (closed_.exchange(
true)) {
return; }
20262 ping_cv_.notify_all();
20263 std::string payload;
20264 auto code =
static_cast<uint16_t
>(status);
20265 payload.push_back(
static_cast<char>((code >> 8) & 0xFF));
20266 payload.push_back(
static_cast<char>(code & 0xFF));
20269 payload += reason.substr(0, 123);
20271 std::lock_guard<std::mutex> lock(write_mutex_);
20272 detail::write_websocket_frame(strm_, Opcode::Close, payload.data(),
20273 payload.size(),
true, !is_server_);
20283 while (impl::read_websocket_frame(strm_, op, resp, fin, is_server_, 125)) {
20284 if (op == Opcode::Close) {
break; }
20288inline WebSocket::~WebSocket() {
20290 std::lock_guard<std::mutex> lock(ping_mutex_);
20293 ping_cv_.notify_all();
20294 if (ping_thread_.joinable()) { ping_thread_.join(); }
20297inline void WebSocket::start_heartbeat() {
20298 if (ping_interval_sec_ == 0) {
return; }
20299 ping_thread_ = std::thread([
this]() {
20300 std::unique_lock<std::mutex> lock(ping_mutex_);
20302 ping_cv_.wait_for(lock, std::chrono::seconds(ping_interval_sec_));
20303 if (closed_) {
break; }
20307 if (max_missed_pongs_ > 0 && unacked_pings_ >= max_missed_pongs_) {
20309 close(CloseStatus::GoingAway,
"pong timeout");
20313 if (!send_frame(Opcode::Ping,
nullptr, 0)) {
20324inline const Request &WebSocket::request()
const {
return req_; }
20326inline bool WebSocket::is_open()
const {
return !closed_; }
20329inline WebSocketClient::WebSocketClient(
20330 const std::string &scheme_host_port_path,
const Headers &headers)
20331 : headers_(headers) {
20332 detail::UrlComponents uc;
20333 if (detail::parse_url(scheme_host_port_path, uc) && !uc.scheme.empty() &&
20334 !uc.host.empty() && !uc.path.empty()) {
20335 auto &scheme = uc.scheme;
20337#ifdef CPPHTTPLIB_SSL_ENABLED
20338 if (scheme !=
"ws" && scheme !=
"wss") {
20340 if (scheme !=
"ws") {
20342#ifndef CPPHTTPLIB_NO_EXCEPTIONS
20343 std::string msg =
"'" + scheme +
"' scheme is not supported.";
20344 throw std::invalid_argument(msg);
20349 auto is_ssl = scheme ==
"wss";
20351 host_ = std::move(uc.host);
20353 port_ = is_ssl ? 443 : 80;
20354 if (!uc.port.empty() && !detail::parse_port(uc.port, port_)) { return; }
20356 path_ = std::move(uc.path);
20357 if (!uc.query.empty()) { path_ += uc.query; }
20359#ifdef CPPHTTPLIB_SSL_ENABLED
20364 tls_ctx_ = tls::create_client_context();
20365 if (!tls_ctx_) { return; }
20368 if (is_ssl) { return; }
20375inline WebSocketClient::~WebSocketClient() {
20376 shutdown_and_close();
20377#ifdef CPPHTTPLIB_SSL_ENABLED
20379 tls::free_context(tls_ctx_);
20380 tls_ctx_ =
nullptr;
20385inline bool WebSocketClient::is_valid()
const {
return is_valid_; }
20387inline void WebSocketClient::shutdown_and_close() {
20388#ifdef CPPHTTPLIB_SSL_ENABLED
20390 if (tls_session_) {
20391 tls::shutdown(tls_session_,
true);
20392 tls::free_session(tls_session_);
20393 tls_session_ =
nullptr;
20397 if (ws_ && ws_->is_open()) { ws_->close(); }
20400 detail::shutdown_socket(sock_);
20401 detail::close_socket(sock_);
20406inline bool WebSocketClient::create_stream(std::unique_ptr<Stream> &strm) {
20407#ifdef CPPHTTPLIB_SSL_ENABLED
20409 if (server_certificate_verification_ && !certs_loaded_) {
20410 uint64_t backend_error = 0;
20411 detail::load_client_ca_config(tls_ctx_, ca_cert_file_path_, std::string(),
20412 custom_ca_loaded_, system_ca_mode_,
20414 certs_loaded_ =
true;
20417 if (!detail::setup_client_tls_session(host_, tls_ctx_, tls_session_, sock_,
20418 server_certificate_verification_,
20420 read_timeout_usec_)) {
20424 strm = std::unique_ptr<Stream>(
new detail::SSLSocketStream(
20425 sock_, tls_session_, read_timeout_sec_, read_timeout_usec_,
20426 write_timeout_sec_, write_timeout_usec_));
20430 strm = std::unique_ptr<Stream>(
20431 new detail::SocketStream(sock_, read_timeout_sec_, read_timeout_usec_,
20432 write_timeout_sec_, write_timeout_usec_));
20436inline bool WebSocketClient::connect() {
20437 if (!is_valid_) {
return false; }
20438 shutdown_and_close();
20442 auto it = addr_map_.find(host_);
20443 if (it != addr_map_.end()) { ip = it->second; }
20446 sock_ = detail::create_client_socket(
20447 host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
20448 socket_options_, connection_timeout_sec_, connection_timeout_usec_,
20449 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
20450 write_timeout_usec_, interface_, error);
20454 std::unique_ptr<Stream> strm;
20455 if (!create_stream(strm)) {
20456 shutdown_and_close();
20460 std::string selected_subprotocol;
20461 if (!detail::perform_websocket_handshake(*strm, host_, port_, path_, headers_,
20462 selected_subprotocol)) {
20463 shutdown_and_close();
20466 subprotocol_ = std::move(selected_subprotocol);
20469 req.method =
"GET";
20471 ws_ = std::unique_ptr<WebSocket>(
new WebSocket(std::move(strm), req,
false,
20472 websocket_ping_interval_sec_,
20473 websocket_max_missed_pongs_));
20477inline ReadResult WebSocketClient::read(std::string &msg) {
20478 if (!ws_) {
return Fail; }
20479 return ws_->read(msg);
20482inline bool WebSocketClient::send(
const std::string &data) {
20483 if (!ws_) {
return false; }
20484 return ws_->send(data);
20487inline bool WebSocketClient::send(
const char *data,
size_t len) {
20488 if (!ws_) {
return false; }
20489 return ws_->send(data, len);
20492inline void WebSocketClient::close(CloseStatus status,
20493 const std::string &reason) {
20494 if (ws_) { ws_->close(status, reason); }
20497inline bool WebSocketClient::is_open()
const {
return ws_ && ws_->is_open(); }
20499inline const std::string &WebSocketClient::subprotocol()
const {
20500 return subprotocol_;
20503inline void WebSocketClient::set_read_timeout(time_t sec, time_t usec) {
20504 read_timeout_sec_ = sec;
20505 read_timeout_usec_ = usec;
20508inline void WebSocketClient::set_write_timeout(time_t sec, time_t usec) {
20509 write_timeout_sec_ = sec;
20510 write_timeout_usec_ = usec;
20513inline void WebSocketClient::set_websocket_ping_interval(time_t sec) {
20514 websocket_ping_interval_sec_ = sec;
20517inline void WebSocketClient::set_websocket_max_missed_pongs(
int count) {
20518 websocket_max_missed_pongs_ = count;
20521inline void WebSocketClient::set_tcp_nodelay(
bool on) { tcp_nodelay_ = on; }
20523inline void WebSocketClient::set_address_family(
int family) {
20524 address_family_ = family;
20527inline void WebSocketClient::set_ipv6_v6only(
bool on) { ipv6_v6only_ = on; }
20529inline void WebSocketClient::set_socket_options(SocketOptions socket_options) {
20530 socket_options_ = std::move(socket_options);
20533inline void WebSocketClient::set_connection_timeout(time_t sec, time_t usec) {
20534 connection_timeout_sec_ = sec;
20535 connection_timeout_usec_ = usec;
20538inline void WebSocketClient::set_interface(
const std::string &intf) {
20542inline void WebSocketClient::set_hostname_addr_map(
20543 std::map<std::string, std::string> addr_map) {
20544 addr_map_ = std::move(addr_map);
20547#ifdef CPPHTTPLIB_SSL_ENABLED
20549inline void WebSocketClient::set_ca_cert_path(
const std::string &path) {
20550 ca_cert_file_path_ = path;
20553inline void WebSocketClient::set_ca_cert_store(tls::ca_store_t store) {
20554 if (store && tls_ctx_) {
20556 tls::set_ca_store(tls_ctx_, store);
20557 custom_ca_loaded_ =
true;
20558 }
else if (store) {
20559 tls::free_ca_store(store);
20563inline void WebSocketClient::load_ca_cert_store(
const char *ca_cert,
20564 std::size_t size) {
20565 if (tls_ctx_ && ca_cert && size > 0) {
20566 tls::load_ca_pem(tls_ctx_, ca_cert, size);
20567 custom_ca_loaded_ =
true;
20572WebSocketClient::enable_server_certificate_verification(
bool enabled) {
20573 server_certificate_verification_ = enabled;
20576inline void WebSocketClient::enable_system_ca(
bool enabled) {
20577 system_ca_mode_ = enabled ? SystemCAMode::Enabled : SystemCAMode::Disabled;
#define CPPHTTPLIB_VERSION
#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_HEADER_MAX_COUNT
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_SEND_FLAGS
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
#define CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_SEND_BUFSIZ
#define CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
#define CPPHTTPLIB_LISTEN_BACKLOG
#define CPPHTTPLIB_IDLE_INTERVAL_SECOND
#define CPPHTTPLIB_EXPECT_100_THRESHOLD
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT
#define CPPHTTPLIB_RANGE_MAX_COUNT
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD
#define CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND
#define CPPHTTPLIB_IPV6_V6ONLY
#define CPPHTTPLIB_RECV_FLAGS
#define CPPHTTPLIB_THREAD_POOL_MAX_COUNT
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_HEADER_MAX_LENGTH
#define CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND
#define CPPHTTPLIB_COMPRESSION_BUFSIZ
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
#define CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND
#define CPPHTTPLIB_THREAD_POOL_COUNT
#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
#define CPPHTTPLIB_RECV_BUFSIZ
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
#define CPPHTTPLIB_REDIRECT_MAX_COUNT
#define CPPHTTPLIB_MAX_LINE_LENGTH
#define CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
#define CPPHTTPLIB_TCP_NODELAY
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
#define CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS
#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND