19#ifdef USE_HTTP_REQUEST_HOST_H
23#ifndef CPPHTTPLIB_HTTPLIB_H
24#define CPPHTTPLIB_HTTPLIB_H
26#define CPPHTTPLIB_VERSION "0.18.2"
32#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
33#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
36#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
37#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
40#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
41#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
44#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
45#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
48#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
49#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
52#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
53#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
56#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
57#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
60#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
61#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
64#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
65#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
68#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
69#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
72#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
73#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
76#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
77#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
80#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
81#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
84#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
85#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
88#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
90#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
92#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
96#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
97#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
100#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
101#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
104#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
105#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
108#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
109#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
112#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
113#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
116#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
117#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
120#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
121#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
124#ifndef CPPHTTPLIB_TCP_NODELAY
125#define CPPHTTPLIB_TCP_NODELAY false
128#ifndef CPPHTTPLIB_IPV6_V6ONLY
129#define CPPHTTPLIB_IPV6_V6ONLY false
132#ifndef CPPHTTPLIB_RECV_BUFSIZ
133#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
136#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
137#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
140#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
141#define CPPHTTPLIB_THREAD_POOL_COUNT \
142 ((std::max)(8u, std::thread::hardware_concurrency() > 0 ? std::thread::hardware_concurrency() - 1 : 0))
145#ifndef CPPHTTPLIB_RECV_FLAGS
146#define CPPHTTPLIB_RECV_FLAGS 0
149#ifndef CPPHTTPLIB_SEND_FLAGS
150#define CPPHTTPLIB_SEND_FLAGS 0
153#ifndef CPPHTTPLIB_LISTEN_BACKLOG
154#define CPPHTTPLIB_LISTEN_BACKLOG 5
162#ifndef _CRT_SECURE_NO_WARNINGS
163#define _CRT_SECURE_NO_WARNINGS
166#ifndef _CRT_NONSTDC_NO_DEPRECATE
167#define _CRT_NONSTDC_NO_DEPRECATE
172#error Sorry, Visual Studio versions prior to 2015 are not supported
175#pragma comment(lib, "ws2_32.lib")
185#define S_ISREG(m) (((m) &S_IFREG) == S_IFREG)
189#define S_ISDIR(m) (((m) &S_IFDIR) == S_IFDIR)
200#ifndef WSA_FLAG_NO_HANDLE_INHERIT
201#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
205#ifdef CPPHTTPLIB_USE_POLL
206#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
211#include <arpa/inet.h>
212#if !defined(_AIX) && !defined(__MVS__)
218#define NI_MAXHOST 1025
223#include <netinet/in.h>
227#include <netinet/tcp.h>
228#ifdef CPPHTTPLIB_USE_POLL
234#include <sys/select.h>
235#include <sys/socket.h>
240#ifndef INVALID_SOCKET
241#define INVALID_SOCKET (-1)
251#include <condition_variable>
271#include <unordered_map>
272#include <unordered_set>
275#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
283#undef X509_EXTENSIONS
284#undef PKCS7_SIGNER_INFO
287#pragma comment(lib, "crypt32.lib")
289#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
290#include <TargetConditionals.h>
292#include <CoreFoundation/CoreFoundation.h>
293#include <Security/Security.h>
297#include <openssl/err.h>
298#include <openssl/evp.h>
299#include <openssl/ssl.h>
300#include <openssl/x509v3.h>
302#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
303#include <openssl/applink.c>
309#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
310#if OPENSSL_VERSION_NUMBER < 0x1010107f
311#error Please use OpenSSL or a current version of BoringSSL
313#define SSL_get1_peer_certificate SSL_get_peer_certificate
314#elif OPENSSL_VERSION_NUMBER < 0x30000000L
315#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
320#ifdef CPPHTTPLIB_ZLIB_SUPPORT
324#ifdef CPPHTTPLIB_BROTLI_SUPPORT
325#include <brotli/decode.h>
326#include <brotli/encode.h>
344template<
class T,
class... Args>
345typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>
::type make_unique(Args &&...args) {
346 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
350typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>
::type make_unique(std::size_t n) {
351 typedef typename std::remove_extent<T>::type RT;
352 return std::unique_ptr<T>(
new RT[n]);
355namespace case_ignore {
357inline unsigned char to_lower(
int c) {
358 const static unsigned char table[256] = {
359 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
360 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
361 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97,
362 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
363 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
364 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
365 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
366 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
367 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226, 227, 228, 229,
368 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 215, 248, 249, 250, 251,
369 252, 253, 254, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
370 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
372 return table[(
unsigned char) (
char) c];
375inline bool equal(
const std::string &a,
const std::string &b) {
376 return a.size() == b.size() &&
377 std::equal(a.begin(), a.end(), b.begin(), [](
char ca,
char cb) { return to_lower(ca) == to_lower(cb); });
381 bool operator()(
const std::string &a,
const std::string &b)
const {
return equal(a, b); }
385 size_t operator()(
const std::string &key)
const {
return hash_core(key.data(), key.size(), 0); }
387 size_t hash_core(
const char *s,
size_t l,
size_t h)
const {
389 : hash_core(s + 1,
l - 1,
392 (((std::numeric_limits<size_t>::max)() >> 6) &
h * 33) ^
393 static_cast<unsigned char>(to_lower(*s)));
403 explicit scope_exit(std::function<
void(
void)> &&f) : exit_function(std::move(f)), execute_on_destruction{
true} {}
405 scope_exit(scope_exit &&rhs) noexcept
406 : exit_function(std::move(rhs.exit_function)), execute_on_destruction{rhs.execute_on_destruction} {
411 if (execute_on_destruction) {
412 this->exit_function();
416 void release() { this->execute_on_destruction =
false; }
419 scope_exit(
const scope_exit &) =
delete;
420 void operator=(
const scope_exit &) =
delete;
421 scope_exit &operator=(scope_exit &&) =
delete;
423 std::function<void(
void)> exit_function;
424 bool execute_on_destruction;
432 SwitchingProtocol_101 = 101,
433 Processing_102 = 102,
434 EarlyHints_103 = 103,
440 NonAuthoritativeInformation_203 = 203,
442 ResetContent_205 = 205,
443 PartialContent_206 = 206,
444 MultiStatus_207 = 207,
445 AlreadyReported_208 = 208,
449 MultipleChoices_300 = 300,
450 MovedPermanently_301 = 301,
453 NotModified_304 = 304,
456 TemporaryRedirect_307 = 307,
457 PermanentRedirect_308 = 308,
460 BadRequest_400 = 400,
461 Unauthorized_401 = 401,
462 PaymentRequired_402 = 402,
465 MethodNotAllowed_405 = 405,
466 NotAcceptable_406 = 406,
467 ProxyAuthenticationRequired_407 = 407,
468 RequestTimeout_408 = 408,
471 LengthRequired_411 = 411,
472 PreconditionFailed_412 = 412,
473 PayloadTooLarge_413 = 413,
474 UriTooLong_414 = 414,
475 UnsupportedMediaType_415 = 415,
476 RangeNotSatisfiable_416 = 416,
477 ExpectationFailed_417 = 417,
479 MisdirectedRequest_421 = 421,
480 UnprocessableContent_422 = 422,
482 FailedDependency_424 = 424,
484 UpgradeRequired_426 = 426,
485 PreconditionRequired_428 = 428,
486 TooManyRequests_429 = 429,
487 RequestHeaderFieldsTooLarge_431 = 431,
488 UnavailableForLegalReasons_451 = 451,
491 InternalServerError_500 = 500,
492 NotImplemented_501 = 501,
493 BadGateway_502 = 502,
494 ServiceUnavailable_503 = 503,
495 GatewayTimeout_504 = 504,
496 HttpVersionNotSupported_505 = 505,
497 VariantAlsoNegotiates_506 = 506,
498 InsufficientStorage_507 = 507,
499 LoopDetected_508 = 508,
500 NotExtended_510 = 510,
501 NetworkAuthenticationRequired_511 = 511,
505 std::unordered_multimap<std::string, std::string, detail::case_ignore::hash, detail::case_ignore::equal_to>;
507using Params = std::multimap<std::string, std::string>;
508using Match = std::smatch;
510using Progress = std::function<bool(uint64_t current, uint64_t total)>;
513using ResponseHandler = std::function<bool(
const Response &response)>;
515struct MultipartFormData {
518 std::string filename;
519 std::string content_type;
521using MultipartFormDataItems = std::vector<MultipartFormData>;
522using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
526 DataSink() : os(&sb_), sb_(*
this) {}
528 DataSink(
const DataSink &) =
delete;
529 DataSink &operator=(
const DataSink &) =
delete;
530 DataSink(DataSink &&) =
delete;
531 DataSink &operator=(DataSink &&) =
delete;
533 std::function<bool(
const char *data,
size_t data_len)> write;
534 std::function<bool()> is_writable;
535 std::function<void()> done;
536 std::function<void(
const Headers &
trailer)> done_with_trailer;
540 class data_sink_streambuf final :
public std::streambuf {
542 explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
545 std::streamsize xsputn(
const char *s, std::streamsize n)
override {
546 sink_.write(s,
static_cast<size_t>(n));
554 data_sink_streambuf sb_;
557using ContentProvider = std::function<bool(
size_t offset,
size_t length, DataSink &sink)>;
559using ContentProviderWithoutLength = std::function<bool(
size_t offset, DataSink &sink)>;
561using ContentProviderResourceReleaser = std::function<void(
bool success)>;
563struct MultipartFormDataProvider {
565 ContentProviderWithoutLength provider;
566 std::string filename;
567 std::string content_type;
569using MultipartFormDataProviderItems = std::vector<MultipartFormDataProvider>;
571using ContentReceiverWithProgress =
572 std::function<bool(
const char *data,
size_t data_length, uint64_t offset, uint64_t total_length)>;
574using ContentReceiver = std::function<bool(
const char *data,
size_t data_length)>;
576using MultipartContentHeader = std::function<bool(
const MultipartFormData &file)>;
580 using Reader = std::function<bool(ContentReceiver receiver)>;
581 using MultipartReader = std::function<bool(MultipartContentHeader header, ContentReceiver receiver)>;
583 ContentReader(Reader reader, MultipartReader multipart_reader)
584 : reader_(std::move(reader)), multipart_reader_(std::move(multipart_reader)) {}
586 bool operator()(MultipartContentHeader header, ContentReceiver receiver)
const {
587 return multipart_reader_(std::move(header), std::move(receiver));
590 bool operator()(ContentReceiver receiver)
const {
return reader_(std::move(receiver)); }
593 MultipartReader multipart_reader_;
596using Range = std::pair<ssize_t, ssize_t>;
597using Ranges = std::vector<Range>;
606 std::string remote_addr;
607 int remote_port = -1;
608 std::string local_addr;
614 MultipartFormDataMap files;
617 std::unordered_map<std::string, std::string> path_params;
620 ResponseHandler response_handler;
621 ContentReceiverWithProgress content_receiver;
623#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
624 const SSL *ssl =
nullptr;
627 bool has_header(
const std::string &key)
const;
628 std::string get_header_value(
const std::string &key,
const char *def =
"",
size_t id = 0)
const;
629 uint64_t get_header_value_u64(
const std::string &key, uint64_t def = 0,
size_t id = 0)
const;
630 size_t get_header_value_count(
const std::string &key)
const;
631 void set_header(
const std::string &key,
const std::string &
val);
633 bool has_param(
const std::string &key)
const;
634 std::string get_param_value(
const std::string &key,
size_t id = 0)
const;
635 size_t get_param_value_count(
const std::string &key)
const;
637 bool is_multipart_form_data()
const;
639 bool has_file(
const std::string &key)
const;
640 MultipartFormData get_file_value(
const std::string &key)
const;
641 std::vector<MultipartFormData> get_file_values(
const std::string &key)
const;
644 size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
645 size_t content_length_ = 0;
646 ContentProvider content_provider_;
647 bool is_chunked_content_provider_ =
false;
648 size_t authorization_count_ = 0;
657 std::string location;
659 bool has_header(
const std::string &key)
const;
660 std::string get_header_value(
const std::string &key,
const char *def =
"",
size_t id = 0)
const;
661 uint64_t get_header_value_u64(
const std::string &key, uint64_t def = 0,
size_t id = 0)
const;
662 size_t get_header_value_count(
const std::string &key)
const;
663 void set_header(
const std::string &key,
const std::string &
val);
665 void set_redirect(
const std::string &url,
int status = StatusCode::Found_302);
666 void set_content(
const char *s,
size_t n,
const std::string &content_type);
667 void set_content(
const std::string &s,
const std::string &content_type);
668 void set_content(std::string &&s,
const std::string &content_type);
670 void set_content_provider(
size_t length,
const std::string &content_type, ContentProvider provider,
671 ContentProviderResourceReleaser resource_releaser =
nullptr);
673 void set_content_provider(
const std::string &content_type, ContentProviderWithoutLength provider,
674 ContentProviderResourceReleaser resource_releaser =
nullptr);
676 void set_chunked_content_provider(
const std::string &content_type, ContentProviderWithoutLength provider,
677 ContentProviderResourceReleaser resource_releaser =
nullptr);
679 void set_file_content(
const std::string &path,
const std::string &content_type);
680 void set_file_content(
const std::string &path);
682 Response() =
default;
683 Response(
const Response &) =
default;
684 Response &operator=(
const Response &) =
default;
685 Response(Response &&) =
default;
686 Response &operator=(Response &&) =
default;
688 if (content_provider_resource_releaser_) {
689 content_provider_resource_releaser_(content_provider_success_);
694 size_t content_length_ = 0;
695 ContentProvider content_provider_;
696 ContentProviderResourceReleaser content_provider_resource_releaser_;
697 bool is_chunked_content_provider_ =
false;
698 bool content_provider_success_ =
false;
699 std::string file_content_path_;
700 std::string file_content_content_type_;
705 virtual ~Stream() =
default;
707 virtual bool is_readable()
const = 0;
708 virtual bool is_writable()
const = 0;
710 virtual ssize_t read(
char *ptr,
size_t size) = 0;
711 virtual ssize_t write(
const char *ptr,
size_t size) = 0;
712 virtual void get_remote_ip_and_port(std::string &ip,
int &port)
const = 0;
713 virtual void get_local_ip_and_port(std::string &ip,
int &port)
const = 0;
714 virtual socket_t socket()
const = 0;
716 ssize_t write(
const char *ptr);
717 ssize_t write(
const std::string &s);
722 TaskQueue() =
default;
723 virtual ~TaskQueue() =
default;
725 virtual bool enqueue(std::function<
void()> fn) = 0;
726 virtual void shutdown() = 0;
728 virtual void on_idle() {}
731class ThreadPool final :
public TaskQueue {
733 explicit ThreadPool(
size_t n,
size_t mqr = 0) : shutdown_(
false), max_queued_requests_(mqr) {
735 threads_.emplace_back(worker(*
this));
740 ThreadPool(
const ThreadPool &) =
delete;
741 ~ThreadPool()
override =
default;
743 bool enqueue(std::function<
void()> fn)
override {
745 std::unique_lock<std::mutex> lock(mutex_);
746 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
749 jobs_.push_back(std::move(fn));
756 void shutdown()
override {
759 std::unique_lock<std::mutex> lock(mutex_);
766 for (
auto &t : threads_) {
773 explicit worker(ThreadPool &pool) : pool_(pool) {}
777 std::function<void()> fn;
779 std::unique_lock<std::mutex> lock(pool_.mutex_);
781 pool_.cond_.wait(lock, [&] {
return !pool_.jobs_.empty() || pool_.shutdown_; });
783 if (pool_.shutdown_ && pool_.jobs_.empty()) {
787 fn = pool_.jobs_.front();
788 pool_.jobs_.pop_front();
791 assert(
true ==
static_cast<bool>(fn));
795#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER)
796 OPENSSL_thread_stop();
802 friend struct worker;
804 std::vector<std::thread> threads_;
805 std::list<std::function<void()>> jobs_;
808 size_t max_queued_requests_ = 0;
810 std::condition_variable cond_;
814using Logger = std::function<void(
const Request &,
const Response &)>;
816using SocketOptions = std::function<void(
socket_t sock)>;
818void default_socket_options(
socket_t sock);
820const char *status_message(
int status);
822std::string get_bearer_token_auth(
const Request &req);
828 virtual ~MatcherBase() =
default;
831 virtual bool match(Request &request)
const = 0;
852class PathParamsMatcher final :
public MatcherBase {
854 PathParamsMatcher(
const std::string &pattern);
856 bool match(Request &request)
const override;
862 static constexpr char separator =
'/';
867 std::vector<std::string> static_fragments_;
870 std::vector<std::string> param_names_;
881class RegexMatcher final :
public MatcherBase {
883 RegexMatcher(
const std::string &pattern) : regex_(pattern) {}
885 bool match(Request &request)
const override;
891ssize_t write_headers(Stream &strm,
const Headers &headers);
897 using Handler = std::function<void(
const Request &, Response &)>;
899 using ExceptionHandler = std::function<void(
const Request &, Response &, std::exception_ptr ep)>;
901 enum class HandlerResponse {
905 using HandlerWithResponse = std::function<HandlerResponse(
const Request &, Response &)>;
907 using HandlerWithContentReader =
908 std::function<void(
const Request &, Response &,
const ContentReader &content_reader)>;
910 using Expect100ContinueHandler = std::function<int(
const Request &, Response &)>;
916 virtual bool is_valid()
const;
918 Server &Get(
const std::string &pattern, Handler handler);
919 Server &Post(
const std::string &pattern, Handler handler);
920 Server &Post(
const std::string &pattern, HandlerWithContentReader handler);
921 Server &Put(
const std::string &pattern, Handler handler);
922 Server &Put(
const std::string &pattern, HandlerWithContentReader handler);
923 Server &Patch(
const std::string &pattern, Handler handler);
924 Server &Patch(
const std::string &pattern, HandlerWithContentReader handler);
925 Server &Delete(
const std::string &pattern, Handler handler);
926 Server &Delete(
const std::string &pattern, HandlerWithContentReader handler);
927 Server &Options(
const std::string &pattern, Handler handler);
929 bool set_base_dir(
const std::string &dir,
const std::string &mount_point = std::string());
930 bool set_mount_point(
const std::string &mount_point,
const std::string &dir, Headers headers = Headers());
931 bool remove_mount_point(
const std::string &mount_point);
932 Server &set_file_extension_and_mimetype_mapping(
const std::string &ext,
const std::string &mime);
933 Server &set_default_file_mimetype(
const std::string &mime);
934 Server &set_file_request_handler(Handler handler);
936 template<
class ErrorHandlerFunc> Server &set_error_handler(ErrorHandlerFunc &&handler) {
937 return set_error_handler_core(std::forward<ErrorHandlerFunc>(handler),
938 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
941 Server &set_exception_handler(ExceptionHandler handler);
942 Server &set_pre_routing_handler(HandlerWithResponse handler);
943 Server &set_post_routing_handler(Handler handler);
945 Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
946 Server &set_logger(Logger logger);
948 Server &set_address_family(
int family);
949 Server &set_tcp_nodelay(
bool on);
950 Server &set_ipv6_v6only(
bool on);
951 Server &set_socket_options(SocketOptions socket_options);
953 Server &set_default_headers(Headers headers);
954 Server &set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer);
956 Server &set_keep_alive_max_count(
size_t count);
957 Server &set_keep_alive_timeout(time_t sec);
959 Server &set_read_timeout(time_t sec, time_t usec = 0);
960 template<
class Rep,
class Period> Server &set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration);
962 Server &set_write_timeout(time_t sec, time_t usec = 0);
963 template<
class Rep,
class Period> Server &set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration);
965 Server &set_idle_interval(time_t sec, time_t usec = 0);
966 template<
class Rep,
class Period> Server &set_idle_interval(
const std::chrono::duration<Rep, Period> &
duration);
968 Server &set_payload_max_length(
size_t length);
970 bool bind_to_port(
const std::string &host,
int port,
int socket_flags = 0);
971 int bind_to_any_port(
const std::string &host,
int socket_flags = 0);
972 bool listen_after_bind();
974 bool listen(
const std::string &host,
int port,
int socket_flags = 0);
976 bool is_running()
const;
977 void wait_until_ready()
const;
981 std::function<TaskQueue *(void)> new_task_queue;
984 bool process_request(Stream &strm,
const std::string &remote_addr,
int remote_port,
const std::string &local_addr,
985 int local_port,
bool close_connection,
bool &connection_closed,
986 const std::function<
void(Request &)> &setup_request);
988 std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
989 size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
990 time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
991 time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
992 time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
993 time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
994 time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
995 time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
996 time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
997 size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
1000 using Handlers = std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
1001 using HandlersForContentReader =
1002 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, HandlerWithContentReader>>;
1004 static std::unique_ptr<detail::MatcherBase> make_matcher(
const std::string &pattern);
1006 Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1007 Server &set_error_handler_core(Handler handler, std::false_type);
1009 socket_t create_server_socket(
const std::string &host,
int port,
int socket_flags,
1010 SocketOptions socket_options)
const;
1011 int bind_internal(
const std::string &host,
int port,
int socket_flags);
1012 bool listen_internal();
1014 bool routing(Request &req, Response &res, Stream &strm);
1015 bool handle_file_request(
const Request &req, Response &res,
bool head =
false);
1016 bool dispatch_request(Request &req, Response &res,
const Handlers &handlers)
const;
1017 bool dispatch_request_for_content_reader(Request &req, Response &res, ContentReader content_reader,
1018 const HandlersForContentReader &handlers)
const;
1020 bool parse_request_line(
const char *s, Request &req)
const;
1021 void apply_ranges(
const Request &req, Response &res, std::string &content_type, std::string &boundary)
const;
1022 bool write_response(Stream &strm,
bool close_connection, Request &req, Response &res);
1023 bool write_response_with_content(Stream &strm,
bool close_connection,
const Request &req, Response &res);
1024 bool write_response_core(Stream &strm,
bool close_connection,
const Request &req, Response &res,
1025 bool need_apply_ranges);
1026 bool write_content_with_provider(Stream &strm,
const Request &req, Response &res,
const std::string &boundary,
1027 const std::string &content_type);
1028 bool read_content(Stream &strm, Request &req, Response &res);
1029 bool read_content_with_content_receiver(Stream &strm, Request &req, Response &res, ContentReceiver receiver,
1030 MultipartContentHeader multipart_header, ContentReceiver multipart_receiver);
1031 bool read_content_core(Stream &strm, Request &req, Response &res, ContentReceiver receiver,
1032 MultipartContentHeader multipart_header, ContentReceiver multipart_receiver)
const;
1034 virtual bool process_and_close_socket(
socket_t sock);
1036 std::atomic<bool> is_running_{
false};
1037 std::atomic<bool> is_decommisioned{
false};
1039 struct MountPointEntry {
1040 std::string mount_point;
1041 std::string base_dir;
1044 std::vector<MountPointEntry> base_dirs_;
1045 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1046 std::string default_file_mimetype_ =
"application/octet-stream";
1047 Handler file_request_handler_;
1049 Handlers get_handlers_;
1050 Handlers post_handlers_;
1051 HandlersForContentReader post_handlers_for_content_reader_;
1052 Handlers put_handlers_;
1053 HandlersForContentReader put_handlers_for_content_reader_;
1054 Handlers patch_handlers_;
1055 HandlersForContentReader patch_handlers_for_content_reader_;
1056 Handlers delete_handlers_;
1057 HandlersForContentReader delete_handlers_for_content_reader_;
1058 Handlers options_handlers_;
1060 HandlerWithResponse error_handler_;
1061 ExceptionHandler exception_handler_;
1062 HandlerWithResponse pre_routing_handler_;
1063 Handler post_routing_handler_;
1064 Expect100ContinueHandler expect_100_continue_handler_;
1068 int address_family_ = AF_UNSPEC;
1069 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1070 bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1071 SocketOptions socket_options_ = default_socket_options;
1073 Headers default_headers_;
1074 std::function<
ssize_t(Stream &, Headers &)> header_writer_ = detail::write_headers;
1084 ExceedRedirectCount,
1088 SSLServerVerification,
1089 SSLServerHostnameVerification,
1090 UnsupportedMultipartBoundaryChars,
1096 SSLPeerCouldBeClosed_,
1099std::string to_string(Error error);
1101std::ostream &operator<<(std::ostream &os,
const Error &obj);
1106 Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers = Headers{})
1107 : res_(std::move(res)), err_(err), request_headers_(std::move(request_headers)) {}
1109 operator bool()
const {
return res_ !=
nullptr; }
1110 bool operator==(std::nullptr_t)
const {
return res_ ==
nullptr; }
1111 bool operator!=(std::nullptr_t)
const {
return res_ !=
nullptr; }
1112 const Response &value()
const {
return *res_; }
1113 Response &value() {
return *res_; }
1114 const Response &operator*()
const {
return *res_; }
1115 Response &operator*() {
return *res_; }
1116 const Response *operator->()
const {
return res_.get(); }
1117 Response *operator->() {
return res_.get(); }
1120 Error error()
const {
return err_; }
1123 bool has_request_header(
const std::string &key)
const;
1124 std::string get_request_header_value(
const std::string &key,
const char *def =
"",
size_t id = 0)
const;
1125 uint64_t get_request_header_value_u64(
const std::string &key, uint64_t def = 0,
size_t id = 0)
const;
1126 size_t get_request_header_value_count(
const std::string &key)
const;
1129 std::unique_ptr<Response> res_;
1130 Error err_ = Error::Unknown;
1131 Headers request_headers_;
1136 explicit ClientImpl(
const std::string &host);
1138 explicit ClientImpl(
const std::string &host,
int port);
1140 explicit ClientImpl(
const std::string &host,
int port,
const std::string &client_cert_path,
1141 const std::string &client_key_path);
1143 virtual ~ClientImpl();
1145 virtual bool is_valid()
const;
1147 Result Get(
const std::string &path);
1148 Result Get(
const std::string &path,
const Headers &headers);
1149 Result Get(
const std::string &path, Progress progress);
1150 Result Get(
const std::string &path,
const Headers &headers, Progress progress);
1151 Result Get(
const std::string &path, ContentReceiver content_receiver);
1152 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver);
1153 Result Get(
const std::string &path, ContentReceiver content_receiver, Progress progress);
1154 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver, Progress progress);
1155 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver);
1156 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
1157 ContentReceiver content_receiver);
1158 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
1160 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
1161 ContentReceiver content_receiver, Progress progress);
1163 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, Progress progress =
nullptr);
1164 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ContentReceiver content_receiver,
1165 Progress progress =
nullptr);
1166 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ResponseHandler response_handler,
1167 ContentReceiver content_receiver, Progress progress =
nullptr);
1169 Result Head(
const std::string &path);
1170 Result Head(
const std::string &path,
const Headers &headers);
1172 Result Post(
const std::string &path);
1173 Result Post(
const std::string &path,
const Headers &headers);
1174 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1175 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1176 const std::string &content_type);
1177 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1178 const std::string &content_type, Progress progress);
1179 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type);
1180 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1181 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
1182 const std::string &content_type);
1183 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1185 Result Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1186 const std::string &content_type);
1187 Result Post(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1188 Result Post(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1189 const std::string &content_type);
1190 Result Post(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1191 const std::string &content_type);
1192 Result Post(
const std::string &path,
const Params ¶ms);
1193 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms);
1194 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress);
1195 Result Post(
const std::string &path,
const MultipartFormDataItems &items);
1196 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items);
1197 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1198 const std::string &boundary);
1199 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1200 const MultipartFormDataProviderItems &provider_items);
1202 Result Put(
const std::string &path);
1203 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1204 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1205 const std::string &content_type);
1206 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1207 const std::string &content_type, Progress progress);
1208 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type);
1209 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1210 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type);
1211 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1213 Result Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1214 const std::string &content_type);
1215 Result Put(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1216 Result Put(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1217 const std::string &content_type);
1218 Result Put(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1219 const std::string &content_type);
1220 Result Put(
const std::string &path,
const Params ¶ms);
1221 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms);
1222 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress);
1223 Result Put(
const std::string &path,
const MultipartFormDataItems &items);
1224 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items);
1225 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1226 const std::string &boundary);
1227 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1228 const MultipartFormDataProviderItems &provider_items);
1230 Result Patch(
const std::string &path);
1231 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1232 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1234 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1235 const std::string &content_type);
1236 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1237 const std::string &content_type, Progress progress);
1238 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type);
1239 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1240 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
1241 const std::string &content_type);
1242 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
1243 const std::string &content_type, Progress progress);
1244 Result Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1245 const std::string &content_type);
1246 Result Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1247 Result Patch(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1248 const std::string &content_type);
1249 Result Patch(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1250 const std::string &content_type);
1252 Result Delete(
const std::string &path);
1253 Result Delete(
const std::string &path,
const Headers &headers);
1254 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1255 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1257 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1258 const std::string &content_type);
1259 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1260 const std::string &content_type, Progress progress);
1261 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type);
1262 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1263 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
1264 const std::string &content_type);
1265 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
1266 const std::string &content_type, Progress progress);
1268 Result Options(
const std::string &path);
1269 Result Options(
const std::string &path,
const Headers &headers);
1271 bool send(Request &req, Response &res, Error &error);
1272 Result send(
const Request &req);
1276 std::string host()
const;
1279 size_t is_socket_open()
const;
1282 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1284 void set_default_headers(Headers headers);
1286 void set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer);
1288 void set_address_family(
int family);
1289 void set_tcp_nodelay(
bool on);
1290 void set_ipv6_v6only(
bool on);
1291 void set_socket_options(SocketOptions socket_options);
1293 void set_connection_timeout(time_t sec, time_t usec = 0);
1294 template<
class Rep,
class Period>
void set_connection_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1296 void set_read_timeout(time_t sec, time_t usec = 0);
1297 template<
class Rep,
class Period>
void set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1299 void set_write_timeout(time_t sec, time_t usec = 0);
1300 template<
class Rep,
class Period>
void set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1302 void set_basic_auth(
const std::string &username,
const std::string &password);
1303 void set_bearer_token_auth(
const std::string &token);
1304#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1305 void set_digest_auth(
const std::string &username,
const std::string &password);
1308 void set_keep_alive(
bool on);
1309 void set_follow_location(
bool on);
1311 void set_url_encode(
bool on);
1313 void set_compress(
bool on);
1315 void set_decompress(
bool on);
1317 void set_interface(
const std::string &intf);
1319 void set_proxy(
const std::string &host,
int port);
1320 void set_proxy_basic_auth(
const std::string &username,
const std::string &password);
1321 void set_proxy_bearer_token_auth(
const std::string &token);
1322#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1323 void set_proxy_digest_auth(
const std::string &username,
const std::string &password);
1326#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1327 void set_ca_cert_path(
const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path = std::string());
1328 void set_ca_cert_store(X509_STORE *ca_cert_store);
1329 X509_STORE *create_ca_cert_store(
const char *ca_cert, std::size_t size)
const;
1332#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1333 void enable_server_certificate_verification(
bool enabled);
1334 void enable_server_hostname_verification(
bool enabled);
1335 void set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier);
1338 void set_logger(Logger logger);
1343#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1347 bool is_open()
const {
return sock != INVALID_SOCKET; }
1350 virtual bool create_and_connect_socket(Socket &socket, Error &error);
1359 virtual void shutdown_ssl(Socket &socket,
bool shutdown_gracefully);
1360 void shutdown_socket(Socket &socket)
const;
1361 void close_socket(Socket &socket);
1363 bool process_request(Stream &strm, Request &req, Response &res,
bool close_connection, Error &error);
1365 bool write_content_with_provider(Stream &strm,
const Request &req, Error &error)
const;
1367 void copy_settings(
const ClientImpl &rhs);
1370 const std::string host_;
1372 const std::string host_and_port_;
1376 mutable std::mutex socket_mutex_;
1377 std::recursive_mutex request_mutex_;
1380 size_t socket_requests_in_flight_ = 0;
1381 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1382 bool socket_should_be_closed_when_request_is_done_ =
false;
1385 std::map<std::string, std::string> addr_map_;
1388 Headers default_headers_;
1391 std::function<
ssize_t(Stream &, Headers &)> header_writer_ = detail::write_headers;
1394 std::string client_cert_path_;
1395 std::string client_key_path_;
1397 time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1398 time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1399 time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
1400 time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1401 time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1402 time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1404 std::string basic_auth_username_;
1405 std::string basic_auth_password_;
1406 std::string bearer_token_auth_token_;
1407#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1408 std::string digest_auth_username_;
1409 std::string digest_auth_password_;
1412 bool keep_alive_ =
false;
1413 bool follow_location_ =
false;
1415 bool url_encode_ =
true;
1417 int address_family_ = AF_UNSPEC;
1418 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1419 bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1420 SocketOptions socket_options_ =
nullptr;
1422 bool compress_ =
false;
1423 bool decompress_ =
true;
1425 std::string interface_;
1427 std::string proxy_host_;
1428 int proxy_port_ = -1;
1430 std::string proxy_basic_auth_username_;
1431 std::string proxy_basic_auth_password_;
1432 std::string proxy_bearer_token_auth_token_;
1433#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1434 std::string proxy_digest_auth_username_;
1435 std::string proxy_digest_auth_password_;
1438#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1439 std::string ca_cert_file_path_;
1440 std::string ca_cert_dir_path_;
1442 X509_STORE *ca_cert_store_ =
nullptr;
1445#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1446 bool server_certificate_verification_ =
true;
1447 bool server_hostname_verification_ =
true;
1448 std::function<bool(SSL *ssl)> server_certificate_verifier_;
1454 bool send_(Request &req, Response &res, Error &error);
1455 Result send_(Request &&req);
1457#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1458 bool is_ssl_peer_could_be_closed(SSL *ssl)
const;
1460 socket_t create_client_socket(Error &error)
const;
1461 bool read_response_line(Stream &strm,
const Request &req, Response &res)
const;
1462 bool write_request(Stream &strm, Request &req,
bool close_connection, Error &error);
1463 bool redirect(Request &req, Response &res, Error &error);
1464 bool handle_request(Stream &strm, Request &req, Response &res,
bool close_connection, Error &error);
1465 std::unique_ptr<Response> send_with_content_provider(Request &req,
const char *body,
size_t content_length,
1466 ContentProvider content_provider,
1467 ContentProviderWithoutLength content_provider_without_length,
1468 const std::string &content_type, Error &error);
1469 Result send_with_content_provider(
const std::string &method,
const std::string &path,
const Headers &headers,
1470 const char *body,
size_t content_length, ContentProvider content_provider,
1471 ContentProviderWithoutLength content_provider_without_length,
1472 const std::string &content_type, Progress progress);
1473 ContentProviderWithoutLength get_multipart_content_provider(
1474 const std::string &boundary,
const MultipartFormDataItems &items,
1475 const MultipartFormDataProviderItems &provider_items)
const;
1477 std::string adjust_host_string(
const std::string &host)
const;
1479 virtual bool process_socket(
const Socket &socket, std::function<
bool(Stream &strm)> callback);
1480 virtual bool is_ssl()
const;
1486 explicit Client(
const std::string &scheme_host_port);
1488 explicit Client(
const std::string &scheme_host_port,
const std::string &client_cert_path,
1489 const std::string &client_key_path);
1492 explicit Client(
const std::string &host,
int port);
1494 explicit Client(
const std::string &host,
int port,
const std::string &client_cert_path,
1495 const std::string &client_key_path);
1497 Client(Client &&) =
default;
1498 Client &operator=(Client &&) =
default;
1502 bool is_valid()
const;
1504 Result Get(
const std::string &path);
1505 Result Get(
const std::string &path,
const Headers &headers);
1506 Result Get(
const std::string &path, Progress progress);
1507 Result Get(
const std::string &path,
const Headers &headers, Progress progress);
1508 Result Get(
const std::string &path, ContentReceiver content_receiver);
1509 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver);
1510 Result Get(
const std::string &path, ContentReceiver content_receiver, Progress progress);
1511 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver, Progress progress);
1512 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver);
1513 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
1514 ContentReceiver content_receiver);
1515 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
1516 ContentReceiver content_receiver, Progress progress);
1517 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
1520 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, Progress progress =
nullptr);
1521 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ContentReceiver content_receiver,
1522 Progress progress =
nullptr);
1523 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ResponseHandler response_handler,
1524 ContentReceiver content_receiver, Progress progress =
nullptr);
1526 Result Head(
const std::string &path);
1527 Result Head(
const std::string &path,
const Headers &headers);
1529 Result Post(
const std::string &path);
1530 Result Post(
const std::string &path,
const Headers &headers);
1531 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1532 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1533 const std::string &content_type);
1534 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1535 const std::string &content_type, Progress progress);
1536 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type);
1537 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1538 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
1539 const std::string &content_type);
1540 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1542 Result Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1543 const std::string &content_type);
1544 Result Post(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1545 Result Post(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1546 const std::string &content_type);
1547 Result Post(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1548 const std::string &content_type);
1549 Result Post(
const std::string &path,
const Params ¶ms);
1550 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms);
1551 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress);
1552 Result Post(
const std::string &path,
const MultipartFormDataItems &items);
1553 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items);
1554 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1555 const std::string &boundary);
1556 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1557 const MultipartFormDataProviderItems &provider_items);
1559 Result Put(
const std::string &path);
1560 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1561 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1562 const std::string &content_type);
1563 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1564 const std::string &content_type, Progress progress);
1565 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type);
1566 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1567 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type);
1568 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1570 Result Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1571 const std::string &content_type);
1572 Result Put(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1573 Result Put(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1574 const std::string &content_type);
1575 Result Put(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1576 const std::string &content_type);
1577 Result Put(
const std::string &path,
const Params ¶ms);
1578 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms);
1579 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress);
1580 Result Put(
const std::string &path,
const MultipartFormDataItems &items);
1581 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items);
1582 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1583 const std::string &boundary);
1584 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1585 const MultipartFormDataProviderItems &provider_items);
1587 Result Patch(
const std::string &path);
1588 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1589 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1591 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1592 const std::string &content_type);
1593 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1594 const std::string &content_type, Progress progress);
1595 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type);
1596 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1597 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
1598 const std::string &content_type);
1599 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
1600 const std::string &content_type, Progress progress);
1601 Result Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1602 const std::string &content_type);
1603 Result Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1604 Result Patch(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1605 const std::string &content_type);
1606 Result Patch(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1607 const std::string &content_type);
1609 Result Delete(
const std::string &path);
1610 Result Delete(
const std::string &path,
const Headers &headers);
1611 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1612 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1614 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1615 const std::string &content_type);
1616 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1617 const std::string &content_type, Progress progress);
1618 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type);
1619 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1620 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
1621 const std::string &content_type);
1622 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
1623 const std::string &content_type, Progress progress);
1625 Result Options(
const std::string &path);
1626 Result Options(
const std::string &path,
const Headers &headers);
1628 bool send(Request &req, Response &res, Error &error);
1629 Result send(
const Request &req);
1633 std::string host()
const;
1636 size_t is_socket_open()
const;
1639 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1641 void set_default_headers(Headers headers);
1643 void set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer);
1645 void set_address_family(
int family);
1646 void set_tcp_nodelay(
bool on);
1647 void set_socket_options(SocketOptions socket_options);
1649 void set_connection_timeout(time_t sec, time_t usec = 0);
1650 template<
class Rep,
class Period>
void set_connection_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1652 void set_read_timeout(time_t sec, time_t usec = 0);
1653 template<
class Rep,
class Period>
void set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1655 void set_write_timeout(time_t sec, time_t usec = 0);
1656 template<
class Rep,
class Period>
void set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1658 void set_basic_auth(
const std::string &username,
const std::string &password);
1659 void set_bearer_token_auth(
const std::string &token);
1660#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1661 void set_digest_auth(
const std::string &username,
const std::string &password);
1664 void set_keep_alive(
bool on);
1665 void set_follow_location(
bool on);
1667 void set_url_encode(
bool on);
1669 void set_compress(
bool on);
1671 void set_decompress(
bool on);
1673 void set_interface(
const std::string &intf);
1675 void set_proxy(
const std::string &host,
int port);
1676 void set_proxy_basic_auth(
const std::string &username,
const std::string &password);
1677 void set_proxy_bearer_token_auth(
const std::string &token);
1678#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1679 void set_proxy_digest_auth(
const std::string &username,
const std::string &password);
1682#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1683 void enable_server_certificate_verification(
bool enabled);
1684 void enable_server_hostname_verification(
bool enabled);
1685 void set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier);
1688 void set_logger(Logger logger);
1691#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1692 void set_ca_cert_path(
const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path = std::string());
1694 void set_ca_cert_store(X509_STORE *ca_cert_store);
1695 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
1697 long get_openssl_verify_result()
const;
1699 SSL_CTX *ssl_context()
const;
1703 std::unique_ptr<ClientImpl> cli_;
1705#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1706 bool is_ssl_ =
false;
1710#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1711class SSLServer :
public Server {
1713 SSLServer(
const char *cert_path,
const char *private_key_path,
const char *client_ca_cert_file_path =
nullptr,
1714 const char *client_ca_cert_dir_path =
nullptr,
const char *private_key_password =
nullptr);
1716 SSLServer(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store =
nullptr);
1718 SSLServer(
const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
1720 ~SSLServer()
override;
1722 bool is_valid()
const override;
1724 SSL_CTX *ssl_context()
const;
1726 void update_certs(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store =
nullptr);
1729 bool process_and_close_socket(
socket_t sock)
override;
1732 std::mutex ctx_mutex_;
1735class SSLClient final :
public ClientImpl {
1737 explicit SSLClient(
const std::string &host);
1739 explicit SSLClient(
const std::string &host,
int port);
1741 explicit SSLClient(
const std::string &host,
int port,
const std::string &client_cert_path,
1742 const std::string &client_key_path,
const std::string &private_key_password = std::string());
1744 explicit SSLClient(
const std::string &host,
int port, X509 *client_cert, EVP_PKEY *client_key,
1745 const std::string &private_key_password = std::string());
1747 ~SSLClient()
override;
1749 bool is_valid()
const override;
1751 void set_ca_cert_store(X509_STORE *ca_cert_store);
1752 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
1754 long get_openssl_verify_result()
const;
1756 SSL_CTX *ssl_context()
const;
1759 bool create_and_connect_socket(Socket &socket, Error &error)
override;
1760 void shutdown_ssl(Socket &socket,
bool shutdown_gracefully)
override;
1761 void shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully);
1763 bool process_socket(
const Socket &socket, std::function<
bool(Stream &strm)> callback)
override;
1764 bool is_ssl()
const override;
1766 bool connect_with_proxy(Socket &sock, Response &res,
bool &success, Error &error);
1767 bool initialize_ssl(Socket &socket, Error &error);
1771 bool verify_host(X509 *server_cert)
const;
1772 bool verify_host_with_subject_alt_name(X509 *server_cert)
const;
1773 bool verify_host_with_common_name(X509 *server_cert)
const;
1774 bool check_host_name(
const char *pattern,
size_t pattern_len)
const;
1777 std::mutex ctx_mutex_;
1778 std::once_flag initialize_cert_;
1780 std::vector<std::string> host_components_;
1782 long verify_result_ = 0;
1784 friend class ClientImpl;
1794template<
typename T,
typename U>
inline void duration_to_sec_and_usec(
const T &
duration, U callback) {
1795 auto sec = std::chrono::duration_cast<std::chrono::seconds>(
duration).count();
1796 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
duration - std::chrono::seconds(sec)).count();
1797 callback(
static_cast<time_t
>(sec),
static_cast<time_t
>(usec));
1800inline uint64_t get_header_value_u64(
const Headers &headers,
const std::string &key, uint64_t def,
size_t id) {
1801 auto rng = headers.equal_range(key);
1802 auto it = rng.first;
1803 std::advance(it,
static_cast<ssize_t>(
id));
1804 if (it != rng.second) {
1805 return std::strtoull(it->second.data(),
nullptr, 10);
1812inline uint64_t Request::get_header_value_u64(
const std::string &key, uint64_t def,
size_t id)
const {
1813 return detail::get_header_value_u64(headers, key, def,
id);
1816inline uint64_t Response::get_header_value_u64(
const std::string &key, uint64_t def,
size_t id)
const {
1817 return detail::get_header_value_u64(headers, key, def,
id);
1820inline void default_socket_options(
socket_t sock) {
1823 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char *
>(&opt),
sizeof(opt));
1824 setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<const char *
>(&opt),
sizeof(opt));
1827 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
reinterpret_cast<const void *
>(&opt),
sizeof(opt));
1829 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const void *
>(&opt),
sizeof(opt));
1834inline const char *status_message(
int status) {
1836 case StatusCode::Continue_100:
1838 case StatusCode::SwitchingProtocol_101:
1839 return "Switching Protocol";
1840 case StatusCode::Processing_102:
1841 return "Processing";
1842 case StatusCode::EarlyHints_103:
1843 return "Early Hints";
1844 case StatusCode::OK_200:
1846 case StatusCode::Created_201:
1848 case StatusCode::Accepted_202:
1850 case StatusCode::NonAuthoritativeInformation_203:
1851 return "Non-Authoritative Information";
1852 case StatusCode::NoContent_204:
1853 return "No Content";
1854 case StatusCode::ResetContent_205:
1855 return "Reset Content";
1856 case StatusCode::PartialContent_206:
1857 return "Partial Content";
1858 case StatusCode::MultiStatus_207:
1859 return "Multi-Status";
1860 case StatusCode::AlreadyReported_208:
1861 return "Already Reported";
1862 case StatusCode::IMUsed_226:
1864 case StatusCode::MultipleChoices_300:
1865 return "Multiple Choices";
1866 case StatusCode::MovedPermanently_301:
1867 return "Moved Permanently";
1868 case StatusCode::Found_302:
1870 case StatusCode::SeeOther_303:
1872 case StatusCode::NotModified_304:
1873 return "Not Modified";
1874 case StatusCode::UseProxy_305:
1876 case StatusCode::unused_306:
1878 case StatusCode::TemporaryRedirect_307:
1879 return "Temporary Redirect";
1880 case StatusCode::PermanentRedirect_308:
1881 return "Permanent Redirect";
1882 case StatusCode::BadRequest_400:
1883 return "Bad Request";
1884 case StatusCode::Unauthorized_401:
1885 return "Unauthorized";
1886 case StatusCode::PaymentRequired_402:
1887 return "Payment Required";
1888 case StatusCode::Forbidden_403:
1890 case StatusCode::NotFound_404:
1892 case StatusCode::MethodNotAllowed_405:
1893 return "Method Not Allowed";
1894 case StatusCode::NotAcceptable_406:
1895 return "Not Acceptable";
1896 case StatusCode::ProxyAuthenticationRequired_407:
1897 return "Proxy Authentication Required";
1898 case StatusCode::RequestTimeout_408:
1899 return "Request Timeout";
1900 case StatusCode::Conflict_409:
1902 case StatusCode::Gone_410:
1904 case StatusCode::LengthRequired_411:
1905 return "Length Required";
1906 case StatusCode::PreconditionFailed_412:
1907 return "Precondition Failed";
1908 case StatusCode::PayloadTooLarge_413:
1909 return "Payload Too Large";
1910 case StatusCode::UriTooLong_414:
1911 return "URI Too Long";
1912 case StatusCode::UnsupportedMediaType_415:
1913 return "Unsupported Media Type";
1914 case StatusCode::RangeNotSatisfiable_416:
1915 return "Range Not Satisfiable";
1916 case StatusCode::ExpectationFailed_417:
1917 return "Expectation Failed";
1918 case StatusCode::ImATeapot_418:
1919 return "I'm a teapot";
1920 case StatusCode::MisdirectedRequest_421:
1921 return "Misdirected Request";
1922 case StatusCode::UnprocessableContent_422:
1923 return "Unprocessable Content";
1924 case StatusCode::Locked_423:
1926 case StatusCode::FailedDependency_424:
1927 return "Failed Dependency";
1928 case StatusCode::TooEarly_425:
1930 case StatusCode::UpgradeRequired_426:
1931 return "Upgrade Required";
1932 case StatusCode::PreconditionRequired_428:
1933 return "Precondition Required";
1934 case StatusCode::TooManyRequests_429:
1935 return "Too Many Requests";
1936 case StatusCode::RequestHeaderFieldsTooLarge_431:
1937 return "Request Header Fields Too Large";
1938 case StatusCode::UnavailableForLegalReasons_451:
1939 return "Unavailable For Legal Reasons";
1940 case StatusCode::NotImplemented_501:
1941 return "Not Implemented";
1942 case StatusCode::BadGateway_502:
1943 return "Bad Gateway";
1944 case StatusCode::ServiceUnavailable_503:
1945 return "Service Unavailable";
1946 case StatusCode::GatewayTimeout_504:
1947 return "Gateway Timeout";
1948 case StatusCode::HttpVersionNotSupported_505:
1949 return "HTTP Version Not Supported";
1950 case StatusCode::VariantAlsoNegotiates_506:
1951 return "Variant Also Negotiates";
1952 case StatusCode::InsufficientStorage_507:
1953 return "Insufficient Storage";
1954 case StatusCode::LoopDetected_508:
1955 return "Loop Detected";
1956 case StatusCode::NotExtended_510:
1957 return "Not Extended";
1958 case StatusCode::NetworkAuthenticationRequired_511:
1959 return "Network Authentication Required";
1962 case StatusCode::InternalServerError_500:
1963 return "Internal Server Error";
1967inline std::string get_bearer_token_auth(
const Request &req) {
1968 if (req.has_header(
"Authorization")) {
1969 static std::string BearerHeaderPrefix =
"Bearer ";
1970 return req.get_header_value(
"Authorization").substr(BearerHeaderPrefix.length());
1975template<
class Rep,
class Period>
1976inline Server &Server::set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
1977 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
1981template<
class Rep,
class Period>
1982inline Server &Server::set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
1983 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
1987template<
class Rep,
class Period>
1988inline Server &Server::set_idle_interval(
const std::chrono::duration<Rep, Period> &
duration) {
1989 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
1993inline std::string to_string(
const Error error) {
1995 case Error::Success:
1996 return "Success (no error)";
1997 case Error::Connection:
1998 return "Could not establish connection";
1999 case Error::BindIPAddress:
2000 return "Failed to bind IP address";
2002 return "Failed to read connection";
2004 return "Failed to write connection";
2005 case Error::ExceedRedirectCount:
2006 return "Maximum redirect count exceeded";
2007 case Error::Canceled:
2008 return "Connection handling canceled";
2009 case Error::SSLConnection:
2010 return "SSL connection failed";
2011 case Error::SSLLoadingCerts:
2012 return "SSL certificate loading failed";
2013 case Error::SSLServerVerification:
2014 return "SSL server verification failed";
2015 case Error::SSLServerHostnameVerification:
2016 return "SSL server hostname verification failed";
2017 case Error::UnsupportedMultipartBoundaryChars:
2018 return "Unsupported HTTP multipart boundary characters";
2019 case Error::Compression:
2020 return "Compression failed";
2021 case Error::ConnectionTimeout:
2022 return "Connection timed out";
2023 case Error::ProxyConnection:
2024 return "Proxy connection failed";
2025 case Error::Unknown:
2034inline std::ostream &operator<<(std::ostream &os,
const Error &obj) {
2035 os << to_string(obj);
2036 os <<
" (" <<
static_cast<std::underlying_type<Error>::type
>(obj) <<
')';
2040inline uint64_t Result::get_request_header_value_u64(
const std::string &key, uint64_t def,
size_t id)
const {
2041 return detail::get_header_value_u64(request_headers_, key, def,
id);
2044template<
class Rep,
class Period>
2045inline void ClientImpl::set_connection_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2046 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_connection_timeout(sec, usec); });
2049template<
class Rep,
class Period>
2050inline void ClientImpl::set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2051 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2054template<
class Rep,
class Period>
2055inline void ClientImpl::set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2056 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2059template<
class Rep,
class Period>
2060inline void Client::set_connection_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2061 cli_->set_connection_timeout(
duration);
2064template<
class Rep,
class Period>
2065inline void Client::set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2069template<
class Rep,
class Period>
2070inline void Client::set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2079std::string hosted_at(
const std::string &hostname);
2081void hosted_at(
const std::string &hostname, std::vector<std::string> &addrs);
2083std::string append_query_params(
const std::string &path,
const Params ¶ms);
2085std::pair<std::string, std::string> make_range_header(
const Ranges &ranges);
2087std::pair<std::string, std::string> make_basic_authentication_header(
const std::string &username,
2088 const std::string &password,
2089 bool is_proxy =
false);
2094inline std::wstring u8string_to_wstring(
const char *s) {
2096 auto len =
static_cast<int>(strlen(s));
2097 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s,
len,
nullptr, 0);
2100 wlen = ::MultiByteToWideChar(CP_UTF8, 0, s,
len,
const_cast<LPWSTR
>(
reinterpret_cast<LPCWSTR
>(ws.data())), wlen);
2101 if (wlen !=
static_cast<int>(ws.size())) {
2110 FileStat(
const std::string &path);
2111 bool is_file()
const;
2112 bool is_dir()
const;
2123std::string encode_query_param(
const std::string &value);
2125std::string decode_url(
const std::string &s,
bool convert_plus_to_space);
2127void read_file(
const std::string &path, std::string &out);
2129std::string trim_copy(
const std::string &s);
2131void divide(
const char *data, std::size_t size,
char d,
2132 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)> fn);
2134void divide(
const std::string &str,
char d,
2135 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)> fn);
2137void split(
const char *b,
const char *e,
char d, std::function<
void(
const char *,
const char *)> fn);
2139void split(
const char *b,
const char *e,
char d,
size_t m, std::function<
void(
const char *,
const char *)> fn);
2141bool process_client_socket(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
2142 time_t write_timeout_usec, std::function<
bool(Stream &)> callback);
2144socket_t create_client_socket(
const std::string &host,
const std::string &ip,
int port,
int address_family,
2145 bool tcp_nodelay,
bool ipv6_v6only, SocketOptions socket_options,
2146 time_t connection_timeout_sec, time_t connection_timeout_usec, time_t read_timeout_sec,
2147 time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec,
2148 const std::string &intf, Error &error);
2150const char *get_header_value(
const Headers &headers,
const std::string &key,
const char *def,
size_t id);
2152std::string params_to_query_str(
const Params ¶ms);
2154void parse_query_text(
const char *data, std::size_t size, Params ¶ms);
2156void parse_query_text(
const std::string &s, Params ¶ms);
2158bool parse_multipart_boundary(
const std::string &content_type, std::string &boundary);
2160bool parse_range_header(
const std::string &s, Ranges &ranges);
2164ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
int flags);
2168enum class EncodingType { None = 0, Gzip, Brotli };
2170EncodingType encoding_type(
const Request &req,
const Response &res);
2172class BufferStream final :
public Stream {
2174 BufferStream() =
default;
2175 ~BufferStream()
override =
default;
2177 bool is_readable()
const override;
2178 bool is_writable()
const override;
2179 ssize_t read(
char *ptr,
size_t size)
override;
2180 ssize_t write(
const char *ptr,
size_t size)
override;
2181 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
2182 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
2185 const std::string &get_buffer()
const;
2194 virtual ~compressor() =
default;
2196 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
2197 virtual bool compress(
const char *data,
size_t data_length,
bool last, Callback callback) = 0;
2202 virtual ~decompressor() =
default;
2204 virtual bool is_valid()
const = 0;
2206 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
2207 virtual bool decompress(
const char *data,
size_t data_length, Callback callback) = 0;
2210class nocompressor final :
public compressor {
2212 ~nocompressor()
override =
default;
2214 bool compress(
const char *data,
size_t data_length,
bool , Callback callback)
override;
2217#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2218class gzip_compressor final :
public compressor {
2221 ~gzip_compressor()
override;
2223 bool compress(
const char *data,
size_t data_length,
bool last, Callback callback)
override;
2226 bool is_valid_ =
false;
2230class gzip_decompressor final :
public decompressor {
2232 gzip_decompressor();
2233 ~gzip_decompressor()
override;
2235 bool is_valid()
const override;
2237 bool decompress(
const char *data,
size_t data_length, Callback callback)
override;
2240 bool is_valid_ =
false;
2245#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2246class brotli_compressor final :
public compressor {
2248 brotli_compressor();
2249 ~brotli_compressor();
2251 bool compress(
const char *data,
size_t data_length,
bool last, Callback callback)
override;
2254 BrotliEncoderState *state_ =
nullptr;
2257class brotli_decompressor final :
public decompressor {
2259 brotli_decompressor();
2260 ~brotli_decompressor();
2262 bool is_valid()
const override;
2264 bool decompress(
const char *data,
size_t data_length, Callback callback)
override;
2267 BrotliDecoderResult decoder_r;
2268 BrotliDecoderState *decoder_s =
nullptr;
2274class stream_line_reader {
2276 stream_line_reader(Stream &strm,
char *fixed_buffer,
size_t fixed_buffer_size);
2277 const char *ptr()
const;
2278 size_t size()
const;
2279 bool end_with_crlf()
const;
2283 void append(
char c);
2286 char *fixed_buffer_;
2287 const size_t fixed_buffer_size_;
2288 size_t fixed_buffer_used_size_ = 0;
2289 std::string glowable_buffer_;
2294 mmap(
const char *path);
2297 bool open(
const char *path);
2300 bool is_open()
const;
2301 size_t size()
const;
2302 const char *data()
const;
2306 HANDLE hFile_ = NULL;
2307 HANDLE hMapping_ = NULL;
2312 void *addr_ =
nullptr;
2313 bool is_open_empty_file =
false;
2326inline bool is_hex(
char c,
int &v) {
2327 if (0x20 <= c && isdigit(c)) {
2330 }
else if (
'A' <= c && c <=
'F') {
2333 }
else if (
'a' <= c && c <=
'f') {
2340inline bool from_hex_to_i(
const std::string &s,
size_t i,
size_t cnt,
int &
val) {
2341 if (i >= s.size()) {
2346 for (; cnt; i++, cnt--) {
2351 if (is_hex(s[i], v)) {
2360inline std::string from_i_to_hex(
size_t n) {
2361 static const auto charset =
"0123456789abcdef";
2364 ret = charset[n & 15] + ret;
2370inline size_t to_utf8(
int code,
char *buff) {
2371 if (code < 0x0080) {
2372 buff[0] =
static_cast<char>(code & 0x7F);
2374 }
else if (code < 0x0800) {
2375 buff[0] =
static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
2376 buff[1] =
static_cast<char>(0x80 | (code & 0x3F));
2378 }
else if (code < 0xD800) {
2379 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2380 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2381 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
2383 }
else if (code < 0xE000) {
2385 }
else if (code < 0x10000) {
2386 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2387 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2388 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
2390 }
else if (code < 0x110000) {
2391 buff[0] =
static_cast<char>(0xF0 | ((code >> 18) & 0x7));
2392 buff[1] =
static_cast<char>(0x80 | ((code >> 12) & 0x3F));
2393 buff[2] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2394 buff[3] =
static_cast<char>(0x80 | (code & 0x3F));
2404inline std::string base64_encode(
const std::string &in) {
2405 static const auto lookup =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2408 out.reserve(in.size());
2417 out.push_back(lookup[(
val >> valb) & 0x3F]);
2423 out.push_back(lookup[((
val << 8) >> (valb + 8)) & 0x3F]);
2426 while (out.size() % 4) {
2433inline bool is_valid_path(
const std::string &path) {
2438 while (i < path.size() && path[i] ==
'/') {
2442 while (i < path.size()) {
2445 while (i < path.size() && path[i] !=
'/') {
2446 if (path[i] ==
'\0') {
2448 }
else if (path[i] ==
'\\') {
2457 if (!path.compare(beg,
len,
".")) {
2459 }
else if (!path.compare(beg,
len,
"..")) {
2469 while (i < path.size() && path[i] ==
'/') {
2477inline FileStat::FileStat(
const std::string &path) {
2479 auto wpath = u8string_to_wstring(path.c_str());
2480 ret_ = _wstat(wpath.c_str(), &st_);
2482 ret_ = stat(path.c_str(), &st_);
2485inline bool FileStat::is_file()
const {
return ret_ >= 0 && S_ISREG(st_.st_mode); }
2486inline bool FileStat::is_dir()
const {
return ret_ >= 0 && S_ISDIR(st_.st_mode); }
2488inline std::string encode_query_param(
const std::string &value) {
2489 std::ostringstream escaped;
2491 escaped << std::hex;
2493 for (
auto c : value) {
2494 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' || c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' ||
2495 c ==
'\'' || c ==
'(' || c ==
')') {
2498 escaped << std::uppercase;
2499 escaped << '%' << std::setw(2) << static_cast<int>(
static_cast<unsigned char>(c));
2500 escaped << std::nouppercase;
2504 return escaped.str();
2507inline std::string encode_url(
const std::string &s) {
2509 result.reserve(s.size());
2511 for (
size_t i = 0; s[i]; i++) {
2536 auto c =
static_cast<uint8_t
>(s[i]);
2540 auto len = snprintf(hex,
sizeof(hex) - 1,
"%02X", c);
2542 result.append(hex,
static_cast<size_t>(
len));
2553inline std::string decode_url(
const std::string &s,
bool convert_plus_to_space) {
2556 for (
size_t i = 0; i < s.size(); i++) {
2557 if (s[i] ==
'%' && i + 1 < s.size()) {
2558 if (s[i + 1] ==
'u') {
2560 if (from_hex_to_i(s, i + 2, 4,
val)) {
2563 size_t len = to_utf8(
val, buff);
2565 result.append(buff,
len);
2573 if (from_hex_to_i(s, i + 1, 2,
val)) {
2575 result +=
static_cast<char>(
val);
2581 }
else if (convert_plus_to_space && s[i] ==
'+') {
2591inline void read_file(
const std::string &path, std::string &out) {
2592 std::ifstream fs(path, std::ios_base::binary);
2593 fs.seekg(0, std::ios_base::end);
2594 auto size = fs.tellg();
2596 out.resize(
static_cast<size_t>(size));
2597 fs.read(&out[0],
static_cast<std::streamsize
>(size));
2600inline std::string file_extension(
const std::string &path) {
2602 static auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
2603 if (std::regex_search(path,
m, re)) {
2606 return std::string();
2609inline bool is_space_or_tab(
char c) {
return c ==
' ' || c ==
'\t'; }
2611inline std::pair<size_t, size_t> trim(
const char *b,
const char *e,
size_t left,
size_t right) {
2612 while (b + left < e && is_space_or_tab(b[left])) {
2615 while (right > 0 && is_space_or_tab(b[right - 1])) {
2618 return std::make_pair(left, right);
2621inline std::string trim_copy(
const std::string &s) {
2622 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
2623 return s.substr(r.first, r.second - r.first);
2626inline std::string trim_double_quotes_copy(
const std::string &s) {
2627 if (s.length() >= 2 && s.front() ==
'"' && s.back() ==
'"') {
2628 return s.substr(1, s.size() - 2);
2633inline void divide(
const char *data, std::size_t size,
char d,
2634 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)> fn) {
2635 const auto it = std::find(data, data + size, d);
2636 const auto found =
static_cast<std::size_t
>(it != data + size);
2637 const auto lhs_data = data;
2638 const auto lhs_size =
static_cast<std::size_t
>(it - data);
2639 const auto rhs_data = it + found;
2640 const auto rhs_size = size - lhs_size - found;
2642 fn(lhs_data, lhs_size, rhs_data, rhs_size);
2645inline void divide(
const std::string &str,
char d,
2646 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)> fn) {
2647 divide(str.data(), str.size(), d, std::move(fn));
2650inline void split(
const char *b,
const char *e,
char d, std::function<
void(
const char *,
const char *)> fn) {
2651 return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
2654inline void split(
const char *b,
const char *e,
char d,
size_t m, std::function<
void(
const char *,
const char *)> fn) {
2659 while (e ? (b + i < e) : (b[i] !=
'\0')) {
2660 if (b[i] == d && count <
m) {
2661 auto r = trim(b, e, beg, i);
2662 if (r.first < r.second) {
2663 fn(&b[r.first], &b[r.second]);
2672 auto r = trim(b, e, beg, i);
2673 if (r.first < r.second) {
2674 fn(&b[r.first], &b[r.second]);
2679inline stream_line_reader::stream_line_reader(Stream &strm,
char *fixed_buffer,
size_t fixed_buffer_size)
2680 : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size) {}
2682inline const char *stream_line_reader::ptr()
const {
2683 if (glowable_buffer_.empty()) {
2684 return fixed_buffer_;
2686 return glowable_buffer_.data();
2690inline size_t stream_line_reader::size()
const {
2691 if (glowable_buffer_.empty()) {
2692 return fixed_buffer_used_size_;
2694 return glowable_buffer_.size();
2698inline bool stream_line_reader::end_with_crlf()
const {
2699 auto end = ptr() + size();
2700 return size() >= 2 &&
end[-2] ==
'\r' &&
end[-1] ==
'\n';
2703inline bool stream_line_reader::getline() {
2704 fixed_buffer_used_size_ = 0;
2705 glowable_buffer_.clear();
2707#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2711 for (
size_t i = 0;; i++) {
2713 auto n = strm_.read(&
byte, 1);
2717 }
else if (n == 0) {
2727#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2732 if (prev_byte ==
'\r' &&
byte ==
'\n') {
2742inline void stream_line_reader::append(
char c) {
2743 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
2744 fixed_buffer_[fixed_buffer_used_size_++] = c;
2745 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
2747 if (glowable_buffer_.empty()) {
2748 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
2749 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
2751 glowable_buffer_ += c;
2755inline mmap::mmap(
const char *path) { open(path); }
2757inline mmap::~mmap() { close(); }
2759inline bool mmap::open(
const char *path) {
2763 auto wpath = u8string_to_wstring(path);
2764 if (wpath.empty()) {
2768#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2769 hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, NULL);
2772 ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2775 if (hFile_ == INVALID_HANDLE_VALUE) {
2779 LARGE_INTEGER size{};
2780 if (!::GetFileSizeEx(hFile_, &size)) {
2786 if (
static_cast<ULONGLONG
>(size.QuadPart) > (std::numeric_limits<
decltype(size_)>::max)()) {
2790 size_ =
static_cast<size_t>(size.QuadPart);
2792#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2793 hMapping_ = ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
2795 hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);
2799 if (hMapping_ == NULL && size_ == 0) {
2801 is_open_empty_file =
true;
2805 if (hMapping_ == NULL) {
2810#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2811 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
2813 addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);
2816 if (addr_ ==
nullptr) {
2821 fd_ = ::open(path, O_RDONLY);
2827 if (fstat(fd_, &sb) == -1) {
2831 size_ =
static_cast<size_t>(sb.st_size);
2833 addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
2836 if (addr_ == MAP_FAILED && size_ == 0) {
2838 is_open_empty_file =
true;
2846inline bool mmap::is_open()
const {
return is_open_empty_file ? true : addr_ !=
nullptr; }
2848inline size_t mmap::size()
const {
return size_; }
2850inline const char *mmap::data()
const {
return is_open_empty_file ?
"" :
static_cast<const char *
>(addr_); }
2852inline void mmap::close() {
2855 ::UnmapViewOfFile(addr_);
2860 ::CloseHandle(hMapping_);
2864 if (hFile_ != INVALID_HANDLE_VALUE) {
2865 ::CloseHandle(hFile_);
2866 hFile_ = INVALID_HANDLE_VALUE;
2869 is_open_empty_file =
false;
2871 if (addr_ !=
nullptr) {
2872 munmap(addr_, size_);
2883inline int close_socket(
socket_t sock) {
2885 return closesocket(sock);
2891template<
typename T>
inline ssize_t handle_EINTR(T fn) {
2895 if (res < 0 && errno == EINTR) {
2896 std::this_thread::sleep_for(std::chrono::microseconds{1});
2904inline ssize_t read_socket(
socket_t sock,
void *ptr,
size_t size,
int flags) {
2905 return handle_EINTR([&]() {
2908 static_cast<char *
>(ptr),
static_cast<int>(size),
2916inline ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
int flags) {
2917 return handle_EINTR([&]() {
2920 static_cast<const char *
>(ptr),
static_cast<int>(size),
2929#ifdef CPPHTTPLIB_USE_POLL
2930 struct pollfd pfd_read;
2932 pfd_read.events = POLLIN;
2934 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2936 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2939 if (sock >= FD_SETSIZE) {
2949 tv.tv_sec =
static_cast<long>(sec);
2950 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2952 return handle_EINTR([&]() {
return select(
static_cast<int>(sock + 1), &fds,
nullptr,
nullptr, &tv); });
2957#ifdef CPPHTTPLIB_USE_POLL
2958 struct pollfd pfd_read;
2960 pfd_read.events = POLLOUT;
2962 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2964 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2967 if (sock >= FD_SETSIZE) {
2977 tv.tv_sec =
static_cast<long>(sec);
2978 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2980 return handle_EINTR([&]() {
return select(
static_cast<int>(sock + 1),
nullptr, &fds,
nullptr, &tv); });
2984inline Error wait_until_socket_is_ready(
socket_t sock, time_t sec, time_t usec) {
2985#ifdef CPPHTTPLIB_USE_POLL
2986 struct pollfd pfd_read;
2988 pfd_read.events = POLLIN | POLLOUT;
2990 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2992 auto poll_res = handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2994 if (poll_res == 0) {
2995 return Error::ConnectionTimeout;
2998 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
3001 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *
>(&error), &
len);
3002 auto successful = res >= 0 && !error;
3003 return successful ? Error::Success : Error::Connection;
3006 return Error::Connection;
3009 if (sock >= FD_SETSIZE) {
3010 return Error::Connection;
3016 FD_SET(sock, &fdsr);
3022 tv.tv_sec =
static_cast<long>(sec);
3023 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
3025 auto ret = handle_EINTR([&]() {
return select(
static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv); });
3028 return Error::ConnectionTimeout;
3031 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
3034 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *
>(&error), &
len);
3035 auto successful = res >= 0 && !error;
3036 return successful ? Error::Success : Error::Connection;
3038 return Error::Connection;
3042inline bool is_socket_alive(
socket_t sock) {
3043 const auto val = detail::select_read(sock, 0, 0);
3046 }
else if (
val < 0 && errno == EBADF) {
3050 return detail::read_socket(sock, &buf[0],
sizeof(buf), MSG_PEEK) > 0;
3053class SocketStream final :
public Stream {
3055 SocketStream(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3056 time_t write_timeout_usec);
3057 ~SocketStream()
override;
3059 bool is_readable()
const override;
3060 bool is_writable()
const override;
3061 ssize_t read(
char *ptr,
size_t size)
override;
3062 ssize_t write(
const char *ptr,
size_t size)
override;
3063 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
3064 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
3069 time_t read_timeout_sec_;
3070 time_t read_timeout_usec_;
3071 time_t write_timeout_sec_;
3072 time_t write_timeout_usec_;
3074 std::vector<char> read_buff_;
3075 size_t read_buff_off_ = 0;
3076 size_t read_buff_content_size_ = 0;
3078 static const size_t read_buff_size_ = 1024l * 4;
3081#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3082class SSLSocketStream final :
public Stream {
3084 SSLSocketStream(
socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3085 time_t write_timeout_usec);
3086 ~SSLSocketStream()
override;
3088 bool is_readable()
const override;
3089 bool is_writable()
const override;
3090 ssize_t read(
char *ptr,
size_t size)
override;
3091 ssize_t write(
const char *ptr,
size_t size)
override;
3092 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
3093 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
3099 time_t read_timeout_sec_;
3100 time_t read_timeout_usec_;
3101 time_t write_timeout_sec_;
3102 time_t write_timeout_usec_;
3106inline bool keep_alive(
const std::atomic<socket_t> &svr_sock,
socket_t sock, time_t keep_alive_timeout_sec) {
3107 using namespace std::chrono;
3109 const auto interval_usec = CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
3112 if (select_read(sock, 0, interval_usec) > 0) {
3116 const auto start = steady_clock::now() - microseconds{interval_usec};
3117 const auto timeout = seconds{keep_alive_timeout_sec};
3120 if (svr_sock == INVALID_SOCKET) {
3124 auto val = select_read(sock, 0, interval_usec);
3127 }
else if (
val == 0) {
3128 if (steady_clock::now() - start > timeout) {
3140inline bool process_server_socket_core(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
3141 size_t keep_alive_max_count, time_t keep_alive_timeout_sec, T callback) {
3142 assert(keep_alive_max_count > 0);
3144 auto count = keep_alive_max_count;
3145 while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
3146 auto close_connection = count == 1;
3147 auto connection_closed =
false;
3148 ret = callback(close_connection, connection_closed);
3149 if (!ret || connection_closed) {
3158inline bool process_server_socket(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
size_t keep_alive_max_count,
3159 time_t keep_alive_timeout_sec, time_t read_timeout_sec, time_t read_timeout_usec,
3160 time_t write_timeout_sec, time_t write_timeout_usec, T callback) {
3161 return process_server_socket_core(svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
3162 [&](
bool close_connection,
bool &connection_closed) {
3163 SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec,
3164 write_timeout_usec);
3165 return callback(strm, close_connection, connection_closed);
3169inline bool process_client_socket(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3170 time_t write_timeout_sec, time_t write_timeout_usec,
3171 std::function<
bool(Stream &)> callback) {
3172 SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);
3173 return callback(strm);
3176inline int shutdown_socket(
socket_t sock) {
3178 return shutdown(sock, SD_BOTH);
3180 return shutdown(sock, SHUT_RDWR);
3184inline std::string escape_abstract_namespace_unix_domain(
const std::string &s) {
3185 if (s.size() > 1 && s[0] ==
'\0') {
3193inline std::string unescape_abstract_namespace_unix_domain(
const std::string &s) {
3194 if (s.size() > 1 && s[0] ==
'@') {
3202template<
typename BindOrConnect>
3203socket_t create_socket(
const std::string &host,
const std::string &ip,
int port,
int address_family,
int socket_flags,
3204 bool tcp_nodelay,
bool ipv6_v6only, SocketOptions socket_options,
3205 BindOrConnect bind_or_connect) {
3207 const char *node =
nullptr;
3208 struct addrinfo hints;
3209 struct addrinfo *result;
3211 memset(&hints, 0,
sizeof(
struct addrinfo));
3212 hints.ai_socktype = SOCK_STREAM;
3213 hints.ai_protocol = IPPROTO_IP;
3218 hints.ai_family = AF_UNSPEC;
3219 hints.ai_flags = AI_NUMERICHOST;
3221 if (!host.empty()) {
3222 node = host.c_str();
3224 hints.ai_family = address_family;
3225 hints.ai_flags = socket_flags;
3229 if (hints.ai_family == AF_UNIX) {
3230 const auto addrlen = host.length();
3231 if (addrlen >
sizeof(sockaddr_un::sun_path)) {
3232 return INVALID_SOCKET;
3236 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC, hints.ai_protocol);
3238 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
3241 if (sock != INVALID_SOCKET) {
3243 addr.sun_family = AF_UNIX;
3245 auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
3246 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
3248 hints.ai_addr =
reinterpret_cast<sockaddr *
>(&addr);
3249 hints.ai_addrlen =
static_cast<socklen_t>(
sizeof(addr) -
sizeof(addr.sun_path) + addrlen);
3252 fcntl(sock, F_SETFD, FD_CLOEXEC);
3255 if (socket_options) {
3256 socket_options(sock);
3260 if (!bind_or_connect(sock, hints, dummy)) {
3262 sock = INVALID_SOCKET;
3269 auto service = std::to_string(port);
3271 if (getaddrinfo(node, service.c_str(), &hints, &result)) {
3272#if defined __linux__ && !defined __ANDROID__
3275 return INVALID_SOCKET;
3277 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3279 for (
auto rp = result; rp; rp = rp->ai_next) {
3282 auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0,
3283 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
3298 if (sock == INVALID_SOCKET) {
3299 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3304 auto sock = socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
3306 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3310 if (sock == INVALID_SOCKET) {
3314#if !defined _WIN32 && !defined SOCK_CLOEXEC
3315 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
3324 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<const char *
>(&opt),
sizeof(opt));
3326 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<const void *
>(&opt),
sizeof(opt));
3330 if (rp->ai_family == AF_INET6) {
3331 auto opt = ipv6_v6only ? 1 : 0;
3333 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const char *
>(&opt),
sizeof(opt));
3335 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const void *
>(&opt),
sizeof(opt));
3339 if (socket_options) {
3340 socket_options(sock);
3345 if (bind_or_connect(sock, *rp, quit)) {
3356 return INVALID_SOCKET;
3359inline void set_nonblocking(
socket_t sock,
bool nonblocking) {
3361 auto flags = nonblocking ? 1UL : 0UL;
3362 ioctlsocket(sock, FIONBIO, &flags);
3364 auto flags = fcntl(sock, F_GETFL, 0);
3365 fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
3369inline bool is_connection_error() {
3371 return WSAGetLastError() != WSAEWOULDBLOCK;
3373 return errno != EINPROGRESS;
3377inline bool bind_ip_address(
socket_t sock,
const std::string &host) {
3378 struct addrinfo hints;
3379 struct addrinfo *result;
3381 memset(&hints, 0,
sizeof(
struct addrinfo));
3382 hints.ai_family = AF_UNSPEC;
3383 hints.ai_socktype = SOCK_STREAM;
3384 hints.ai_protocol = 0;
3386 if (getaddrinfo(host.c_str(),
"0", &hints, &result)) {
3389 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3392 for (
auto rp = result; rp; rp = rp->ai_next) {
3393 const auto &ai = *rp;
3394 if (!::bind(sock, ai.ai_addr,
static_cast<socklen_t>(ai.ai_addrlen))) {
3403#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
3408inline std::string if2ip(
int address_family,
const std::string &ifn) {
3409 struct ifaddrs *ifap;
3411 auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
3413 std::string addr_candidate;
3414 for (
auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
3415 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
3416 (AF_UNSPEC == address_family || ifa->ifa_addr->sa_family == address_family)) {
3417 if (ifa->ifa_addr->sa_family == AF_INET) {
3418 auto sa =
reinterpret_cast<struct
sockaddr_in *
>(ifa->ifa_addr);
3419 char buf[INET_ADDRSTRLEN];
3420 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
3421 return std::string(buf, INET_ADDRSTRLEN);
3423 }
else if (ifa->ifa_addr->sa_family == AF_INET6) {
3424 auto sa =
reinterpret_cast<struct
sockaddr_in6 *
>(ifa->ifa_addr);
3425 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
3426 char buf[INET6_ADDRSTRLEN] = {};
3427 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
3429 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
3430 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
3431 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
3433 return std::string(buf, INET6_ADDRSTRLEN);
3440 return addr_candidate;
3444inline socket_t create_client_socket(
const std::string &host,
const std::string &ip,
int port,
int address_family,
3445 bool tcp_nodelay,
bool ipv6_v6only, SocketOptions socket_options,
3446 time_t connection_timeout_sec, time_t connection_timeout_usec,
3447 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3448 time_t write_timeout_usec,
const std::string &intf, Error &error) {
3449 auto sock = create_socket(
3450 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only, std::move(socket_options),
3451 [&](
socket_t sock2,
struct addrinfo &ai,
bool &quit) ->
bool {
3452 if (!intf.empty()) {
3454 auto ip_from_if = if2ip(address_family, intf);
3455 if (ip_from_if.empty()) {
3458 if (!bind_ip_address(sock2, ip_from_if)) {
3459 error = Error::BindIPAddress;
3465 set_nonblocking(sock2,
true);
3467 auto ret = ::connect(sock2, ai.ai_addr,
static_cast<socklen_t>(ai.ai_addrlen));
3470 if (is_connection_error()) {
3471 error = Error::Connection;
3474 error = wait_until_socket_is_ready(sock2, connection_timeout_sec, connection_timeout_usec);
3475 if (error != Error::Success) {
3476 if (error == Error::ConnectionTimeout) {
3483 set_nonblocking(sock2,
false);
3487 auto timeout =
static_cast<uint32_t
>(read_timeout_sec * 1000 + read_timeout_usec / 1000);
3488 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
3491 tv.tv_sec =
static_cast<long>(read_timeout_sec);
3492 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(read_timeout_usec);
3493 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
3499 auto timeout =
static_cast<uint32_t
>(write_timeout_sec * 1000 + write_timeout_usec / 1000);
3500 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
3503 tv.tv_sec =
static_cast<long>(write_timeout_sec);
3504 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(write_timeout_usec);
3505 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
3509 error = Error::Success;
3513 if (sock != INVALID_SOCKET) {
3514 error = Error::Success;
3516 if (error == Error::Success) {
3517 error = Error::Connection;
3526 port = ntohs(
reinterpret_cast<const struct
sockaddr_in *
>(&addr)->sin_port);
3527 }
else if (addr.
ss_family == AF_INET6) {
3528 port = ntohs(
reinterpret_cast<const struct
sockaddr_in6 *
>(&addr)->sin6_port);
3533 std::array<char, NI_MAXHOST> ipstr{};
3534 if (getnameinfo(
reinterpret_cast<const struct
sockaddr *
>(&addr), addr_len, ipstr.data(),
3535 static_cast<socklen_t>(ipstr.size()),
nullptr, 0, NI_NUMERICHOST)) {
3543inline void get_local_ip_and_port(
socket_t sock, std::string &ip,
int &port) {
3546 if (!getsockname(sock,
reinterpret_cast<struct
sockaddr *
>(&addr), &addr_len)) {
3547 get_ip_and_port(addr, addr_len, ip, port);
3551inline void get_remote_ip_and_port(
socket_t sock, std::string &ip,
int &port) {
3555 if (!getpeername(sock,
reinterpret_cast<struct
sockaddr *
>(&addr), &addr_len)) {
3558#if defined(__linux__)
3561 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &
len) == 0) {
3564#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
3567 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &
len) == 0) {
3574 get_ip_and_port(addr, addr_len, ip, port);
3578inline constexpr unsigned int str2tag_core(
const char *s,
size_t l,
unsigned int h) {
3583 (((std::numeric_limits<unsigned int>::max)() >> 6) &
h * 33) ^
static_cast<unsigned char>(*s));
3586inline unsigned int str2tag(
const std::string &s) {
return str2tag_core(s.data(), s.size(), 0); }
3590inline constexpr unsigned int operator""_t(
const char *s,
size_t l) {
return str2tag_core(s,
l, 0); }
3594inline std::string find_content_type(
const std::string &path,
const std::map<std::string, std::string> &user_data,
3595 const std::string &default_content_type) {
3596 auto ext = file_extension(path);
3598 auto it = user_data.find(ext);
3599 if (it != user_data.end()) {
3603 using udl::operator
""_t;
3605 switch (str2tag(ext)) {
3607 return default_content_type;
3618 return "text/javascript";
3620 return "text/plain";
3625 return "image/apng";
3627 return "image/avif";
3635 return "image/svg+xml";
3637 return "image/webp";
3639 return "image/x-icon";
3641 return "image/tiff";
3643 return "image/tiff";
3646 return "image/jpeg";
3651 return "video/mpeg";
3653 return "video/webm";
3658 return "audio/mpeg";
3660 return "audio/webm";
3662 return "audio/wave";
3671 return "font/woff2";
3674 return "application/x-7z-compressed";
3676 return "application/atom+xml";
3678 return "application/pdf";
3680 return "application/json";
3682 return "application/rss+xml";
3684 return "application/x-tar";
3687 return "application/xhtml+xml";
3689 return "application/xslt+xml";
3691 return "application/xml";
3693 return "application/gzip";
3695 return "application/zip";
3697 return "application/wasm";
3701inline bool can_compress_content_type(
const std::string &content_type) {
3702 using udl::operator
""_t;
3704 auto tag = str2tag(content_type);
3707 case "image/svg+xml"_t:
3708 case "application/javascript"_t:
3709 case "application/json"_t:
3710 case "application/xml"_t:
3711 case "application/protobuf"_t:
3712 case "application/xhtml+xml"_t:
3715 case "text/event-stream"_t:
3719 return !content_type.rfind(
"text/", 0);
3723inline EncodingType encoding_type(
const Request &req,
const Response &res) {
3724 auto ret = detail::can_compress_content_type(res.get_header_value(
"Content-Type"));
3726 return EncodingType::None;
3729 const auto &s = req.get_header_value(
"Accept-Encoding");
3732#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3734 ret = s.find(
"br") != std::string::npos;
3736 return EncodingType::Brotli;
3740#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3742 ret = s.find(
"gzip") != std::string::npos;
3744 return EncodingType::Gzip;
3748 return EncodingType::None;
3751inline bool nocompressor::compress(
const char *data,
size_t data_length,
bool , Callback callback) {
3755 return callback(data, data_length);
3758#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3759inline gzip_compressor::gzip_compressor() {
3760 std::memset(&strm_, 0,
sizeof(strm_));
3761 strm_.zalloc = Z_NULL;
3762 strm_.zfree = Z_NULL;
3763 strm_.opaque = Z_NULL;
3765 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK;
3768inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
3770inline bool gzip_compressor::compress(
const char *data,
size_t data_length,
bool last, Callback callback) {
3774 constexpr size_t max_avail_in = (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
3776 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>((std::min)(data_length, max_avail_in));
3777 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
3779 data_length -= strm_.avail_in;
3780 data += strm_.avail_in;
3782 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
3785 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3787 strm_.avail_out =
static_cast<uInt
>(buff.size());
3788 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
3790 ret = deflate(&strm_, flush);
3791 if (ret == Z_STREAM_ERROR) {
3795 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
3798 }
while (strm_.avail_out == 0);
3800 assert((flush == Z_FINISH && ret == Z_STREAM_END) || (flush == Z_NO_FLUSH && ret == Z_OK));
3801 assert(strm_.avail_in == 0);
3802 }
while (data_length > 0);
3807inline gzip_decompressor::gzip_decompressor() {
3808 std::memset(&strm_, 0,
sizeof(strm_));
3809 strm_.zalloc = Z_NULL;
3810 strm_.zfree = Z_NULL;
3811 strm_.opaque = Z_NULL;
3817 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
3820inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
3822inline bool gzip_decompressor::is_valid()
const {
return is_valid_; }
3824inline bool gzip_decompressor::decompress(
const char *data,
size_t data_length, Callback callback) {
3830 constexpr size_t max_avail_in = (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
3832 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>((std::min)(data_length, max_avail_in));
3833 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
3835 data_length -= strm_.avail_in;
3836 data += strm_.avail_in;
3838 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3839 while (strm_.avail_in > 0 && ret == Z_OK) {
3840 strm_.avail_out = static_cast<uInt>(buff.size());
3841 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
3843 ret = inflate(&strm_, Z_NO_FLUSH);
3845 assert(ret != Z_STREAM_ERROR);
3854 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
3859 if (ret != Z_OK && ret != Z_STREAM_END) {
3863 }
while (data_length > 0);
3869#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3870inline brotli_compressor::brotli_compressor() { state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr); }
3872inline brotli_compressor::~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
3874inline bool brotli_compressor::compress(
const char *data,
size_t data_length,
bool last, Callback callback) {
3875 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3877 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
3878 auto available_in = data_length;
3879 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
3883 if (BrotliEncoderIsFinished(state_)) {
3887 if (!available_in) {
3892 auto available_out = buff.size();
3893 auto next_out = buff.data();
3895 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in, &available_out, &next_out,
nullptr)) {
3899 auto output_bytes = buff.size() - available_out;
3901 callback(
reinterpret_cast<const char *
>(buff.data()), output_bytes);
3908inline brotli_decompressor::brotli_decompressor() {
3909 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
3910 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT : BROTLI_DECODER_RESULT_ERROR;
3913inline brotli_decompressor::~brotli_decompressor() {
3915 BrotliDecoderDestroyInstance(decoder_s);
3919inline bool brotli_decompressor::is_valid()
const {
return decoder_s; }
3921inline bool brotli_decompressor::decompress(
const char *data,
size_t data_length, Callback callback) {
3922 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || decoder_r == BROTLI_DECODER_RESULT_ERROR) {
3926 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
3927 size_t avail_in = data_length;
3930 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
3932 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3933 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
3934 char *next_out = buff.data();
3935 size_t avail_out = buff.size();
3937 decoder_r = BrotliDecoderDecompressStream(decoder_s, &avail_in, &next_in, &avail_out,
3938 reinterpret_cast<uint8_t **
>(&next_out), &total_out);
3940 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) {
3944 if (!callback(buff.data(), buff.size() - avail_out)) {
3949 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
3953inline bool has_header(
const Headers &headers,
const std::string &key) {
return headers.find(key) != headers.end(); }
3955inline const char *get_header_value(
const Headers &headers,
const std::string &key,
const char *def,
size_t id) {
3956 auto rng = headers.equal_range(key);
3957 auto it = rng.first;
3958 std::advance(it,
static_cast<ssize_t>(
id));
3959 if (it != rng.second) {
3960 return it->second.c_str();
3965template<
typename T>
inline bool parse_header(
const char *beg,
const char *
end, T fn) {
3967 while (beg <
end && is_space_or_tab(
end[-1])) {
3972 while (p <
end && *p !=
':') {
3986 while (p <
end && is_space_or_tab(*p)) {
3991 auto key_len = key_end - beg;
3996 auto key = std::string(beg, key_end);
3997 auto val = case_ignore::equal(key,
"Location") ? std::string(p,
end) : decode_url(std::string(p,
end),
false);
4007 static const std::string CR_LF_NUL(
"\r\n\0", 3);
4008 if (
val.find_first_of(CR_LF_NUL) != std::string::npos) {
4019inline bool read_headers(Stream &strm, Headers &headers) {
4020 const auto bufsiz = 2048;
4022 stream_line_reader line_reader(strm, buf, bufsiz);
4025 if (!line_reader.getline()) {
4030 auto line_terminator_len = 2;
4031 if (line_reader.end_with_crlf()) {
4033 if (line_reader.size() == 2) {
4037#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4039 if (line_reader.size() == 1) {
4042 line_terminator_len = 1;
4048 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4053 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4055 if (!parse_header(line_reader.ptr(),
end,
4056 [&](
const std::string &key, std::string &
val) { headers.emplace(key, val); })) {
4064inline bool read_content_with_length(Stream &strm, uint64_t
len, Progress progress, ContentReceiverWithProgress out) {
4065 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4069 auto read_len =
static_cast<size_t>(
len - r);
4070 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4075 if (!out(buf,
static_cast<size_t>(n), r,
len)) {
4078 r +=
static_cast<uint64_t
>(n);
4081 if (!progress(r,
len)) {
4090inline void skip_content_with_length(Stream &strm, uint64_t
len) {
4091 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4094 auto read_len =
static_cast<size_t>(
len - r);
4095 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4099 r +=
static_cast<uint64_t
>(n);
4103inline bool read_content_without_length(Stream &strm, ContentReceiverWithProgress out) {
4104 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4107 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
4112 if (!out(buf,
static_cast<size_t>(n), r, 0)) {
4115 r +=
static_cast<uint64_t
>(n);
4121template<
typename T>
inline bool read_content_chunked(Stream &strm, T &
x, ContentReceiverWithProgress out) {
4122 const auto bufsiz = 16;
4125 stream_line_reader line_reader(strm, buf, bufsiz);
4127 if (!line_reader.getline()) {
4131 unsigned long chunk_len;
4135 chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
4137 if (end_ptr == line_reader.ptr()) {
4140 if (chunk_len == ULONG_MAX) {
4144 if (chunk_len == 0) {
4148 if (!read_content_with_length(strm, chunk_len,
nullptr, out)) {
4152 if (!line_reader.getline()) {
4156 if (strcmp(line_reader.ptr(),
"\r\n") != 0) {
4160 if (!line_reader.getline()) {
4165 assert(chunk_len == 0);
4179 if (!line_reader.getline()) {
4183 while (strcmp(line_reader.ptr(),
"\r\n") != 0) {
4184 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4189 constexpr auto line_terminator_len = 2;
4190 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4192 parse_header(line_reader.ptr(),
end,
4193 [&](
const std::string &key,
const std::string &
val) { x.headers.emplace(key, val); });
4195 if (!line_reader.getline()) {
4203inline bool is_chunked_transfer_encoding(
const Headers &headers) {
4204 return case_ignore::equal(get_header_value(headers,
"Transfer-Encoding",
"", 0),
"chunked");
4207template<
typename T,
typename U>
4208bool prepare_content_receiver(T &
x,
int &
status, ContentReceiverWithProgress receiver,
bool decompress, U callback) {
4210 std::string encoding =
x.get_header_value(
"Content-Encoding");
4211 std::unique_ptr<decompressor> decompressor;
4213 if (encoding ==
"gzip" || encoding ==
"deflate") {
4214#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4215 decompressor = detail::make_unique<gzip_decompressor>();
4217 status = StatusCode::UnsupportedMediaType_415;
4220 }
else if (encoding.find(
"br") != std::string::npos) {
4221#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4222 decompressor = detail::make_unique<brotli_decompressor>();
4224 status = StatusCode::UnsupportedMediaType_415;
4230 if (decompressor->is_valid()) {
4231 ContentReceiverWithProgress out = [&](
const char *buf,
size_t n, uint64_t off, uint64_t
len) {
4232 return decompressor->decompress(buf, n,
4233 [&](
const char *buf2,
size_t n2) {
return receiver(buf2, n2, off,
len); });
4235 return callback(std::move(out));
4237 status = StatusCode::InternalServerError_500;
4243 ContentReceiverWithProgress out = [&](
const char *buf,
size_t n, uint64_t off, uint64_t
len) {
4244 return receiver(buf, n, off,
len);
4246 return callback(std::move(out));
4250bool read_content(Stream &strm, T &
x,
size_t payload_max_length,
int &
status, Progress progress,
4251 ContentReceiverWithProgress receiver,
bool decompress) {
4252 return prepare_content_receiver(
4253 x,
status, std::move(receiver), decompress, [&](
const ContentReceiverWithProgress &out) {
4255 auto exceed_payload_max_length =
false;
4257 if (is_chunked_transfer_encoding(
x.headers)) {
4258 ret = read_content_chunked(strm,
x, out);
4259 }
else if (!has_header(
x.headers,
"Content-Length")) {
4260 ret = read_content_without_length(strm, out);
4262 auto len = get_header_value_u64(
x.headers,
"Content-Length", 0, 0);
4263 if (
len > payload_max_length) {
4264 exceed_payload_max_length =
true;
4265 skip_content_with_length(strm,
len);
4267 }
else if (
len > 0) {
4268 ret = read_content_with_length(strm,
len, std::move(progress), out);
4273 status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413 : StatusCode::BadRequest_400;
4279inline ssize_t write_request_line(Stream &strm,
const std::string &method,
const std::string &path) {
4280 std::string s = method;
4283 s +=
" HTTP/1.1\r\n";
4284 return strm.write(s.data(), s.size());
4287inline ssize_t write_response_line(Stream &strm,
int status) {
4288 std::string s =
"HTTP/1.1 ";
4289 s += std::to_string(
status);
4291 s += httplib::status_message(
status);
4293 return strm.write(s.data(), s.size());
4296inline ssize_t write_headers(Stream &strm,
const Headers &headers) {
4298 for (
const auto &
x : headers) {
4305 auto len = strm.write(s.data(), s.size());
4311 auto len = strm.write(
"\r\n");
4319inline bool write_data(Stream &strm,
const char *d,
size_t l) {
4321 while (offset <
l) {
4322 auto length = strm.write(d + offset,
l - offset);
4326 offset +=
static_cast<size_t>(
length);
4332inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
size_t offset,
size_t length,
4333 T is_shutting_down, Error &error) {
4334 size_t end_offset = offset +
length;
4338 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
4340 if (strm.is_writable() && write_data(strm, d,
l)) {
4349 data_sink.is_writable = [&]() ->
bool {
return strm.is_writable(); };
4351 while (offset < end_offset && !is_shutting_down()) {
4352 if (!strm.is_writable()) {
4353 error = Error::Write;
4355 }
else if (!content_provider(offset, end_offset - offset, data_sink)) {
4356 error = Error::Canceled;
4359 error = Error::Write;
4364 error = Error::Success;
4369inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
size_t offset,
size_t length,
4370 const T &is_shutting_down) {
4371 auto error = Error::Success;
4372 return write_content(strm, content_provider, offset,
length, is_shutting_down, error);
4376inline bool write_content_without_length(Stream &strm,
const ContentProvider &content_provider,
4377 const T &is_shutting_down) {
4379 auto data_available =
true;
4383 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
4386 if (!strm.is_writable() || !write_data(strm, d,
l)) {
4393 data_sink.is_writable = [&]() ->
bool {
return strm.is_writable(); };
4395 data_sink.done = [&](void) { data_available =
false; };
4397 while (data_available && !is_shutting_down()) {
4398 if (!strm.is_writable()) {
4400 }
else if (!content_provider(offset, 0, data_sink)) {
4409template<
typename T,
typename U>
4410inline bool write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
const T &is_shutting_down,
4411 U &compressor, Error &error) {
4413 auto data_available =
true;
4417 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
4419 data_available =
l > 0;
4422 std::string payload;
4423 if (compressor.compress(d,
l,
false, [&](
const char *data,
size_t data_len) {
4424 payload.append(data, data_len);
4427 if (!payload.empty()) {
4429 auto chunk = from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
4430 if (!strm.is_writable() || !write_data(strm, chunk.data(), chunk.size())) {
4441 data_sink.is_writable = [&]() ->
bool {
return strm.is_writable(); };
4443 auto done_with_trailer = [&](
const Headers *
trailer) {
4448 data_available =
false;
4450 std::string payload;
4451 if (!compressor.compress(
nullptr, 0,
true, [&](
const char *data,
size_t data_len) {
4452 payload.append(data, data_len);
4459 if (!payload.empty()) {
4461 auto chunk = from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
4462 if (!strm.is_writable() || !write_data(strm, chunk.data(), chunk.size())) {
4468 static const std::string done_marker(
"0\r\n");
4469 if (!write_data(strm, done_marker.data(), done_marker.size())) {
4475 for (
const auto &kv : *
trailer) {
4476 std::string field_line = kv.first +
": " + kv.second +
"\r\n";
4477 if (!write_data(strm, field_line.data(), field_line.size())) {
4483 static const std::string crlf(
"\r\n");
4484 if (!write_data(strm, crlf.data(), crlf.size())) {
4489 data_sink.done = [&](void) { done_with_trailer(
nullptr); };
4491 data_sink.done_with_trailer = [&](
const Headers &
trailer) { done_with_trailer(&
trailer); };
4493 while (data_available && !is_shutting_down()) {
4494 if (!strm.is_writable()) {
4495 error = Error::Write;
4497 }
else if (!content_provider(offset, 0, data_sink)) {
4498 error = Error::Canceled;
4501 error = Error::Write;
4506 error = Error::Success;
4510template<
typename T,
typename U>
4511inline bool write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
const T &is_shutting_down,
4513 auto error = Error::Success;
4514 return write_content_chunked(strm, content_provider, is_shutting_down, compressor, error);
4518inline bool redirect(T &cli, Request &req, Response &res,
const std::string &path,
const std::string &location,
4520 Request new_req = req;
4521 new_req.path = path;
4522 new_req.redirect_count_ -= 1;
4524 if (res.status == StatusCode::SeeOther_303 && (req.method !=
"GET" && req.method !=
"HEAD")) {
4525 new_req.method =
"GET";
4526 new_req.body.clear();
4527 new_req.headers.clear();
4532 auto ret = cli.send(new_req, new_res, error);
4537 if (res.location.empty()) {
4538 res.location = location;
4544inline std::string params_to_query_str(
const Params ¶ms) {
4547 for (
auto it = params.begin(); it != params.end(); ++it) {
4548 if (it != params.begin()) {
4553 query += encode_query_param(it->second);
4558inline void parse_query_text(
const char *data, std::size_t size, Params ¶ms) {
4559 std::set<std::string> cache;
4560 split(data, data + size,
'&', [&](
const char *b,
const char *e) {
4561 std::string kv(b, e);
4562 if (cache.find(kv) != cache.end()) {
4565 cache.insert(std::move(kv));
4569 divide(b,
static_cast<std::size_t
>(e - b),
'=',
4570 [&](
const char *lhs_data, std::size_t lhs_size,
const char *rhs_data, std::size_t rhs_size) {
4571 key.assign(lhs_data, lhs_size);
4572 val.assign(rhs_data, rhs_size);
4576 params.emplace(decode_url(key,
true), decode_url(
val,
true));
4581inline void parse_query_text(
const std::string &s, Params ¶ms) { parse_query_text(s.data(), s.size(), params); }
4583inline bool parse_multipart_boundary(
const std::string &content_type, std::string &boundary) {
4584 auto boundary_keyword =
"boundary=";
4585 auto pos = content_type.find(boundary_keyword);
4586 if (pos == std::string::npos) {
4589 auto end = content_type.find(
';', pos);
4590 auto beg = pos + strlen(boundary_keyword);
4591 boundary = trim_double_quotes_copy(content_type.substr(beg,
end - beg));
4592 return !boundary.empty();
4595inline void parse_disposition_params(
const std::string &s, Params ¶ms) {
4596 std::set<std::string> cache;
4597 split(s.data(), s.data() + s.size(),
';', [&](
const char *b,
const char *e) {
4598 std::string kv(b, e);
4599 if (cache.find(kv) != cache.end()) {
4606 split(b, e,
'=', [&](
const char *b2,
const char *e2) {
4615 params.emplace(trim_double_quotes_copy((key)), trim_double_quotes_copy((val)));
4620#ifdef CPPHTTPLIB_NO_EXCEPTIONS
4621inline bool parse_range_header(
const std::string &s, Ranges &ranges) {
4623inline bool parse_range_header(
const std::string &s, Ranges &ranges)
try {
4625 auto is_valid = [](
const std::string &str) {
4626 return std::all_of(str.cbegin(), str.cend(), [](
unsigned char c) { return std::isdigit(c); });
4629 if (s.size() > 7 && s.compare(0, 6,
"bytes=") == 0) {
4630 const auto pos =
static_cast<size_t>(6);
4631 const auto len =
static_cast<size_t>(s.size() - 6);
4632 auto all_valid_ranges =
true;
4633 split(&s[pos], &s[pos +
len],
',', [&](
const char *b,
const char *e) {
4634 if (!all_valid_ranges) {
4638 const auto it = std::find(b, e,
'-');
4640 all_valid_ranges =
false;
4644 const auto lhs = std::string(b, it);
4645 const auto rhs = std::string(it + 1, e);
4646 if (!is_valid(lhs) || !is_valid(rhs)) {
4647 all_valid_ranges =
false;
4651 const auto first =
static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
4652 const auto last =
static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
4653 if ((first == -1 && last == -1) || (first != -1 && last != -1 && first > last)) {
4654 all_valid_ranges =
false;
4658 ranges.emplace_back(first, last);
4660 return all_valid_ranges && !ranges.empty();
4663#ifdef CPPHTTPLIB_NO_EXCEPTIONS
4671class MultipartFormDataParser {
4673 MultipartFormDataParser() =
default;
4675 void set_boundary(std::string &&boundary) {
4676 boundary_ = boundary;
4677 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
4678 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
4681 bool is_valid()
const {
return is_valid_; }
4683 bool parse(
const char *buf,
size_t n,
const ContentReceiver &content_callback,
4684 const MultipartContentHeader &header_callback) {
4687 while (buf_size() > 0) {
4690 buf_erase(buf_find(dash_boundary_crlf_));
4691 if (dash_boundary_crlf_.size() > buf_size()) {
4694 if (!buf_start_with(dash_boundary_crlf_)) {
4697 buf_erase(dash_boundary_crlf_.size());
4707 auto pos = buf_find(crlf_);
4708 if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4711 while (pos < buf_size()) {
4714 if (!header_callback(file_)) {
4718 buf_erase(crlf_.size());
4723 const auto header = buf_head(pos);
4725 if (!parse_header(header.data(), header.data() + header.size(),
4726 [&](
const std::string &,
const std::string &) {})) {
4731 static const std::string header_content_type =
"Content-Type:";
4733 if (start_with_case_ignore(header, header_content_type)) {
4734 file_.content_type = trim_copy(header.substr(header_content_type.size()));
4736 static const std::regex re_content_disposition(R
"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
4737 std::regex_constants::icase);
4740 if (std::regex_match(header,
m, re_content_disposition)) {
4742 parse_disposition_params(
m[1], params);
4744 auto it = params.find(
"name");
4745 if (it != params.end()) {
4746 file_.name = it->second;
4752 it = params.find(
"filename");
4753 if (it != params.end()) {
4754 file_.filename = it->second;
4757 it = params.find(
"filename*");
4758 if (it != params.end()) {
4760 static const std::regex re_rfc5987_encoding(R
"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
4763 if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
4764 file_.filename = decode_url(m2[1],
false);
4772 buf_erase(pos + crlf_.size());
4773 pos = buf_find(crlf_);
4781 if (crlf_dash_boundary_.size() > buf_size()) {
4784 auto pos = buf_find(crlf_dash_boundary_);
4785 if (pos < buf_size()) {
4786 if (!content_callback(buf_data(), pos)) {
4790 buf_erase(pos + crlf_dash_boundary_.size());
4793 auto len = buf_size() - crlf_dash_boundary_.size();
4795 if (!content_callback(buf_data(),
len)) {
4806 if (crlf_.size() > buf_size()) {
4809 if (buf_start_with(crlf_)) {
4810 buf_erase(crlf_.size());
4813 if (dash_.size() > buf_size()) {
4816 if (buf_start_with(dash_)) {
4817 buf_erase(dash_.size());
4819 buf_erase(buf_size());
4833 void clear_file_info() {
4835 file_.filename.clear();
4836 file_.content_type.clear();
4839 bool start_with_case_ignore(
const std::string &a,
const std::string &b)
const {
4840 if (a.size() < b.size()) {
4843 for (
size_t i = 0; i < b.size(); i++) {
4844 if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
4851 const std::string dash_ =
"--";
4852 const std::string crlf_ =
"\r\n";
4853 std::string boundary_;
4854 std::string dash_boundary_crlf_;
4855 std::string crlf_dash_boundary_;
4858 bool is_valid_ =
false;
4859 MultipartFormData file_;
4862 bool start_with(
const std::string &a,
size_t spos,
size_t epos,
const std::string &b)
const {
4863 if (epos - spos < b.size()) {
4866 for (
size_t i = 0; i < b.size(); i++) {
4867 if (a[i + spos] != b[i]) {
4874 size_t buf_size()
const {
return buf_epos_ - buf_spos_; }
4876 const char *buf_data()
const {
return &buf_[buf_spos_]; }
4878 std::string buf_head(
size_t l)
const {
return buf_.substr(buf_spos_,
l); }
4880 bool buf_start_with(
const std::string &s)
const {
return start_with(buf_, buf_spos_, buf_epos_, s); }
4882 size_t buf_find(
const std::string &s)
const {
4885 size_t off = buf_spos_;
4886 while (off < buf_epos_) {
4889 if (pos == buf_epos_) {
4892 if (buf_[pos] == c) {
4898 auto remaining_size = buf_epos_ - pos;
4899 if (s.size() > remaining_size) {
4903 if (start_with(buf_, pos, buf_epos_, s)) {
4904 return pos - buf_spos_;
4913 void buf_append(
const char *data,
size_t n) {
4914 auto remaining_size = buf_size();
4915 if (remaining_size > 0 && buf_spos_ > 0) {
4916 for (
size_t i = 0; i < remaining_size; i++) {
4917 buf_[i] = buf_[buf_spos_ + i];
4921 buf_epos_ = remaining_size;
4923 if (remaining_size + n > buf_.size()) {
4924 buf_.resize(remaining_size + n);
4927 for (
size_t i = 0; i < n; i++) {
4928 buf_[buf_epos_ + i] = data[i];
4933 void buf_erase(
size_t size) { buf_spos_ += size; }
4936 size_t buf_spos_ = 0;
4937 size_t buf_epos_ = 0;
4940inline std::string random_string(
size_t length) {
4941 static const char data[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
4946 static std::random_device seed_gen;
4949 static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
4951 static std::mt19937 engine(seed_sequence);
4954 for (
size_t i = 0; i <
length; i++) {
4955 result += data[engine() % (
sizeof(data) - 1)];
4960inline std::string make_multipart_data_boundary() {
4961 return "--cpp-httplib-multipart-data-" + detail::random_string(16);
4964inline bool is_multipart_boundary_chars_valid(
const std::string &boundary) {
4966 for (
size_t i = 0; i < boundary.size(); i++) {
4967 auto c = boundary[i];
4968 if (!std::isalnum(c) && c !=
'-' && c !=
'_') {
4977inline std::string serialize_multipart_formdata_item_begin(
const T &item,
const std::string &boundary) {
4978 std::string body =
"--" + boundary +
"\r\n";
4979 body +=
"Content-Disposition: form-data; name=\"" + item.name +
"\"";
4980 if (!item.filename.empty()) {
4981 body +=
"; filename=\"" + item.filename +
"\"";
4984 if (!item.content_type.empty()) {
4985 body +=
"Content-Type: " + item.content_type +
"\r\n";
4992inline std::string serialize_multipart_formdata_item_end() {
return "\r\n"; }
4994inline std::string serialize_multipart_formdata_finish(
const std::string &boundary) {
4995 return "--" + boundary +
"--\r\n";
4998inline std::string serialize_multipart_formdata_get_content_type(
const std::string &boundary) {
4999 return "multipart/form-data; boundary=" + boundary;
5002inline std::string serialize_multipart_formdata(
const MultipartFormDataItems &items,
const std::string &boundary,
5003 bool finish =
true) {
5006 for (
const auto &item : items) {
5007 body += serialize_multipart_formdata_item_begin(item, boundary);
5008 body += item.content + serialize_multipart_formdata_item_end();
5012 body += serialize_multipart_formdata_finish(boundary);
5018inline bool range_error(Request &req, Response &res) {
5019 if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
5020 ssize_t contant_len =
static_cast<ssize_t>(res.content_length_ ? res.content_length_ : res.body.size());
5024 size_t overwrapping_count = 0;
5031 if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) {
5035 for (
auto &r : req.ranges) {
5036 auto &first_pos = r.first;
5037 auto &last_pos = r.second;
5039 if (first_pos == -1 && last_pos == -1) {
5041 last_pos = contant_len;
5044 if (first_pos == -1) {
5045 first_pos = contant_len - last_pos;
5046 last_pos = contant_len - 1;
5049 if (last_pos == -1) {
5050 last_pos = contant_len - 1;
5054 if (!(0 <= first_pos && first_pos <= last_pos && last_pos <= contant_len - 1)) {
5059 if (first_pos <= prev_first_pos) {
5064 if (first_pos <= prev_last_pos) {
5065 overwrapping_count++;
5066 if (overwrapping_count > 2) {
5071 prev_first_pos = (std::max)(prev_first_pos, first_pos);
5072 prev_last_pos = (std::max)(prev_last_pos, last_pos);
5079inline std::pair<size_t, size_t> get_range_offset_and_length(Range r,
size_t content_length) {
5080 assert(r.first != -1 && r.second != -1);
5081 assert(0 <= r.first && r.first <
static_cast<ssize_t>(content_length));
5082 assert(r.first <= r.second && r.second <
static_cast<ssize_t>(content_length));
5083 (void) (content_length);
5084 return std::make_pair(r.first,
static_cast<size_t>(r.second - r.first) + 1);
5087inline std::string make_content_range_header_field(
const std::pair<size_t, size_t> &offset_and_length,
5088 size_t content_length) {
5089 auto st = offset_and_length.first;
5090 auto ed = st + offset_and_length.second - 1;
5092 std::string field =
"bytes ";
5093 field += std::to_string(st);
5095 field += std::to_string(ed);
5097 field += std::to_string(content_length);
5101template<
typename SToken,
typename CToken,
typename Content>
5102bool process_multipart_ranges_data(
const Request &req,
const std::string &boundary,
const std::string &content_type,
5103 size_t content_length, SToken stoken, CToken ctoken, Content content) {
5104 for (
size_t i = 0; i < req.ranges.size(); i++) {
5108 if (!content_type.empty()) {
5109 ctoken(
"Content-Type: ");
5110 stoken(content_type);
5114 auto offset_and_length = get_range_offset_and_length(req.ranges[i], content_length);
5116 ctoken(
"Content-Range: ");
5117 stoken(make_content_range_header_field(offset_and_length, content_length));
5121 if (!content(offset_and_length.first, offset_and_length.second)) {
5134inline void make_multipart_ranges_data(
const Request &req, Response &res,
const std::string &boundary,
5135 const std::string &content_type,
size_t content_length, std::string &data) {
5136 process_multipart_ranges_data(
5137 req, boundary, content_type, content_length, [&](
const std::string &token) { data += token; },
5138 [&](
const std::string &token) { data += token; },
5139 [&](
size_t offset,
size_t length) {
5140 assert(offset +
length <= content_length);
5141 data += res.body.substr(offset,
length);
5146inline size_t get_multipart_ranges_data_length(
const Request &req,
const std::string &boundary,
5147 const std::string &content_type,
size_t content_length) {
5148 size_t data_length = 0;
5150 process_multipart_ranges_data(
5151 req, boundary, content_type, content_length, [&](
const std::string &token) { data_length += token.size(); },
5152 [&](
const std::string &token) { data_length += token.size(); },
5153 [&](
size_t ,
size_t length) {
5162inline bool write_multipart_ranges_data(Stream &strm,
const Request &req, Response &res,
const std::string &boundary,
5163 const std::string &content_type,
size_t content_length,
5164 const T &is_shutting_down) {
5165 return process_multipart_ranges_data(
5166 req, boundary, content_type, content_length, [&](
const std::string &token) { strm.write(token); },
5167 [&](
const std::string &token) { strm.write(token); },
5168 [&](
size_t offset,
size_t length) {
5169 return write_content(strm, res.content_provider_, offset,
length, is_shutting_down);
5173inline bool expect_content(
const Request &req) {
5174 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH" || req.method ==
"PRI" ||
5175 req.method ==
"DELETE") {
5182inline bool has_crlf(
const std::string &s) {
5185 if (*p ==
'\r' || *p ==
'\n') {
5193#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5194inline std::string message_digest(
const std::string &s,
const EVP_MD *algo) {
5195 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(EVP_MD_CTX_new(), EVP_MD_CTX_free);
5197 unsigned int hash_length = 0;
5198 unsigned char hash[EVP_MAX_MD_SIZE];
5200 EVP_DigestInit_ex(context.get(), algo,
nullptr);
5201 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
5202 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
5204 std::stringstream ss;
5205 for (
auto i = 0u; i < hash_length; ++i) {
5206 ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(hash[i]);
5212inline std::string MD5(
const std::string &s) {
return message_digest(s, EVP_md5()); }
5214inline std::string SHA_256(
const std::string &s) {
return message_digest(s, EVP_sha256()); }
5216inline std::string SHA_512(
const std::string &s) {
return message_digest(s, EVP_sha512()); }
5219#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5223inline bool load_system_certs_on_windows(X509_STORE *store) {
5224 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY) NULL, L
"ROOT");
5229 auto result =
false;
5230 PCCERT_CONTEXT pContext = NULL;
5231 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
nullptr) {
5232 auto encoded_cert =
static_cast<const unsigned char *
>(pContext->pbCertEncoded);
5234 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
5236 X509_STORE_add_cert(store, x509);
5242 CertFreeCertificateContext(pContext);
5243 CertCloseStore(hStore, 0);
5247#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
5249template<
typename T>
using CFObjectPtr = std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
5251inline void cf_object_ptr_deleter(CFTypeRef obj) {
5257inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5258 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
5259 CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll, kCFBooleanTrue};
5261 CFObjectPtr<CFDictionaryRef> query(
5262 CFDictionaryCreate(
nullptr,
reinterpret_cast<const void **
>(keys), values,
sizeof(keys) /
sizeof(keys[0]),
5263 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks),
5264 cf_object_ptr_deleter);
5270 CFTypeRef security_items =
nullptr;
5271 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
5272 CFArrayGetTypeID() != CFGetTypeID(security_items)) {
5276 certs.reset(
reinterpret_cast<CFArrayRef
>(security_items));
5280inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5281 CFArrayRef root_security_items =
nullptr;
5282 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
5286 certs.reset(root_security_items);
5290inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
5291 auto result =
false;
5292 for (
auto i = 0; i < CFArrayGetCount(certs); ++i) {
5293 const auto cert =
reinterpret_cast<const __SecCertificate *
>(CFArrayGetValueAtIndex(certs, i));
5295 if (SecCertificateGetTypeID() != CFGetTypeID(cert)) {
5299 CFDataRef cert_data =
nullptr;
5300 if (SecItemExport(cert, kSecFormatX509Cert, 0,
nullptr, &cert_data) != errSecSuccess) {
5304 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
5306 auto encoded_cert =
static_cast<const unsigned char *
>(CFDataGetBytePtr(cert_data_ptr.get()));
5308 auto x509 = d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
5311 X509_STORE_add_cert(store, x509);
5320inline bool load_system_certs_on_macos(X509_STORE *store) {
5321 auto result =
false;
5322 CFObjectPtr<CFArrayRef> certs(
nullptr, cf_object_ptr_deleter);
5323 if (retrieve_certs_from_keychain(certs) && certs) {
5324 result = add_certs_to_x509_store(certs.get(), store);
5327 if (retrieve_root_certs_from_keychain(certs) && certs) {
5328 result = add_certs_to_x509_store(certs.get(), store) || result;
5342 if (WSAStartup(0x0002, &wsaData) == 0)
5351 bool is_valid_ =
false;
5354static WSInit wsinit_;
5357#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5358inline std::pair<std::string, std::string> make_digest_authentication_header(
5359 const Request &req,
const std::map<std::string, std::string> &auth,
size_t cnonce_count,
const std::string &cnonce,
5360 const std::string &username,
const std::string &password,
bool is_proxy =
false) {
5363 std::stringstream ss;
5364 ss << std::setfill(
'0') << std::setw(8) << std::hex << cnonce_count;
5369 if (auth.find(
"qop") != auth.end()) {
5370 qop = auth.at(
"qop");
5371 if (qop.find(
"auth-int") != std::string::npos) {
5373 }
else if (qop.find(
"auth") != std::string::npos) {
5380 std::string algo =
"MD5";
5381 if (auth.find(
"algorithm") != auth.end()) {
5382 algo = auth.at(
"algorithm");
5385 std::string response;
5387 auto H = algo ==
"SHA-256" ? detail::SHA_256 : algo ==
"SHA-512" ? detail::SHA_512 : detail::MD5;
5389 auto A1 = username +
":" + auth.at(
"realm") +
":" + password;
5391 auto A2 = req.method +
":" + req.path;
5392 if (qop ==
"auth-int") {
5393 A2 +=
":" + H(req.body);
5397 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + H(A2));
5399 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + nc +
":" + cnonce +
":" + qop +
":" + H(A2));
5403 auto opaque = (auth.find(
"opaque") != auth.end()) ? auth.at(
"opaque") :
"";
5406 "Digest username=\"" + username +
"\", realm=\"" + auth.at(
"realm") +
"\", nonce=\"" + auth.at(
"nonce") +
5407 "\", uri=\"" + req.path +
"\", algorithm=" + algo +
5408 (qop.empty() ?
", response=\"" :
", qop=" + qop +
", nc=" + nc +
", cnonce=\"" + cnonce +
"\", response=\"") +
5409 response +
"\"" + (opaque.empty() ?
"" :
", opaque=\"" + opaque +
"\"");
5411 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5412 return std::make_pair(key, field);
5416inline bool parse_www_authenticate(
const Response &res, std::map<std::string, std::string> &auth,
bool is_proxy) {
5417 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
5418 if (res.has_header(auth_key)) {
5419 static auto re = std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
5420 auto s = res.get_header_value(auth_key);
5421 auto pos = s.find(
' ');
5422 if (pos != std::string::npos) {
5423 auto type = s.substr(0, pos);
5424 if (
type ==
"Basic") {
5426 }
else if (
type ==
"Digest") {
5427 s = s.substr(pos + 1);
5428 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
5429 for (
auto i = beg; i != std::sregex_iterator(); ++i) {
5431 auto key = s.substr(
static_cast<size_t>(
m.position(1)),
static_cast<size_t>(
m.length(1)));
5432 auto val =
m.length(2) > 0 ? s.substr(
static_cast<size_t>(
m.position(2)),
static_cast<size_t>(
m.length(2)))
5433 : s.substr(
static_cast<size_t>(
m.position(3)),
static_cast<size_t>(
m.length(3)));
5443class ContentProviderAdapter {
5445 explicit ContentProviderAdapter(ContentProviderWithoutLength &&content_provider)
5446 : content_provider_(content_provider) {}
5448 bool operator()(
size_t offset,
size_t, DataSink &sink) {
return content_provider_(offset, sink); }
5451 ContentProviderWithoutLength content_provider_;
5456inline std::string hosted_at(
const std::string &hostname) {
5457 std::vector<std::string> addrs;
5458 hosted_at(hostname, addrs);
5459 if (addrs.empty()) {
5460 return std::string();
5465inline void hosted_at(
const std::string &hostname, std::vector<std::string> &addrs) {
5466 struct addrinfo hints;
5467 struct addrinfo *result;
5469 memset(&hints, 0,
sizeof(
struct addrinfo));
5470 hints.ai_family = AF_UNSPEC;
5471 hints.ai_socktype = SOCK_STREAM;
5472 hints.ai_protocol = 0;
5474 if (getaddrinfo(hostname.c_str(),
nullptr, &hints, &result)) {
5475#if defined __linux__ && !defined __ANDROID__
5480 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5482 for (
auto rp = result; rp; rp = rp->ai_next) {
5483 const auto &addr = *
reinterpret_cast<struct
sockaddr_storage *
>(rp->ai_addr);
5486 if (detail::get_ip_and_port(addr,
sizeof(
struct sockaddr_storage), ip, dummy)) {
5487 addrs.push_back(ip);
5492inline std::string append_query_params(
const std::string &path,
const Params ¶ms) {
5493 std::string path_with_query = path;
5494 const static std::regex re(
"[^?]+\\?.*");
5495 auto delm = std::regex_match(path, re) ?
'&' :
'?';
5496 path_with_query += delm + detail::params_to_query_str(params);
5497 return path_with_query;
5501inline std::pair<std::string, std::string> make_range_header(
const Ranges &ranges) {
5502 std::string field =
"bytes=";
5504 for (
const auto &r : ranges) {
5508 if (r.first != -1) {
5509 field += std::to_string(r.first);
5512 if (r.second != -1) {
5513 field += std::to_string(r.second);
5517 return std::make_pair(
"Range", std::move(field));
5520inline std::pair<std::string, std::string> make_basic_authentication_header(
const std::string &username,
5521 const std::string &password,
5523 auto field =
"Basic " + detail::base64_encode(username +
":" + password);
5524 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5525 return std::make_pair(key, std::move(field));
5528inline std::pair<std::string, std::string> make_bearer_token_authentication_header(
const std::string &token,
5529 bool is_proxy =
false) {
5530 auto field =
"Bearer " + token;
5531 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5532 return std::make_pair(key, std::move(field));
5536inline bool Request::has_header(
const std::string &key)
const {
return detail::has_header(headers, key); }
5538inline std::string Request::get_header_value(
const std::string &key,
const char *def,
size_t id)
const {
5539 return detail::get_header_value(headers, key, def,
id);
5542inline size_t Request::get_header_value_count(
const std::string &key)
const {
5543 auto r = headers.equal_range(key);
5544 return static_cast<size_t>(std::distance(r.first, r.second));
5547inline void Request::set_header(
const std::string &key,
const std::string &
val) {
5548 if (!detail::has_crlf(key) && !detail::has_crlf(
val)) {
5549 headers.emplace(key,
val);
5553inline bool Request::has_param(
const std::string &key)
const {
return params.find(key) != params.end(); }
5555inline std::string Request::get_param_value(
const std::string &key,
size_t id)
const {
5556 auto rng = params.equal_range(key);
5557 auto it = rng.first;
5558 std::advance(it,
static_cast<ssize_t>(
id));
5559 if (it != rng.second) {
5562 return std::string();
5565inline size_t Request::get_param_value_count(
const std::string &key)
const {
5566 auto r = params.equal_range(key);
5567 return static_cast<size_t>(std::distance(r.first, r.second));
5570inline bool Request::is_multipart_form_data()
const {
5571 const auto &content_type = get_header_value(
"Content-Type");
5572 return !content_type.rfind(
"multipart/form-data", 0);
5575inline bool Request::has_file(
const std::string &key)
const {
return files.find(key) != files.end(); }
5577inline MultipartFormData Request::get_file_value(
const std::string &key)
const {
5578 auto it = files.find(key);
5579 if (it != files.end()) {
5582 return MultipartFormData();
5585inline std::vector<MultipartFormData> Request::get_file_values(
const std::string &key)
const {
5586 std::vector<MultipartFormData> values;
5587 auto rng = files.equal_range(key);
5588 for (
auto it = rng.first; it != rng.second; it++) {
5589 values.push_back(it->second);
5595inline bool Response::has_header(
const std::string &key)
const {
return headers.find(key) != headers.end(); }
5597inline std::string Response::get_header_value(
const std::string &key,
const char *def,
size_t id)
const {
5598 return detail::get_header_value(headers, key, def,
id);
5601inline size_t Response::get_header_value_count(
const std::string &key)
const {
5602 auto r = headers.equal_range(key);
5603 return static_cast<size_t>(std::distance(r.first, r.second));
5606inline void Response::set_header(
const std::string &key,
const std::string &
val) {
5607 if (!detail::has_crlf(key) && !detail::has_crlf(
val)) {
5608 headers.emplace(key,
val);
5612inline void Response::set_redirect(
const std::string &url,
int stat) {
5613 if (!detail::has_crlf(url)) {
5614 set_header(
"Location", url);
5615 if (300 <= stat && stat < 400) {
5618 this->
status = StatusCode::Found_302;
5623inline void Response::set_content(
const char *s,
size_t n,
const std::string &content_type) {
5626 auto rng = headers.equal_range(
"Content-Type");
5627 headers.erase(rng.first, rng.second);
5628 set_header(
"Content-Type", content_type);
5631inline void Response::set_content(
const std::string &s,
const std::string &content_type) {
5632 set_content(s.data(), s.size(), content_type);
5635inline void Response::set_content(std::string &&s,
const std::string &content_type) {
5636 body = std::move(s);
5638 auto rng = headers.equal_range(
"Content-Type");
5639 headers.erase(rng.first, rng.second);
5640 set_header(
"Content-Type", content_type);
5643inline void Response::set_content_provider(
size_t in_length,
const std::string &content_type, ContentProvider provider,
5644 ContentProviderResourceReleaser resource_releaser) {
5645 set_header(
"Content-Type", content_type);
5646 content_length_ = in_length;
5647 if (in_length > 0) {
5648 content_provider_ = std::move(provider);
5650 content_provider_resource_releaser_ = std::move(resource_releaser);
5651 is_chunked_content_provider_ =
false;
5654inline void Response::set_content_provider(
const std::string &content_type, ContentProviderWithoutLength provider,
5655 ContentProviderResourceReleaser resource_releaser) {
5656 set_header(
"Content-Type", content_type);
5657 content_length_ = 0;
5658 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
5659 content_provider_resource_releaser_ = std::move(resource_releaser);
5660 is_chunked_content_provider_ =
false;
5663inline void Response::set_chunked_content_provider(
const std::string &content_type,
5664 ContentProviderWithoutLength provider,
5665 ContentProviderResourceReleaser resource_releaser) {
5666 set_header(
"Content-Type", content_type);
5667 content_length_ = 0;
5668 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
5669 content_provider_resource_releaser_ = std::move(resource_releaser);
5670 is_chunked_content_provider_ =
true;
5673inline void Response::set_file_content(
const std::string &path,
const std::string &content_type) {
5674 file_content_path_ = path;
5675 file_content_content_type_ = content_type;
5678inline void Response::set_file_content(
const std::string &path) { file_content_path_ = path; }
5681inline bool Result::has_request_header(
const std::string &key)
const {
5682 return request_headers_.find(key) != request_headers_.end();
5685inline std::string Result::get_request_header_value(
const std::string &key,
const char *def,
size_t id)
const {
5686 return detail::get_header_value(request_headers_, key, def,
id);
5689inline size_t Result::get_request_header_value_count(
const std::string &key)
const {
5690 auto r = request_headers_.equal_range(key);
5691 return static_cast<size_t>(std::distance(r.first, r.second));
5695inline ssize_t Stream::write(
const char *ptr) {
return write(ptr, strlen(ptr)); }
5697inline ssize_t Stream::write(
const std::string &s) {
return write(s.data(), s.size()); }
5702inline SocketStream::SocketStream(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5703 time_t write_timeout_sec, time_t write_timeout_usec)
5705 read_timeout_sec_(read_timeout_sec),
5706 read_timeout_usec_(read_timeout_usec),
5707 write_timeout_sec_(write_timeout_sec),
5708 write_timeout_usec_(write_timeout_usec),
5709 read_buff_(read_buff_size_, 0) {}
5711inline SocketStream::~SocketStream() =
default;
5713inline bool SocketStream::is_readable()
const {
return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; }
5715inline bool SocketStream::is_writable()
const {
5716 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && is_socket_alive(sock_);
5719inline ssize_t SocketStream::read(
char *ptr,
size_t size) {
5721 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
5723 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
5726 if (read_buff_off_ < read_buff_content_size_) {
5727 auto remaining_size = read_buff_content_size_ - read_buff_off_;
5728 if (size <= remaining_size) {
5729 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
5730 read_buff_off_ += size;
5731 return static_cast<ssize_t>(size);
5733 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
5734 read_buff_off_ += remaining_size;
5735 return static_cast<ssize_t>(remaining_size);
5739 if (!is_readable()) {
5744 read_buff_content_size_ = 0;
5746 if (size < read_buff_size_) {
5747 auto n = read_socket(sock_, read_buff_.data(), read_buff_size_, CPPHTTPLIB_RECV_FLAGS);
5750 }
else if (n <=
static_cast<ssize_t>(size)) {
5751 memcpy(ptr, read_buff_.data(),
static_cast<size_t>(n));
5754 memcpy(ptr, read_buff_.data(), size);
5755 read_buff_off_ = size;
5756 read_buff_content_size_ =
static_cast<size_t>(n);
5757 return static_cast<ssize_t>(size);
5760 return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
5764inline ssize_t SocketStream::write(
const char *ptr,
size_t size) {
5765 if (!is_writable()) {
5769#if defined(_WIN32) && !defined(_WIN64)
5770 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
5773 return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
5776inline void SocketStream::get_remote_ip_and_port(std::string &ip,
int &port)
const {
5777 return detail::get_remote_ip_and_port(sock_, ip, port);
5780inline void SocketStream::get_local_ip_and_port(std::string &ip,
int &port)
const {
5781 return detail::get_local_ip_and_port(sock_, ip, port);
5784inline socket_t SocketStream::socket()
const {
return sock_; }
5787inline bool BufferStream::is_readable()
const {
return true; }
5789inline bool BufferStream::is_writable()
const {
return true; }
5791inline ssize_t BufferStream::read(
char *ptr,
size_t size) {
5792#if defined(_MSC_VER) && _MSC_VER < 1910
5793 auto len_read = buffer._Copy_s(ptr, size, size,
position);
5795 auto len_read = buffer.copy(ptr, size,
position);
5797 position +=
static_cast<size_t>(len_read);
5798 return static_cast<ssize_t>(len_read);
5801inline ssize_t BufferStream::write(
const char *ptr,
size_t size) {
5802 buffer.append(ptr, size);
5803 return static_cast<ssize_t>(size);
5806inline void BufferStream::get_remote_ip_and_port(std::string & ,
int & )
const {}
5808inline void BufferStream::get_local_ip_and_port(std::string & ,
int & )
const {}
5810inline socket_t BufferStream::socket()
const {
return 0; }
5812inline const std::string &BufferStream::get_buffer()
const {
return buffer; }
5814inline PathParamsMatcher::PathParamsMatcher(
const std::string &pattern) {
5815 static constexpr char marker[] =
"/:";
5818 std::size_t last_param_end = 0;
5820#ifndef CPPHTTPLIB_NO_EXCEPTIONS
5825 std::unordered_set<std::string> param_name_set;
5829 const auto marker_pos = pattern.find(marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
5830 if (marker_pos == std::string::npos) {
5834 static_fragments_.push_back(pattern.substr(last_param_end, marker_pos - last_param_end + 1));
5836 const auto param_name_start = marker_pos + 2;
5838 auto sep_pos = pattern.find(separator, param_name_start);
5839 if (sep_pos == std::string::npos) {
5840 sep_pos = pattern.length();
5843 auto param_name = pattern.substr(param_name_start, sep_pos - param_name_start);
5845#ifndef CPPHTTPLIB_NO_EXCEPTIONS
5846 if (param_name_set.find(param_name) != param_name_set.cend()) {
5848 "Encountered path parameter '" + param_name +
"' multiple times in route pattern '" + pattern +
"'.";
5849 throw std::invalid_argument(msg);
5853 param_names_.push_back(std::move(param_name));
5855 last_param_end = sep_pos + 1;
5858 if (last_param_end < pattern.length()) {
5859 static_fragments_.push_back(pattern.substr(last_param_end));
5863inline bool PathParamsMatcher::match(Request &request)
const {
5864 request.matches = std::smatch();
5865 request.path_params.clear();
5866 request.path_params.reserve(param_names_.size());
5869 std::size_t starting_pos = 0;
5870 for (
size_t i = 0; i < static_fragments_.size(); ++i) {
5871 const auto &fragment = static_fragments_[i];
5873 if (starting_pos + fragment.length() > request.path.length()) {
5879 if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(), fragment.length()) != 0) {
5883 starting_pos += fragment.length();
5888 if (i >= param_names_.size()) {
5892 auto sep_pos = request.path.find(separator, starting_pos);
5893 if (sep_pos == std::string::npos) {
5894 sep_pos = request.path.length();
5897 const auto ¶m_name = param_names_[i];
5899 request.path_params.emplace(param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
5902 starting_pos = sep_pos + 1;
5905 return starting_pos >= request.path.length();
5908inline bool RegexMatcher::match(Request &request)
const {
5909 request.path_params.clear();
5910 return std::regex_match(request.path, request.matches, regex_);
5916inline Server::Server() : new_task_queue([] {
return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
5918 signal(SIGPIPE, SIG_IGN);
5922inline Server::~Server() =
default;
5924inline std::unique_ptr<detail::MatcherBase> Server::make_matcher(
const std::string &pattern) {
5925 if (pattern.find(
"/:") != std::string::npos) {
5926 return detail::make_unique<detail::PathParamsMatcher>(pattern);
5928 return detail::make_unique<detail::RegexMatcher>(pattern);
5932inline Server &Server::Get(
const std::string &pattern, Handler handler) {
5933 get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5937inline Server &Server::Post(
const std::string &pattern, Handler handler) {
5938 post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5942inline Server &Server::Post(
const std::string &pattern, HandlerWithContentReader handler) {
5943 post_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5947inline Server &Server::Put(
const std::string &pattern, Handler handler) {
5948 put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5952inline Server &Server::Put(
const std::string &pattern, HandlerWithContentReader handler) {
5953 put_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5957inline Server &Server::Patch(
const std::string &pattern, Handler handler) {
5958 patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5962inline Server &Server::Patch(
const std::string &pattern, HandlerWithContentReader handler) {
5963 patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5967inline Server &Server::Delete(
const std::string &pattern, Handler handler) {
5968 delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5972inline Server &Server::Delete(
const std::string &pattern, HandlerWithContentReader handler) {
5973 delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5977inline Server &Server::Options(
const std::string &pattern, Handler handler) {
5978 options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5982inline bool Server::set_base_dir(
const std::string &dir,
const std::string &mount_point) {
5983 return set_mount_point(mount_point, dir);
5986inline bool Server::set_mount_point(
const std::string &mount_point,
const std::string &dir, Headers headers) {
5987 detail::FileStat stat(dir);
5988 if (stat.is_dir()) {
5989 std::string mnt = !mount_point.empty() ? mount_point :
"/";
5990 if (!mnt.empty() && mnt[0] ==
'/') {
5991 base_dirs_.push_back({mnt, dir, std::move(headers)});
5998inline bool Server::remove_mount_point(
const std::string &mount_point) {
5999 for (
auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
6000 if (it->mount_point == mount_point) {
6001 base_dirs_.erase(it);
6008inline Server &Server::set_file_extension_and_mimetype_mapping(
const std::string &ext,
const std::string &mime) {
6009 file_extension_and_mimetype_map_[ext] = mime;
6013inline Server &Server::set_default_file_mimetype(
const std::string &mime) {
6014 default_file_mimetype_ = mime;
6018inline Server &Server::set_file_request_handler(Handler handler) {
6019 file_request_handler_ = std::move(handler);
6023inline Server &Server::set_error_handler_core(HandlerWithResponse handler, std::true_type) {
6024 error_handler_ = std::move(handler);
6028inline Server &Server::set_error_handler_core(Handler handler, std::false_type) {
6029 error_handler_ = [handler](
const Request &req, Response &res) {
6031 return HandlerResponse::Handled;
6036inline Server &Server::set_exception_handler(ExceptionHandler handler) {
6037 exception_handler_ = std::move(handler);
6041inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
6042 pre_routing_handler_ = std::move(handler);
6046inline Server &Server::set_post_routing_handler(Handler handler) {
6047 post_routing_handler_ = std::move(handler);
6051inline Server &Server::set_logger(Logger logger) {
6052 logger_ = std::move(logger);
6056inline Server &Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
6057 expect_100_continue_handler_ = std::move(handler);
6061inline Server &Server::set_address_family(
int family) {
6062 address_family_ = family;
6066inline Server &Server::set_tcp_nodelay(
bool on) {
6071inline Server &Server::set_ipv6_v6only(
bool on) {
6076inline Server &Server::set_socket_options(SocketOptions socket_options) {
6077 socket_options_ = std::move(socket_options);
6081inline Server &Server::set_default_headers(Headers headers) {
6082 default_headers_ = std::move(headers);
6086inline Server &Server::set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer) {
6087 header_writer_ = writer;
6091inline Server &Server::set_keep_alive_max_count(
size_t count) {
6092 keep_alive_max_count_ = count;
6096inline Server &Server::set_keep_alive_timeout(time_t sec) {
6097 keep_alive_timeout_sec_ = sec;
6101inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
6102 read_timeout_sec_ = sec;
6103 read_timeout_usec_ = usec;
6107inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
6108 write_timeout_sec_ = sec;
6109 write_timeout_usec_ = usec;
6113inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
6114 idle_interval_sec_ = sec;
6115 idle_interval_usec_ = usec;
6119inline Server &Server::set_payload_max_length(
size_t length) {
6120 payload_max_length_ =
length;
6124inline bool Server::bind_to_port(
const std::string &host,
int port,
int socket_flags) {
6125 auto ret = bind_internal(host, port, socket_flags);
6127 is_decommisioned =
true;
6131inline int Server::bind_to_any_port(
const std::string &host,
int socket_flags) {
6132 auto ret = bind_internal(host, 0, socket_flags);
6134 is_decommisioned =
true;
6139inline bool Server::listen_after_bind() {
return listen_internal(); }
6141inline bool Server::listen(
const std::string &host,
int port,
int socket_flags) {
6142 return bind_to_port(host, port, socket_flags) && listen_internal();
6145inline bool Server::is_running()
const {
return is_running_; }
6147inline void Server::wait_until_ready()
const {
6148 while (!is_running_ && !is_decommisioned) {
6149 std::this_thread::sleep_for(std::chrono::milliseconds{1});
6153inline void Server::stop() {
6155 assert(svr_sock_ != INVALID_SOCKET);
6156 std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
6157 detail::shutdown_socket(sock);
6158 detail::close_socket(sock);
6160 is_decommisioned =
false;
6163inline void Server::decommission() { is_decommisioned =
true; }
6165inline bool Server::parse_request_line(
const char *s, Request &req)
const {
6166 auto len = strlen(s);
6167 if (
len < 2 || s[
len - 2] !=
'\r' || s[
len - 1] !=
'\n') {
6175 detail::split(s, s +
len,
' ', [&](
const char *b,
const char *e) {
6178 req.method = std::string(b, e);
6181 req.target = std::string(b, e);
6184 req.version = std::string(b, e);
6197 static const std::set<std::string> methods{
"GET",
"HEAD",
"POST",
"PUT",
"DELETE",
6198 "CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PRI"};
6200 if (methods.find(req.method) == methods.end()) {
6204 if (req.version !=
"HTTP/1.1" && req.version !=
"HTTP/1.0") {
6210 for (
size_t i = 0; i < req.target.size(); i++) {
6211 if (req.target[i] ==
'#') {
6212 req.target.erase(i);
6217 detail::divide(req.target,
'?',
6218 [&](
const char *lhs_data, std::size_t lhs_size,
const char *rhs_data, std::size_t rhs_size) {
6219 req.path = detail::decode_url(std::string(lhs_data, lhs_size), false);
6220 detail::parse_query_text(rhs_data, rhs_size, req.params);
6227inline bool Server::write_response(Stream &strm,
bool close_connection, Request &req, Response &res) {
6231 return write_response_core(strm, close_connection, req, res,
false);
6234inline bool Server::write_response_with_content(Stream &strm,
bool close_connection,
const Request &req,
6236 return write_response_core(strm, close_connection, req, res,
true);
6239inline bool Server::write_response_core(Stream &strm,
bool close_connection,
const Request &req, Response &res,
6240 bool need_apply_ranges) {
6241 assert(res.status != -1);
6243 if (400 <= res.status && error_handler_ && error_handler_(req, res) == HandlerResponse::Handled) {
6244 need_apply_ranges =
true;
6247 std::string content_type;
6248 std::string boundary;
6249 if (need_apply_ranges) {
6250 apply_ranges(req, res, content_type, boundary);
6254 if (close_connection || req.get_header_value(
"Connection") ==
"close") {
6255 res.set_header(
"Connection",
"close");
6257 std::string s =
"timeout=";
6258 s += std::to_string(keep_alive_timeout_sec_);
6260 s += std::to_string(keep_alive_max_count_);
6261 res.set_header(
"Keep-Alive", s);
6264 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) && !res.has_header(
"Content-Type")) {
6265 res.set_header(
"Content-Type",
"text/plain");
6268 if (res.body.empty() && !res.content_length_ && !res.content_provider_ && !res.has_header(
"Content-Length")) {
6269 res.set_header(
"Content-Length",
"0");
6272 if (req.method ==
"HEAD" && !res.has_header(
"Accept-Ranges")) {
6273 res.set_header(
"Accept-Ranges",
"bytes");
6276 if (post_routing_handler_) {
6277 post_routing_handler_(req, res);
6282 detail::BufferStream bstrm;
6283 if (!detail::write_response_line(bstrm, res.status)) {
6286 if (!header_writer_(bstrm, res.headers)) {
6291 auto &data = bstrm.get_buffer();
6292 detail::write_data(strm, data.data(), data.size());
6297 if (req.method !=
"HEAD") {
6298 if (!res.body.empty()) {
6299 if (!detail::write_data(strm, res.body.data(), res.body.size())) {
6302 }
else if (res.content_provider_) {
6303 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
6304 res.content_provider_success_ =
true;
6319inline bool Server::write_content_with_provider(Stream &strm,
const Request &req, Response &res,
6320 const std::string &boundary,
const std::string &content_type) {
6321 auto is_shutting_down = [
this]() {
return this->svr_sock_ == INVALID_SOCKET; };
6323 if (res.content_length_ > 0) {
6324 if (req.ranges.empty()) {
6325 return detail::write_content(strm, res.content_provider_, 0, res.content_length_, is_shutting_down);
6326 }
else if (req.ranges.size() == 1) {
6327 auto offset_and_length = detail::get_range_offset_and_length(req.ranges[0], res.content_length_);
6329 return detail::write_content(strm, res.content_provider_, offset_and_length.first, offset_and_length.second,
6332 return detail::write_multipart_ranges_data(strm, req, res, boundary, content_type, res.content_length_,
6336 if (res.is_chunked_content_provider_) {
6337 auto type = detail::encoding_type(req, res);
6339 std::unique_ptr<detail::compressor> compressor;
6340 if (
type == detail::EncodingType::Gzip) {
6341#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6342 compressor = detail::make_unique<detail::gzip_compressor>();
6344 }
else if (
type == detail::EncodingType::Brotli) {
6345#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6346 compressor = detail::make_unique<detail::brotli_compressor>();
6349 compressor = detail::make_unique<detail::nocompressor>();
6351 assert(compressor !=
nullptr);
6353 return detail::write_content_chunked(strm, res.content_provider_, is_shutting_down, *compressor);
6355 return detail::write_content_without_length(strm, res.content_provider_, is_shutting_down);
6360inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
6361 MultipartFormDataMap::iterator cur;
6362 auto file_count = 0;
6363 if (read_content_core(
6366 [&](
const char *buf,
size_t n) {
6367 if (req.body.size() + n > req.body.max_size()) {
6370 req.body.append(buf, n);
6374 [&](
const MultipartFormData &file) {
6375 if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
6378 cur = req.files.emplace(file.name, file);
6381 [&](
const char *buf,
size_t n) {
6382 auto &content = cur->second.content;
6383 if (content.size() + n > content.max_size()) {
6386 content.append(buf, n);
6389 const auto &content_type = req.get_header_value(
"Content-Type");
6390 if (!content_type.find(
"application/x-www-form-urlencoded")) {
6391 if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
6392 res.status = StatusCode::PayloadTooLarge_413;
6395 detail::parse_query_text(req.body, req.params);
6402inline bool Server::read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
6403 ContentReceiver receiver,
6404 MultipartContentHeader multipart_header,
6405 ContentReceiver multipart_receiver) {
6406 return read_content_core(strm, req, res, std::move(receiver), std::move(multipart_header),
6407 std::move(multipart_receiver));
6410inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, ContentReceiver receiver,
6411 MultipartContentHeader multipart_header,
6412 ContentReceiver multipart_receiver)
const {
6413 detail::MultipartFormDataParser multipart_form_data_parser;
6414 ContentReceiverWithProgress out;
6416 if (req.is_multipart_form_data()) {
6417 const auto &content_type = req.get_header_value(
"Content-Type");
6418 std::string boundary;
6419 if (!detail::parse_multipart_boundary(content_type, boundary)) {
6420 res.status = StatusCode::BadRequest_400;
6424 multipart_form_data_parser.set_boundary(std::move(boundary));
6425 out = [&](
const char *buf,
size_t n, uint64_t , uint64_t ) {
6437 return multipart_form_data_parser.parse(buf, n, multipart_receiver, multipart_header);
6440 out = [receiver](
const char *buf,
size_t n, uint64_t , uint64_t ) {
return receiver(buf, n); };
6443 if (req.method ==
"DELETE" && !req.has_header(
"Content-Length")) {
6447 if (!detail::read_content(strm, req, payload_max_length_, res.status,
nullptr, out,
true)) {
6451 if (req.is_multipart_form_data()) {
6452 if (!multipart_form_data_parser.is_valid()) {
6453 res.status = StatusCode::BadRequest_400;
6461inline bool Server::handle_file_request(
const Request &req, Response &res,
bool head) {
6462 for (
const auto &entry : base_dirs_) {
6464 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
6465 std::string sub_path =
"/" + req.path.substr(entry.mount_point.size());
6466 if (detail::is_valid_path(sub_path)) {
6467 auto path = entry.base_dir + sub_path;
6468 if (path.back() ==
'/') {
6469 path +=
"index.html";
6472 detail::FileStat stat(path);
6474 if (stat.is_dir()) {
6475 res.set_redirect(sub_path +
"/", StatusCode::MovedPermanently_301);
6479 if (stat.is_file()) {
6480 for (
const auto &kv : entry.headers) {
6481 res.set_header(kv.first, kv.second);
6484 auto mm = std::make_shared<detail::mmap>(path.c_str());
6485 if (!mm->is_open()) {
6489 res.set_content_provider(
6490 mm->size(), detail::find_content_type(path, file_extension_and_mimetype_map_, default_file_mimetype_),
6491 [mm](
size_t offset,
size_t length, DataSink &sink) ->
bool {
6492 sink.write(mm->data() + offset, length);
6496 if (!head && file_request_handler_) {
6497 file_request_handler_(req, res);
6508inline socket_t Server::create_server_socket(
const std::string &host,
int port,
int socket_flags,
6509 SocketOptions socket_options)
const {
6510 return detail::create_socket(host, std::string(), port, address_family_, socket_flags, tcp_nodelay_, ipv6_v6only_,
6511 std::move(socket_options),
6512 [](
socket_t sock,
struct addrinfo &ai,
bool & ) ->
bool {
6513 if (::bind(sock, ai.ai_addr,
static_cast<socklen_t>(ai.ai_addrlen))) {
6516 if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
6523inline int Server::bind_internal(
const std::string &host,
int port,
int socket_flags) {
6524 if (is_decommisioned) {
6532 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
6533 if (svr_sock_ == INVALID_SOCKET) {
6540 if (getsockname(svr_sock_,
reinterpret_cast<struct
sockaddr *
>(&addr), &addr_len) == -1) {
6543 if (addr.ss_family == AF_INET) {
6544 return ntohs(
reinterpret_cast<struct
sockaddr_in *
>(&addr)->sin_port);
6545 }
else if (addr.ss_family == AF_INET6) {
6546 return ntohs(
reinterpret_cast<struct
sockaddr_in6 *
>(&addr)->sin6_port);
6555inline bool Server::listen_internal() {
6556 if (is_decommisioned) {
6562 auto se = detail::scope_exit([&]() { is_running_ =
false; });
6565 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
6567 while (svr_sock_ != INVALID_SOCKET) {
6569 if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
6571 auto val = detail::select_read(svr_sock_, idle_interval_sec_, idle_interval_usec_);
6573 task_queue->on_idle();
6583 socket_t sock = WSAAccept(svr_sock_,
nullptr,
nullptr,
nullptr, 0);
6584#elif defined SOCK_CLOEXEC
6585 socket_t sock = accept4(svr_sock_,
nullptr,
nullptr, SOCK_CLOEXEC);
6587 socket_t sock = accept(svr_sock_,
nullptr,
nullptr);
6590 if (sock == INVALID_SOCKET) {
6591 if (errno == EMFILE) {
6594 std::this_thread::sleep_for(std::chrono::microseconds{1});
6596 }
else if (errno == EINTR || errno == EAGAIN) {
6599 if (svr_sock_ != INVALID_SOCKET) {
6600 detail::close_socket(svr_sock_);
6610 auto timeout =
static_cast<uint32_t>(read_timeout_sec_ * 1000 + read_timeout_usec_ / 1000);
6611 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
6614 tv.tv_sec =
static_cast<long>(read_timeout_sec_);
6615 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(read_timeout_usec_);
6616 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
6621 auto timeout =
static_cast<uint32_t>(write_timeout_sec_ * 1000 + write_timeout_usec_ / 1000);
6622 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
6625 tv.tv_sec =
static_cast<long>(write_timeout_sec_);
6626 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(write_timeout_usec_);
6627 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
6631 if (!task_queue->enqueue([
this, sock]() { process_and_close_socket(sock); })) {
6632 detail::shutdown_socket(sock);
6633 detail::close_socket(sock);
6637 task_queue->shutdown();
6640 is_decommisioned = !ret;
6644inline bool Server::routing(Request &req, Response &res, Stream &strm) {
6645 if (pre_routing_handler_ && pre_routing_handler_(req, res) == HandlerResponse::Handled) {
6650 auto is_head_request = req.method ==
"HEAD";
6651 if ((req.method ==
"GET" || is_head_request) && handle_file_request(req, res, is_head_request)) {
6655 if (detail::expect_content(req)) {
6658 ContentReader reader(
6659 [&](ContentReceiver receiver) {
6660 return read_content_with_content_receiver(strm, req, res, std::move(receiver),
nullptr,
nullptr);
6662 [&](MultipartContentHeader header, ContentReceiver receiver) {
6663 return read_content_with_content_receiver(strm, req, res,
nullptr, std::move(header), std::move(receiver));
6666 if (req.method ==
"POST") {
6667 if (dispatch_request_for_content_reader(req, res, std::move(reader), post_handlers_for_content_reader_)) {
6670 }
else if (req.method ==
"PUT") {
6671 if (dispatch_request_for_content_reader(req, res, std::move(reader), put_handlers_for_content_reader_)) {
6674 }
else if (req.method ==
"PATCH") {
6675 if (dispatch_request_for_content_reader(req, res, std::move(reader), patch_handlers_for_content_reader_)) {
6678 }
else if (req.method ==
"DELETE") {
6679 if (dispatch_request_for_content_reader(req, res, std::move(reader), delete_handlers_for_content_reader_)) {
6686 if (!read_content(strm, req, res)) {
6692 if (req.method ==
"GET" || req.method ==
"HEAD") {
6693 return dispatch_request(req, res, get_handlers_);
6694 }
else if (req.method ==
"POST") {
6695 return dispatch_request(req, res, post_handlers_);
6696 }
else if (req.method ==
"PUT") {
6697 return dispatch_request(req, res, put_handlers_);
6698 }
else if (req.method ==
"DELETE") {
6699 return dispatch_request(req, res, delete_handlers_);
6700 }
else if (req.method ==
"OPTIONS") {
6701 return dispatch_request(req, res, options_handlers_);
6702 }
else if (req.method ==
"PATCH") {
6703 return dispatch_request(req, res, patch_handlers_);
6706 res.status = StatusCode::BadRequest_400;
6710inline bool Server::dispatch_request(Request &req, Response &res,
const Handlers &handlers)
const {
6711 for (
const auto &
x : handlers) {
6712 const auto &matcher =
x.first;
6713 const auto &handler =
x.second;
6715 if (matcher->match(req)) {
6723inline void Server::apply_ranges(
const Request &req, Response &res, std::string &content_type,
6724 std::string &boundary)
const {
6725 if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
6726 auto it = res.headers.find(
"Content-Type");
6727 if (it != res.headers.end()) {
6728 content_type = it->second;
6729 res.headers.erase(it);
6732 boundary = detail::make_multipart_data_boundary();
6734 res.set_header(
"Content-Type",
"multipart/byteranges; boundary=" + boundary);
6737 auto type = detail::encoding_type(req, res);
6739 if (res.body.empty()) {
6740 if (res.content_length_ > 0) {
6742 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
6743 length = res.content_length_;
6744 }
else if (req.ranges.size() == 1) {
6745 auto offset_and_length = detail::get_range_offset_and_length(req.ranges[0], res.content_length_);
6747 length = offset_and_length.second;
6749 auto content_range = detail::make_content_range_header_field(offset_and_length, res.content_length_);
6750 res.set_header(
"Content-Range", content_range);
6752 length = detail::get_multipart_ranges_data_length(req, boundary, content_type, res.content_length_);
6754 res.set_header(
"Content-Length", std::to_string(
length));
6756 if (res.content_provider_) {
6757 if (res.is_chunked_content_provider_) {
6758 res.set_header(
"Transfer-Encoding",
"chunked");
6759 if (
type == detail::EncodingType::Gzip) {
6760 res.set_header(
"Content-Encoding",
"gzip");
6761 }
else if (
type == detail::EncodingType::Brotli) {
6762 res.set_header(
"Content-Encoding",
"br");
6768 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
6770 }
else if (req.ranges.size() == 1) {
6771 auto offset_and_length = detail::get_range_offset_and_length(req.ranges[0], res.body.size());
6772 auto offset = offset_and_length.first;
6773 auto length = offset_and_length.second;
6775 auto content_range = detail::make_content_range_header_field(offset_and_length, res.body.size());
6776 res.set_header(
"Content-Range", content_range);
6778 assert(offset +
length <= res.body.size());
6779 res.body = res.body.substr(offset,
length);
6782 detail::make_multipart_ranges_data(req, res, boundary, content_type, res.body.size(), data);
6783 res.body.swap(data);
6786 if (
type != detail::EncodingType::None) {
6787 std::unique_ptr<detail::compressor> compressor;
6788 std::string content_encoding;
6790 if (
type == detail::EncodingType::Gzip) {
6791#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6792 compressor = detail::make_unique<detail::gzip_compressor>();
6793 content_encoding =
"gzip";
6795 }
else if (
type == detail::EncodingType::Brotli) {
6796#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6797 compressor = detail::make_unique<detail::brotli_compressor>();
6798 content_encoding =
"br";
6803 std::string compressed;
6804 if (compressor->compress(res.body.data(), res.body.size(),
true, [&](
const char *data,
size_t data_len) {
6805 compressed.append(data, data_len);
6808 res.body.swap(compressed);
6809 res.set_header(
"Content-Encoding", content_encoding);
6814 auto length = std::to_string(res.body.size());
6815 res.set_header(
"Content-Length",
length);
6819inline bool Server::dispatch_request_for_content_reader(Request &req, Response &res, ContentReader content_reader,
6820 const HandlersForContentReader &handlers)
const {
6821 for (
const auto &
x : handlers) {
6822 const auto &matcher =
x.first;
6823 const auto &handler =
x.second;
6825 if (matcher->match(req)) {
6826 handler(req, res, content_reader);
6833inline bool Server::process_request(Stream &strm,
const std::string &remote_addr,
int remote_port,
6834 const std::string &local_addr,
int local_port,
bool close_connection,
6835 bool &connection_closed,
const std::function<
void(Request &)> &setup_request) {
6836 std::array<char, 2048> buf{};
6838 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
6841 if (!line_reader.getline()) {
6848 res.version =
"HTTP/1.1";
6849 res.headers = default_headers_;
6854#ifndef CPPHTTPLIB_USE_POLL
6856 if (strm.socket() >= FD_SETSIZE) {
6858 detail::read_headers(strm, dummy);
6859 res.status = StatusCode::InternalServerError_500;
6860 return write_response(strm, close_connection, req, res);
6866 if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
6868 detail::read_headers(strm, dummy);
6869 res.status = StatusCode::UriTooLong_414;
6870 return write_response(strm, close_connection, req, res);
6874 if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) {
6875 res.status = StatusCode::BadRequest_400;
6876 return write_response(strm, close_connection, req, res);
6879 if (req.get_header_value(
"Connection") ==
"close") {
6880 connection_closed =
true;
6883 if (req.version ==
"HTTP/1.0" && req.get_header_value(
"Connection") !=
"Keep-Alive") {
6884 connection_closed =
true;
6887 req.remote_addr = remote_addr;
6888 req.remote_port = remote_port;
6889 req.set_header(
"REMOTE_ADDR", req.remote_addr);
6890 req.set_header(
"REMOTE_PORT", std::to_string(req.remote_port));
6892 req.local_addr = local_addr;
6893 req.local_port = local_port;
6894 req.set_header(
"LOCAL_ADDR", req.local_addr);
6895 req.set_header(
"LOCAL_PORT", std::to_string(req.local_port));
6897 if (req.has_header(
"Range")) {
6898 const auto &range_header_value = req.get_header_value(
"Range");
6899 if (!detail::parse_range_header(range_header_value, req.ranges)) {
6900 res.status = StatusCode::RangeNotSatisfiable_416;
6901 return write_response(strm, close_connection, req, res);
6905 if (setup_request) {
6909 if (req.get_header_value(
"Expect") ==
"100-continue") {
6910 int status = StatusCode::Continue_100;
6911 if (expect_100_continue_handler_) {
6912 status = expect_100_continue_handler_(req, res);
6915 case StatusCode::Continue_100:
6916 case StatusCode::ExpectationFailed_417:
6917 detail::write_response_line(strm,
status);
6921 connection_closed =
true;
6922 return write_response(strm,
true, req, res);
6927 auto routed =
false;
6928#ifdef CPPHTTPLIB_NO_EXCEPTIONS
6929 routed = routing(req, res, strm);
6932 routed = routing(req, res, strm);
6933 }
catch (std::exception &e) {
6934 if (exception_handler_) {
6935 auto ep = std::current_exception();
6936 exception_handler_(req, res, ep);
6939 res.status = StatusCode::InternalServerError_500;
6942 for (
size_t i = 0; s[i]; i++) {
6955 res.set_header(
"EXCEPTION_WHAT",
val);
6958 if (exception_handler_) {
6959 auto ep = std::current_exception();
6960 exception_handler_(req, res, ep);
6963 res.status = StatusCode::InternalServerError_500;
6964 res.set_header(
"EXCEPTION_WHAT",
"UNKNOWN");
6969 if (res.status == -1) {
6970 res.status = req.ranges.empty() ? StatusCode::OK_200 : StatusCode::PartialContent_206;
6973 if (detail::range_error(req, res)) {
6975 res.content_length_ = 0;
6976 res.content_provider_ =
nullptr;
6977 res.status = StatusCode::RangeNotSatisfiable_416;
6978 return write_response(strm, close_connection, req, res);
6982 if (!res.file_content_path_.empty()) {
6983 const auto &path = res.file_content_path_;
6984 auto mm = std::make_shared<detail::mmap>(path.c_str());
6985 if (!mm->is_open()) {
6987 res.content_length_ = 0;
6988 res.content_provider_ =
nullptr;
6989 res.status = StatusCode::NotFound_404;
6990 return write_response(strm, close_connection, req, res);
6993 auto content_type = res.file_content_content_type_;
6994 if (content_type.empty()) {
6995 content_type = detail::find_content_type(path, file_extension_and_mimetype_map_, default_file_mimetype_);
6998 res.set_content_provider(mm->size(), content_type, [mm](
size_t offset,
size_t length, DataSink &sink) ->
bool {
6999 sink.write(mm->data() + offset, length);
7004 return write_response_with_content(strm, close_connection, req, res);
7006 if (res.status == -1) {
7007 res.status = StatusCode::NotFound_404;
7010 return write_response(strm, close_connection, req, res);
7014inline bool Server::is_valid()
const {
return true; }
7016inline bool Server::process_and_close_socket(
socket_t sock) {
7017 std::string remote_addr;
7018 int remote_port = 0;
7019 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
7021 std::string local_addr;
7023 detail::get_local_ip_and_port(sock, local_addr, local_port);
7025 auto ret = detail::process_server_socket(
7026 svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, read_timeout_usec_,
7027 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm,
bool close_connection,
bool &connection_closed) {
7028 return process_request(strm, remote_addr, remote_port, local_addr, local_port, close_connection,
7029 connection_closed,
nullptr);
7032 detail::shutdown_socket(sock);
7033 detail::close_socket(sock);
7038inline ClientImpl::ClientImpl(
const std::string &host) : ClientImpl(host, 80, std::string(), std::string()) {}
7040inline ClientImpl::ClientImpl(
const std::string &host,
int port)
7041 : ClientImpl(host, port, std::string(), std::string()) {}
7043inline ClientImpl::ClientImpl(
const std::string &host,
int port,
const std::string &client_cert_path,
7044 const std::string &client_key_path)
7045 : host_(detail::escape_abstract_namespace_unix_domain(host)),
7047 host_and_port_(adjust_host_string(host_) +
":" + std::to_string(port)),
7048 client_cert_path_(client_cert_path),
7049 client_key_path_(client_key_path) {}
7051inline ClientImpl::~ClientImpl() {
7052 std::lock_guard<std::mutex> guard(socket_mutex_);
7053 shutdown_socket(socket_);
7054 close_socket(socket_);
7057inline bool ClientImpl::is_valid()
const {
return true; }
7059inline void ClientImpl::copy_settings(
const ClientImpl &rhs) {
7060 client_cert_path_ = rhs.client_cert_path_;
7061 client_key_path_ = rhs.client_key_path_;
7062 connection_timeout_sec_ = rhs.connection_timeout_sec_;
7063 read_timeout_sec_ = rhs.read_timeout_sec_;
7064 read_timeout_usec_ = rhs.read_timeout_usec_;
7065 write_timeout_sec_ = rhs.write_timeout_sec_;
7066 write_timeout_usec_ = rhs.write_timeout_usec_;
7067 basic_auth_username_ = rhs.basic_auth_username_;
7068 basic_auth_password_ = rhs.basic_auth_password_;
7069 bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
7070#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7071 digest_auth_username_ = rhs.digest_auth_username_;
7072 digest_auth_password_ = rhs.digest_auth_password_;
7074 keep_alive_ = rhs.keep_alive_;
7075 follow_location_ = rhs.follow_location_;
7076 url_encode_ = rhs.url_encode_;
7077 address_family_ = rhs.address_family_;
7078 tcp_nodelay_ = rhs.tcp_nodelay_;
7079 ipv6_v6only_ = rhs.ipv6_v6only_;
7080 socket_options_ = rhs.socket_options_;
7081 compress_ = rhs.compress_;
7082 decompress_ = rhs.decompress_;
7083 interface_ = rhs.interface_;
7084 proxy_host_ = rhs.proxy_host_;
7085 proxy_port_ = rhs.proxy_port_;
7086 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
7087 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
7088 proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
7089#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7090 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
7091 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
7093#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7094 ca_cert_file_path_ = rhs.ca_cert_file_path_;
7095 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
7096 ca_cert_store_ = rhs.ca_cert_store_;
7098#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7099 server_certificate_verification_ = rhs.server_certificate_verification_;
7100 server_hostname_verification_ = rhs.server_hostname_verification_;
7101 server_certificate_verifier_ = rhs.server_certificate_verifier_;
7103 logger_ = rhs.logger_;
7106inline socket_t ClientImpl::create_client_socket(Error &error)
const {
7107 if (!proxy_host_.empty() && proxy_port_ != -1) {
7108 return detail::create_client_socket(proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
7109 ipv6_v6only_, socket_options_, connection_timeout_sec_,
7110 connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
7111 write_timeout_sec_, write_timeout_usec_, interface_, error);
7116 auto it = addr_map_.find(host_);
7117 if (it != addr_map_.end()) {
7121 return detail::create_client_socket(host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_, socket_options_,
7122 connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_,
7123 read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_, error);
7126inline bool ClientImpl::create_and_connect_socket(Socket &socket, Error &error) {
7127 auto sock = create_client_socket(error);
7128 if (sock == INVALID_SOCKET) {
7135inline void ClientImpl::shutdown_ssl(Socket & ,
bool ) {
7138 assert(socket_requests_in_flight_ == 0 || socket_requests_are_from_thread_ == std::this_thread::get_id());
7141inline void ClientImpl::shutdown_socket(Socket &socket)
const {
7142 if (
socket.sock == INVALID_SOCKET) {
7145 detail::shutdown_socket(
socket.sock);
7148inline void ClientImpl::close_socket(Socket &socket) {
7155 assert(socket_requests_in_flight_ == 0 || socket_requests_are_from_thread_ == std::this_thread::get_id());
7158#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7159 assert(
socket.ssl ==
nullptr);
7161 if (
socket.sock == INVALID_SOCKET) {
7164 detail::close_socket(
socket.sock);
7165 socket.sock = INVALID_SOCKET;
7168inline bool ClientImpl::read_response_line(Stream &strm,
const Request &req, Response &res)
const {
7169 std::array<char, 2048> buf{};
7171 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
7173 if (!line_reader.getline()) {
7177#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7178 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
7180 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
7184 if (!std::regex_match(line_reader.ptr(),
m, re)) {
7185 return req.method ==
"CONNECT";
7187 res.version = std::string(
m[1]);
7188 res.status = std::stoi(std::string(
m[2]));
7189 res.reason = std::string(
m[3]);
7192 while (res.status == StatusCode::Continue_100) {
7193 if (!line_reader.getline()) {
7196 if (!line_reader.getline()) {
7200 if (!std::regex_match(line_reader.ptr(),
m, re)) {
7203 res.version = std::string(
m[1]);
7204 res.status = std::stoi(std::string(
m[2]));
7205 res.reason = std::string(
m[3]);
7211inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
7212 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
7213 auto ret = send_(req, res, error);
7214 if (error == Error::SSLPeerCouldBeClosed_) {
7216 ret = send_(req, res, error);
7221#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7222inline bool ClientImpl::is_ssl_peer_could_be_closed(SSL *ssl)
const {
7224 return !SSL_peek(ssl, buf, 1) && SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
7228inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
7230 std::lock_guard<std::mutex> guard(socket_mutex_);
7234 socket_should_be_closed_when_request_is_done_ =
false;
7236 auto is_alive =
false;
7237 if (socket_.is_open()) {
7238 is_alive = detail::is_socket_alive(socket_.sock);
7240#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7241 if (is_alive && is_ssl()) {
7242 if (is_ssl_peer_could_be_closed(socket_.ssl)) {
7253 const bool shutdown_gracefully =
false;
7254 shutdown_ssl(socket_, shutdown_gracefully);
7255 shutdown_socket(socket_);
7256 close_socket(socket_);
7261 if (!create_and_connect_socket(socket_, error)) {
7265#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7268 auto &scli =
static_cast<SSLClient &
>(*this);
7269 if (!proxy_host_.empty() && proxy_port_ != -1) {
7270 auto success =
false;
7271 if (!scli.connect_with_proxy(socket_, res, success, error)) {
7276 if (!scli.initialize_ssl(socket_, error)) {
7286 if (socket_requests_in_flight_ > 1) {
7287 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
7289 socket_requests_in_flight_ += 1;
7290 socket_requests_are_from_thread_ = std::this_thread::get_id();
7293 for (
const auto &header : default_headers_) {
7294 if (req.headers.find(header.first) == req.headers.end()) {
7295 req.headers.insert(header);
7300 auto close_connection = !keep_alive_;
7302 auto se = detail::scope_exit([&]() {
7304 std::lock_guard<std::mutex> guard(socket_mutex_);
7305 socket_requests_in_flight_ -= 1;
7306 if (socket_requests_in_flight_ <= 0) {
7307 assert(socket_requests_in_flight_ == 0);
7308 socket_requests_are_from_thread_ = std::thread::id();
7311 if (socket_should_be_closed_when_request_is_done_ || close_connection || !ret) {
7312 shutdown_ssl(socket_,
true);
7313 shutdown_socket(socket_);
7314 close_socket(socket_);
7318 ret = process_socket(socket_, [&](Stream &strm) {
return handle_request(strm, req, res, close_connection, error); });
7321 if (error == Error::Success) {
7322 error = Error::Unknown;
7329inline Result ClientImpl::send(
const Request &req) {
7331 return send_(std::move(req2));
7334inline Result ClientImpl::send_(Request &&req) {
7335 auto res = detail::make_unique<Response>();
7336 auto error = Error::Success;
7337 auto ret = send(req, *res, error);
7338 return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
7341inline bool ClientImpl::handle_request(Stream &strm, Request &req, Response &res,
bool close_connection, Error &error) {
7342 if (req.path.empty()) {
7343 error = Error::Connection;
7347 auto req_save = req;
7351 if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
7353 req2.path =
"http://" + host_and_port_ + req.path;
7354 ret = process_request(strm, req2, res, close_connection, error);
7356 req.path = req_save.path;
7358 ret = process_request(strm, req, res, close_connection, error);
7365 if (res.get_header_value(
"Connection") ==
"close" ||
7366 (res.version ==
"HTTP/1.0" && res.reason !=
"Connection established")) {
7374 std::lock_guard<std::mutex> guard(socket_mutex_);
7375 shutdown_ssl(socket_,
true);
7376 shutdown_socket(socket_);
7377 close_socket(socket_);
7380 if (300 < res.status && res.status < 400 && follow_location_) {
7382 ret = redirect(req, res, error);
7385#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7386 if ((res.status == StatusCode::Unauthorized_401 || res.status == StatusCode::ProxyAuthenticationRequired_407) &&
7387 req.authorization_count_ < 5) {
7388 auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
7389 const auto &username = is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
7390 const auto &password = is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
7392 if (!username.empty() && !password.empty()) {
7393 std::map<std::string, std::string> auth;
7394 if (detail::parse_www_authenticate(res, auth, is_proxy)) {
7395 Request new_req = req;
7396 new_req.authorization_count_ += 1;
7397 new_req.headers.erase(is_proxy ?
"Proxy-Authorization" :
"Authorization");
7398 new_req.headers.insert(detail::make_digest_authentication_header(
7399 req, auth, new_req.authorization_count_, detail::random_string(10), username, password, is_proxy));
7403 ret = send(new_req, new_res, error);
7415inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
7416 if (req.redirect_count_ == 0) {
7417 error = Error::ExceedRedirectCount;
7421 auto location = res.get_header_value(
"location");
7422 if (location.empty()) {
7426 const static std::regex re(
7427 R
"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
7430 if (!std::regex_match(location,
m, re)) {
7434 auto scheme = is_ssl() ?
"https" :
"http";
7436 auto next_scheme =
m[1].str();
7437 auto next_host =
m[2].str();
7438 if (next_host.empty()) {
7439 next_host =
m[3].str();
7441 auto port_str =
m[4].str();
7442 auto next_path =
m[5].str();
7443 auto next_query =
m[6].str();
7445 auto next_port = port_;
7446 if (!port_str.empty()) {
7447 next_port = std::stoi(port_str);
7448 }
else if (!next_scheme.empty()) {
7449 next_port = next_scheme ==
"https" ? 443 : 80;
7452 if (next_scheme.empty()) {
7453 next_scheme = scheme;
7455 if (next_host.empty()) {
7458 if (next_path.empty()) {
7462 auto path = detail::decode_url(next_path,
true) + next_query;
7464 if (next_scheme == scheme && next_host == host_ && next_port == port_) {
7465 return detail::redirect(*
this, req, res, path, location, error);
7467 if (next_scheme ==
"https") {
7468#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7469 SSLClient cli(next_host, next_port);
7470 cli.copy_settings(*
this);
7471 if (ca_cert_store_) {
7472 cli.set_ca_cert_store(ca_cert_store_);
7474 return detail::redirect(cli, req, res, path, location, error);
7479 ClientImpl cli(next_host, next_port);
7480 cli.copy_settings(*
this);
7481 return detail::redirect(cli, req, res, path, location, error);
7486inline bool ClientImpl::write_content_with_provider(Stream &strm,
const Request &req, Error &error)
const {
7487 auto is_shutting_down = []() {
return false; };
7489 if (req.is_chunked_content_provider_) {
7491 std::unique_ptr<detail::compressor> compressor;
7492#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7494 compressor = detail::make_unique<detail::gzip_compressor>();
7498 compressor = detail::make_unique<detail::nocompressor>();
7501 return detail::write_content_chunked(strm, req.content_provider_, is_shutting_down, *compressor, error);
7503 return detail::write_content(strm, req.content_provider_, 0, req.content_length_, is_shutting_down, error);
7507inline bool ClientImpl::write_request(Stream &strm, Request &req,
bool close_connection, Error &error) {
7509 if (close_connection) {
7510 if (!req.has_header(
"Connection")) {
7511 req.set_header(
"Connection",
"close");
7515 if (!req.has_header(
"Host")) {
7518 req.set_header(
"Host", host_);
7520 req.set_header(
"Host", host_and_port_);
7524 req.set_header(
"Host", host_);
7526 req.set_header(
"Host", host_and_port_);
7531 if (!req.has_header(
"Accept")) {
7532 req.set_header(
"Accept",
"*/*");
7535 if (!req.content_receiver) {
7536 if (!req.has_header(
"Accept-Encoding")) {
7537 std::string accept_encoding;
7538#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7539 accept_encoding =
"br";
7541#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7542 if (!accept_encoding.empty()) {
7543 accept_encoding +=
", ";
7545 accept_encoding +=
"gzip, deflate";
7547 req.set_header(
"Accept-Encoding", accept_encoding);
7550#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
7551 if (!req.has_header(
"User-Agent")) {
7552 auto agent = std::string(
"cpp-httplib/") + CPPHTTPLIB_VERSION;
7553 req.set_header(
"User-Agent", agent);
7558 if (req.body.empty()) {
7559 if (req.content_provider_) {
7560 if (!req.is_chunked_content_provider_) {
7561 if (!req.has_header(
"Content-Length")) {
7562 auto length = std::to_string(req.content_length_);
7563 req.set_header(
"Content-Length",
length);
7567 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH") {
7568 req.set_header(
"Content-Length",
"0");
7572 if (!req.has_header(
"Content-Type")) {
7573 req.set_header(
"Content-Type",
"text/plain");
7576 if (!req.has_header(
"Content-Length")) {
7577 auto length = std::to_string(req.body.size());
7578 req.set_header(
"Content-Length",
length);
7582 if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
7583 if (!req.has_header(
"Authorization")) {
7584 req.headers.insert(make_basic_authentication_header(basic_auth_username_, basic_auth_password_,
false));
7588 if (!proxy_basic_auth_username_.empty() && !proxy_basic_auth_password_.empty()) {
7589 if (!req.has_header(
"Proxy-Authorization")) {
7591 make_basic_authentication_header(proxy_basic_auth_username_, proxy_basic_auth_password_,
true));
7595 if (!bearer_token_auth_token_.empty()) {
7596 if (!req.has_header(
"Authorization")) {
7597 req.headers.insert(make_bearer_token_authentication_header(bearer_token_auth_token_,
false));
7601 if (!proxy_bearer_token_auth_token_.empty()) {
7602 if (!req.has_header(
"Proxy-Authorization")) {
7603 req.headers.insert(make_bearer_token_authentication_header(proxy_bearer_token_auth_token_,
true));
7609 detail::BufferStream bstrm;
7611 const auto &path_with_query = req.params.empty() ? req.path : append_query_params(req.path, req.params);
7613 const auto &path = url_encode_ ? detail::encode_url(path_with_query) : path_with_query;
7615 detail::write_request_line(bstrm, req.method, path);
7617 header_writer_(bstrm, req.headers);
7620 auto &data = bstrm.get_buffer();
7621 if (!detail::write_data(strm, data.data(), data.size())) {
7622 error = Error::Write;
7628 if (req.body.empty()) {
7629 return write_content_with_provider(strm, req, error);
7632 if (!detail::write_data(strm, req.body.data(), req.body.size())) {
7633 error = Error::Write;
7640inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
7641 Request &req,
const char *body,
size_t content_length, ContentProvider content_provider,
7642 ContentProviderWithoutLength content_provider_without_length,
const std::string &content_type, Error &error) {
7643 if (!content_type.empty()) {
7644 req.set_header(
"Content-Type", content_type);
7647#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7649 req.set_header(
"Content-Encoding",
"gzip");
7653#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7654 if (compress_ && !content_provider_without_length) {
7656 detail::gzip_compressor compressor;
7658 if (content_provider) {
7663 data_sink.write = [&](
const char *data,
size_t data_len) ->
bool {
7665 auto last = offset + data_len == content_length;
7668 compressor.compress(data, data_len, last, [&](
const char *compressed_data,
size_t compressed_data_len) {
7669 req.body.append(compressed_data, compressed_data_len);
7682 while (ok && offset < content_length) {
7683 if (!content_provider(offset, content_length - offset, data_sink)) {
7684 error = Error::Canceled;
7689 if (!compressor.compress(body, content_length,
true, [&](
const char *data,
size_t data_len) {
7690 req.body.append(data, data_len);
7693 error = Error::Compression;
7700 if (content_provider) {
7701 req.content_length_ = content_length;
7702 req.content_provider_ = std::move(content_provider);
7703 req.is_chunked_content_provider_ =
false;
7704 }
else if (content_provider_without_length) {
7705 req.content_length_ = 0;
7706 req.content_provider_ = detail::ContentProviderAdapter(std::move(content_provider_without_length));
7707 req.is_chunked_content_provider_ =
true;
7708 req.set_header(
"Transfer-Encoding",
"chunked");
7710 req.body.assign(body, content_length);
7714 auto res = detail::make_unique<Response>();
7715 return send(req, *res, error) ? std::move(res) : nullptr;
7718inline Result ClientImpl::send_with_content_provider(
const std::string &method,
const std::string &path,
7719 const Headers &headers,
const char *body,
size_t content_length,
7720 ContentProvider content_provider,
7721 ContentProviderWithoutLength content_provider_without_length,
7722 const std::string &content_type, Progress progress) {
7724 req.method = method;
7725 req.headers = headers;
7727 req.progress = progress;
7729 auto error = Error::Success;
7731 auto res = send_with_content_provider(req, body, content_length, std::move(content_provider),
7732 std::move(content_provider_without_length), content_type, error);
7734 return Result{std::move(res), error, std::move(req.headers)};
7737inline std::string ClientImpl::adjust_host_string(
const std::string &host)
const {
7738 if (host.find(
':') != std::string::npos) {
7739 return "[" + host +
"]";
7744inline bool ClientImpl::process_request(Stream &strm, Request &req, Response &res,
bool close_connection,
7747 if (!write_request(strm, req, close_connection, error)) {
7751#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7753 auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
7754 if (!is_proxy_enabled) {
7755 if (is_ssl_peer_could_be_closed(socket_.ssl)) {
7756 error = Error::SSLPeerCouldBeClosed_;
7764 if (!read_response_line(strm, req, res) || !detail::read_headers(strm, res.headers)) {
7765 error = Error::Read;
7770 if ((res.status != StatusCode::NoContent_204) && req.method !=
"HEAD" && req.method !=
"CONNECT") {
7771 auto redirect = 300 < res.status && res.status < 400 && follow_location_;
7773 if (req.response_handler && !redirect) {
7774 if (!req.response_handler(res)) {
7775 error = Error::Canceled;
7781 req.content_receiver
7782 ?
static_cast<ContentReceiverWithProgress
>([&](
const char *buf,
size_t n, uint64_t off, uint64_t
len) {
7786 auto ret = req.content_receiver(buf, n, off,
len);
7788 error = Error::Canceled;
7792 : static_cast<ContentReceiverWithProgress>(
7793 [&](const char *buf, size_t n, uint64_t , uint64_t ) {
7794 assert(res.body.size() + n <= res.body.max_size());
7795 res.body.append(buf, n);
7799 auto progress = [&](uint64_t current, uint64_t total) {
7800 if (!req.progress || redirect) {
7803 auto ret = req.progress(current, total);
7805 error = Error::Canceled;
7810 if (res.has_header(
"Content-Length")) {
7811 if (!req.content_receiver) {
7812 auto len = res.get_header_value_u64(
"Content-Length");
7813 if (
len > res.body.max_size()) {
7814 error = Error::Read;
7817 res.body.reserve(
len);
7822 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(), dummy_status, std::move(progress),
7823 std::move(out), decompress_)) {
7824 if (error != Error::Canceled) {
7825 error = Error::Read;
7839inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
7840 const std::string &boundary,
const MultipartFormDataItems &items,
7841 const MultipartFormDataProviderItems &provider_items)
const {
7842 size_t cur_item = 0;
7843 size_t cur_start = 0;
7846 return [&, cur_item, cur_start](
size_t offset, DataSink &sink)
mutable ->
bool {
7847 if (!offset && !items.empty()) {
7848 sink.os << detail::serialize_multipart_formdata(items, boundary,
false);
7850 }
else if (cur_item < provider_items.size()) {
7852 const auto &begin = detail::serialize_multipart_formdata_item_begin(provider_items[cur_item], boundary);
7853 offset += begin.size();
7859 auto has_data =
true;
7860 cur_sink.write = sink.write;
7861 cur_sink.done = [&]() { has_data =
false; };
7863 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
7868 sink.os << detail::serialize_multipart_formdata_item_end();
7874 sink.os << detail::serialize_multipart_formdata_finish(boundary);
7881inline bool ClientImpl::process_socket(
const Socket &socket, std::function<
bool(Stream &strm)> callback) {
7882 return detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
7883 write_timeout_usec_, std::move(callback));
7886inline bool ClientImpl::is_ssl()
const {
return false; }
7888inline Result ClientImpl::Get(
const std::string &path) {
return Get(path, Headers(), Progress()); }
7890inline Result ClientImpl::Get(
const std::string &path, Progress progress) {
7891 return Get(path, Headers(), std::move(progress));
7894inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers) {
7895 return Get(path, headers, Progress());
7898inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, Progress progress) {
7902 req.headers = headers;
7903 req.progress = std::move(progress);
7905 return send_(std::move(req));
7908inline Result ClientImpl::Get(
const std::string &path, ContentReceiver content_receiver) {
7909 return Get(path, Headers(),
nullptr, std::move(content_receiver),
nullptr);
7912inline Result ClientImpl::Get(
const std::string &path, ContentReceiver content_receiver, Progress progress) {
7913 return Get(path, Headers(),
nullptr, std::move(content_receiver), std::move(progress));
7916inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver) {
7917 return Get(path, headers,
nullptr, std::move(content_receiver),
nullptr);
7920inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver,
7921 Progress progress) {
7922 return Get(path, headers,
nullptr, std::move(content_receiver), std::move(progress));
7925inline Result ClientImpl::Get(
const std::string &path, ResponseHandler response_handler,
7926 ContentReceiver content_receiver) {
7927 return Get(path, Headers(), std::move(response_handler), std::move(content_receiver),
nullptr);
7930inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
7931 ContentReceiver content_receiver) {
7932 return Get(path, headers, std::move(response_handler), std::move(content_receiver),
nullptr);
7935inline Result ClientImpl::Get(
const std::string &path, ResponseHandler response_handler,
7936 ContentReceiver content_receiver, Progress progress) {
7937 return Get(path, Headers(), std::move(response_handler), std::move(content_receiver), std::move(progress));
7940inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
7941 ContentReceiver content_receiver, Progress progress) {
7945 req.headers = headers;
7946 req.response_handler = std::move(response_handler);
7947 req.content_receiver = [content_receiver](
const char *data,
size_t data_length, uint64_t ,
7948 uint64_t ) {
return content_receiver(data, data_length); };
7949 req.progress = std::move(progress);
7951 return send_(std::move(req));
7954inline Result ClientImpl::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
7955 Progress progress) {
7956 if (params.empty()) {
7957 return Get(path, headers);
7960 std::string path_with_query = append_query_params(path, params);
7961 return Get(path_with_query, headers, std::move(progress));
7964inline Result ClientImpl::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
7965 ContentReceiver content_receiver, Progress progress) {
7966 return Get(path, params, headers,
nullptr, std::move(content_receiver), std::move(progress));
7969inline Result ClientImpl::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
7970 ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) {
7971 if (params.empty()) {
7972 return Get(path, headers, std::move(response_handler), std::move(content_receiver), std::move(progress));
7975 std::string path_with_query = append_query_params(path, params);
7976 return Get(path_with_query, headers, std::move(response_handler), std::move(content_receiver), std::move(progress));
7979inline Result ClientImpl::Head(
const std::string &path) {
return Head(path, Headers()); }
7981inline Result ClientImpl::Head(
const std::string &path,
const Headers &headers) {
7983 req.method =
"HEAD";
7984 req.headers = headers;
7987 return send_(std::move(req));
7990inline Result ClientImpl::Post(
const std::string &path) {
return Post(path, std::string(), std::string()); }
7992inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers) {
7993 return Post(path, headers,
nullptr, 0, std::string());
7996inline Result ClientImpl::Post(
const std::string &path,
const char *body,
size_t content_length,
7997 const std::string &content_type) {
7998 return Post(path, Headers(), body, content_length, content_type,
nullptr);
8001inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
8002 const std::string &content_type) {
8003 return send_with_content_provider(
"POST", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8007inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
8008 const std::string &content_type, Progress progress) {
8009 return send_with_content_provider(
"POST", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8013inline Result ClientImpl::Post(
const std::string &path,
const std::string &body,
const std::string &content_type) {
8014 return Post(path, Headers(), body, content_type);
8017inline Result ClientImpl::Post(
const std::string &path,
const std::string &body,
const std::string &content_type,
8018 Progress progress) {
8019 return Post(path, Headers(), body, content_type, progress);
8022inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const std::string &body,
8023 const std::string &content_type) {
8024 return send_with_content_provider(
"POST", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8028inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const std::string &body,
8029 const std::string &content_type, Progress progress) {
8030 return send_with_content_provider(
"POST", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8034inline Result ClientImpl::Post(
const std::string &path,
const Params ¶ms) {
return Post(path, Headers(), params); }
8036inline Result ClientImpl::Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
8037 const std::string &content_type) {
8038 return Post(path, Headers(), content_length, std::move(content_provider), content_type);
8041inline Result ClientImpl::Post(
const std::string &path, ContentProviderWithoutLength content_provider,
8042 const std::string &content_type) {
8043 return Post(path, Headers(), std::move(content_provider), content_type);
8046inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
size_t content_length,
8047 ContentProvider content_provider,
const std::string &content_type) {
8048 return send_with_content_provider(
"POST", path, headers,
nullptr, content_length, std::move(content_provider),
8049 nullptr, content_type,
nullptr);
8052inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
8053 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
8054 return send_with_content_provider(
"POST", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
8055 content_type,
nullptr);
8058inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const Params ¶ms) {
8059 auto query = detail::params_to_query_str(params);
8060 return Post(path, headers, query,
"application/x-www-form-urlencoded");
8063inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const Params ¶ms,
8064 Progress progress) {
8065 auto query = detail::params_to_query_str(params);
8066 return Post(path, headers, query,
"application/x-www-form-urlencoded", progress);
8069inline Result ClientImpl::Post(
const std::string &path,
const MultipartFormDataItems &items) {
8070 return Post(path, Headers(), items);
8073inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items) {
8074 const auto &boundary = detail::make_multipart_data_boundary();
8075 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8076 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8077 return Post(path, headers, body, content_type);
8080inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
8081 const std::string &boundary) {
8082 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
8083 return Result{
nullptr, Error::UnsupportedMultipartBoundaryChars};
8086 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8087 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8088 return Post(path, headers, body, content_type);
8091inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
8092 const MultipartFormDataProviderItems &provider_items) {
8093 const auto &boundary = detail::make_multipart_data_boundary();
8094 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8095 return send_with_content_provider(
"POST", path, headers,
nullptr, 0,
nullptr,
8096 get_multipart_content_provider(boundary, items, provider_items), content_type,
8100inline Result ClientImpl::Put(
const std::string &path) {
return Put(path, std::string(), std::string()); }
8102inline Result ClientImpl::Put(
const std::string &path,
const char *body,
size_t content_length,
8103 const std::string &content_type) {
8104 return Put(path, Headers(), body, content_length, content_type);
8107inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
8108 const std::string &content_type) {
8109 return send_with_content_provider(
"PUT", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8113inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
8114 const std::string &content_type, Progress progress) {
8115 return send_with_content_provider(
"PUT", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8119inline Result ClientImpl::Put(
const std::string &path,
const std::string &body,
const std::string &content_type) {
8120 return Put(path, Headers(), body, content_type);
8123inline Result ClientImpl::Put(
const std::string &path,
const std::string &body,
const std::string &content_type,
8124 Progress progress) {
8125 return Put(path, Headers(), body, content_type, progress);
8128inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const std::string &body,
8129 const std::string &content_type) {
8130 return send_with_content_provider(
"PUT", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8134inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const std::string &body,
8135 const std::string &content_type, Progress progress) {
8136 return send_with_content_provider(
"PUT", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8140inline Result ClientImpl::Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
8141 const std::string &content_type) {
8142 return Put(path, Headers(), content_length, std::move(content_provider), content_type);
8145inline Result ClientImpl::Put(
const std::string &path, ContentProviderWithoutLength content_provider,
8146 const std::string &content_type) {
8147 return Put(path, Headers(), std::move(content_provider), content_type);
8150inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
size_t content_length,
8151 ContentProvider content_provider,
const std::string &content_type) {
8152 return send_with_content_provider(
"PUT", path, headers,
nullptr, content_length, std::move(content_provider),
nullptr,
8153 content_type,
nullptr);
8156inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
8157 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
8158 return send_with_content_provider(
"PUT", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
8159 content_type,
nullptr);
8162inline Result ClientImpl::Put(
const std::string &path,
const Params ¶ms) {
return Put(path, Headers(), params); }
8164inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const Params ¶ms) {
8165 auto query = detail::params_to_query_str(params);
8166 return Put(path, headers, query,
"application/x-www-form-urlencoded");
8169inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const Params ¶ms,
8170 Progress progress) {
8171 auto query = detail::params_to_query_str(params);
8172 return Put(path, headers, query,
"application/x-www-form-urlencoded", progress);
8175inline Result ClientImpl::Put(
const std::string &path,
const MultipartFormDataItems &items) {
8176 return Put(path, Headers(), items);
8179inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items) {
8180 const auto &boundary = detail::make_multipart_data_boundary();
8181 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8182 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8183 return Put(path, headers, body, content_type);
8186inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
8187 const std::string &boundary) {
8188 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
8189 return Result{
nullptr, Error::UnsupportedMultipartBoundaryChars};
8192 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8193 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8194 return Put(path, headers, body, content_type);
8197inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
8198 const MultipartFormDataProviderItems &provider_items) {
8199 const auto &boundary = detail::make_multipart_data_boundary();
8200 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8201 return send_with_content_provider(
"PUT", path, headers,
nullptr, 0,
nullptr,
8202 get_multipart_content_provider(boundary, items, provider_items), content_type,
8205inline Result ClientImpl::Patch(
const std::string &path) {
return Patch(path, std::string(), std::string()); }
8207inline Result ClientImpl::Patch(
const std::string &path,
const char *body,
size_t content_length,
8208 const std::string &content_type) {
8209 return Patch(path, Headers(), body, content_length, content_type);
8212inline Result ClientImpl::Patch(
const std::string &path,
const char *body,
size_t content_length,
8213 const std::string &content_type, Progress progress) {
8214 return Patch(path, Headers(), body, content_length, content_type, progress);
8217inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
const char *body,
8218 size_t content_length,
const std::string &content_type) {
8219 return Patch(path, headers, body, content_length, content_type,
nullptr);
8222inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
const char *body,
8223 size_t content_length,
const std::string &content_type, Progress progress) {
8224 return send_with_content_provider(
"PATCH", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8228inline Result ClientImpl::Patch(
const std::string &path,
const std::string &body,
const std::string &content_type) {
8229 return Patch(path, Headers(), body, content_type);
8232inline Result ClientImpl::Patch(
const std::string &path,
const std::string &body,
const std::string &content_type,
8233 Progress progress) {
8234 return Patch(path, Headers(), body, content_type, progress);
8237inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
8238 const std::string &content_type) {
8239 return Patch(path, headers, body, content_type,
nullptr);
8242inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
8243 const std::string &content_type, Progress progress) {
8244 return send_with_content_provider(
"PATCH", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8248inline Result ClientImpl::Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
8249 const std::string &content_type) {
8250 return Patch(path, Headers(), content_length, std::move(content_provider), content_type);
8253inline Result ClientImpl::Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
8254 const std::string &content_type) {
8255 return Patch(path, Headers(), std::move(content_provider), content_type);
8258inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
size_t content_length,
8259 ContentProvider content_provider,
const std::string &content_type) {
8260 return send_with_content_provider(
"PATCH", path, headers,
nullptr, content_length, std::move(content_provider),
8261 nullptr, content_type,
nullptr);
8264inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
8265 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
8266 return send_with_content_provider(
"PATCH", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
8267 content_type,
nullptr);
8270inline Result ClientImpl::Delete(
const std::string &path) {
8271 return Delete(path, Headers(), std::string(), std::string());
8274inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers) {
8275 return Delete(path, headers, std::string(), std::string());
8278inline Result ClientImpl::Delete(
const std::string &path,
const char *body,
size_t content_length,
8279 const std::string &content_type) {
8280 return Delete(path, Headers(), body, content_length, content_type);
8283inline Result ClientImpl::Delete(
const std::string &path,
const char *body,
size_t content_length,
8284 const std::string &content_type, Progress progress) {
8285 return Delete(path, Headers(), body, content_length, content_type, progress);
8288inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers,
const char *body,
8289 size_t content_length,
const std::string &content_type) {
8290 return Delete(path, headers, body, content_length, content_type,
nullptr);
8293inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers,
const char *body,
8294 size_t content_length,
const std::string &content_type, Progress progress) {
8296 req.method =
"DELETE";
8297 req.headers = headers;
8299 req.progress = progress;
8301 if (!content_type.empty()) {
8302 req.set_header(
"Content-Type", content_type);
8304 req.body.assign(body, content_length);
8306 return send_(std::move(req));
8309inline Result ClientImpl::Delete(
const std::string &path,
const std::string &body,
const std::string &content_type) {
8310 return Delete(path, Headers(), body.data(), body.size(), content_type);
8313inline Result ClientImpl::Delete(
const std::string &path,
const std::string &body,
const std::string &content_type,
8314 Progress progress) {
8315 return Delete(path, Headers(), body.data(), body.size(), content_type, progress);
8318inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
8319 const std::string &content_type) {
8320 return Delete(path, headers, body.data(), body.size(), content_type);
8323inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
8324 const std::string &content_type, Progress progress) {
8325 return Delete(path, headers, body.data(), body.size(), content_type, progress);
8328inline Result ClientImpl::Options(
const std::string &path) {
return Options(path, Headers()); }
8330inline Result ClientImpl::Options(
const std::string &path,
const Headers &headers) {
8332 req.method =
"OPTIONS";
8333 req.headers = headers;
8336 return send_(std::move(req));
8339inline void ClientImpl::stop() {
8340 std::lock_guard<std::mutex> guard(socket_mutex_);
8347 if (socket_requests_in_flight_ > 0) {
8348 shutdown_socket(socket_);
8352 socket_should_be_closed_when_request_is_done_ =
true;
8357 shutdown_ssl(socket_,
true);
8358 shutdown_socket(socket_);
8359 close_socket(socket_);
8362inline std::string ClientImpl::host()
const {
return host_; }
8364inline int ClientImpl::port()
const {
return port_; }
8366inline size_t ClientImpl::is_socket_open()
const {
8367 std::lock_guard<std::mutex> guard(socket_mutex_);
8368 return socket_.is_open();
8371inline socket_t ClientImpl::socket()
const {
return socket_.sock; }
8373inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
8374 connection_timeout_sec_ = sec;
8375 connection_timeout_usec_ = usec;
8378inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
8379 read_timeout_sec_ = sec;
8380 read_timeout_usec_ = usec;
8383inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
8384 write_timeout_sec_ = sec;
8385 write_timeout_usec_ = usec;
8388inline void ClientImpl::set_basic_auth(
const std::string &username,
const std::string &password) {
8389 basic_auth_username_ = username;
8390 basic_auth_password_ = password;
8393inline void ClientImpl::set_bearer_token_auth(
const std::string &token) { bearer_token_auth_token_ = token; }
8395#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8396inline void ClientImpl::set_digest_auth(
const std::string &username,
const std::string &password) {
8397 digest_auth_username_ = username;
8398 digest_auth_password_ = password;
8402inline void ClientImpl::set_keep_alive(
bool on) { keep_alive_ = on; }
8404inline void ClientImpl::set_follow_location(
bool on) { follow_location_ = on; }
8406inline void ClientImpl::set_url_encode(
bool on) { url_encode_ = on; }
8408inline void ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
8409 addr_map_ = std::move(addr_map);
8412inline void ClientImpl::set_default_headers(Headers headers) { default_headers_ = std::move(headers); }
8414inline void ClientImpl::set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer) {
8415 header_writer_ = writer;
8418inline void ClientImpl::set_address_family(
int family) { address_family_ = family; }
8420inline void ClientImpl::set_tcp_nodelay(
bool on) { tcp_nodelay_ = on; }
8422inline void ClientImpl::set_ipv6_v6only(
bool on) { ipv6_v6only_ = on; }
8424inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
8425 socket_options_ = std::move(socket_options);
8428inline void ClientImpl::set_compress(
bool on) { compress_ = on; }
8430inline void ClientImpl::set_decompress(
bool on) { decompress_ = on; }
8432inline void ClientImpl::set_interface(
const std::string &intf) { interface_ = intf; }
8434inline void ClientImpl::set_proxy(
const std::string &host,
int port) {
8439inline void ClientImpl::set_proxy_basic_auth(
const std::string &username,
const std::string &password) {
8440 proxy_basic_auth_username_ = username;
8441 proxy_basic_auth_password_ = password;
8444inline void ClientImpl::set_proxy_bearer_token_auth(
const std::string &token) {
8445 proxy_bearer_token_auth_token_ = token;
8448#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8449inline void ClientImpl::set_proxy_digest_auth(
const std::string &username,
const std::string &password) {
8450 proxy_digest_auth_username_ = username;
8451 proxy_digest_auth_password_ = password;
8454inline void ClientImpl::set_ca_cert_path(
const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path) {
8455 ca_cert_file_path_ = ca_cert_file_path;
8456 ca_cert_dir_path_ = ca_cert_dir_path;
8459inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
8460 if (ca_cert_store && ca_cert_store != ca_cert_store_) {
8461 ca_cert_store_ = ca_cert_store;
8465inline X509_STORE *ClientImpl::create_ca_cert_store(
const char *ca_cert, std::size_t size)
const {
8466 auto mem = BIO_new_mem_buf(ca_cert,
static_cast<int>(size));
8467 auto se = detail::scope_exit([&] { BIO_free_all(mem); });
8472 auto inf = PEM_X509_INFO_read_bio(mem,
nullptr,
nullptr,
nullptr);
8477 auto cts = X509_STORE_new();
8479 for (
auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
8480 auto itmp = sk_X509_INFO_value(inf, i);
8486 X509_STORE_add_cert(cts, itmp->x509);
8489 X509_STORE_add_crl(cts, itmp->crl);
8494 sk_X509_INFO_pop_free(inf, X509_INFO_free);
8498inline void ClientImpl::enable_server_certificate_verification(
bool enabled) {
8499 server_certificate_verification_ = enabled;
8502inline void ClientImpl::enable_server_hostname_verification(
bool enabled) { server_hostname_verification_ = enabled; }
8504inline void ClientImpl::set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier) {
8505 server_certificate_verifier_ = verifier;
8509inline void ClientImpl::set_logger(Logger logger) { logger_ = std::move(logger); }
8514#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8517template<
typename U,
typename V>
8518inline SSL *ssl_new(
socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, U SSL_connect_or_accept, V
setup) {
8521 std::lock_guard<std::mutex> guard(ctx_mutex);
8526 set_nonblocking(sock,
true);
8527 auto bio = BIO_new_socket(
static_cast<int>(sock), BIO_NOCLOSE);
8528 BIO_set_nbio(bio, 1);
8529 SSL_set_bio(ssl, bio, bio);
8531 if (!
setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
8534 std::lock_guard<std::mutex> guard(ctx_mutex);
8537 set_nonblocking(sock,
false);
8540 BIO_set_nbio(bio, 0);
8541 set_nonblocking(sock,
false);
8547inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
socket_t sock,
bool shutdown_gracefully) {
8552 if (shutdown_gracefully) {
8559 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
8561 auto ret = SSL_shutdown(ssl);
8563 std::this_thread::sleep_for(std::chrono::milliseconds{100});
8564 ret = SSL_shutdown(ssl);
8569 std::lock_guard<std::mutex> guard(ctx_mutex);
8574bool ssl_connect_or_accept_nonblocking(
socket_t sock, SSL *ssl, U ssl_connect_or_accept, time_t timeout_sec,
8575 time_t timeout_usec) {
8577 while ((res = ssl_connect_or_accept(ssl)) != 1) {
8578 auto err = SSL_get_error(ssl, res);
8580 case SSL_ERROR_WANT_READ:
8581 if (select_read(sock, timeout_sec, timeout_usec) > 0) {
8585 case SSL_ERROR_WANT_WRITE:
8586 if (select_write(sock, timeout_sec, timeout_usec) > 0) {
8599inline bool process_server_socket_ssl(
const std::atomic<socket_t> &svr_sock, SSL *ssl,
socket_t sock,
8600 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
8601 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
8602 time_t write_timeout_usec, T callback) {
8603 return process_server_socket_core(svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
8604 [&](
bool close_connection,
bool &connection_closed) {
8605 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
8606 write_timeout_sec, write_timeout_usec);
8607 return callback(strm, close_connection, connection_closed);
8612inline bool process_client_socket_ssl(SSL *ssl,
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
8613 time_t write_timeout_sec, time_t write_timeout_usec, T callback) {
8614 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);
8615 return callback(strm);
8620 SSLInit() { OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); }
8624inline SSLSocketStream::SSLSocketStream(
socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
8625 time_t write_timeout_sec, time_t write_timeout_usec)
8628 read_timeout_sec_(read_timeout_sec),
8629 read_timeout_usec_(read_timeout_usec),
8630 write_timeout_sec_(write_timeout_sec),
8631 write_timeout_usec_(write_timeout_usec) {
8632 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
8635inline SSLSocketStream::~SSLSocketStream() =
default;
8637inline bool SSLSocketStream::is_readable()
const {
8638 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
8641inline bool SSLSocketStream::is_writable()
const {
8642 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && is_socket_alive(sock_);
8645inline ssize_t SSLSocketStream::read(
char *ptr,
size_t size) {
8646 if (SSL_pending(ssl_) > 0) {
8647 return SSL_read(ssl_, ptr,
static_cast<int>(size));
8648 }
else if (is_readable()) {
8649 auto ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
8651 auto err = SSL_get_error(ssl_, ret);
8655 (err == SSL_ERROR_WANT_READ || (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT))) {
8657 while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
8659 if (SSL_pending(ssl_) > 0) {
8660 return SSL_read(ssl_, ptr,
static_cast<int>(size));
8661 }
else if (is_readable()) {
8662 std::this_thread::sleep_for(std::chrono::microseconds{10});
8663 ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
8667 err = SSL_get_error(ssl_, ret);
8678inline ssize_t SSLSocketStream::write(
const char *ptr,
size_t size) {
8679 if (is_writable()) {
8680 auto handle_size =
static_cast<int>(std::min<size_t>(size, (std::numeric_limits<int>::max)()));
8682 auto ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
8684 auto err = SSL_get_error(ssl_, ret);
8688 (err == SSL_ERROR_WANT_WRITE || (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT))) {
8690 while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
8692 if (is_writable()) {
8693 std::this_thread::sleep_for(std::chrono::microseconds{10});
8694 ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
8698 err = SSL_get_error(ssl_, ret);
8709inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
int &port)
const {
8710 detail::get_remote_ip_and_port(sock_, ip, port);
8713inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
int &port)
const {
8714 detail::get_local_ip_and_port(sock_, ip, port);
8717inline socket_t SSLSocketStream::socket()
const {
return sock_; }
8719static SSLInit sslinit_;
8724inline SSLServer::SSLServer(
const char *cert_path,
const char *private_key_path,
const char *client_ca_cert_file_path,
8725 const char *client_ca_cert_dir_path,
const char *private_key_password) {
8726 ctx_ = SSL_CTX_new(TLS_server_method());
8729 SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
8731 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8733 if (private_key_password !=
nullptr && (private_key_password[0] !=
'\0')) {
8734 SSL_CTX_set_default_passwd_cb_userdata(ctx_,
reinterpret_cast<void *
>(
const_cast<char *
>(private_key_password)));
8737 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
8738 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1 ||
8739 SSL_CTX_check_private_key(ctx_) != 1) {
8742 }
else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
8743 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, client_ca_cert_dir_path);
8745 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
8750inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store) {
8751 ctx_ = SSL_CTX_new(TLS_server_method());
8754 SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
8756 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8758 if (SSL_CTX_use_certificate(ctx_, cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
8761 }
else if (client_ca_cert_store) {
8762 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
8764 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
8769inline SSLServer::SSLServer(
const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
8770 ctx_ = SSL_CTX_new(TLS_method());
8772 if (!setup_ssl_ctx_callback(*ctx_)) {
8779inline SSLServer::~SSLServer() {
8785inline bool SSLServer::is_valid()
const {
return ctx_; }
8787inline SSL_CTX *SSLServer::ssl_context()
const {
return ctx_; }
8789inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store) {
8790 std::lock_guard<std::mutex> guard(ctx_mutex_);
8792 SSL_CTX_use_certificate(ctx_, cert);
8793 SSL_CTX_use_PrivateKey(ctx_, private_key);
8795 if (client_ca_cert_store !=
nullptr) {
8796 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
8800inline bool SSLServer::process_and_close_socket(
socket_t sock) {
8801 auto ssl = detail::ssl_new(
8802 sock, ctx_, ctx_mutex_,
8804 return detail::ssl_connect_or_accept_nonblocking(sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
8806 [](SSL * ) {
return true; });
8810 std::string remote_addr;
8811 int remote_port = 0;
8812 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
8814 std::string local_addr;
8816 detail::get_local_ip_and_port(sock, local_addr, local_port);
8818 ret = detail::process_server_socket_ssl(
8819 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, read_timeout_usec_,
8820 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm,
bool close_connection,
bool &connection_closed) {
8821 return process_request(strm, remote_addr, remote_port, local_addr, local_port, close_connection,
8822 connection_closed, [&](Request &req) { req.ssl = ssl; });
8827 const bool shutdown_gracefully = ret;
8828 detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
8831 detail::shutdown_socket(sock);
8832 detail::close_socket(sock);
8837inline SSLClient::SSLClient(
const std::string &host) : SSLClient(host, 443, std::string(), std::string()) {}
8839inline SSLClient::SSLClient(
const std::string &host,
int port) : SSLClient(host, port, std::string(), std::string()) {}
8841inline SSLClient::SSLClient(
const std::string &host,
int port,
const std::string &client_cert_path,
8842 const std::string &client_key_path,
const std::string &private_key_password)
8843 : ClientImpl(host, port, client_cert_path, client_key_path) {
8844 ctx_ = SSL_CTX_new(TLS_client_method());
8846 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8848 detail::split(&host_[0], &host_[host_.size()],
'.',
8849 [&](
const char *b,
const char *e) { host_components_.emplace_back(b, e); });
8851 if (!client_cert_path.empty() && !client_key_path.empty()) {
8852 if (!private_key_password.empty()) {
8853 SSL_CTX_set_default_passwd_cb_userdata(
8854 ctx_,
reinterpret_cast<void *
>(
const_cast<char *
>(private_key_password.c_str())));
8857 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), SSL_FILETYPE_PEM) != 1 ||
8858 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), SSL_FILETYPE_PEM) != 1) {
8865inline SSLClient::SSLClient(
const std::string &host,
int port, X509 *client_cert, EVP_PKEY *client_key,
8866 const std::string &private_key_password)
8867 : ClientImpl(host, port) {
8868 ctx_ = SSL_CTX_new(TLS_client_method());
8870 detail::split(&host_[0], &host_[host_.size()],
'.',
8871 [&](
const char *b,
const char *e) { host_components_.emplace_back(b, e); });
8873 if (client_cert !=
nullptr && client_key !=
nullptr) {
8874 if (!private_key_password.empty()) {
8875 SSL_CTX_set_default_passwd_cb_userdata(
8876 ctx_,
reinterpret_cast<void *
>(
const_cast<char *
>(private_key_password.c_str())));
8879 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
8886inline SSLClient::~SSLClient() {
8893 shutdown_ssl_impl(socket_,
true);
8896inline bool SSLClient::is_valid()
const {
return ctx_; }
8898inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
8899 if (ca_cert_store) {
8901 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
8903 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
8906 X509_STORE_free(ca_cert_store);
8911inline void SSLClient::load_ca_cert_store(
const char *ca_cert, std::size_t size) {
8912 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
8915inline long SSLClient::get_openssl_verify_result()
const {
return verify_result_; }
8917inline SSL_CTX *SSLClient::ssl_context()
const {
return ctx_; }
8919inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
8920 return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
8924inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
bool &success, Error &error) {
8927 if (!detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8928 write_timeout_usec_, [&](Stream &strm) {
8930 req2.method =
"CONNECT";
8931 req2.path = host_and_port_;
8932 return process_request(strm, req2, proxy_res, false, error);
8936 shutdown_ssl(socket,
true);
8937 shutdown_socket(socket);
8938 close_socket(socket);
8943 if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
8944 if (!proxy_digest_auth_username_.empty() && !proxy_digest_auth_password_.empty()) {
8945 std::map<std::string, std::string> auth;
8946 if (detail::parse_www_authenticate(proxy_res, auth,
true)) {
8947 proxy_res = Response();
8948 if (!detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8949 write_timeout_usec_, [&](Stream &strm) {
8951 req3.method =
"CONNECT";
8952 req3.path = host_and_port_;
8953 req3.headers.insert(detail::make_digest_authentication_header(
8954 req3, auth, 1, detail::random_string(10), proxy_digest_auth_username_,
8955 proxy_digest_auth_password_, true));
8956 return process_request(strm, req3, proxy_res, false, error);
8960 shutdown_ssl(socket,
true);
8961 shutdown_socket(socket);
8962 close_socket(socket);
8973 if (proxy_res.status != StatusCode::OK_200) {
8974 error = Error::ProxyConnection;
8975 res = std::move(proxy_res);
8978 shutdown_ssl(socket,
true);
8979 shutdown_socket(socket);
8980 close_socket(socket);
8987inline bool SSLClient::load_certs() {
8990 std::call_once(initialize_cert_, [&]() {
8991 std::lock_guard<std::mutex> guard(ctx_mutex_);
8992 if (!ca_cert_file_path_.empty()) {
8993 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
nullptr)) {
8996 }
else if (!ca_cert_dir_path_.empty()) {
8997 if (!SSL_CTX_load_verify_locations(ctx_,
nullptr, ca_cert_dir_path_.c_str())) {
9001 auto loaded =
false;
9003 loaded = detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
9004#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
9006 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
9010 SSL_CTX_set_default_verify_paths(ctx_);
9018inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
9019 auto ssl = detail::ssl_new(
9020 socket.sock, ctx_, ctx_mutex_,
9022 if (server_certificate_verification_) {
9023 if (!load_certs()) {
9024 error = Error::SSLLoadingCerts;
9027 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
9030 if (!detail::ssl_connect_or_accept_nonblocking(
socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
9031 connection_timeout_usec_)) {
9032 error = Error::SSLConnection;
9036 if (server_certificate_verification_) {
9037 if (server_certificate_verifier_) {
9038 if (!server_certificate_verifier_(ssl2)) {
9039 error = Error::SSLServerVerification;
9043 verify_result_ = SSL_get_verify_result(ssl2);
9045 if (verify_result_ != X509_V_OK) {
9046 error = Error::SSLServerVerification;
9050 auto server_cert = SSL_get1_peer_certificate(ssl2);
9051 auto se = detail::scope_exit([&] { X509_free(server_cert); });
9053 if (server_cert ==
nullptr) {
9054 error = Error::SSLServerVerification;
9058 if (server_hostname_verification_) {
9059 if (!verify_host(server_cert)) {
9060 error = Error::SSLServerHostnameVerification;
9070#if defined(OPENSSL_IS_BORINGSSL)
9071 SSL_set_tlsext_host_name(ssl2, host_.c_str());
9075 SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
9076 static_cast<void *
>(
const_cast<char *
>(host_.c_str())));
9086 shutdown_socket(socket);
9087 close_socket(socket);
9091inline void SSLClient::shutdown_ssl(Socket &socket,
bool shutdown_gracefully) {
9092 shutdown_ssl_impl(socket, shutdown_gracefully);
9095inline void SSLClient::shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully) {
9096 if (
socket.sock == INVALID_SOCKET) {
9097 assert(
socket.ssl ==
nullptr);
9101 detail::ssl_delete(ctx_mutex_,
socket.ssl,
socket.sock, shutdown_gracefully);
9104 assert(
socket.ssl ==
nullptr);
9107inline bool SSLClient::process_socket(
const Socket &socket, std::function<
bool(Stream &strm)> callback) {
9109 return detail::process_client_socket_ssl(
socket.ssl,
socket.sock, read_timeout_sec_, read_timeout_usec_,
9110 write_timeout_sec_, write_timeout_usec_, std::move(callback));
9113inline bool SSLClient::is_ssl()
const {
return true; }
9115inline bool SSLClient::verify_host(X509 *server_cert)
const {
9137 return verify_host_with_subject_alt_name(server_cert) || verify_host_with_common_name(server_cert);
9140inline bool SSLClient::verify_host_with_subject_alt_name(X509 *server_cert)
const {
9143 auto type = GEN_DNS;
9145 struct in6_addr addr6 {};
9146 struct in_addr addr {};
9147 size_t addr_len = 0;
9150 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
9152 addr_len =
sizeof(
struct in6_addr);
9153 }
else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
9155 addr_len =
sizeof(
struct in_addr);
9159 auto alt_names =
static_cast<const struct stack_st_GENERAL_NAME *
>(
9160 X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr,
nullptr));
9163 auto dsn_matched =
false;
9164 auto ip_matched =
false;
9166 auto count = sk_GENERAL_NAME_num(alt_names);
9168 for (
decltype(count) i = 0; i < count && !dsn_matched; i++) {
9169 auto val = sk_GENERAL_NAME_value(alt_names, i);
9171 auto name =
reinterpret_cast<const char *
>(ASN1_STRING_get0_data(
val->d.ia5));
9172 auto name_len =
static_cast<size_t>(ASN1_STRING_length(
val->d.ia5));
9176 dsn_matched = check_host_name(name, name_len);
9180 if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {
9188 if (dsn_matched || ip_matched) {
9193 GENERAL_NAMES_free(
const_cast<STACK_OF(GENERAL_NAME) *
>(
reinterpret_cast<const STACK_OF(GENERAL_NAME) *
>(alt_names)));
9197inline bool SSLClient::verify_host_with_common_name(X509 *server_cert)
const {
9198 const auto subject_name = X509_get_subject_name(server_cert);
9200 if (subject_name !=
nullptr) {
9202 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name,
sizeof(name));
9204 if (name_len != -1) {
9205 return check_host_name(name,
static_cast<size_t>(name_len));
9212inline bool SSLClient::check_host_name(
const char *pattern,
size_t pattern_len)
const {
9213 if (host_.size() == pattern_len && host_ == pattern) {
9219 std::vector<std::string> pattern_components;
9220 detail::split(&pattern[0], &pattern[pattern_len],
'.',
9221 [&](
const char *b,
const char *e) { pattern_components.emplace_back(b, e); });
9223 if (host_components_.size() != pattern_components.size()) {
9227 auto itr = pattern_components.begin();
9228 for (
const auto &
h : host_components_) {
9230 if (p !=
h && p !=
"*") {
9231 auto partial_match = (p.size() > 0 && p[p.size() - 1] ==
'*' && !p.compare(0, p.size() - 1,
h));
9232 if (!partial_match) {
9244inline Client::Client(
const std::string &scheme_host_port) : Client(scheme_host_port, std::string(), std::string()) {}
9246inline Client::Client(
const std::string &scheme_host_port,
const std::string &client_cert_path,
9247 const std::string &client_key_path) {
9248 const static std::regex re(R
"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
9251 if (std::regex_match(scheme_host_port,
m, re)) {
9252 auto scheme =
m[1].str();
9254#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9255 if (!scheme.empty() && (scheme !=
"http" && scheme !=
"https")) {
9257 if (!scheme.empty() && scheme !=
"http") {
9259#ifndef CPPHTTPLIB_NO_EXCEPTIONS
9260 std::string msg =
"'" + scheme +
"' scheme is not supported.";
9261 throw std::invalid_argument(msg);
9266 auto is_ssl = scheme ==
"https";
9268 auto host =
m[2].str();
9273 auto port_str =
m[4].str();
9274 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
9277#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9278 cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path, client_key_path);
9282 cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path, client_key_path);
9287 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80, client_cert_path, client_key_path);
9291inline Client::Client(
const std::string &host,
int port) : cli_(detail::make_unique<ClientImpl>(host, port)) {}
9293inline Client::Client(
const std::string &host,
int port,
const std::string &client_cert_path,
9294 const std::string &client_key_path)
9295 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path, client_key_path)) {}
9297inline Client::~Client() =
default;
9299inline bool Client::is_valid()
const {
return cli_ !=
nullptr && cli_->is_valid(); }
9301inline Result Client::Get(
const std::string &path) {
return cli_->Get(path); }
9302inline Result Client::Get(
const std::string &path,
const Headers &headers) {
return cli_->Get(path, headers); }
9303inline Result Client::Get(
const std::string &path, Progress progress) {
return cli_->Get(path, std::move(progress)); }
9304inline Result Client::Get(
const std::string &path,
const Headers &headers, Progress progress) {
9305 return cli_->Get(path, headers, std::move(progress));
9307inline Result Client::Get(
const std::string &path, ContentReceiver content_receiver) {
9308 return cli_->Get(path, std::move(content_receiver));
9310inline Result Client::Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver) {
9311 return cli_->Get(path, headers, std::move(content_receiver));
9313inline Result Client::Get(
const std::string &path, ContentReceiver content_receiver, Progress progress) {
9314 return cli_->Get(path, std::move(content_receiver), std::move(progress));
9316inline Result Client::Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver,
9317 Progress progress) {
9318 return cli_->Get(path, headers, std::move(content_receiver), std::move(progress));
9320inline Result Client::Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver) {
9321 return cli_->Get(path, std::move(response_handler), std::move(content_receiver));
9323inline Result Client::Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
9324 ContentReceiver content_receiver) {
9325 return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver));
9327inline Result Client::Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
9328 Progress progress) {
9329 return cli_->Get(path, std::move(response_handler), std::move(content_receiver), std::move(progress));
9331inline Result Client::Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
9332 ContentReceiver content_receiver, Progress progress) {
9333 return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver), std::move(progress));
9335inline Result Client::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, Progress progress) {
9336 return cli_->Get(path, params, headers, std::move(progress));
9338inline Result Client::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
9339 ContentReceiver content_receiver, Progress progress) {
9340 return cli_->Get(path, params, headers, std::move(content_receiver), std::move(progress));
9342inline Result Client::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
9343 ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) {
9344 return cli_->Get(path, params, headers, std::move(response_handler), std::move(content_receiver),
9345 std::move(progress));
9348inline Result Client::Head(
const std::string &path) {
return cli_->Head(path); }
9349inline Result Client::Head(
const std::string &path,
const Headers &headers) {
return cli_->Head(path, headers); }
9351inline Result Client::Post(
const std::string &path) {
return cli_->Post(path); }
9352inline Result Client::Post(
const std::string &path,
const Headers &headers) {
return cli_->Post(path, headers); }
9353inline Result Client::Post(
const std::string &path,
const char *body,
size_t content_length,
9354 const std::string &content_type) {
9355 return cli_->Post(path, body, content_length, content_type);
9357inline Result Client::Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9358 const std::string &content_type) {
9359 return cli_->Post(path, headers, body, content_length, content_type);
9361inline Result Client::Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9362 const std::string &content_type, Progress progress) {
9363 return cli_->Post(path, headers, body, content_length, content_type, progress);
9365inline Result Client::Post(
const std::string &path,
const std::string &body,
const std::string &content_type) {
9366 return cli_->Post(path, body, content_type);
9368inline Result Client::Post(
const std::string &path,
const std::string &body,
const std::string &content_type,
9369 Progress progress) {
9370 return cli_->Post(path, body, content_type, progress);
9372inline Result Client::Post(
const std::string &path,
const Headers &headers,
const std::string &body,
9373 const std::string &content_type) {
9374 return cli_->Post(path, headers, body, content_type);
9376inline Result Client::Post(
const std::string &path,
const Headers &headers,
const std::string &body,
9377 const std::string &content_type, Progress progress) {
9378 return cli_->Post(path, headers, body, content_type, progress);
9380inline Result Client::Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
9381 const std::string &content_type) {
9382 return cli_->Post(path, content_length, std::move(content_provider), content_type);
9384inline Result Client::Post(
const std::string &path, ContentProviderWithoutLength content_provider,
9385 const std::string &content_type) {
9386 return cli_->Post(path, std::move(content_provider), content_type);
9388inline Result Client::Post(
const std::string &path,
const Headers &headers,
size_t content_length,
9389 ContentProvider content_provider,
const std::string &content_type) {
9390 return cli_->Post(path, headers, content_length, std::move(content_provider), content_type);
9392inline Result Client::Post(
const std::string &path,
const Headers &headers,
9393 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
9394 return cli_->Post(path, headers, std::move(content_provider), content_type);
9396inline Result Client::Post(
const std::string &path,
const Params ¶ms) {
return cli_->Post(path, params); }
9397inline Result Client::Post(
const std::string &path,
const Headers &headers,
const Params ¶ms) {
9398 return cli_->Post(path, headers, params);
9400inline Result Client::Post(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress) {
9401 return cli_->Post(path, headers, params, progress);
9403inline Result Client::Post(
const std::string &path,
const MultipartFormDataItems &items) {
9404 return cli_->Post(path, items);
9406inline Result Client::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items) {
9407 return cli_->Post(path, headers, items);
9409inline Result Client::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
9410 const std::string &boundary) {
9411 return cli_->Post(path, headers, items, boundary);
9413inline Result Client::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
9414 const MultipartFormDataProviderItems &provider_items) {
9415 return cli_->Post(path, headers, items, provider_items);
9417inline Result Client::Put(
const std::string &path) {
return cli_->Put(path); }
9418inline Result Client::Put(
const std::string &path,
const char *body,
size_t content_length,
9419 const std::string &content_type) {
9420 return cli_->Put(path, body, content_length, content_type);
9422inline Result Client::Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9423 const std::string &content_type) {
9424 return cli_->Put(path, headers, body, content_length, content_type);
9426inline Result Client::Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9427 const std::string &content_type, Progress progress) {
9428 return cli_->Put(path, headers, body, content_length, content_type, progress);
9430inline Result Client::Put(
const std::string &path,
const std::string &body,
const std::string &content_type) {
9431 return cli_->Put(path, body, content_type);
9433inline Result Client::Put(
const std::string &path,
const std::string &body,
const std::string &content_type,
9434 Progress progress) {
9435 return cli_->Put(path, body, content_type, progress);
9437inline Result Client::Put(
const std::string &path,
const Headers &headers,
const std::string &body,
9438 const std::string &content_type) {
9439 return cli_->Put(path, headers, body, content_type);
9441inline Result Client::Put(
const std::string &path,
const Headers &headers,
const std::string &body,
9442 const std::string &content_type, Progress progress) {
9443 return cli_->Put(path, headers, body, content_type, progress);
9445inline Result Client::Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
9446 const std::string &content_type) {
9447 return cli_->Put(path, content_length, std::move(content_provider), content_type);
9449inline Result Client::Put(
const std::string &path, ContentProviderWithoutLength content_provider,
9450 const std::string &content_type) {
9451 return cli_->Put(path, std::move(content_provider), content_type);
9453inline Result Client::Put(
const std::string &path,
const Headers &headers,
size_t content_length,
9454 ContentProvider content_provider,
const std::string &content_type) {
9455 return cli_->Put(path, headers, content_length, std::move(content_provider), content_type);
9457inline Result Client::Put(
const std::string &path,
const Headers &headers,
9458 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
9459 return cli_->Put(path, headers, std::move(content_provider), content_type);
9461inline Result Client::Put(
const std::string &path,
const Params ¶ms) {
return cli_->Put(path, params); }
9462inline Result Client::Put(
const std::string &path,
const Headers &headers,
const Params ¶ms) {
9463 return cli_->Put(path, headers, params);
9465inline Result Client::Put(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress) {
9466 return cli_->Put(path, headers, params, progress);
9468inline Result Client::Put(
const std::string &path,
const MultipartFormDataItems &items) {
9469 return cli_->Put(path, items);
9471inline Result Client::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items) {
9472 return cli_->Put(path, headers, items);
9474inline Result Client::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
9475 const std::string &boundary) {
9476 return cli_->Put(path, headers, items, boundary);
9478inline Result Client::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
9479 const MultipartFormDataProviderItems &provider_items) {
9480 return cli_->Put(path, headers, items, provider_items);
9482inline Result Client::Patch(
const std::string &path) {
return cli_->Patch(path); }
9483inline Result Client::Patch(
const std::string &path,
const char *body,
size_t content_length,
9484 const std::string &content_type) {
9485 return cli_->Patch(path, body, content_length, content_type);
9487inline Result Client::Patch(
const std::string &path,
const char *body,
size_t content_length,
9488 const std::string &content_type, Progress progress) {
9489 return cli_->Patch(path, body, content_length, content_type, progress);
9491inline Result Client::Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9492 const std::string &content_type) {
9493 return cli_->Patch(path, headers, body, content_length, content_type);
9495inline Result Client::Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9496 const std::string &content_type, Progress progress) {
9497 return cli_->Patch(path, headers, body, content_length, content_type, progress);
9499inline Result Client::Patch(
const std::string &path,
const std::string &body,
const std::string &content_type) {
9500 return cli_->Patch(path, body, content_type);
9502inline Result Client::Patch(
const std::string &path,
const std::string &body,
const std::string &content_type,
9503 Progress progress) {
9504 return cli_->Patch(path, body, content_type, progress);
9506inline Result Client::Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
9507 const std::string &content_type) {
9508 return cli_->Patch(path, headers, body, content_type);
9510inline Result Client::Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
9511 const std::string &content_type, Progress progress) {
9512 return cli_->Patch(path, headers, body, content_type, progress);
9514inline Result Client::Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
9515 const std::string &content_type) {
9516 return cli_->Patch(path, content_length, std::move(content_provider), content_type);
9518inline Result Client::Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
9519 const std::string &content_type) {
9520 return cli_->Patch(path, std::move(content_provider), content_type);
9522inline Result Client::Patch(
const std::string &path,
const Headers &headers,
size_t content_length,
9523 ContentProvider content_provider,
const std::string &content_type) {
9524 return cli_->Patch(path, headers, content_length, std::move(content_provider), content_type);
9526inline Result Client::Patch(
const std::string &path,
const Headers &headers,
9527 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
9528 return cli_->Patch(path, headers, std::move(content_provider), content_type);
9530inline Result Client::Delete(
const std::string &path) {
return cli_->Delete(path); }
9531inline Result Client::Delete(
const std::string &path,
const Headers &headers) {
return cli_->Delete(path, headers); }
9532inline Result Client::Delete(
const std::string &path,
const char *body,
size_t content_length,
9533 const std::string &content_type) {
9534 return cli_->Delete(path, body, content_length, content_type);
9536inline Result Client::Delete(
const std::string &path,
const char *body,
size_t content_length,
9537 const std::string &content_type, Progress progress) {
9538 return cli_->Delete(path, body, content_length, content_type, progress);
9540inline Result Client::Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9541 const std::string &content_type) {
9542 return cli_->Delete(path, headers, body, content_length, content_type);
9544inline Result Client::Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9545 const std::string &content_type, Progress progress) {
9546 return cli_->Delete(path, headers, body, content_length, content_type, progress);
9548inline Result Client::Delete(
const std::string &path,
const std::string &body,
const std::string &content_type) {
9549 return cli_->Delete(path, body, content_type);
9551inline Result Client::Delete(
const std::string &path,
const std::string &body,
const std::string &content_type,
9552 Progress progress) {
9553 return cli_->Delete(path, body, content_type, progress);
9555inline Result Client::Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
9556 const std::string &content_type) {
9557 return cli_->Delete(path, headers, body, content_type);
9559inline Result Client::Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
9560 const std::string &content_type, Progress progress) {
9561 return cli_->Delete(path, headers, body, content_type, progress);
9563inline Result Client::Options(
const std::string &path) {
return cli_->Options(path); }
9564inline Result Client::Options(
const std::string &path,
const Headers &headers) {
return cli_->Options(path, headers); }
9566inline bool Client::send(Request &req, Response &res, Error &error) {
return cli_->send(req, res, error); }
9568inline Result Client::send(
const Request &req) {
return cli_->send(req); }
9570inline void Client::stop() { cli_->stop(); }
9572inline std::string Client::host()
const {
return cli_->host(); }
9574inline int Client::port()
const {
return cli_->port(); }
9576inline size_t Client::is_socket_open()
const {
return cli_->is_socket_open(); }
9578inline socket_t Client::socket()
const {
return cli_->socket(); }
9580inline void Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
9581 cli_->set_hostname_addr_map(std::move(addr_map));
9584inline void Client::set_default_headers(Headers headers) { cli_->set_default_headers(std::move(headers)); }
9586inline void Client::set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer) {
9587 cli_->set_header_writer(writer);
9590inline void Client::set_address_family(
int family) { cli_->set_address_family(family); }
9592inline void Client::set_tcp_nodelay(
bool on) { cli_->set_tcp_nodelay(on); }
9594inline void Client::set_socket_options(SocketOptions socket_options) {
9595 cli_->set_socket_options(std::move(socket_options));
9598inline void Client::set_connection_timeout(time_t sec, time_t usec) { cli_->set_connection_timeout(sec, usec); }
9600inline void Client::set_read_timeout(time_t sec, time_t usec) { cli_->set_read_timeout(sec, usec); }
9602inline void Client::set_write_timeout(time_t sec, time_t usec) { cli_->set_write_timeout(sec, usec); }
9604inline void Client::set_basic_auth(
const std::string &username,
const std::string &password) {
9605 cli_->set_basic_auth(username, password);
9607inline void Client::set_bearer_token_auth(
const std::string &token) { cli_->set_bearer_token_auth(token); }
9608#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9609inline void Client::set_digest_auth(
const std::string &username,
const std::string &password) {
9610 cli_->set_digest_auth(username, password);
9614inline void Client::set_keep_alive(
bool on) { cli_->set_keep_alive(on); }
9615inline void Client::set_follow_location(
bool on) { cli_->set_follow_location(on); }
9617inline void Client::set_url_encode(
bool on) { cli_->set_url_encode(on); }
9619inline void Client::set_compress(
bool on) { cli_->set_compress(on); }
9621inline void Client::set_decompress(
bool on) { cli_->set_decompress(on); }
9623inline void Client::set_interface(
const std::string &intf) { cli_->set_interface(intf); }
9625inline void Client::set_proxy(
const std::string &host,
int port) { cli_->set_proxy(host, port); }
9626inline void Client::set_proxy_basic_auth(
const std::string &username,
const std::string &password) {
9627 cli_->set_proxy_basic_auth(username, password);
9629inline void Client::set_proxy_bearer_token_auth(
const std::string &token) { cli_->set_proxy_bearer_token_auth(token); }
9630#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9631inline void Client::set_proxy_digest_auth(
const std::string &username,
const std::string &password) {
9632 cli_->set_proxy_digest_auth(username, password);
9636#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9637inline void Client::enable_server_certificate_verification(
bool enabled) {
9638 cli_->enable_server_certificate_verification(enabled);
9641inline void Client::enable_server_hostname_verification(
bool enabled) {
9642 cli_->enable_server_hostname_verification(enabled);
9645inline void Client::set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier) {
9646 cli_->set_server_certificate_verifier(verifier);
9650inline void Client::set_logger(Logger logger) { cli_->set_logger(std::move(logger)); }
9652#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9653inline void Client::set_ca_cert_path(
const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path) {
9654 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
9657inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
9659 static_cast<SSLClient &
>(*cli_).set_ca_cert_store(ca_cert_store);
9661 cli_->set_ca_cert_store(ca_cert_store);
9665inline void Client::load_ca_cert_store(
const char *ca_cert, std::size_t size) {
9666 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
9669inline long Client::get_openssl_verify_result()
const {
9671 return static_cast<SSLClient &
>(*cli_).get_openssl_verify_result();
9676inline SSL_CTX *Client::ssl_context()
const {
9678 return static_cast<SSLClient &
>(*cli_).ssl_context();
9688#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.
bool operator==(const DeferredEvent &test) const