8 #ifndef CPPHTTPLIB_HTTPLIB_H
9 #define CPPHTTPLIB_HTTPLIB_H
15 #ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
16 #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
19 #ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
20 #define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
23 #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
24 #define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
27 #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
28 #define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
31 #ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
32 #define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
35 #ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
36 #define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
39 #ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND
40 #define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5
43 #ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND
44 #define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0
47 #ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
48 #define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
51 #ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
53 #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
55 #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
59 #ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
60 #define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
63 #ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
64 #define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
67 #ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
68 #define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
71 #ifndef CPPHTTPLIB_TCP_NODELAY
72 #define CPPHTTPLIB_TCP_NODELAY false
75 #ifndef CPPHTTPLIB_RECV_BUFSIZ
76 #define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
79 #ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
80 #define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
83 #ifndef CPPHTTPLIB_THREAD_POOL_COUNT
84 #define CPPHTTPLIB_THREAD_POOL_COUNT \
85 ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
86 ? std::thread::hardware_concurrency() - 1 \
90 #ifndef CPPHTTPLIB_RECV_FLAGS
91 #define CPPHTTPLIB_RECV_FLAGS 0
94 #ifndef CPPHTTPLIB_SEND_FLAGS
95 #define CPPHTTPLIB_SEND_FLAGS 0
103 #ifndef _CRT_SECURE_NO_WARNINGS
104 #define _CRT_SECURE_NO_WARNINGS
107 #ifndef _CRT_NONSTDC_NO_DEPRECATE
108 #define _CRT_NONSTDC_NO_DEPRECATE
111 #if defined(_MSC_VER)
119 #define snprintf _snprintf_s
124 #define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
128 #define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
136 #include <winsock2.h>
138 #include <wincrypt.h>
139 #include <ws2tcpip.h>
141 #ifndef WSA_FLAG_NO_HANDLE_INHERIT
142 #define WSA_FLAG_NO_HANDLE_INHERIT 0x80
146 #pragma comment(lib, "ws2_32.lib")
147 #pragma comment(lib, "crypt32.lib")
148 #pragma comment(lib, "cryptui.lib")
152 #define strcasecmp _stricmp
156 #ifdef CPPHTTPLIB_USE_POLL
157 #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
162 #include <arpa/inet.h>
166 #include <netinet/in.h>
170 #include <netinet/tcp.h>
171 #ifdef CPPHTTPLIB_USE_POLL
176 #include <sys/select.h>
177 #include <sys/socket.h>
181 #ifndef INVALID_SOCKET
182 #define INVALID_SOCKET (-1)
192 #include <condition_variable>
196 #include <functional>
208 #include <sys/stat.h>
211 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
212 #include <openssl/err.h>
213 #include <openssl/md5.h>
214 #include <openssl/ssl.h>
215 #include <openssl/x509v3.h>
217 #if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
218 #include <openssl/applink.c>
224 #if OPENSSL_VERSION_NUMBER < 0x1010100fL
225 #error Sorry, OpenSSL versions prior to 1.1.1 are not supported
228 #if OPENSSL_VERSION_NUMBER < 0x10100000L
229 #include <openssl/crypto.h>
231 return M_ASN1_STRING_data(asn1);
236 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
240 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
241 #include <brotli/decode.h>
242 #include <brotli/encode.h>
260 template <
class T,
class... Args>
262 make_unique(Args &&...args) {
263 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
268 make_unique(std::size_t
n) {
269 typedef typename std::remove_extent<T>::type RT;
270 return std::unique_ptr<T>(
new RT[
n]);
274 bool operator()(
const std::string &s1,
const std::string &s2)
const {
275 return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(),
277 [](
unsigned char c1,
unsigned char c2) {
278 return ::tolower(c1) < ::tolower(c2);
285 using Headers = std::multimap<std::string, std::string, detail::ci>;
287 using Params = std::multimap<std::string, std::string>;
288 using Match = std::smatch;
290 using Progress = std::function<bool(uint64_t current, uint64_t total)>;
293 using ResponseHandler = std::function<bool(
const Response &response)>;
295 struct MultipartFormData {
298 std::string filename;
299 std::string content_type;
301 using MultipartFormDataItems = std::vector<MultipartFormData>;
302 using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
306 DataSink() : os(&sb_), sb_(*this) {}
308 DataSink(
const DataSink &) =
delete;
309 DataSink &operator=(
const DataSink &) =
delete;
310 DataSink(DataSink &&) =
delete;
311 DataSink &operator=(DataSink &&) =
delete;
313 std::function<bool(
const char *data,
size_t data_len)> write;
314 std::function<void()> done;
315 std::function<bool()> is_writable;
319 class data_sink_streambuf :
public std::streambuf {
321 explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
324 std::streamsize xsputn(
const char *s, std::streamsize
n) {
325 sink_.write(s,
static_cast<size_t>(
n));
333 data_sink_streambuf sb_;
336 using ContentProvider =
337 std::function<bool(
size_t offset,
size_t length, DataSink &sink)>;
339 using ContentProviderWithoutLength =
340 std::function<bool(
size_t offset, DataSink &sink)>;
342 using ContentProviderResourceReleaser = std::function<void(
bool success)>;
344 using ContentReceiverWithProgress =
345 std::function<bool(
const char *data,
size_t data_length, uint64_t offset,
346 uint64_t total_length)>;
348 using ContentReceiver =
349 std::function<bool(
const char *data,
size_t data_length)>;
351 using MultipartContentHeader =
352 std::function<bool(
const MultipartFormData &file)>;
354 class ContentReader {
356 using Reader = std::function<bool(ContentReceiver receiver)>;
357 using MultipartReader = std::function<bool(MultipartContentHeader header,
358 ContentReceiver receiver)>;
360 ContentReader(Reader reader, MultipartReader multipart_reader)
361 : reader_(std::move(reader)),
362 multipart_reader_(std::move(multipart_reader)) {}
364 bool operator()(MultipartContentHeader header,
365 ContentReceiver receiver)
const {
366 return multipart_reader_(std::move(header), std::move(receiver));
369 bool operator()(ContentReceiver receiver)
const {
370 return reader_(std::move(receiver));
374 MultipartReader multipart_reader_;
377 using Range = std::pair<ssize_t, ssize_t>;
378 using Ranges = std::vector<Range>;
386 std::string remote_addr;
387 int remote_port = -1;
393 MultipartFormDataMap files;
398 ResponseHandler response_handler;
399 ContentReceiverWithProgress content_receiver;
401 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
402 const SSL *ssl =
nullptr;
405 bool has_header(
const char *key)
const;
406 std::string get_header_value(
const char *key,
size_t id = 0)
const;
407 template <
typename T>
408 T get_header_value(
const char *key,
size_t id = 0)
const;
409 size_t get_header_value_count(
const char *key)
const;
410 void set_header(
const char *key,
const char *val);
411 void set_header(
const char *key,
const std::string &val);
413 bool has_param(
const char *key)
const;
414 std::string get_param_value(
const char *key,
size_t id = 0)
const;
415 size_t get_param_value_count(
const char *key)
const;
417 bool is_multipart_form_data()
const;
419 bool has_file(
const char *key)
const;
420 MultipartFormData get_file_value(
const char *key)
const;
423 size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
424 size_t content_length_ = 0;
425 ContentProvider content_provider_;
426 bool is_chunked_content_provider_ =
false;
427 size_t authorization_count_ = 0;
436 std::string location;
438 bool has_header(
const char *key)
const;
439 std::string get_header_value(
const char *key,
size_t id = 0)
const;
440 template <
typename T>
441 T get_header_value(
const char *key,
size_t id = 0)
const;
442 size_t get_header_value_count(
const char *key)
const;
443 void set_header(
const char *key,
const char *val);
444 void set_header(
const char *key,
const std::string &val);
446 void set_redirect(
const char *url,
int status = 302);
447 void set_redirect(
const std::string &url,
int status = 302);
448 void set_content(
const char *s,
size_t n,
const char *content_type);
449 void set_content(
const std::string &s,
const char *content_type);
451 void set_content_provider(
452 size_t length,
const char *content_type, ContentProvider provider,
453 ContentProviderResourceReleaser resource_releaser =
nullptr);
455 void set_content_provider(
456 const char *content_type, ContentProviderWithoutLength provider,
457 ContentProviderResourceReleaser resource_releaser =
nullptr);
459 void set_chunked_content_provider(
460 const char *content_type, ContentProviderWithoutLength provider,
461 ContentProviderResourceReleaser resource_releaser =
nullptr);
463 Response() =
default;
464 Response(
const Response &) =
default;
465 Response &operator=(
const Response &) =
default;
466 Response(Response &&) =
default;
467 Response &operator=(Response &&) =
default;
469 if (content_provider_resource_releaser_) {
470 content_provider_resource_releaser_(content_provider_success_);
475 size_t content_length_ = 0;
476 ContentProvider content_provider_;
477 ContentProviderResourceReleaser content_provider_resource_releaser_;
478 bool is_chunked_content_provider_ =
false;
479 bool content_provider_success_ =
false;
484 virtual ~Stream() =
default;
486 virtual bool is_readable()
const = 0;
487 virtual bool is_writable()
const = 0;
489 virtual ssize_t read(
char *ptr,
size_t size) = 0;
490 virtual ssize_t write(
const char *ptr,
size_t size) = 0;
491 virtual void get_remote_ip_and_port(std::string &ip,
int &port)
const = 0;
492 virtual socket_t socket()
const = 0;
494 template <
typename... Args>
495 ssize_t write_format(
const char *fmt,
const Args &...args);
496 ssize_t write(
const char *ptr);
497 ssize_t write(
const std::string &s);
502 TaskQueue() =
default;
503 virtual ~TaskQueue() =
default;
505 virtual void enqueue(std::function<
void()> fn) = 0;
506 virtual void shutdown() = 0;
508 virtual void on_idle() {}
511 class ThreadPool :
public TaskQueue {
513 explicit ThreadPool(
size_t n) : shutdown_(false) {
515 threads_.emplace_back(worker(*
this));
520 ThreadPool(
const ThreadPool &) =
delete;
521 ~ThreadPool()
override =
default;
523 void enqueue(std::function<
void()> fn)
override {
524 std::unique_lock<std::mutex> lock(mutex_);
525 jobs_.push_back(std::move(fn));
529 void shutdown()
override {
532 std::unique_lock<std::mutex> lock(mutex_);
539 for (
auto &t : threads_) {
546 explicit worker(ThreadPool &pool) : pool_(pool) {}
550 std::function<void()> fn;
552 std::unique_lock<std::mutex> lock(pool_.mutex_);
555 lock, [&] {
return !pool_.jobs_.empty() || pool_.shutdown_; });
557 if (pool_.shutdown_ && pool_.jobs_.empty()) {
break; }
559 fn = pool_.jobs_.front();
560 pool_.jobs_.pop_front();
563 assert(
true ==
static_cast<bool>(fn));
570 friend struct worker;
572 std::vector<std::thread> threads_;
573 std::list<std::function<void()>> jobs_;
577 std::condition_variable cond_;
581 using Logger = std::function<void(
const Request &,
const Response &)>;
583 using SocketOptions = std::function<void(
socket_t sock)>;
585 void default_socket_options(
socket_t sock);
589 using Handler = std::function<void(
const Request &, Response &)>;
591 using ExceptionHandler =
592 std::function<void(
const Request &, Response &, std::exception &
e)>;
594 enum class HandlerResponse {
598 using HandlerWithResponse =
599 std::function<HandlerResponse(
const Request &, Response &)>;
601 using HandlerWithContentReader = std::function<void(
602 const Request &, Response &,
const ContentReader &content_reader)>;
604 using Expect100ContinueHandler =
605 std::function<int(
const Request &, Response &)>;
611 virtual bool is_valid()
const;
613 Server &Get(
const std::string &pattern, Handler handler);
614 Server &Post(
const std::string &pattern, Handler handler);
615 Server &Post(
const std::string &pattern, HandlerWithContentReader handler);
616 Server &Put(
const std::string &pattern, Handler handler);
617 Server &Put(
const std::string &pattern, HandlerWithContentReader handler);
618 Server &Patch(
const std::string &pattern, Handler handler);
619 Server &Patch(
const std::string &pattern, HandlerWithContentReader handler);
620 Server &Delete(
const std::string &pattern, Handler handler);
621 Server &Delete(
const std::string &pattern, HandlerWithContentReader handler);
622 Server &Options(
const std::string &pattern, Handler handler);
624 bool set_base_dir(
const std::string &dir,
625 const std::string &mount_point = std::string());
626 bool set_mount_point(
const std::string &mount_point,
const std::string &dir,
627 Headers headers = Headers());
628 bool remove_mount_point(
const std::string &mount_point);
629 Server &set_file_extension_and_mimetype_mapping(
const char *ext,
631 Server &set_file_request_handler(Handler handler);
633 Server &set_error_handler(HandlerWithResponse handler);
634 Server &set_error_handler(Handler handler);
635 Server &set_exception_handler(ExceptionHandler handler);
636 Server &set_pre_routing_handler(HandlerWithResponse handler);
637 Server &set_post_routing_handler(Handler handler);
639 Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
640 Server &set_logger(Logger logger);
642 Server &set_address_family(
int family);
643 Server &set_tcp_nodelay(
bool on);
644 Server &set_socket_options(SocketOptions socket_options);
646 Server &set_default_headers(Headers headers);
648 Server &set_keep_alive_max_count(
size_t count);
649 Server &set_keep_alive_timeout(time_t sec);
651 Server &set_read_timeout(time_t sec, time_t usec = 0);
652 template <
class Rep,
class Period>
653 Server &set_read_timeout(
const std::chrono::duration<Rep, Period> &duration);
655 Server &set_write_timeout(time_t sec, time_t usec = 0);
656 template <
class Rep,
class Period>
657 Server &set_write_timeout(
const std::chrono::duration<Rep, Period> &duration);
659 Server &set_idle_interval(time_t sec, time_t usec = 0);
660 template <
class Rep,
class Period>
661 Server &set_idle_interval(
const std::chrono::duration<Rep, Period> &duration);
663 Server &set_payload_max_length(
size_t length);
665 bool bind_to_port(
const char *host,
int port,
int socket_flags = 0);
666 int bind_to_any_port(
const char *host,
int socket_flags = 0);
667 bool listen_after_bind();
669 bool listen(
const char *host,
int port,
int socket_flags = 0);
671 bool is_running()
const;
674 std::function<TaskQueue *(void)> new_task_queue;
677 bool process_request(Stream &strm,
bool close_connection,
678 bool &connection_closed,
679 const std::function<
void(Request &)> &setup_request);
681 std::atomic<socket_t> svr_sock_;
682 size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
683 time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
684 time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
685 time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
686 time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
687 time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
688 time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
689 time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
690 size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
693 using Handlers = std::vector<std::pair<std::regex, Handler>>;
694 using HandlersForContentReader =
695 std::vector<std::pair<std::regex, HandlerWithContentReader>>;
697 socket_t create_server_socket(
const char *host,
int port,
int socket_flags,
698 SocketOptions socket_options)
const;
699 int bind_internal(
const char *host,
int port,
int socket_flags);
700 bool listen_internal();
702 bool routing(Request &req, Response &res, Stream &strm);
703 bool handle_file_request(
const Request &req, Response &res,
705 bool dispatch_request(Request &req, Response &res,
const Handlers &handlers);
707 dispatch_request_for_content_reader(Request &req, Response &res,
708 ContentReader content_reader,
709 const HandlersForContentReader &handlers);
711 bool parse_request_line(
const char *s, Request &req);
712 void apply_ranges(
const Request &req, Response &res,
713 std::string &content_type, std::string &boundary);
714 bool write_response(Stream &strm,
bool close_connection,
const Request &req,
716 bool write_response_with_content(Stream &strm,
bool close_connection,
717 const Request &req, Response &res);
718 bool write_response_core(Stream &strm,
bool close_connection,
719 const Request &req, Response &res,
720 bool need_apply_ranges);
721 bool write_content_with_provider(Stream &strm,
const Request &req,
722 Response &res,
const std::string &boundary,
723 const std::string &content_type);
724 bool read_content(Stream &strm, Request &req, Response &res);
726 read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
727 ContentReceiver receiver,
728 MultipartContentHeader multipart_header,
729 ContentReceiver multipart_receiver);
730 bool read_content_core(Stream &strm, Request &req, Response &res,
731 ContentReceiver receiver,
732 MultipartContentHeader mulitpart_header,
733 ContentReceiver multipart_receiver);
735 virtual bool process_and_close_socket(
socket_t sock);
737 struct MountPointEntry {
738 std::string mount_point;
739 std::string base_dir;
742 std::vector<MountPointEntry> base_dirs_;
744 std::atomic<bool> is_running_;
745 std::map<std::string, std::string> file_extension_and_mimetype_map_;
746 Handler file_request_handler_;
747 Handlers get_handlers_;
748 Handlers post_handlers_;
749 HandlersForContentReader post_handlers_for_content_reader_;
750 Handlers put_handlers_;
751 HandlersForContentReader put_handlers_for_content_reader_;
752 Handlers patch_handlers_;
753 HandlersForContentReader patch_handlers_for_content_reader_;
754 Handlers delete_handlers_;
755 HandlersForContentReader delete_handlers_for_content_reader_;
756 Handlers options_handlers_;
757 HandlerWithResponse error_handler_;
758 ExceptionHandler exception_handler_;
759 HandlerWithResponse pre_routing_handler_;
760 Handler post_routing_handler_;
762 Expect100ContinueHandler expect_100_continue_handler_;
764 int address_family_ = AF_UNSPEC;
765 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
766 SocketOptions socket_options_ = default_socket_options;
768 Headers default_headers_;
782 SSLServerVerification,
783 UnsupportedMultipartBoundaryChars,
787 std::string
to_string(
const Error error);
789 std::ostream &operator<<(std::ostream &os,
const Error &obj);
793 Result(std::unique_ptr<Response> &&res, Error err,
794 Headers &&request_headers = Headers{})
795 : res_(std::move(res)), err_(err),
796 request_headers_(std::move(request_headers)) {}
798 operator bool()
const {
return res_ !=
nullptr; }
799 bool operator==(std::nullptr_t)
const {
return res_ ==
nullptr; }
800 bool operator!=(std::nullptr_t)
const {
return res_ !=
nullptr; }
801 const Response &
value()
const {
return *res_; }
802 Response &
value() {
return *res_; }
803 const Response &
operator*()
const {
return *res_; }
805 const Response *operator->()
const {
return res_.get(); }
806 Response *operator->() {
return res_.get(); }
809 Error
error()
const {
return err_; }
812 bool has_request_header(
const char *key)
const;
813 std::string get_request_header_value(
const char *key,
size_t id = 0)
const;
814 template <
typename T>
815 T get_request_header_value(
const char *key,
size_t id = 0)
const;
816 size_t get_request_header_value_count(
const char *key)
const;
819 std::unique_ptr<Response> res_;
821 Headers request_headers_;
826 explicit ClientImpl(
const std::string &host);
828 explicit ClientImpl(
const std::string &host,
int port);
830 explicit ClientImpl(
const std::string &host,
int port,
831 const std::string &client_cert_path,
832 const std::string &client_key_path);
834 virtual ~ClientImpl();
836 virtual bool is_valid()
const;
838 Result Get(
const char *path);
839 Result Get(
const char *path,
const Headers &headers);
840 Result Get(
const char *path, Progress progress);
841 Result Get(
const char *path,
const Headers &headers, Progress progress);
842 Result Get(
const char *path, ContentReceiver content_receiver);
843 Result Get(
const char *path,
const Headers &headers,
844 ContentReceiver content_receiver);
845 Result Get(
const char *path, ContentReceiver content_receiver,
847 Result Get(
const char *path,
const Headers &headers,
848 ContentReceiver content_receiver, Progress progress);
849 Result Get(
const char *path, ResponseHandler response_handler,
850 ContentReceiver content_receiver);
851 Result Get(
const char *path,
const Headers &headers,
852 ResponseHandler response_handler,
853 ContentReceiver content_receiver);
854 Result Get(
const char *path, ResponseHandler response_handler,
855 ContentReceiver content_receiver, Progress progress);
856 Result Get(
const char *path,
const Headers &headers,
857 ResponseHandler response_handler, ContentReceiver content_receiver,
860 Result Get(
const char *path,
const Params ¶ms,
const Headers &headers,
861 Progress progress =
nullptr);
862 Result Get(
const char *path,
const Params ¶ms,
const Headers &headers,
863 ContentReceiver content_receiver, Progress progress =
nullptr);
864 Result Get(
const char *path,
const Params ¶ms,
const Headers &headers,
865 ResponseHandler response_handler, ContentReceiver content_receiver,
866 Progress progress =
nullptr);
868 Result Head(
const char *path);
869 Result Head(
const char *path,
const Headers &headers);
871 Result Post(
const char *path);
872 Result Post(
const char *path,
const char *body,
size_t content_length,
873 const char *content_type);
874 Result Post(
const char *path,
const Headers &headers,
const char *body,
875 size_t content_length,
const char *content_type);
876 Result Post(
const char *path,
const std::string &body,
877 const char *content_type);
878 Result Post(
const char *path,
const Headers &headers,
const std::string &body,
879 const char *content_type);
880 Result Post(
const char *path,
size_t content_length,
881 ContentProvider content_provider,
const char *content_type);
882 Result Post(
const char *path, ContentProviderWithoutLength content_provider,
883 const char *content_type);
884 Result Post(
const char *path,
const Headers &headers,
size_t content_length,
885 ContentProvider content_provider,
const char *content_type);
886 Result Post(
const char *path,
const Headers &headers,
887 ContentProviderWithoutLength content_provider,
888 const char *content_type);
889 Result Post(
const char *path,
const Params ¶ms);
890 Result Post(
const char *path,
const Headers &headers,
const Params ¶ms);
891 Result Post(
const char *path,
const MultipartFormDataItems &items);
892 Result Post(
const char *path,
const Headers &headers,
893 const MultipartFormDataItems &items);
894 Result Post(
const char *path,
const Headers &headers,
895 const MultipartFormDataItems &items,
const std::string &boundary);
897 Result Put(
const char *path);
898 Result Put(
const char *path,
const char *body,
size_t content_length,
899 const char *content_type);
900 Result Put(
const char *path,
const Headers &headers,
const char *body,
901 size_t content_length,
const char *content_type);
902 Result Put(
const char *path,
const std::string &body,
903 const char *content_type);
904 Result Put(
const char *path,
const Headers &headers,
const std::string &body,
905 const char *content_type);
906 Result Put(
const char *path,
size_t content_length,
907 ContentProvider content_provider,
const char *content_type);
908 Result Put(
const char *path, ContentProviderWithoutLength content_provider,
909 const char *content_type);
910 Result Put(
const char *path,
const Headers &headers,
size_t content_length,
911 ContentProvider content_provider,
const char *content_type);
912 Result Put(
const char *path,
const Headers &headers,
913 ContentProviderWithoutLength content_provider,
914 const char *content_type);
915 Result Put(
const char *path,
const Params ¶ms);
916 Result Put(
const char *path,
const Headers &headers,
const Params ¶ms);
918 Result Patch(
const char *path);
919 Result Patch(
const char *path,
const char *body,
size_t content_length,
920 const char *content_type);
921 Result Patch(
const char *path,
const Headers &headers,
const char *body,
922 size_t content_length,
const char *content_type);
923 Result Patch(
const char *path,
const std::string &body,
924 const char *content_type);
925 Result Patch(
const char *path,
const Headers &headers,
926 const std::string &body,
const char *content_type);
927 Result Patch(
const char *path,
size_t content_length,
928 ContentProvider content_provider,
const char *content_type);
929 Result Patch(
const char *path, ContentProviderWithoutLength content_provider,
930 const char *content_type);
931 Result Patch(
const char *path,
const Headers &headers,
size_t content_length,
932 ContentProvider content_provider,
const char *content_type);
933 Result Patch(
const char *path,
const Headers &headers,
934 ContentProviderWithoutLength content_provider,
935 const char *content_type);
937 Result Delete(
const char *path);
938 Result Delete(
const char *path,
const Headers &headers);
939 Result Delete(
const char *path,
const char *body,
size_t content_length,
940 const char *content_type);
941 Result Delete(
const char *path,
const Headers &headers,
const char *body,
942 size_t content_length,
const char *content_type);
943 Result Delete(
const char *path,
const std::string &body,
944 const char *content_type);
945 Result Delete(
const char *path,
const Headers &headers,
946 const std::string &body,
const char *content_type);
948 Result Options(
const char *path);
949 Result Options(
const char *path,
const Headers &headers);
951 bool send(Request &req, Response &res, Error &error);
952 Result send(
const Request &req);
954 size_t is_socket_open()
const;
958 void set_hostname_addr_map(
const std::map<std::string, std::string> addr_map);
960 void set_default_headers(Headers headers);
962 void set_address_family(
int family);
963 void set_tcp_nodelay(
bool on);
964 void set_socket_options(SocketOptions socket_options);
966 void set_connection_timeout(time_t sec, time_t usec = 0);
967 template <
class Rep,
class Period>
969 set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration);
971 void set_read_timeout(time_t sec, time_t usec = 0);
972 template <
class Rep,
class Period>
973 void set_read_timeout(
const std::chrono::duration<Rep, Period> &duration);
975 void set_write_timeout(time_t sec, time_t usec = 0);
976 template <
class Rep,
class Period>
977 void set_write_timeout(
const std::chrono::duration<Rep, Period> &duration);
979 void set_basic_auth(
const char *username,
const char *password);
980 void set_bearer_token_auth(
const char *token);
981 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
982 void set_digest_auth(
const char *username,
const char *password);
985 void set_keep_alive(
bool on);
986 void set_follow_location(
bool on);
988 void set_url_encode(
bool on);
990 void set_compress(
bool on);
992 void set_decompress(
bool on);
994 void set_interface(
const char *intf);
996 void set_proxy(
const char *host,
int port);
997 void set_proxy_basic_auth(
const char *username,
const char *password);
998 void set_proxy_bearer_token_auth(
const char *token);
999 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1000 void set_proxy_digest_auth(
const char *username,
const char *password);
1003 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1004 void set_ca_cert_path(
const char *ca_cert_file_path,
1005 const char *ca_cert_dir_path =
nullptr);
1006 void set_ca_cert_store(X509_STORE *ca_cert_store);
1009 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1010 void enable_server_certificate_verification(
bool enabled);
1013 void set_logger(Logger logger);
1018 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1022 bool is_open()
const {
return sock != INVALID_SOCKET; }
1025 Result send_(Request &&req);
1027 virtual bool create_and_connect_socket(Socket &socket, Error &error);
1036 virtual void shutdown_ssl(Socket &socket,
bool shutdown_gracefully);
1037 void shutdown_socket(Socket &socket);
1038 void close_socket(Socket &socket);
1040 bool process_request(Stream &strm, Request &req, Response &res,
1041 bool close_connection, Error &error);
1043 bool write_content_with_provider(Stream &strm,
const Request &req,
1046 void copy_settings(
const ClientImpl &rhs);
1049 const std::string host_;
1051 const std::string host_and_port_;
1055 mutable std::mutex socket_mutex_;
1056 std::recursive_mutex request_mutex_;
1059 size_t socket_requests_in_flight_ = 0;
1060 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1061 bool socket_should_be_closed_when_request_is_done_ =
false;
1064 std::map<std::string, std::string> addr_map_;
1067 Headers default_headers_;
1070 std::string client_cert_path_;
1071 std::string client_key_path_;
1073 time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1074 time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1075 time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
1076 time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
1077 time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
1078 time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
1080 std::string basic_auth_username_;
1081 std::string basic_auth_password_;
1082 std::string bearer_token_auth_token_;
1083 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1084 std::string digest_auth_username_;
1085 std::string digest_auth_password_;
1088 bool keep_alive_ =
false;
1089 bool follow_location_ =
false;
1091 bool url_encode_ =
true;
1093 int address_family_ = AF_UNSPEC;
1094 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1095 SocketOptions socket_options_ =
nullptr;
1097 bool compress_ =
false;
1098 bool decompress_ =
true;
1100 std::string interface_;
1102 std::string proxy_host_;
1103 int proxy_port_ = -1;
1105 std::string proxy_basic_auth_username_;
1106 std::string proxy_basic_auth_password_;
1107 std::string proxy_bearer_token_auth_token_;
1108 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1109 std::string proxy_digest_auth_username_;
1110 std::string proxy_digest_auth_password_;
1113 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1114 std::string ca_cert_file_path_;
1115 std::string ca_cert_dir_path_;
1117 X509_STORE *ca_cert_store_ =
nullptr;
1120 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1121 bool server_certificate_verification_ =
true;
1127 socket_t create_client_socket(Error &error)
const;
1128 bool read_response_line(Stream &strm,
const Request &req, Response &res);
1129 bool write_request(Stream &strm, Request &req,
bool close_connection,
1131 bool redirect(Request &req, Response &res, Error &error);
1132 bool handle_request(Stream &strm, Request &req, Response &res,
1133 bool close_connection, Error &error);
1134 std::unique_ptr<Response> send_with_content_provider(
1137 const char *body,
size_t content_length, ContentProvider content_provider,
1138 ContentProviderWithoutLength content_provider_without_length,
1139 const char *content_type, Error &error);
1140 Result send_with_content_provider(
1141 const char *method,
const char *path,
const Headers &headers,
1142 const char *body,
size_t content_length, ContentProvider content_provider,
1143 ContentProviderWithoutLength content_provider_without_length,
1144 const char *content_type);
1146 std::string adjust_host_string(
const std::string &host)
const;
1148 virtual bool process_socket(
const Socket &socket,
1149 std::function<
bool(Stream &strm)> callback);
1150 virtual bool is_ssl()
const;
1156 explicit Client(
const std::string &scheme_host_port);
1158 explicit Client(
const std::string &scheme_host_port,
1159 const std::string &client_cert_path,
1160 const std::string &client_key_path);
1163 explicit Client(
const std::string &host,
int port);
1165 explicit Client(
const std::string &host,
int port,
1166 const std::string &client_cert_path,
1167 const std::string &client_key_path);
1169 Client(Client &&) =
default;
1173 bool is_valid()
const;
1175 Result Get(
const char *path);
1176 Result Get(
const char *path,
const Headers &headers);
1177 Result Get(
const char *path, Progress progress);
1178 Result Get(
const char *path,
const Headers &headers, Progress progress);
1179 Result Get(
const char *path, ContentReceiver content_receiver);
1180 Result Get(
const char *path,
const Headers &headers,
1181 ContentReceiver content_receiver);
1182 Result Get(
const char *path, ContentReceiver content_receiver,
1184 Result Get(
const char *path,
const Headers &headers,
1185 ContentReceiver content_receiver, Progress progress);
1186 Result Get(
const char *path, ResponseHandler response_handler,
1187 ContentReceiver content_receiver);
1188 Result Get(
const char *path,
const Headers &headers,
1189 ResponseHandler response_handler,
1190 ContentReceiver content_receiver);
1191 Result Get(
const char *path,
const Headers &headers,
1192 ResponseHandler response_handler, ContentReceiver content_receiver,
1194 Result Get(
const char *path, ResponseHandler response_handler,
1195 ContentReceiver content_receiver, Progress progress);
1197 Result Get(
const char *path,
const Params ¶ms,
const Headers &headers,
1198 Progress progress =
nullptr);
1199 Result Get(
const char *path,
const Params ¶ms,
const Headers &headers,
1200 ContentReceiver content_receiver, Progress progress =
nullptr);
1201 Result Get(
const char *path,
const Params ¶ms,
const Headers &headers,
1202 ResponseHandler response_handler, ContentReceiver content_receiver,
1203 Progress progress =
nullptr);
1205 Result Head(
const char *path);
1206 Result Head(
const char *path,
const Headers &headers);
1208 Result Post(
const char *path);
1209 Result Post(
const char *path,
const char *body,
size_t content_length,
1210 const char *content_type);
1211 Result Post(
const char *path,
const Headers &headers,
const char *body,
1212 size_t content_length,
const char *content_type);
1213 Result Post(
const char *path,
const std::string &body,
1214 const char *content_type);
1215 Result Post(
const char *path,
const Headers &headers,
const std::string &body,
1216 const char *content_type);
1217 Result Post(
const char *path,
size_t content_length,
1218 ContentProvider content_provider,
const char *content_type);
1219 Result Post(
const char *path, ContentProviderWithoutLength content_provider,
1220 const char *content_type);
1221 Result Post(
const char *path,
const Headers &headers,
size_t content_length,
1222 ContentProvider content_provider,
const char *content_type);
1223 Result Post(
const char *path,
const Headers &headers,
1224 ContentProviderWithoutLength content_provider,
1225 const char *content_type);
1226 Result Post(
const char *path,
const Params ¶ms);
1227 Result Post(
const char *path,
const Headers &headers,
const Params ¶ms);
1228 Result Post(
const char *path,
const MultipartFormDataItems &items);
1229 Result Post(
const char *path,
const Headers &headers,
1230 const MultipartFormDataItems &items);
1231 Result Post(
const char *path,
const Headers &headers,
1232 const MultipartFormDataItems &items,
const std::string &boundary);
1233 Result Put(
const char *path);
1234 Result Put(
const char *path,
const char *body,
size_t content_length,
1235 const char *content_type);
1236 Result Put(
const char *path,
const Headers &headers,
const char *body,
1237 size_t content_length,
const char *content_type);
1238 Result Put(
const char *path,
const std::string &body,
1239 const char *content_type);
1240 Result Put(
const char *path,
const Headers &headers,
const std::string &body,
1241 const char *content_type);
1242 Result Put(
const char *path,
size_t content_length,
1243 ContentProvider content_provider,
const char *content_type);
1244 Result Put(
const char *path, ContentProviderWithoutLength content_provider,
1245 const char *content_type);
1246 Result Put(
const char *path,
const Headers &headers,
size_t content_length,
1247 ContentProvider content_provider,
const char *content_type);
1248 Result Put(
const char *path,
const Headers &headers,
1249 ContentProviderWithoutLength content_provider,
1250 const char *content_type);
1251 Result Put(
const char *path,
const Params ¶ms);
1252 Result Put(
const char *path,
const Headers &headers,
const Params ¶ms);
1253 Result Patch(
const char *path);
1254 Result Patch(
const char *path,
const char *body,
size_t content_length,
1255 const char *content_type);
1256 Result Patch(
const char *path,
const Headers &headers,
const char *body,
1257 size_t content_length,
const char *content_type);
1258 Result Patch(
const char *path,
const std::string &body,
1259 const char *content_type);
1260 Result Patch(
const char *path,
const Headers &headers,
1261 const std::string &body,
const char *content_type);
1262 Result Patch(
const char *path,
size_t content_length,
1263 ContentProvider content_provider,
const char *content_type);
1264 Result Patch(
const char *path, ContentProviderWithoutLength content_provider,
1265 const char *content_type);
1266 Result Patch(
const char *path,
const Headers &headers,
size_t content_length,
1267 ContentProvider content_provider,
const char *content_type);
1268 Result Patch(
const char *path,
const Headers &headers,
1269 ContentProviderWithoutLength content_provider,
1270 const char *content_type);
1272 Result Delete(
const char *path);
1273 Result Delete(
const char *path,
const Headers &headers);
1274 Result Delete(
const char *path,
const char *body,
size_t content_length,
1275 const char *content_type);
1276 Result Delete(
const char *path,
const Headers &headers,
const char *body,
1277 size_t content_length,
const char *content_type);
1278 Result Delete(
const char *path,
const std::string &body,
1279 const char *content_type);
1280 Result Delete(
const char *path,
const Headers &headers,
1281 const std::string &body,
const char *content_type);
1283 Result Options(
const char *path);
1284 Result Options(
const char *path,
const Headers &headers);
1286 bool send(Request &req, Response &res, Error &error);
1287 Result send(
const Request &req);
1289 size_t is_socket_open()
const;
1293 void set_hostname_addr_map(
const std::map<std::string, std::string> addr_map);
1295 void set_default_headers(Headers headers);
1297 void set_address_family(
int family);
1298 void set_tcp_nodelay(
bool on);
1299 void set_socket_options(SocketOptions socket_options);
1301 void set_connection_timeout(time_t sec, time_t usec = 0);
1302 template <
class Rep,
class Period>
1304 set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration);
1306 void set_read_timeout(time_t sec, time_t usec = 0);
1307 template <
class Rep,
class Period>
1308 void set_read_timeout(
const std::chrono::duration<Rep, Period> &duration);
1310 void set_write_timeout(time_t sec, time_t usec = 0);
1311 template <
class Rep,
class Period>
1312 void set_write_timeout(
const std::chrono::duration<Rep, Period> &duration);
1314 void set_basic_auth(
const char *username,
const char *password);
1315 void set_bearer_token_auth(
const char *token);
1316 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1317 void set_digest_auth(
const char *username,
const char *password);
1320 void set_keep_alive(
bool on);
1321 void set_follow_location(
bool on);
1323 void set_url_encode(
bool on);
1325 void set_compress(
bool on);
1327 void set_decompress(
bool on);
1329 void set_interface(
const char *intf);
1331 void set_proxy(
const char *host,
int port);
1332 void set_proxy_basic_auth(
const char *username,
const char *password);
1333 void set_proxy_bearer_token_auth(
const char *token);
1334 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1335 void set_proxy_digest_auth(
const char *username,
const char *password);
1338 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1339 void enable_server_certificate_verification(
bool enabled);
1342 void set_logger(Logger logger);
1345 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1346 void set_ca_cert_path(
const char *ca_cert_file_path,
1347 const char *ca_cert_dir_path =
nullptr);
1349 void set_ca_cert_store(X509_STORE *ca_cert_store);
1351 long get_openssl_verify_result()
const;
1353 SSL_CTX *ssl_context()
const;
1357 std::unique_ptr<ClientImpl> cli_;
1359 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1360 bool is_ssl_ =
false;
1364 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1365 class SSLServer :
public Server {
1367 SSLServer(
const char *cert_path,
const char *private_key_path,
1368 const char *client_ca_cert_file_path =
nullptr,
1369 const char *client_ca_cert_dir_path =
nullptr);
1371 SSLServer(X509 *cert, EVP_PKEY *private_key,
1372 X509_STORE *client_ca_cert_store =
nullptr);
1375 const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
1377 ~SSLServer()
override;
1379 bool is_valid()
const override;
1382 bool process_and_close_socket(
socket_t sock)
override;
1385 std::mutex ctx_mutex_;
1388 class SSLClient :
public ClientImpl {
1390 explicit SSLClient(
const std::string &host);
1392 explicit SSLClient(
const std::string &host,
int port);
1394 explicit SSLClient(
const std::string &host,
int port,
1395 const std::string &client_cert_path,
1396 const std::string &client_key_path);
1398 explicit SSLClient(
const std::string &host,
int port, X509 *client_cert,
1399 EVP_PKEY *client_key);
1401 ~SSLClient()
override;
1403 bool is_valid()
const override;
1405 void set_ca_cert_store(X509_STORE *ca_cert_store);
1407 long get_openssl_verify_result()
const;
1409 SSL_CTX *ssl_context()
const;
1412 bool create_and_connect_socket(Socket &socket, Error &error)
override;
1413 void shutdown_ssl(Socket &socket,
bool shutdown_gracefully)
override;
1414 void shutdown_ssl_impl(Socket &socket,
bool shutdown_socket);
1416 bool process_socket(
const Socket &socket,
1417 std::function<
bool(Stream &strm)> callback)
override;
1418 bool is_ssl()
const override;
1420 bool connect_with_proxy(Socket &sock, Response &res,
bool &success,
1422 bool initialize_ssl(Socket &socket, Error &error);
1426 bool verify_host(X509 *server_cert)
const;
1427 bool verify_host_with_subject_alt_name(X509 *server_cert)
const;
1428 bool verify_host_with_common_name(X509 *server_cert)
const;
1429 bool check_host_name(
const char *pattern,
size_t pattern_len)
const;
1432 std::mutex ctx_mutex_;
1433 std::once_flag initialize_cert_;
1435 std::vector<std::string> host_components_;
1437 long verify_result_ = 0;
1439 friend class ClientImpl;
1449 template <
typename T,
typename U>
1450 inline void duration_to_sec_and_usec(
const T &duration, U callback) {
1451 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
1452 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
1453 duration - std::chrono::seconds(sec))
1455 callback(sec, usec);
1458 template <
typename T>
1459 inline T get_header_value(
const Headers & ,
const char * ,
1460 size_t = 0, uint64_t = 0) {}
1463 inline uint64_t get_header_value<uint64_t>(
const Headers &headers,
1464 const char *key,
size_t id,
1466 auto rng = headers.equal_range(key);
1467 auto it = rng.first;
1468 std::advance(it,
static_cast<ssize_t>(
id));
1469 if (it != rng.second) {
1470 return std::strtoull(it->second.data(),
nullptr, 10);
1477 template <
typename T>
1478 inline T Request::get_header_value(
const char *key,
size_t id)
const {
1479 return detail::get_header_value<T>(headers, key,
id, 0);
1482 template <
typename T>
1483 inline T Response::get_header_value(
const char *key,
size_t id)
const {
1484 return detail::get_header_value<T>(headers, key,
id, 0);
1487 template <
typename... Args>
1488 inline ssize_t Stream::write_format(
const char *fmt,
const Args &...args) {
1489 const auto bufsiz = 2048;
1490 std::array<char, bufsiz>
buf{};
1492 #if defined(_MSC_VER) && _MSC_VER < 1900
1493 auto sn = _snprintf_s(
buf.data(), bufsiz, _TRUNCATE, fmt, args...);
1495 auto sn = snprintf(
buf.data(),
buf.size() - 1, fmt, args...);
1497 if (sn <= 0) {
return sn; }
1499 auto n =
static_cast<size_t>(sn);
1501 if (
n >=
buf.size() - 1) {
1502 std::vector<char> glowable_buf(
buf.size());
1504 while (
n >= glowable_buf.size() - 1) {
1505 glowable_buf.resize(glowable_buf.size() * 2);
1506 #if defined(_MSC_VER) && _MSC_VER < 1900
1507 n =
static_cast<size_t>(_snprintf_s(&glowable_buf[0], glowable_buf.size(),
1508 glowable_buf.size() - 1, fmt,
1511 n =
static_cast<size_t>(
1512 snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
1515 return write(&glowable_buf[0],
n);
1517 return write(
buf.data(),
n);
1521 inline void default_socket_options(
socket_t sock) {
1524 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<char *
>(&yes),
1526 setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
1527 reinterpret_cast<char *
>(&yes),
sizeof(yes));
1530 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
reinterpret_cast<void *
>(&yes),
1533 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<void *
>(&yes),
1539 template <
class Rep,
class Period>
1541 Server::set_read_timeout(
const std::chrono::duration<Rep, Period> &duration) {
1542 detail::duration_to_sec_and_usec(
1543 duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
1547 template <
class Rep,
class Period>
1549 Server::set_write_timeout(
const std::chrono::duration<Rep, Period> &duration) {
1550 detail::duration_to_sec_and_usec(
1551 duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
1555 template <
class Rep,
class Period>
1557 Server::set_idle_interval(
const std::chrono::duration<Rep, Period> &duration) {
1558 detail::duration_to_sec_and_usec(
1559 duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
1563 inline std::string
to_string(
const Error error) {
1565 case Error::Success:
return "Success";
1566 case Error::Connection:
return "Connection";
1567 case Error::BindIPAddress:
return "BindIPAddress";
1568 case Error::Read:
return "Read";
1569 case Error::Write:
return "Write";
1570 case Error::ExceedRedirectCount:
return "ExceedRedirectCount";
1571 case Error::Canceled:
return "Canceled";
1572 case Error::SSLConnection:
return "SSLConnection";
1573 case Error::SSLLoadingCerts:
return "SSLLoadingCerts";
1574 case Error::SSLServerVerification:
return "SSLServerVerification";
1575 case Error::UnsupportedMultipartBoundaryChars:
1576 return "UnsupportedMultipartBoundaryChars";
1577 case Error::Compression:
return "Compression";
1578 case Error::Unknown:
return "Unknown";
1585 inline std::ostream &operator<<(std::ostream &os,
const Error &obj) {
1587 os <<
" (" <<
static_cast<std::underlying_type<Error>::type
>(obj) <<
')';
1591 template <
typename T>
1592 inline T Result::get_request_header_value(
const char *key,
size_t id)
const {
1593 return detail::get_header_value<T>(request_headers_, key,
id, 0);
1596 template <
class Rep,
class Period>
1597 inline void ClientImpl::set_connection_timeout(
1598 const std::chrono::duration<Rep, Period> &duration) {
1599 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
1600 set_connection_timeout(sec, usec);
1604 template <
class Rep,
class Period>
1605 inline void ClientImpl::set_read_timeout(
1606 const std::chrono::duration<Rep, Period> &duration) {
1607 detail::duration_to_sec_and_usec(
1608 duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
1611 template <
class Rep,
class Period>
1612 inline void ClientImpl::set_write_timeout(
1613 const std::chrono::duration<Rep, Period> &duration) {
1614 detail::duration_to_sec_and_usec(
1615 duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
1618 template <
class Rep,
class Period>
1619 inline void Client::set_connection_timeout(
1620 const std::chrono::duration<Rep, Period> &duration) {
1621 cli_->set_connection_timeout(duration);
1624 template <
class Rep,
class Period>
1626 Client::set_read_timeout(
const std::chrono::duration<Rep, Period> &duration) {
1627 cli_->set_read_timeout(duration);
1630 template <
class Rep,
class Period>
1632 Client::set_write_timeout(
const std::chrono::duration<Rep, Period> &duration) {
1633 cli_->set_write_timeout(duration);
1641 std::string append_query_params(
const char *path,
const Params ¶ms);
1643 std::pair<std::string, std::string> make_range_header(Ranges ranges);
1645 std::pair<std::string, std::string>
1646 make_basic_authentication_header(
const std::string &username,
1647 const std::string &password,
1648 bool is_proxy =
false);
1652 std::string encode_query_param(
const std::string &
value);
1654 void read_file(
const std::string &path, std::string &out);
1656 std::string trim_copy(
const std::string &s);
1658 void split(
const char *b,
const char *
e,
char d,
1659 std::function<
void(
const char *,
const char *)> fn);
1661 bool process_client_socket(
socket_t sock, time_t read_timeout_sec,
1662 time_t read_timeout_usec, time_t write_timeout_sec,
1663 time_t write_timeout_usec,
1664 std::function<
bool(Stream &)> callback);
1667 const char *host,
const char *ip,
int port,
int address_family,
1668 bool tcp_nodelay, SocketOptions socket_options,
1669 time_t connection_timeout_sec, time_t connection_timeout_usec,
1670 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
1671 time_t write_timeout_usec,
const std::string &intf, Error &error);
1673 const char *get_header_value(
const Headers &headers,
const char *key,
1674 size_t id = 0,
const char *def =
nullptr);
1676 std::string params_to_query_str(
const Params ¶ms);
1678 void parse_query_text(
const std::string &s, Params ¶ms);
1680 bool parse_range_header(
const std::string &s, Ranges &ranges);
1684 ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
int flags);
1686 ssize_t read_socket(
socket_t sock,
void *ptr,
size_t size,
int flags);
1688 enum class EncodingType { None = 0, Gzip, Brotli };
1690 EncodingType encoding_type(
const Request &req,
const Response &res);
1692 class BufferStream :
public Stream {
1694 BufferStream() =
default;
1695 ~BufferStream()
override =
default;
1697 bool is_readable()
const override;
1698 bool is_writable()
const override;
1699 ssize_t read(
char *ptr,
size_t size)
override;
1700 ssize_t write(
const char *ptr,
size_t size)
override;
1701 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
1704 const std::string &get_buffer()
const;
1708 size_t position = 0;
1713 virtual ~compressor() =
default;
1715 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
1716 virtual bool compress(
const char *data,
size_t data_length,
bool last,
1717 Callback callback) = 0;
1720 class decompressor {
1722 virtual ~decompressor() =
default;
1724 virtual bool is_valid()
const = 0;
1726 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
1727 virtual bool decompress(
const char *data,
size_t data_length,
1728 Callback callback) = 0;
1731 class nocompressor :
public compressor {
1733 virtual ~nocompressor() =
default;
1735 bool compress(
const char *data,
size_t data_length,
bool ,
1736 Callback callback)
override;
1739 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
1740 class gzip_compressor :
public compressor {
1745 bool compress(
const char *data,
size_t data_length,
bool last,
1746 Callback callback)
override;
1749 bool is_valid_ =
false;
1753 class gzip_decompressor :
public decompressor {
1755 gzip_decompressor();
1756 ~gzip_decompressor();
1758 bool is_valid()
const override;
1760 bool decompress(
const char *data,
size_t data_length,
1761 Callback callback)
override;
1764 bool is_valid_ =
false;
1769 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
1770 class brotli_compressor :
public compressor {
1772 brotli_compressor();
1773 ~brotli_compressor();
1775 bool compress(
const char *data,
size_t data_length,
bool last,
1776 Callback callback)
override;
1779 BrotliEncoderState *state_ =
nullptr;
1782 class brotli_decompressor :
public decompressor {
1784 brotli_decompressor();
1785 ~brotli_decompressor();
1787 bool is_valid()
const override;
1789 bool decompress(
const char *data,
size_t data_length,
1790 Callback callback)
override;
1793 BrotliDecoderResult decoder_r;
1794 BrotliDecoderState *decoder_s =
nullptr;
1800 class stream_line_reader {
1802 stream_line_reader(Stream &strm,
char *fixed_buffer,
1803 size_t fixed_buffer_size);
1804 const char *ptr()
const;
1805 size_t size()
const;
1806 bool end_with_crlf()
const;
1810 void append(
char c);
1813 char *fixed_buffer_;
1814 const size_t fixed_buffer_size_;
1815 size_t fixed_buffer_used_size_ = 0;
1816 std::string glowable_buffer_;
1829 inline bool is_hex(
char c,
int &
v) {
1830 if (0x20 <= c && isdigit(c)) {
1833 }
else if (
'A' <= c && c <=
'F') {
1836 }
else if (
'a' <= c && c <=
'f') {
1843 inline bool from_hex_to_i(
const std::string &s,
size_t i,
size_t cnt,
1845 if (i >= s.size()) {
return false; }
1848 for (; cnt; i++, cnt--) {
1849 if (!s[i]) {
return false; }
1851 if (is_hex(s[i],
v)) {
1860 inline std::string from_i_to_hex(
size_t n) {
1861 const char *charset =
"0123456789abcdef";
1864 ret = charset[
n & 15] + ret;
1870 inline size_t to_utf8(
int code,
char *buff) {
1871 if (code < 0x0080) {
1872 buff[0] = (code & 0x7F);
1874 }
else if (code < 0x0800) {
1875 buff[0] =
static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
1876 buff[1] =
static_cast<char>(0x80 | (code & 0x3F));
1878 }
else if (code < 0xD800) {
1879 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
1880 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
1881 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
1883 }
else if (code < 0xE000) {
1885 }
else if (code < 0x10000) {
1886 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
1887 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
1888 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
1890 }
else if (code < 0x110000) {
1891 buff[0] =
static_cast<char>(0xF0 | ((code >> 18) & 0x7));
1892 buff[1] =
static_cast<char>(0x80 | ((code >> 12) & 0x3F));
1893 buff[2] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
1894 buff[3] =
static_cast<char>(0x80 | (code & 0x3F));
1904 inline std::string base64_encode(
const std::string &in) {
1905 static const auto lookup =
1906 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1909 out.reserve(in.size());
1915 val = (val << 8) + static_cast<uint8_t>(c);
1918 out.push_back(lookup[(val >> valb) & 0x3F]);
1923 if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
1925 while (out.size() % 4) {
1932 inline bool is_file(
const std::string &path) {
1934 return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
1937 inline bool is_dir(
const std::string &path) {
1939 return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
1942 inline bool is_valid_path(
const std::string &path) {
1947 while (i < path.size() && path[i] ==
'/') {
1951 while (i < path.size()) {
1954 while (i < path.size() && path[i] !=
'/') {
1961 if (!path.compare(beg,
len,
".")) {
1963 }
else if (!path.compare(beg,
len,
"..")) {
1964 if (level == 0) {
return false; }
1971 while (i < path.size() && path[i] ==
'/') {
1979 inline std::string encode_query_param(
const std::string &
value) {
1980 std::ostringstream escaped;
1982 escaped << std::hex;
1984 for (
auto c :
value) {
1985 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' ||
1986 c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' || c ==
'\'' || c ==
'(' ||
1990 escaped << std::uppercase;
1991 escaped <<
'%' << std::setw(2)
1992 <<
static_cast<int>(
static_cast<unsigned char>(c));
1993 escaped << std::nouppercase;
1997 return escaped.str();
2000 inline std::string encode_url(
const std::string &s) {
2002 result.reserve(s.size());
2004 for (
size_t i = 0; s[i]; i++) {
2006 case ' ': result +=
"%20";
break;
2007 case '+': result +=
"%2B";
break;
2008 case '\r': result +=
"%0D";
break;
2009 case '\n': result +=
"%0A";
break;
2010 case '\'': result +=
"%27";
break;
2011 case ',': result +=
"%2C";
break;
2013 case ';': result +=
"%3B";
break;
2015 auto c =
static_cast<uint8_t
>(s[i]);
2019 auto len = snprintf(hex,
sizeof(hex) - 1,
"%02X", c);
2021 result.append(hex,
static_cast<size_t>(
len));
2032 inline std::string decode_url(
const std::string &s,
2033 bool convert_plus_to_space) {
2036 for (
size_t i = 0; i < s.size(); i++) {
2037 if (s[i] ==
'%' && i + 1 < s.size()) {
2038 if (s[i + 1] ==
'u') {
2040 if (from_hex_to_i(s, i + 2, 4, val)) {
2043 size_t len = to_utf8(val, buff);
2044 if (
len > 0) { result.append(buff,
len); }
2051 if (from_hex_to_i(s, i + 1, 2, val)) {
2053 result +=
static_cast<char>(val);
2059 }
else if (convert_plus_to_space && s[i] ==
'+') {
2069 inline void read_file(
const std::string &path, std::string &out) {
2070 std::ifstream fs(path, std::ios_base::binary);
2071 fs.seekg(0, std::ios_base::end);
2072 auto size = fs.tellg();
2074 out.resize(
static_cast<size_t>(size));
2075 fs.read(&out[0],
static_cast<std::streamsize
>(size));
2078 inline std::string file_extension(
const std::string &path) {
2080 static auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
2081 if (std::regex_search(path, m, re)) {
return m[1].str(); }
2082 return std::string();
2085 inline bool is_space_or_tab(
char c) {
return c ==
' ' || c ==
'\t'; }
2087 inline std::pair<size_t, size_t> trim(
const char *b,
const char *
e,
size_t left,
2089 while (b + left <
e && is_space_or_tab(b[left])) {
2092 while (right > 0 && is_space_or_tab(b[right - 1])) {
2095 return std::make_pair(left, right);
2098 inline std::string trim_copy(
const std::string &s) {
2099 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
2100 return s.substr(r.first, r.second - r.first);
2103 inline void split(
const char *b,
const char *
e,
char d,
2104 std::function<
void(
const char *,
const char *)> fn) {
2108 while (
e ? (b + i <
e) : (b[i] !=
'\0')) {
2110 auto r = trim(b,
e, beg, i);
2111 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2118 auto r = trim(b,
e, beg, i);
2119 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2123 inline stream_line_reader::stream_line_reader(Stream &strm,
char *fixed_buffer,
2124 size_t fixed_buffer_size)
2125 : strm_(strm), fixed_buffer_(fixed_buffer),
2126 fixed_buffer_size_(fixed_buffer_size) {}
2128 inline const char *stream_line_reader::ptr()
const {
2129 if (glowable_buffer_.empty()) {
2130 return fixed_buffer_;
2132 return glowable_buffer_.data();
2136 inline size_t stream_line_reader::size()
const {
2137 if (glowable_buffer_.empty()) {
2138 return fixed_buffer_used_size_;
2140 return glowable_buffer_.size();
2144 inline bool stream_line_reader::end_with_crlf()
const {
2145 auto end = ptr() + size();
2146 return size() >= 2 && end[-2] ==
'\r' && end[-1] ==
'\n';
2149 inline bool stream_line_reader::getline() {
2150 fixed_buffer_used_size_ = 0;
2151 glowable_buffer_.clear();
2153 for (
size_t i = 0;; i++) {
2155 auto n = strm_.read(&
byte, 1);
2159 }
else if (
n == 0) {
2169 if (
byte ==
'\n') {
break; }
2175 inline void stream_line_reader::append(
char c) {
2176 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
2177 fixed_buffer_[fixed_buffer_used_size_++] = c;
2178 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
2180 if (glowable_buffer_.empty()) {
2181 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
2182 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
2184 glowable_buffer_ += c;
2188 inline int close_socket(
socket_t sock) {
2190 return closesocket(sock);
2196 template <
typename T>
inline ssize_t handle_EINTR(T fn) {
2200 if (res < 0 && errno == EINTR) {
continue; }
2206 inline ssize_t read_socket(
socket_t sock,
void *ptr,
size_t size,
int flags) {
2207 return handle_EINTR([&]() {
2210 static_cast<char *
>(ptr),
static_cast<int>(size),
2218 inline ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
2220 return handle_EINTR([&]() {
2223 static_cast<const char *
>(ptr),
static_cast<int>(size),
2232 #ifdef CPPHTTPLIB_USE_POLL
2233 struct pollfd pfd_read;
2235 pfd_read.events = POLLIN;
2237 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2239 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2242 if (sock >= FD_SETSIZE) {
return 1; }
2250 tv.tv_sec =
static_cast<long>(sec);
2251 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2253 return handle_EINTR([&]() {
2254 return select(
static_cast<int>(sock + 1), &fds,
nullptr,
nullptr, &tv);
2260 #ifdef CPPHTTPLIB_USE_POLL
2261 struct pollfd pfd_read;
2263 pfd_read.events = POLLOUT;
2265 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2267 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2270 if (sock >= FD_SETSIZE) {
return 1; }
2278 tv.tv_sec =
static_cast<long>(sec);
2279 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2281 return handle_EINTR([&]() {
2282 return select(
static_cast<int>(sock + 1),
nullptr, &fds,
nullptr, &tv);
2287 inline bool wait_until_socket_is_ready(
socket_t sock, time_t sec, time_t usec) {
2288 #ifdef CPPHTTPLIB_USE_POLL
2289 struct pollfd pfd_read;
2291 pfd_read.events = POLLIN | POLLOUT;
2293 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2295 auto poll_res = handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2297 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
2300 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
2301 reinterpret_cast<char *
>(&error), &
len);
2302 return res >= 0 && !
error;
2307 if (sock >= FD_SETSIZE) {
return false; }
2312 FD_SET(sock, &fdsr);
2318 tv.tv_sec =
static_cast<long>(sec);
2319 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2321 auto ret = handle_EINTR([&]() {
2322 return select(
static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
2325 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
2328 return getsockopt(sock, SOL_SOCKET, SO_ERROR,
2329 reinterpret_cast<char *
>(&error), &
len) >= 0 &&
2336 class SocketStream :
public Stream {
2338 SocketStream(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2339 time_t write_timeout_sec, time_t write_timeout_usec);
2340 ~SocketStream()
override;
2342 bool is_readable()
const override;
2343 bool is_writable()
const override;
2344 ssize_t read(
char *ptr,
size_t size)
override;
2345 ssize_t write(
const char *ptr,
size_t size)
override;
2346 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
2351 time_t read_timeout_sec_;
2352 time_t read_timeout_usec_;
2353 time_t write_timeout_sec_;
2354 time_t write_timeout_usec_;
2356 std::vector<char> read_buff_;
2357 size_t read_buff_off_ = 0;
2358 size_t read_buff_content_size_ = 0;
2360 static const size_t read_buff_size_ = 1024 * 4;
2363 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2364 class SSLSocketStream :
public Stream {
2366 SSLSocketStream(
socket_t sock, SSL *ssl, time_t read_timeout_sec,
2367 time_t read_timeout_usec, time_t write_timeout_sec,
2368 time_t write_timeout_usec);
2369 ~SSLSocketStream()
override;
2371 bool is_readable()
const override;
2372 bool is_writable()
const override;
2373 ssize_t read(
char *ptr,
size_t size)
override;
2374 ssize_t write(
const char *ptr,
size_t size)
override;
2375 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
2381 time_t read_timeout_sec_;
2382 time_t read_timeout_usec_;
2383 time_t write_timeout_sec_;
2384 time_t write_timeout_usec_;
2388 inline bool keep_alive(
socket_t sock, time_t keep_alive_timeout_sec) {
2389 using namespace std::chrono;
2390 auto start = steady_clock::now();
2392 auto val = select_read(sock, 0, 10000);
2395 }
else if (val == 0) {
2396 auto current = steady_clock::now();
2397 auto duration = duration_cast<milliseconds>(current - start);
2398 auto timeout = keep_alive_timeout_sec * 1000;
2399 if (duration.count() > timeout) {
return false; }
2400 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2407 template <
typename T>
2409 process_server_socket_core(
socket_t sock,
size_t keep_alive_max_count,
2410 time_t keep_alive_timeout_sec, T callback) {
2411 assert(keep_alive_max_count > 0);
2413 auto count = keep_alive_max_count;
2414 while (count > 0 && keep_alive(sock, keep_alive_timeout_sec)) {
2415 auto close_connection = count == 1;
2416 auto connection_closed =
false;
2417 ret = callback(close_connection, connection_closed);
2418 if (!ret || connection_closed) {
break; }
2424 template <
typename T>
2426 process_server_socket(
socket_t sock,
size_t keep_alive_max_count,
2427 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
2428 time_t read_timeout_usec, time_t write_timeout_sec,
2429 time_t write_timeout_usec, T callback) {
2430 return process_server_socket_core(
2431 sock, keep_alive_max_count, keep_alive_timeout_sec,
2432 [&](
bool close_connection,
bool &connection_closed) {
2433 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
2434 write_timeout_sec, write_timeout_usec);
2435 return callback(strm, close_connection, connection_closed);
2439 inline bool process_client_socket(
socket_t sock, time_t read_timeout_sec,
2440 time_t read_timeout_usec,
2441 time_t write_timeout_sec,
2442 time_t write_timeout_usec,
2443 std::function<
bool(Stream &)> callback) {
2444 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
2445 write_timeout_sec, write_timeout_usec);
2446 return callback(strm);
2449 inline int shutdown_socket(
socket_t sock) {
2451 return shutdown(sock, SD_BOTH);
2453 return shutdown(sock, SHUT_RDWR);
2457 template <
typename BindOrConnect>
2458 socket_t create_socket(
const char *host,
const char *ip,
int port,
2459 int address_family,
int socket_flags,
bool tcp_nodelay,
2460 SocketOptions socket_options,
2461 BindOrConnect bind_or_connect) {
2463 struct addrinfo hints;
2464 struct addrinfo *result;
2466 memset(&hints, 0,
sizeof(
struct addrinfo));
2467 hints.ai_family = address_family;
2468 hints.ai_socktype = SOCK_STREAM;
2469 hints.ai_flags = socket_flags;
2470 hints.ai_protocol = 0;
2473 if (ip[0] !=
'\0') {
2474 hints.ai_family = AF_UNSPEC;
2475 hints.ai_flags = AI_NUMERICHOST;
2480 if (ip[0] !=
'\0' ? getaddrinfo(ip, service.c_str(), &hints, &result)
2481 : getaddrinfo(host, service.c_str(), &hints, &result)) {
2482 #if defined __linux__ && !defined __ANDROID__
2485 return INVALID_SOCKET;
2488 for (
auto rp = result; rp; rp = rp->ai_next) {
2492 WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0,
2493 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
2508 if (sock == INVALID_SOCKET) {
2509 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2512 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2514 if (sock == INVALID_SOCKET) {
continue; }
2517 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
continue; }
2522 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<char *
>(&yes),
2526 if (socket_options) { socket_options(sock); }
2528 if (rp->ai_family == AF_INET6) {
2530 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<char *
>(&no),
2535 if (bind_or_connect(sock, *rp)) {
2536 freeaddrinfo(result);
2543 freeaddrinfo(result);
2544 return INVALID_SOCKET;
2547 inline void set_nonblocking(
socket_t sock,
bool nonblocking) {
2549 auto flags = nonblocking ? 1UL : 0UL;
2550 ioctlsocket(sock, FIONBIO, &flags);
2552 auto flags = fcntl(sock, F_GETFL, 0);
2553 fcntl(sock, F_SETFL,
2554 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
2558 inline bool is_connection_error() {
2560 return WSAGetLastError() != WSAEWOULDBLOCK;
2562 return errno != EINPROGRESS;
2566 inline bool bind_ip_address(
socket_t sock,
const char *host) {
2567 struct addrinfo hints;
2568 struct addrinfo *result;
2570 memset(&hints, 0,
sizeof(
struct addrinfo));
2571 hints.ai_family = AF_UNSPEC;
2572 hints.ai_socktype = SOCK_STREAM;
2573 hints.ai_protocol = 0;
2575 if (getaddrinfo(host,
"0", &hints, &result)) {
return false; }
2578 for (
auto rp = result; rp; rp = rp->ai_next) {
2579 const auto &ai = *rp;
2580 if (!::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen))) {
2586 freeaddrinfo(result);
2590 #if !defined _WIN32 && !defined ANDROID
2595 inline std::string if2ip(
const std::string &ifn) {
2596 struct ifaddrs *ifap;
2598 for (
auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
2599 if (ifa->ifa_addr && ifn == ifa->ifa_name) {
2600 if (ifa->ifa_addr->sa_family == AF_INET) {
2601 auto sa =
reinterpret_cast<struct sockaddr_in *
>(ifa->ifa_addr);
2602 char buf[INET_ADDRSTRLEN];
2603 if (inet_ntop(AF_INET, &sa->sin_addr,
buf, INET_ADDRSTRLEN)) {
2605 return std::string(
buf, INET_ADDRSTRLEN);
2611 return std::string();
2615 inline socket_t create_client_socket(
2616 const char *host,
const char *ip,
int port,
int address_family,
2617 bool tcp_nodelay, SocketOptions socket_options,
2618 time_t connection_timeout_sec, time_t connection_timeout_usec,
2619 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
2620 time_t write_timeout_usec,
const std::string &intf, Error &error) {
2621 auto sock = create_socket(
2622 host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options),
2623 [&](
socket_t sock2,
struct addrinfo &ai) ->
bool {
2624 if (!intf.empty()) {
2626 auto ip = if2ip(intf);
2627 if (ip.empty()) { ip = intf; }
2628 if (!bind_ip_address(sock2, ip.c_str())) {
2629 error = Error::BindIPAddress;
2635 set_nonblocking(sock2,
true);
2638 ::connect(sock2, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen));
2641 if (is_connection_error() ||
2642 !wait_until_socket_is_ready(sock2, connection_timeout_sec,
2643 connection_timeout_usec)) {
2644 error = Error::Connection;
2649 set_nonblocking(sock2,
false);
2653 tv.tv_sec =
static_cast<long>(read_timeout_sec);
2654 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(read_timeout_usec);
2655 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, (
char *)&tv,
sizeof(tv));
2659 tv.tv_sec =
static_cast<long>(write_timeout_sec);
2660 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(write_timeout_usec);
2661 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, (
char *)&tv,
sizeof(tv));
2664 error = Error::Success;
2668 if (sock != INVALID_SOCKET) {
2669 error = Error::Success;
2671 if (error == Error::Success) {
error = Error::Connection; }
2677 inline void get_remote_ip_and_port(
const struct sockaddr_storage &addr,
2678 socklen_t addr_len, std::string &ip,
2680 if (addr.ss_family == AF_INET) {
2681 port = ntohs(
reinterpret_cast<const struct sockaddr_in *
>(&addr)->sin_port);
2682 }
else if (addr.ss_family == AF_INET6) {
2684 ntohs(
reinterpret_cast<const struct sockaddr_in6 *
>(&addr)->sin6_port);
2687 std::array<char, NI_MAXHOST> ipstr{};
2688 if (!getnameinfo(
reinterpret_cast<const struct sockaddr *
>(&addr), addr_len,
2689 ipstr.data(),
static_cast<socklen_t
>(ipstr.size()),
nullptr,
2690 0, NI_NUMERICHOST)) {
2695 inline void get_remote_ip_and_port(
socket_t sock, std::string &ip,
int &port) {
2696 struct sockaddr_storage addr;
2697 socklen_t addr_len =
sizeof(addr);
2699 if (!getpeername(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
2701 get_remote_ip_and_port(addr, addr_len, ip, port);
2705 inline constexpr
unsigned int str2tag_core(
const char *s,
size_t l,
2708 : str2tag_core(s + 1, l - 1,
2709 (h * 33) ^
static_cast<unsigned char>(*s));
2712 inline unsigned int str2tag(
const std::string &s) {
2713 return str2tag_core(s.data(), s.size(), 0);
2718 inline constexpr
unsigned int operator"" _t(
const char *s,
size_t l) {
2719 return str2tag_core(s, l, 0);
2725 find_content_type(
const std::string &path,
2726 const std::map<std::string, std::string> &user_data) {
2727 auto ext = file_extension(path);
2729 auto it = user_data.find(ext);
2730 if (it != user_data.end()) {
return it->second.c_str(); }
2732 using udl::operator
""_t;
2734 switch (str2tag(ext)) {
2735 default:
return nullptr;
2736 case "css"_t:
return "text/css";
2737 case "csv"_t:
return "text/csv";
2738 case "txt"_t:
return "text/plain";
2739 case "vtt"_t:
return "text/vtt";
2741 case "html"_t:
return "text/html";
2743 case "apng"_t:
return "image/apng";
2744 case "avif"_t:
return "image/avif";
2745 case "bmp"_t:
return "image/bmp";
2746 case "gif"_t:
return "image/gif";
2747 case "png"_t:
return "image/png";
2748 case "svg"_t:
return "image/svg+xml";
2749 case "webp"_t:
return "image/webp";
2750 case "ico"_t:
return "image/x-icon";
2751 case "tif"_t:
return "image/tiff";
2752 case "tiff"_t:
return "image/tiff";
2754 case "jpeg"_t:
return "image/jpeg";
2756 case "mp4"_t:
return "video/mp4";
2757 case "mpeg"_t:
return "video/mpeg";
2758 case "webm"_t:
return "video/webm";
2760 case "mp3"_t:
return "audio/mp3";
2761 case "mpga"_t:
return "audio/mpeg";
2762 case "weba"_t:
return "audio/webm";
2763 case "wav"_t:
return "audio/wave";
2765 case "otf"_t:
return "font/otf";
2766 case "ttf"_t:
return "font/ttf";
2767 case "woff"_t:
return "font/woff";
2768 case "woff2"_t:
return "font/woff2";
2770 case "7z"_t:
return "application/x-7z-compressed";
2771 case "atom"_t:
return "application/atom+xml";
2772 case "pdf"_t:
return "application/pdf";
2774 case "mjs"_t:
return "application/javascript";
2775 case "json"_t:
return "application/json";
2776 case "rss"_t:
return "application/rss+xml";
2777 case "tar"_t:
return "application/x-tar";
2779 case "xhtml"_t:
return "application/xhtml+xml";
2780 case "xslt"_t:
return "application/xslt+xml";
2781 case "xml"_t:
return "application/xml";
2782 case "gz"_t:
return "application/gzip";
2783 case "zip"_t:
return "application/zip";
2784 case "wasm"_t:
return "application/wasm";
2788 inline const char *status_message(
int status) {
2790 case 100:
return "Continue";
2791 case 101:
return "Switching Protocol";
2792 case 102:
return "Processing";
2793 case 103:
return "Early Hints";
2794 case 200:
return "OK";
2795 case 201:
return "Created";
2796 case 202:
return "Accepted";
2797 case 203:
return "Non-Authoritative Information";
2798 case 204:
return "No Content";
2799 case 205:
return "Reset Content";
2800 case 206:
return "Partial Content";
2801 case 207:
return "Multi-Status";
2802 case 208:
return "Already Reported";
2803 case 226:
return "IM Used";
2804 case 300:
return "Multiple Choice";
2805 case 301:
return "Moved Permanently";
2806 case 302:
return "Found";
2807 case 303:
return "See Other";
2808 case 304:
return "Not Modified";
2809 case 305:
return "Use Proxy";
2810 case 306:
return "unused";
2811 case 307:
return "Temporary Redirect";
2812 case 308:
return "Permanent Redirect";
2813 case 400:
return "Bad Request";
2814 case 401:
return "Unauthorized";
2815 case 402:
return "Payment Required";
2816 case 403:
return "Forbidden";
2817 case 404:
return "Not Found";
2818 case 405:
return "Method Not Allowed";
2819 case 406:
return "Not Acceptable";
2820 case 407:
return "Proxy Authentication Required";
2821 case 408:
return "Request Timeout";
2822 case 409:
return "Conflict";
2823 case 410:
return "Gone";
2824 case 411:
return "Length Required";
2825 case 412:
return "Precondition Failed";
2826 case 413:
return "Payload Too Large";
2827 case 414:
return "URI Too Long";
2828 case 415:
return "Unsupported Media Type";
2829 case 416:
return "Range Not Satisfiable";
2830 case 417:
return "Expectation Failed";
2831 case 418:
return "I'm a teapot";
2832 case 421:
return "Misdirected Request";
2833 case 422:
return "Unprocessable Entity";
2834 case 423:
return "Locked";
2835 case 424:
return "Failed Dependency";
2836 case 425:
return "Too Early";
2837 case 426:
return "Upgrade Required";
2838 case 428:
return "Precondition Required";
2839 case 429:
return "Too Many Requests";
2840 case 431:
return "Request Header Fields Too Large";
2841 case 451:
return "Unavailable For Legal Reasons";
2842 case 501:
return "Not Implemented";
2843 case 502:
return "Bad Gateway";
2844 case 503:
return "Service Unavailable";
2845 case 504:
return "Gateway Timeout";
2846 case 505:
return "HTTP Version Not Supported";
2847 case 506:
return "Variant Also Negotiates";
2848 case 507:
return "Insufficient Storage";
2849 case 508:
return "Loop Detected";
2850 case 510:
return "Not Extended";
2851 case 511:
return "Network Authentication Required";
2854 case 500:
return "Internal Server Error";
2858 inline bool can_compress_content_type(
const std::string &content_type) {
2859 return (!content_type.find(
"text/") && content_type !=
"text/event-stream") ||
2860 content_type ==
"image/svg+xml" ||
2861 content_type ==
"application/javascript" ||
2862 content_type ==
"application/json" ||
2863 content_type ==
"application/xml" ||
2864 content_type ==
"application/protobuf" ||
2865 content_type ==
"application/xhtml+xml";
2868 inline EncodingType encoding_type(
const Request &req,
const Response &res) {
2870 detail::can_compress_content_type(res.get_header_value(
"Content-Type"));
2871 if (!ret) {
return EncodingType::None; }
2873 const auto &s = req.get_header_value(
"Accept-Encoding");
2876 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
2878 ret = s.find(
"br") != std::string::npos;
2879 if (ret) {
return EncodingType::Brotli; }
2882 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2884 ret = s.find(
"gzip") != std::string::npos;
2885 if (ret) {
return EncodingType::Gzip; }
2888 return EncodingType::None;
2891 inline bool nocompressor::compress(
const char *data,
size_t data_length,
2892 bool , Callback callback) {
2893 if (!data_length) {
return true; }
2894 return callback(data, data_length);
2897 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2898 inline gzip_compressor::gzip_compressor() {
2899 std::memset(&strm_, 0,
sizeof(strm_));
2900 strm_.zalloc = Z_NULL;
2901 strm_.zfree = Z_NULL;
2902 strm_.opaque = Z_NULL;
2904 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
2905 Z_DEFAULT_STRATEGY) == Z_OK;
2908 inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
2910 inline bool gzip_compressor::compress(
const char *data,
size_t data_length,
2911 bool last, Callback callback) {
2915 constexpr
size_t max_avail_in =
2916 (std::numeric_limits<decltype(strm_.avail_in)>::max)();
2918 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
2919 (std::min)(data_length, max_avail_in));
2920 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
2922 data_length -= strm_.avail_in;
2923 data += strm_.avail_in;
2925 auto flush = (
last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
2928 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
2930 strm_.avail_out =
static_cast<uInt
>(buff.size());
2931 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
2933 ret = deflate(&strm_, flush);
2934 if (ret == Z_STREAM_ERROR) {
return false; }
2936 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
2939 }
while (strm_.avail_out == 0);
2941 assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
2942 (flush == Z_NO_FLUSH && ret == Z_OK));
2943 assert(strm_.avail_in == 0);
2945 }
while (data_length > 0);
2950 inline gzip_decompressor::gzip_decompressor() {
2951 std::memset(&strm_, 0,
sizeof(strm_));
2952 strm_.zalloc = Z_NULL;
2953 strm_.zfree = Z_NULL;
2954 strm_.opaque = Z_NULL;
2960 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
2963 inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
2965 inline bool gzip_decompressor::is_valid()
const {
return is_valid_; }
2967 inline bool gzip_decompressor::decompress(
const char *data,
size_t data_length,
2968 Callback callback) {
2974 constexpr
size_t max_avail_in =
2975 (std::numeric_limits<decltype(strm_.avail_in)>::max)();
2977 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
2978 (std::min)(data_length, max_avail_in));
2979 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
2981 data_length -= strm_.avail_in;
2982 data += strm_.avail_in;
2984 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
2985 while (strm_.avail_in > 0) {
2986 strm_.avail_out =
static_cast<uInt
>(buff.size());
2987 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
2989 auto prev_avail_in = strm_.avail_in;
2991 ret = inflate(&strm_, Z_NO_FLUSH);
2993 if (prev_avail_in - strm_.avail_in == 0) {
return false; }
2995 assert(ret != Z_STREAM_ERROR);
2999 case Z_MEM_ERROR: inflateEnd(&strm_);
return false;
3002 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
3007 if (ret != Z_OK && ret != Z_STREAM_END)
return false;
3009 }
while (data_length > 0);
3015 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
3016 inline brotli_compressor::brotli_compressor() {
3017 state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr);
3020 inline brotli_compressor::~brotli_compressor() {
3021 BrotliEncoderDestroyInstance(state_);
3024 inline bool brotli_compressor::compress(
const char *data,
size_t data_length,
3025 bool last, Callback callback) {
3026 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3028 auto operation =
last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
3029 auto available_in = data_length;
3030 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
3034 if (BrotliEncoderIsFinished(state_)) {
break; }
3036 if (!available_in) {
break; }
3039 auto available_out = buff.size();
3040 auto next_out = buff.data();
3042 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
3043 &available_out, &next_out,
nullptr)) {
3047 auto output_bytes = buff.size() - available_out;
3049 callback(
reinterpret_cast<const char *
>(buff.data()), output_bytes);
3056 inline brotli_decompressor::brotli_decompressor() {
3057 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
3058 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
3059 : BROTLI_DECODER_RESULT_ERROR;
3062 inline brotli_decompressor::~brotli_decompressor() {
3063 if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
3066 inline bool brotli_decompressor::is_valid()
const {
return decoder_s; }
3068 inline bool brotli_decompressor::decompress(
const char *data,
3070 Callback callback) {
3071 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
3072 decoder_r == BROTLI_DECODER_RESULT_ERROR) {
3076 const uint8_t *next_in = (
const uint8_t *)data;
3077 size_t avail_in = data_length;
3080 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
3082 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3083 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
3084 char *next_out = buff.data();
3085 size_t avail_out = buff.size();
3087 decoder_r = BrotliDecoderDecompressStream(
3088 decoder_s, &avail_in, &next_in, &avail_out,
3089 reinterpret_cast<uint8_t **
>(&next_out), &total_out);
3091 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) {
return false; }
3093 if (!callback(buff.data(), buff.size() - avail_out)) {
return false; }
3096 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
3097 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
3101 inline bool has_header(
const Headers &headers,
const char *key) {
3102 return headers.find(key) != headers.end();
3105 inline const char *get_header_value(
const Headers &headers,
const char *key,
3106 size_t id,
const char *def) {
3107 auto rng = headers.equal_range(key);
3108 auto it = rng.first;
3109 std::advance(it,
static_cast<ssize_t>(
id));
3110 if (it != rng.second) {
return it->second.c_str(); }
3114 template <
typename T>
3115 inline bool parse_header(
const char *beg,
const char *end, T fn) {
3117 while (beg < end && is_space_or_tab(end[-1])) {
3122 while (p < end && *p !=
':') {
3126 if (p == end) {
return false; }
3130 if (*p++ !=
':') {
return false; }
3132 while (p < end && is_space_or_tab(*p)) {
3137 fn(std::string(beg, key_end), decode_url(std::string(p, end),
false));
3144 inline bool read_headers(Stream &strm, Headers &headers) {
3145 const auto bufsiz = 2048;
3147 stream_line_reader line_reader(strm,
buf, bufsiz);
3150 if (!line_reader.getline()) {
return false; }
3153 if (line_reader.end_with_crlf()) {
3155 if (line_reader.size() == 2) {
break; }
3161 auto end = line_reader.ptr() + line_reader.size() - 2;
3163 parse_header(line_reader.ptr(), end,
3164 [&](std::string &&key, std::string &&val) {
3165 headers.emplace(std::move(key), std::move(val));
3172 inline bool read_content_with_length(Stream &strm, uint64_t
len,
3174 ContentReceiverWithProgress out) {
3175 char buf[CPPHTTPLIB_RECV_BUFSIZ];
3179 auto read_len =
static_cast<size_t>(
len - r);
3180 auto n = strm.read(
buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
3181 if (
n <= 0) {
return false; }
3183 if (!out(
buf,
static_cast<size_t>(
n), r,
len)) {
return false; }
3184 r +=
static_cast<uint64_t
>(
n);
3187 if (!progress(r,
len)) {
return false; }
3194 inline void skip_content_with_length(Stream &strm, uint64_t
len) {
3195 char buf[CPPHTTPLIB_RECV_BUFSIZ];
3198 auto read_len =
static_cast<size_t>(
len - r);
3199 auto n = strm.read(
buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
3200 if (
n <= 0) {
return; }
3201 r +=
static_cast<uint64_t
>(
n);
3205 inline bool read_content_without_length(Stream &strm,
3206 ContentReceiverWithProgress out) {
3207 char buf[CPPHTTPLIB_RECV_BUFSIZ];
3210 auto n = strm.read(
buf, CPPHTTPLIB_RECV_BUFSIZ);
3213 }
else if (
n == 0) {
3217 if (!out(
buf,
static_cast<size_t>(
n), r, 0)) {
return false; }
3218 r +=
static_cast<uint64_t
>(
n);
3224 inline bool read_content_chunked(Stream &strm,
3225 ContentReceiverWithProgress out) {
3226 const auto bufsiz = 16;
3229 stream_line_reader line_reader(strm,
buf, bufsiz);
3231 if (!line_reader.getline()) {
return false; }
3233 unsigned long chunk_len;
3237 chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
3239 if (end_ptr == line_reader.ptr()) {
return false; }
3240 if (chunk_len == ULONG_MAX) {
return false; }
3242 if (chunk_len == 0) {
break; }
3244 if (!read_content_with_length(strm, chunk_len,
nullptr, out)) {
3248 if (!line_reader.getline()) {
return false; }
3250 if (strcmp(line_reader.ptr(),
"\r\n")) {
break; }
3252 if (!line_reader.getline()) {
return false; }
3255 if (chunk_len == 0) {
3257 if (!line_reader.getline() || strcmp(line_reader.ptr(),
"\r\n"))
3264 inline bool is_chunked_transfer_encoding(
const Headers &headers) {
3265 return !strcasecmp(get_header_value(headers,
"Transfer-Encoding", 0,
""),
3269 template <
typename T,
typename U>
3270 bool prepare_content_receiver(T &x,
int &status,
3271 ContentReceiverWithProgress receiver,
3272 bool decompress, U callback) {
3274 std::string encoding = x.get_header_value(
"Content-Encoding");
3275 std::unique_ptr<decompressor> decompressor;
3277 if (encoding ==
"gzip" || encoding ==
"deflate") {
3278 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
3279 decompressor = detail::make_unique<gzip_decompressor>();
3284 }
else if (encoding.find(
"br") != std::string::npos) {
3285 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
3286 decompressor = detail::make_unique<brotli_decompressor>();
3294 if (decompressor->is_valid()) {
3295 ContentReceiverWithProgress out = [&](
const char *
buf,
size_t n,
3296 uint64_t off, uint64_t
len) {
3297 return decompressor->decompress(
buf,
n,
3298 [&](
const char *buf2,
size_t n2) {
3299 return receiver(buf2, n2, off,
len);
3302 return callback(std::move(out));
3310 ContentReceiverWithProgress out = [&](
const char *
buf,
size_t n, uint64_t off,
3312 return receiver(
buf,
n, off,
len);
3314 return callback(std::move(out));
3317 template <
typename T>
3318 bool read_content(Stream &strm, T &x,
size_t payload_max_length,
int &status,
3319 Progress progress, ContentReceiverWithProgress receiver,
3321 return prepare_content_receiver(
3322 x, status, std::move(receiver), decompress,
3323 [&](
const ContentReceiverWithProgress &out) {
3325 auto exceed_payload_max_length =
false;
3327 if (is_chunked_transfer_encoding(x.headers)) {
3328 ret = read_content_chunked(strm, out);
3329 }
else if (!has_header(x.headers,
"Content-Length")) {
3330 ret = read_content_without_length(strm, out);
3332 auto len = get_header_value<uint64_t>(x.headers,
"Content-Length");
3333 if (len > payload_max_length) {
3334 exceed_payload_max_length = true;
3335 skip_content_with_length(strm, len);
3337 }
else if (
len > 0) {
3338 ret = read_content_with_length(strm,
len, std::move(progress), out);
3342 if (!ret) { status = exceed_payload_max_length ? 413 : 400; }
3347 inline ssize_t write_headers(Stream &strm,
const Headers &headers) {
3349 for (
const auto &x : headers) {
3351 strm.write_format(
"%s: %s\r\n", x.first.c_str(), x.second.c_str());
3352 if (
len < 0) {
return len; }
3355 auto len = strm.write(
"\r\n");
3356 if (
len < 0) {
return len; }
3361 inline bool write_data(Stream &strm,
const char *d,
size_t l) {
3363 while (offset < l) {
3364 auto length = strm.write(d + offset, l - offset);
3365 if (length < 0) {
return false; }
3366 offset +=
static_cast<size_t>(length);
3371 template <
typename T>
3372 inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
3373 size_t offset,
size_t length, T is_shutting_down,
3375 size_t end_offset = offset + length;
3379 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
3381 if (write_data(strm, d, l)) {
3390 data_sink.is_writable = [&](void) {
return ok && strm.is_writable(); };
3392 while (offset < end_offset && !is_shutting_down()) {
3393 if (!content_provider(offset, end_offset - offset, data_sink)) {
3394 error = Error::Canceled;
3398 error = Error::Write;
3403 error = Error::Success;
3407 template <
typename T>
3408 inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
3409 size_t offset,
size_t length,
3410 const T &is_shutting_down) {
3411 auto error = Error::Success;
3412 return write_content(strm, content_provider, offset, length, is_shutting_down,
3416 template <
typename T>
3418 write_content_without_length(Stream &strm,
3419 const ContentProvider &content_provider,
3420 const T &is_shutting_down) {
3422 auto data_available =
true;
3426 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
3429 if (!write_data(strm, d, l)) { ok =
false; }
3434 data_sink.done = [&](void) { data_available =
false; };
3436 data_sink.is_writable = [&](void) {
return ok && strm.is_writable(); };
3438 while (data_available && !is_shutting_down()) {
3439 if (!content_provider(offset, 0, data_sink)) {
return false; }
3440 if (!ok) {
return false; }
3445 template <
typename T,
typename U>
3447 write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
3448 const T &is_shutting_down, U &compressor, Error &error) {
3450 auto data_available =
true;
3454 data_sink.write = [&](
const char *d,
size_t l) ->
bool {
3456 data_available = l > 0;
3459 std::string payload;
3460 if (compressor.compress(d, l,
false,
3461 [&](
const char *data,
size_t data_len) {
3462 payload.append(data, data_len);
3465 if (!payload.empty()) {
3468 from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
3469 if (!write_data(strm, chunk.data(), chunk.size())) { ok =
false; }
3478 data_sink.done = [&](void) {
3479 if (!ok) {
return; }
3481 data_available =
false;
3483 std::string payload;
3484 if (!compressor.compress(
nullptr, 0,
true,
3485 [&](
const char *data,
size_t data_len) {
3486 payload.append(data, data_len);
3493 if (!payload.empty()) {
3495 auto chunk = from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
3496 if (!write_data(strm, chunk.data(), chunk.size())) {
3502 static const std::string done_marker(
"0\r\n\r\n");
3503 if (!write_data(strm, done_marker.data(), done_marker.size())) {
3508 data_sink.is_writable = [&](void) {
return ok && strm.is_writable(); };
3510 while (data_available && !is_shutting_down()) {
3511 if (!content_provider(offset, 0, data_sink)) {
3512 error = Error::Canceled;
3516 error = Error::Write;
3521 error = Error::Success;
3525 template <
typename T,
typename U>
3526 inline bool write_content_chunked(Stream &strm,
3527 const ContentProvider &content_provider,
3528 const T &is_shutting_down, U &compressor) {
3529 auto error = Error::Success;
3530 return write_content_chunked(strm, content_provider, is_shutting_down,
3534 template <
typename T>
3535 inline bool redirect(T &cli, Request &req, Response &res,
3536 const std::string &path,
const std::string &location,
3538 Request new_req = req;
3539 new_req.path = path;
3540 new_req.redirect_count_ -= 1;
3542 if (res.status == 303 && (req.method !=
"GET" && req.method !=
"HEAD")) {
3543 new_req.method =
"GET";
3544 new_req.body.clear();
3545 new_req.headers.clear();
3550 auto ret = cli.send(new_req, new_res, error);
3554 res.location = location;
3559 inline std::string params_to_query_str(
const Params ¶ms) {
3562 for (
auto it = params.begin(); it != params.end(); ++it) {
3563 if (it != params.begin()) { query +=
"&"; }
3566 query += encode_query_param(it->second);
3571 inline void parse_query_text(
const std::string &s, Params ¶ms) {
3572 std::set<std::string> cache;
3573 split(s.data(), s.data() + s.size(),
'&', [&](
const char *b,
const char *
e) {
3574 std::string kv(b, e);
3575 if (cache.find(kv) != cache.end()) { return; }
3580 split(b,
e,
'=', [&](
const char *b2,
const char *e2) {
3589 params.emplace(decode_url(key, true), decode_url(val, true));
3594 inline bool parse_multipart_boundary(
const std::string &content_type,
3595 std::string &boundary) {
3596 auto pos = content_type.find(
"boundary=");
3597 if (pos == std::string::npos) {
return false; }
3598 boundary = content_type.substr(pos + 9);
3599 if (boundary.length() >= 2 && boundary.front() ==
'"' &&
3600 boundary.back() ==
'"') {
3601 boundary = boundary.substr(1, boundary.size() - 2);
3603 return !boundary.empty();
3606 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
3607 inline bool parse_range_header(
const std::string &s, Ranges &ranges) {
3609 inline bool parse_range_header(
const std::string &s, Ranges &ranges)
try {
3611 static auto re_first_range = std::regex(R
"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
3613 if (std::regex_match(s, m, re_first_range)) {
3614 auto pos =
static_cast<size_t>(m.position(1));
3615 auto len =
static_cast<size_t>(m.length(1));
3616 bool all_valid_ranges =
true;
3617 split(&s[pos], &s[pos +
len],
',', [&](
const char *b,
const char *
e) {
3618 if (!all_valid_ranges)
return;
3619 static auto re_another_range = std::regex(R
"(\s*(\d*)-(\d*))");
3621 if (std::regex_match(b,
e, cm, re_another_range)) {
3623 if (!cm.str(1).empty()) {
3624 first =
static_cast<ssize_t>(std::stoll(cm.str(1)));
3628 if (!cm.str(2).empty()) {
3629 last =
static_cast<ssize_t>(std::stoll(cm.str(2)));
3632 if (first != -1 &&
last != -1 && first >
last) {
3633 all_valid_ranges =
false;
3636 ranges.emplace_back(std::make_pair(first,
last));
3639 return all_valid_ranges;
3642 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
3645 }
catch (...) {
return false; }
3648 class MultipartFormDataParser {
3650 MultipartFormDataParser() =
default;
3652 void set_boundary(std::string &&boundary) { boundary_ = boundary; }
3654 bool is_valid()
const {
return is_valid_; }
3656 bool parse(
const char *
buf,
size_t n,
const ContentReceiver &content_callback,
3657 const MultipartContentHeader &header_callback) {
3659 static const std::regex re_content_disposition(
3660 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
3662 std::regex_constants::icase);
3663 static const std::string dash_ =
"--";
3664 static const std::string crlf_ =
"\r\n";
3666 buf_.append(
buf,
n);
3668 while (!buf_.empty()) {
3671 auto pattern = dash_ + boundary_ + crlf_;
3672 if (pattern.size() > buf_.size()) {
return true; }
3673 auto pos = buf_.find(pattern);
3674 if (pos != 0) {
return false; }
3675 buf_.erase(0, pattern.size());
3676 off_ += pattern.size();
3686 auto pos = buf_.find(crlf_);
3687 while (pos != std::string::npos) {
3690 if (!header_callback(file_)) {
3694 buf_.erase(0, crlf_.size());
3695 off_ += crlf_.size();
3700 static const std::string header_name =
"content-type:";
3701 const auto header = buf_.substr(0, pos);
3702 if (start_with_case_ignore(header, header_name)) {
3703 file_.content_type = trim_copy(header.substr(header_name.size()));
3706 if (std::regex_match(header, m, re_content_disposition)) {
3708 file_.filename = m[2];
3712 buf_.erase(0, pos + crlf_.size());
3713 off_ += pos + crlf_.size();
3714 pos = buf_.find(crlf_);
3716 if (state_ != 3) {
return true; }
3721 auto pattern = crlf_ + dash_;
3722 if (pattern.size() > buf_.size()) {
return true; }
3724 auto pos = find_string(buf_, pattern);
3726 if (!content_callback(buf_.data(), pos)) {
3735 auto pattern = crlf_ + dash_ + boundary_;
3736 if (pattern.size() > buf_.size()) {
return true; }
3738 auto pos = buf_.find(pattern);
3739 if (pos != std::string::npos) {
3740 if (!content_callback(buf_.data(), pos)) {
3745 off_ += pos + pattern.size();
3746 buf_.erase(0, pos + pattern.size());
3749 if (!content_callback(buf_.data(), pattern.size())) {
3754 off_ += pattern.size();
3755 buf_.erase(0, pattern.size());
3761 if (crlf_.size() > buf_.size()) {
return true; }
3762 if (buf_.compare(0, crlf_.size(), crlf_) == 0) {
3763 buf_.erase(0, crlf_.size());
3764 off_ += crlf_.size();
3767 auto pattern = dash_ + crlf_;
3768 if (pattern.size() > buf_.size()) {
return true; }
3769 if (buf_.compare(0, pattern.size(), pattern) == 0) {
3770 buf_.erase(0, pattern.size());
3771 off_ += pattern.size();
3791 void clear_file_info() {
3793 file_.filename.clear();
3794 file_.content_type.clear();
3797 bool start_with_case_ignore(
const std::string &a,
3798 const std::string &b)
const {
3799 if (a.size() < b.size()) {
return false; }
3800 for (
size_t i = 0; i < b.size(); i++) {
3801 if (::tolower(a[i]) != ::tolower(b[i])) {
return false; }
3806 bool start_with(
const std::string &a,
size_t off,
3807 const std::string &b)
const {
3808 if (a.size() - off < b.size()) {
return false; }
3809 for (
size_t i = 0; i < b.size(); i++) {
3810 if (a[i + off] != b[i]) {
return false; }
3815 size_t find_string(
const std::string &s,
const std::string &pattern)
const {
3816 auto c = pattern.front();
3819 while (off < s.size()) {
3820 auto pos = s.find(c, off);
3821 if (pos == std::string::npos) {
return s.size(); }
3823 auto rem = s.size() - pos;
3824 if (pattern.size() > rem) {
return pos; }
3826 if (start_with(s, pos, pattern)) {
return pos; }
3834 std::string boundary_;
3838 bool is_valid_ =
false;
3840 MultipartFormData file_;
3843 inline std::string to_lower(
const char *beg,
const char *end) {
3847 out +=
static_cast<char>(::tolower(*it));
3853 inline std::string make_multipart_data_boundary() {
3854 static const char data[] =
3855 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3860 std::random_device seed_gen;
3862 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
3863 std::mt19937 engine(seed_sequence);
3865 std::string result =
"--cpp-httplib-multipart-data-";
3867 for (
auto i = 0; i < 16; i++) {
3868 result += data[engine() % (
sizeof(data) - 1)];
3874 inline std::pair<size_t, size_t>
3875 get_range_offset_and_length(
const Request &req,
size_t content_length,
3877 auto r = req.ranges[index];
3879 if (r.first == -1 && r.second == -1) {
3880 return std::make_pair(0, content_length);
3883 auto slen =
static_cast<ssize_t>(content_length);
3885 if (r.first == -1) {
3886 r.first = (std::max)(
static_cast<ssize_t>(0), slen - r.second);
3887 r.second = slen - 1;
3890 if (r.second == -1) { r.second = slen - 1; }
3891 return std::make_pair(r.first,
static_cast<size_t>(r.second - r.first) + 1);
3894 inline std::string make_content_range_header_field(
size_t offset,
size_t length,
3895 size_t content_length) {
3896 std::string field =
"bytes ";
3905 template <
typename SToken,
typename CToken,
typename Content>
3906 bool process_multipart_ranges_data(
const Request &req, Response &res,
3907 const std::string &boundary,
3908 const std::string &content_type,
3909 SToken stoken, CToken ctoken,
3911 for (
size_t i = 0; i < req.ranges.size(); i++) {
3915 if (!content_type.empty()) {
3916 ctoken(
"Content-Type: ");
3917 stoken(content_type);
3921 auto offsets = get_range_offset_and_length(req, res.body.size(), i);
3922 auto offset = offsets.first;
3923 auto length = offsets.second;
3925 ctoken(
"Content-Range: ");
3926 stoken(make_content_range_header_field(offset, length, res.body.size()));
3929 if (!content(offset, length)) {
return false; }
3940 inline bool make_multipart_ranges_data(
const Request &req, Response &res,
3941 const std::string &boundary,
3942 const std::string &content_type,
3943 std::string &data) {
3944 return process_multipart_ranges_data(
3945 req, res, boundary, content_type,
3946 [&](
const std::string &token) { data += token; },
3947 [&](
const char *token) { data += token; },
3948 [&](
size_t offset,
size_t length) {
3949 if (offset < res.body.size()) {
3950 data += res.body.substr(offset, length);
3958 get_multipart_ranges_data_length(
const Request &req, Response &res,
3959 const std::string &boundary,
3960 const std::string &content_type) {
3961 size_t data_length = 0;
3963 process_multipart_ranges_data(
3964 req, res, boundary, content_type,
3965 [&](
const std::string &token) { data_length += token.size(); },
3966 [&](
const char *token) { data_length += strlen(token); },
3967 [&](
size_t ,
size_t length) {
3968 data_length += length;
3975 template <
typename T>
3976 inline bool write_multipart_ranges_data(Stream &strm,
const Request &req,
3978 const std::string &boundary,
3979 const std::string &content_type,
3980 const T &is_shutting_down) {
3981 return process_multipart_ranges_data(
3982 req, res, boundary, content_type,
3983 [&](
const std::string &token) { strm.write(token); },
3984 [&](
const char *token) { strm.write(token); },
3985 [&](
size_t offset,
size_t length) {
3986 return write_content(strm, res.content_provider_, offset, length,
3991 inline std::pair<size_t, size_t>
3992 get_range_offset_and_length(
const Request &req,
const Response &res,
3994 auto r = req.ranges[index];
3996 if (r.second == -1) {
3997 r.second =
static_cast<ssize_t>(res.content_length_) - 1;
4000 return std::make_pair(r.first, r.second - r.first + 1);
4003 inline bool expect_content(
const Request &req) {
4004 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH" ||
4005 req.method ==
"PRI" || req.method ==
"DELETE") {
4012 inline bool has_crlf(
const char *s) {
4015 if (*p ==
'\r' || *p ==
'\n') {
return true; }
4021 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4022 template <
typename CTX,
typename Init,
typename Update,
typename Final>
4023 inline std::string message_digest(
const std::string &s, Init init,
4024 Update update, Final
final,
4025 size_t digest_length) {
4026 std::vector<unsigned char> md(digest_length, 0);
4029 update(&ctx, s.data(), s.size());
4030 final(md.data(), &ctx);
4032 std::stringstream ss;
4034 ss << std::setfill(
'0') << std::setw(2) << std::hex << (
unsigned int)c;
4039 inline std::string MD5(
const std::string &s) {
4040 return message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final,
4044 inline std::string SHA_256(
const std::string &s) {
4045 return message_digest<SHA256_CTX>(s, SHA256_Init, SHA256_Update, SHA256_Final,
4046 SHA256_DIGEST_LENGTH);
4049 inline std::string SHA_512(
const std::string &s) {
4050 return message_digest<SHA512_CTX>(s, SHA512_Init, SHA512_Update, SHA512_Final,
4051 SHA512_DIGEST_LENGTH);
4056 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4059 inline bool load_system_certs_on_windows(X509_STORE *store) {
4060 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L
"ROOT");
4062 if (!hStore) {
return false; }
4064 PCCERT_CONTEXT pContext = NULL;
4065 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
4068 static_cast<const unsigned char *
>(pContext->pbCertEncoded);
4070 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
4072 X509_STORE_add_cert(store, x509);
4077 CertFreeCertificateContext(pContext);
4078 CertCloseStore(hStore, 0);
4088 WSAStartup(0x0002, &wsaData);
4091 ~WSInit() { WSACleanup(); }
4094 static WSInit wsinit_;
4097 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4098 inline std::pair<std::string, std::string> make_digest_authentication_header(
4099 const Request &req,
const std::map<std::string, std::string> &auth,
4100 size_t cnonce_count,
const std::string &cnonce,
const std::string &username,
4101 const std::string &password,
bool is_proxy =
false) {
4104 std::stringstream ss;
4105 ss << std::setfill(
'0') << std::setw(8) << std::hex << cnonce_count;
4110 if (auth.find(
"qop") != auth.end()) {
4111 qop = auth.at(
"qop");
4112 if (qop.find(
"auth-int") != std::string::npos) {
4114 }
else if (qop.find(
"auth") != std::string::npos) {
4121 std::string algo =
"MD5";
4122 if (auth.find(
"algorithm") != auth.end()) { algo = auth.at(
"algorithm"); }
4124 std::string response;
4126 auto H = algo ==
"SHA-256" ? detail::SHA_256
4127 : algo ==
"SHA-512" ? detail::SHA_512
4130 auto A1 = username +
":" + auth.at(
"realm") +
":" + password;
4132 auto A2 = req.method +
":" + req.path;
4133 if (qop ==
"auth-int") { A2 +=
":" + H(req.body); }
4136 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + H(A2));
4138 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + nc +
":" + cnonce +
4139 ":" + qop +
":" + H(A2));
4144 "Digest username=\"" + username +
"\", realm=\"" + auth.at(
"realm") +
4145 "\", nonce=\"" + auth.at(
"nonce") +
"\", uri=\"" + req.path +
4146 "\", algorithm=" + algo +
4147 (qop.empty() ?
", response=\""
4148 :
", qop=" + qop +
", nc=\"" + nc +
"\", cnonce=\"" +
4149 cnonce +
"\", response=\"") +
4152 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
4153 return std::make_pair(key, field);
4157 inline bool parse_www_authenticate(
const Response &res,
4158 std::map<std::string, std::string> &auth,
4160 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
4161 if (res.has_header(auth_key)) {
4162 static auto re = std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
4163 auto s = res.get_header_value(auth_key);
4164 auto pos = s.find(
' ');
4165 if (pos != std::string::npos) {
4166 auto type = s.substr(0, pos);
4167 if (type ==
"Basic") {
4169 }
else if (type ==
"Digest") {
4170 s = s.substr(pos + 1);
4171 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
4172 for (
auto i = beg; i != std::sregex_iterator(); ++i) {
4174 auto key = s.substr(
static_cast<size_t>(m.position(1)),
4175 static_cast<size_t>(m.length(1)));
4176 auto val = m.length(2) > 0
4177 ? s.substr(
static_cast<size_t>(m.position(2)),
4178 static_cast<size_t>(m.length(2)))
4179 : s.substr(
static_cast<size_t>(m.position(3)),
4180 static_cast<size_t>(m.length(3)));
4191 inline std::string random_string(
size_t length) {
4192 auto randchar = []() ->
char {
4193 const char charset[] =
"0123456789"
4194 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
4195 "abcdefghijklmnopqrstuvwxyz";
4196 const size_t max_index = (
sizeof(charset) - 1);
4197 return charset[
static_cast<size_t>(std::rand()) % max_index];
4199 std::string str(length, 0);
4200 std::generate_n(str.begin(), length, randchar);
4204 class ContentProviderAdapter {
4206 explicit ContentProviderAdapter(
4207 ContentProviderWithoutLength &&content_provider)
4208 : content_provider_(content_provider) {}
4210 bool operator()(
size_t offset,
size_t, DataSink &sink) {
4211 return content_provider_(offset, sink);
4215 ContentProviderWithoutLength content_provider_;
4220 inline std::string append_query_params(
const char *path,
const Params ¶ms) {
4221 std::string path_with_query = path;
4222 const static std::regex re(
"[^?]+\\?.*");
4223 auto delm = std::regex_match(path, re) ?
'&' :
'?';
4224 path_with_query += delm + detail::params_to_query_str(params);
4225 return path_with_query;
4229 inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
4230 std::string field =
"bytes=";
4232 for (
auto r : ranges) {
4233 if (i != 0) { field +=
", "; }
4239 return std::make_pair(
"Range", std::move(field));
4242 inline std::pair<std::string, std::string>
4243 make_basic_authentication_header(
const std::string &username,
4244 const std::string &password,
bool is_proxy) {
4245 auto field =
"Basic " + detail::base64_encode(username +
":" + password);
4246 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
4247 return std::make_pair(key, std::move(field));
4250 inline std::pair<std::string, std::string>
4251 make_bearer_token_authentication_header(
const std::string &token,
4252 bool is_proxy =
false) {
4253 auto field =
"Bearer " + token;
4254 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
4255 return std::make_pair(key, std::move(field));
4259 inline bool Request::has_header(
const char *key)
const {
4260 return detail::has_header(headers, key);
4263 inline std::string Request::get_header_value(
const char *key,
size_t id)
const {
4264 return detail::get_header_value(headers, key,
id,
"");
4267 inline size_t Request::get_header_value_count(
const char *key)
const {
4268 auto r = headers.equal_range(key);
4269 return static_cast<size_t>(std::distance(r.first, r.second));
4272 inline void Request::set_header(
const char *key,
const char *val) {
4273 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
4274 headers.emplace(key, val);
4278 inline void Request::set_header(
const char *key,
const std::string &val) {
4279 if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
4280 headers.emplace(key, val);
4284 inline bool Request::has_param(
const char *key)
const {
4285 return params.find(key) != params.end();
4288 inline std::string Request::get_param_value(
const char *key,
size_t id)
const {
4289 auto rng = params.equal_range(key);
4290 auto it = rng.first;
4291 std::advance(it,
static_cast<ssize_t>(
id));
4292 if (it != rng.second) {
return it->second; }
4293 return std::string();
4296 inline size_t Request::get_param_value_count(
const char *key)
const {
4297 auto r = params.equal_range(key);
4298 return static_cast<size_t>(std::distance(r.first, r.second));
4301 inline bool Request::is_multipart_form_data()
const {
4302 const auto &content_type = get_header_value(
"Content-Type");
4303 return !content_type.find(
"multipart/form-data");
4306 inline bool Request::has_file(
const char *key)
const {
4307 return files.find(key) != files.end();
4310 inline MultipartFormData Request::get_file_value(
const char *key)
const {
4311 auto it = files.find(key);
4312 if (it != files.end()) {
return it->second; }
4313 return MultipartFormData();
4317 inline bool Response::has_header(
const char *key)
const {
4318 return headers.find(key) != headers.end();
4321 inline std::string Response::get_header_value(
const char *key,
4323 return detail::get_header_value(headers, key,
id,
"");
4326 inline size_t Response::get_header_value_count(
const char *key)
const {
4327 auto r = headers.equal_range(key);
4328 return static_cast<size_t>(std::distance(r.first, r.second));
4331 inline void Response::set_header(
const char *key,
const char *val) {
4332 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
4333 headers.emplace(key, val);
4337 inline void Response::set_header(
const char *key,
const std::string &val) {
4338 if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
4339 headers.emplace(key, val);
4343 inline void Response::set_redirect(
const char *url,
int stat) {
4344 if (!detail::has_crlf(url)) {
4345 set_header(
"Location", url);
4346 if (300 <= stat && stat < 400) {
4347 this->status = stat;
4354 inline void Response::set_redirect(
const std::string &url,
int stat) {
4355 set_redirect(url.c_str(), stat);
4358 inline void Response::set_content(
const char *s,
size_t n,
4359 const char *content_type) {
4362 auto rng = headers.equal_range(
"Content-Type");
4363 headers.erase(rng.first, rng.second);
4364 set_header(
"Content-Type", content_type);
4367 inline void Response::set_content(
const std::string &s,
4368 const char *content_type) {
4369 set_content(s.data(), s.size(), content_type);
4372 inline void Response::set_content_provider(
4373 size_t in_length,
const char *content_type, ContentProvider provider,
4374 ContentProviderResourceReleaser resource_releaser) {
4375 assert(in_length > 0);
4376 set_header(
"Content-Type", content_type);
4377 content_length_ = in_length;
4378 content_provider_ = std::move(provider);
4379 content_provider_resource_releaser_ = resource_releaser;
4380 is_chunked_content_provider_ =
false;
4383 inline void Response::set_content_provider(
4384 const char *content_type, ContentProviderWithoutLength provider,
4385 ContentProviderResourceReleaser resource_releaser) {
4386 set_header(
"Content-Type", content_type);
4387 content_length_ = 0;
4388 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
4389 content_provider_resource_releaser_ = resource_releaser;
4390 is_chunked_content_provider_ =
false;
4393 inline void Response::set_chunked_content_provider(
4394 const char *content_type, ContentProviderWithoutLength provider,
4395 ContentProviderResourceReleaser resource_releaser) {
4396 set_header(
"Content-Type", content_type);
4397 content_length_ = 0;
4398 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
4399 content_provider_resource_releaser_ = resource_releaser;
4400 is_chunked_content_provider_ =
true;
4404 inline bool Result::has_request_header(
const char *key)
const {
4405 return request_headers_.find(key) != request_headers_.end();
4408 inline std::string Result::get_request_header_value(
const char *key,
4410 return detail::get_header_value(request_headers_, key,
id,
"");
4413 inline size_t Result::get_request_header_value_count(
const char *key)
const {
4414 auto r = request_headers_.equal_range(key);
4415 return static_cast<size_t>(std::distance(r.first, r.second));
4419 inline ssize_t Stream::write(
const char *ptr) {
4420 return write(ptr, strlen(ptr));
4423 inline ssize_t Stream::write(
const std::string &s) {
4424 return write(s.data(), s.size());
4430 inline SocketStream::SocketStream(
socket_t sock, time_t read_timeout_sec,
4431 time_t read_timeout_usec,
4432 time_t write_timeout_sec,
4433 time_t write_timeout_usec)
4434 : sock_(sock), read_timeout_sec_(read_timeout_sec),
4435 read_timeout_usec_(read_timeout_usec),
4436 write_timeout_sec_(write_timeout_sec),
4437 write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {}
4439 inline SocketStream::~SocketStream() {}
4441 inline bool SocketStream::is_readable()
const {
4442 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
4445 inline bool SocketStream::is_writable()
const {
4446 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
4449 inline ssize_t SocketStream::read(
char *ptr,
size_t size) {
4452 (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
4454 size = (std::min)(size,
4455 static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
4458 if (read_buff_off_ < read_buff_content_size_) {
4459 auto remaining_size = read_buff_content_size_ - read_buff_off_;
4460 if (size <= remaining_size) {
4461 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
4462 read_buff_off_ += size;
4463 return static_cast<ssize_t>(size);
4465 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
4466 read_buff_off_ += remaining_size;
4467 return static_cast<ssize_t>(remaining_size);
4471 if (!is_readable()) {
return -1; }
4474 read_buff_content_size_ = 0;
4476 if (size < read_buff_size_) {
4477 auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
4478 CPPHTTPLIB_RECV_FLAGS);
4481 }
else if (
n <=
static_cast<ssize_t>(size)) {
4482 memcpy(ptr, read_buff_.data(),
static_cast<size_t>(
n));
4485 memcpy(ptr, read_buff_.data(), size);
4486 read_buff_off_ = size;
4487 read_buff_content_size_ =
static_cast<size_t>(
n);
4488 return static_cast<ssize_t>(size);
4491 return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
4495 inline ssize_t SocketStream::write(
const char *ptr,
size_t size) {
4496 if (!is_writable()) {
return -1; }
4500 (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
4503 return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
4506 inline void SocketStream::get_remote_ip_and_port(std::string &ip,
4508 return detail::get_remote_ip_and_port(sock_, ip, port);
4511 inline socket_t SocketStream::socket()
const {
return sock_; }
4514 inline bool BufferStream::is_readable()
const {
return true; }
4516 inline bool BufferStream::is_writable()
const {
return true; }
4518 inline ssize_t BufferStream::read(
char *ptr,
size_t size) {
4519 #if defined(_MSC_VER) && _MSC_VER <= 1900
4520 auto len_read = buffer._Copy_s(ptr, size, size, position);
4522 auto len_read = buffer.copy(ptr, size, position);
4524 position +=
static_cast<size_t>(len_read);
4525 return static_cast<ssize_t>(len_read);
4528 inline ssize_t BufferStream::write(
const char *ptr,
size_t size) {
4529 buffer.append(ptr, size);
4530 return static_cast<ssize_t>(size);
4533 inline void BufferStream::get_remote_ip_and_port(std::string & ,
4536 inline socket_t BufferStream::socket()
const {
return 0; }
4538 inline const std::string &BufferStream::get_buffer()
const {
return buffer; }
4543 inline Server::Server()
4545 [] {
return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }),
4546 svr_sock_(INVALID_SOCKET), is_running_(
false) {
4548 signal(SIGPIPE, SIG_IGN);
4552 inline Server::~Server() {}
4554 inline Server &Server::Get(
const std::string &pattern, Handler handler) {
4555 get_handlers_.push_back(
4556 std::make_pair(std::regex(pattern), std::move(handler)));
4560 inline Server &Server::Post(
const std::string &pattern, Handler handler) {
4561 post_handlers_.push_back(
4562 std::make_pair(std::regex(pattern), std::move(handler)));
4566 inline Server &Server::Post(
const std::string &pattern,
4567 HandlerWithContentReader handler) {
4568 post_handlers_for_content_reader_.push_back(
4569 std::make_pair(std::regex(pattern), std::move(handler)));
4573 inline Server &Server::Put(
const std::string &pattern, Handler handler) {
4574 put_handlers_.push_back(
4575 std::make_pair(std::regex(pattern), std::move(handler)));
4579 inline Server &Server::Put(
const std::string &pattern,
4580 HandlerWithContentReader handler) {
4581 put_handlers_for_content_reader_.push_back(
4582 std::make_pair(std::regex(pattern), std::move(handler)));
4586 inline Server &Server::Patch(
const std::string &pattern, Handler handler) {
4587 patch_handlers_.push_back(
4588 std::make_pair(std::regex(pattern), std::move(handler)));
4592 inline Server &Server::Patch(
const std::string &pattern,
4593 HandlerWithContentReader handler) {
4594 patch_handlers_for_content_reader_.push_back(
4595 std::make_pair(std::regex(pattern), std::move(handler)));
4599 inline Server &Server::Delete(
const std::string &pattern, Handler handler) {
4600 delete_handlers_.push_back(
4601 std::make_pair(std::regex(pattern), std::move(handler)));
4605 inline Server &Server::Delete(
const std::string &pattern,
4606 HandlerWithContentReader handler) {
4607 delete_handlers_for_content_reader_.push_back(
4608 std::make_pair(std::regex(pattern), std::move(handler)));
4612 inline Server &Server::Options(
const std::string &pattern, Handler handler) {
4613 options_handlers_.push_back(
4614 std::make_pair(std::regex(pattern), std::move(handler)));
4618 inline bool Server::set_base_dir(
const std::string &dir,
4619 const std::string &mount_point) {
4620 return set_mount_point(mount_point, dir);
4623 inline bool Server::set_mount_point(
const std::string &mount_point,
4624 const std::string &dir, Headers headers) {
4625 if (detail::is_dir(dir)) {
4626 std::string mnt = !mount_point.empty() ? mount_point :
"/";
4627 if (!mnt.empty() && mnt[0] ==
'/') {
4628 base_dirs_.push_back({mnt, dir, std::move(headers)});
4635 inline bool Server::remove_mount_point(
const std::string &mount_point) {
4636 for (
auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
4637 if (it->mount_point == mount_point) {
4638 base_dirs_.erase(it);
4646 Server::set_file_extension_and_mimetype_mapping(
const char *ext,
4648 file_extension_and_mimetype_map_[ext] = mime;
4652 inline Server &Server::set_file_request_handler(Handler handler) {
4653 file_request_handler_ = std::move(handler);
4657 inline Server &Server::set_error_handler(HandlerWithResponse handler) {
4658 error_handler_ = std::move(handler);
4662 inline Server &Server::set_error_handler(Handler handler) {
4663 error_handler_ = [handler](
const Request &req, Response &res) {
4665 return HandlerResponse::Handled;
4670 inline Server &Server::set_exception_handler(ExceptionHandler handler) {
4671 exception_handler_ = std::move(handler);
4675 inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
4676 pre_routing_handler_ = std::move(handler);
4680 inline Server &Server::set_post_routing_handler(Handler handler) {
4681 post_routing_handler_ = std::move(handler);
4685 inline Server &Server::set_logger(Logger logger) {
4686 logger_ = std::move(logger);
4691 Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
4692 expect_100_continue_handler_ = std::move(handler);
4697 inline Server &Server::set_address_family(
int family) {
4698 address_family_ = family;
4702 inline Server &Server::set_tcp_nodelay(
bool on) {
4707 inline Server &Server::set_socket_options(SocketOptions socket_options) {
4708 socket_options_ = std::move(socket_options);
4712 inline Server &Server::set_default_headers(Headers headers) {
4713 default_headers_ = std::move(headers);
4717 inline Server &Server::set_keep_alive_max_count(
size_t count) {
4718 keep_alive_max_count_ = count;
4722 inline Server &Server::set_keep_alive_timeout(time_t sec) {
4723 keep_alive_timeout_sec_ = sec;
4727 inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
4728 read_timeout_sec_ = sec;
4729 read_timeout_usec_ = usec;
4733 inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
4734 write_timeout_sec_ = sec;
4735 write_timeout_usec_ = usec;
4739 inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
4740 idle_interval_sec_ = sec;
4741 idle_interval_usec_ = usec;
4745 inline Server &Server::set_payload_max_length(
size_t length) {
4746 payload_max_length_ = length;
4750 inline bool Server::bind_to_port(
const char *host,
int port,
int socket_flags) {
4751 if (bind_internal(host, port, socket_flags) < 0)
return false;
4754 inline int Server::bind_to_any_port(
const char *host,
int socket_flags) {
4755 return bind_internal(host, 0, socket_flags);
4758 inline bool Server::listen_after_bind() {
return listen_internal(); }
4760 inline bool Server::listen(
const char *host,
int port,
int socket_flags) {
4761 return bind_to_port(host, port, socket_flags) && listen_internal();
4764 inline bool Server::is_running()
const {
return is_running_; }
4766 inline void Server::stop() {
4768 assert(svr_sock_ != INVALID_SOCKET);
4769 std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
4770 detail::shutdown_socket(sock);
4771 detail::close_socket(sock);
4775 inline bool Server::parse_request_line(
const char *s, Request &req) {
4776 auto len = strlen(s);
4777 if (
len < 2 || s[
len - 2] !=
'\r' || s[
len - 1] !=
'\n') {
return false; }
4783 detail::split(s, s +
len,
' ', [&](
const char *b,
const char *
e) {
4785 case 0: req.method = std::string(b,
e);
break;
4786 case 1: req.target = std::string(b,
e);
break;
4787 case 2: req.version = std::string(b,
e);
break;
4793 if (count != 3) {
return false; }
4796 static const std::set<std::string> methods{
4797 "GET",
"HEAD",
"POST",
"PUT",
"DELETE",
4798 "CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PRI"};
4800 if (methods.find(req.method) == methods.end()) {
return false; }
4802 if (req.version !=
"HTTP/1.1" && req.version !=
"HTTP/1.0") {
return false; }
4807 detail::split(req.target.data(), req.target.data() + req.target.size(),
'?',
4808 [&](
const char *b,
const char *
e) {
4811 req.path = detail::decode_url(std::string(b, e), false);
4815 detail::parse_query_text(std::string(b, e), req.params);
4824 if (count > 2) {
return false; }
4830 inline bool Server::write_response(Stream &strm,
bool close_connection,
4831 const Request &req, Response &res) {
4832 return write_response_core(strm, close_connection, req, res,
false);
4835 inline bool Server::write_response_with_content(Stream &strm,
4836 bool close_connection,
4839 return write_response_core(strm, close_connection, req, res,
true);
4842 inline bool Server::write_response_core(Stream &strm,
bool close_connection,
4843 const Request &req, Response &res,
4844 bool need_apply_ranges) {
4845 assert(res.status != -1);
4847 if (400 <= res.status && error_handler_ &&
4848 error_handler_(req, res) == HandlerResponse::Handled) {
4849 need_apply_ranges =
true;
4852 std::string content_type;
4853 std::string boundary;
4854 if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
4857 if (close_connection || req.get_header_value(
"Connection") ==
"close") {
4858 res.set_header(
"Connection",
"close");
4860 std::stringstream ss;
4861 ss <<
"timeout=" << keep_alive_timeout_sec_
4862 <<
", max=" << keep_alive_max_count_;
4863 res.set_header(
"Keep-Alive", ss.str());
4866 if (!res.has_header(
"Content-Type") &&
4867 (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) {
4868 res.set_header(
"Content-Type",
"text/plain");
4871 if (!res.has_header(
"Content-Length") && res.body.empty() &&
4872 !res.content_length_ && !res.content_provider_) {
4873 res.set_header(
"Content-Length",
"0");
4876 if (!res.has_header(
"Accept-Ranges") && req.method ==
"HEAD") {
4877 res.set_header(
"Accept-Ranges",
"bytes");
4880 if (post_routing_handler_) { post_routing_handler_(req, res); }
4884 detail::BufferStream bstrm;
4886 if (!bstrm.write_format(
"HTTP/1.1 %d %s\r\n", res.status,
4887 detail::status_message(res.status))) {
4891 if (!detail::write_headers(bstrm, res.headers)) {
return false; }
4894 auto &data = bstrm.get_buffer();
4895 strm.write(data.data(), data.size());
4900 if (req.method !=
"HEAD") {
4901 if (!res.body.empty()) {
4902 if (!strm.write(res.body)) { ret =
false; }
4903 }
else if (res.content_provider_) {
4904 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
4905 res.content_provider_success_ =
true;
4907 res.content_provider_success_ =
false;
4914 if (logger_) { logger_(req, res); }
4920 Server::write_content_with_provider(Stream &strm,
const Request &req,
4921 Response &res,
const std::string &boundary,
4922 const std::string &content_type) {
4923 auto is_shutting_down = [
this]() {
4924 return this->svr_sock_ == INVALID_SOCKET;
4927 if (res.content_length_ > 0) {
4928 if (req.ranges.empty()) {
4929 return detail::write_content(strm, res.content_provider_, 0,
4930 res.content_length_, is_shutting_down);
4931 }
else if (req.ranges.size() == 1) {
4933 detail::get_range_offset_and_length(req, res.content_length_, 0);
4934 auto offset = offsets.first;
4935 auto length = offsets.second;
4936 return detail::write_content(strm, res.content_provider_, offset, length,
4939 return detail::write_multipart_ranges_data(
4940 strm, req, res, boundary, content_type, is_shutting_down);
4943 if (res.is_chunked_content_provider_) {
4944 auto type = detail::encoding_type(req, res);
4946 std::unique_ptr<detail::compressor> compressor;
4947 if (type == detail::EncodingType::Gzip) {
4948 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4949 compressor = detail::make_unique<detail::gzip_compressor>();
4951 }
else if (type == detail::EncodingType::Brotli) {
4952 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4953 compressor = detail::make_unique<detail::brotli_compressor>();
4956 compressor = detail::make_unique<detail::nocompressor>();
4958 assert(compressor !=
nullptr);
4960 return detail::write_content_chunked(strm, res.content_provider_,
4961 is_shutting_down, *compressor);
4963 return detail::write_content_without_length(strm, res.content_provider_,
4969 inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
4970 MultipartFormDataMap::iterator cur;
4971 if (read_content_core(
4974 [&](
const char *
buf,
size_t n) {
4975 if (req.body.size() +
n > req.body.max_size()) { return false; }
4976 req.body.append(
buf,
n);
4980 [&](
const MultipartFormData &file) {
4981 cur = req.files.emplace(file.name, file);
4984 [&](
const char *
buf,
size_t n) {
4985 auto &content = cur->second.content;
4986 if (content.size() +
n > content.max_size()) {
return false; }
4987 content.append(
buf,
n);
4990 const auto &content_type = req.get_header_value(
"Content-Type");
4991 if (!content_type.find(
"application/x-www-form-urlencoded")) {
4992 if (req.body.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
4996 detail::parse_query_text(req.body, req.params);
5003 inline bool Server::read_content_with_content_receiver(
5004 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
5005 MultipartContentHeader multipart_header,
5006 ContentReceiver multipart_receiver) {
5007 return read_content_core(strm, req, res, std::move(receiver),
5008 std::move(multipart_header),
5009 std::move(multipart_receiver));
5012 inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
5013 ContentReceiver receiver,
5014 MultipartContentHeader mulitpart_header,
5015 ContentReceiver multipart_receiver) {
5016 detail::MultipartFormDataParser multipart_form_data_parser;
5017 ContentReceiverWithProgress out;
5019 if (req.is_multipart_form_data()) {
5020 const auto &content_type = req.get_header_value(
"Content-Type");
5021 std::string boundary;
5022 if (!detail::parse_multipart_boundary(content_type, boundary)) {
5027 multipart_form_data_parser.set_boundary(std::move(boundary));
5028 out = [&](
const char *
buf,
size_t n, uint64_t , uint64_t ) {
5040 return multipart_form_data_parser.parse(
buf,
n, multipart_receiver,
5044 out = [receiver](
const char *
buf,
size_t n, uint64_t ,
5045 uint64_t ) {
return receiver(
buf,
n); };
5048 if (req.method ==
"DELETE" && !req.has_header(
"Content-Length")) {
5052 if (!detail::read_content(strm, req, payload_max_length_, res.status,
nullptr,
5057 if (req.is_multipart_form_data()) {
5058 if (!multipart_form_data_parser.is_valid()) {
5067 inline bool Server::handle_file_request(
const Request &req, Response &res,
5069 for (
const auto &entry : base_dirs_) {
5071 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
5072 std::string sub_path =
"/" + req.path.substr(entry.mount_point.size());
5073 if (detail::is_valid_path(sub_path)) {
5074 auto path = entry.base_dir + sub_path;
5075 if (path.back() ==
'/') { path +=
"index.html"; }
5077 if (detail::is_file(path)) {
5078 detail::read_file(path, res.body);
5080 detail::find_content_type(path, file_extension_and_mimetype_map_);
5081 if (type) { res.set_header(
"Content-Type", type); }
5082 for (
const auto &kv : entry.headers) {
5083 res.set_header(kv.first.c_str(), kv.second);
5085 res.status = req.has_header(
"Range") ? 206 : 200;
5086 if (!head && file_request_handler_) {
5087 file_request_handler_(req, res);
5098 Server::create_server_socket(
const char *host,
int port,
int socket_flags,
5099 SocketOptions socket_options)
const {
5100 return detail::create_socket(
5101 host,
"", port, address_family_, socket_flags, tcp_nodelay_,
5102 std::move(socket_options),
5103 [](
socket_t sock,
struct addrinfo &ai) ->
bool {
5104 if (::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen))) {
5107 if (::listen(sock, 5)) {
5114 inline int Server::bind_internal(
const char *host,
int port,
int socket_flags) {
5115 if (!is_valid()) {
return -1; }
5117 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
5118 if (svr_sock_ == INVALID_SOCKET) {
return -1; }
5121 struct sockaddr_storage addr;
5122 socklen_t addr_len =
sizeof(addr);
5123 if (getsockname(svr_sock_,
reinterpret_cast<struct sockaddr *
>(&addr),
5127 if (addr.ss_family == AF_INET) {
5128 return ntohs(
reinterpret_cast<struct sockaddr_in *
>(&addr)->sin_port);
5129 }
else if (addr.ss_family == AF_INET6) {
5130 return ntohs(
reinterpret_cast<struct sockaddr_in6 *
>(&addr)->sin6_port);
5139 inline bool Server::listen_internal() {
5144 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
5146 while (svr_sock_ != INVALID_SOCKET) {
5148 if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
5150 auto val = detail::select_read(svr_sock_, idle_interval_sec_,
5151 idle_interval_usec_);
5153 task_queue->on_idle();
5159 socket_t sock = accept(svr_sock_,
nullptr,
nullptr);
5161 if (sock == INVALID_SOCKET) {
5162 if (errno == EMFILE) {
5165 std::this_thread::sleep_for(std::chrono::milliseconds(1));
5168 if (svr_sock_ != INVALID_SOCKET) {
5169 detail::close_socket(svr_sock_);
5179 tv.tv_sec =
static_cast<long>(read_timeout_sec_);
5180 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(read_timeout_usec_);
5181 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (
char *)&tv,
sizeof(tv));
5185 tv.tv_sec =
static_cast<long>(write_timeout_sec_);
5186 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(write_timeout_usec_);
5187 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (
char *)&tv,
sizeof(tv));
5190 #if __cplusplus > 201703L
5191 task_queue->enqueue([=,
this]() { process_and_close_socket(sock); });
5193 task_queue->enqueue([=]() { process_and_close_socket(sock); });
5197 task_queue->shutdown();
5200 is_running_ =
false;
5204 inline bool Server::routing(Request &req, Response &res, Stream &strm) {
5205 if (pre_routing_handler_ &&
5206 pre_routing_handler_(req, res) == HandlerResponse::Handled) {
5211 bool is_head_request = req.method ==
"HEAD";
5212 if ((req.method ==
"GET" || is_head_request) &&
5213 handle_file_request(req, res, is_head_request)) {
5217 if (detail::expect_content(req)) {
5220 ContentReader reader(
5221 [&](ContentReceiver receiver) {
5222 return read_content_with_content_receiver(
5223 strm, req, res, std::move(receiver),
nullptr,
nullptr);
5225 [&](MultipartContentHeader header, ContentReceiver receiver) {
5226 return read_content_with_content_receiver(strm, req, res,
nullptr,
5228 std::move(receiver));
5231 if (req.method ==
"POST") {
5232 if (dispatch_request_for_content_reader(
5233 req, res, std::move(reader),
5234 post_handlers_for_content_reader_)) {
5237 }
else if (req.method ==
"PUT") {
5238 if (dispatch_request_for_content_reader(
5239 req, res, std::move(reader),
5240 put_handlers_for_content_reader_)) {
5243 }
else if (req.method ==
"PATCH") {
5244 if (dispatch_request_for_content_reader(
5245 req, res, std::move(reader),
5246 patch_handlers_for_content_reader_)) {
5249 }
else if (req.method ==
"DELETE") {
5250 if (dispatch_request_for_content_reader(
5251 req, res, std::move(reader),
5252 delete_handlers_for_content_reader_)) {
5259 if (!read_content(strm, req, res)) {
return false; }
5263 if (req.method ==
"GET" || req.method ==
"HEAD") {
5264 return dispatch_request(req, res, get_handlers_);
5265 }
else if (req.method ==
"POST") {
5266 return dispatch_request(req, res, post_handlers_);
5267 }
else if (req.method ==
"PUT") {
5268 return dispatch_request(req, res, put_handlers_);
5269 }
else if (req.method ==
"DELETE") {
5270 return dispatch_request(req, res, delete_handlers_);
5271 }
else if (req.method ==
"OPTIONS") {
5272 return dispatch_request(req, res, options_handlers_);
5273 }
else if (req.method ==
"PATCH") {
5274 return dispatch_request(req, res, patch_handlers_);
5281 inline bool Server::dispatch_request(Request &req, Response &res,
5282 const Handlers &handlers) {
5283 for (
const auto &x : handlers) {
5284 const auto &pattern = x.first;
5285 const auto &handler = x.second;
5287 if (std::regex_match(req.path, req.matches, pattern)) {
5295 inline void Server::apply_ranges(
const Request &req, Response &res,
5296 std::string &content_type,
5297 std::string &boundary) {
5298 if (req.ranges.size() > 1) {
5299 boundary = detail::make_multipart_data_boundary();
5301 auto it = res.headers.find(
"Content-Type");
5302 if (it != res.headers.end()) {
5303 content_type = it->second;
5304 res.headers.erase(it);
5307 res.headers.emplace(
"Content-Type",
5308 "multipart/byteranges; boundary=" + boundary);
5311 auto type = detail::encoding_type(req, res);
5313 if (res.body.empty()) {
5314 if (res.content_length_ > 0) {
5316 if (req.ranges.empty()) {
5317 length = res.content_length_;
5318 }
else if (req.ranges.size() == 1) {
5320 detail::get_range_offset_and_length(req, res.content_length_, 0);
5321 auto offset = offsets.first;
5322 length = offsets.second;
5323 auto content_range = detail::make_content_range_header_field(
5324 offset, length, res.content_length_);
5325 res.set_header(
"Content-Range", content_range);
5327 length = detail::get_multipart_ranges_data_length(req, res, boundary,
5332 if (res.content_provider_) {
5333 if (res.is_chunked_content_provider_) {
5334 res.set_header(
"Transfer-Encoding",
"chunked");
5335 if (type == detail::EncodingType::Gzip) {
5336 res.set_header(
"Content-Encoding",
"gzip");
5337 }
else if (type == detail::EncodingType::Brotli) {
5338 res.set_header(
"Content-Encoding",
"br");
5344 if (req.ranges.empty()) {
5346 }
else if (req.ranges.size() == 1) {
5348 detail::get_range_offset_and_length(req, res.body.size(), 0);
5349 auto offset = offsets.first;
5350 auto length = offsets.second;
5351 auto content_range = detail::make_content_range_header_field(
5352 offset, length, res.body.size());
5353 res.set_header(
"Content-Range", content_range);
5354 if (offset < res.body.size()) {
5355 res.body = res.body.substr(offset, length);
5362 if (detail::make_multipart_ranges_data(req, res, boundary, content_type,
5364 res.body.swap(data);
5371 if (type != detail::EncodingType::None) {
5372 std::unique_ptr<detail::compressor> compressor;
5373 std::string content_encoding;
5375 if (type == detail::EncodingType::Gzip) {
5376 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
5377 compressor = detail::make_unique<detail::gzip_compressor>();
5378 content_encoding =
"gzip";
5380 }
else if (type == detail::EncodingType::Brotli) {
5381 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
5382 compressor = detail::make_unique<detail::brotli_compressor>();
5383 content_encoding =
"br";
5388 std::string compressed;
5389 if (compressor->compress(res.body.data(), res.body.size(),
true,
5390 [&](
const char *data,
size_t data_len) {
5391 compressed.append(data, data_len);
5394 res.body.swap(compressed);
5395 res.set_header(
"Content-Encoding", content_encoding);
5401 res.set_header(
"Content-Length", length);
5405 inline bool Server::dispatch_request_for_content_reader(
5406 Request &req, Response &res, ContentReader content_reader,
5407 const HandlersForContentReader &handlers) {
5408 for (
const auto &x : handlers) {
5409 const auto &pattern = x.first;
5410 const auto &handler = x.second;
5412 if (std::regex_match(req.path, req.matches, pattern)) {
5413 handler(req, res, content_reader);
5421 Server::process_request(Stream &strm,
bool close_connection,
5422 bool &connection_closed,
5423 const std::function<
void(Request &)> &setup_request) {
5424 std::array<char, 2048>
buf{};
5426 detail::stream_line_reader line_reader(strm,
buf.data(),
buf.size());
5429 if (!line_reader.getline()) {
return false; }
5434 res.version =
"HTTP/1.1";
5436 for (
const auto &header : default_headers_) {
5437 if (res.headers.find(header.first) == res.headers.end()) {
5438 res.headers.insert(header);
5445 #ifndef CPPHTTPLIB_USE_POLL
5447 if (strm.socket() >= FD_SETSIZE) {
5449 detail::read_headers(strm, dummy);
5451 return write_response(strm, close_connection, req, res);
5457 if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
5459 detail::read_headers(strm, dummy);
5461 return write_response(strm, close_connection, req, res);
5465 if (!parse_request_line(line_reader.ptr(), req) ||
5466 !detail::read_headers(strm, req.headers)) {
5468 return write_response(strm, close_connection, req, res);
5471 if (req.get_header_value(
"Connection") ==
"close") {
5472 connection_closed =
true;
5475 if (req.version ==
"HTTP/1.0" &&
5476 req.get_header_value(
"Connection") !=
"Keep-Alive") {
5477 connection_closed =
true;
5480 strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);
5481 req.set_header(
"REMOTE_ADDR", req.remote_addr);
5484 if (req.has_header(
"Range")) {
5485 const auto &range_header_value = req.get_header_value(
"Range");
5486 if (!detail::parse_range_header(range_header_value, req.ranges)) {
5488 return write_response(strm, close_connection, req, res);
5492 if (setup_request) { setup_request(req); }
5494 if (req.get_header_value(
"Expect") ==
"100-continue") {
5496 if (expect_100_continue_handler_) {
5497 status = expect_100_continue_handler_(req, res);
5502 strm.write_format(
"HTTP/1.1 %d %s\r\n\r\n", status,
5503 detail::status_message(status));
5505 default:
return write_response(strm, close_connection, req, res);
5510 bool routed =
false;
5511 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
5512 routed = routing(req, res, strm);
5515 routed = routing(req, res, strm);
5516 }
catch (std::exception &
e) {
5517 if (exception_handler_) {
5518 exception_handler_(req, res,
e);
5522 res.set_header(
"EXCEPTION_WHAT",
e.what());
5526 res.set_header(
"EXCEPTION_WHAT",
"UNKNOWN");
5531 if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
5532 return write_response_with_content(strm, close_connection, req, res);
5534 if (res.status == -1) { res.status = 404; }
5535 return write_response(strm, close_connection, req, res);
5539 inline bool Server::is_valid()
const {
return true; }
5541 inline bool Server::process_and_close_socket(
socket_t sock) {
5542 auto ret = detail::process_server_socket(
5543 sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_,
5544 read_timeout_usec_, write_timeout_sec_, write_timeout_usec_,
5545 [
this](Stream &strm,
bool close_connection,
bool &connection_closed) {
5546 return process_request(strm, close_connection, connection_closed,
5550 detail::shutdown_socket(sock);
5551 detail::close_socket(sock);
5556 inline ClientImpl::ClientImpl(
const std::string &host)
5557 : ClientImpl(host, 80, std::
string(), std::
string()) {}
5559 inline ClientImpl::ClientImpl(
const std::string &host,
int port)
5560 : ClientImpl(host, port, std::
string(), std::
string()) {}
5562 inline ClientImpl::ClientImpl(
const std::string &host,
int port,
5563 const std::string &client_cert_path,
5564 const std::string &client_key_path)
5565 : host_(host), port_(port),
5566 host_and_port_(adjust_host_string(host) +
":" + std::
to_string(port)),
5567 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
5569 inline ClientImpl::~ClientImpl() {
5570 std::lock_guard<std::mutex> guard(socket_mutex_);
5571 shutdown_socket(socket_);
5572 close_socket(socket_);
5575 inline bool ClientImpl::is_valid()
const {
return true; }
5577 inline void ClientImpl::copy_settings(
const ClientImpl &rhs) {
5578 client_cert_path_ = rhs.client_cert_path_;
5579 client_key_path_ = rhs.client_key_path_;
5580 connection_timeout_sec_ = rhs.connection_timeout_sec_;
5581 read_timeout_sec_ = rhs.read_timeout_sec_;
5582 read_timeout_usec_ = rhs.read_timeout_usec_;
5583 write_timeout_sec_ = rhs.write_timeout_sec_;
5584 write_timeout_usec_ = rhs.write_timeout_usec_;
5585 basic_auth_username_ = rhs.basic_auth_username_;
5586 basic_auth_password_ = rhs.basic_auth_password_;
5587 bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
5588 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5589 digest_auth_username_ = rhs.digest_auth_username_;
5590 digest_auth_password_ = rhs.digest_auth_password_;
5592 keep_alive_ = rhs.keep_alive_;
5593 follow_location_ = rhs.follow_location_;
5594 url_encode_ = rhs.url_encode_;
5595 address_family_ = rhs.address_family_;
5596 tcp_nodelay_ = rhs.tcp_nodelay_;
5597 socket_options_ = rhs.socket_options_;
5598 compress_ = rhs.compress_;
5599 decompress_ = rhs.decompress_;
5600 interface_ = rhs.interface_;
5601 proxy_host_ = rhs.proxy_host_;
5602 proxy_port_ = rhs.proxy_port_;
5603 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
5604 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
5605 proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
5606 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5607 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
5608 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
5610 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5611 ca_cert_file_path_ = rhs.ca_cert_file_path_;
5612 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
5613 ca_cert_store_ = rhs.ca_cert_store_;
5615 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5616 server_certificate_verification_ = rhs.server_certificate_verification_;
5618 logger_ = rhs.logger_;
5621 inline socket_t ClientImpl::create_client_socket(Error &error)
const {
5622 if (!proxy_host_.empty() && proxy_port_ != -1) {
5623 return detail::create_client_socket(
5624 proxy_host_.c_str(),
"", proxy_port_, address_family_, tcp_nodelay_,
5625 socket_options_, connection_timeout_sec_, connection_timeout_usec_,
5626 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
5627 write_timeout_usec_, interface_, error);
5631 auto it = addr_map_.find(host_);
5632 if (it != addr_map_.end()) ip = it->second;
5634 return detail::create_client_socket(
5635 host_.c_str(), ip.c_str(), port_, address_family_, tcp_nodelay_,
5636 socket_options_, connection_timeout_sec_, connection_timeout_usec_,
5637 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
5638 write_timeout_usec_, interface_, error);
5641 inline bool ClientImpl::create_and_connect_socket(Socket &socket,
5643 auto sock = create_client_socket(error);
5644 if (sock == INVALID_SOCKET) {
return false; }
5649 inline void ClientImpl::shutdown_ssl(Socket & ,
5653 assert(socket_requests_in_flight_ == 0 ||
5654 socket_requests_are_from_thread_ == std::this_thread::get_id());
5657 inline void ClientImpl::shutdown_socket(Socket &socket) {
5658 if (socket.sock == INVALID_SOCKET) {
return; }
5659 detail::shutdown_socket(socket.sock);
5662 inline void ClientImpl::close_socket(Socket &socket) {
5669 assert(socket_requests_in_flight_ == 0 ||
5670 socket_requests_are_from_thread_ == std::this_thread::get_id());
5673 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5674 assert(socket.ssl ==
nullptr);
5676 if (socket.sock == INVALID_SOCKET) {
return; }
5677 detail::close_socket(socket.sock);
5678 socket.sock = INVALID_SOCKET;
5681 inline bool ClientImpl::read_response_line(Stream &strm,
const Request &req,
5683 std::array<char, 2048>
buf{};
5685 detail::stream_line_reader line_reader(strm,
buf.data(),
buf.size());
5687 if (!line_reader.getline()) {
return false; }
5689 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
5692 if (!std::regex_match(line_reader.ptr(), m, re)) {
5693 return req.method ==
"CONNECT";
5695 res.version = std::string(m[1]);
5696 res.status = std::stoi(std::string(m[2]));
5697 res.reason = std::string(m[3]);
5700 while (res.status == 100) {
5701 if (!line_reader.getline()) {
return false; }
5702 if (!line_reader.getline()) {
return false; }
5704 if (!std::regex_match(line_reader.ptr(), m, re)) {
return false; }
5705 res.version = std::string(m[1]);
5706 res.status = std::stoi(std::string(m[2]));
5707 res.reason = std::string(m[3]);
5713 inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
5714 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
5717 std::lock_guard<std::mutex> guard(socket_mutex_);
5720 socket_should_be_closed_when_request_is_done_ =
false;
5722 auto is_alive =
false;
5723 if (socket_.is_open()) {
5724 is_alive = detail::select_write(socket_.sock, 0, 0) > 0;
5730 const bool shutdown_gracefully =
false;
5731 shutdown_ssl(socket_, shutdown_gracefully);
5732 shutdown_socket(socket_);
5733 close_socket(socket_);
5738 if (!create_and_connect_socket(socket_, error)) {
return false; }
5740 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5743 auto &scli =
static_cast<SSLClient &
>(*this);
5744 if (!proxy_host_.empty() && proxy_port_ != -1) {
5745 bool success =
false;
5746 if (!scli.connect_with_proxy(socket_, res, success, error)) {
5751 if (!scli.initialize_ssl(socket_, error)) {
return false; }
5759 if (socket_requests_in_flight_ > 1) {
5760 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
5762 socket_requests_in_flight_ += 1;
5763 socket_requests_are_from_thread_ = std::this_thread::get_id();
5766 for (
const auto &header : default_headers_) {
5767 if (req.headers.find(header.first) == req.headers.end()) {
5768 req.headers.insert(header);
5772 auto close_connection = !keep_alive_;
5773 auto ret = process_socket(socket_, [&](Stream &strm) {
5774 return handle_request(strm, req, res, close_connection, error);
5779 std::lock_guard<std::mutex> guard(socket_mutex_);
5780 socket_requests_in_flight_ -= 1;
5781 if (socket_requests_in_flight_ <= 0) {
5782 assert(socket_requests_in_flight_ == 0);
5783 socket_requests_are_from_thread_ = std::thread::id();
5786 if (socket_should_be_closed_when_request_is_done_ || close_connection ||
5788 shutdown_ssl(socket_,
true);
5789 shutdown_socket(socket_);
5790 close_socket(socket_);
5795 if (error == Error::Success) {
error = Error::Unknown; }
5801 inline Result ClientImpl::send(
const Request &req) {
5803 return send_(std::move(req2));
5806 inline Result ClientImpl::send_(Request &&req) {
5807 auto res = detail::make_unique<Response>();
5808 auto error = Error::Success;
5809 auto ret = send(req, *res, error);
5810 return Result{ret ? std::move(res) : nullptr,
error, std::move(req.headers)};
5813 inline bool ClientImpl::handle_request(Stream &strm, Request &req,
5814 Response &res,
bool close_connection,
5816 if (req.path.empty()) {
5817 error = Error::Connection;
5821 auto req_save = req;
5825 if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
5827 req2.path =
"http://" + host_and_port_ + req.path;
5828 ret = process_request(strm, req2, res, close_connection, error);
5830 req.path = req_save.path;
5832 ret = process_request(strm, req, res, close_connection, error);
5835 if (!ret) {
return false; }
5837 if (300 < res.status && res.status < 400 && follow_location_) {
5839 ret = redirect(req, res, error);
5842 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5843 if ((res.status == 401 || res.status == 407) &&
5844 req.authorization_count_ < 5) {
5845 auto is_proxy = res.status == 407;
5846 const auto &username =
5847 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
5848 const auto &password =
5849 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
5851 if (!username.empty() && !password.empty()) {
5852 std::map<std::string, std::string> auth;
5853 if (detail::parse_www_authenticate(res, auth, is_proxy)) {
5854 Request new_req = req;
5855 new_req.authorization_count_ += 1;
5856 new_req.headers.erase(is_proxy ?
"Proxy-Authorization"
5858 new_req.headers.insert(detail::make_digest_authentication_header(
5859 req, auth, new_req.authorization_count_, detail::random_string(10),
5860 username, password, is_proxy));
5864 ret = send(new_req, new_res, error);
5865 if (ret) { res = new_res; }
5874 inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
5875 if (req.redirect_count_ == 0) {
5876 error = Error::ExceedRedirectCount;
5880 auto location = detail::decode_url(res.get_header_value(
"location"),
true);
5881 if (location.empty()) {
return false; }
5883 const static std::regex re(
5884 R
"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
5887 if (!std::regex_match(location, m, re)) {
return false; }
5889 auto scheme = is_ssl() ?
"https" :
"http";
5891 auto next_scheme = m[1].str();
5892 auto next_host = m[2].str();
5893 if (next_host.empty()) { next_host = m[3].str(); }
5894 auto port_str = m[4].str();
5895 auto next_path = m[5].str();
5897 auto next_port = port_;
5898 if (!port_str.empty()) {
5899 next_port = std::stoi(port_str);
5900 }
else if (!next_scheme.empty()) {
5901 next_port = next_scheme ==
"https" ? 443 : 80;
5904 if (next_scheme.empty()) { next_scheme = scheme; }
5905 if (next_host.empty()) { next_host = host_; }
5906 if (next_path.empty()) { next_path =
"/"; }
5908 if (next_scheme == scheme && next_host == host_ && next_port == port_) {
5909 return detail::redirect(*
this, req, res, next_path, location, error);
5911 if (next_scheme ==
"https") {
5912 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5913 SSLClient cli(next_host.c_str(), next_port);
5914 cli.copy_settings(*
this);
5915 if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
5916 return detail::redirect(cli, req, res, next_path, location, error);
5921 ClientImpl cli(next_host.c_str(), next_port);
5922 cli.copy_settings(*
this);
5923 return detail::redirect(cli, req, res, next_path, location, error);
5928 inline bool ClientImpl::write_content_with_provider(Stream &strm,
5931 auto is_shutting_down = []() {
return false; };
5933 if (req.is_chunked_content_provider_) {
5935 std::unique_ptr<detail::compressor> compressor;
5936 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
5938 compressor = detail::make_unique<detail::gzip_compressor>();
5942 compressor = detail::make_unique<detail::nocompressor>();
5945 return detail::write_content_chunked(strm, req.content_provider_,
5946 is_shutting_down, *compressor, error);
5948 return detail::write_content(strm, req.content_provider_, 0,
5949 req.content_length_, is_shutting_down, error);
5953 inline bool ClientImpl::write_request(Stream &strm, Request &req,
5954 bool close_connection, Error &error) {
5956 if (close_connection) {
5957 if (!req.has_header(
"Connection")) {
5958 req.headers.emplace(
"Connection",
"close");
5962 if (!req.has_header(
"Host")) {
5965 req.headers.emplace(
"Host", host_);
5967 req.headers.emplace(
"Host", host_and_port_);
5971 req.headers.emplace(
"Host", host_);
5973 req.headers.emplace(
"Host", host_and_port_);
5978 if (!req.has_header(
"Accept")) { req.headers.emplace(
"Accept",
"*/*"); }
5980 if (!req.has_header(
"User-Agent")) {
5981 req.headers.emplace(
"User-Agent",
"cpp-httplib/0.9");
5984 if (req.body.empty()) {
5985 if (req.content_provider_) {
5986 if (!req.is_chunked_content_provider_) {
5987 if (!req.has_header(
"Content-Length")) {
5989 req.headers.emplace(
"Content-Length", length);
5993 if (req.method ==
"POST" || req.method ==
"PUT" ||
5994 req.method ==
"PATCH") {
5995 req.headers.emplace(
"Content-Length",
"0");
5999 if (!req.has_header(
"Content-Type")) {
6000 req.headers.emplace(
"Content-Type",
"text/plain");
6003 if (!req.has_header(
"Content-Length")) {
6005 req.headers.emplace(
"Content-Length", length);
6009 if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
6010 if (!req.has_header(
"Authorization")) {
6011 req.headers.insert(make_basic_authentication_header(
6012 basic_auth_username_, basic_auth_password_,
false));
6016 if (!proxy_basic_auth_username_.empty() &&
6017 !proxy_basic_auth_password_.empty()) {
6018 if (!req.has_header(
"Proxy-Authorization")) {
6019 req.headers.insert(make_basic_authentication_header(
6020 proxy_basic_auth_username_, proxy_basic_auth_password_,
true));
6024 if (!bearer_token_auth_token_.empty()) {
6025 if (!req.has_header(
"Authorization")) {
6026 req.headers.insert(make_bearer_token_authentication_header(
6027 bearer_token_auth_token_,
false));
6031 if (!proxy_bearer_token_auth_token_.empty()) {
6032 if (!req.has_header(
"Proxy-Authorization")) {
6033 req.headers.insert(make_bearer_token_authentication_header(
6034 proxy_bearer_token_auth_token_,
true));
6040 detail::BufferStream bstrm;
6042 const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path;
6043 bstrm.write_format(
"%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
6045 detail::write_headers(bstrm, req.headers);
6048 auto &data = bstrm.get_buffer();
6049 if (!detail::write_data(strm, data.data(), data.size())) {
6050 error = Error::Write;
6056 if (req.body.empty()) {
6057 return write_content_with_provider(strm, req, error);
6060 if (!detail::write_data(strm, req.body.data(), req.body.size())) {
6061 error = Error::Write;
6068 inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
6071 const char *body,
size_t content_length, ContentProvider content_provider,
6072 ContentProviderWithoutLength content_provider_without_length,
6073 const char *content_type, Error &error) {
6080 if (content_type) { req.headers.emplace(
"Content-Type", content_type); }
6082 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
6083 if (compress_) { req.headers.emplace(
"Content-Encoding",
"gzip"); }
6086 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
6087 if (compress_ && !content_provider_without_length) {
6089 detail::gzip_compressor compressor;
6091 if (content_provider) {
6096 data_sink.write = [&](
const char *data,
size_t data_len) ->
bool {
6098 auto last = offset + data_len == content_length;
6100 auto ret = compressor.compress(
6101 data, data_len,
last, [&](
const char *data,
size_t data_len) {
6102 req.body.append(data, data_len);
6115 data_sink.is_writable = [&](void) {
return ok &&
true; };
6117 while (ok && offset < content_length) {
6118 if (!content_provider(offset, content_length - offset, data_sink)) {
6119 error = Error::Canceled;
6124 if (!compressor.compress(body, content_length,
true,
6125 [&](
const char *data,
size_t data_len) {
6126 req.body.append(data, data_len);
6129 error = Error::Compression;
6136 if (content_provider) {
6137 req.content_length_ = content_length;
6138 req.content_provider_ = std::move(content_provider);
6139 req.is_chunked_content_provider_ =
false;
6140 }
else if (content_provider_without_length) {
6141 req.content_length_ = 0;
6142 req.content_provider_ = detail::ContentProviderAdapter(
6143 std::move(content_provider_without_length));
6144 req.is_chunked_content_provider_ =
true;
6145 req.headers.emplace(
"Transfer-Encoding",
"chunked");
6147 req.body.assign(body, content_length);
6152 auto res = detail::make_unique<Response>();
6153 return send(req, *res, error) ? std::move(res) : nullptr;
6156 inline Result ClientImpl::send_with_content_provider(
6157 const char *method,
const char *path,
const Headers &headers,
6158 const char *body,
size_t content_length, ContentProvider content_provider,
6159 ContentProviderWithoutLength content_provider_without_length,
6160 const char *content_type) {
6162 req.method = method;
6163 req.headers = headers;
6166 auto error = Error::Success;
6168 auto res = send_with_content_provider(
6171 body, content_length, std::move(content_provider),
6172 std::move(content_provider_without_length), content_type, error);
6174 return Result{std::move(res),
error, std::move(req.headers)};
6178 ClientImpl::adjust_host_string(
const std::string &host)
const {
6179 if (host.find(
':') != std::string::npos) {
return "[" + host +
"]"; }
6183 inline bool ClientImpl::process_request(Stream &strm, Request &req,
6184 Response &res,
bool close_connection,
6187 if (!write_request(strm, req, close_connection, error)) {
return false; }
6190 if (!read_response_line(strm, req, res) ||
6191 !detail::read_headers(strm, res.headers)) {
6192 error = Error::Read;
6197 if ((res.status != 204) && req.method !=
"HEAD" && req.method !=
"CONNECT") {
6198 auto redirect = 300 < res.status && res.status < 400 && follow_location_;
6200 if (req.response_handler && !redirect) {
6201 if (!req.response_handler(res)) {
6202 error = Error::Canceled;
6208 req.content_receiver
6209 ?
static_cast<ContentReceiverWithProgress
>(
6210 [&](
const char *
buf,
size_t n, uint64_t off, uint64_t
len) {
6211 if (redirect) {
return true; }
6212 auto ret = req.content_receiver(
buf,
n, off,
len);
6213 if (!ret) {
error = Error::Canceled; }
6216 :
static_cast<ContentReceiverWithProgress
>(
6217 [&](
const char *
buf,
size_t n, uint64_t ,
6219 if (res.body.size() +
n > res.body.max_size()) {
6222 res.body.append(
buf,
n);
6226 auto progress = [&](uint64_t current, uint64_t total) {
6227 if (!req.progress || redirect) {
return true; }
6228 auto ret = req.progress(current, total);
6229 if (!ret) {
error = Error::Canceled; }
6234 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
6235 dummy_status, std::move(progress), std::move(out),
6237 if (error != Error::Canceled) {
error = Error::Read; }
6242 if (res.get_header_value(
"Connection") ==
"close" ||
6243 (res.version ==
"HTTP/1.0" && res.reason !=
"Connection established")) {
6254 std::lock_guard<std::mutex> guard(socket_mutex_);
6255 shutdown_ssl(socket_,
true);
6256 shutdown_socket(socket_);
6257 close_socket(socket_);
6261 if (logger_) { logger_(req, res); }
6267 ClientImpl::process_socket(
const Socket &socket,
6268 std::function<
bool(Stream &strm)> callback) {
6269 return detail::process_client_socket(
6270 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
6271 write_timeout_usec_, std::move(callback));
6274 inline bool ClientImpl::is_ssl()
const {
return false; }
6276 inline Result ClientImpl::Get(
const char *path) {
6277 return Get(path, Headers(), Progress());
6280 inline Result ClientImpl::Get(
const char *path, Progress progress) {
6281 return Get(path, Headers(), std::move(progress));
6284 inline Result ClientImpl::Get(
const char *path,
const Headers &headers) {
6285 return Get(path, headers, Progress());
6288 inline Result ClientImpl::Get(
const char *path,
const Headers &headers,
6289 Progress progress) {
6293 req.headers = headers;
6294 req.progress = std::move(progress);
6296 return send_(std::move(req));
6299 inline Result ClientImpl::Get(
const char *path,
6300 ContentReceiver content_receiver) {
6301 return Get(path, Headers(),
nullptr, std::move(content_receiver),
nullptr);
6304 inline Result ClientImpl::Get(
const char *path,
6305 ContentReceiver content_receiver,
6306 Progress progress) {
6307 return Get(path, Headers(),
nullptr, std::move(content_receiver),
6308 std::move(progress));
6311 inline Result ClientImpl::Get(
const char *path,
const Headers &headers,
6312 ContentReceiver content_receiver) {
6313 return Get(path, headers,
nullptr, std::move(content_receiver),
nullptr);
6316 inline Result ClientImpl::Get(
const char *path,
const Headers &headers,
6317 ContentReceiver content_receiver,
6318 Progress progress) {
6319 return Get(path, headers,
nullptr, std::move(content_receiver),
6320 std::move(progress));
6323 inline Result ClientImpl::Get(
const char *path,
6324 ResponseHandler response_handler,
6325 ContentReceiver content_receiver) {
6326 return Get(path, Headers(), std::move(response_handler),
6327 std::move(content_receiver),
nullptr);
6330 inline Result ClientImpl::Get(
const char *path,
const Headers &headers,
6331 ResponseHandler response_handler,
6332 ContentReceiver content_receiver) {
6333 return Get(path, headers, std::move(response_handler),
6334 std::move(content_receiver),
nullptr);
6337 inline Result ClientImpl::Get(
const char *path,
6338 ResponseHandler response_handler,
6339 ContentReceiver content_receiver,
6340 Progress progress) {
6341 return Get(path, Headers(), std::move(response_handler),
6342 std::move(content_receiver), std::move(progress));
6345 inline Result ClientImpl::Get(
const char *path,
const Headers &headers,
6346 ResponseHandler response_handler,
6347 ContentReceiver content_receiver,
6348 Progress progress) {
6352 req.headers = headers;
6353 req.response_handler = std::move(response_handler);
6354 req.content_receiver =
6355 [content_receiver](
const char *data,
size_t data_length,
6356 uint64_t , uint64_t ) {
6357 return content_receiver(data, data_length);
6359 req.progress = std::move(progress);
6361 return send_(std::move(req));
6364 inline Result ClientImpl::Get(
const char *path,
const Params ¶ms,
6365 const Headers &headers, Progress progress) {
6366 if (params.empty()) {
return Get(path, headers); }
6368 std::string path_with_query = append_query_params(path, params);
6369 return Get(path_with_query.c_str(), headers, progress);
6372 inline Result ClientImpl::Get(
const char *path,
const Params ¶ms,
6373 const Headers &headers,
6374 ContentReceiver content_receiver,
6375 Progress progress) {
6376 return Get(path, params, headers,
nullptr, content_receiver, progress);
6379 inline Result ClientImpl::Get(
const char *path,
const Params ¶ms,
6380 const Headers &headers,
6381 ResponseHandler response_handler,
6382 ContentReceiver content_receiver,
6383 Progress progress) {
6384 if (params.empty()) {
6385 return Get(path, headers, response_handler, content_receiver, progress);
6388 std::string path_with_query = append_query_params(path, params);
6389 return Get(path_with_query.c_str(), headers, response_handler,
6390 content_receiver, progress);
6393 inline Result ClientImpl::Head(
const char *path) {
6394 return Head(path, Headers());
6397 inline Result ClientImpl::Head(
const char *path,
const Headers &headers) {
6399 req.method =
"HEAD";
6400 req.headers = headers;
6403 return send_(std::move(req));
6406 inline Result ClientImpl::Post(
const char *path) {
6407 return Post(path, std::string(),
nullptr);
6410 inline Result ClientImpl::Post(
const char *path,
const char *body,
6411 size_t content_length,
6412 const char *content_type) {
6413 return Post(path, Headers(), body, content_length, content_type);
6416 inline Result ClientImpl::Post(
const char *path,
const Headers &headers,
6417 const char *body,
size_t content_length,
6418 const char *content_type) {
6419 return send_with_content_provider(
"POST", path, headers, body, content_length,
6420 nullptr,
nullptr, content_type);
6423 inline Result ClientImpl::Post(
const char *path,
const std::string &body,
6424 const char *content_type) {
6425 return Post(path, Headers(), body, content_type);
6428 inline Result ClientImpl::Post(
const char *path,
const Headers &headers,
6429 const std::string &body,
6430 const char *content_type) {
6431 return send_with_content_provider(
"POST", path, headers, body.data(),
6432 body.size(),
nullptr,
nullptr,
6436 inline Result ClientImpl::Post(
const char *path,
const Params ¶ms) {
6437 return Post(path, Headers(), params);
6440 inline Result ClientImpl::Post(
const char *path,
size_t content_length,
6441 ContentProvider content_provider,
6442 const char *content_type) {
6443 return Post(path, Headers(), content_length, std::move(content_provider),
6447 inline Result ClientImpl::Post(
const char *path,
6448 ContentProviderWithoutLength content_provider,
6449 const char *content_type) {
6450 return Post(path, Headers(), std::move(content_provider), content_type);
6453 inline Result ClientImpl::Post(
const char *path,
const Headers &headers,
6454 size_t content_length,
6455 ContentProvider content_provider,
6456 const char *content_type) {
6457 return send_with_content_provider(
"POST", path, headers,
nullptr,
6458 content_length, std::move(content_provider),
6459 nullptr, content_type);
6462 inline Result ClientImpl::Post(
const char *path,
const Headers &headers,
6463 ContentProviderWithoutLength content_provider,
6464 const char *content_type) {
6465 return send_with_content_provider(
"POST", path, headers,
nullptr, 0,
nullptr,
6466 std::move(content_provider), content_type);
6469 inline Result ClientImpl::Post(
const char *path,
const Headers &headers,
6470 const Params ¶ms) {
6471 auto query = detail::params_to_query_str(params);
6472 return Post(path, headers, query,
"application/x-www-form-urlencoded");
6475 inline Result ClientImpl::Post(
const char *path,
6476 const MultipartFormDataItems &items) {
6477 return Post(path, Headers(), items);
6480 inline Result ClientImpl::Post(
const char *path,
const Headers &headers,
6481 const MultipartFormDataItems &items) {
6482 return Post(path, headers, items, detail::make_multipart_data_boundary());
6484 inline Result ClientImpl::Post(
const char *path,
const Headers &headers,
6485 const MultipartFormDataItems &items,
6486 const std::string &boundary) {
6487 for (
size_t i = 0; i < boundary.size(); i++) {
6488 char c = boundary[i];
6489 if (!std::isalnum(c) && c !=
'-' && c !=
'_') {
6490 return Result{
nullptr, Error::UnsupportedMultipartBoundaryChars};
6496 for (
const auto &item : items) {
6497 body +=
"--" + boundary +
"\r\n";
6498 body +=
"Content-Disposition: form-data; name=\"" + item.name +
"\"";
6499 if (!item.filename.empty()) {
6500 body +=
"; filename=\"" + item.filename +
"\"";
6503 if (!item.content_type.empty()) {
6504 body +=
"Content-Type: " + item.content_type +
"\r\n";
6507 body += item.content +
"\r\n";
6510 body +=
"--" + boundary +
"--\r\n";
6512 std::string content_type =
"multipart/form-data; boundary=" + boundary;
6513 return Post(path, headers, body, content_type.c_str());
6516 inline Result ClientImpl::Put(
const char *path) {
6517 return Put(path, std::string(),
nullptr);
6520 inline Result ClientImpl::Put(
const char *path,
const char *body,
6521 size_t content_length,
const char *content_type) {
6522 return Put(path, Headers(), body, content_length, content_type);
6525 inline Result ClientImpl::Put(
const char *path,
const Headers &headers,
6526 const char *body,
size_t content_length,
6527 const char *content_type) {
6528 return send_with_content_provider(
"PUT", path, headers, body, content_length,
6529 nullptr,
nullptr, content_type);
6532 inline Result ClientImpl::Put(
const char *path,
const std::string &body,
6533 const char *content_type) {
6534 return Put(path, Headers(), body, content_type);
6537 inline Result ClientImpl::Put(
const char *path,
const Headers &headers,
6538 const std::string &body,
6539 const char *content_type) {
6540 return send_with_content_provider(
"PUT", path, headers, body.data(),
6541 body.size(),
nullptr,
nullptr,
6545 inline Result ClientImpl::Put(
const char *path,
size_t content_length,
6546 ContentProvider content_provider,
6547 const char *content_type) {
6548 return Put(path, Headers(), content_length, std::move(content_provider),
6552 inline Result ClientImpl::Put(
const char *path,
6553 ContentProviderWithoutLength content_provider,
6554 const char *content_type) {
6555 return Put(path, Headers(), std::move(content_provider), content_type);
6558 inline Result ClientImpl::Put(
const char *path,
const Headers &headers,
6559 size_t content_length,
6560 ContentProvider content_provider,
6561 const char *content_type) {
6562 return send_with_content_provider(
"PUT", path, headers,
nullptr,
6563 content_length, std::move(content_provider),
6564 nullptr, content_type);
6567 inline Result ClientImpl::Put(
const char *path,
const Headers &headers,
6568 ContentProviderWithoutLength content_provider,
6569 const char *content_type) {
6570 return send_with_content_provider(
"PUT", path, headers,
nullptr, 0,
nullptr,
6571 std::move(content_provider), content_type);
6574 inline Result ClientImpl::Put(
const char *path,
const Params ¶ms) {
6575 return Put(path, Headers(), params);
6578 inline Result ClientImpl::Put(
const char *path,
const Headers &headers,
6579 const Params ¶ms) {
6580 auto query = detail::params_to_query_str(params);
6581 return Put(path, headers, query,
"application/x-www-form-urlencoded");
6584 inline Result ClientImpl::Patch(
const char *path) {
6585 return Patch(path, std::string(),
nullptr);
6588 inline Result ClientImpl::Patch(
const char *path,
const char *body,
6589 size_t content_length,
6590 const char *content_type) {
6591 return Patch(path, Headers(), body, content_length, content_type);
6594 inline Result ClientImpl::Patch(
const char *path,
const Headers &headers,
6595 const char *body,
size_t content_length,
6596 const char *content_type) {
6597 return send_with_content_provider(
"PATCH", path, headers, body,
6598 content_length,
nullptr,
nullptr,
6602 inline Result ClientImpl::Patch(
const char *path,
const std::string &body,
6603 const char *content_type) {
6604 return Patch(path, Headers(), body, content_type);
6607 inline Result ClientImpl::Patch(
const char *path,
const Headers &headers,
6608 const std::string &body,
6609 const char *content_type) {
6610 return send_with_content_provider(
"PATCH", path, headers, body.data(),
6611 body.size(),
nullptr,
nullptr,
6615 inline Result ClientImpl::Patch(
const char *path,
size_t content_length,
6616 ContentProvider content_provider,
6617 const char *content_type) {
6618 return Patch(path, Headers(), content_length, std::move(content_provider),
6622 inline Result ClientImpl::Patch(
const char *path,
6623 ContentProviderWithoutLength content_provider,
6624 const char *content_type) {
6625 return Patch(path, Headers(), std::move(content_provider), content_type);
6628 inline Result ClientImpl::Patch(
const char *path,
const Headers &headers,
6629 size_t content_length,
6630 ContentProvider content_provider,
6631 const char *content_type) {
6632 return send_with_content_provider(
"PATCH", path, headers,
nullptr,
6633 content_length, std::move(content_provider),
6634 nullptr, content_type);
6637 inline Result ClientImpl::Patch(
const char *path,
const Headers &headers,
6638 ContentProviderWithoutLength content_provider,
6639 const char *content_type) {
6640 return send_with_content_provider(
"PATCH", path, headers,
nullptr, 0,
nullptr,
6641 std::move(content_provider), content_type);
6644 inline Result ClientImpl::Delete(
const char *path) {
6645 return Delete(path, Headers(), std::string(),
nullptr);
6648 inline Result ClientImpl::Delete(
const char *path,
const Headers &headers) {
6649 return Delete(path, headers, std::string(),
nullptr);
6652 inline Result ClientImpl::Delete(
const char *path,
const char *body,
6653 size_t content_length,
6654 const char *content_type) {
6655 return Delete(path, Headers(), body, content_length, content_type);
6658 inline Result ClientImpl::Delete(
const char *path,
const Headers &headers,
6659 const char *body,
size_t content_length,
6660 const char *content_type) {
6662 req.method =
"DELETE";
6663 req.headers = headers;
6666 if (content_type) { req.headers.emplace(
"Content-Type", content_type); }
6667 req.body.assign(body, content_length);
6669 return send_(std::move(req));
6672 inline Result ClientImpl::Delete(
const char *path,
const std::string &body,
6673 const char *content_type) {
6674 return Delete(path, Headers(), body.data(), body.size(), content_type);
6677 inline Result ClientImpl::Delete(
const char *path,
const Headers &headers,
6678 const std::string &body,
6679 const char *content_type) {
6680 return Delete(path, headers, body.data(), body.size(), content_type);
6683 inline Result ClientImpl::Options(
const char *path) {
6684 return Options(path, Headers());
6687 inline Result ClientImpl::Options(
const char *path,
const Headers &headers) {
6689 req.method =
"OPTIONS";
6690 req.headers = headers;
6693 return send_(std::move(req));
6696 inline size_t ClientImpl::is_socket_open()
const {
6697 std::lock_guard<std::mutex> guard(socket_mutex_);
6698 return socket_.is_open();
6701 inline void ClientImpl::stop() {
6702 std::lock_guard<std::mutex> guard(socket_mutex_);
6709 if (socket_requests_in_flight_ > 0) {
6710 shutdown_socket(socket_);
6714 socket_should_be_closed_when_request_is_done_ =
true;
6719 shutdown_ssl(socket_,
true);
6720 shutdown_socket(socket_);
6721 close_socket(socket_);
6724 inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
6725 connection_timeout_sec_ = sec;
6726 connection_timeout_usec_ = usec;
6729 inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
6730 read_timeout_sec_ = sec;
6731 read_timeout_usec_ = usec;
6734 inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
6735 write_timeout_sec_ = sec;
6736 write_timeout_usec_ = usec;
6739 inline void ClientImpl::set_basic_auth(
const char *username,
6740 const char *password) {
6741 basic_auth_username_ = username;
6742 basic_auth_password_ = password;
6745 inline void ClientImpl::set_bearer_token_auth(
const char *token) {
6746 bearer_token_auth_token_ = token;
6749 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6750 inline void ClientImpl::set_digest_auth(
const char *username,
6751 const char *password) {
6752 digest_auth_username_ = username;
6753 digest_auth_password_ = password;
6757 inline void ClientImpl::set_keep_alive(
bool on) { keep_alive_ = on; }
6759 inline void ClientImpl::set_follow_location(
bool on) { follow_location_ = on; }
6761 inline void ClientImpl::set_url_encode(
bool on) { url_encode_ = on; }
6763 inline void ClientImpl::set_hostname_addr_map(
6764 const std::map<std::string, std::string> addr_map) {
6765 addr_map_ = std::move(addr_map);
6768 inline void ClientImpl::set_default_headers(Headers headers) {
6769 default_headers_ = std::move(headers);
6772 inline void ClientImpl::set_address_family(
int family) {
6773 address_family_ = family;
6776 inline void ClientImpl::set_tcp_nodelay(
bool on) { tcp_nodelay_ = on; }
6778 inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
6779 socket_options_ = std::move(socket_options);
6782 inline void ClientImpl::set_compress(
bool on) { compress_ = on; }
6784 inline void ClientImpl::set_decompress(
bool on) { decompress_ = on; }
6786 inline void ClientImpl::set_interface(
const char *intf) { interface_ = intf; }
6788 inline void ClientImpl::set_proxy(
const char *host,
int port) {
6793 inline void ClientImpl::set_proxy_basic_auth(
const char *username,
6794 const char *password) {
6795 proxy_basic_auth_username_ = username;
6796 proxy_basic_auth_password_ = password;
6799 inline void ClientImpl::set_proxy_bearer_token_auth(
const char *token) {
6800 proxy_bearer_token_auth_token_ = token;
6803 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6804 inline void ClientImpl::set_proxy_digest_auth(
const char *username,
6805 const char *password) {
6806 proxy_digest_auth_username_ = username;
6807 proxy_digest_auth_password_ = password;
6811 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6812 inline void ClientImpl::set_ca_cert_path(
const char *ca_cert_file_path,
6813 const char *ca_cert_dir_path) {
6814 if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; }
6815 if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; }
6818 inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
6819 if (ca_cert_store && ca_cert_store != ca_cert_store_) {
6820 ca_cert_store_ = ca_cert_store;
6825 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6826 inline void ClientImpl::enable_server_certificate_verification(
bool enabled) {
6827 server_certificate_verification_ = enabled;
6831 inline void ClientImpl::set_logger(Logger logger) {
6832 logger_ = std::move(logger);
6838 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6841 template <
typename U,
typename V>
6842 inline SSL *ssl_new(
socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
6843 U SSL_connect_or_accept, V setup) {
6846 std::lock_guard<std::mutex> guard(ctx_mutex);
6851 set_nonblocking(sock,
true);
6852 auto bio = BIO_new_socket(
static_cast<int>(sock), BIO_NOCLOSE);
6853 BIO_set_nbio(bio, 1);
6854 SSL_set_bio(ssl, bio, bio);
6856 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
6859 std::lock_guard<std::mutex> guard(ctx_mutex);
6862 set_nonblocking(sock,
false);
6865 BIO_set_nbio(bio, 0);
6866 set_nonblocking(sock,
false);
6872 inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
6873 bool shutdown_gracefully) {
6878 if (shutdown_gracefully) { SSL_shutdown(ssl); }
6880 std::lock_guard<std::mutex> guard(ctx_mutex);
6884 template <
typename U>
6885 bool ssl_connect_or_accept_nonblocking(
socket_t sock, SSL *ssl,
6886 U ssl_connect_or_accept,
6888 time_t timeout_usec) {
6890 while ((res = ssl_connect_or_accept(ssl)) != 1) {
6891 auto err = SSL_get_error(ssl, res);
6893 case SSL_ERROR_WANT_READ:
6894 if (select_read(sock, timeout_sec, timeout_usec) > 0) {
continue; }
6896 case SSL_ERROR_WANT_WRITE:
6897 if (select_write(sock, timeout_sec, timeout_usec) > 0) {
continue; }
6906 template <
typename T>
6908 process_server_socket_ssl(SSL *ssl,
socket_t sock,
size_t keep_alive_max_count,
6909 time_t keep_alive_timeout_sec,
6910 time_t read_timeout_sec, time_t read_timeout_usec,
6911 time_t write_timeout_sec, time_t write_timeout_usec,
6913 return process_server_socket_core(
6914 sock, keep_alive_max_count, keep_alive_timeout_sec,
6915 [&](
bool close_connection,
bool &connection_closed) {
6916 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
6917 write_timeout_sec, write_timeout_usec);
6918 return callback(strm, close_connection, connection_closed);
6922 template <
typename T>
6924 process_client_socket_ssl(SSL *ssl,
socket_t sock, time_t read_timeout_sec,
6925 time_t read_timeout_usec, time_t write_timeout_sec,
6926 time_t write_timeout_usec, T callback) {
6927 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
6928 write_timeout_sec, write_timeout_usec);
6929 return callback(strm);
6932 #if OPENSSL_VERSION_NUMBER < 0x10100000L
6933 static std::shared_ptr<std::vector<std::mutex>> openSSL_locks_;
6935 class SSLThreadLocks {
6939 std::make_shared<std::vector<std::mutex>>(CRYPTO_num_locks());
6940 CRYPTO_set_locking_callback(locking_callback);
6943 ~SSLThreadLocks() { CRYPTO_set_locking_callback(
nullptr); }
6946 static void locking_callback(
int mode,
int type,
const char * ,
6948 auto &lk = (*openSSL_locks_)[
static_cast<size_t>(type)];
6949 if (mode & CRYPTO_LOCK) {
6962 #if OPENSSL_VERSION_NUMBER < 0x1010001fL
6963 SSL_load_error_strings();
6967 OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
6972 #if OPENSSL_VERSION_NUMBER < 0x1010001fL
6978 #if OPENSSL_VERSION_NUMBER < 0x10100000L
6979 SSLThreadLocks thread_init_;
6984 inline SSLSocketStream::SSLSocketStream(
socket_t sock, SSL *ssl,
6985 time_t read_timeout_sec,
6986 time_t read_timeout_usec,
6987 time_t write_timeout_sec,
6988 time_t write_timeout_usec)
6989 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
6990 read_timeout_usec_(read_timeout_usec),
6991 write_timeout_sec_(write_timeout_sec),
6992 write_timeout_usec_(write_timeout_usec) {
6993 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
6996 inline SSLSocketStream::~SSLSocketStream() {}
6998 inline bool SSLSocketStream::is_readable()
const {
6999 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
7002 inline bool SSLSocketStream::is_writable()
const {
7003 return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) >
7007 inline ssize_t SSLSocketStream::read(
char *ptr,
size_t size) {
7008 if (SSL_pending(ssl_) > 0) {
7009 return SSL_read(ssl_, ptr,
static_cast<int>(size));
7010 }
else if (is_readable()) {
7011 auto ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
7013 auto err = SSL_get_error(ssl_, ret);
7017 (err == SSL_ERROR_WANT_READ ||
7018 err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT)) {
7020 while (--
n >= 0 && err == SSL_ERROR_WANT_READ) {
7022 if (SSL_pending(ssl_) > 0) {
7023 return SSL_read(ssl_, ptr,
static_cast<int>(size));
7024 }
else if (is_readable()) {
7025 std::this_thread::sleep_for(std::chrono::milliseconds(1));
7026 ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
7027 if (ret >= 0) {
return ret; }
7028 err = SSL_get_error(ssl_, ret);
7039 inline ssize_t SSLSocketStream::write(
const char *ptr,
size_t size) {
7040 if (is_writable()) {
7041 auto ret = SSL_write(ssl_, ptr,
static_cast<int>(size));
7043 auto err = SSL_get_error(ssl_, ret);
7047 (err == SSL_ERROR_WANT_WRITE ||
7048 err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT)) {
7050 while (--
n >= 0 && err == SSL_ERROR_WANT_WRITE) {
7052 if (is_writable()) {
7053 std::this_thread::sleep_for(std::chrono::milliseconds(1));
7054 ret = SSL_write(ssl_, ptr,
static_cast<int>(size));
7055 if (ret >= 0) {
return ret; }
7056 err = SSL_get_error(ssl_, ret);
7067 inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
7069 detail::get_remote_ip_and_port(sock_, ip, port);
7072 inline socket_t SSLSocketStream::socket()
const {
return sock_; }
7074 static SSLInit sslinit_;
7079 inline SSLServer::SSLServer(
const char *cert_path,
const char *private_key_path,
7080 const char *client_ca_cert_file_path,
7081 const char *client_ca_cert_dir_path) {
7082 ctx_ = SSL_CTX_new(TLS_method());
7085 SSL_CTX_set_options(ctx_,
7086 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
7087 SSL_OP_NO_COMPRESSION |
7088 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
7094 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
7095 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
7099 }
else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
7105 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
7106 client_ca_cert_dir_path);
7111 SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
7117 inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
7118 X509_STORE *client_ca_cert_store) {
7119 ctx_ = SSL_CTX_new(SSLv23_server_method());
7122 SSL_CTX_set_options(ctx_,
7123 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
7124 SSL_OP_NO_COMPRESSION |
7125 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
7127 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
7128 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
7131 }
else if (client_ca_cert_store) {
7133 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
7138 SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
7144 inline SSLServer::SSLServer(
7145 const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
7146 ctx_ = SSL_CTX_new(TLS_method());
7148 if (!setup_ssl_ctx_callback(*ctx_)) {
7155 inline SSLServer::~SSLServer() {
7156 if (ctx_) { SSL_CTX_free(ctx_); }
7159 inline bool SSLServer::is_valid()
const {
return ctx_; }
7161 inline bool SSLServer::process_and_close_socket(
socket_t sock) {
7162 auto ssl = detail::ssl_new(
7163 sock, ctx_, ctx_mutex_,
7165 return detail::ssl_connect_or_accept_nonblocking(
7166 sock, ssl, SSL_accept, read_timeout_sec_, read_timeout_usec_);
7168 [](SSL * ) {
return true; });
7172 ret = detail::process_server_socket_ssl(
7173 ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
7174 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
7175 write_timeout_usec_,
7176 [
this, ssl](Stream &strm,
bool close_connection,
7177 bool &connection_closed) {
7178 return process_request(strm, close_connection, connection_closed,
7179 [&](Request &req) { req.ssl = ssl; });
7184 const bool shutdown_gracefully = ret;
7185 detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully);
7188 detail::shutdown_socket(sock);
7189 detail::close_socket(sock);
7194 inline SSLClient::SSLClient(
const std::string &host)
7195 : SSLClient(host, 443, std::
string(), std::
string()) {}
7197 inline SSLClient::SSLClient(
const std::string &host,
int port)
7198 : SSLClient(host, port, std::
string(), std::
string()) {}
7200 inline SSLClient::SSLClient(
const std::string &host,
int port,
7201 const std::string &client_cert_path,
7202 const std::string &client_key_path)
7203 : ClientImpl(host, port, client_cert_path, client_key_path) {
7204 ctx_ = SSL_CTX_new(SSLv23_client_method());
7206 detail::split(&host_[0], &host_[host_.size()],
'.',
7207 [&](
const char *b,
const char *
e) {
7208 host_components_.emplace_back(std::string(b, e));
7210 if (!client_cert_path.empty() && !client_key_path.empty()) {
7211 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
7212 SSL_FILETYPE_PEM) != 1 ||
7213 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
7214 SSL_FILETYPE_PEM) != 1) {
7221 inline SSLClient::SSLClient(
const std::string &host,
int port,
7222 X509 *client_cert, EVP_PKEY *client_key)
7223 : ClientImpl(host, port) {
7224 ctx_ = SSL_CTX_new(SSLv23_client_method());
7226 detail::split(&host_[0], &host_[host_.size()],
'.',
7227 [&](
const char *b,
const char *
e) {
7228 host_components_.emplace_back(std::string(b, e));
7230 if (client_cert !=
nullptr && client_key !=
nullptr) {
7231 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
7232 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
7239 inline SSLClient::~SSLClient() {
7240 if (ctx_) { SSL_CTX_free(ctx_); }
7244 shutdown_ssl_impl(socket_,
true);
7247 inline bool SSLClient::is_valid()
const {
return ctx_; }
7249 inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
7250 if (ca_cert_store) {
7252 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
7254 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
7257 X509_STORE_free(ca_cert_store);
7262 inline long SSLClient::get_openssl_verify_result()
const {
7263 return verify_result_;
7266 inline SSL_CTX *SSLClient::ssl_context()
const {
return ctx_; }
7268 inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
7269 return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
7273 inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
7274 bool &success, Error &error) {
7277 if (!detail::process_client_socket(
7278 socket.sock, read_timeout_sec_, read_timeout_usec_,
7279 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
7281 req2.method =
"CONNECT";
7282 req2.path = host_and_port_;
7283 return process_request(strm, req2, res2, false, error);
7287 shutdown_ssl(socket,
true);
7288 shutdown_socket(socket);
7289 close_socket(socket);
7294 if (res2.status == 407) {
7295 if (!proxy_digest_auth_username_.empty() &&
7296 !proxy_digest_auth_password_.empty()) {
7297 std::map<std::string, std::string> auth;
7298 if (detail::parse_www_authenticate(res2, auth,
true)) {
7300 if (!detail::process_client_socket(
7301 socket.sock, read_timeout_sec_, read_timeout_usec_,
7302 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
7304 req3.method =
"CONNECT";
7305 req3.path = host_and_port_;
7306 req3.headers.insert(detail::make_digest_authentication_header(
7307 req3, auth, 1, detail::random_string(10),
7308 proxy_digest_auth_username_, proxy_digest_auth_password_,
7310 return process_request(strm, req3, res3, false, error);
7314 shutdown_ssl(socket,
true);
7315 shutdown_socket(socket);
7316 close_socket(socket);
7330 inline bool SSLClient::load_certs() {
7333 std::call_once(initialize_cert_, [&]() {
7334 std::lock_guard<std::mutex> guard(ctx_mutex_);
7335 if (!ca_cert_file_path_.empty()) {
7336 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
7340 }
else if (!ca_cert_dir_path_.empty()) {
7341 if (!SSL_CTX_load_verify_locations(ctx_,
nullptr,
7342 ca_cert_dir_path_.c_str())) {
7347 detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
7349 SSL_CTX_set_default_verify_paths(ctx_);
7357 inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
7358 auto ssl = detail::ssl_new(
7359 socket.sock, ctx_, ctx_mutex_,
7361 if (server_certificate_verification_) {
7362 if (!load_certs()) {
7363 error = Error::SSLLoadingCerts;
7366 SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
7369 if (!detail::ssl_connect_or_accept_nonblocking(
7370 socket.sock, ssl, SSL_connect, connection_timeout_sec_,
7371 connection_timeout_usec_)) {
7372 error = Error::SSLConnection;
7376 if (server_certificate_verification_) {
7377 verify_result_ = SSL_get_verify_result(ssl);
7379 if (verify_result_ != X509_V_OK) {
7380 error = Error::SSLServerVerification;
7384 auto server_cert = SSL_get_peer_certificate(ssl);
7386 if (server_cert ==
nullptr) {
7387 error = Error::SSLServerVerification;
7391 if (!verify_host(server_cert)) {
7392 X509_free(server_cert);
7393 error = Error::SSLServerVerification;
7396 X509_free(server_cert);
7402 SSL_set_tlsext_host_name(ssl, host_.c_str());
7411 shutdown_socket(socket);
7412 close_socket(socket);
7416 inline void SSLClient::shutdown_ssl(Socket &socket,
bool shutdown_gracefully) {
7417 shutdown_ssl_impl(socket, shutdown_gracefully);
7420 inline void SSLClient::shutdown_ssl_impl(Socket &socket,
7421 bool shutdown_gracefully) {
7422 if (socket.sock == INVALID_SOCKET) {
7423 assert(socket.ssl ==
nullptr);
7427 detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
7428 socket.ssl =
nullptr;
7430 assert(socket.ssl ==
nullptr);
7434 SSLClient::process_socket(
const Socket &socket,
7435 std::function<
bool(Stream &strm)> callback) {
7437 return detail::process_client_socket_ssl(
7438 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
7439 write_timeout_sec_, write_timeout_usec_, std::move(callback));
7442 inline bool SSLClient::is_ssl()
const {
return true; }
7444 inline bool SSLClient::verify_host(X509 *server_cert)
const {
7466 return verify_host_with_subject_alt_name(server_cert) ||
7467 verify_host_with_common_name(server_cert);
7471 SSLClient::verify_host_with_subject_alt_name(X509 *server_cert)
const {
7474 auto type = GEN_DNS;
7476 struct in6_addr addr6;
7477 struct in_addr addr;
7478 size_t addr_len = 0;
7481 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
7483 addr_len =
sizeof(
struct in6_addr);
7484 }
else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
7486 addr_len =
sizeof(
struct in_addr);
7490 auto alt_names =
static_cast<const struct stack_st_GENERAL_NAME *
>(
7491 X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr,
nullptr));
7494 auto dsn_matched =
false;
7495 auto ip_mached =
false;
7497 auto count = sk_GENERAL_NAME_num(alt_names);
7499 for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
7500 auto val = sk_GENERAL_NAME_value(alt_names, i);
7501 if (val->type == type) {
7503 auto name_len = (size_t)ASN1_STRING_length(val->d.ia5);
7506 case GEN_DNS: dsn_matched = check_host_name(name, name_len);
break;
7509 if (!memcmp(&addr6, name, addr_len) ||
7510 !memcmp(&addr, name, addr_len)) {
7518 if (dsn_matched || ip_mached) { ret =
true; }
7521 GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
7525 inline bool SSLClient::verify_host_with_common_name(X509 *server_cert)
const {
7526 const auto subject_name = X509_get_subject_name(server_cert);
7528 if (subject_name !=
nullptr) {
7530 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
7531 name,
sizeof(name));
7533 if (name_len != -1) {
7534 return check_host_name(name,
static_cast<size_t>(name_len));
7541 inline bool SSLClient::check_host_name(
const char *pattern,
7542 size_t pattern_len)
const {
7543 if (host_.size() == pattern_len && host_ == pattern) {
return true; }
7547 std::vector<std::string> pattern_components;
7548 detail::split(&pattern[0], &pattern[pattern_len],
'.',
7549 [&](
const char *b,
const char *
e) {
7550 pattern_components.emplace_back(std::string(b,
e));
7553 if (host_components_.size() != pattern_components.size()) {
return false; }
7555 auto itr = pattern_components.begin();
7556 for (
const auto &h : host_components_) {
7558 if (p != h && p !=
"*") {
7559 auto partial_match = (p.size() > 0 && p[p.size() - 1] ==
'*' &&
7560 !p.compare(0, p.size() - 1, h));
7561 if (!partial_match) {
return false; }
7571 inline Client::Client(
const std::string &scheme_host_port)
7572 : Client(scheme_host_port, std::
string(), std::
string()) {}
7574 inline Client::Client(
const std::string &scheme_host_port,
7575 const std::string &client_cert_path,
7576 const std::string &client_key_path) {
7577 const static std::regex re(
7578 R
"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
7581 if (std::regex_match(scheme_host_port, m, re)) {
7582 auto scheme = m[1].str();
7584 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7585 if (!scheme.empty() && (scheme !=
"http" && scheme !=
"https")) {
7587 if (!scheme.empty() && scheme !=
"http") {
7589 #ifndef CPPHTTPLIB_NO_EXCEPTIONS
7590 std::string msg =
"'" + scheme +
"' scheme is not supported.";
7591 throw std::invalid_argument(msg);
7596 auto is_ssl = scheme ==
"https";
7598 auto host = m[2].str();
7599 if (host.empty()) { host = m[3].str(); }
7601 auto port_str = m[4].str();
7602 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
7605 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7606 cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
7607 client_cert_path, client_key_path);
7611 cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
7612 client_cert_path, client_key_path);
7615 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
7616 client_cert_path, client_key_path);
7620 inline Client::Client(
const std::string &host,
int port)
7621 : cli_(detail::make_unique<ClientImpl>(host, port)) {}
7623 inline Client::Client(
const std::string &host,
int port,
7624 const std::string &client_cert_path,
7625 const std::string &client_key_path)
7626 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
7627 client_key_path)) {}
7629 inline Client::~Client() {}
7631 inline bool Client::is_valid()
const {
7632 return cli_ !=
nullptr && cli_->is_valid();
7635 inline Result Client::Get(
const char *path) {
return cli_->Get(path); }
7636 inline Result Client::Get(
const char *path,
const Headers &headers) {
7637 return cli_->Get(path, headers);
7639 inline Result Client::Get(
const char *path, Progress progress) {
7640 return cli_->Get(path, std::move(progress));
7642 inline Result Client::Get(
const char *path,
const Headers &headers,
7643 Progress progress) {
7644 return cli_->Get(path, headers, std::move(progress));
7646 inline Result Client::Get(
const char *path, ContentReceiver content_receiver) {
7647 return cli_->Get(path, std::move(content_receiver));
7649 inline Result Client::Get(
const char *path,
const Headers &headers,
7650 ContentReceiver content_receiver) {
7651 return cli_->Get(path, headers, std::move(content_receiver));
7653 inline Result Client::Get(
const char *path, ContentReceiver content_receiver,
7654 Progress progress) {
7655 return cli_->Get(path, std::move(content_receiver), std::move(progress));
7657 inline Result Client::Get(
const char *path,
const Headers &headers,
7658 ContentReceiver content_receiver, Progress progress) {
7659 return cli_->Get(path, headers, std::move(content_receiver),
7660 std::move(progress));
7662 inline Result Client::Get(
const char *path, ResponseHandler response_handler,
7663 ContentReceiver content_receiver) {
7664 return cli_->Get(path, std::move(response_handler),
7665 std::move(content_receiver));
7667 inline Result Client::Get(
const char *path,
const Headers &headers,
7668 ResponseHandler response_handler,
7669 ContentReceiver content_receiver) {
7670 return cli_->Get(path, headers, std::move(response_handler),
7671 std::move(content_receiver));
7673 inline Result Client::Get(
const char *path, ResponseHandler response_handler,
7674 ContentReceiver content_receiver, Progress progress) {
7675 return cli_->Get(path, std::move(response_handler),
7676 std::move(content_receiver), std::move(progress));
7678 inline Result Client::Get(
const char *path,
const Headers &headers,
7679 ResponseHandler response_handler,
7680 ContentReceiver content_receiver, Progress progress) {
7681 return cli_->Get(path, headers, std::move(response_handler),
7682 std::move(content_receiver), std::move(progress));
7684 inline Result Client::Get(
const char *path,
const Params ¶ms,
7685 const Headers &headers, Progress progress) {
7686 return cli_->Get(path, params, headers, progress);
7688 inline Result Client::Get(
const char *path,
const Params ¶ms,
7689 const Headers &headers,
7690 ContentReceiver content_receiver, Progress progress) {
7691 return cli_->Get(path, params, headers, content_receiver, progress);
7693 inline Result Client::Get(
const char *path,
const Params ¶ms,
7694 const Headers &headers,
7695 ResponseHandler response_handler,
7696 ContentReceiver content_receiver, Progress progress) {
7697 return cli_->Get(path, params, headers, response_handler, content_receiver,
7701 inline Result Client::Head(
const char *path) {
return cli_->Head(path); }
7702 inline Result Client::Head(
const char *path,
const Headers &headers) {
7703 return cli_->Head(path, headers);
7706 inline Result Client::Post(
const char *path) {
return cli_->Post(path); }
7707 inline Result Client::Post(
const char *path,
const char *body,
7708 size_t content_length,
const char *content_type) {
7709 return cli_->Post(path, body, content_length, content_type);
7711 inline Result Client::Post(
const char *path,
const Headers &headers,
7712 const char *body,
size_t content_length,
7713 const char *content_type) {
7714 return cli_->Post(path, headers, body, content_length, content_type);
7716 inline Result Client::Post(
const char *path,
const std::string &body,
7717 const char *content_type) {
7718 return cli_->Post(path, body, content_type);
7720 inline Result Client::Post(
const char *path,
const Headers &headers,
7721 const std::string &body,
const char *content_type) {
7722 return cli_->Post(path, headers, body, content_type);
7724 inline Result Client::Post(
const char *path,
size_t content_length,
7725 ContentProvider content_provider,
7726 const char *content_type) {
7727 return cli_->Post(path, content_length, std::move(content_provider),
7730 inline Result Client::Post(
const char *path,
7731 ContentProviderWithoutLength content_provider,
7732 const char *content_type) {
7733 return cli_->Post(path, std::move(content_provider), content_type);
7735 inline Result Client::Post(
const char *path,
const Headers &headers,
7736 size_t content_length,
7737 ContentProvider content_provider,
7738 const char *content_type) {
7739 return cli_->Post(path, headers, content_length, std::move(content_provider),
7742 inline Result Client::Post(
const char *path,
const Headers &headers,
7743 ContentProviderWithoutLength content_provider,
7744 const char *content_type) {
7745 return cli_->Post(path, headers, std::move(content_provider), content_type);
7747 inline Result Client::Post(
const char *path,
const Params ¶ms) {
7748 return cli_->Post(path, params);
7750 inline Result Client::Post(
const char *path,
const Headers &headers,
7751 const Params ¶ms) {
7752 return cli_->Post(path, headers, params);
7754 inline Result Client::Post(
const char *path,
7755 const MultipartFormDataItems &items) {
7756 return cli_->Post(path, items);
7758 inline Result Client::Post(
const char *path,
const Headers &headers,
7759 const MultipartFormDataItems &items) {
7760 return cli_->Post(path, headers, items);
7762 inline Result Client::Post(
const char *path,
const Headers &headers,
7763 const MultipartFormDataItems &items,
7764 const std::string &boundary) {
7765 return cli_->Post(path, headers, items, boundary);
7767 inline Result Client::Put(
const char *path) {
return cli_->Put(path); }
7768 inline Result Client::Put(
const char *path,
const char *body,
7769 size_t content_length,
const char *content_type) {
7770 return cli_->Put(path, body, content_length, content_type);
7772 inline Result Client::Put(
const char *path,
const Headers &headers,
7773 const char *body,
size_t content_length,
7774 const char *content_type) {
7775 return cli_->Put(path, headers, body, content_length, content_type);
7777 inline Result Client::Put(
const char *path,
const std::string &body,
7778 const char *content_type) {
7779 return cli_->Put(path, body, content_type);
7781 inline Result Client::Put(
const char *path,
const Headers &headers,
7782 const std::string &body,
const char *content_type) {
7783 return cli_->Put(path, headers, body, content_type);
7785 inline Result Client::Put(
const char *path,
size_t content_length,
7786 ContentProvider content_provider,
7787 const char *content_type) {
7788 return cli_->Put(path, content_length, std::move(content_provider),
7791 inline Result Client::Put(
const char *path,
7792 ContentProviderWithoutLength content_provider,
7793 const char *content_type) {
7794 return cli_->Put(path, std::move(content_provider), content_type);
7796 inline Result Client::Put(
const char *path,
const Headers &headers,
7797 size_t content_length,
7798 ContentProvider content_provider,
7799 const char *content_type) {
7800 return cli_->Put(path, headers, content_length, std::move(content_provider),
7803 inline Result Client::Put(
const char *path,
const Headers &headers,
7804 ContentProviderWithoutLength content_provider,
7805 const char *content_type) {
7806 return cli_->Put(path, headers, std::move(content_provider), content_type);
7808 inline Result Client::Put(
const char *path,
const Params ¶ms) {
7809 return cli_->Put(path, params);
7811 inline Result Client::Put(
const char *path,
const Headers &headers,
7812 const Params ¶ms) {
7813 return cli_->Put(path, headers, params);
7815 inline Result Client::Patch(
const char *path) {
return cli_->Patch(path); }
7816 inline Result Client::Patch(
const char *path,
const char *body,
7817 size_t content_length,
const char *content_type) {
7818 return cli_->Patch(path, body, content_length, content_type);
7820 inline Result Client::Patch(
const char *path,
const Headers &headers,
7821 const char *body,
size_t content_length,
7822 const char *content_type) {
7823 return cli_->Patch(path, headers, body, content_length, content_type);
7825 inline Result Client::Patch(
const char *path,
const std::string &body,
7826 const char *content_type) {
7827 return cli_->Patch(path, body, content_type);
7829 inline Result Client::Patch(
const char *path,
const Headers &headers,
7830 const std::string &body,
const char *content_type) {
7831 return cli_->Patch(path, headers, body, content_type);
7833 inline Result Client::Patch(
const char *path,
size_t content_length,
7834 ContentProvider content_provider,
7835 const char *content_type) {
7836 return cli_->Patch(path, content_length, std::move(content_provider),
7839 inline Result Client::Patch(
const char *path,
7840 ContentProviderWithoutLength content_provider,
7841 const char *content_type) {
7842 return cli_->Patch(path, std::move(content_provider), content_type);
7844 inline Result Client::Patch(
const char *path,
const Headers &headers,
7845 size_t content_length,
7846 ContentProvider content_provider,
7847 const char *content_type) {
7848 return cli_->Patch(path, headers, content_length, std::move(content_provider),
7851 inline Result Client::Patch(
const char *path,
const Headers &headers,
7852 ContentProviderWithoutLength content_provider,
7853 const char *content_type) {
7854 return cli_->Patch(path, headers, std::move(content_provider), content_type);
7856 inline Result Client::Delete(
const char *path) {
return cli_->Delete(path); }
7857 inline Result Client::Delete(
const char *path,
const Headers &headers) {
7858 return cli_->Delete(path, headers);
7860 inline Result Client::Delete(
const char *path,
const char *body,
7861 size_t content_length,
const char *content_type) {
7862 return cli_->Delete(path, body, content_length, content_type);
7864 inline Result Client::Delete(
const char *path,
const Headers &headers,
7865 const char *body,
size_t content_length,
7866 const char *content_type) {
7867 return cli_->Delete(path, headers, body, content_length, content_type);
7869 inline Result Client::Delete(
const char *path,
const std::string &body,
7870 const char *content_type) {
7871 return cli_->Delete(path, body, content_type);
7873 inline Result Client::Delete(
const char *path,
const Headers &headers,
7874 const std::string &body,
7875 const char *content_type) {
7876 return cli_->Delete(path, headers, body, content_type);
7878 inline Result Client::Options(
const char *path) {
return cli_->Options(path); }
7879 inline Result Client::Options(
const char *path,
const Headers &headers) {
7880 return cli_->Options(path, headers);
7883 inline bool Client::send(Request &req, Response &res, Error &error) {
7884 return cli_->send(req, res, error);
7887 inline Result Client::send(
const Request &req) {
return cli_->send(req); }
7889 inline size_t Client::is_socket_open()
const {
return cli_->is_socket_open(); }
7891 inline void Client::stop() { cli_->stop(); }
7893 inline void Client::set_hostname_addr_map(
7894 const std::map<std::string, std::string> addr_map) {
7895 cli_->set_hostname_addr_map(std::move(addr_map));
7898 inline void Client::set_default_headers(Headers headers) {
7899 cli_->set_default_headers(std::move(headers));
7902 inline void Client::set_address_family(
int family) {
7903 cli_->set_address_family(family);
7906 inline void Client::set_tcp_nodelay(
bool on) { cli_->set_tcp_nodelay(on); }
7908 inline void Client::set_socket_options(SocketOptions socket_options) {
7909 cli_->set_socket_options(std::move(socket_options));
7912 inline void Client::set_connection_timeout(time_t sec, time_t usec) {
7913 cli_->set_connection_timeout(sec, usec);
7916 inline void Client::set_read_timeout(time_t sec, time_t usec) {
7917 cli_->set_read_timeout(sec, usec);
7920 inline void Client::set_write_timeout(time_t sec, time_t usec) {
7921 cli_->set_write_timeout(sec, usec);
7924 inline void Client::set_basic_auth(
const char *username,
const char *password) {
7925 cli_->set_basic_auth(username, password);
7927 inline void Client::set_bearer_token_auth(
const char *token) {
7928 cli_->set_bearer_token_auth(token);
7930 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7931 inline void Client::set_digest_auth(
const char *username,
7932 const char *password) {
7933 cli_->set_digest_auth(username, password);
7937 inline void Client::set_keep_alive(
bool on) { cli_->set_keep_alive(on); }
7938 inline void Client::set_follow_location(
bool on) {
7939 cli_->set_follow_location(on);
7942 inline void Client::set_url_encode(
bool on) { cli_->set_url_encode(on); }
7944 inline void Client::set_compress(
bool on) { cli_->set_compress(on); }
7946 inline void Client::set_decompress(
bool on) { cli_->set_decompress(on); }
7948 inline void Client::set_interface(
const char *intf) {
7949 cli_->set_interface(intf);
7952 inline void Client::set_proxy(
const char *host,
int port) {
7953 cli_->set_proxy(host, port);
7955 inline void Client::set_proxy_basic_auth(
const char *username,
7956 const char *password) {
7957 cli_->set_proxy_basic_auth(username, password);
7959 inline void Client::set_proxy_bearer_token_auth(
const char *token) {
7960 cli_->set_proxy_bearer_token_auth(token);
7962 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7963 inline void Client::set_proxy_digest_auth(
const char *username,
7964 const char *password) {
7965 cli_->set_proxy_digest_auth(username, password);
7969 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7970 inline void Client::enable_server_certificate_verification(
bool enabled) {
7971 cli_->enable_server_certificate_verification(enabled);
7975 inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); }
7977 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7978 inline void Client::set_ca_cert_path(
const char *ca_cert_file_path,
7979 const char *ca_cert_dir_path) {
7980 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
7983 inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
7985 static_cast<SSLClient &
>(*cli_).set_ca_cert_store(ca_cert_store);
7987 cli_->set_ca_cert_store(ca_cert_store);
7991 inline long Client::get_openssl_verify_result()
const {
7993 return static_cast<SSLClient &
>(*cli_).get_openssl_verify_result();
7998 inline SSL_CTX *Client::ssl_context()
const {
7999 if (is_ssl_) {
return static_cast<SSLClient &
>(*cli_).ssl_context(); }
const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *asn1)
LinearTransformMean< MeanType > operator*(Eigen::MatrixXd const &A, MeanType const &K)
@ key
the parser read a key of a value in an object
@ error
throw a parse_error exception in case of a tag
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values