20#ifndef CPPHTTPLIB_HTTPLIB_H
21#define CPPHTTPLIB_HTTPLIB_H
23#define CPPHTTPLIB_VERSION "0.18.2"
29#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
30#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
33#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
34#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
37#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
38#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
41#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
42#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
45#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
46#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
49#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
50#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
53#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
54#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
57#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
58#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
61#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
62#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
65#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
66#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
69#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
70#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
73#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
74#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
77#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
78#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
81#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
82#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
85#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
87#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
89#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
93#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
94#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
97#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
98#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
101#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
102#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
105#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
106#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
109#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
110#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
113#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
114#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
117#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
118#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
121#ifndef CPPHTTPLIB_TCP_NODELAY
122#define CPPHTTPLIB_TCP_NODELAY false
125#ifndef CPPHTTPLIB_IPV6_V6ONLY
126#define CPPHTTPLIB_IPV6_V6ONLY false
129#ifndef CPPHTTPLIB_RECV_BUFSIZ
130#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
133#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
134#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
137#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
138#define CPPHTTPLIB_THREAD_POOL_COUNT \
139 ((std::max)(8u, std::thread::hardware_concurrency() > 0 ? std::thread::hardware_concurrency() - 1 : 0))
142#ifndef CPPHTTPLIB_RECV_FLAGS
143#define CPPHTTPLIB_RECV_FLAGS 0
146#ifndef CPPHTTPLIB_SEND_FLAGS
147#define CPPHTTPLIB_SEND_FLAGS 0
150#ifndef CPPHTTPLIB_LISTEN_BACKLOG
151#define CPPHTTPLIB_LISTEN_BACKLOG 5
159#ifndef _CRT_SECURE_NO_WARNINGS
160#define _CRT_SECURE_NO_WARNINGS
163#ifndef _CRT_NONSTDC_NO_DEPRECATE
164#define _CRT_NONSTDC_NO_DEPRECATE
169#error Sorry, Visual Studio versions prior to 2015 are not supported
172#pragma comment(lib, "ws2_32.lib")
182#define S_ISREG(m) (((m) &S_IFREG) == S_IFREG)
186#define S_ISDIR(m) (((m) &S_IFDIR) == S_IFDIR)
197#ifndef WSA_FLAG_NO_HANDLE_INHERIT
198#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
202#ifdef CPPHTTPLIB_USE_POLL
203#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
208#include <arpa/inet.h>
209#if !defined(_AIX) && !defined(__MVS__)
215#define NI_MAXHOST 1025
220#include <netinet/in.h>
224#include <netinet/tcp.h>
225#ifdef CPPHTTPLIB_USE_POLL
231#include <sys/select.h>
232#include <sys/socket.h>
237#ifndef INVALID_SOCKET
238#define INVALID_SOCKET (-1)
248#include <condition_variable>
268#include <unordered_map>
269#include <unordered_set>
272#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
280#undef X509_EXTENSIONS
281#undef PKCS7_SIGNER_INFO
284#pragma comment(lib, "crypt32.lib")
286#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
287#include <TargetConditionals.h>
289#include <CoreFoundation/CoreFoundation.h>
290#include <Security/Security.h>
294#include <openssl/err.h>
295#include <openssl/evp.h>
296#include <openssl/ssl.h>
297#include <openssl/x509v3.h>
299#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
300#include <openssl/applink.c>
306#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
307#if OPENSSL_VERSION_NUMBER < 0x1010107f
308#error Please use OpenSSL or a current version of BoringSSL
310#define SSL_get1_peer_certificate SSL_get_peer_certificate
311#elif OPENSSL_VERSION_NUMBER < 0x30000000L
312#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
317#ifdef CPPHTTPLIB_ZLIB_SUPPORT
321#ifdef CPPHTTPLIB_BROTLI_SUPPORT
322#include <brotli/decode.h>
323#include <brotli/encode.h>
341template<
class T,
class... Args>
342typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>
::type make_unique(Args &&...args) {
343 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
347typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>
::type make_unique(std::size_t n) {
348 typedef typename std::remove_extent<T>::type RT;
349 return std::unique_ptr<T>(
new RT[n]);
352namespace case_ignore {
354inline unsigned char to_lower(
int c) {
355 const static unsigned char table[256] = {
356 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
357 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
358 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97,
359 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
360 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
361 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
362 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
363 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
364 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226, 227, 228, 229,
365 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 215, 248, 249, 250, 251,
366 252, 253, 254, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
367 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
369 return table[(
unsigned char) (
char) c];
372inline bool equal(
const std::string &a,
const std::string &b) {
373 return a.size() == b.size() &&
374 std::equal(a.begin(), a.end(), b.begin(), [](
char ca,
char cb) { return to_lower(ca) == to_lower(cb); });
378 bool operator()(
const std::string &a,
const std::string &b)
const {
return equal(a, b); }
382 size_t operator()(
const std::string &key)
const {
return hash_core(key.data(), key.size(), 0); }
384 size_t hash_core(
const char *s,
size_t l,
size_t h)
const {
386 : hash_core(s + 1,
l - 1,
389 (((std::numeric_limits<size_t>::max)() >> 6) &
h * 33) ^
390 static_cast<unsigned char>(to_lower(*s)));
400 explicit scope_exit(std::function<
void(
void)> &&f) : exit_function(std::move(f)), execute_on_destruction{
true} {}
402 scope_exit(scope_exit &&rhs) noexcept
403 : exit_function(std::move(rhs.exit_function)), execute_on_destruction{rhs.execute_on_destruction} {
408 if (execute_on_destruction) {
409 this->exit_function();
413 void release() { this->execute_on_destruction =
false; }
416 scope_exit(
const scope_exit &) =
delete;
417 void operator=(
const scope_exit &) =
delete;
418 scope_exit &operator=(scope_exit &&) =
delete;
420 std::function<void(
void)> exit_function;
421 bool execute_on_destruction;
429 SwitchingProtocol_101 = 101,
430 Processing_102 = 102,
431 EarlyHints_103 = 103,
437 NonAuthoritativeInformation_203 = 203,
439 ResetContent_205 = 205,
440 PartialContent_206 = 206,
441 MultiStatus_207 = 207,
442 AlreadyReported_208 = 208,
446 MultipleChoices_300 = 300,
447 MovedPermanently_301 = 301,
450 NotModified_304 = 304,
453 TemporaryRedirect_307 = 307,
454 PermanentRedirect_308 = 308,
457 BadRequest_400 = 400,
458 Unauthorized_401 = 401,
459 PaymentRequired_402 = 402,
462 MethodNotAllowed_405 = 405,
463 NotAcceptable_406 = 406,
464 ProxyAuthenticationRequired_407 = 407,
465 RequestTimeout_408 = 408,
468 LengthRequired_411 = 411,
469 PreconditionFailed_412 = 412,
470 PayloadTooLarge_413 = 413,
471 UriTooLong_414 = 414,
472 UnsupportedMediaType_415 = 415,
473 RangeNotSatisfiable_416 = 416,
474 ExpectationFailed_417 = 417,
476 MisdirectedRequest_421 = 421,
477 UnprocessableContent_422 = 422,
479 FailedDependency_424 = 424,
481 UpgradeRequired_426 = 426,
482 PreconditionRequired_428 = 428,
483 TooManyRequests_429 = 429,
484 RequestHeaderFieldsTooLarge_431 = 431,
485 UnavailableForLegalReasons_451 = 451,
488 InternalServerError_500 = 500,
489 NotImplemented_501 = 501,
490 BadGateway_502 = 502,
491 ServiceUnavailable_503 = 503,
492 GatewayTimeout_504 = 504,
493 HttpVersionNotSupported_505 = 505,
494 VariantAlsoNegotiates_506 = 506,
495 InsufficientStorage_507 = 507,
496 LoopDetected_508 = 508,
497 NotExtended_510 = 510,
498 NetworkAuthenticationRequired_511 = 511,
502 std::unordered_multimap<std::string, std::string, detail::case_ignore::hash, detail::case_ignore::equal_to>;
504using Params = std::multimap<std::string, std::string>;
505using Match = std::smatch;
507using Progress = std::function<bool(uint64_t current, uint64_t total)>;
510using ResponseHandler = std::function<bool(
const Response &response)>;
512struct MultipartFormData {
515 std::string filename;
516 std::string content_type;
518using MultipartFormDataItems = std::vector<MultipartFormData>;
519using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
523 DataSink() : os(&sb_), sb_(*
this) {}
525 DataSink(
const DataSink &) =
delete;
526 DataSink &operator=(
const DataSink &) =
delete;
527 DataSink(DataSink &&) =
delete;
528 DataSink &operator=(DataSink &&) =
delete;
530 std::function<bool(
const char *data,
size_t data_len)> write;
531 std::function<bool()> is_writable;
532 std::function<void()> done;
533 std::function<void(
const Headers &
trailer)> done_with_trailer;
537 class data_sink_streambuf final :
public std::streambuf {
539 explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
542 std::streamsize xsputn(
const char *s, std::streamsize n)
override {
543 sink_.write(s,
static_cast<size_t>(n));
551 data_sink_streambuf sb_;
554using ContentProvider = std::function<bool(
size_t offset,
size_t length, DataSink &sink)>;
556using ContentProviderWithoutLength = std::function<bool(
size_t offset, DataSink &sink)>;
558using ContentProviderResourceReleaser = std::function<void(
bool success)>;
560struct MultipartFormDataProvider {
562 ContentProviderWithoutLength provider;
563 std::string filename;
564 std::string content_type;
566using MultipartFormDataProviderItems = std::vector<MultipartFormDataProvider>;
568using ContentReceiverWithProgress =
569 std::function<bool(
const char *data,
size_t data_length, uint64_t offset, uint64_t total_length)>;
571using ContentReceiver = std::function<bool(
const char *data,
size_t data_length)>;
573using MultipartContentHeader = std::function<bool(
const MultipartFormData &file)>;
577 using Reader = std::function<bool(ContentReceiver receiver)>;
578 using MultipartReader = std::function<bool(MultipartContentHeader header, ContentReceiver receiver)>;
580 ContentReader(Reader reader, MultipartReader multipart_reader)
581 : reader_(std::move(reader)), multipart_reader_(std::move(multipart_reader)) {}
583 bool operator()(MultipartContentHeader header, ContentReceiver receiver)
const {
584 return multipart_reader_(std::move(header), std::move(receiver));
587 bool operator()(ContentReceiver receiver)
const {
return reader_(std::move(receiver)); }
590 MultipartReader multipart_reader_;
593using Range = std::pair<ssize_t, ssize_t>;
594using Ranges = std::vector<Range>;
603 std::string remote_addr;
604 int remote_port = -1;
605 std::string local_addr;
611 MultipartFormDataMap files;
614 std::unordered_map<std::string, std::string> path_params;
617 ResponseHandler response_handler;
618 ContentReceiverWithProgress content_receiver;
620#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
621 const SSL *ssl =
nullptr;
624 bool has_header(
const std::string &key)
const;
625 std::string get_header_value(
const std::string &key,
const char *def =
"",
size_t id = 0)
const;
626 uint64_t get_header_value_u64(
const std::string &key, uint64_t def = 0,
size_t id = 0)
const;
627 size_t get_header_value_count(
const std::string &key)
const;
628 void set_header(
const std::string &key,
const std::string &
val);
630 bool has_param(
const std::string &key)
const;
631 std::string get_param_value(
const std::string &key,
size_t id = 0)
const;
632 size_t get_param_value_count(
const std::string &key)
const;
634 bool is_multipart_form_data()
const;
636 bool has_file(
const std::string &key)
const;
637 MultipartFormData get_file_value(
const std::string &key)
const;
638 std::vector<MultipartFormData> get_file_values(
const std::string &key)
const;
641 size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
642 size_t content_length_ = 0;
643 ContentProvider content_provider_;
644 bool is_chunked_content_provider_ =
false;
645 size_t authorization_count_ = 0;
654 std::string location;
656 bool has_header(
const std::string &key)
const;
657 std::string get_header_value(
const std::string &key,
const char *def =
"",
size_t id = 0)
const;
658 uint64_t get_header_value_u64(
const std::string &key, uint64_t def = 0,
size_t id = 0)
const;
659 size_t get_header_value_count(
const std::string &key)
const;
660 void set_header(
const std::string &key,
const std::string &
val);
662 void set_redirect(
const std::string &url,
int status = StatusCode::Found_302);
663 void set_content(
const char *s,
size_t n,
const std::string &content_type);
664 void set_content(
const std::string &s,
const std::string &content_type);
665 void set_content(std::string &&s,
const std::string &content_type);
667 void set_content_provider(
size_t length,
const std::string &content_type, ContentProvider provider,
668 ContentProviderResourceReleaser resource_releaser =
nullptr);
670 void set_content_provider(
const std::string &content_type, ContentProviderWithoutLength provider,
671 ContentProviderResourceReleaser resource_releaser =
nullptr);
673 void set_chunked_content_provider(
const std::string &content_type, ContentProviderWithoutLength provider,
674 ContentProviderResourceReleaser resource_releaser =
nullptr);
676 void set_file_content(
const std::string &path,
const std::string &content_type);
677 void set_file_content(
const std::string &path);
679 Response() =
default;
680 Response(
const Response &) =
default;
681 Response &operator=(
const Response &) =
default;
682 Response(Response &&) =
default;
683 Response &operator=(Response &&) =
default;
685 if (content_provider_resource_releaser_) {
686 content_provider_resource_releaser_(content_provider_success_);
691 size_t content_length_ = 0;
692 ContentProvider content_provider_;
693 ContentProviderResourceReleaser content_provider_resource_releaser_;
694 bool is_chunked_content_provider_ =
false;
695 bool content_provider_success_ =
false;
696 std::string file_content_path_;
697 std::string file_content_content_type_;
702 virtual ~Stream() =
default;
704 virtual bool is_readable()
const = 0;
705 virtual bool is_writable()
const = 0;
707 virtual ssize_t read(
char *ptr,
size_t size) = 0;
708 virtual ssize_t write(
const char *ptr,
size_t size) = 0;
709 virtual void get_remote_ip_and_port(std::string &ip,
int &port)
const = 0;
710 virtual void get_local_ip_and_port(std::string &ip,
int &port)
const = 0;
711 virtual socket_t socket()
const = 0;
713 ssize_t write(
const char *ptr);
714 ssize_t write(
const std::string &s);
719 TaskQueue() =
default;
720 virtual ~TaskQueue() =
default;
722 virtual bool enqueue(std::function<
void()> fn) = 0;
723 virtual void shutdown() = 0;
725 virtual void on_idle() {}
728class ThreadPool final :
public TaskQueue {
730 explicit ThreadPool(
size_t n,
size_t mqr = 0) : shutdown_(
false), max_queued_requests_(mqr) {
732 threads_.emplace_back(worker(*
this));
737 ThreadPool(
const ThreadPool &) =
delete;
738 ~ThreadPool()
override =
default;
740 bool enqueue(std::function<
void()> fn)
override {
742 std::unique_lock<std::mutex> lock(mutex_);
743 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
746 jobs_.push_back(std::move(fn));
753 void shutdown()
override {
756 std::unique_lock<std::mutex> lock(mutex_);
763 for (
auto &t : threads_) {
770 explicit worker(ThreadPool &pool) : pool_(pool) {}
774 std::function<void()> fn;
776 std::unique_lock<std::mutex> lock(pool_.mutex_);
778 pool_.cond_.wait(lock, [&] {
return !pool_.jobs_.empty() || pool_.shutdown_; });
780 if (pool_.shutdown_ && pool_.jobs_.empty()) {
784 fn = pool_.jobs_.front();
785 pool_.jobs_.pop_front();
788 assert(
true ==
static_cast<bool>(fn));
792#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER)
793 OPENSSL_thread_stop();
799 friend struct worker;
801 std::vector<std::thread> threads_;
802 std::list<std::function<void()>> jobs_;
805 size_t max_queued_requests_ = 0;
807 std::condition_variable cond_;
811using Logger = std::function<void(
const Request &,
const Response &)>;
813using SocketOptions = std::function<void(
socket_t sock)>;
815void default_socket_options(
socket_t sock);
817const char *status_message(
int status);
819std::string get_bearer_token_auth(
const Request &req);
825 virtual ~MatcherBase() =
default;
828 virtual bool match(Request &request)
const = 0;
849class PathParamsMatcher final :
public MatcherBase {
851 PathParamsMatcher(
const std::string &pattern);
853 bool match(Request &request)
const override;
859 static constexpr char separator =
'/';
864 std::vector<std::string> static_fragments_;
867 std::vector<std::string> param_names_;
878class RegexMatcher final :
public MatcherBase {
880 RegexMatcher(
const std::string &pattern) : regex_(pattern) {}
882 bool match(Request &request)
const override;
888ssize_t write_headers(Stream &strm,
const Headers &headers);
894 using Handler = std::function<void(
const Request &, Response &)>;
896 using ExceptionHandler = std::function<void(
const Request &, Response &, std::exception_ptr ep)>;
898 enum class HandlerResponse {
902 using HandlerWithResponse = std::function<HandlerResponse(
const Request &, Response &)>;
904 using HandlerWithContentReader =
905 std::function<void(
const Request &, Response &,
const ContentReader &content_reader)>;
907 using Expect100ContinueHandler = std::function<int(
const Request &, Response &)>;
913 virtual bool is_valid()
const;
915 Server &Get(
const std::string &pattern, Handler handler);
916 Server &Post(
const std::string &pattern, Handler handler);
917 Server &Post(
const std::string &pattern, HandlerWithContentReader handler);
918 Server &Put(
const std::string &pattern, Handler handler);
919 Server &Put(
const std::string &pattern, HandlerWithContentReader handler);
920 Server &Patch(
const std::string &pattern, Handler handler);
921 Server &Patch(
const std::string &pattern, HandlerWithContentReader handler);
922 Server &Delete(
const std::string &pattern, Handler handler);
923 Server &Delete(
const std::string &pattern, HandlerWithContentReader handler);
924 Server &Options(
const std::string &pattern, Handler handler);
926 bool set_base_dir(
const std::string &dir,
const std::string &mount_point = std::string());
927 bool set_mount_point(
const std::string &mount_point,
const std::string &dir, Headers headers = Headers());
928 bool remove_mount_point(
const std::string &mount_point);
929 Server &set_file_extension_and_mimetype_mapping(
const std::string &ext,
const std::string &mime);
930 Server &set_default_file_mimetype(
const std::string &mime);
931 Server &set_file_request_handler(Handler handler);
933 template<
class ErrorHandlerFunc> Server &set_error_handler(ErrorHandlerFunc &&handler) {
934 return set_error_handler_core(std::forward<ErrorHandlerFunc>(handler),
935 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
938 Server &set_exception_handler(ExceptionHandler handler);
939 Server &set_pre_routing_handler(HandlerWithResponse handler);
940 Server &set_post_routing_handler(Handler handler);
942 Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
943 Server &set_logger(Logger logger);
945 Server &set_address_family(
int family);
946 Server &set_tcp_nodelay(
bool on);
947 Server &set_ipv6_v6only(
bool on);
948 Server &set_socket_options(SocketOptions socket_options);
950 Server &set_default_headers(Headers headers);
951 Server &set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer);
953 Server &set_keep_alive_max_count(
size_t count);
954 Server &set_keep_alive_timeout(time_t sec);
956 Server &set_read_timeout(time_t sec, time_t usec = 0);
957 template<
class Rep,
class Period> Server &set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration);
959 Server &set_write_timeout(time_t sec, time_t usec = 0);
960 template<
class Rep,
class Period> Server &set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration);
962 Server &set_idle_interval(time_t sec, time_t usec = 0);
963 template<
class Rep,
class Period> Server &set_idle_interval(
const std::chrono::duration<Rep, Period> &
duration);
965 Server &set_payload_max_length(
size_t length);
967 bool bind_to_port(
const std::string &host,
int port,
int socket_flags = 0);
968 int bind_to_any_port(
const std::string &host,
int socket_flags = 0);
969 bool listen_after_bind();
971 bool listen(
const std::string &host,
int port,
int socket_flags = 0);
973 bool is_running()
const;
974 void wait_until_ready()
const;
978 std::function<TaskQueue *(void)> new_task_queue;
981 bool process_request(Stream &strm,
const std::string &remote_addr,
int remote_port,
const std::string &local_addr,
982 int local_port,
bool close_connection,
bool &connection_closed,
983 const std::function<
void(Request &)> &setup_request);
985 std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
986 size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
987 time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
988 time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
989 time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
990 time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
991 time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
992 time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
993 time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
994 size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
997 using Handlers = std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
998 using HandlersForContentReader =
999 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, HandlerWithContentReader>>;
1001 static std::unique_ptr<detail::MatcherBase> make_matcher(
const std::string &pattern);
1003 Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1004 Server &set_error_handler_core(Handler handler, std::false_type);
1006 socket_t create_server_socket(
const std::string &host,
int port,
int socket_flags,
1007 SocketOptions socket_options)
const;
1008 int bind_internal(
const std::string &host,
int port,
int socket_flags);
1009 bool listen_internal();
1011 bool routing(Request &req, Response &res, Stream &strm);
1012 bool handle_file_request(
const Request &req, Response &res,
bool head =
false);
1013 bool dispatch_request(Request &req, Response &res,
const Handlers &handlers)
const;
1014 bool dispatch_request_for_content_reader(Request &req, Response &res, ContentReader content_reader,
1015 const HandlersForContentReader &handlers)
const;
1017 bool parse_request_line(
const char *s, Request &req)
const;
1018 void apply_ranges(
const Request &req, Response &res, std::string &content_type, std::string &boundary)
const;
1019 bool write_response(Stream &strm,
bool close_connection, Request &req, Response &res);
1020 bool write_response_with_content(Stream &strm,
bool close_connection,
const Request &req, Response &res);
1021 bool write_response_core(Stream &strm,
bool close_connection,
const Request &req, Response &res,
1022 bool need_apply_ranges);
1023 bool write_content_with_provider(Stream &strm,
const Request &req, Response &res,
const std::string &boundary,
1024 const std::string &content_type);
1025 bool read_content(Stream &strm, Request &req, Response &res);
1026 bool read_content_with_content_receiver(Stream &strm, Request &req, Response &res, ContentReceiver receiver,
1027 MultipartContentHeader multipart_header, ContentReceiver multipart_receiver);
1028 bool read_content_core(Stream &strm, Request &req, Response &res, ContentReceiver receiver,
1029 MultipartContentHeader multipart_header, ContentReceiver multipart_receiver)
const;
1031 virtual bool process_and_close_socket(
socket_t sock);
1033 std::atomic<bool> is_running_{
false};
1034 std::atomic<bool> is_decommisioned{
false};
1036 struct MountPointEntry {
1037 std::string mount_point;
1038 std::string base_dir;
1041 std::vector<MountPointEntry> base_dirs_;
1042 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1043 std::string default_file_mimetype_ =
"application/octet-stream";
1044 Handler file_request_handler_;
1046 Handlers get_handlers_;
1047 Handlers post_handlers_;
1048 HandlersForContentReader post_handlers_for_content_reader_;
1049 Handlers put_handlers_;
1050 HandlersForContentReader put_handlers_for_content_reader_;
1051 Handlers patch_handlers_;
1052 HandlersForContentReader patch_handlers_for_content_reader_;
1053 Handlers delete_handlers_;
1054 HandlersForContentReader delete_handlers_for_content_reader_;
1055 Handlers options_handlers_;
1057 HandlerWithResponse error_handler_;
1058 ExceptionHandler exception_handler_;
1059 HandlerWithResponse pre_routing_handler_;
1060 Handler post_routing_handler_;
1061 Expect100ContinueHandler expect_100_continue_handler_;
1065 int address_family_ = AF_UNSPEC;
1066 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1067 bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1068 SocketOptions socket_options_ = default_socket_options;
1070 Headers default_headers_;
1071 std::function<
ssize_t(Stream &, Headers &)> header_writer_ = detail::write_headers;
1081 ExceedRedirectCount,
1085 SSLServerVerification,
1086 SSLServerHostnameVerification,
1087 UnsupportedMultipartBoundaryChars,
1093 SSLPeerCouldBeClosed_,
1096std::string to_string(Error error);
1098std::ostream &operator<<(std::ostream &os,
const Error &obj);
1103 Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers = Headers{})
1104 : res_(std::move(res)), err_(err), request_headers_(std::move(request_headers)) {}
1106 operator bool()
const {
return res_ !=
nullptr; }
1107 bool operator==(std::nullptr_t)
const {
return res_ ==
nullptr; }
1108 bool operator!=(std::nullptr_t)
const {
return res_ !=
nullptr; }
1109 const Response &value()
const {
return *res_; }
1110 Response &value() {
return *res_; }
1111 const Response &operator*()
const {
return *res_; }
1112 Response &operator*() {
return *res_; }
1113 const Response *operator->()
const {
return res_.get(); }
1114 Response *operator->() {
return res_.get(); }
1117 Error error()
const {
return err_; }
1120 bool has_request_header(
const std::string &key)
const;
1121 std::string get_request_header_value(
const std::string &key,
const char *def =
"",
size_t id = 0)
const;
1122 uint64_t get_request_header_value_u64(
const std::string &key, uint64_t def = 0,
size_t id = 0)
const;
1123 size_t get_request_header_value_count(
const std::string &key)
const;
1126 std::unique_ptr<Response> res_;
1127 Error err_ = Error::Unknown;
1128 Headers request_headers_;
1133 explicit ClientImpl(
const std::string &host);
1135 explicit ClientImpl(
const std::string &host,
int port);
1137 explicit ClientImpl(
const std::string &host,
int port,
const std::string &client_cert_path,
1138 const std::string &client_key_path);
1140 virtual ~ClientImpl();
1142 virtual bool is_valid()
const;
1144 Result Get(
const std::string &path);
1145 Result Get(
const std::string &path,
const Headers &headers);
1146 Result Get(
const std::string &path, Progress progress);
1147 Result Get(
const std::string &path,
const Headers &headers, Progress progress);
1148 Result Get(
const std::string &path, ContentReceiver content_receiver);
1149 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver);
1150 Result Get(
const std::string &path, ContentReceiver content_receiver, Progress progress);
1151 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver, Progress progress);
1152 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver);
1153 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
1154 ContentReceiver content_receiver);
1155 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
1157 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
1158 ContentReceiver content_receiver, Progress progress);
1160 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, Progress progress =
nullptr);
1161 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ContentReceiver content_receiver,
1162 Progress progress =
nullptr);
1163 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ResponseHandler response_handler,
1164 ContentReceiver content_receiver, Progress progress =
nullptr);
1166 Result Head(
const std::string &path);
1167 Result Head(
const std::string &path,
const Headers &headers);
1169 Result Post(
const std::string &path);
1170 Result Post(
const std::string &path,
const Headers &headers);
1171 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1172 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1173 const std::string &content_type);
1174 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1175 const std::string &content_type, Progress progress);
1176 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type);
1177 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1178 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
1179 const std::string &content_type);
1180 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1182 Result Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1183 const std::string &content_type);
1184 Result Post(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1185 Result Post(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1186 const std::string &content_type);
1187 Result Post(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1188 const std::string &content_type);
1189 Result Post(
const std::string &path,
const Params ¶ms);
1190 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms);
1191 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress);
1192 Result Post(
const std::string &path,
const MultipartFormDataItems &items);
1193 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items);
1194 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1195 const std::string &boundary);
1196 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1197 const MultipartFormDataProviderItems &provider_items);
1199 Result Put(
const std::string &path);
1200 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1201 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1202 const std::string &content_type);
1203 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1204 const std::string &content_type, Progress progress);
1205 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type);
1206 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1207 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type);
1208 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1210 Result Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1211 const std::string &content_type);
1212 Result Put(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1213 Result Put(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1214 const std::string &content_type);
1215 Result Put(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1216 const std::string &content_type);
1217 Result Put(
const std::string &path,
const Params ¶ms);
1218 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms);
1219 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress);
1220 Result Put(
const std::string &path,
const MultipartFormDataItems &items);
1221 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items);
1222 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1223 const std::string &boundary);
1224 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1225 const MultipartFormDataProviderItems &provider_items);
1227 Result Patch(
const std::string &path);
1228 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1229 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1231 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1232 const std::string &content_type);
1233 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1234 const std::string &content_type, Progress progress);
1235 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type);
1236 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1237 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
1238 const std::string &content_type);
1239 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
1240 const std::string &content_type, Progress progress);
1241 Result Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1242 const std::string &content_type);
1243 Result Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1244 Result Patch(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1245 const std::string &content_type);
1246 Result Patch(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1247 const std::string &content_type);
1249 Result Delete(
const std::string &path);
1250 Result Delete(
const std::string &path,
const Headers &headers);
1251 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1252 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1254 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1255 const std::string &content_type);
1256 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1257 const std::string &content_type, Progress progress);
1258 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type);
1259 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1260 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
1261 const std::string &content_type);
1262 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
1263 const std::string &content_type, Progress progress);
1265 Result Options(
const std::string &path);
1266 Result Options(
const std::string &path,
const Headers &headers);
1268 bool send(Request &req, Response &res, Error &error);
1269 Result send(
const Request &req);
1273 std::string host()
const;
1276 size_t is_socket_open()
const;
1279 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1281 void set_default_headers(Headers headers);
1283 void set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer);
1285 void set_address_family(
int family);
1286 void set_tcp_nodelay(
bool on);
1287 void set_ipv6_v6only(
bool on);
1288 void set_socket_options(SocketOptions socket_options);
1290 void set_connection_timeout(time_t sec, time_t usec = 0);
1291 template<
class Rep,
class Period>
void set_connection_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1293 void set_read_timeout(time_t sec, time_t usec = 0);
1294 template<
class Rep,
class Period>
void set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1296 void set_write_timeout(time_t sec, time_t usec = 0);
1297 template<
class Rep,
class Period>
void set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1299 void set_basic_auth(
const std::string &username,
const std::string &password);
1300 void set_bearer_token_auth(
const std::string &token);
1301#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1302 void set_digest_auth(
const std::string &username,
const std::string &password);
1305 void set_keep_alive(
bool on);
1306 void set_follow_location(
bool on);
1308 void set_url_encode(
bool on);
1310 void set_compress(
bool on);
1312 void set_decompress(
bool on);
1314 void set_interface(
const std::string &intf);
1316 void set_proxy(
const std::string &host,
int port);
1317 void set_proxy_basic_auth(
const std::string &username,
const std::string &password);
1318 void set_proxy_bearer_token_auth(
const std::string &token);
1319#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1320 void set_proxy_digest_auth(
const std::string &username,
const std::string &password);
1323#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1324 void set_ca_cert_path(
const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path = std::string());
1325 void set_ca_cert_store(X509_STORE *ca_cert_store);
1326 X509_STORE *create_ca_cert_store(
const char *ca_cert, std::size_t size)
const;
1329#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1330 void enable_server_certificate_verification(
bool enabled);
1331 void enable_server_hostname_verification(
bool enabled);
1332 void set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier);
1335 void set_logger(Logger logger);
1340#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1344 bool is_open()
const {
return sock != INVALID_SOCKET; }
1347 virtual bool create_and_connect_socket(Socket &socket, Error &error);
1356 virtual void shutdown_ssl(Socket &socket,
bool shutdown_gracefully);
1357 void shutdown_socket(Socket &socket)
const;
1358 void close_socket(Socket &socket);
1360 bool process_request(Stream &strm, Request &req, Response &res,
bool close_connection, Error &error);
1362 bool write_content_with_provider(Stream &strm,
const Request &req, Error &error)
const;
1364 void copy_settings(
const ClientImpl &rhs);
1367 const std::string host_;
1369 const std::string host_and_port_;
1373 mutable std::mutex socket_mutex_;
1374 std::recursive_mutex request_mutex_;
1377 size_t socket_requests_in_flight_ = 0;
1378 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1379 bool socket_should_be_closed_when_request_is_done_ =
false;
1382 std::map<std::string, std::string> addr_map_;
1385 Headers default_headers_;
1388 std::function<
ssize_t(Stream &, Headers &)> header_writer_ = detail::write_headers;
1391 std::string client_cert_path_;
1392 std::string client_key_path_;
1394 time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1395 time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1396 time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
1397 time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1398 time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1399 time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1401 std::string basic_auth_username_;
1402 std::string basic_auth_password_;
1403 std::string bearer_token_auth_token_;
1404#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1405 std::string digest_auth_username_;
1406 std::string digest_auth_password_;
1409 bool keep_alive_ =
false;
1410 bool follow_location_ =
false;
1412 bool url_encode_ =
true;
1414 int address_family_ = AF_UNSPEC;
1415 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1416 bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1417 SocketOptions socket_options_ =
nullptr;
1419 bool compress_ =
false;
1420 bool decompress_ =
true;
1422 std::string interface_;
1424 std::string proxy_host_;
1425 int proxy_port_ = -1;
1427 std::string proxy_basic_auth_username_;
1428 std::string proxy_basic_auth_password_;
1429 std::string proxy_bearer_token_auth_token_;
1430#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1431 std::string proxy_digest_auth_username_;
1432 std::string proxy_digest_auth_password_;
1435#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1436 std::string ca_cert_file_path_;
1437 std::string ca_cert_dir_path_;
1439 X509_STORE *ca_cert_store_ =
nullptr;
1442#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1443 bool server_certificate_verification_ =
true;
1444 bool server_hostname_verification_ =
true;
1445 std::function<bool(SSL *ssl)> server_certificate_verifier_;
1451 bool send_(Request &req, Response &res, Error &error);
1452 Result send_(Request &&req);
1454#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1455 bool is_ssl_peer_could_be_closed(SSL *ssl)
const;
1457 socket_t create_client_socket(Error &error)
const;
1458 bool read_response_line(Stream &strm,
const Request &req, Response &res)
const;
1459 bool write_request(Stream &strm, Request &req,
bool close_connection, Error &error);
1460 bool redirect(Request &req, Response &res, Error &error);
1461 bool handle_request(Stream &strm, Request &req, Response &res,
bool close_connection, Error &error);
1462 std::unique_ptr<Response> send_with_content_provider(Request &req,
const char *body,
size_t content_length,
1463 ContentProvider content_provider,
1464 ContentProviderWithoutLength content_provider_without_length,
1465 const std::string &content_type, Error &error);
1466 Result send_with_content_provider(
const std::string &method,
const std::string &path,
const Headers &headers,
1467 const char *body,
size_t content_length, ContentProvider content_provider,
1468 ContentProviderWithoutLength content_provider_without_length,
1469 const std::string &content_type, Progress progress);
1470 ContentProviderWithoutLength get_multipart_content_provider(
1471 const std::string &boundary,
const MultipartFormDataItems &items,
1472 const MultipartFormDataProviderItems &provider_items)
const;
1474 std::string adjust_host_string(
const std::string &host)
const;
1476 virtual bool process_socket(
const Socket &socket, std::function<
bool(Stream &strm)> callback);
1477 virtual bool is_ssl()
const;
1483 explicit Client(
const std::string &scheme_host_port);
1485 explicit Client(
const std::string &scheme_host_port,
const std::string &client_cert_path,
1486 const std::string &client_key_path);
1489 explicit Client(
const std::string &host,
int port);
1491 explicit Client(
const std::string &host,
int port,
const std::string &client_cert_path,
1492 const std::string &client_key_path);
1494 Client(Client &&) =
default;
1495 Client &operator=(Client &&) =
default;
1499 bool is_valid()
const;
1501 Result Get(
const std::string &path);
1502 Result Get(
const std::string &path,
const Headers &headers);
1503 Result Get(
const std::string &path, Progress progress);
1504 Result Get(
const std::string &path,
const Headers &headers, Progress progress);
1505 Result Get(
const std::string &path, ContentReceiver content_receiver);
1506 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver);
1507 Result Get(
const std::string &path, ContentReceiver content_receiver, Progress progress);
1508 Result Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver, Progress progress);
1509 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver);
1510 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
1511 ContentReceiver content_receiver);
1512 Result Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
1513 ContentReceiver content_receiver, Progress progress);
1514 Result Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
1517 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, Progress progress =
nullptr);
1518 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ContentReceiver content_receiver,
1519 Progress progress =
nullptr);
1520 Result Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, ResponseHandler response_handler,
1521 ContentReceiver content_receiver, Progress progress =
nullptr);
1523 Result Head(
const std::string &path);
1524 Result Head(
const std::string &path,
const Headers &headers);
1526 Result Post(
const std::string &path);
1527 Result Post(
const std::string &path,
const Headers &headers);
1528 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1529 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1530 const std::string &content_type);
1531 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1532 const std::string &content_type, Progress progress);
1533 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type);
1534 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1535 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
1536 const std::string &content_type);
1537 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1539 Result Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1540 const std::string &content_type);
1541 Result Post(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1542 Result Post(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1543 const std::string &content_type);
1544 Result Post(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1545 const std::string &content_type);
1546 Result Post(
const std::string &path,
const Params ¶ms);
1547 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms);
1548 Result Post(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress);
1549 Result Post(
const std::string &path,
const MultipartFormDataItems &items);
1550 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items);
1551 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1552 const std::string &boundary);
1553 Result Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1554 const MultipartFormDataProviderItems &provider_items);
1556 Result Put(
const std::string &path);
1557 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1558 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1559 const std::string &content_type);
1560 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1561 const std::string &content_type, Progress progress);
1562 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type);
1563 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1564 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type);
1565 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1567 Result Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1568 const std::string &content_type);
1569 Result Put(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1570 Result Put(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1571 const std::string &content_type);
1572 Result Put(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1573 const std::string &content_type);
1574 Result Put(
const std::string &path,
const Params ¶ms);
1575 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms);
1576 Result Put(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress);
1577 Result Put(
const std::string &path,
const MultipartFormDataItems &items);
1578 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items);
1579 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1580 const std::string &boundary);
1581 Result Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
1582 const MultipartFormDataProviderItems &provider_items);
1584 Result Patch(
const std::string &path);
1585 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1586 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1588 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1589 const std::string &content_type);
1590 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1591 const std::string &content_type, Progress progress);
1592 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type);
1593 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1594 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
1595 const std::string &content_type);
1596 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
1597 const std::string &content_type, Progress progress);
1598 Result Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
1599 const std::string &content_type);
1600 Result Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
const std::string &content_type);
1601 Result Patch(
const std::string &path,
const Headers &headers,
size_t content_length, ContentProvider content_provider,
1602 const std::string &content_type);
1603 Result Patch(
const std::string &path,
const Headers &headers, ContentProviderWithoutLength content_provider,
1604 const std::string &content_type);
1606 Result Delete(
const std::string &path);
1607 Result Delete(
const std::string &path,
const Headers &headers);
1608 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type);
1609 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1611 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1612 const std::string &content_type);
1613 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1614 const std::string &content_type, Progress progress);
1615 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type);
1616 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type, Progress progress);
1617 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
1618 const std::string &content_type);
1619 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
1620 const std::string &content_type, Progress progress);
1622 Result Options(
const std::string &path);
1623 Result Options(
const std::string &path,
const Headers &headers);
1625 bool send(Request &req, Response &res, Error &error);
1626 Result send(
const Request &req);
1630 std::string host()
const;
1633 size_t is_socket_open()
const;
1636 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1638 void set_default_headers(Headers headers);
1640 void set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer);
1642 void set_address_family(
int family);
1643 void set_tcp_nodelay(
bool on);
1644 void set_socket_options(SocketOptions socket_options);
1646 void set_connection_timeout(time_t sec, time_t usec = 0);
1647 template<
class Rep,
class Period>
void set_connection_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1649 void set_read_timeout(time_t sec, time_t usec = 0);
1650 template<
class Rep,
class Period>
void set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1652 void set_write_timeout(time_t sec, time_t usec = 0);
1653 template<
class Rep,
class Period>
void set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration);
1655 void set_basic_auth(
const std::string &username,
const std::string &password);
1656 void set_bearer_token_auth(
const std::string &token);
1657#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1658 void set_digest_auth(
const std::string &username,
const std::string &password);
1661 void set_keep_alive(
bool on);
1662 void set_follow_location(
bool on);
1664 void set_url_encode(
bool on);
1666 void set_compress(
bool on);
1668 void set_decompress(
bool on);
1670 void set_interface(
const std::string &intf);
1672 void set_proxy(
const std::string &host,
int port);
1673 void set_proxy_basic_auth(
const std::string &username,
const std::string &password);
1674 void set_proxy_bearer_token_auth(
const std::string &token);
1675#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1676 void set_proxy_digest_auth(
const std::string &username,
const std::string &password);
1679#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1680 void enable_server_certificate_verification(
bool enabled);
1681 void enable_server_hostname_verification(
bool enabled);
1682 void set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier);
1685 void set_logger(Logger logger);
1688#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1689 void set_ca_cert_path(
const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path = std::string());
1691 void set_ca_cert_store(X509_STORE *ca_cert_store);
1692 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
1694 long get_openssl_verify_result()
const;
1696 SSL_CTX *ssl_context()
const;
1700 std::unique_ptr<ClientImpl> cli_;
1702#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1703 bool is_ssl_ =
false;
1707#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1708class SSLServer :
public Server {
1710 SSLServer(
const char *cert_path,
const char *private_key_path,
const char *client_ca_cert_file_path =
nullptr,
1711 const char *client_ca_cert_dir_path =
nullptr,
const char *private_key_password =
nullptr);
1713 SSLServer(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store =
nullptr);
1715 SSLServer(
const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
1717 ~SSLServer()
override;
1719 bool is_valid()
const override;
1721 SSL_CTX *ssl_context()
const;
1723 void update_certs(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store =
nullptr);
1726 bool process_and_close_socket(
socket_t sock)
override;
1729 std::mutex ctx_mutex_;
1732class SSLClient final :
public ClientImpl {
1734 explicit SSLClient(
const std::string &host);
1736 explicit SSLClient(
const std::string &host,
int port);
1738 explicit SSLClient(
const std::string &host,
int port,
const std::string &client_cert_path,
1739 const std::string &client_key_path,
const std::string &private_key_password = std::string());
1741 explicit SSLClient(
const std::string &host,
int port, X509 *client_cert, EVP_PKEY *client_key,
1742 const std::string &private_key_password = std::string());
1744 ~SSLClient()
override;
1746 bool is_valid()
const override;
1748 void set_ca_cert_store(X509_STORE *ca_cert_store);
1749 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
1751 long get_openssl_verify_result()
const;
1753 SSL_CTX *ssl_context()
const;
1756 bool create_and_connect_socket(Socket &socket, Error &error)
override;
1757 void shutdown_ssl(Socket &socket,
bool shutdown_gracefully)
override;
1758 void shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully);
1760 bool process_socket(
const Socket &socket, std::function<
bool(Stream &strm)> callback)
override;
1761 bool is_ssl()
const override;
1763 bool connect_with_proxy(Socket &sock, Response &res,
bool &success, Error &error);
1764 bool initialize_ssl(Socket &socket, Error &error);
1768 bool verify_host(X509 *server_cert)
const;
1769 bool verify_host_with_subject_alt_name(X509 *server_cert)
const;
1770 bool verify_host_with_common_name(X509 *server_cert)
const;
1771 bool check_host_name(
const char *pattern,
size_t pattern_len)
const;
1774 std::mutex ctx_mutex_;
1775 std::once_flag initialize_cert_;
1777 std::vector<std::string> host_components_;
1779 long verify_result_ = 0;
1781 friend class ClientImpl;
1791template<
typename T,
typename U>
inline void duration_to_sec_and_usec(
const T &
duration, U callback) {
1792 auto sec = std::chrono::duration_cast<std::chrono::seconds>(
duration).count();
1793 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
duration - std::chrono::seconds(sec)).count();
1794 callback(
static_cast<time_t
>(sec),
static_cast<time_t
>(usec));
1797inline uint64_t get_header_value_u64(
const Headers &headers,
const std::string &key, uint64_t def,
size_t id) {
1798 auto rng = headers.equal_range(key);
1799 auto it = rng.first;
1800 std::advance(it,
static_cast<ssize_t>(
id));
1801 if (it != rng.second) {
1802 return std::strtoull(it->second.data(),
nullptr, 10);
1809inline uint64_t Request::get_header_value_u64(
const std::string &key, uint64_t def,
size_t id)
const {
1810 return detail::get_header_value_u64(headers, key, def,
id);
1813inline uint64_t Response::get_header_value_u64(
const std::string &key, uint64_t def,
size_t id)
const {
1814 return detail::get_header_value_u64(headers, key, def,
id);
1817inline void default_socket_options(
socket_t sock) {
1820 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char *
>(&opt),
sizeof(opt));
1821 setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<const char *
>(&opt),
sizeof(opt));
1824 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
reinterpret_cast<const void *
>(&opt),
sizeof(opt));
1826 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const void *
>(&opt),
sizeof(opt));
1831inline const char *status_message(
int status) {
1833 case StatusCode::Continue_100:
1835 case StatusCode::SwitchingProtocol_101:
1836 return "Switching Protocol";
1837 case StatusCode::Processing_102:
1838 return "Processing";
1839 case StatusCode::EarlyHints_103:
1840 return "Early Hints";
1841 case StatusCode::OK_200:
1843 case StatusCode::Created_201:
1845 case StatusCode::Accepted_202:
1847 case StatusCode::NonAuthoritativeInformation_203:
1848 return "Non-Authoritative Information";
1849 case StatusCode::NoContent_204:
1850 return "No Content";
1851 case StatusCode::ResetContent_205:
1852 return "Reset Content";
1853 case StatusCode::PartialContent_206:
1854 return "Partial Content";
1855 case StatusCode::MultiStatus_207:
1856 return "Multi-Status";
1857 case StatusCode::AlreadyReported_208:
1858 return "Already Reported";
1859 case StatusCode::IMUsed_226:
1861 case StatusCode::MultipleChoices_300:
1862 return "Multiple Choices";
1863 case StatusCode::MovedPermanently_301:
1864 return "Moved Permanently";
1865 case StatusCode::Found_302:
1867 case StatusCode::SeeOther_303:
1869 case StatusCode::NotModified_304:
1870 return "Not Modified";
1871 case StatusCode::UseProxy_305:
1873 case StatusCode::unused_306:
1875 case StatusCode::TemporaryRedirect_307:
1876 return "Temporary Redirect";
1877 case StatusCode::PermanentRedirect_308:
1878 return "Permanent Redirect";
1879 case StatusCode::BadRequest_400:
1880 return "Bad Request";
1881 case StatusCode::Unauthorized_401:
1882 return "Unauthorized";
1883 case StatusCode::PaymentRequired_402:
1884 return "Payment Required";
1885 case StatusCode::Forbidden_403:
1887 case StatusCode::NotFound_404:
1889 case StatusCode::MethodNotAllowed_405:
1890 return "Method Not Allowed";
1891 case StatusCode::NotAcceptable_406:
1892 return "Not Acceptable";
1893 case StatusCode::ProxyAuthenticationRequired_407:
1894 return "Proxy Authentication Required";
1895 case StatusCode::RequestTimeout_408:
1896 return "Request Timeout";
1897 case StatusCode::Conflict_409:
1899 case StatusCode::Gone_410:
1901 case StatusCode::LengthRequired_411:
1902 return "Length Required";
1903 case StatusCode::PreconditionFailed_412:
1904 return "Precondition Failed";
1905 case StatusCode::PayloadTooLarge_413:
1906 return "Payload Too Large";
1907 case StatusCode::UriTooLong_414:
1908 return "URI Too Long";
1909 case StatusCode::UnsupportedMediaType_415:
1910 return "Unsupported Media Type";
1911 case StatusCode::RangeNotSatisfiable_416:
1912 return "Range Not Satisfiable";
1913 case StatusCode::ExpectationFailed_417:
1914 return "Expectation Failed";
1915 case StatusCode::ImATeapot_418:
1916 return "I'm a teapot";
1917 case StatusCode::MisdirectedRequest_421:
1918 return "Misdirected Request";
1919 case StatusCode::UnprocessableContent_422:
1920 return "Unprocessable Content";
1921 case StatusCode::Locked_423:
1923 case StatusCode::FailedDependency_424:
1924 return "Failed Dependency";
1925 case StatusCode::TooEarly_425:
1927 case StatusCode::UpgradeRequired_426:
1928 return "Upgrade Required";
1929 case StatusCode::PreconditionRequired_428:
1930 return "Precondition Required";
1931 case StatusCode::TooManyRequests_429:
1932 return "Too Many Requests";
1933 case StatusCode::RequestHeaderFieldsTooLarge_431:
1934 return "Request Header Fields Too Large";
1935 case StatusCode::UnavailableForLegalReasons_451:
1936 return "Unavailable For Legal Reasons";
1937 case StatusCode::NotImplemented_501:
1938 return "Not Implemented";
1939 case StatusCode::BadGateway_502:
1940 return "Bad Gateway";
1941 case StatusCode::ServiceUnavailable_503:
1942 return "Service Unavailable";
1943 case StatusCode::GatewayTimeout_504:
1944 return "Gateway Timeout";
1945 case StatusCode::HttpVersionNotSupported_505:
1946 return "HTTP Version Not Supported";
1947 case StatusCode::VariantAlsoNegotiates_506:
1948 return "Variant Also Negotiates";
1949 case StatusCode::InsufficientStorage_507:
1950 return "Insufficient Storage";
1951 case StatusCode::LoopDetected_508:
1952 return "Loop Detected";
1953 case StatusCode::NotExtended_510:
1954 return "Not Extended";
1955 case StatusCode::NetworkAuthenticationRequired_511:
1956 return "Network Authentication Required";
1959 case StatusCode::InternalServerError_500:
1960 return "Internal Server Error";
1964inline std::string get_bearer_token_auth(
const Request &req) {
1965 if (req.has_header(
"Authorization")) {
1966 static std::string BearerHeaderPrefix =
"Bearer ";
1967 return req.get_header_value(
"Authorization").substr(BearerHeaderPrefix.length());
1972template<
class Rep,
class Period>
1973inline Server &Server::set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
1974 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
1978template<
class Rep,
class Period>
1979inline Server &Server::set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
1980 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
1984template<
class Rep,
class Period>
1985inline Server &Server::set_idle_interval(
const std::chrono::duration<Rep, Period> &
duration) {
1986 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
1990inline std::string to_string(
const Error error) {
1992 case Error::Success:
1993 return "Success (no error)";
1994 case Error::Connection:
1995 return "Could not establish connection";
1996 case Error::BindIPAddress:
1997 return "Failed to bind IP address";
1999 return "Failed to read connection";
2001 return "Failed to write connection";
2002 case Error::ExceedRedirectCount:
2003 return "Maximum redirect count exceeded";
2004 case Error::Canceled:
2005 return "Connection handling canceled";
2006 case Error::SSLConnection:
2007 return "SSL connection failed";
2008 case Error::SSLLoadingCerts:
2009 return "SSL certificate loading failed";
2010 case Error::SSLServerVerification:
2011 return "SSL server verification failed";
2012 case Error::SSLServerHostnameVerification:
2013 return "SSL server hostname verification failed";
2014 case Error::UnsupportedMultipartBoundaryChars:
2015 return "Unsupported HTTP multipart boundary characters";
2016 case Error::Compression:
2017 return "Compression failed";
2018 case Error::ConnectionTimeout:
2019 return "Connection timed out";
2020 case Error::ProxyConnection:
2021 return "Proxy connection failed";
2022 case Error::Unknown:
2031inline std::ostream &operator<<(std::ostream &os,
const Error &obj) {
2032 os << to_string(obj);
2033 os <<
" (" <<
static_cast<std::underlying_type<Error>::type
>(obj) <<
')';
2037inline uint64_t Result::get_request_header_value_u64(
const std::string &key, uint64_t def,
size_t id)
const {
2038 return detail::get_header_value_u64(request_headers_, key, def,
id);
2041template<
class Rep,
class Period>
2042inline void ClientImpl::set_connection_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2043 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_connection_timeout(sec, usec); });
2046template<
class Rep,
class Period>
2047inline void ClientImpl::set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2048 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2051template<
class Rep,
class Period>
2052inline void ClientImpl::set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2053 detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2056template<
class Rep,
class Period>
2057inline void Client::set_connection_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2058 cli_->set_connection_timeout(
duration);
2061template<
class Rep,
class Period>
2062inline void Client::set_read_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2066template<
class Rep,
class Period>
2067inline void Client::set_write_timeout(
const std::chrono::duration<Rep, Period> &
duration) {
2076std::string hosted_at(
const std::string &hostname);
2078void hosted_at(
const std::string &hostname, std::vector<std::string> &addrs);
2080std::string append_query_params(
const std::string &path,
const Params ¶ms);
2082std::pair<std::string, std::string> make_range_header(
const Ranges &ranges);
2084std::pair<std::string, std::string> make_basic_authentication_header(
const std::string &username,
2085 const std::string &password,
2086 bool is_proxy =
false);
2091inline std::wstring u8string_to_wstring(
const char *s) {
2093 auto len =
static_cast<int>(strlen(s));
2094 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s,
len,
nullptr, 0);
2097 wlen = ::MultiByteToWideChar(CP_UTF8, 0, s,
len,
const_cast<LPWSTR
>(
reinterpret_cast<LPCWSTR
>(ws.data())), wlen);
2098 if (wlen !=
static_cast<int>(ws.size())) {
2107 FileStat(
const std::string &path);
2108 bool is_file()
const;
2109 bool is_dir()
const;
2120std::string encode_query_param(
const std::string &value);
2122std::string decode_url(
const std::string &s,
bool convert_plus_to_space);
2124void read_file(
const std::string &path, std::string &out);
2126std::string trim_copy(
const std::string &s);
2128void divide(
const char *data, std::size_t size,
char d,
2129 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)> fn);
2131void divide(
const std::string &str,
char d,
2132 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)> fn);
2134void split(
const char *b,
const char *e,
char d, std::function<
void(
const char *,
const char *)> fn);
2136void split(
const char *b,
const char *e,
char d,
size_t m, std::function<
void(
const char *,
const char *)> fn);
2138bool process_client_socket(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
2139 time_t write_timeout_usec, std::function<
bool(Stream &)> callback);
2141socket_t create_client_socket(
const std::string &host,
const std::string &ip,
int port,
int address_family,
2142 bool tcp_nodelay,
bool ipv6_v6only, SocketOptions socket_options,
2143 time_t connection_timeout_sec, time_t connection_timeout_usec, time_t read_timeout_sec,
2144 time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec,
2145 const std::string &intf, Error &error);
2147const char *get_header_value(
const Headers &headers,
const std::string &key,
const char *def,
size_t id);
2149std::string params_to_query_str(
const Params ¶ms);
2151void parse_query_text(
const char *data, std::size_t size, Params ¶ms);
2153void parse_query_text(
const std::string &s, Params ¶ms);
2155bool parse_multipart_boundary(
const std::string &content_type, std::string &boundary);
2157bool parse_range_header(
const std::string &s, Ranges &ranges);
2161ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
int flags);
2165enum class EncodingType { None = 0, Gzip, Brotli };
2167EncodingType encoding_type(
const Request &req,
const Response &res);
2169class BufferStream final :
public Stream {
2171 BufferStream() =
default;
2172 ~BufferStream()
override =
default;
2174 bool is_readable()
const override;
2175 bool is_writable()
const override;
2176 ssize_t read(
char *ptr,
size_t size)
override;
2177 ssize_t write(
const char *ptr,
size_t size)
override;
2178 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
2179 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
2182 const std::string &get_buffer()
const;
2191 virtual ~compressor() =
default;
2193 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
2194 virtual bool compress(
const char *data,
size_t data_length,
bool last, Callback callback) = 0;
2199 virtual ~decompressor() =
default;
2201 virtual bool is_valid()
const = 0;
2203 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
2204 virtual bool decompress(
const char *data,
size_t data_length, Callback callback) = 0;
2207class nocompressor final :
public compressor {
2209 ~nocompressor()
override =
default;
2211 bool compress(
const char *data,
size_t data_length,
bool , Callback callback)
override;
2214#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2215class gzip_compressor final :
public compressor {
2218 ~gzip_compressor()
override;
2220 bool compress(
const char *data,
size_t data_length,
bool last, Callback callback)
override;
2223 bool is_valid_ =
false;
2227class gzip_decompressor final :
public decompressor {
2229 gzip_decompressor();
2230 ~gzip_decompressor()
override;
2232 bool is_valid()
const override;
2234 bool decompress(
const char *data,
size_t data_length, Callback callback)
override;
2237 bool is_valid_ =
false;
2242#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2243class brotli_compressor final :
public compressor {
2245 brotli_compressor();
2246 ~brotli_compressor();
2248 bool compress(
const char *data,
size_t data_length,
bool last, Callback callback)
override;
2251 BrotliEncoderState *state_ =
nullptr;
2254class brotli_decompressor final :
public decompressor {
2256 brotli_decompressor();
2257 ~brotli_decompressor();
2259 bool is_valid()
const override;
2261 bool decompress(
const char *data,
size_t data_length, Callback callback)
override;
2264 BrotliDecoderResult decoder_r;
2265 BrotliDecoderState *decoder_s =
nullptr;
2271class stream_line_reader {
2273 stream_line_reader(Stream &strm,
char *fixed_buffer,
size_t fixed_buffer_size);
2274 const char *ptr()
const;
2275 size_t size()
const;
2276 bool end_with_crlf()
const;
2280 void append(
char c);
2283 char *fixed_buffer_;
2284 const size_t fixed_buffer_size_;
2285 size_t fixed_buffer_used_size_ = 0;
2286 std::string glowable_buffer_;
2291 mmap(
const char *path);
2294 bool open(
const char *path);
2297 bool is_open()
const;
2298 size_t size()
const;
2299 const char *data()
const;
2303 HANDLE hFile_ = NULL;
2304 HANDLE hMapping_ = NULL;
2309 void *addr_ =
nullptr;
2310 bool is_open_empty_file =
false;
2323inline bool is_hex(
char c,
int &v) {
2324 if (0x20 <= c && isdigit(c)) {
2327 }
else if (
'A' <= c && c <=
'F') {
2330 }
else if (
'a' <= c && c <=
'f') {
2337inline bool from_hex_to_i(
const std::string &s,
size_t i,
size_t cnt,
int &
val) {
2338 if (i >= s.size()) {
2343 for (; cnt; i++, cnt--) {
2348 if (is_hex(s[i], v)) {
2357inline std::string from_i_to_hex(
size_t n) {
2358 static const auto charset =
"0123456789abcdef";
2361 ret = charset[n & 15] + ret;
2367inline size_t to_utf8(
int code,
char *buff) {
2368 if (code < 0x0080) {
2369 buff[0] =
static_cast<char>(code & 0x7F);
2371 }
else if (code < 0x0800) {
2372 buff[0] =
static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
2373 buff[1] =
static_cast<char>(0x80 | (code & 0x3F));
2375 }
else if (code < 0xD800) {
2376 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2377 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2378 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
2380 }
else if (code < 0xE000) {
2382 }
else if (code < 0x10000) {
2383 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2384 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2385 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
2387 }
else if (code < 0x110000) {
2388 buff[0] =
static_cast<char>(0xF0 | ((code >> 18) & 0x7));
2389 buff[1] =
static_cast<char>(0x80 | ((code >> 12) & 0x3F));
2390 buff[2] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2391 buff[3] =
static_cast<char>(0x80 | (code & 0x3F));
2401inline std::string base64_encode(
const std::string &in) {
2402 static const auto lookup =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2405 out.reserve(in.size());
2414 out.push_back(lookup[(
val >> valb) & 0x3F]);
2420 out.push_back(lookup[((
val << 8) >> (valb + 8)) & 0x3F]);
2423 while (out.size() % 4) {
2430inline bool is_valid_path(
const std::string &path) {
2435 while (i < path.size() && path[i] ==
'/') {
2439 while (i < path.size()) {
2442 while (i < path.size() && path[i] !=
'/') {
2443 if (path[i] ==
'\0') {
2445 }
else if (path[i] ==
'\\') {
2454 if (!path.compare(beg,
len,
".")) {
2456 }
else if (!path.compare(beg,
len,
"..")) {
2466 while (i < path.size() && path[i] ==
'/') {
2474inline FileStat::FileStat(
const std::string &path) {
2476 auto wpath = u8string_to_wstring(path.c_str());
2477 ret_ = _wstat(wpath.c_str(), &st_);
2479 ret_ = stat(path.c_str(), &st_);
2482inline bool FileStat::is_file()
const {
return ret_ >= 0 && S_ISREG(st_.st_mode); }
2483inline bool FileStat::is_dir()
const {
return ret_ >= 0 && S_ISDIR(st_.st_mode); }
2485inline std::string encode_query_param(
const std::string &value) {
2486 std::ostringstream escaped;
2488 escaped << std::hex;
2490 for (
auto c : value) {
2491 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' || c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' ||
2492 c ==
'\'' || c ==
'(' || c ==
')') {
2495 escaped << std::uppercase;
2496 escaped << '%' << std::setw(2) << static_cast<int>(
static_cast<unsigned char>(c));
2497 escaped << std::nouppercase;
2501 return escaped.str();
2504inline std::string encode_url(
const std::string &s) {
2506 result.reserve(s.size());
2508 for (
size_t i = 0; s[i]; i++) {
2533 auto c =
static_cast<uint8_t
>(s[i]);
2537 auto len = snprintf(hex,
sizeof(hex) - 1,
"%02X", c);
2539 result.append(hex,
static_cast<size_t>(
len));
2550inline std::string decode_url(
const std::string &s,
bool convert_plus_to_space) {
2553 for (
size_t i = 0; i < s.size(); i++) {
2554 if (s[i] ==
'%' && i + 1 < s.size()) {
2555 if (s[i + 1] ==
'u') {
2557 if (from_hex_to_i(s, i + 2, 4,
val)) {
2560 size_t len = to_utf8(
val, buff);
2562 result.append(buff,
len);
2570 if (from_hex_to_i(s, i + 1, 2,
val)) {
2572 result +=
static_cast<char>(
val);
2578 }
else if (convert_plus_to_space && s[i] ==
'+') {
2588inline void read_file(
const std::string &path, std::string &out) {
2589 std::ifstream fs(path, std::ios_base::binary);
2590 fs.seekg(0, std::ios_base::end);
2591 auto size = fs.tellg();
2593 out.resize(
static_cast<size_t>(size));
2594 fs.read(&out[0],
static_cast<std::streamsize
>(size));
2597inline std::string file_extension(
const std::string &path) {
2599 static auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
2600 if (std::regex_search(path,
m, re)) {
2603 return std::string();
2606inline bool is_space_or_tab(
char c) {
return c ==
' ' || c ==
'\t'; }
2608inline std::pair<size_t, size_t> trim(
const char *b,
const char *e,
size_t left,
size_t right) {
2609 while (b + left < e && is_space_or_tab(b[left])) {
2612 while (right > 0 && is_space_or_tab(b[right - 1])) {
2615 return std::make_pair(left, right);
2618inline std::string trim_copy(
const std::string &s) {
2619 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
2620 return s.substr(r.first, r.second - r.first);
2623inline std::string trim_double_quotes_copy(
const std::string &s) {
2624 if (s.length() >= 2 && s.front() ==
'"' && s.back() ==
'"') {
2625 return s.substr(1, s.size() - 2);
2630inline void divide(
const char *data, std::size_t size,
char d,
2631 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)> fn) {
2632 const auto it = std::find(data, data + size, d);
2633 const auto found =
static_cast<std::size_t
>(it != data + size);
2634 const auto lhs_data = data;
2635 const auto lhs_size =
static_cast<std::size_t
>(it - data);
2636 const auto rhs_data = it + found;
2637 const auto rhs_size = size - lhs_size - found;
2639 fn(lhs_data, lhs_size, rhs_data, rhs_size);
2642inline void divide(
const std::string &str,
char d,
2643 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)> fn) {
2644 divide(str.data(), str.size(), d, std::move(fn));
2647inline void split(
const char *b,
const char *e,
char d, std::function<
void(
const char *,
const char *)> fn) {
2648 return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
2651inline void split(
const char *b,
const char *e,
char d,
size_t m, std::function<
void(
const char *,
const char *)> fn) {
2656 while (e ? (b + i < e) : (b[i] !=
'\0')) {
2657 if (b[i] == d && count <
m) {
2658 auto r = trim(b, e, beg, i);
2659 if (r.first < r.second) {
2660 fn(&b[r.first], &b[r.second]);
2669 auto r = trim(b, e, beg, i);
2670 if (r.first < r.second) {
2671 fn(&b[r.first], &b[r.second]);
2676inline stream_line_reader::stream_line_reader(Stream &strm,
char *fixed_buffer,
size_t fixed_buffer_size)
2677 : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size) {}
2679inline const char *stream_line_reader::ptr()
const {
2680 if (glowable_buffer_.empty()) {
2681 return fixed_buffer_;
2683 return glowable_buffer_.data();
2687inline size_t stream_line_reader::size()
const {
2688 if (glowable_buffer_.empty()) {
2689 return fixed_buffer_used_size_;
2691 return glowable_buffer_.size();
2695inline bool stream_line_reader::end_with_crlf()
const {
2696 auto end = ptr() + size();
2697 return size() >= 2 &&
end[-2] ==
'\r' &&
end[-1] ==
'\n';
2700inline bool stream_line_reader::getline() {
2701 fixed_buffer_used_size_ = 0;
2702 glowable_buffer_.clear();
2704#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2708 for (
size_t i = 0;; i++) {
2710 auto n = strm_.read(&
byte, 1);
2714 }
else if (n == 0) {
2724#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2729 if (prev_byte ==
'\r' &&
byte ==
'\n') {
2739inline void stream_line_reader::append(
char c) {
2740 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
2741 fixed_buffer_[fixed_buffer_used_size_++] = c;
2742 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
2744 if (glowable_buffer_.empty()) {
2745 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
2746 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
2748 glowable_buffer_ += c;
2752inline mmap::mmap(
const char *path) { open(path); }
2754inline mmap::~mmap() { close(); }
2756inline bool mmap::open(
const char *path) {
2760 auto wpath = u8string_to_wstring(path);
2761 if (wpath.empty()) {
2765#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2766 hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, NULL);
2769 ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2772 if (hFile_ == INVALID_HANDLE_VALUE) {
2776 LARGE_INTEGER size{};
2777 if (!::GetFileSizeEx(hFile_, &size)) {
2783 if (
static_cast<ULONGLONG
>(size.QuadPart) > (std::numeric_limits<
decltype(size_)>::max)()) {
2787 size_ =
static_cast<size_t>(size.QuadPart);
2789#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2790 hMapping_ = ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
2792 hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);
2796 if (hMapping_ == NULL && size_ == 0) {
2798 is_open_empty_file =
true;
2802 if (hMapping_ == NULL) {
2807#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2808 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
2810 addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);
2813 if (addr_ ==
nullptr) {
2818 fd_ = ::open(path, O_RDONLY);
2824 if (fstat(fd_, &sb) == -1) {
2828 size_ =
static_cast<size_t>(sb.st_size);
2830 addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
2833 if (addr_ == MAP_FAILED && size_ == 0) {
2835 is_open_empty_file =
true;
2843inline bool mmap::is_open()
const {
return is_open_empty_file ? true : addr_ !=
nullptr; }
2845inline size_t mmap::size()
const {
return size_; }
2847inline const char *mmap::data()
const {
return is_open_empty_file ?
"" :
static_cast<const char *
>(addr_); }
2849inline void mmap::close() {
2852 ::UnmapViewOfFile(addr_);
2857 ::CloseHandle(hMapping_);
2861 if (hFile_ != INVALID_HANDLE_VALUE) {
2862 ::CloseHandle(hFile_);
2863 hFile_ = INVALID_HANDLE_VALUE;
2866 is_open_empty_file =
false;
2868 if (addr_ !=
nullptr) {
2869 munmap(addr_, size_);
2880inline int close_socket(
socket_t sock) {
2882 return closesocket(sock);
2888template<
typename T>
inline ssize_t handle_EINTR(T fn) {
2892 if (res < 0 && errno == EINTR) {
2893 std::this_thread::sleep_for(std::chrono::microseconds{1});
2901inline ssize_t read_socket(
socket_t sock,
void *ptr,
size_t size,
int flags) {
2902 return handle_EINTR([&]() {
2905 static_cast<char *
>(ptr),
static_cast<int>(size),
2913inline ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
int flags) {
2914 return handle_EINTR([&]() {
2917 static_cast<const char *
>(ptr),
static_cast<int>(size),
2926#ifdef CPPHTTPLIB_USE_POLL
2927 struct pollfd pfd_read;
2929 pfd_read.events = POLLIN;
2931 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2933 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2936 if (sock >= FD_SETSIZE) {
2946 tv.tv_sec =
static_cast<long>(sec);
2947 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2949 return handle_EINTR([&]() {
return select(
static_cast<int>(sock + 1), &fds,
nullptr,
nullptr, &tv); });
2954#ifdef CPPHTTPLIB_USE_POLL
2955 struct pollfd pfd_read;
2957 pfd_read.events = POLLOUT;
2959 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2961 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2964 if (sock >= FD_SETSIZE) {
2974 tv.tv_sec =
static_cast<long>(sec);
2975 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2977 return handle_EINTR([&]() {
return select(
static_cast<int>(sock + 1),
nullptr, &fds,
nullptr, &tv); });
2981inline Error wait_until_socket_is_ready(
socket_t sock, time_t sec, time_t usec) {
2982#ifdef CPPHTTPLIB_USE_POLL
2983 struct pollfd pfd_read;
2985 pfd_read.events = POLLIN | POLLOUT;
2987 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2989 auto poll_res = handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2991 if (poll_res == 0) {
2992 return Error::ConnectionTimeout;
2995 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
2998 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *
>(&error), &
len);
2999 auto successful = res >= 0 && !error;
3000 return successful ? Error::Success : Error::Connection;
3003 return Error::Connection;
3006 if (sock >= FD_SETSIZE) {
3007 return Error::Connection;
3013 FD_SET(sock, &fdsr);
3019 tv.tv_sec =
static_cast<long>(sec);
3020 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
3022 auto ret = handle_EINTR([&]() {
return select(
static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv); });
3025 return Error::ConnectionTimeout;
3028 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
3031 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *
>(&error), &
len);
3032 auto successful = res >= 0 && !error;
3033 return successful ? Error::Success : Error::Connection;
3035 return Error::Connection;
3039inline bool is_socket_alive(
socket_t sock) {
3040 const auto val = detail::select_read(sock, 0, 0);
3043 }
else if (
val < 0 && errno == EBADF) {
3047 return detail::read_socket(sock, &buf[0],
sizeof(buf), MSG_PEEK) > 0;
3050class SocketStream final :
public Stream {
3052 SocketStream(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3053 time_t write_timeout_usec);
3054 ~SocketStream()
override;
3056 bool is_readable()
const override;
3057 bool is_writable()
const override;
3058 ssize_t read(
char *ptr,
size_t size)
override;
3059 ssize_t write(
const char *ptr,
size_t size)
override;
3060 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
3061 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
3066 time_t read_timeout_sec_;
3067 time_t read_timeout_usec_;
3068 time_t write_timeout_sec_;
3069 time_t write_timeout_usec_;
3071 std::vector<char> read_buff_;
3072 size_t read_buff_off_ = 0;
3073 size_t read_buff_content_size_ = 0;
3075 static const size_t read_buff_size_ = 1024l * 4;
3078#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3079class SSLSocketStream final :
public Stream {
3081 SSLSocketStream(
socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3082 time_t write_timeout_usec);
3083 ~SSLSocketStream()
override;
3085 bool is_readable()
const override;
3086 bool is_writable()
const override;
3087 ssize_t read(
char *ptr,
size_t size)
override;
3088 ssize_t write(
const char *ptr,
size_t size)
override;
3089 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
3090 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
3096 time_t read_timeout_sec_;
3097 time_t read_timeout_usec_;
3098 time_t write_timeout_sec_;
3099 time_t write_timeout_usec_;
3103inline bool keep_alive(
const std::atomic<socket_t> &svr_sock,
socket_t sock, time_t keep_alive_timeout_sec) {
3104 using namespace std::chrono;
3106 const auto interval_usec = CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
3109 if (select_read(sock, 0, interval_usec) > 0) {
3113 const auto start = steady_clock::now() - microseconds{interval_usec};
3114 const auto timeout = seconds{keep_alive_timeout_sec};
3117 if (svr_sock == INVALID_SOCKET) {
3121 auto val = select_read(sock, 0, interval_usec);
3124 }
else if (
val == 0) {
3125 if (steady_clock::now() - start > timeout) {
3137inline bool process_server_socket_core(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
3138 size_t keep_alive_max_count, time_t keep_alive_timeout_sec, T callback) {
3139 assert(keep_alive_max_count > 0);
3141 auto count = keep_alive_max_count;
3142 while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
3143 auto close_connection = count == 1;
3144 auto connection_closed =
false;
3145 ret = callback(close_connection, connection_closed);
3146 if (!ret || connection_closed) {
3155inline bool process_server_socket(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
size_t keep_alive_max_count,
3156 time_t keep_alive_timeout_sec, time_t read_timeout_sec, time_t read_timeout_usec,
3157 time_t write_timeout_sec, time_t write_timeout_usec, T callback) {
3158 return process_server_socket_core(svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
3159 [&](
bool close_connection,
bool &connection_closed) {
3160 SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec,
3161 write_timeout_usec);
3162 return callback(strm, close_connection, connection_closed);
3166inline bool process_client_socket(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3167 time_t write_timeout_sec, time_t write_timeout_usec,
3168 std::function<
bool(Stream &)> callback) {
3169 SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);
3170 return callback(strm);
3173inline int shutdown_socket(
socket_t sock) {
3175 return shutdown(sock, SD_BOTH);
3177 return shutdown(sock, SHUT_RDWR);
3181inline std::string escape_abstract_namespace_unix_domain(
const std::string &s) {
3182 if (s.size() > 1 && s[0] ==
'\0') {
3190inline std::string unescape_abstract_namespace_unix_domain(
const std::string &s) {
3191 if (s.size() > 1 && s[0] ==
'@') {
3199template<
typename BindOrConnect>
3200socket_t create_socket(
const std::string &host,
const std::string &ip,
int port,
int address_family,
int socket_flags,
3201 bool tcp_nodelay,
bool ipv6_v6only, SocketOptions socket_options,
3202 BindOrConnect bind_or_connect) {
3204 const char *node =
nullptr;
3205 struct addrinfo hints;
3206 struct addrinfo *result;
3208 memset(&hints, 0,
sizeof(
struct addrinfo));
3209 hints.ai_socktype = SOCK_STREAM;
3210 hints.ai_protocol = IPPROTO_IP;
3215 hints.ai_family = AF_UNSPEC;
3216 hints.ai_flags = AI_NUMERICHOST;
3218 if (!host.empty()) {
3219 node = host.c_str();
3221 hints.ai_family = address_family;
3222 hints.ai_flags = socket_flags;
3226 if (hints.ai_family == AF_UNIX) {
3227 const auto addrlen = host.length();
3228 if (addrlen >
sizeof(sockaddr_un::sun_path)) {
3229 return INVALID_SOCKET;
3233 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC, hints.ai_protocol);
3235 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
3238 if (sock != INVALID_SOCKET) {
3240 addr.sun_family = AF_UNIX;
3242 auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
3243 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
3245 hints.ai_addr =
reinterpret_cast<sockaddr *
>(&addr);
3246 hints.ai_addrlen =
static_cast<socklen_t>(
sizeof(addr) -
sizeof(addr.sun_path) + addrlen);
3249 fcntl(sock, F_SETFD, FD_CLOEXEC);
3252 if (socket_options) {
3253 socket_options(sock);
3257 if (!bind_or_connect(sock, hints, dummy)) {
3259 sock = INVALID_SOCKET;
3266 auto service = std::to_string(port);
3268 if (getaddrinfo(node, service.c_str(), &hints, &result)) {
3269#if defined __linux__ && !defined __ANDROID__
3272 return INVALID_SOCKET;
3274 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3276 for (
auto rp = result; rp; rp = rp->ai_next) {
3279 auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0,
3280 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
3295 if (sock == INVALID_SOCKET) {
3296 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3301 auto sock = socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
3303 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3307 if (sock == INVALID_SOCKET) {
3311#if !defined _WIN32 && !defined SOCK_CLOEXEC
3312 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
3321 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<const char *
>(&opt),
sizeof(opt));
3323 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<const void *
>(&opt),
sizeof(opt));
3327 if (rp->ai_family == AF_INET6) {
3328 auto opt = ipv6_v6only ? 1 : 0;
3330 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const char *
>(&opt),
sizeof(opt));
3332 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const void *
>(&opt),
sizeof(opt));
3336 if (socket_options) {
3337 socket_options(sock);
3342 if (bind_or_connect(sock, *rp, quit)) {
3353 return INVALID_SOCKET;
3356inline void set_nonblocking(
socket_t sock,
bool nonblocking) {
3358 auto flags = nonblocking ? 1UL : 0UL;
3359 ioctlsocket(sock, FIONBIO, &flags);
3361 auto flags = fcntl(sock, F_GETFL, 0);
3362 fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
3366inline bool is_connection_error() {
3368 return WSAGetLastError() != WSAEWOULDBLOCK;
3370 return errno != EINPROGRESS;
3374inline bool bind_ip_address(
socket_t sock,
const std::string &host) {
3375 struct addrinfo hints;
3376 struct addrinfo *result;
3378 memset(&hints, 0,
sizeof(
struct addrinfo));
3379 hints.ai_family = AF_UNSPEC;
3380 hints.ai_socktype = SOCK_STREAM;
3381 hints.ai_protocol = 0;
3383 if (getaddrinfo(host.c_str(),
"0", &hints, &result)) {
3386 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3389 for (
auto rp = result; rp; rp = rp->ai_next) {
3390 const auto &ai = *rp;
3391 if (!::bind(sock, ai.ai_addr,
static_cast<socklen_t>(ai.ai_addrlen))) {
3400#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
3405inline std::string if2ip(
int address_family,
const std::string &ifn) {
3406 struct ifaddrs *ifap;
3408 auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
3410 std::string addr_candidate;
3411 for (
auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
3412 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
3413 (AF_UNSPEC == address_family || ifa->ifa_addr->sa_family == address_family)) {
3414 if (ifa->ifa_addr->sa_family == AF_INET) {
3415 auto sa =
reinterpret_cast<struct
sockaddr_in *
>(ifa->ifa_addr);
3416 char buf[INET_ADDRSTRLEN];
3417 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
3418 return std::string(buf, INET_ADDRSTRLEN);
3420 }
else if (ifa->ifa_addr->sa_family == AF_INET6) {
3421 auto sa =
reinterpret_cast<struct
sockaddr_in6 *
>(ifa->ifa_addr);
3422 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
3423 char buf[INET6_ADDRSTRLEN] = {};
3424 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
3426 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
3427 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
3428 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
3430 return std::string(buf, INET6_ADDRSTRLEN);
3437 return addr_candidate;
3441inline socket_t create_client_socket(
const std::string &host,
const std::string &ip,
int port,
int address_family,
3442 bool tcp_nodelay,
bool ipv6_v6only, SocketOptions socket_options,
3443 time_t connection_timeout_sec, time_t connection_timeout_usec,
3444 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3445 time_t write_timeout_usec,
const std::string &intf, Error &error) {
3446 auto sock = create_socket(
3447 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only, std::move(socket_options),
3448 [&](
socket_t sock2,
struct addrinfo &ai,
bool &quit) ->
bool {
3449 if (!intf.empty()) {
3451 auto ip_from_if = if2ip(address_family, intf);
3452 if (ip_from_if.empty()) {
3455 if (!bind_ip_address(sock2, ip_from_if)) {
3456 error = Error::BindIPAddress;
3462 set_nonblocking(sock2,
true);
3464 auto ret = ::connect(sock2, ai.ai_addr,
static_cast<socklen_t>(ai.ai_addrlen));
3467 if (is_connection_error()) {
3468 error = Error::Connection;
3471 error = wait_until_socket_is_ready(sock2, connection_timeout_sec, connection_timeout_usec);
3472 if (error != Error::Success) {
3473 if (error == Error::ConnectionTimeout) {
3480 set_nonblocking(sock2,
false);
3484 auto timeout =
static_cast<uint32_t
>(read_timeout_sec * 1000 + read_timeout_usec / 1000);
3485 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
3488 tv.tv_sec =
static_cast<long>(read_timeout_sec);
3489 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(read_timeout_usec);
3490 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
3496 auto timeout =
static_cast<uint32_t
>(write_timeout_sec * 1000 + write_timeout_usec / 1000);
3497 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
3500 tv.tv_sec =
static_cast<long>(write_timeout_sec);
3501 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(write_timeout_usec);
3502 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
3506 error = Error::Success;
3510 if (sock != INVALID_SOCKET) {
3511 error = Error::Success;
3513 if (error == Error::Success) {
3514 error = Error::Connection;
3523 port = ntohs(
reinterpret_cast<const struct
sockaddr_in *
>(&addr)->sin_port);
3524 }
else if (addr.
ss_family == AF_INET6) {
3525 port = ntohs(
reinterpret_cast<const struct
sockaddr_in6 *
>(&addr)->sin6_port);
3530 std::array<char, NI_MAXHOST> ipstr{};
3531 if (getnameinfo(
reinterpret_cast<const struct
sockaddr *
>(&addr), addr_len, ipstr.data(),
3532 static_cast<socklen_t>(ipstr.size()),
nullptr, 0, NI_NUMERICHOST)) {
3540inline void get_local_ip_and_port(
socket_t sock, std::string &ip,
int &port) {
3543 if (!getsockname(sock,
reinterpret_cast<struct
sockaddr *
>(&addr), &addr_len)) {
3544 get_ip_and_port(addr, addr_len, ip, port);
3548inline void get_remote_ip_and_port(
socket_t sock, std::string &ip,
int &port) {
3552 if (!getpeername(sock,
reinterpret_cast<struct
sockaddr *
>(&addr), &addr_len)) {
3555#if defined(__linux__)
3558 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &
len) == 0) {
3561#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
3564 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &
len) == 0) {
3571 get_ip_and_port(addr, addr_len, ip, port);
3575inline constexpr unsigned int str2tag_core(
const char *s,
size_t l,
unsigned int h) {
3580 (((std::numeric_limits<unsigned int>::max)() >> 6) &
h * 33) ^
static_cast<unsigned char>(*s));
3583inline unsigned int str2tag(
const std::string &s) {
return str2tag_core(s.data(), s.size(), 0); }
3587inline constexpr unsigned int operator""_t(
const char *s,
size_t l) {
return str2tag_core(s,
l, 0); }
3591inline std::string find_content_type(
const std::string &path,
const std::map<std::string, std::string> &user_data,
3592 const std::string &default_content_type) {
3593 auto ext = file_extension(path);
3595 auto it = user_data.find(ext);
3596 if (it != user_data.end()) {
3600 using udl::operator
""_t;
3602 switch (str2tag(ext)) {
3604 return default_content_type;
3615 return "text/javascript";
3617 return "text/plain";
3622 return "image/apng";
3624 return "image/avif";
3632 return "image/svg+xml";
3634 return "image/webp";
3636 return "image/x-icon";
3638 return "image/tiff";
3640 return "image/tiff";
3643 return "image/jpeg";
3648 return "video/mpeg";
3650 return "video/webm";
3655 return "audio/mpeg";
3657 return "audio/webm";
3659 return "audio/wave";
3668 return "font/woff2";
3671 return "application/x-7z-compressed";
3673 return "application/atom+xml";
3675 return "application/pdf";
3677 return "application/json";
3679 return "application/rss+xml";
3681 return "application/x-tar";
3684 return "application/xhtml+xml";
3686 return "application/xslt+xml";
3688 return "application/xml";
3690 return "application/gzip";
3692 return "application/zip";
3694 return "application/wasm";
3698inline bool can_compress_content_type(
const std::string &content_type) {
3699 using udl::operator
""_t;
3701 auto tag = str2tag(content_type);
3704 case "image/svg+xml"_t:
3705 case "application/javascript"_t:
3706 case "application/json"_t:
3707 case "application/xml"_t:
3708 case "application/protobuf"_t:
3709 case "application/xhtml+xml"_t:
3712 case "text/event-stream"_t:
3716 return !content_type.rfind(
"text/", 0);
3720inline EncodingType encoding_type(
const Request &req,
const Response &res) {
3721 auto ret = detail::can_compress_content_type(res.get_header_value(
"Content-Type"));
3723 return EncodingType::None;
3726 const auto &s = req.get_header_value(
"Accept-Encoding");
3729#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3731 ret = s.find(
"br") != std::string::npos;
3733 return EncodingType::Brotli;
3737#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3739 ret = s.find(
"gzip") != std::string::npos;
3741 return EncodingType::Gzip;
3745 return EncodingType::None;
3748inline bool nocompressor::compress(
const char *data,
size_t data_length,
bool , Callback callback) {
3752 return callback(data, data_length);
3755#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3756inline gzip_compressor::gzip_compressor() {
3757 std::memset(&strm_, 0,
sizeof(strm_));
3758 strm_.zalloc = Z_NULL;
3759 strm_.zfree = Z_NULL;
3760 strm_.opaque = Z_NULL;
3762 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK;
3765inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
3767inline bool gzip_compressor::compress(
const char *data,
size_t data_length,
bool last, Callback callback) {
3771 constexpr size_t max_avail_in = (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
3773 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>((std::min)(data_length, max_avail_in));
3774 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
3776 data_length -= strm_.avail_in;
3777 data += strm_.avail_in;
3779 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
3782 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3784 strm_.avail_out =
static_cast<uInt
>(buff.size());
3785 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
3787 ret = deflate(&strm_, flush);
3788 if (ret == Z_STREAM_ERROR) {
3792 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
3795 }
while (strm_.avail_out == 0);
3797 assert((flush == Z_FINISH && ret == Z_STREAM_END) || (flush == Z_NO_FLUSH && ret == Z_OK));
3798 assert(strm_.avail_in == 0);
3799 }
while (data_length > 0);
3804inline gzip_decompressor::gzip_decompressor() {
3805 std::memset(&strm_, 0,
sizeof(strm_));
3806 strm_.zalloc = Z_NULL;
3807 strm_.zfree = Z_NULL;
3808 strm_.opaque = Z_NULL;
3814 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
3817inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
3819inline bool gzip_decompressor::is_valid()
const {
return is_valid_; }
3821inline bool gzip_decompressor::decompress(
const char *data,
size_t data_length, Callback callback) {
3827 constexpr size_t max_avail_in = (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
3829 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>((std::min)(data_length, max_avail_in));
3830 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
3832 data_length -= strm_.avail_in;
3833 data += strm_.avail_in;
3835 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3836 while (strm_.avail_in > 0 && ret == Z_OK) {
3837 strm_.avail_out = static_cast<uInt>(buff.size());
3838 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
3840 ret = inflate(&strm_, Z_NO_FLUSH);
3842 assert(ret != Z_STREAM_ERROR);
3851 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
3856 if (ret != Z_OK && ret != Z_STREAM_END) {
3860 }
while (data_length > 0);
3866#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3867inline brotli_compressor::brotli_compressor() { state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr); }
3869inline brotli_compressor::~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
3871inline bool brotli_compressor::compress(
const char *data,
size_t data_length,
bool last, Callback callback) {
3872 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3874 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
3875 auto available_in = data_length;
3876 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
3880 if (BrotliEncoderIsFinished(state_)) {
3884 if (!available_in) {
3889 auto available_out = buff.size();
3890 auto next_out = buff.data();
3892 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in, &available_out, &next_out,
nullptr)) {
3896 auto output_bytes = buff.size() - available_out;
3898 callback(
reinterpret_cast<const char *
>(buff.data()), output_bytes);
3905inline brotli_decompressor::brotli_decompressor() {
3906 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
3907 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT : BROTLI_DECODER_RESULT_ERROR;
3910inline brotli_decompressor::~brotli_decompressor() {
3912 BrotliDecoderDestroyInstance(decoder_s);
3916inline bool brotli_decompressor::is_valid()
const {
return decoder_s; }
3918inline bool brotli_decompressor::decompress(
const char *data,
size_t data_length, Callback callback) {
3919 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || decoder_r == BROTLI_DECODER_RESULT_ERROR) {
3923 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
3924 size_t avail_in = data_length;
3927 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
3929 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3930 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
3931 char *next_out = buff.data();
3932 size_t avail_out = buff.size();
3934 decoder_r = BrotliDecoderDecompressStream(decoder_s, &avail_in, &next_in, &avail_out,
3935 reinterpret_cast<uint8_t **
>(&next_out), &total_out);
3937 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) {
3941 if (!callback(buff.data(), buff.size() - avail_out)) {
3946 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
3950inline bool has_header(
const Headers &headers,
const std::string &key) {
return headers.find(key) != headers.end(); }
3952inline const char *get_header_value(
const Headers &headers,
const std::string &key,
const char *def,
size_t id) {
3953 auto rng = headers.equal_range(key);
3954 auto it = rng.first;
3955 std::advance(it,
static_cast<ssize_t>(
id));
3956 if (it != rng.second) {
3957 return it->second.c_str();
3962template<
typename T>
inline bool parse_header(
const char *beg,
const char *
end, T fn) {
3964 while (beg <
end && is_space_or_tab(
end[-1])) {
3969 while (p <
end && *p !=
':') {
3983 while (p <
end && is_space_or_tab(*p)) {
3988 auto key_len = key_end - beg;
3993 auto key = std::string(beg, key_end);
3994 auto val = case_ignore::equal(key,
"Location") ? std::string(p,
end) : decode_url(std::string(p,
end),
false);
4004 static const std::string CR_LF_NUL(
"\r\n\0", 3);
4005 if (
val.find_first_of(CR_LF_NUL) != std::string::npos) {
4016inline bool read_headers(Stream &strm, Headers &headers) {
4017 const auto bufsiz = 2048;
4019 stream_line_reader line_reader(strm, buf, bufsiz);
4022 if (!line_reader.getline()) {
4027 auto line_terminator_len = 2;
4028 if (line_reader.end_with_crlf()) {
4030 if (line_reader.size() == 2) {
4034#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4036 if (line_reader.size() == 1) {
4039 line_terminator_len = 1;
4045 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4050 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4052 if (!parse_header(line_reader.ptr(),
end,
4053 [&](
const std::string &key, std::string &
val) { headers.emplace(key, val); })) {
4061inline bool read_content_with_length(Stream &strm, uint64_t
len, Progress progress, ContentReceiverWithProgress out) {
4062 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4066 auto read_len =
static_cast<size_t>(
len - r);
4067 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4072 if (!out(buf,
static_cast<size_t>(n), r,
len)) {
4075 r +=
static_cast<uint64_t
>(n);
4078 if (!progress(r,
len)) {
4087inline void skip_content_with_length(Stream &strm, uint64_t
len) {
4088 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4091 auto read_len =
static_cast<size_t>(
len - r);
4092 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4096 r +=
static_cast<uint64_t
>(n);
4100inline bool read_content_without_length(Stream &strm, ContentReceiverWithProgress out) {
4101 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4104 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
4109 if (!out(buf,
static_cast<size_t>(n), r, 0)) {
4112 r +=
static_cast<uint64_t
>(n);
4118template<
typename T>
inline bool read_content_chunked(Stream &strm, T &
x, ContentReceiverWithProgress out) {
4119 const auto bufsiz = 16;
4122 stream_line_reader line_reader(strm, buf, bufsiz);
4124 if (!line_reader.getline()) {
4128 unsigned long chunk_len;
4132 chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
4134 if (end_ptr == line_reader.ptr()) {
4137 if (chunk_len == ULONG_MAX) {
4141 if (chunk_len == 0) {
4145 if (!read_content_with_length(strm, chunk_len,
nullptr, out)) {
4149 if (!line_reader.getline()) {
4153 if (strcmp(line_reader.ptr(),
"\r\n") != 0) {
4157 if (!line_reader.getline()) {
4162 assert(chunk_len == 0);
4176 if (!line_reader.getline()) {
4180 while (strcmp(line_reader.ptr(),
"\r\n") != 0) {
4181 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4186 constexpr auto line_terminator_len = 2;
4187 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4189 parse_header(line_reader.ptr(),
end,
4190 [&](
const std::string &key,
const std::string &
val) { x.headers.emplace(key, val); });
4192 if (!line_reader.getline()) {
4200inline bool is_chunked_transfer_encoding(
const Headers &headers) {
4201 return case_ignore::equal(get_header_value(headers,
"Transfer-Encoding",
"", 0),
"chunked");
4204template<
typename T,
typename U>
4205bool prepare_content_receiver(T &
x,
int &
status, ContentReceiverWithProgress receiver,
bool decompress, U callback) {
4207 std::string encoding =
x.get_header_value(
"Content-Encoding");
4208 std::unique_ptr<decompressor> decompressor;
4210 if (encoding ==
"gzip" || encoding ==
"deflate") {
4211#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4212 decompressor = detail::make_unique<gzip_decompressor>();
4214 status = StatusCode::UnsupportedMediaType_415;
4217 }
else if (encoding.find(
"br") != std::string::npos) {
4218#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4219 decompressor = detail::make_unique<brotli_decompressor>();
4221 status = StatusCode::UnsupportedMediaType_415;
4227 if (decompressor->is_valid()) {
4228 ContentReceiverWithProgress out = [&](
const char *buf,
size_t n, uint64_t off, uint64_t
len) {
4229 return decompressor->decompress(buf, n,
4230 [&](
const char *buf2,
size_t n2) {
return receiver(buf2, n2, off,
len); });
4232 return callback(std::move(out));
4234 status = StatusCode::InternalServerError_500;
4240 ContentReceiverWithProgress out = [&](
const char *buf,
size_t n, uint64_t off, uint64_t
len) {
4241 return receiver(buf, n, off,
len);
4243 return callback(std::move(out));
4247bool read_content(Stream &strm, T &
x,
size_t payload_max_length,
int &
status, Progress progress,
4248 ContentReceiverWithProgress receiver,
bool decompress) {
4249 return prepare_content_receiver(
4250 x,
status, std::move(receiver), decompress, [&](
const ContentReceiverWithProgress &out) {
4252 auto exceed_payload_max_length =
false;
4254 if (is_chunked_transfer_encoding(
x.headers)) {
4255 ret = read_content_chunked(strm,
x, out);
4256 }
else if (!has_header(
x.headers,
"Content-Length")) {
4257 ret = read_content_without_length(strm, out);
4259 auto len = get_header_value_u64(
x.headers,
"Content-Length", 0, 0);
4260 if (
len > payload_max_length) {
4261 exceed_payload_max_length =
true;
4262 skip_content_with_length(strm,
len);
4264 }
else if (
len > 0) {
4265 ret = read_content_with_length(strm,
len, std::move(progress), out);
4270 status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413 : StatusCode::BadRequest_400;
4276inline ssize_t write_request_line(Stream &strm,
const std::string &method,
const std::string &path) {
4277 std::string s = method;
4280 s +=
" HTTP/1.1\r\n";
4281 return strm.write(s.data(), s.size());
4284inline ssize_t write_response_line(Stream &strm,
int status) {
4285 std::string s =
"HTTP/1.1 ";
4286 s += std::to_string(
status);
4288 s += httplib::status_message(
status);
4290 return strm.write(s.data(), s.size());
4293inline ssize_t write_headers(Stream &strm,
const Headers &headers) {
4295 for (
const auto &
x : headers) {
4302 auto len = strm.write(s.data(), s.size());
4308 auto len = strm.write(
"\r\n");
4316inline bool write_data(Stream &strm,
const char *d,
size_t l) {
4318 while (offset <
l) {
4319 auto length = strm.write(d + offset,
l - offset);
4323 offset +=
static_cast<size_t>(
length);
4329inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
size_t offset,
size_t length,
4330 T is_shutting_down, Error &error) {
4331 size_t end_offset = offset +
length;
4335 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
4337 if (strm.is_writable() && write_data(strm, d,
l)) {
4346 data_sink.is_writable = [&]() ->
bool {
return strm.is_writable(); };
4348 while (offset < end_offset && !is_shutting_down()) {
4349 if (!strm.is_writable()) {
4350 error = Error::Write;
4352 }
else if (!content_provider(offset, end_offset - offset, data_sink)) {
4353 error = Error::Canceled;
4356 error = Error::Write;
4361 error = Error::Success;
4366inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
size_t offset,
size_t length,
4367 const T &is_shutting_down) {
4368 auto error = Error::Success;
4369 return write_content(strm, content_provider, offset,
length, is_shutting_down, error);
4373inline bool write_content_without_length(Stream &strm,
const ContentProvider &content_provider,
4374 const T &is_shutting_down) {
4376 auto data_available =
true;
4380 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
4383 if (!strm.is_writable() || !write_data(strm, d,
l)) {
4390 data_sink.is_writable = [&]() ->
bool {
return strm.is_writable(); };
4392 data_sink.done = [&](void) { data_available =
false; };
4394 while (data_available && !is_shutting_down()) {
4395 if (!strm.is_writable()) {
4397 }
else if (!content_provider(offset, 0, data_sink)) {
4406template<
typename T,
typename U>
4407inline bool write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
const T &is_shutting_down,
4408 U &compressor, Error &error) {
4410 auto data_available =
true;
4414 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
4416 data_available =
l > 0;
4419 std::string payload;
4420 if (compressor.compress(d,
l,
false, [&](
const char *data,
size_t data_len) {
4421 payload.append(data, data_len);
4424 if (!payload.empty()) {
4426 auto chunk = from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
4427 if (!strm.is_writable() || !write_data(strm, chunk.data(), chunk.size())) {
4438 data_sink.is_writable = [&]() ->
bool {
return strm.is_writable(); };
4440 auto done_with_trailer = [&](
const Headers *
trailer) {
4445 data_available =
false;
4447 std::string payload;
4448 if (!compressor.compress(
nullptr, 0,
true, [&](
const char *data,
size_t data_len) {
4449 payload.append(data, data_len);
4456 if (!payload.empty()) {
4458 auto chunk = from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
4459 if (!strm.is_writable() || !write_data(strm, chunk.data(), chunk.size())) {
4465 static const std::string done_marker(
"0\r\n");
4466 if (!write_data(strm, done_marker.data(), done_marker.size())) {
4472 for (
const auto &kv : *
trailer) {
4473 std::string field_line = kv.first +
": " + kv.second +
"\r\n";
4474 if (!write_data(strm, field_line.data(), field_line.size())) {
4480 static const std::string crlf(
"\r\n");
4481 if (!write_data(strm, crlf.data(), crlf.size())) {
4486 data_sink.done = [&](void) { done_with_trailer(
nullptr); };
4488 data_sink.done_with_trailer = [&](
const Headers &
trailer) { done_with_trailer(&
trailer); };
4490 while (data_available && !is_shutting_down()) {
4491 if (!strm.is_writable()) {
4492 error = Error::Write;
4494 }
else if (!content_provider(offset, 0, data_sink)) {
4495 error = Error::Canceled;
4498 error = Error::Write;
4503 error = Error::Success;
4507template<
typename T,
typename U>
4508inline bool write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
const T &is_shutting_down,
4510 auto error = Error::Success;
4511 return write_content_chunked(strm, content_provider, is_shutting_down, compressor, error);
4515inline bool redirect(T &cli, Request &req, Response &res,
const std::string &path,
const std::string &location,
4517 Request new_req = req;
4518 new_req.path = path;
4519 new_req.redirect_count_ -= 1;
4521 if (res.status == StatusCode::SeeOther_303 && (req.method !=
"GET" && req.method !=
"HEAD")) {
4522 new_req.method =
"GET";
4523 new_req.body.clear();
4524 new_req.headers.clear();
4529 auto ret = cli.send(new_req, new_res, error);
4534 if (res.location.empty()) {
4535 res.location = location;
4541inline std::string params_to_query_str(
const Params ¶ms) {
4544 for (
auto it = params.begin(); it != params.end(); ++it) {
4545 if (it != params.begin()) {
4550 query += encode_query_param(it->second);
4555inline void parse_query_text(
const char *data, std::size_t size, Params ¶ms) {
4556 std::set<std::string> cache;
4557 split(data, data + size,
'&', [&](
const char *b,
const char *e) {
4558 std::string kv(b, e);
4559 if (cache.find(kv) != cache.end()) {
4562 cache.insert(std::move(kv));
4566 divide(b,
static_cast<std::size_t
>(e - b),
'=',
4567 [&](
const char *lhs_data, std::size_t lhs_size,
const char *rhs_data, std::size_t rhs_size) {
4568 key.assign(lhs_data, lhs_size);
4569 val.assign(rhs_data, rhs_size);
4573 params.emplace(decode_url(key,
true), decode_url(
val,
true));
4578inline void parse_query_text(
const std::string &s, Params ¶ms) { parse_query_text(s.data(), s.size(), params); }
4580inline bool parse_multipart_boundary(
const std::string &content_type, std::string &boundary) {
4581 auto boundary_keyword =
"boundary=";
4582 auto pos = content_type.find(boundary_keyword);
4583 if (pos == std::string::npos) {
4586 auto end = content_type.find(
';', pos);
4587 auto beg = pos + strlen(boundary_keyword);
4588 boundary = trim_double_quotes_copy(content_type.substr(beg,
end - beg));
4589 return !boundary.empty();
4592inline void parse_disposition_params(
const std::string &s, Params ¶ms) {
4593 std::set<std::string> cache;
4594 split(s.data(), s.data() + s.size(),
';', [&](
const char *b,
const char *e) {
4595 std::string kv(b, e);
4596 if (cache.find(kv) != cache.end()) {
4603 split(b, e,
'=', [&](
const char *b2,
const char *e2) {
4612 params.emplace(trim_double_quotes_copy((key)), trim_double_quotes_copy((val)));
4617#ifdef CPPHTTPLIB_NO_EXCEPTIONS
4618inline bool parse_range_header(
const std::string &s, Ranges &ranges) {
4620inline bool parse_range_header(
const std::string &s, Ranges &ranges)
try {
4622 auto is_valid = [](
const std::string &str) {
4623 return std::all_of(str.cbegin(), str.cend(), [](
unsigned char c) { return std::isdigit(c); });
4626 if (s.size() > 7 && s.compare(0, 6,
"bytes=") == 0) {
4627 const auto pos =
static_cast<size_t>(6);
4628 const auto len =
static_cast<size_t>(s.size() - 6);
4629 auto all_valid_ranges =
true;
4630 split(&s[pos], &s[pos +
len],
',', [&](
const char *b,
const char *e) {
4631 if (!all_valid_ranges) {
4635 const auto it = std::find(b, e,
'-');
4637 all_valid_ranges =
false;
4641 const auto lhs = std::string(b, it);
4642 const auto rhs = std::string(it + 1, e);
4643 if (!is_valid(lhs) || !is_valid(rhs)) {
4644 all_valid_ranges =
false;
4648 const auto first =
static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
4649 const auto last =
static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
4650 if ((first == -1 && last == -1) || (first != -1 && last != -1 && first > last)) {
4651 all_valid_ranges =
false;
4655 ranges.emplace_back(first, last);
4657 return all_valid_ranges && !ranges.empty();
4660#ifdef CPPHTTPLIB_NO_EXCEPTIONS
4668class MultipartFormDataParser {
4670 MultipartFormDataParser() =
default;
4672 void set_boundary(std::string &&boundary) {
4673 boundary_ = boundary;
4674 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
4675 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
4678 bool is_valid()
const {
return is_valid_; }
4680 bool parse(
const char *buf,
size_t n,
const ContentReceiver &content_callback,
4681 const MultipartContentHeader &header_callback) {
4684 while (buf_size() > 0) {
4687 buf_erase(buf_find(dash_boundary_crlf_));
4688 if (dash_boundary_crlf_.size() > buf_size()) {
4691 if (!buf_start_with(dash_boundary_crlf_)) {
4694 buf_erase(dash_boundary_crlf_.size());
4704 auto pos = buf_find(crlf_);
4705 if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4708 while (pos < buf_size()) {
4711 if (!header_callback(file_)) {
4715 buf_erase(crlf_.size());
4720 const auto header = buf_head(pos);
4722 if (!parse_header(header.data(), header.data() + header.size(),
4723 [&](
const std::string &,
const std::string &) {})) {
4728 static const std::string header_content_type =
"Content-Type:";
4730 if (start_with_case_ignore(header, header_content_type)) {
4731 file_.content_type = trim_copy(header.substr(header_content_type.size()));
4733 static const std::regex re_content_disposition(R
"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
4734 std::regex_constants::icase);
4737 if (std::regex_match(header,
m, re_content_disposition)) {
4739 parse_disposition_params(
m[1], params);
4741 auto it = params.find(
"name");
4742 if (it != params.end()) {
4743 file_.name = it->second;
4749 it = params.find(
"filename");
4750 if (it != params.end()) {
4751 file_.filename = it->second;
4754 it = params.find(
"filename*");
4755 if (it != params.end()) {
4757 static const std::regex re_rfc5987_encoding(R
"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
4760 if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
4761 file_.filename = decode_url(m2[1],
false);
4769 buf_erase(pos + crlf_.size());
4770 pos = buf_find(crlf_);
4778 if (crlf_dash_boundary_.size() > buf_size()) {
4781 auto pos = buf_find(crlf_dash_boundary_);
4782 if (pos < buf_size()) {
4783 if (!content_callback(buf_data(), pos)) {
4787 buf_erase(pos + crlf_dash_boundary_.size());
4790 auto len = buf_size() - crlf_dash_boundary_.size();
4792 if (!content_callback(buf_data(),
len)) {
4803 if (crlf_.size() > buf_size()) {
4806 if (buf_start_with(crlf_)) {
4807 buf_erase(crlf_.size());
4810 if (dash_.size() > buf_size()) {
4813 if (buf_start_with(dash_)) {
4814 buf_erase(dash_.size());
4816 buf_erase(buf_size());
4830 void clear_file_info() {
4832 file_.filename.clear();
4833 file_.content_type.clear();
4836 bool start_with_case_ignore(
const std::string &a,
const std::string &b)
const {
4837 if (a.size() < b.size()) {
4840 for (
size_t i = 0; i < b.size(); i++) {
4841 if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
4848 const std::string dash_ =
"--";
4849 const std::string crlf_ =
"\r\n";
4850 std::string boundary_;
4851 std::string dash_boundary_crlf_;
4852 std::string crlf_dash_boundary_;
4855 bool is_valid_ =
false;
4856 MultipartFormData file_;
4859 bool start_with(
const std::string &a,
size_t spos,
size_t epos,
const std::string &b)
const {
4860 if (epos - spos < b.size()) {
4863 for (
size_t i = 0; i < b.size(); i++) {
4864 if (a[i + spos] != b[i]) {
4871 size_t buf_size()
const {
return buf_epos_ - buf_spos_; }
4873 const char *buf_data()
const {
return &buf_[buf_spos_]; }
4875 std::string buf_head(
size_t l)
const {
return buf_.substr(buf_spos_,
l); }
4877 bool buf_start_with(
const std::string &s)
const {
return start_with(buf_, buf_spos_, buf_epos_, s); }
4879 size_t buf_find(
const std::string &s)
const {
4882 size_t off = buf_spos_;
4883 while (off < buf_epos_) {
4886 if (pos == buf_epos_) {
4889 if (buf_[pos] == c) {
4895 auto remaining_size = buf_epos_ - pos;
4896 if (s.size() > remaining_size) {
4900 if (start_with(buf_, pos, buf_epos_, s)) {
4901 return pos - buf_spos_;
4910 void buf_append(
const char *data,
size_t n) {
4911 auto remaining_size = buf_size();
4912 if (remaining_size > 0 && buf_spos_ > 0) {
4913 for (
size_t i = 0; i < remaining_size; i++) {
4914 buf_[i] = buf_[buf_spos_ + i];
4918 buf_epos_ = remaining_size;
4920 if (remaining_size + n > buf_.size()) {
4921 buf_.resize(remaining_size + n);
4924 for (
size_t i = 0; i < n; i++) {
4925 buf_[buf_epos_ + i] = data[i];
4930 void buf_erase(
size_t size) { buf_spos_ += size; }
4933 size_t buf_spos_ = 0;
4934 size_t buf_epos_ = 0;
4937inline std::string random_string(
size_t length) {
4938 static const char data[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
4943 static std::random_device seed_gen;
4946 static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
4948 static std::mt19937 engine(seed_sequence);
4951 for (
size_t i = 0; i <
length; i++) {
4952 result += data[engine() % (
sizeof(data) - 1)];
4957inline std::string make_multipart_data_boundary() {
4958 return "--cpp-httplib-multipart-data-" + detail::random_string(16);
4961inline bool is_multipart_boundary_chars_valid(
const std::string &boundary) {
4963 for (
size_t i = 0; i < boundary.size(); i++) {
4964 auto c = boundary[i];
4965 if (!std::isalnum(c) && c !=
'-' && c !=
'_') {
4974inline std::string serialize_multipart_formdata_item_begin(
const T &item,
const std::string &boundary) {
4975 std::string body =
"--" + boundary +
"\r\n";
4976 body +=
"Content-Disposition: form-data; name=\"" + item.name +
"\"";
4977 if (!item.filename.empty()) {
4978 body +=
"; filename=\"" + item.filename +
"\"";
4981 if (!item.content_type.empty()) {
4982 body +=
"Content-Type: " + item.content_type +
"\r\n";
4989inline std::string serialize_multipart_formdata_item_end() {
return "\r\n"; }
4991inline std::string serialize_multipart_formdata_finish(
const std::string &boundary) {
4992 return "--" + boundary +
"--\r\n";
4995inline std::string serialize_multipart_formdata_get_content_type(
const std::string &boundary) {
4996 return "multipart/form-data; boundary=" + boundary;
4999inline std::string serialize_multipart_formdata(
const MultipartFormDataItems &items,
const std::string &boundary,
5000 bool finish =
true) {
5003 for (
const auto &item : items) {
5004 body += serialize_multipart_formdata_item_begin(item, boundary);
5005 body += item.content + serialize_multipart_formdata_item_end();
5009 body += serialize_multipart_formdata_finish(boundary);
5015inline bool range_error(Request &req, Response &res) {
5016 if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
5017 ssize_t contant_len =
static_cast<ssize_t>(res.content_length_ ? res.content_length_ : res.body.size());
5021 size_t overwrapping_count = 0;
5028 if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) {
5032 for (
auto &r : req.ranges) {
5033 auto &first_pos = r.first;
5034 auto &last_pos = r.second;
5036 if (first_pos == -1 && last_pos == -1) {
5038 last_pos = contant_len;
5041 if (first_pos == -1) {
5042 first_pos = contant_len - last_pos;
5043 last_pos = contant_len - 1;
5046 if (last_pos == -1) {
5047 last_pos = contant_len - 1;
5051 if (!(0 <= first_pos && first_pos <= last_pos && last_pos <= contant_len - 1)) {
5056 if (first_pos <= prev_first_pos) {
5061 if (first_pos <= prev_last_pos) {
5062 overwrapping_count++;
5063 if (overwrapping_count > 2) {
5068 prev_first_pos = (std::max)(prev_first_pos, first_pos);
5069 prev_last_pos = (std::max)(prev_last_pos, last_pos);
5076inline std::pair<size_t, size_t> get_range_offset_and_length(Range r,
size_t content_length) {
5077 assert(r.first != -1 && r.second != -1);
5078 assert(0 <= r.first && r.first <
static_cast<ssize_t>(content_length));
5079 assert(r.first <= r.second && r.second <
static_cast<ssize_t>(content_length));
5080 (void) (content_length);
5081 return std::make_pair(r.first,
static_cast<size_t>(r.second - r.first) + 1);
5084inline std::string make_content_range_header_field(
const std::pair<size_t, size_t> &offset_and_length,
5085 size_t content_length) {
5086 auto st = offset_and_length.first;
5087 auto ed = st + offset_and_length.second - 1;
5089 std::string field =
"bytes ";
5090 field += std::to_string(st);
5092 field += std::to_string(ed);
5094 field += std::to_string(content_length);
5098template<
typename SToken,
typename CToken,
typename Content>
5099bool process_multipart_ranges_data(
const Request &req,
const std::string &boundary,
const std::string &content_type,
5100 size_t content_length, SToken stoken, CToken ctoken, Content content) {
5101 for (
size_t i = 0; i < req.ranges.size(); i++) {
5105 if (!content_type.empty()) {
5106 ctoken(
"Content-Type: ");
5107 stoken(content_type);
5111 auto offset_and_length = get_range_offset_and_length(req.ranges[i], content_length);
5113 ctoken(
"Content-Range: ");
5114 stoken(make_content_range_header_field(offset_and_length, content_length));
5118 if (!content(offset_and_length.first, offset_and_length.second)) {
5131inline void make_multipart_ranges_data(
const Request &req, Response &res,
const std::string &boundary,
5132 const std::string &content_type,
size_t content_length, std::string &data) {
5133 process_multipart_ranges_data(
5134 req, boundary, content_type, content_length, [&](
const std::string &token) { data += token; },
5135 [&](
const std::string &token) { data += token; },
5136 [&](
size_t offset,
size_t length) {
5137 assert(offset +
length <= content_length);
5138 data += res.body.substr(offset,
length);
5143inline size_t get_multipart_ranges_data_length(
const Request &req,
const std::string &boundary,
5144 const std::string &content_type,
size_t content_length) {
5145 size_t data_length = 0;
5147 process_multipart_ranges_data(
5148 req, boundary, content_type, content_length, [&](
const std::string &token) { data_length += token.size(); },
5149 [&](
const std::string &token) { data_length += token.size(); },
5150 [&](
size_t ,
size_t length) {
5159inline bool write_multipart_ranges_data(Stream &strm,
const Request &req, Response &res,
const std::string &boundary,
5160 const std::string &content_type,
size_t content_length,
5161 const T &is_shutting_down) {
5162 return process_multipart_ranges_data(
5163 req, boundary, content_type, content_length, [&](
const std::string &token) { strm.write(token); },
5164 [&](
const std::string &token) { strm.write(token); },
5165 [&](
size_t offset,
size_t length) {
5166 return write_content(strm, res.content_provider_, offset,
length, is_shutting_down);
5170inline bool expect_content(
const Request &req) {
5171 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH" || req.method ==
"PRI" ||
5172 req.method ==
"DELETE") {
5179inline bool has_crlf(
const std::string &s) {
5182 if (*p ==
'\r' || *p ==
'\n') {
5190#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5191inline std::string message_digest(
const std::string &s,
const EVP_MD *algo) {
5192 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(EVP_MD_CTX_new(), EVP_MD_CTX_free);
5194 unsigned int hash_length = 0;
5195 unsigned char hash[EVP_MAX_MD_SIZE];
5197 EVP_DigestInit_ex(context.get(), algo,
nullptr);
5198 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
5199 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
5201 std::stringstream ss;
5202 for (
auto i = 0u; i < hash_length; ++i) {
5203 ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(hash[i]);
5209inline std::string MD5(
const std::string &s) {
return message_digest(s, EVP_md5()); }
5211inline std::string SHA_256(
const std::string &s) {
return message_digest(s, EVP_sha256()); }
5213inline std::string SHA_512(
const std::string &s) {
return message_digest(s, EVP_sha512()); }
5216#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5220inline bool load_system_certs_on_windows(X509_STORE *store) {
5221 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY) NULL, L
"ROOT");
5226 auto result =
false;
5227 PCCERT_CONTEXT pContext = NULL;
5228 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
nullptr) {
5229 auto encoded_cert =
static_cast<const unsigned char *
>(pContext->pbCertEncoded);
5231 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
5233 X509_STORE_add_cert(store, x509);
5239 CertFreeCertificateContext(pContext);
5240 CertCloseStore(hStore, 0);
5244#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
5246template<
typename T>
using CFObjectPtr = std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
5248inline void cf_object_ptr_deleter(CFTypeRef obj) {
5254inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5255 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
5256 CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll, kCFBooleanTrue};
5258 CFObjectPtr<CFDictionaryRef> query(
5259 CFDictionaryCreate(
nullptr,
reinterpret_cast<const void **
>(keys), values,
sizeof(keys) /
sizeof(keys[0]),
5260 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks),
5261 cf_object_ptr_deleter);
5267 CFTypeRef security_items =
nullptr;
5268 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
5269 CFArrayGetTypeID() != CFGetTypeID(security_items)) {
5273 certs.reset(
reinterpret_cast<CFArrayRef
>(security_items));
5277inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5278 CFArrayRef root_security_items =
nullptr;
5279 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
5283 certs.reset(root_security_items);
5287inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
5288 auto result =
false;
5289 for (
auto i = 0; i < CFArrayGetCount(certs); ++i) {
5290 const auto cert =
reinterpret_cast<const __SecCertificate *
>(CFArrayGetValueAtIndex(certs, i));
5292 if (SecCertificateGetTypeID() != CFGetTypeID(cert)) {
5296 CFDataRef cert_data =
nullptr;
5297 if (SecItemExport(cert, kSecFormatX509Cert, 0,
nullptr, &cert_data) != errSecSuccess) {
5301 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
5303 auto encoded_cert =
static_cast<const unsigned char *
>(CFDataGetBytePtr(cert_data_ptr.get()));
5305 auto x509 = d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
5308 X509_STORE_add_cert(store, x509);
5317inline bool load_system_certs_on_macos(X509_STORE *store) {
5318 auto result =
false;
5319 CFObjectPtr<CFArrayRef> certs(
nullptr, cf_object_ptr_deleter);
5320 if (retrieve_certs_from_keychain(certs) && certs) {
5321 result = add_certs_to_x509_store(certs.get(), store);
5324 if (retrieve_root_certs_from_keychain(certs) && certs) {
5325 result = add_certs_to_x509_store(certs.get(), store) || result;
5339 if (WSAStartup(0x0002, &wsaData) == 0)
5348 bool is_valid_ =
false;
5351static WSInit wsinit_;
5354#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5355inline std::pair<std::string, std::string> make_digest_authentication_header(
5356 const Request &req,
const std::map<std::string, std::string> &auth,
size_t cnonce_count,
const std::string &cnonce,
5357 const std::string &username,
const std::string &password,
bool is_proxy =
false) {
5360 std::stringstream ss;
5361 ss << std::setfill(
'0') << std::setw(8) << std::hex << cnonce_count;
5366 if (auth.find(
"qop") != auth.end()) {
5367 qop = auth.at(
"qop");
5368 if (qop.find(
"auth-int") != std::string::npos) {
5370 }
else if (qop.find(
"auth") != std::string::npos) {
5377 std::string algo =
"MD5";
5378 if (auth.find(
"algorithm") != auth.end()) {
5379 algo = auth.at(
"algorithm");
5382 std::string response;
5384 auto H = algo ==
"SHA-256" ? detail::SHA_256 : algo ==
"SHA-512" ? detail::SHA_512 : detail::MD5;
5386 auto A1 = username +
":" + auth.at(
"realm") +
":" + password;
5388 auto A2 = req.method +
":" + req.path;
5389 if (qop ==
"auth-int") {
5390 A2 +=
":" + H(req.body);
5394 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + H(A2));
5396 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + nc +
":" + cnonce +
":" + qop +
":" + H(A2));
5400 auto opaque = (auth.find(
"opaque") != auth.end()) ? auth.at(
"opaque") :
"";
5403 "Digest username=\"" + username +
"\", realm=\"" + auth.at(
"realm") +
"\", nonce=\"" + auth.at(
"nonce") +
5404 "\", uri=\"" + req.path +
"\", algorithm=" + algo +
5405 (qop.empty() ?
", response=\"" :
", qop=" + qop +
", nc=" + nc +
", cnonce=\"" + cnonce +
"\", response=\"") +
5406 response +
"\"" + (opaque.empty() ?
"" :
", opaque=\"" + opaque +
"\"");
5408 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5409 return std::make_pair(key, field);
5413inline bool parse_www_authenticate(
const Response &res, std::map<std::string, std::string> &auth,
bool is_proxy) {
5414 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
5415 if (res.has_header(auth_key)) {
5416 static auto re = std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
5417 auto s = res.get_header_value(auth_key);
5418 auto pos = s.find(
' ');
5419 if (pos != std::string::npos) {
5420 auto type = s.substr(0, pos);
5421 if (
type ==
"Basic") {
5423 }
else if (
type ==
"Digest") {
5424 s = s.substr(pos + 1);
5425 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
5426 for (
auto i = beg; i != std::sregex_iterator(); ++i) {
5428 auto key = s.substr(
static_cast<size_t>(
m.position(1)),
static_cast<size_t>(
m.length(1)));
5429 auto val =
m.length(2) > 0 ? s.substr(
static_cast<size_t>(
m.position(2)),
static_cast<size_t>(
m.length(2)))
5430 : s.substr(
static_cast<size_t>(
m.position(3)),
static_cast<size_t>(
m.length(3)));
5440class ContentProviderAdapter {
5442 explicit ContentProviderAdapter(ContentProviderWithoutLength &&content_provider)
5443 : content_provider_(content_provider) {}
5445 bool operator()(
size_t offset,
size_t, DataSink &sink) {
return content_provider_(offset, sink); }
5448 ContentProviderWithoutLength content_provider_;
5453inline std::string hosted_at(
const std::string &hostname) {
5454 std::vector<std::string> addrs;
5455 hosted_at(hostname, addrs);
5456 if (addrs.empty()) {
5457 return std::string();
5462inline void hosted_at(
const std::string &hostname, std::vector<std::string> &addrs) {
5463 struct addrinfo hints;
5464 struct addrinfo *result;
5466 memset(&hints, 0,
sizeof(
struct addrinfo));
5467 hints.ai_family = AF_UNSPEC;
5468 hints.ai_socktype = SOCK_STREAM;
5469 hints.ai_protocol = 0;
5471 if (getaddrinfo(hostname.c_str(),
nullptr, &hints, &result)) {
5472#if defined __linux__ && !defined __ANDROID__
5477 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5479 for (
auto rp = result; rp; rp = rp->ai_next) {
5480 const auto &addr = *
reinterpret_cast<struct
sockaddr_storage *
>(rp->ai_addr);
5483 if (detail::get_ip_and_port(addr,
sizeof(
struct sockaddr_storage), ip, dummy)) {
5484 addrs.push_back(ip);
5489inline std::string append_query_params(
const std::string &path,
const Params ¶ms) {
5490 std::string path_with_query = path;
5491 const static std::regex re(
"[^?]+\\?.*");
5492 auto delm = std::regex_match(path, re) ?
'&' :
'?';
5493 path_with_query += delm + detail::params_to_query_str(params);
5494 return path_with_query;
5498inline std::pair<std::string, std::string> make_range_header(
const Ranges &ranges) {
5499 std::string field =
"bytes=";
5501 for (
const auto &r : ranges) {
5505 if (r.first != -1) {
5506 field += std::to_string(r.first);
5509 if (r.second != -1) {
5510 field += std::to_string(r.second);
5514 return std::make_pair(
"Range", std::move(field));
5517inline std::pair<std::string, std::string> make_basic_authentication_header(
const std::string &username,
5518 const std::string &password,
5520 auto field =
"Basic " + detail::base64_encode(username +
":" + password);
5521 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5522 return std::make_pair(key, std::move(field));
5525inline std::pair<std::string, std::string> make_bearer_token_authentication_header(
const std::string &token,
5526 bool is_proxy =
false) {
5527 auto field =
"Bearer " + token;
5528 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5529 return std::make_pair(key, std::move(field));
5533inline bool Request::has_header(
const std::string &key)
const {
return detail::has_header(headers, key); }
5535inline std::string Request::get_header_value(
const std::string &key,
const char *def,
size_t id)
const {
5536 return detail::get_header_value(headers, key, def,
id);
5539inline size_t Request::get_header_value_count(
const std::string &key)
const {
5540 auto r = headers.equal_range(key);
5541 return static_cast<size_t>(std::distance(r.first, r.second));
5544inline void Request::set_header(
const std::string &key,
const std::string &
val) {
5545 if (!detail::has_crlf(key) && !detail::has_crlf(
val)) {
5546 headers.emplace(key,
val);
5550inline bool Request::has_param(
const std::string &key)
const {
return params.find(key) != params.end(); }
5552inline std::string Request::get_param_value(
const std::string &key,
size_t id)
const {
5553 auto rng = params.equal_range(key);
5554 auto it = rng.first;
5555 std::advance(it,
static_cast<ssize_t>(
id));
5556 if (it != rng.second) {
5559 return std::string();
5562inline size_t Request::get_param_value_count(
const std::string &key)
const {
5563 auto r = params.equal_range(key);
5564 return static_cast<size_t>(std::distance(r.first, r.second));
5567inline bool Request::is_multipart_form_data()
const {
5568 const auto &content_type = get_header_value(
"Content-Type");
5569 return !content_type.rfind(
"multipart/form-data", 0);
5572inline bool Request::has_file(
const std::string &key)
const {
return files.find(key) != files.end(); }
5574inline MultipartFormData Request::get_file_value(
const std::string &key)
const {
5575 auto it = files.find(key);
5576 if (it != files.end()) {
5579 return MultipartFormData();
5582inline std::vector<MultipartFormData> Request::get_file_values(
const std::string &key)
const {
5583 std::vector<MultipartFormData> values;
5584 auto rng = files.equal_range(key);
5585 for (
auto it = rng.first; it != rng.second; it++) {
5586 values.push_back(it->second);
5592inline bool Response::has_header(
const std::string &key)
const {
return headers.find(key) != headers.end(); }
5594inline std::string Response::get_header_value(
const std::string &key,
const char *def,
size_t id)
const {
5595 return detail::get_header_value(headers, key, def,
id);
5598inline size_t Response::get_header_value_count(
const std::string &key)
const {
5599 auto r = headers.equal_range(key);
5600 return static_cast<size_t>(std::distance(r.first, r.second));
5603inline void Response::set_header(
const std::string &key,
const std::string &
val) {
5604 if (!detail::has_crlf(key) && !detail::has_crlf(
val)) {
5605 headers.emplace(key,
val);
5609inline void Response::set_redirect(
const std::string &url,
int stat) {
5610 if (!detail::has_crlf(url)) {
5611 set_header(
"Location", url);
5612 if (300 <= stat && stat < 400) {
5615 this->
status = StatusCode::Found_302;
5620inline void Response::set_content(
const char *s,
size_t n,
const std::string &content_type) {
5623 auto rng = headers.equal_range(
"Content-Type");
5624 headers.erase(rng.first, rng.second);
5625 set_header(
"Content-Type", content_type);
5628inline void Response::set_content(
const std::string &s,
const std::string &content_type) {
5629 set_content(s.data(), s.size(), content_type);
5632inline void Response::set_content(std::string &&s,
const std::string &content_type) {
5633 body = std::move(s);
5635 auto rng = headers.equal_range(
"Content-Type");
5636 headers.erase(rng.first, rng.second);
5637 set_header(
"Content-Type", content_type);
5640inline void Response::set_content_provider(
size_t in_length,
const std::string &content_type, ContentProvider provider,
5641 ContentProviderResourceReleaser resource_releaser) {
5642 set_header(
"Content-Type", content_type);
5643 content_length_ = in_length;
5644 if (in_length > 0) {
5645 content_provider_ = std::move(provider);
5647 content_provider_resource_releaser_ = std::move(resource_releaser);
5648 is_chunked_content_provider_ =
false;
5651inline void Response::set_content_provider(
const std::string &content_type, ContentProviderWithoutLength provider,
5652 ContentProviderResourceReleaser resource_releaser) {
5653 set_header(
"Content-Type", content_type);
5654 content_length_ = 0;
5655 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
5656 content_provider_resource_releaser_ = std::move(resource_releaser);
5657 is_chunked_content_provider_ =
false;
5660inline void Response::set_chunked_content_provider(
const std::string &content_type,
5661 ContentProviderWithoutLength provider,
5662 ContentProviderResourceReleaser resource_releaser) {
5663 set_header(
"Content-Type", content_type);
5664 content_length_ = 0;
5665 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
5666 content_provider_resource_releaser_ = std::move(resource_releaser);
5667 is_chunked_content_provider_ =
true;
5670inline void Response::set_file_content(
const std::string &path,
const std::string &content_type) {
5671 file_content_path_ = path;
5672 file_content_content_type_ = content_type;
5675inline void Response::set_file_content(
const std::string &path) { file_content_path_ = path; }
5678inline bool Result::has_request_header(
const std::string &key)
const {
5679 return request_headers_.find(key) != request_headers_.end();
5682inline std::string Result::get_request_header_value(
const std::string &key,
const char *def,
size_t id)
const {
5683 return detail::get_header_value(request_headers_, key, def,
id);
5686inline size_t Result::get_request_header_value_count(
const std::string &key)
const {
5687 auto r = request_headers_.equal_range(key);
5688 return static_cast<size_t>(std::distance(r.first, r.second));
5692inline ssize_t Stream::write(
const char *ptr) {
return write(ptr, strlen(ptr)); }
5694inline ssize_t Stream::write(
const std::string &s) {
return write(s.data(), s.size()); }
5699inline SocketStream::SocketStream(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5700 time_t write_timeout_sec, time_t write_timeout_usec)
5702 read_timeout_sec_(read_timeout_sec),
5703 read_timeout_usec_(read_timeout_usec),
5704 write_timeout_sec_(write_timeout_sec),
5705 write_timeout_usec_(write_timeout_usec),
5706 read_buff_(read_buff_size_, 0) {}
5708inline SocketStream::~SocketStream() =
default;
5710inline bool SocketStream::is_readable()
const {
return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; }
5712inline bool SocketStream::is_writable()
const {
5713 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && is_socket_alive(sock_);
5716inline ssize_t SocketStream::read(
char *ptr,
size_t size) {
5718 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
5720 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
5723 if (read_buff_off_ < read_buff_content_size_) {
5724 auto remaining_size = read_buff_content_size_ - read_buff_off_;
5725 if (size <= remaining_size) {
5726 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
5727 read_buff_off_ += size;
5728 return static_cast<ssize_t>(size);
5730 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
5731 read_buff_off_ += remaining_size;
5732 return static_cast<ssize_t>(remaining_size);
5736 if (!is_readable()) {
5741 read_buff_content_size_ = 0;
5743 if (size < read_buff_size_) {
5744 auto n = read_socket(sock_, read_buff_.data(), read_buff_size_, CPPHTTPLIB_RECV_FLAGS);
5747 }
else if (n <=
static_cast<ssize_t>(size)) {
5748 memcpy(ptr, read_buff_.data(),
static_cast<size_t>(n));
5751 memcpy(ptr, read_buff_.data(), size);
5752 read_buff_off_ = size;
5753 read_buff_content_size_ =
static_cast<size_t>(n);
5754 return static_cast<ssize_t>(size);
5757 return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
5761inline ssize_t SocketStream::write(
const char *ptr,
size_t size) {
5762 if (!is_writable()) {
5766#if defined(_WIN32) && !defined(_WIN64)
5767 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
5770 return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
5773inline void SocketStream::get_remote_ip_and_port(std::string &ip,
int &port)
const {
5774 return detail::get_remote_ip_and_port(sock_, ip, port);
5777inline void SocketStream::get_local_ip_and_port(std::string &ip,
int &port)
const {
5778 return detail::get_local_ip_and_port(sock_, ip, port);
5781inline socket_t SocketStream::socket()
const {
return sock_; }
5784inline bool BufferStream::is_readable()
const {
return true; }
5786inline bool BufferStream::is_writable()
const {
return true; }
5788inline ssize_t BufferStream::read(
char *ptr,
size_t size) {
5789#if defined(_MSC_VER) && _MSC_VER < 1910
5790 auto len_read = buffer._Copy_s(ptr, size, size,
position);
5792 auto len_read = buffer.copy(ptr, size,
position);
5794 position +=
static_cast<size_t>(len_read);
5795 return static_cast<ssize_t>(len_read);
5798inline ssize_t BufferStream::write(
const char *ptr,
size_t size) {
5799 buffer.append(ptr, size);
5800 return static_cast<ssize_t>(size);
5803inline void BufferStream::get_remote_ip_and_port(std::string & ,
int & )
const {}
5805inline void BufferStream::get_local_ip_and_port(std::string & ,
int & )
const {}
5807inline socket_t BufferStream::socket()
const {
return 0; }
5809inline const std::string &BufferStream::get_buffer()
const {
return buffer; }
5811inline PathParamsMatcher::PathParamsMatcher(
const std::string &pattern) {
5812 static constexpr char marker[] =
"/:";
5815 std::size_t last_param_end = 0;
5817#ifndef CPPHTTPLIB_NO_EXCEPTIONS
5822 std::unordered_set<std::string> param_name_set;
5826 const auto marker_pos = pattern.find(marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
5827 if (marker_pos == std::string::npos) {
5831 static_fragments_.push_back(pattern.substr(last_param_end, marker_pos - last_param_end + 1));
5833 const auto param_name_start = marker_pos + 2;
5835 auto sep_pos = pattern.find(separator, param_name_start);
5836 if (sep_pos == std::string::npos) {
5837 sep_pos = pattern.length();
5840 auto param_name = pattern.substr(param_name_start, sep_pos - param_name_start);
5842#ifndef CPPHTTPLIB_NO_EXCEPTIONS
5843 if (param_name_set.find(param_name) != param_name_set.cend()) {
5845 "Encountered path parameter '" + param_name +
"' multiple times in route pattern '" + pattern +
"'.";
5846 throw std::invalid_argument(msg);
5850 param_names_.push_back(std::move(param_name));
5852 last_param_end = sep_pos + 1;
5855 if (last_param_end < pattern.length()) {
5856 static_fragments_.push_back(pattern.substr(last_param_end));
5860inline bool PathParamsMatcher::match(Request &request)
const {
5861 request.matches = std::smatch();
5862 request.path_params.clear();
5863 request.path_params.reserve(param_names_.size());
5866 std::size_t starting_pos = 0;
5867 for (
size_t i = 0; i < static_fragments_.size(); ++i) {
5868 const auto &fragment = static_fragments_[i];
5870 if (starting_pos + fragment.length() > request.path.length()) {
5876 if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(), fragment.length()) != 0) {
5880 starting_pos += fragment.length();
5885 if (i >= param_names_.size()) {
5889 auto sep_pos = request.path.find(separator, starting_pos);
5890 if (sep_pos == std::string::npos) {
5891 sep_pos = request.path.length();
5894 const auto ¶m_name = param_names_[i];
5896 request.path_params.emplace(param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
5899 starting_pos = sep_pos + 1;
5902 return starting_pos >= request.path.length();
5905inline bool RegexMatcher::match(Request &request)
const {
5906 request.path_params.clear();
5907 return std::regex_match(request.path, request.matches, regex_);
5913inline Server::Server() : new_task_queue([] {
return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
5915 signal(SIGPIPE, SIG_IGN);
5919inline Server::~Server() =
default;
5921inline std::unique_ptr<detail::MatcherBase> Server::make_matcher(
const std::string &pattern) {
5922 if (pattern.find(
"/:") != std::string::npos) {
5923 return detail::make_unique<detail::PathParamsMatcher>(pattern);
5925 return detail::make_unique<detail::RegexMatcher>(pattern);
5929inline Server &Server::Get(
const std::string &pattern, Handler handler) {
5930 get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5934inline Server &Server::Post(
const std::string &pattern, Handler handler) {
5935 post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5939inline Server &Server::Post(
const std::string &pattern, HandlerWithContentReader handler) {
5940 post_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5944inline Server &Server::Put(
const std::string &pattern, Handler handler) {
5945 put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5949inline Server &Server::Put(
const std::string &pattern, HandlerWithContentReader handler) {
5950 put_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5954inline Server &Server::Patch(
const std::string &pattern, Handler handler) {
5955 patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5959inline Server &Server::Patch(
const std::string &pattern, HandlerWithContentReader handler) {
5960 patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5964inline Server &Server::Delete(
const std::string &pattern, Handler handler) {
5965 delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5969inline Server &Server::Delete(
const std::string &pattern, HandlerWithContentReader handler) {
5970 delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5974inline Server &Server::Options(
const std::string &pattern, Handler handler) {
5975 options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5979inline bool Server::set_base_dir(
const std::string &dir,
const std::string &mount_point) {
5980 return set_mount_point(mount_point, dir);
5983inline bool Server::set_mount_point(
const std::string &mount_point,
const std::string &dir, Headers headers) {
5984 detail::FileStat stat(dir);
5985 if (stat.is_dir()) {
5986 std::string mnt = !mount_point.empty() ? mount_point :
"/";
5987 if (!mnt.empty() && mnt[0] ==
'/') {
5988 base_dirs_.push_back({mnt, dir, std::move(headers)});
5995inline bool Server::remove_mount_point(
const std::string &mount_point) {
5996 for (
auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
5997 if (it->mount_point == mount_point) {
5998 base_dirs_.erase(it);
6005inline Server &Server::set_file_extension_and_mimetype_mapping(
const std::string &ext,
const std::string &mime) {
6006 file_extension_and_mimetype_map_[ext] = mime;
6010inline Server &Server::set_default_file_mimetype(
const std::string &mime) {
6011 default_file_mimetype_ = mime;
6015inline Server &Server::set_file_request_handler(Handler handler) {
6016 file_request_handler_ = std::move(handler);
6020inline Server &Server::set_error_handler_core(HandlerWithResponse handler, std::true_type) {
6021 error_handler_ = std::move(handler);
6025inline Server &Server::set_error_handler_core(Handler handler, std::false_type) {
6026 error_handler_ = [handler](
const Request &req, Response &res) {
6028 return HandlerResponse::Handled;
6033inline Server &Server::set_exception_handler(ExceptionHandler handler) {
6034 exception_handler_ = std::move(handler);
6038inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
6039 pre_routing_handler_ = std::move(handler);
6043inline Server &Server::set_post_routing_handler(Handler handler) {
6044 post_routing_handler_ = std::move(handler);
6048inline Server &Server::set_logger(Logger logger) {
6049 logger_ = std::move(logger);
6053inline Server &Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
6054 expect_100_continue_handler_ = std::move(handler);
6058inline Server &Server::set_address_family(
int family) {
6059 address_family_ = family;
6063inline Server &Server::set_tcp_nodelay(
bool on) {
6068inline Server &Server::set_ipv6_v6only(
bool on) {
6073inline Server &Server::set_socket_options(SocketOptions socket_options) {
6074 socket_options_ = std::move(socket_options);
6078inline Server &Server::set_default_headers(Headers headers) {
6079 default_headers_ = std::move(headers);
6083inline Server &Server::set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer) {
6084 header_writer_ = writer;
6088inline Server &Server::set_keep_alive_max_count(
size_t count) {
6089 keep_alive_max_count_ = count;
6093inline Server &Server::set_keep_alive_timeout(time_t sec) {
6094 keep_alive_timeout_sec_ = sec;
6098inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
6099 read_timeout_sec_ = sec;
6100 read_timeout_usec_ = usec;
6104inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
6105 write_timeout_sec_ = sec;
6106 write_timeout_usec_ = usec;
6110inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
6111 idle_interval_sec_ = sec;
6112 idle_interval_usec_ = usec;
6116inline Server &Server::set_payload_max_length(
size_t length) {
6117 payload_max_length_ =
length;
6121inline bool Server::bind_to_port(
const std::string &host,
int port,
int socket_flags) {
6122 auto ret = bind_internal(host, port, socket_flags);
6124 is_decommisioned =
true;
6128inline int Server::bind_to_any_port(
const std::string &host,
int socket_flags) {
6129 auto ret = bind_internal(host, 0, socket_flags);
6131 is_decommisioned =
true;
6136inline bool Server::listen_after_bind() {
return listen_internal(); }
6138inline bool Server::listen(
const std::string &host,
int port,
int socket_flags) {
6139 return bind_to_port(host, port, socket_flags) && listen_internal();
6142inline bool Server::is_running()
const {
return is_running_; }
6144inline void Server::wait_until_ready()
const {
6145 while (!is_running_ && !is_decommisioned) {
6146 std::this_thread::sleep_for(std::chrono::milliseconds{1});
6150inline void Server::stop() {
6152 assert(svr_sock_ != INVALID_SOCKET);
6153 std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
6154 detail::shutdown_socket(sock);
6155 detail::close_socket(sock);
6157 is_decommisioned =
false;
6160inline void Server::decommission() { is_decommisioned =
true; }
6162inline bool Server::parse_request_line(
const char *s, Request &req)
const {
6163 auto len = strlen(s);
6164 if (
len < 2 || s[
len - 2] !=
'\r' || s[
len - 1] !=
'\n') {
6172 detail::split(s, s +
len,
' ', [&](
const char *b,
const char *e) {
6175 req.method = std::string(b, e);
6178 req.target = std::string(b, e);
6181 req.version = std::string(b, e);
6194 static const std::set<std::string> methods{
"GET",
"HEAD",
"POST",
"PUT",
"DELETE",
6195 "CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PRI"};
6197 if (methods.find(req.method) == methods.end()) {
6201 if (req.version !=
"HTTP/1.1" && req.version !=
"HTTP/1.0") {
6207 for (
size_t i = 0; i < req.target.size(); i++) {
6208 if (req.target[i] ==
'#') {
6209 req.target.erase(i);
6214 detail::divide(req.target,
'?',
6215 [&](
const char *lhs_data, std::size_t lhs_size,
const char *rhs_data, std::size_t rhs_size) {
6216 req.path = detail::decode_url(std::string(lhs_data, lhs_size), false);
6217 detail::parse_query_text(rhs_data, rhs_size, req.params);
6224inline bool Server::write_response(Stream &strm,
bool close_connection, Request &req, Response &res) {
6228 return write_response_core(strm, close_connection, req, res,
false);
6231inline bool Server::write_response_with_content(Stream &strm,
bool close_connection,
const Request &req,
6233 return write_response_core(strm, close_connection, req, res,
true);
6236inline bool Server::write_response_core(Stream &strm,
bool close_connection,
const Request &req, Response &res,
6237 bool need_apply_ranges) {
6238 assert(res.status != -1);
6240 if (400 <= res.status && error_handler_ && error_handler_(req, res) == HandlerResponse::Handled) {
6241 need_apply_ranges =
true;
6244 std::string content_type;
6245 std::string boundary;
6246 if (need_apply_ranges) {
6247 apply_ranges(req, res, content_type, boundary);
6251 if (close_connection || req.get_header_value(
"Connection") ==
"close") {
6252 res.set_header(
"Connection",
"close");
6254 std::string s =
"timeout=";
6255 s += std::to_string(keep_alive_timeout_sec_);
6257 s += std::to_string(keep_alive_max_count_);
6258 res.set_header(
"Keep-Alive", s);
6261 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) && !res.has_header(
"Content-Type")) {
6262 res.set_header(
"Content-Type",
"text/plain");
6265 if (res.body.empty() && !res.content_length_ && !res.content_provider_ && !res.has_header(
"Content-Length")) {
6266 res.set_header(
"Content-Length",
"0");
6269 if (req.method ==
"HEAD" && !res.has_header(
"Accept-Ranges")) {
6270 res.set_header(
"Accept-Ranges",
"bytes");
6273 if (post_routing_handler_) {
6274 post_routing_handler_(req, res);
6279 detail::BufferStream bstrm;
6280 if (!detail::write_response_line(bstrm, res.status)) {
6283 if (!header_writer_(bstrm, res.headers)) {
6288 auto &data = bstrm.get_buffer();
6289 detail::write_data(strm, data.data(), data.size());
6294 if (req.method !=
"HEAD") {
6295 if (!res.body.empty()) {
6296 if (!detail::write_data(strm, res.body.data(), res.body.size())) {
6299 }
else if (res.content_provider_) {
6300 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
6301 res.content_provider_success_ =
true;
6316inline bool Server::write_content_with_provider(Stream &strm,
const Request &req, Response &res,
6317 const std::string &boundary,
const std::string &content_type) {
6318 auto is_shutting_down = [
this]() {
return this->svr_sock_ == INVALID_SOCKET; };
6320 if (res.content_length_ > 0) {
6321 if (req.ranges.empty()) {
6322 return detail::write_content(strm, res.content_provider_, 0, res.content_length_, is_shutting_down);
6323 }
else if (req.ranges.size() == 1) {
6324 auto offset_and_length = detail::get_range_offset_and_length(req.ranges[0], res.content_length_);
6326 return detail::write_content(strm, res.content_provider_, offset_and_length.first, offset_and_length.second,
6329 return detail::write_multipart_ranges_data(strm, req, res, boundary, content_type, res.content_length_,
6333 if (res.is_chunked_content_provider_) {
6334 auto type = detail::encoding_type(req, res);
6336 std::unique_ptr<detail::compressor> compressor;
6337 if (
type == detail::EncodingType::Gzip) {
6338#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6339 compressor = detail::make_unique<detail::gzip_compressor>();
6341 }
else if (
type == detail::EncodingType::Brotli) {
6342#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6343 compressor = detail::make_unique<detail::brotli_compressor>();
6346 compressor = detail::make_unique<detail::nocompressor>();
6348 assert(compressor !=
nullptr);
6350 return detail::write_content_chunked(strm, res.content_provider_, is_shutting_down, *compressor);
6352 return detail::write_content_without_length(strm, res.content_provider_, is_shutting_down);
6357inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
6358 MultipartFormDataMap::iterator cur;
6359 auto file_count = 0;
6360 if (read_content_core(
6363 [&](
const char *buf,
size_t n) {
6364 if (req.body.size() + n > req.body.max_size()) {
6367 req.body.append(buf, n);
6371 [&](
const MultipartFormData &file) {
6372 if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
6375 cur = req.files.emplace(file.name, file);
6378 [&](
const char *buf,
size_t n) {
6379 auto &content = cur->second.content;
6380 if (content.size() + n > content.max_size()) {
6383 content.append(buf, n);
6386 const auto &content_type = req.get_header_value(
"Content-Type");
6387 if (!content_type.find(
"application/x-www-form-urlencoded")) {
6388 if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
6389 res.status = StatusCode::PayloadTooLarge_413;
6392 detail::parse_query_text(req.body, req.params);
6399inline bool Server::read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
6400 ContentReceiver receiver,
6401 MultipartContentHeader multipart_header,
6402 ContentReceiver multipart_receiver) {
6403 return read_content_core(strm, req, res, std::move(receiver), std::move(multipart_header),
6404 std::move(multipart_receiver));
6407inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, ContentReceiver receiver,
6408 MultipartContentHeader multipart_header,
6409 ContentReceiver multipart_receiver)
const {
6410 detail::MultipartFormDataParser multipart_form_data_parser;
6411 ContentReceiverWithProgress out;
6413 if (req.is_multipart_form_data()) {
6414 const auto &content_type = req.get_header_value(
"Content-Type");
6415 std::string boundary;
6416 if (!detail::parse_multipart_boundary(content_type, boundary)) {
6417 res.status = StatusCode::BadRequest_400;
6421 multipart_form_data_parser.set_boundary(std::move(boundary));
6422 out = [&](
const char *buf,
size_t n, uint64_t , uint64_t ) {
6434 return multipart_form_data_parser.parse(buf, n, multipart_receiver, multipart_header);
6437 out = [receiver](
const char *buf,
size_t n, uint64_t , uint64_t ) {
return receiver(buf, n); };
6440 if (req.method ==
"DELETE" && !req.has_header(
"Content-Length")) {
6444 if (!detail::read_content(strm, req, payload_max_length_, res.status,
nullptr, out,
true)) {
6448 if (req.is_multipart_form_data()) {
6449 if (!multipart_form_data_parser.is_valid()) {
6450 res.status = StatusCode::BadRequest_400;
6458inline bool Server::handle_file_request(
const Request &req, Response &res,
bool head) {
6459 for (
const auto &entry : base_dirs_) {
6461 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
6462 std::string sub_path =
"/" + req.path.substr(entry.mount_point.size());
6463 if (detail::is_valid_path(sub_path)) {
6464 auto path = entry.base_dir + sub_path;
6465 if (path.back() ==
'/') {
6466 path +=
"index.html";
6469 detail::FileStat stat(path);
6471 if (stat.is_dir()) {
6472 res.set_redirect(sub_path +
"/", StatusCode::MovedPermanently_301);
6476 if (stat.is_file()) {
6477 for (
const auto &kv : entry.headers) {
6478 res.set_header(kv.first, kv.second);
6481 auto mm = std::make_shared<detail::mmap>(path.c_str());
6482 if (!mm->is_open()) {
6486 res.set_content_provider(
6487 mm->size(), detail::find_content_type(path, file_extension_and_mimetype_map_, default_file_mimetype_),
6488 [mm](
size_t offset,
size_t length, DataSink &sink) ->
bool {
6489 sink.write(mm->data() + offset, length);
6493 if (!head && file_request_handler_) {
6494 file_request_handler_(req, res);
6505inline socket_t Server::create_server_socket(
const std::string &host,
int port,
int socket_flags,
6506 SocketOptions socket_options)
const {
6507 return detail::create_socket(host, std::string(), port, address_family_, socket_flags, tcp_nodelay_, ipv6_v6only_,
6508 std::move(socket_options),
6509 [](
socket_t sock,
struct addrinfo &ai,
bool & ) ->
bool {
6510 if (::bind(sock, ai.ai_addr,
static_cast<socklen_t>(ai.ai_addrlen))) {
6513 if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
6520inline int Server::bind_internal(
const std::string &host,
int port,
int socket_flags) {
6521 if (is_decommisioned) {
6529 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
6530 if (svr_sock_ == INVALID_SOCKET) {
6537 if (getsockname(svr_sock_,
reinterpret_cast<struct
sockaddr *
>(&addr), &addr_len) == -1) {
6540 if (addr.ss_family == AF_INET) {
6541 return ntohs(
reinterpret_cast<struct
sockaddr_in *
>(&addr)->sin_port);
6542 }
else if (addr.ss_family == AF_INET6) {
6543 return ntohs(
reinterpret_cast<struct
sockaddr_in6 *
>(&addr)->sin6_port);
6552inline bool Server::listen_internal() {
6553 if (is_decommisioned) {
6559 auto se = detail::scope_exit([&]() { is_running_ =
false; });
6562 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
6564 while (svr_sock_ != INVALID_SOCKET) {
6566 if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
6568 auto val = detail::select_read(svr_sock_, idle_interval_sec_, idle_interval_usec_);
6570 task_queue->on_idle();
6580 socket_t sock = WSAAccept(svr_sock_,
nullptr,
nullptr,
nullptr, 0);
6581#elif defined SOCK_CLOEXEC
6582 socket_t sock = accept4(svr_sock_,
nullptr,
nullptr, SOCK_CLOEXEC);
6584 socket_t sock = accept(svr_sock_,
nullptr,
nullptr);
6587 if (sock == INVALID_SOCKET) {
6588 if (errno == EMFILE) {
6591 std::this_thread::sleep_for(std::chrono::microseconds{1});
6593 }
else if (errno == EINTR || errno == EAGAIN) {
6596 if (svr_sock_ != INVALID_SOCKET) {
6597 detail::close_socket(svr_sock_);
6607 auto timeout =
static_cast<uint32_t
>(read_timeout_sec_ * 1000 + read_timeout_usec_ / 1000);
6608 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
6611 tv.tv_sec =
static_cast<long>(read_timeout_sec_);
6612 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(read_timeout_usec_);
6613 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
6618 auto timeout =
static_cast<uint32_t
>(write_timeout_sec_ * 1000 + write_timeout_usec_ / 1000);
6619 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
6622 tv.tv_sec =
static_cast<long>(write_timeout_sec_);
6623 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(write_timeout_usec_);
6624 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
6628 if (!task_queue->enqueue([
this, sock]() { process_and_close_socket(sock); })) {
6629 detail::shutdown_socket(sock);
6630 detail::close_socket(sock);
6634 task_queue->shutdown();
6637 is_decommisioned = !ret;
6641inline bool Server::routing(Request &req, Response &res, Stream &strm) {
6642 if (pre_routing_handler_ && pre_routing_handler_(req, res) == HandlerResponse::Handled) {
6647 auto is_head_request = req.method ==
"HEAD";
6648 if ((req.method ==
"GET" || is_head_request) && handle_file_request(req, res, is_head_request)) {
6652 if (detail::expect_content(req)) {
6655 ContentReader reader(
6656 [&](ContentReceiver receiver) {
6657 return read_content_with_content_receiver(strm, req, res, std::move(receiver),
nullptr,
nullptr);
6659 [&](MultipartContentHeader header, ContentReceiver receiver) {
6660 return read_content_with_content_receiver(strm, req, res,
nullptr, std::move(header), std::move(receiver));
6663 if (req.method ==
"POST") {
6664 if (dispatch_request_for_content_reader(req, res, std::move(reader), post_handlers_for_content_reader_)) {
6667 }
else if (req.method ==
"PUT") {
6668 if (dispatch_request_for_content_reader(req, res, std::move(reader), put_handlers_for_content_reader_)) {
6671 }
else if (req.method ==
"PATCH") {
6672 if (dispatch_request_for_content_reader(req, res, std::move(reader), patch_handlers_for_content_reader_)) {
6675 }
else if (req.method ==
"DELETE") {
6676 if (dispatch_request_for_content_reader(req, res, std::move(reader), delete_handlers_for_content_reader_)) {
6683 if (!read_content(strm, req, res)) {
6689 if (req.method ==
"GET" || req.method ==
"HEAD") {
6690 return dispatch_request(req, res, get_handlers_);
6691 }
else if (req.method ==
"POST") {
6692 return dispatch_request(req, res, post_handlers_);
6693 }
else if (req.method ==
"PUT") {
6694 return dispatch_request(req, res, put_handlers_);
6695 }
else if (req.method ==
"DELETE") {
6696 return dispatch_request(req, res, delete_handlers_);
6697 }
else if (req.method ==
"OPTIONS") {
6698 return dispatch_request(req, res, options_handlers_);
6699 }
else if (req.method ==
"PATCH") {
6700 return dispatch_request(req, res, patch_handlers_);
6703 res.status = StatusCode::BadRequest_400;
6707inline bool Server::dispatch_request(Request &req, Response &res,
const Handlers &handlers)
const {
6708 for (
const auto &
x : handlers) {
6709 const auto &matcher =
x.first;
6710 const auto &handler =
x.second;
6712 if (matcher->match(req)) {
6720inline void Server::apply_ranges(
const Request &req, Response &res, std::string &content_type,
6721 std::string &boundary)
const {
6722 if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
6723 auto it = res.headers.find(
"Content-Type");
6724 if (it != res.headers.end()) {
6725 content_type = it->second;
6726 res.headers.erase(it);
6729 boundary = detail::make_multipart_data_boundary();
6731 res.set_header(
"Content-Type",
"multipart/byteranges; boundary=" + boundary);
6734 auto type = detail::encoding_type(req, res);
6736 if (res.body.empty()) {
6737 if (res.content_length_ > 0) {
6739 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
6740 length = res.content_length_;
6741 }
else if (req.ranges.size() == 1) {
6742 auto offset_and_length = detail::get_range_offset_and_length(req.ranges[0], res.content_length_);
6744 length = offset_and_length.second;
6746 auto content_range = detail::make_content_range_header_field(offset_and_length, res.content_length_);
6747 res.set_header(
"Content-Range", content_range);
6749 length = detail::get_multipart_ranges_data_length(req, boundary, content_type, res.content_length_);
6751 res.set_header(
"Content-Length", std::to_string(
length));
6753 if (res.content_provider_) {
6754 if (res.is_chunked_content_provider_) {
6755 res.set_header(
"Transfer-Encoding",
"chunked");
6756 if (
type == detail::EncodingType::Gzip) {
6757 res.set_header(
"Content-Encoding",
"gzip");
6758 }
else if (
type == detail::EncodingType::Brotli) {
6759 res.set_header(
"Content-Encoding",
"br");
6765 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
6767 }
else if (req.ranges.size() == 1) {
6768 auto offset_and_length = detail::get_range_offset_and_length(req.ranges[0], res.body.size());
6769 auto offset = offset_and_length.first;
6770 auto length = offset_and_length.second;
6772 auto content_range = detail::make_content_range_header_field(offset_and_length, res.body.size());
6773 res.set_header(
"Content-Range", content_range);
6775 assert(offset +
length <= res.body.size());
6776 res.body = res.body.substr(offset,
length);
6779 detail::make_multipart_ranges_data(req, res, boundary, content_type, res.body.size(), data);
6780 res.body.swap(data);
6783 if (
type != detail::EncodingType::None) {
6784 std::unique_ptr<detail::compressor> compressor;
6785 std::string content_encoding;
6787 if (
type == detail::EncodingType::Gzip) {
6788#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6789 compressor = detail::make_unique<detail::gzip_compressor>();
6790 content_encoding =
"gzip";
6792 }
else if (
type == detail::EncodingType::Brotli) {
6793#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6794 compressor = detail::make_unique<detail::brotli_compressor>();
6795 content_encoding =
"br";
6800 std::string compressed;
6801 if (compressor->compress(res.body.data(), res.body.size(),
true, [&](
const char *data,
size_t data_len) {
6802 compressed.append(data, data_len);
6805 res.body.swap(compressed);
6806 res.set_header(
"Content-Encoding", content_encoding);
6811 auto length = std::to_string(res.body.size());
6812 res.set_header(
"Content-Length",
length);
6816inline bool Server::dispatch_request_for_content_reader(Request &req, Response &res, ContentReader content_reader,
6817 const HandlersForContentReader &handlers)
const {
6818 for (
const auto &
x : handlers) {
6819 const auto &matcher =
x.first;
6820 const auto &handler =
x.second;
6822 if (matcher->match(req)) {
6823 handler(req, res, content_reader);
6830inline bool Server::process_request(Stream &strm,
const std::string &remote_addr,
int remote_port,
6831 const std::string &local_addr,
int local_port,
bool close_connection,
6832 bool &connection_closed,
const std::function<
void(Request &)> &setup_request) {
6833 std::array<char, 2048> buf{};
6835 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
6838 if (!line_reader.getline()) {
6845 res.version =
"HTTP/1.1";
6846 res.headers = default_headers_;
6851#ifndef CPPHTTPLIB_USE_POLL
6853 if (strm.socket() >= FD_SETSIZE) {
6855 detail::read_headers(strm, dummy);
6856 res.status = StatusCode::InternalServerError_500;
6857 return write_response(strm, close_connection, req, res);
6863 if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
6865 detail::read_headers(strm, dummy);
6866 res.status = StatusCode::UriTooLong_414;
6867 return write_response(strm, close_connection, req, res);
6871 if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) {
6872 res.status = StatusCode::BadRequest_400;
6873 return write_response(strm, close_connection, req, res);
6876 if (req.get_header_value(
"Connection") ==
"close") {
6877 connection_closed =
true;
6880 if (req.version ==
"HTTP/1.0" && req.get_header_value(
"Connection") !=
"Keep-Alive") {
6881 connection_closed =
true;
6884 req.remote_addr = remote_addr;
6885 req.remote_port = remote_port;
6886 req.set_header(
"REMOTE_ADDR", req.remote_addr);
6887 req.set_header(
"REMOTE_PORT", std::to_string(req.remote_port));
6889 req.local_addr = local_addr;
6890 req.local_port = local_port;
6891 req.set_header(
"LOCAL_ADDR", req.local_addr);
6892 req.set_header(
"LOCAL_PORT", std::to_string(req.local_port));
6894 if (req.has_header(
"Range")) {
6895 const auto &range_header_value = req.get_header_value(
"Range");
6896 if (!detail::parse_range_header(range_header_value, req.ranges)) {
6897 res.status = StatusCode::RangeNotSatisfiable_416;
6898 return write_response(strm, close_connection, req, res);
6902 if (setup_request) {
6906 if (req.get_header_value(
"Expect") ==
"100-continue") {
6907 int status = StatusCode::Continue_100;
6908 if (expect_100_continue_handler_) {
6909 status = expect_100_continue_handler_(req, res);
6912 case StatusCode::Continue_100:
6913 case StatusCode::ExpectationFailed_417:
6914 detail::write_response_line(strm,
status);
6918 connection_closed =
true;
6919 return write_response(strm,
true, req, res);
6924 auto routed =
false;
6925#ifdef CPPHTTPLIB_NO_EXCEPTIONS
6926 routed = routing(req, res, strm);
6929 routed = routing(req, res, strm);
6930 }
catch (std::exception &e) {
6931 if (exception_handler_) {
6932 auto ep = std::current_exception();
6933 exception_handler_(req, res, ep);
6936 res.status = StatusCode::InternalServerError_500;
6939 for (
size_t i = 0; s[i]; i++) {
6952 res.set_header(
"EXCEPTION_WHAT",
val);
6955 if (exception_handler_) {
6956 auto ep = std::current_exception();
6957 exception_handler_(req, res, ep);
6960 res.status = StatusCode::InternalServerError_500;
6961 res.set_header(
"EXCEPTION_WHAT",
"UNKNOWN");
6966 if (res.status == -1) {
6967 res.status = req.ranges.empty() ? StatusCode::OK_200 : StatusCode::PartialContent_206;
6970 if (detail::range_error(req, res)) {
6972 res.content_length_ = 0;
6973 res.content_provider_ =
nullptr;
6974 res.status = StatusCode::RangeNotSatisfiable_416;
6975 return write_response(strm, close_connection, req, res);
6979 if (!res.file_content_path_.empty()) {
6980 const auto &path = res.file_content_path_;
6981 auto mm = std::make_shared<detail::mmap>(path.c_str());
6982 if (!mm->is_open()) {
6984 res.content_length_ = 0;
6985 res.content_provider_ =
nullptr;
6986 res.status = StatusCode::NotFound_404;
6987 return write_response(strm, close_connection, req, res);
6990 auto content_type = res.file_content_content_type_;
6991 if (content_type.empty()) {
6992 content_type = detail::find_content_type(path, file_extension_and_mimetype_map_, default_file_mimetype_);
6995 res.set_content_provider(mm->size(), content_type, [mm](
size_t offset,
size_t length, DataSink &sink) ->
bool {
6996 sink.write(mm->data() + offset, length);
7001 return write_response_with_content(strm, close_connection, req, res);
7003 if (res.status == -1) {
7004 res.status = StatusCode::NotFound_404;
7007 return write_response(strm, close_connection, req, res);
7011inline bool Server::is_valid()
const {
return true; }
7013inline bool Server::process_and_close_socket(
socket_t sock) {
7014 std::string remote_addr;
7015 int remote_port = 0;
7016 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
7018 std::string local_addr;
7020 detail::get_local_ip_and_port(sock, local_addr, local_port);
7022 auto ret = detail::process_server_socket(
7023 svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, read_timeout_usec_,
7024 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm,
bool close_connection,
bool &connection_closed) {
7025 return process_request(strm, remote_addr, remote_port, local_addr, local_port, close_connection,
7026 connection_closed,
nullptr);
7029 detail::shutdown_socket(sock);
7030 detail::close_socket(sock);
7035inline ClientImpl::ClientImpl(
const std::string &host) : ClientImpl(host, 80, std::string(), std::string()) {}
7037inline ClientImpl::ClientImpl(
const std::string &host,
int port)
7038 : ClientImpl(host, port, std::string(), std::string()) {}
7040inline ClientImpl::ClientImpl(
const std::string &host,
int port,
const std::string &client_cert_path,
7041 const std::string &client_key_path)
7042 : host_(detail::escape_abstract_namespace_unix_domain(host)),
7044 host_and_port_(adjust_host_string(host_) +
":" + std::to_string(port)),
7045 client_cert_path_(client_cert_path),
7046 client_key_path_(client_key_path) {}
7048inline ClientImpl::~ClientImpl() {
7049 std::lock_guard<std::mutex> guard(socket_mutex_);
7050 shutdown_socket(socket_);
7051 close_socket(socket_);
7054inline bool ClientImpl::is_valid()
const {
return true; }
7056inline void ClientImpl::copy_settings(
const ClientImpl &rhs) {
7057 client_cert_path_ = rhs.client_cert_path_;
7058 client_key_path_ = rhs.client_key_path_;
7059 connection_timeout_sec_ = rhs.connection_timeout_sec_;
7060 read_timeout_sec_ = rhs.read_timeout_sec_;
7061 read_timeout_usec_ = rhs.read_timeout_usec_;
7062 write_timeout_sec_ = rhs.write_timeout_sec_;
7063 write_timeout_usec_ = rhs.write_timeout_usec_;
7064 basic_auth_username_ = rhs.basic_auth_username_;
7065 basic_auth_password_ = rhs.basic_auth_password_;
7066 bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
7067#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7068 digest_auth_username_ = rhs.digest_auth_username_;
7069 digest_auth_password_ = rhs.digest_auth_password_;
7071 keep_alive_ = rhs.keep_alive_;
7072 follow_location_ = rhs.follow_location_;
7073 url_encode_ = rhs.url_encode_;
7074 address_family_ = rhs.address_family_;
7075 tcp_nodelay_ = rhs.tcp_nodelay_;
7076 ipv6_v6only_ = rhs.ipv6_v6only_;
7077 socket_options_ = rhs.socket_options_;
7078 compress_ = rhs.compress_;
7079 decompress_ = rhs.decompress_;
7080 interface_ = rhs.interface_;
7081 proxy_host_ = rhs.proxy_host_;
7082 proxy_port_ = rhs.proxy_port_;
7083 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
7084 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
7085 proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
7086#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7087 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
7088 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
7090#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7091 ca_cert_file_path_ = rhs.ca_cert_file_path_;
7092 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
7093 ca_cert_store_ = rhs.ca_cert_store_;
7095#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7096 server_certificate_verification_ = rhs.server_certificate_verification_;
7097 server_hostname_verification_ = rhs.server_hostname_verification_;
7098 server_certificate_verifier_ = rhs.server_certificate_verifier_;
7100 logger_ = rhs.logger_;
7103inline socket_t ClientImpl::create_client_socket(Error &error)
const {
7104 if (!proxy_host_.empty() && proxy_port_ != -1) {
7105 return detail::create_client_socket(proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
7106 ipv6_v6only_, socket_options_, connection_timeout_sec_,
7107 connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
7108 write_timeout_sec_, write_timeout_usec_, interface_, error);
7113 auto it = addr_map_.find(host_);
7114 if (it != addr_map_.end()) {
7118 return detail::create_client_socket(host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_, socket_options_,
7119 connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_,
7120 read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_, error);
7123inline bool ClientImpl::create_and_connect_socket(Socket &socket, Error &error) {
7124 auto sock = create_client_socket(error);
7125 if (sock == INVALID_SOCKET) {
7132inline void ClientImpl::shutdown_ssl(Socket & ,
bool ) {
7135 assert(socket_requests_in_flight_ == 0 || socket_requests_are_from_thread_ == std::this_thread::get_id());
7138inline void ClientImpl::shutdown_socket(Socket &socket)
const {
7139 if (
socket.sock == INVALID_SOCKET) {
7142 detail::shutdown_socket(
socket.sock);
7145inline void ClientImpl::close_socket(Socket &socket) {
7152 assert(socket_requests_in_flight_ == 0 || socket_requests_are_from_thread_ == std::this_thread::get_id());
7155#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7156 assert(
socket.ssl ==
nullptr);
7158 if (
socket.sock == INVALID_SOCKET) {
7161 detail::close_socket(
socket.sock);
7162 socket.sock = INVALID_SOCKET;
7165inline bool ClientImpl::read_response_line(Stream &strm,
const Request &req, Response &res)
const {
7166 std::array<char, 2048> buf{};
7168 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
7170 if (!line_reader.getline()) {
7174#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7175 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
7177 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
7181 if (!std::regex_match(line_reader.ptr(),
m, re)) {
7182 return req.method ==
"CONNECT";
7184 res.version = std::string(
m[1]);
7185 res.status = std::stoi(std::string(
m[2]));
7186 res.reason = std::string(
m[3]);
7189 while (res.status == StatusCode::Continue_100) {
7190 if (!line_reader.getline()) {
7193 if (!line_reader.getline()) {
7197 if (!std::regex_match(line_reader.ptr(),
m, re)) {
7200 res.version = std::string(
m[1]);
7201 res.status = std::stoi(std::string(
m[2]));
7202 res.reason = std::string(
m[3]);
7208inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
7209 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
7210 auto ret = send_(req, res, error);
7211 if (error == Error::SSLPeerCouldBeClosed_) {
7213 ret = send_(req, res, error);
7218#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7219inline bool ClientImpl::is_ssl_peer_could_be_closed(SSL *ssl)
const {
7221 return !SSL_peek(ssl, buf, 1) && SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
7225inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
7227 std::lock_guard<std::mutex> guard(socket_mutex_);
7231 socket_should_be_closed_when_request_is_done_ =
false;
7233 auto is_alive =
false;
7234 if (socket_.is_open()) {
7235 is_alive = detail::is_socket_alive(socket_.sock);
7237#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7238 if (is_alive && is_ssl()) {
7239 if (is_ssl_peer_could_be_closed(socket_.ssl)) {
7250 const bool shutdown_gracefully =
false;
7251 shutdown_ssl(socket_, shutdown_gracefully);
7252 shutdown_socket(socket_);
7253 close_socket(socket_);
7258 if (!create_and_connect_socket(socket_, error)) {
7262#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7265 auto &scli =
static_cast<SSLClient &
>(*this);
7266 if (!proxy_host_.empty() && proxy_port_ != -1) {
7267 auto success =
false;
7268 if (!scli.connect_with_proxy(socket_, res, success, error)) {
7273 if (!scli.initialize_ssl(socket_, error)) {
7283 if (socket_requests_in_flight_ > 1) {
7284 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
7286 socket_requests_in_flight_ += 1;
7287 socket_requests_are_from_thread_ = std::this_thread::get_id();
7290 for (
const auto &header : default_headers_) {
7291 if (req.headers.find(header.first) == req.headers.end()) {
7292 req.headers.insert(header);
7297 auto close_connection = !keep_alive_;
7299 auto se = detail::scope_exit([&]() {
7301 std::lock_guard<std::mutex> guard(socket_mutex_);
7302 socket_requests_in_flight_ -= 1;
7303 if (socket_requests_in_flight_ <= 0) {
7304 assert(socket_requests_in_flight_ == 0);
7305 socket_requests_are_from_thread_ = std::thread::id();
7308 if (socket_should_be_closed_when_request_is_done_ || close_connection || !ret) {
7309 shutdown_ssl(socket_,
true);
7310 shutdown_socket(socket_);
7311 close_socket(socket_);
7315 ret = process_socket(socket_, [&](Stream &strm) {
return handle_request(strm, req, res, close_connection, error); });
7318 if (error == Error::Success) {
7319 error = Error::Unknown;
7326inline Result ClientImpl::send(
const Request &req) {
7328 return send_(std::move(req2));
7331inline Result ClientImpl::send_(Request &&req) {
7332 auto res = detail::make_unique<Response>();
7333 auto error = Error::Success;
7334 auto ret = send(req, *res, error);
7335 return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
7338inline bool ClientImpl::handle_request(Stream &strm, Request &req, Response &res,
bool close_connection, Error &error) {
7339 if (req.path.empty()) {
7340 error = Error::Connection;
7344 auto req_save = req;
7348 if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
7350 req2.path =
"http://" + host_and_port_ + req.path;
7351 ret = process_request(strm, req2, res, close_connection, error);
7353 req.path = req_save.path;
7355 ret = process_request(strm, req, res, close_connection, error);
7362 if (res.get_header_value(
"Connection") ==
"close" ||
7363 (res.version ==
"HTTP/1.0" && res.reason !=
"Connection established")) {
7371 std::lock_guard<std::mutex> guard(socket_mutex_);
7372 shutdown_ssl(socket_,
true);
7373 shutdown_socket(socket_);
7374 close_socket(socket_);
7377 if (300 < res.status && res.status < 400 && follow_location_) {
7379 ret = redirect(req, res, error);
7382#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7383 if ((res.status == StatusCode::Unauthorized_401 || res.status == StatusCode::ProxyAuthenticationRequired_407) &&
7384 req.authorization_count_ < 5) {
7385 auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
7386 const auto &username = is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
7387 const auto &password = is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
7389 if (!username.empty() && !password.empty()) {
7390 std::map<std::string, std::string> auth;
7391 if (detail::parse_www_authenticate(res, auth, is_proxy)) {
7392 Request new_req = req;
7393 new_req.authorization_count_ += 1;
7394 new_req.headers.erase(is_proxy ?
"Proxy-Authorization" :
"Authorization");
7395 new_req.headers.insert(detail::make_digest_authentication_header(
7396 req, auth, new_req.authorization_count_, detail::random_string(10), username, password, is_proxy));
7400 ret = send(new_req, new_res, error);
7412inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
7413 if (req.redirect_count_ == 0) {
7414 error = Error::ExceedRedirectCount;
7418 auto location = res.get_header_value(
"location");
7419 if (location.empty()) {
7423 const static std::regex re(
7424 R
"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
7427 if (!std::regex_match(location,
m, re)) {
7431 auto scheme = is_ssl() ?
"https" :
"http";
7433 auto next_scheme =
m[1].str();
7434 auto next_host =
m[2].str();
7435 if (next_host.empty()) {
7436 next_host =
m[3].str();
7438 auto port_str =
m[4].str();
7439 auto next_path =
m[5].str();
7440 auto next_query =
m[6].str();
7442 auto next_port = port_;
7443 if (!port_str.empty()) {
7444 next_port = std::stoi(port_str);
7445 }
else if (!next_scheme.empty()) {
7446 next_port = next_scheme ==
"https" ? 443 : 80;
7449 if (next_scheme.empty()) {
7450 next_scheme = scheme;
7452 if (next_host.empty()) {
7455 if (next_path.empty()) {
7459 auto path = detail::decode_url(next_path,
true) + next_query;
7461 if (next_scheme == scheme && next_host == host_ && next_port == port_) {
7462 return detail::redirect(*
this, req, res, path, location, error);
7464 if (next_scheme ==
"https") {
7465#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7466 SSLClient cli(next_host, next_port);
7467 cli.copy_settings(*
this);
7468 if (ca_cert_store_) {
7469 cli.set_ca_cert_store(ca_cert_store_);
7471 return detail::redirect(cli, req, res, path, location, error);
7476 ClientImpl cli(next_host, next_port);
7477 cli.copy_settings(*
this);
7478 return detail::redirect(cli, req, res, path, location, error);
7483inline bool ClientImpl::write_content_with_provider(Stream &strm,
const Request &req, Error &error)
const {
7484 auto is_shutting_down = []() {
return false; };
7486 if (req.is_chunked_content_provider_) {
7488 std::unique_ptr<detail::compressor> compressor;
7489#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7491 compressor = detail::make_unique<detail::gzip_compressor>();
7495 compressor = detail::make_unique<detail::nocompressor>();
7498 return detail::write_content_chunked(strm, req.content_provider_, is_shutting_down, *compressor, error);
7500 return detail::write_content(strm, req.content_provider_, 0, req.content_length_, is_shutting_down, error);
7504inline bool ClientImpl::write_request(Stream &strm, Request &req,
bool close_connection, Error &error) {
7506 if (close_connection) {
7507 if (!req.has_header(
"Connection")) {
7508 req.set_header(
"Connection",
"close");
7512 if (!req.has_header(
"Host")) {
7515 req.set_header(
"Host", host_);
7517 req.set_header(
"Host", host_and_port_);
7521 req.set_header(
"Host", host_);
7523 req.set_header(
"Host", host_and_port_);
7528 if (!req.has_header(
"Accept")) {
7529 req.set_header(
"Accept",
"*/*");
7532 if (!req.content_receiver) {
7533 if (!req.has_header(
"Accept-Encoding")) {
7534 std::string accept_encoding;
7535#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7536 accept_encoding =
"br";
7538#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7539 if (!accept_encoding.empty()) {
7540 accept_encoding +=
", ";
7542 accept_encoding +=
"gzip, deflate";
7544 req.set_header(
"Accept-Encoding", accept_encoding);
7547#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
7548 if (!req.has_header(
"User-Agent")) {
7549 auto agent = std::string(
"cpp-httplib/") + CPPHTTPLIB_VERSION;
7550 req.set_header(
"User-Agent", agent);
7555 if (req.body.empty()) {
7556 if (req.content_provider_) {
7557 if (!req.is_chunked_content_provider_) {
7558 if (!req.has_header(
"Content-Length")) {
7559 auto length = std::to_string(req.content_length_);
7560 req.set_header(
"Content-Length",
length);
7564 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH") {
7565 req.set_header(
"Content-Length",
"0");
7569 if (!req.has_header(
"Content-Type")) {
7570 req.set_header(
"Content-Type",
"text/plain");
7573 if (!req.has_header(
"Content-Length")) {
7574 auto length = std::to_string(req.body.size());
7575 req.set_header(
"Content-Length",
length);
7579 if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
7580 if (!req.has_header(
"Authorization")) {
7581 req.headers.insert(make_basic_authentication_header(basic_auth_username_, basic_auth_password_,
false));
7585 if (!proxy_basic_auth_username_.empty() && !proxy_basic_auth_password_.empty()) {
7586 if (!req.has_header(
"Proxy-Authorization")) {
7588 make_basic_authentication_header(proxy_basic_auth_username_, proxy_basic_auth_password_,
true));
7592 if (!bearer_token_auth_token_.empty()) {
7593 if (!req.has_header(
"Authorization")) {
7594 req.headers.insert(make_bearer_token_authentication_header(bearer_token_auth_token_,
false));
7598 if (!proxy_bearer_token_auth_token_.empty()) {
7599 if (!req.has_header(
"Proxy-Authorization")) {
7600 req.headers.insert(make_bearer_token_authentication_header(proxy_bearer_token_auth_token_,
true));
7606 detail::BufferStream bstrm;
7608 const auto &path_with_query = req.params.empty() ? req.path : append_query_params(req.path, req.params);
7610 const auto &path = url_encode_ ? detail::encode_url(path_with_query) : path_with_query;
7612 detail::write_request_line(bstrm, req.method, path);
7614 header_writer_(bstrm, req.headers);
7617 auto &data = bstrm.get_buffer();
7618 if (!detail::write_data(strm, data.data(), data.size())) {
7619 error = Error::Write;
7625 if (req.body.empty()) {
7626 return write_content_with_provider(strm, req, error);
7629 if (!detail::write_data(strm, req.body.data(), req.body.size())) {
7630 error = Error::Write;
7637inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
7638 Request &req,
const char *body,
size_t content_length, ContentProvider content_provider,
7639 ContentProviderWithoutLength content_provider_without_length,
const std::string &content_type, Error &error) {
7640 if (!content_type.empty()) {
7641 req.set_header(
"Content-Type", content_type);
7644#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7646 req.set_header(
"Content-Encoding",
"gzip");
7650#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7651 if (compress_ && !content_provider_without_length) {
7653 detail::gzip_compressor compressor;
7655 if (content_provider) {
7660 data_sink.write = [&](
const char *data,
size_t data_len) ->
bool {
7662 auto last = offset + data_len == content_length;
7665 compressor.compress(data, data_len, last, [&](
const char *compressed_data,
size_t compressed_data_len) {
7666 req.body.append(compressed_data, compressed_data_len);
7679 while (ok && offset < content_length) {
7680 if (!content_provider(offset, content_length - offset, data_sink)) {
7681 error = Error::Canceled;
7686 if (!compressor.compress(body, content_length,
true, [&](
const char *data,
size_t data_len) {
7687 req.body.append(data, data_len);
7690 error = Error::Compression;
7697 if (content_provider) {
7698 req.content_length_ = content_length;
7699 req.content_provider_ = std::move(content_provider);
7700 req.is_chunked_content_provider_ =
false;
7701 }
else if (content_provider_without_length) {
7702 req.content_length_ = 0;
7703 req.content_provider_ = detail::ContentProviderAdapter(std::move(content_provider_without_length));
7704 req.is_chunked_content_provider_ =
true;
7705 req.set_header(
"Transfer-Encoding",
"chunked");
7707 req.body.assign(body, content_length);
7711 auto res = detail::make_unique<Response>();
7712 return send(req, *res, error) ? std::move(res) : nullptr;
7715inline Result ClientImpl::send_with_content_provider(
const std::string &method,
const std::string &path,
7716 const Headers &headers,
const char *body,
size_t content_length,
7717 ContentProvider content_provider,
7718 ContentProviderWithoutLength content_provider_without_length,
7719 const std::string &content_type, Progress progress) {
7721 req.method = method;
7722 req.headers = headers;
7724 req.progress = progress;
7726 auto error = Error::Success;
7728 auto res = send_with_content_provider(req, body, content_length, std::move(content_provider),
7729 std::move(content_provider_without_length), content_type, error);
7731 return Result{std::move(res), error, std::move(req.headers)};
7734inline std::string ClientImpl::adjust_host_string(
const std::string &host)
const {
7735 if (host.find(
':') != std::string::npos) {
7736 return "[" + host +
"]";
7741inline bool ClientImpl::process_request(Stream &strm, Request &req, Response &res,
bool close_connection,
7744 if (!write_request(strm, req, close_connection, error)) {
7748#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7750 auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
7751 if (!is_proxy_enabled) {
7752 if (is_ssl_peer_could_be_closed(socket_.ssl)) {
7753 error = Error::SSLPeerCouldBeClosed_;
7761 if (!read_response_line(strm, req, res) || !detail::read_headers(strm, res.headers)) {
7762 error = Error::Read;
7767 if ((res.status != StatusCode::NoContent_204) && req.method !=
"HEAD" && req.method !=
"CONNECT") {
7768 auto redirect = 300 < res.status && res.status < 400 && follow_location_;
7770 if (req.response_handler && !redirect) {
7771 if (!req.response_handler(res)) {
7772 error = Error::Canceled;
7778 req.content_receiver
7779 ?
static_cast<ContentReceiverWithProgress
>([&](
const char *buf,
size_t n, uint64_t off, uint64_t
len) {
7783 auto ret = req.content_receiver(buf, n, off,
len);
7785 error = Error::Canceled;
7789 : static_cast<ContentReceiverWithProgress>(
7790 [&](const char *buf, size_t n, uint64_t , uint64_t ) {
7791 assert(res.body.size() + n <= res.body.max_size());
7792 res.body.append(buf, n);
7796 auto progress = [&](uint64_t current, uint64_t total) {
7797 if (!req.progress || redirect) {
7800 auto ret = req.progress(current, total);
7802 error = Error::Canceled;
7807 if (res.has_header(
"Content-Length")) {
7808 if (!req.content_receiver) {
7809 auto len = res.get_header_value_u64(
"Content-Length");
7810 if (
len > res.body.max_size()) {
7811 error = Error::Read;
7814 res.body.reserve(
len);
7819 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(), dummy_status, std::move(progress),
7820 std::move(out), decompress_)) {
7821 if (error != Error::Canceled) {
7822 error = Error::Read;
7836inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
7837 const std::string &boundary,
const MultipartFormDataItems &items,
7838 const MultipartFormDataProviderItems &provider_items)
const {
7839 size_t cur_item = 0;
7840 size_t cur_start = 0;
7843 return [&, cur_item, cur_start](
size_t offset, DataSink &sink)
mutable ->
bool {
7844 if (!offset && !items.empty()) {
7845 sink.os << detail::serialize_multipart_formdata(items, boundary,
false);
7847 }
else if (cur_item < provider_items.size()) {
7849 const auto &begin = detail::serialize_multipart_formdata_item_begin(provider_items[cur_item], boundary);
7850 offset += begin.size();
7856 auto has_data =
true;
7857 cur_sink.write = sink.write;
7858 cur_sink.done = [&]() { has_data =
false; };
7860 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
7865 sink.os << detail::serialize_multipart_formdata_item_end();
7871 sink.os << detail::serialize_multipart_formdata_finish(boundary);
7878inline bool ClientImpl::process_socket(
const Socket &socket, std::function<
bool(Stream &strm)> callback) {
7879 return detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
7880 write_timeout_usec_, std::move(callback));
7883inline bool ClientImpl::is_ssl()
const {
return false; }
7885inline Result ClientImpl::Get(
const std::string &path) {
return Get(path, Headers(), Progress()); }
7887inline Result ClientImpl::Get(
const std::string &path, Progress progress) {
7888 return Get(path, Headers(), std::move(progress));
7891inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers) {
7892 return Get(path, headers, Progress());
7895inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, Progress progress) {
7899 req.headers = headers;
7900 req.progress = std::move(progress);
7902 return send_(std::move(req));
7905inline Result ClientImpl::Get(
const std::string &path, ContentReceiver content_receiver) {
7906 return Get(path, Headers(),
nullptr, std::move(content_receiver),
nullptr);
7909inline Result ClientImpl::Get(
const std::string &path, ContentReceiver content_receiver, Progress progress) {
7910 return Get(path, Headers(),
nullptr, std::move(content_receiver), std::move(progress));
7913inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver) {
7914 return Get(path, headers,
nullptr, std::move(content_receiver),
nullptr);
7917inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver,
7918 Progress progress) {
7919 return Get(path, headers,
nullptr, std::move(content_receiver), std::move(progress));
7922inline Result ClientImpl::Get(
const std::string &path, ResponseHandler response_handler,
7923 ContentReceiver content_receiver) {
7924 return Get(path, Headers(), std::move(response_handler), std::move(content_receiver),
nullptr);
7927inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
7928 ContentReceiver content_receiver) {
7929 return Get(path, headers, std::move(response_handler), std::move(content_receiver),
nullptr);
7932inline Result ClientImpl::Get(
const std::string &path, ResponseHandler response_handler,
7933 ContentReceiver content_receiver, Progress progress) {
7934 return Get(path, Headers(), std::move(response_handler), std::move(content_receiver), std::move(progress));
7937inline Result ClientImpl::Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
7938 ContentReceiver content_receiver, Progress progress) {
7942 req.headers = headers;
7943 req.response_handler = std::move(response_handler);
7944 req.content_receiver = [content_receiver](
const char *data,
size_t data_length, uint64_t ,
7945 uint64_t ) {
return content_receiver(data, data_length); };
7946 req.progress = std::move(progress);
7948 return send_(std::move(req));
7951inline Result ClientImpl::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
7952 Progress progress) {
7953 if (params.empty()) {
7954 return Get(path, headers);
7957 std::string path_with_query = append_query_params(path, params);
7958 return Get(path_with_query, headers, std::move(progress));
7961inline Result ClientImpl::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
7962 ContentReceiver content_receiver, Progress progress) {
7963 return Get(path, params, headers,
nullptr, std::move(content_receiver), std::move(progress));
7966inline Result ClientImpl::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
7967 ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) {
7968 if (params.empty()) {
7969 return Get(path, headers, std::move(response_handler), std::move(content_receiver), std::move(progress));
7972 std::string path_with_query = append_query_params(path, params);
7973 return Get(path_with_query, headers, std::move(response_handler), std::move(content_receiver), std::move(progress));
7976inline Result ClientImpl::Head(
const std::string &path) {
return Head(path, Headers()); }
7978inline Result ClientImpl::Head(
const std::string &path,
const Headers &headers) {
7980 req.method =
"HEAD";
7981 req.headers = headers;
7984 return send_(std::move(req));
7987inline Result ClientImpl::Post(
const std::string &path) {
return Post(path, std::string(), std::string()); }
7989inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers) {
7990 return Post(path, headers,
nullptr, 0, std::string());
7993inline Result ClientImpl::Post(
const std::string &path,
const char *body,
size_t content_length,
7994 const std::string &content_type) {
7995 return Post(path, Headers(), body, content_length, content_type,
nullptr);
7998inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
7999 const std::string &content_type) {
8000 return send_with_content_provider(
"POST", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8004inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
8005 const std::string &content_type, Progress progress) {
8006 return send_with_content_provider(
"POST", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8010inline Result ClientImpl::Post(
const std::string &path,
const std::string &body,
const std::string &content_type) {
8011 return Post(path, Headers(), body, content_type);
8014inline Result ClientImpl::Post(
const std::string &path,
const std::string &body,
const std::string &content_type,
8015 Progress progress) {
8016 return Post(path, Headers(), body, content_type, progress);
8019inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const std::string &body,
8020 const std::string &content_type) {
8021 return send_with_content_provider(
"POST", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8025inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const std::string &body,
8026 const std::string &content_type, Progress progress) {
8027 return send_with_content_provider(
"POST", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8031inline Result ClientImpl::Post(
const std::string &path,
const Params ¶ms) {
return Post(path, Headers(), params); }
8033inline Result ClientImpl::Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
8034 const std::string &content_type) {
8035 return Post(path, Headers(), content_length, std::move(content_provider), content_type);
8038inline Result ClientImpl::Post(
const std::string &path, ContentProviderWithoutLength content_provider,
8039 const std::string &content_type) {
8040 return Post(path, Headers(), std::move(content_provider), content_type);
8043inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
size_t content_length,
8044 ContentProvider content_provider,
const std::string &content_type) {
8045 return send_with_content_provider(
"POST", path, headers,
nullptr, content_length, std::move(content_provider),
8046 nullptr, content_type,
nullptr);
8049inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
8050 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
8051 return send_with_content_provider(
"POST", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
8052 content_type,
nullptr);
8055inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const Params ¶ms) {
8056 auto query = detail::params_to_query_str(params);
8057 return Post(path, headers, query,
"application/x-www-form-urlencoded");
8060inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const Params ¶ms,
8061 Progress progress) {
8062 auto query = detail::params_to_query_str(params);
8063 return Post(path, headers, query,
"application/x-www-form-urlencoded", progress);
8066inline Result ClientImpl::Post(
const std::string &path,
const MultipartFormDataItems &items) {
8067 return Post(path, Headers(), items);
8070inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items) {
8071 const auto &boundary = detail::make_multipart_data_boundary();
8072 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8073 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8074 return Post(path, headers, body, content_type);
8077inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
8078 const std::string &boundary) {
8079 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
8080 return Result{
nullptr, Error::UnsupportedMultipartBoundaryChars};
8083 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8084 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8085 return Post(path, headers, body, content_type);
8088inline Result ClientImpl::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
8089 const MultipartFormDataProviderItems &provider_items) {
8090 const auto &boundary = detail::make_multipart_data_boundary();
8091 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8092 return send_with_content_provider(
"POST", path, headers,
nullptr, 0,
nullptr,
8093 get_multipart_content_provider(boundary, items, provider_items), content_type,
8097inline Result ClientImpl::Put(
const std::string &path) {
return Put(path, std::string(), std::string()); }
8099inline Result ClientImpl::Put(
const std::string &path,
const char *body,
size_t content_length,
8100 const std::string &content_type) {
8101 return Put(path, Headers(), body, content_length, content_type);
8104inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
8105 const std::string &content_type) {
8106 return send_with_content_provider(
"PUT", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8110inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
8111 const std::string &content_type, Progress progress) {
8112 return send_with_content_provider(
"PUT", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8116inline Result ClientImpl::Put(
const std::string &path,
const std::string &body,
const std::string &content_type) {
8117 return Put(path, Headers(), body, content_type);
8120inline Result ClientImpl::Put(
const std::string &path,
const std::string &body,
const std::string &content_type,
8121 Progress progress) {
8122 return Put(path, Headers(), body, content_type, progress);
8125inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const std::string &body,
8126 const std::string &content_type) {
8127 return send_with_content_provider(
"PUT", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8131inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const std::string &body,
8132 const std::string &content_type, Progress progress) {
8133 return send_with_content_provider(
"PUT", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8137inline Result ClientImpl::Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
8138 const std::string &content_type) {
8139 return Put(path, Headers(), content_length, std::move(content_provider), content_type);
8142inline Result ClientImpl::Put(
const std::string &path, ContentProviderWithoutLength content_provider,
8143 const std::string &content_type) {
8144 return Put(path, Headers(), std::move(content_provider), content_type);
8147inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
size_t content_length,
8148 ContentProvider content_provider,
const std::string &content_type) {
8149 return send_with_content_provider(
"PUT", path, headers,
nullptr, content_length, std::move(content_provider),
nullptr,
8150 content_type,
nullptr);
8153inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
8154 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
8155 return send_with_content_provider(
"PUT", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
8156 content_type,
nullptr);
8159inline Result ClientImpl::Put(
const std::string &path,
const Params ¶ms) {
return Put(path, Headers(), params); }
8161inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const Params ¶ms) {
8162 auto query = detail::params_to_query_str(params);
8163 return Put(path, headers, query,
"application/x-www-form-urlencoded");
8166inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const Params ¶ms,
8167 Progress progress) {
8168 auto query = detail::params_to_query_str(params);
8169 return Put(path, headers, query,
"application/x-www-form-urlencoded", progress);
8172inline Result ClientImpl::Put(
const std::string &path,
const MultipartFormDataItems &items) {
8173 return Put(path, Headers(), items);
8176inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items) {
8177 const auto &boundary = detail::make_multipart_data_boundary();
8178 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8179 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8180 return Put(path, headers, body, content_type);
8183inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
8184 const std::string &boundary) {
8185 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
8186 return Result{
nullptr, Error::UnsupportedMultipartBoundaryChars};
8189 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8190 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8191 return Put(path, headers, body, content_type);
8194inline Result ClientImpl::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
8195 const MultipartFormDataProviderItems &provider_items) {
8196 const auto &boundary = detail::make_multipart_data_boundary();
8197 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8198 return send_with_content_provider(
"PUT", path, headers,
nullptr, 0,
nullptr,
8199 get_multipart_content_provider(boundary, items, provider_items), content_type,
8202inline Result ClientImpl::Patch(
const std::string &path) {
return Patch(path, std::string(), std::string()); }
8204inline Result ClientImpl::Patch(
const std::string &path,
const char *body,
size_t content_length,
8205 const std::string &content_type) {
8206 return Patch(path, Headers(), body, content_length, content_type);
8209inline Result ClientImpl::Patch(
const std::string &path,
const char *body,
size_t content_length,
8210 const std::string &content_type, Progress progress) {
8211 return Patch(path, Headers(), body, content_length, content_type, progress);
8214inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
const char *body,
8215 size_t content_length,
const std::string &content_type) {
8216 return Patch(path, headers, body, content_length, content_type,
nullptr);
8219inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
const char *body,
8220 size_t content_length,
const std::string &content_type, Progress progress) {
8221 return send_with_content_provider(
"PATCH", path, headers, body, content_length,
nullptr,
nullptr, content_type,
8225inline Result ClientImpl::Patch(
const std::string &path,
const std::string &body,
const std::string &content_type) {
8226 return Patch(path, Headers(), body, content_type);
8229inline Result ClientImpl::Patch(
const std::string &path,
const std::string &body,
const std::string &content_type,
8230 Progress progress) {
8231 return Patch(path, Headers(), body, content_type, progress);
8234inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
8235 const std::string &content_type) {
8236 return Patch(path, headers, body, content_type,
nullptr);
8239inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
8240 const std::string &content_type, Progress progress) {
8241 return send_with_content_provider(
"PATCH", path, headers, body.data(), body.size(),
nullptr,
nullptr, content_type,
8245inline Result ClientImpl::Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
8246 const std::string &content_type) {
8247 return Patch(path, Headers(), content_length, std::move(content_provider), content_type);
8250inline Result ClientImpl::Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
8251 const std::string &content_type) {
8252 return Patch(path, Headers(), std::move(content_provider), content_type);
8255inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
size_t content_length,
8256 ContentProvider content_provider,
const std::string &content_type) {
8257 return send_with_content_provider(
"PATCH", path, headers,
nullptr, content_length, std::move(content_provider),
8258 nullptr, content_type,
nullptr);
8261inline Result ClientImpl::Patch(
const std::string &path,
const Headers &headers,
8262 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
8263 return send_with_content_provider(
"PATCH", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
8264 content_type,
nullptr);
8267inline Result ClientImpl::Delete(
const std::string &path) {
8268 return Delete(path, Headers(), std::string(), std::string());
8271inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers) {
8272 return Delete(path, headers, std::string(), std::string());
8275inline Result ClientImpl::Delete(
const std::string &path,
const char *body,
size_t content_length,
8276 const std::string &content_type) {
8277 return Delete(path, Headers(), body, content_length, content_type);
8280inline Result ClientImpl::Delete(
const std::string &path,
const char *body,
size_t content_length,
8281 const std::string &content_type, Progress progress) {
8282 return Delete(path, Headers(), body, content_length, content_type, progress);
8285inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers,
const char *body,
8286 size_t content_length,
const std::string &content_type) {
8287 return Delete(path, headers, body, content_length, content_type,
nullptr);
8290inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers,
const char *body,
8291 size_t content_length,
const std::string &content_type, Progress progress) {
8293 req.method =
"DELETE";
8294 req.headers = headers;
8296 req.progress = progress;
8298 if (!content_type.empty()) {
8299 req.set_header(
"Content-Type", content_type);
8301 req.body.assign(body, content_length);
8303 return send_(std::move(req));
8306inline Result ClientImpl::Delete(
const std::string &path,
const std::string &body,
const std::string &content_type) {
8307 return Delete(path, Headers(), body.data(), body.size(), content_type);
8310inline Result ClientImpl::Delete(
const std::string &path,
const std::string &body,
const std::string &content_type,
8311 Progress progress) {
8312 return Delete(path, Headers(), body.data(), body.size(), content_type, progress);
8315inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
8316 const std::string &content_type) {
8317 return Delete(path, headers, body.data(), body.size(), content_type);
8320inline Result ClientImpl::Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
8321 const std::string &content_type, Progress progress) {
8322 return Delete(path, headers, body.data(), body.size(), content_type, progress);
8325inline Result ClientImpl::Options(
const std::string &path) {
return Options(path, Headers()); }
8327inline Result ClientImpl::Options(
const std::string &path,
const Headers &headers) {
8329 req.method =
"OPTIONS";
8330 req.headers = headers;
8333 return send_(std::move(req));
8336inline void ClientImpl::stop() {
8337 std::lock_guard<std::mutex> guard(socket_mutex_);
8344 if (socket_requests_in_flight_ > 0) {
8345 shutdown_socket(socket_);
8349 socket_should_be_closed_when_request_is_done_ =
true;
8354 shutdown_ssl(socket_,
true);
8355 shutdown_socket(socket_);
8356 close_socket(socket_);
8359inline std::string ClientImpl::host()
const {
return host_; }
8361inline int ClientImpl::port()
const {
return port_; }
8363inline size_t ClientImpl::is_socket_open()
const {
8364 std::lock_guard<std::mutex> guard(socket_mutex_);
8365 return socket_.is_open();
8368inline socket_t ClientImpl::socket()
const {
return socket_.sock; }
8370inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
8371 connection_timeout_sec_ = sec;
8372 connection_timeout_usec_ = usec;
8375inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
8376 read_timeout_sec_ = sec;
8377 read_timeout_usec_ = usec;
8380inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
8381 write_timeout_sec_ = sec;
8382 write_timeout_usec_ = usec;
8385inline void ClientImpl::set_basic_auth(
const std::string &username,
const std::string &password) {
8386 basic_auth_username_ = username;
8387 basic_auth_password_ = password;
8390inline void ClientImpl::set_bearer_token_auth(
const std::string &token) { bearer_token_auth_token_ = token; }
8392#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8393inline void ClientImpl::set_digest_auth(
const std::string &username,
const std::string &password) {
8394 digest_auth_username_ = username;
8395 digest_auth_password_ = password;
8399inline void ClientImpl::set_keep_alive(
bool on) { keep_alive_ = on; }
8401inline void ClientImpl::set_follow_location(
bool on) { follow_location_ = on; }
8403inline void ClientImpl::set_url_encode(
bool on) { url_encode_ = on; }
8405inline void ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
8406 addr_map_ = std::move(addr_map);
8409inline void ClientImpl::set_default_headers(Headers headers) { default_headers_ = std::move(headers); }
8411inline void ClientImpl::set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer) {
8412 header_writer_ = writer;
8415inline void ClientImpl::set_address_family(
int family) { address_family_ = family; }
8417inline void ClientImpl::set_tcp_nodelay(
bool on) { tcp_nodelay_ = on; }
8419inline void ClientImpl::set_ipv6_v6only(
bool on) { ipv6_v6only_ = on; }
8421inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
8422 socket_options_ = std::move(socket_options);
8425inline void ClientImpl::set_compress(
bool on) { compress_ = on; }
8427inline void ClientImpl::set_decompress(
bool on) { decompress_ = on; }
8429inline void ClientImpl::set_interface(
const std::string &intf) { interface_ = intf; }
8431inline void ClientImpl::set_proxy(
const std::string &host,
int port) {
8436inline void ClientImpl::set_proxy_basic_auth(
const std::string &username,
const std::string &password) {
8437 proxy_basic_auth_username_ = username;
8438 proxy_basic_auth_password_ = password;
8441inline void ClientImpl::set_proxy_bearer_token_auth(
const std::string &token) {
8442 proxy_bearer_token_auth_token_ = token;
8445#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8446inline void ClientImpl::set_proxy_digest_auth(
const std::string &username,
const std::string &password) {
8447 proxy_digest_auth_username_ = username;
8448 proxy_digest_auth_password_ = password;
8451inline void ClientImpl::set_ca_cert_path(
const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path) {
8452 ca_cert_file_path_ = ca_cert_file_path;
8453 ca_cert_dir_path_ = ca_cert_dir_path;
8456inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
8457 if (ca_cert_store && ca_cert_store != ca_cert_store_) {
8458 ca_cert_store_ = ca_cert_store;
8462inline X509_STORE *ClientImpl::create_ca_cert_store(
const char *ca_cert, std::size_t size)
const {
8463 auto mem = BIO_new_mem_buf(ca_cert,
static_cast<int>(size));
8464 auto se = detail::scope_exit([&] { BIO_free_all(mem); });
8469 auto inf = PEM_X509_INFO_read_bio(mem,
nullptr,
nullptr,
nullptr);
8474 auto cts = X509_STORE_new();
8476 for (
auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
8477 auto itmp = sk_X509_INFO_value(inf, i);
8483 X509_STORE_add_cert(cts, itmp->x509);
8486 X509_STORE_add_crl(cts, itmp->crl);
8491 sk_X509_INFO_pop_free(inf, X509_INFO_free);
8495inline void ClientImpl::enable_server_certificate_verification(
bool enabled) {
8496 server_certificate_verification_ = enabled;
8499inline void ClientImpl::enable_server_hostname_verification(
bool enabled) { server_hostname_verification_ = enabled; }
8501inline void ClientImpl::set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier) {
8502 server_certificate_verifier_ = verifier;
8506inline void ClientImpl::set_logger(Logger logger) { logger_ = std::move(logger); }
8511#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8514template<
typename U,
typename V>
8515inline SSL *ssl_new(
socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, U SSL_connect_or_accept, V
setup) {
8518 std::lock_guard<std::mutex> guard(ctx_mutex);
8523 set_nonblocking(sock,
true);
8524 auto bio = BIO_new_socket(
static_cast<int>(sock), BIO_NOCLOSE);
8525 BIO_set_nbio(bio, 1);
8526 SSL_set_bio(ssl, bio, bio);
8528 if (!
setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
8531 std::lock_guard<std::mutex> guard(ctx_mutex);
8534 set_nonblocking(sock,
false);
8537 BIO_set_nbio(bio, 0);
8538 set_nonblocking(sock,
false);
8544inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
socket_t sock,
bool shutdown_gracefully) {
8549 if (shutdown_gracefully) {
8556 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const void *
>(&tv),
sizeof(tv));
8558 auto ret = SSL_shutdown(ssl);
8560 std::this_thread::sleep_for(std::chrono::milliseconds{100});
8561 ret = SSL_shutdown(ssl);
8566 std::lock_guard<std::mutex> guard(ctx_mutex);
8571bool ssl_connect_or_accept_nonblocking(
socket_t sock, SSL *ssl, U ssl_connect_or_accept, time_t timeout_sec,
8572 time_t timeout_usec) {
8574 while ((res = ssl_connect_or_accept(ssl)) != 1) {
8575 auto err = SSL_get_error(ssl, res);
8577 case SSL_ERROR_WANT_READ:
8578 if (select_read(sock, timeout_sec, timeout_usec) > 0) {
8582 case SSL_ERROR_WANT_WRITE:
8583 if (select_write(sock, timeout_sec, timeout_usec) > 0) {
8596inline bool process_server_socket_ssl(
const std::atomic<socket_t> &svr_sock, SSL *ssl,
socket_t sock,
8597 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
8598 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
8599 time_t write_timeout_usec, T callback) {
8600 return process_server_socket_core(svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
8601 [&](
bool close_connection,
bool &connection_closed) {
8602 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
8603 write_timeout_sec, write_timeout_usec);
8604 return callback(strm, close_connection, connection_closed);
8609inline bool process_client_socket_ssl(SSL *ssl,
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
8610 time_t write_timeout_sec, time_t write_timeout_usec, T callback) {
8611 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);
8612 return callback(strm);
8617 SSLInit() { OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); }
8621inline SSLSocketStream::SSLSocketStream(
socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
8622 time_t write_timeout_sec, time_t write_timeout_usec)
8625 read_timeout_sec_(read_timeout_sec),
8626 read_timeout_usec_(read_timeout_usec),
8627 write_timeout_sec_(write_timeout_sec),
8628 write_timeout_usec_(write_timeout_usec) {
8629 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
8632inline SSLSocketStream::~SSLSocketStream() =
default;
8634inline bool SSLSocketStream::is_readable()
const {
8635 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
8638inline bool SSLSocketStream::is_writable()
const {
8639 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && is_socket_alive(sock_);
8642inline ssize_t SSLSocketStream::read(
char *ptr,
size_t size) {
8643 if (SSL_pending(ssl_) > 0) {
8644 return SSL_read(ssl_, ptr,
static_cast<int>(size));
8645 }
else if (is_readable()) {
8646 auto ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
8648 auto err = SSL_get_error(ssl_, ret);
8652 (err == SSL_ERROR_WANT_READ || (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT))) {
8654 while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
8656 if (SSL_pending(ssl_) > 0) {
8657 return SSL_read(ssl_, ptr,
static_cast<int>(size));
8658 }
else if (is_readable()) {
8659 std::this_thread::sleep_for(std::chrono::microseconds{10});
8660 ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
8664 err = SSL_get_error(ssl_, ret);
8675inline ssize_t SSLSocketStream::write(
const char *ptr,
size_t size) {
8676 if (is_writable()) {
8677 auto handle_size =
static_cast<int>(std::min<size_t>(size, (std::numeric_limits<int>::max)()));
8679 auto ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
8681 auto err = SSL_get_error(ssl_, ret);
8685 (err == SSL_ERROR_WANT_WRITE || (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT))) {
8687 while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
8689 if (is_writable()) {
8690 std::this_thread::sleep_for(std::chrono::microseconds{10});
8691 ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
8695 err = SSL_get_error(ssl_, ret);
8706inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
int &port)
const {
8707 detail::get_remote_ip_and_port(sock_, ip, port);
8710inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
int &port)
const {
8711 detail::get_local_ip_and_port(sock_, ip, port);
8714inline socket_t SSLSocketStream::socket()
const {
return sock_; }
8716static SSLInit sslinit_;
8721inline SSLServer::SSLServer(
const char *cert_path,
const char *private_key_path,
const char *client_ca_cert_file_path,
8722 const char *client_ca_cert_dir_path,
const char *private_key_password) {
8723 ctx_ = SSL_CTX_new(TLS_server_method());
8726 SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
8728 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8730 if (private_key_password !=
nullptr && (private_key_password[0] !=
'\0')) {
8731 SSL_CTX_set_default_passwd_cb_userdata(ctx_,
reinterpret_cast<void *
>(
const_cast<char *
>(private_key_password)));
8734 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
8735 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1 ||
8736 SSL_CTX_check_private_key(ctx_) != 1) {
8739 }
else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
8740 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, client_ca_cert_dir_path);
8742 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
8747inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store) {
8748 ctx_ = SSL_CTX_new(TLS_server_method());
8751 SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
8753 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8755 if (SSL_CTX_use_certificate(ctx_, cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
8758 }
else if (client_ca_cert_store) {
8759 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
8761 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
8766inline SSLServer::SSLServer(
const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
8767 ctx_ = SSL_CTX_new(TLS_method());
8769 if (!setup_ssl_ctx_callback(*ctx_)) {
8776inline SSLServer::~SSLServer() {
8782inline bool SSLServer::is_valid()
const {
return ctx_; }
8784inline SSL_CTX *SSLServer::ssl_context()
const {
return ctx_; }
8786inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store) {
8787 std::lock_guard<std::mutex> guard(ctx_mutex_);
8789 SSL_CTX_use_certificate(ctx_, cert);
8790 SSL_CTX_use_PrivateKey(ctx_, private_key);
8792 if (client_ca_cert_store !=
nullptr) {
8793 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
8797inline bool SSLServer::process_and_close_socket(
socket_t sock) {
8798 auto ssl = detail::ssl_new(
8799 sock, ctx_, ctx_mutex_,
8801 return detail::ssl_connect_or_accept_nonblocking(sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
8803 [](SSL * ) {
return true; });
8807 std::string remote_addr;
8808 int remote_port = 0;
8809 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
8811 std::string local_addr;
8813 detail::get_local_ip_and_port(sock, local_addr, local_port);
8815 ret = detail::process_server_socket_ssl(
8816 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, read_timeout_usec_,
8817 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm,
bool close_connection,
bool &connection_closed) {
8818 return process_request(strm, remote_addr, remote_port, local_addr, local_port, close_connection,
8819 connection_closed, [&](Request &req) { req.ssl = ssl; });
8824 const bool shutdown_gracefully = ret;
8825 detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
8828 detail::shutdown_socket(sock);
8829 detail::close_socket(sock);
8834inline SSLClient::SSLClient(
const std::string &host) : SSLClient(host, 443, std::string(), std::string()) {}
8836inline SSLClient::SSLClient(
const std::string &host,
int port) : SSLClient(host, port, std::string(), std::string()) {}
8838inline SSLClient::SSLClient(
const std::string &host,
int port,
const std::string &client_cert_path,
8839 const std::string &client_key_path,
const std::string &private_key_password)
8840 : ClientImpl(host, port, client_cert_path, client_key_path) {
8841 ctx_ = SSL_CTX_new(TLS_client_method());
8843 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8845 detail::split(&host_[0], &host_[host_.size()],
'.',
8846 [&](
const char *b,
const char *e) { host_components_.emplace_back(b, e); });
8848 if (!client_cert_path.empty() && !client_key_path.empty()) {
8849 if (!private_key_password.empty()) {
8850 SSL_CTX_set_default_passwd_cb_userdata(
8851 ctx_,
reinterpret_cast<void *
>(
const_cast<char *
>(private_key_password.c_str())));
8854 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), SSL_FILETYPE_PEM) != 1 ||
8855 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), SSL_FILETYPE_PEM) != 1) {
8862inline SSLClient::SSLClient(
const std::string &host,
int port, X509 *client_cert, EVP_PKEY *client_key,
8863 const std::string &private_key_password)
8864 : ClientImpl(host, port) {
8865 ctx_ = SSL_CTX_new(TLS_client_method());
8867 detail::split(&host_[0], &host_[host_.size()],
'.',
8868 [&](
const char *b,
const char *e) { host_components_.emplace_back(b, e); });
8870 if (client_cert !=
nullptr && client_key !=
nullptr) {
8871 if (!private_key_password.empty()) {
8872 SSL_CTX_set_default_passwd_cb_userdata(
8873 ctx_,
reinterpret_cast<void *
>(
const_cast<char *
>(private_key_password.c_str())));
8876 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
8883inline SSLClient::~SSLClient() {
8890 shutdown_ssl_impl(socket_,
true);
8893inline bool SSLClient::is_valid()
const {
return ctx_; }
8895inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
8896 if (ca_cert_store) {
8898 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
8900 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
8903 X509_STORE_free(ca_cert_store);
8908inline void SSLClient::load_ca_cert_store(
const char *ca_cert, std::size_t size) {
8909 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
8912inline long SSLClient::get_openssl_verify_result()
const {
return verify_result_; }
8914inline SSL_CTX *SSLClient::ssl_context()
const {
return ctx_; }
8916inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
8917 return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
8921inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
bool &success, Error &error) {
8924 if (!detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8925 write_timeout_usec_, [&](Stream &strm) {
8927 req2.method =
"CONNECT";
8928 req2.path = host_and_port_;
8929 return process_request(strm, req2, proxy_res, false, error);
8933 shutdown_ssl(socket,
true);
8934 shutdown_socket(socket);
8935 close_socket(socket);
8940 if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
8941 if (!proxy_digest_auth_username_.empty() && !proxy_digest_auth_password_.empty()) {
8942 std::map<std::string, std::string> auth;
8943 if (detail::parse_www_authenticate(proxy_res, auth,
true)) {
8944 proxy_res = Response();
8945 if (!detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8946 write_timeout_usec_, [&](Stream &strm) {
8948 req3.method =
"CONNECT";
8949 req3.path = host_and_port_;
8950 req3.headers.insert(detail::make_digest_authentication_header(
8951 req3, auth, 1, detail::random_string(10), proxy_digest_auth_username_,
8952 proxy_digest_auth_password_, true));
8953 return process_request(strm, req3, proxy_res, false, error);
8957 shutdown_ssl(socket,
true);
8958 shutdown_socket(socket);
8959 close_socket(socket);
8970 if (proxy_res.status != StatusCode::OK_200) {
8971 error = Error::ProxyConnection;
8972 res = std::move(proxy_res);
8975 shutdown_ssl(socket,
true);
8976 shutdown_socket(socket);
8977 close_socket(socket);
8984inline bool SSLClient::load_certs() {
8987 std::call_once(initialize_cert_, [&]() {
8988 std::lock_guard<std::mutex> guard(ctx_mutex_);
8989 if (!ca_cert_file_path_.empty()) {
8990 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
nullptr)) {
8993 }
else if (!ca_cert_dir_path_.empty()) {
8994 if (!SSL_CTX_load_verify_locations(ctx_,
nullptr, ca_cert_dir_path_.c_str())) {
8998 auto loaded =
false;
9000 loaded = detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
9001#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
9003 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
9007 SSL_CTX_set_default_verify_paths(ctx_);
9015inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
9016 auto ssl = detail::ssl_new(
9017 socket.sock, ctx_, ctx_mutex_,
9019 if (server_certificate_verification_) {
9020 if (!load_certs()) {
9021 error = Error::SSLLoadingCerts;
9024 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
9027 if (!detail::ssl_connect_or_accept_nonblocking(
socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
9028 connection_timeout_usec_)) {
9029 error = Error::SSLConnection;
9033 if (server_certificate_verification_) {
9034 if (server_certificate_verifier_) {
9035 if (!server_certificate_verifier_(ssl2)) {
9036 error = Error::SSLServerVerification;
9040 verify_result_ = SSL_get_verify_result(ssl2);
9042 if (verify_result_ != X509_V_OK) {
9043 error = Error::SSLServerVerification;
9047 auto server_cert = SSL_get1_peer_certificate(ssl2);
9048 auto se = detail::scope_exit([&] { X509_free(server_cert); });
9050 if (server_cert ==
nullptr) {
9051 error = Error::SSLServerVerification;
9055 if (server_hostname_verification_) {
9056 if (!verify_host(server_cert)) {
9057 error = Error::SSLServerHostnameVerification;
9067#if defined(OPENSSL_IS_BORINGSSL)
9068 SSL_set_tlsext_host_name(ssl2, host_.c_str());
9072 SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
9073 static_cast<void *
>(
const_cast<char *
>(host_.c_str())));
9083 shutdown_socket(socket);
9084 close_socket(socket);
9088inline void SSLClient::shutdown_ssl(Socket &socket,
bool shutdown_gracefully) {
9089 shutdown_ssl_impl(socket, shutdown_gracefully);
9092inline void SSLClient::shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully) {
9093 if (
socket.sock == INVALID_SOCKET) {
9094 assert(
socket.ssl ==
nullptr);
9098 detail::ssl_delete(ctx_mutex_,
socket.ssl,
socket.sock, shutdown_gracefully);
9101 assert(
socket.ssl ==
nullptr);
9104inline bool SSLClient::process_socket(
const Socket &socket, std::function<
bool(Stream &strm)> callback) {
9106 return detail::process_client_socket_ssl(
socket.ssl,
socket.sock, read_timeout_sec_, read_timeout_usec_,
9107 write_timeout_sec_, write_timeout_usec_, std::move(callback));
9110inline bool SSLClient::is_ssl()
const {
return true; }
9112inline bool SSLClient::verify_host(X509 *server_cert)
const {
9134 return verify_host_with_subject_alt_name(server_cert) || verify_host_with_common_name(server_cert);
9137inline bool SSLClient::verify_host_with_subject_alt_name(X509 *server_cert)
const {
9140 auto type = GEN_DNS;
9142 struct in6_addr addr6 {};
9143 struct in_addr addr {};
9144 size_t addr_len = 0;
9147 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
9149 addr_len =
sizeof(
struct in6_addr);
9150 }
else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
9152 addr_len =
sizeof(
struct in_addr);
9156 auto alt_names =
static_cast<const struct stack_st_GENERAL_NAME *
>(
9157 X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr,
nullptr));
9160 auto dsn_matched =
false;
9161 auto ip_matched =
false;
9163 auto count = sk_GENERAL_NAME_num(alt_names);
9165 for (
decltype(count) i = 0; i < count && !dsn_matched; i++) {
9166 auto val = sk_GENERAL_NAME_value(alt_names, i);
9168 auto name =
reinterpret_cast<const char *
>(ASN1_STRING_get0_data(
val->d.ia5));
9169 auto name_len =
static_cast<size_t>(ASN1_STRING_length(
val->d.ia5));
9173 dsn_matched = check_host_name(name, name_len);
9177 if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {
9185 if (dsn_matched || ip_matched) {
9190 GENERAL_NAMES_free(
const_cast<STACK_OF(GENERAL_NAME) *
>(
reinterpret_cast<const STACK_OF(GENERAL_NAME) *
>(alt_names)));
9194inline bool SSLClient::verify_host_with_common_name(X509 *server_cert)
const {
9195 const auto subject_name = X509_get_subject_name(server_cert);
9197 if (subject_name !=
nullptr) {
9199 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name,
sizeof(name));
9201 if (name_len != -1) {
9202 return check_host_name(name,
static_cast<size_t>(name_len));
9209inline bool SSLClient::check_host_name(
const char *pattern,
size_t pattern_len)
const {
9210 if (host_.size() == pattern_len && host_ == pattern) {
9216 std::vector<std::string> pattern_components;
9217 detail::split(&pattern[0], &pattern[pattern_len],
'.',
9218 [&](
const char *b,
const char *e) { pattern_components.emplace_back(b, e); });
9220 if (host_components_.size() != pattern_components.size()) {
9224 auto itr = pattern_components.begin();
9225 for (
const auto &
h : host_components_) {
9227 if (p !=
h && p !=
"*") {
9228 auto partial_match = (p.size() > 0 && p[p.size() - 1] ==
'*' && !p.compare(0, p.size() - 1,
h));
9229 if (!partial_match) {
9241inline Client::Client(
const std::string &scheme_host_port) : Client(scheme_host_port, std::string(), std::string()) {}
9243inline Client::Client(
const std::string &scheme_host_port,
const std::string &client_cert_path,
9244 const std::string &client_key_path) {
9245 const static std::regex re(R
"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
9248 if (std::regex_match(scheme_host_port,
m, re)) {
9249 auto scheme =
m[1].str();
9251#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9252 if (!scheme.empty() && (scheme !=
"http" && scheme !=
"https")) {
9254 if (!scheme.empty() && scheme !=
"http") {
9256#ifndef CPPHTTPLIB_NO_EXCEPTIONS
9257 std::string msg =
"'" + scheme +
"' scheme is not supported.";
9258 throw std::invalid_argument(msg);
9263 auto is_ssl = scheme ==
"https";
9265 auto host =
m[2].str();
9270 auto port_str =
m[4].str();
9271 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
9274#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9275 cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path, client_key_path);
9279 cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path, client_key_path);
9284 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80, client_cert_path, client_key_path);
9288inline Client::Client(
const std::string &host,
int port) : cli_(detail::make_unique<ClientImpl>(host, port)) {}
9290inline Client::Client(
const std::string &host,
int port,
const std::string &client_cert_path,
9291 const std::string &client_key_path)
9292 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path, client_key_path)) {}
9294inline Client::~Client() =
default;
9296inline bool Client::is_valid()
const {
return cli_ !=
nullptr && cli_->is_valid(); }
9298inline Result Client::Get(
const std::string &path) {
return cli_->Get(path); }
9299inline Result Client::Get(
const std::string &path,
const Headers &headers) {
return cli_->Get(path, headers); }
9300inline Result Client::Get(
const std::string &path, Progress progress) {
return cli_->Get(path, std::move(progress)); }
9301inline Result Client::Get(
const std::string &path,
const Headers &headers, Progress progress) {
9302 return cli_->Get(path, headers, std::move(progress));
9304inline Result Client::Get(
const std::string &path, ContentReceiver content_receiver) {
9305 return cli_->Get(path, std::move(content_receiver));
9307inline Result Client::Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver) {
9308 return cli_->Get(path, headers, std::move(content_receiver));
9310inline Result Client::Get(
const std::string &path, ContentReceiver content_receiver, Progress progress) {
9311 return cli_->Get(path, std::move(content_receiver), std::move(progress));
9313inline Result Client::Get(
const std::string &path,
const Headers &headers, ContentReceiver content_receiver,
9314 Progress progress) {
9315 return cli_->Get(path, headers, std::move(content_receiver), std::move(progress));
9317inline Result Client::Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver) {
9318 return cli_->Get(path, std::move(response_handler), std::move(content_receiver));
9320inline Result Client::Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
9321 ContentReceiver content_receiver) {
9322 return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver));
9324inline Result Client::Get(
const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
9325 Progress progress) {
9326 return cli_->Get(path, std::move(response_handler), std::move(content_receiver), std::move(progress));
9328inline Result Client::Get(
const std::string &path,
const Headers &headers, ResponseHandler response_handler,
9329 ContentReceiver content_receiver, Progress progress) {
9330 return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver), std::move(progress));
9332inline Result Client::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers, Progress progress) {
9333 return cli_->Get(path, params, headers, std::move(progress));
9335inline Result Client::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
9336 ContentReceiver content_receiver, Progress progress) {
9337 return cli_->Get(path, params, headers, std::move(content_receiver), std::move(progress));
9339inline Result Client::Get(
const std::string &path,
const Params ¶ms,
const Headers &headers,
9340 ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) {
9341 return cli_->Get(path, params, headers, std::move(response_handler), std::move(content_receiver),
9342 std::move(progress));
9345inline Result Client::Head(
const std::string &path) {
return cli_->Head(path); }
9346inline Result Client::Head(
const std::string &path,
const Headers &headers) {
return cli_->Head(path, headers); }
9348inline Result Client::Post(
const std::string &path) {
return cli_->Post(path); }
9349inline Result Client::Post(
const std::string &path,
const Headers &headers) {
return cli_->Post(path, headers); }
9350inline Result Client::Post(
const std::string &path,
const char *body,
size_t content_length,
9351 const std::string &content_type) {
9352 return cli_->Post(path, body, content_length, content_type);
9354inline Result Client::Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9355 const std::string &content_type) {
9356 return cli_->Post(path, headers, body, content_length, content_type);
9358inline Result Client::Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9359 const std::string &content_type, Progress progress) {
9360 return cli_->Post(path, headers, body, content_length, content_type, progress);
9362inline Result Client::Post(
const std::string &path,
const std::string &body,
const std::string &content_type) {
9363 return cli_->Post(path, body, content_type);
9365inline Result Client::Post(
const std::string &path,
const std::string &body,
const std::string &content_type,
9366 Progress progress) {
9367 return cli_->Post(path, body, content_type, progress);
9369inline Result Client::Post(
const std::string &path,
const Headers &headers,
const std::string &body,
9370 const std::string &content_type) {
9371 return cli_->Post(path, headers, body, content_type);
9373inline Result Client::Post(
const std::string &path,
const Headers &headers,
const std::string &body,
9374 const std::string &content_type, Progress progress) {
9375 return cli_->Post(path, headers, body, content_type, progress);
9377inline Result Client::Post(
const std::string &path,
size_t content_length, ContentProvider content_provider,
9378 const std::string &content_type) {
9379 return cli_->Post(path, content_length, std::move(content_provider), content_type);
9381inline Result Client::Post(
const std::string &path, ContentProviderWithoutLength content_provider,
9382 const std::string &content_type) {
9383 return cli_->Post(path, std::move(content_provider), content_type);
9385inline Result Client::Post(
const std::string &path,
const Headers &headers,
size_t content_length,
9386 ContentProvider content_provider,
const std::string &content_type) {
9387 return cli_->Post(path, headers, content_length, std::move(content_provider), content_type);
9389inline Result Client::Post(
const std::string &path,
const Headers &headers,
9390 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
9391 return cli_->Post(path, headers, std::move(content_provider), content_type);
9393inline Result Client::Post(
const std::string &path,
const Params ¶ms) {
return cli_->Post(path, params); }
9394inline Result Client::Post(
const std::string &path,
const Headers &headers,
const Params ¶ms) {
9395 return cli_->Post(path, headers, params);
9397inline Result Client::Post(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress) {
9398 return cli_->Post(path, headers, params, progress);
9400inline Result Client::Post(
const std::string &path,
const MultipartFormDataItems &items) {
9401 return cli_->Post(path, items);
9403inline Result Client::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items) {
9404 return cli_->Post(path, headers, items);
9406inline Result Client::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
9407 const std::string &boundary) {
9408 return cli_->Post(path, headers, items, boundary);
9410inline Result Client::Post(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
9411 const MultipartFormDataProviderItems &provider_items) {
9412 return cli_->Post(path, headers, items, provider_items);
9414inline Result Client::Put(
const std::string &path) {
return cli_->Put(path); }
9415inline Result Client::Put(
const std::string &path,
const char *body,
size_t content_length,
9416 const std::string &content_type) {
9417 return cli_->Put(path, body, content_length, content_type);
9419inline Result Client::Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9420 const std::string &content_type) {
9421 return cli_->Put(path, headers, body, content_length, content_type);
9423inline Result Client::Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9424 const std::string &content_type, Progress progress) {
9425 return cli_->Put(path, headers, body, content_length, content_type, progress);
9427inline Result Client::Put(
const std::string &path,
const std::string &body,
const std::string &content_type) {
9428 return cli_->Put(path, body, content_type);
9430inline Result Client::Put(
const std::string &path,
const std::string &body,
const std::string &content_type,
9431 Progress progress) {
9432 return cli_->Put(path, body, content_type, progress);
9434inline Result Client::Put(
const std::string &path,
const Headers &headers,
const std::string &body,
9435 const std::string &content_type) {
9436 return cli_->Put(path, headers, body, content_type);
9438inline Result Client::Put(
const std::string &path,
const Headers &headers,
const std::string &body,
9439 const std::string &content_type, Progress progress) {
9440 return cli_->Put(path, headers, body, content_type, progress);
9442inline Result Client::Put(
const std::string &path,
size_t content_length, ContentProvider content_provider,
9443 const std::string &content_type) {
9444 return cli_->Put(path, content_length, std::move(content_provider), content_type);
9446inline Result Client::Put(
const std::string &path, ContentProviderWithoutLength content_provider,
9447 const std::string &content_type) {
9448 return cli_->Put(path, std::move(content_provider), content_type);
9450inline Result Client::Put(
const std::string &path,
const Headers &headers,
size_t content_length,
9451 ContentProvider content_provider,
const std::string &content_type) {
9452 return cli_->Put(path, headers, content_length, std::move(content_provider), content_type);
9454inline Result Client::Put(
const std::string &path,
const Headers &headers,
9455 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
9456 return cli_->Put(path, headers, std::move(content_provider), content_type);
9458inline Result Client::Put(
const std::string &path,
const Params ¶ms) {
return cli_->Put(path, params); }
9459inline Result Client::Put(
const std::string &path,
const Headers &headers,
const Params ¶ms) {
9460 return cli_->Put(path, headers, params);
9462inline Result Client::Put(
const std::string &path,
const Headers &headers,
const Params ¶ms, Progress progress) {
9463 return cli_->Put(path, headers, params, progress);
9465inline Result Client::Put(
const std::string &path,
const MultipartFormDataItems &items) {
9466 return cli_->Put(path, items);
9468inline Result Client::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items) {
9469 return cli_->Put(path, headers, items);
9471inline Result Client::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
9472 const std::string &boundary) {
9473 return cli_->Put(path, headers, items, boundary);
9475inline Result Client::Put(
const std::string &path,
const Headers &headers,
const MultipartFormDataItems &items,
9476 const MultipartFormDataProviderItems &provider_items) {
9477 return cli_->Put(path, headers, items, provider_items);
9479inline Result Client::Patch(
const std::string &path) {
return cli_->Patch(path); }
9480inline Result Client::Patch(
const std::string &path,
const char *body,
size_t content_length,
9481 const std::string &content_type) {
9482 return cli_->Patch(path, body, content_length, content_type);
9484inline Result Client::Patch(
const std::string &path,
const char *body,
size_t content_length,
9485 const std::string &content_type, Progress progress) {
9486 return cli_->Patch(path, body, content_length, content_type, progress);
9488inline Result Client::Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9489 const std::string &content_type) {
9490 return cli_->Patch(path, headers, body, content_length, content_type);
9492inline Result Client::Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9493 const std::string &content_type, Progress progress) {
9494 return cli_->Patch(path, headers, body, content_length, content_type, progress);
9496inline Result Client::Patch(
const std::string &path,
const std::string &body,
const std::string &content_type) {
9497 return cli_->Patch(path, body, content_type);
9499inline Result Client::Patch(
const std::string &path,
const std::string &body,
const std::string &content_type,
9500 Progress progress) {
9501 return cli_->Patch(path, body, content_type, progress);
9503inline Result Client::Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
9504 const std::string &content_type) {
9505 return cli_->Patch(path, headers, body, content_type);
9507inline Result Client::Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
9508 const std::string &content_type, Progress progress) {
9509 return cli_->Patch(path, headers, body, content_type, progress);
9511inline Result Client::Patch(
const std::string &path,
size_t content_length, ContentProvider content_provider,
9512 const std::string &content_type) {
9513 return cli_->Patch(path, content_length, std::move(content_provider), content_type);
9515inline Result Client::Patch(
const std::string &path, ContentProviderWithoutLength content_provider,
9516 const std::string &content_type) {
9517 return cli_->Patch(path, std::move(content_provider), content_type);
9519inline Result Client::Patch(
const std::string &path,
const Headers &headers,
size_t content_length,
9520 ContentProvider content_provider,
const std::string &content_type) {
9521 return cli_->Patch(path, headers, content_length, std::move(content_provider), content_type);
9523inline Result Client::Patch(
const std::string &path,
const Headers &headers,
9524 ContentProviderWithoutLength content_provider,
const std::string &content_type) {
9525 return cli_->Patch(path, headers, std::move(content_provider), content_type);
9527inline Result Client::Delete(
const std::string &path) {
return cli_->Delete(path); }
9528inline Result Client::Delete(
const std::string &path,
const Headers &headers) {
return cli_->Delete(path, headers); }
9529inline Result Client::Delete(
const std::string &path,
const char *body,
size_t content_length,
9530 const std::string &content_type) {
9531 return cli_->Delete(path, body, content_length, content_type);
9533inline Result Client::Delete(
const std::string &path,
const char *body,
size_t content_length,
9534 const std::string &content_type, Progress progress) {
9535 return cli_->Delete(path, body, content_length, content_type, progress);
9537inline Result Client::Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9538 const std::string &content_type) {
9539 return cli_->Delete(path, headers, body, content_length, content_type);
9541inline Result Client::Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
9542 const std::string &content_type, Progress progress) {
9543 return cli_->Delete(path, headers, body, content_length, content_type, progress);
9545inline Result Client::Delete(
const std::string &path,
const std::string &body,
const std::string &content_type) {
9546 return cli_->Delete(path, body, content_type);
9548inline Result Client::Delete(
const std::string &path,
const std::string &body,
const std::string &content_type,
9549 Progress progress) {
9550 return cli_->Delete(path, body, content_type, progress);
9552inline Result Client::Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
9553 const std::string &content_type) {
9554 return cli_->Delete(path, headers, body, content_type);
9556inline Result Client::Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
9557 const std::string &content_type, Progress progress) {
9558 return cli_->Delete(path, headers, body, content_type, progress);
9560inline Result Client::Options(
const std::string &path) {
return cli_->Options(path); }
9561inline Result Client::Options(
const std::string &path,
const Headers &headers) {
return cli_->Options(path, headers); }
9563inline bool Client::send(Request &req, Response &res, Error &error) {
return cli_->send(req, res, error); }
9565inline Result Client::send(
const Request &req) {
return cli_->send(req); }
9567inline void Client::stop() { cli_->stop(); }
9569inline std::string Client::host()
const {
return cli_->host(); }
9571inline int Client::port()
const {
return cli_->port(); }
9573inline size_t Client::is_socket_open()
const {
return cli_->is_socket_open(); }
9575inline socket_t Client::socket()
const {
return cli_->socket(); }
9577inline void Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
9578 cli_->set_hostname_addr_map(std::move(addr_map));
9581inline void Client::set_default_headers(Headers headers) { cli_->set_default_headers(std::move(headers)); }
9583inline void Client::set_header_writer(std::function<
ssize_t(Stream &, Headers &)>
const &writer) {
9584 cli_->set_header_writer(writer);
9587inline void Client::set_address_family(
int family) { cli_->set_address_family(family); }
9589inline void Client::set_tcp_nodelay(
bool on) { cli_->set_tcp_nodelay(on); }
9591inline void Client::set_socket_options(SocketOptions socket_options) {
9592 cli_->set_socket_options(std::move(socket_options));
9595inline void Client::set_connection_timeout(time_t sec, time_t usec) { cli_->set_connection_timeout(sec, usec); }
9597inline void Client::set_read_timeout(time_t sec, time_t usec) { cli_->set_read_timeout(sec, usec); }
9599inline void Client::set_write_timeout(time_t sec, time_t usec) { cli_->set_write_timeout(sec, usec); }
9601inline void Client::set_basic_auth(
const std::string &username,
const std::string &password) {
9602 cli_->set_basic_auth(username, password);
9604inline void Client::set_bearer_token_auth(
const std::string &token) { cli_->set_bearer_token_auth(token); }
9605#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9606inline void Client::set_digest_auth(
const std::string &username,
const std::string &password) {
9607 cli_->set_digest_auth(username, password);
9611inline void Client::set_keep_alive(
bool on) { cli_->set_keep_alive(on); }
9612inline void Client::set_follow_location(
bool on) { cli_->set_follow_location(on); }
9614inline void Client::set_url_encode(
bool on) { cli_->set_url_encode(on); }
9616inline void Client::set_compress(
bool on) { cli_->set_compress(on); }
9618inline void Client::set_decompress(
bool on) { cli_->set_decompress(on); }
9620inline void Client::set_interface(
const std::string &intf) { cli_->set_interface(intf); }
9622inline void Client::set_proxy(
const std::string &host,
int port) { cli_->set_proxy(host, port); }
9623inline void Client::set_proxy_basic_auth(
const std::string &username,
const std::string &password) {
9624 cli_->set_proxy_basic_auth(username, password);
9626inline void Client::set_proxy_bearer_token_auth(
const std::string &token) { cli_->set_proxy_bearer_token_auth(token); }
9627#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9628inline void Client::set_proxy_digest_auth(
const std::string &username,
const std::string &password) {
9629 cli_->set_proxy_digest_auth(username, password);
9633#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9634inline void Client::enable_server_certificate_verification(
bool enabled) {
9635 cli_->enable_server_certificate_verification(enabled);
9638inline void Client::enable_server_hostname_verification(
bool enabled) {
9639 cli_->enable_server_hostname_verification(enabled);
9642inline void Client::set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier) {
9643 cli_->set_server_certificate_verifier(verifier);
9647inline void Client::set_logger(Logger logger) { cli_->set_logger(std::move(logger)); }
9649#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9650inline void Client::set_ca_cert_path(
const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path) {
9651 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
9654inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
9656 static_cast<SSLClient &
>(*cli_).set_ca_cert_store(ca_cert_store);
9658 cli_->set_ca_cert_store(ca_cert_store);
9662inline void Client::load_ca_cert_store(
const char *ca_cert, std::size_t size) {
9663 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
9666inline long Client::get_openssl_verify_result()
const {
9668 return static_cast<SSLClient &
>(*cli_).get_openssl_verify_result();
9673inline SSL_CTX *Client::ssl_context()
const {
9675 return static_cast<SSLClient &
>(*cli_).ssl_context();
9685#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)
bool operator==(const DeferredMessage &test) const
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.