summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--externals/httplib/README.md2
-rw-r--r--externals/httplib/httplib.h1175
2 files changed, 750 insertions, 427 deletions
diff --git a/externals/httplib/README.md b/externals/httplib/README.md
index c28db9c76..73037d297 100644
--- a/externals/httplib/README.md
+++ b/externals/httplib/README.md
@@ -1,4 +1,4 @@
1From https://github.com/yhirose/cpp-httplib/tree/v0.4.2 1From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53
2 2
3MIT License 3MIT License
4 4
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
index ad4ce279e..a554b8619 100644
--- a/externals/httplib/httplib.h
+++ b/externals/httplib/httplib.h
@@ -1,7 +1,7 @@
1// 1//
2// httplib.h 2// httplib.h
3// 3//
4// Copyright (c) 2019 Yuji Hirose. All rights reserved. 4// Copyright (c) 2020 Yuji Hirose. All rights reserved.
5// MIT License 5// MIT License
6// 6//
7 7
@@ -41,7 +41,7 @@
41#endif 41#endif
42 42
43#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH 43#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
44#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max)() 44#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max())
45#endif 45#endif
46 46
47#ifndef CPPHTTPLIB_RECV_BUFSIZ 47#ifndef CPPHTTPLIB_RECV_BUFSIZ
@@ -49,7 +49,8 @@
49#endif 49#endif
50 50
51#ifndef CPPHTTPLIB_THREAD_POOL_COUNT 51#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
52#define CPPHTTPLIB_THREAD_POOL_COUNT 8 52#define CPPHTTPLIB_THREAD_POOL_COUNT \
53 (std::max(1u, std::thread::hardware_concurrency() - 1))
53#endif 54#endif
54 55
55/* 56/*
@@ -190,23 +191,11 @@ struct ci {
190 191
191} // namespace detail 192} // namespace detail
192 193
193enum class HttpVersion { v1_0 = 0, v1_1 };
194
195using Headers = std::multimap<std::string, std::string, detail::ci>; 194using Headers = std::multimap<std::string, std::string, detail::ci>;
196 195
197using Params = std::multimap<std::string, std::string>; 196using Params = std::multimap<std::string, std::string>;
198using Match = std::smatch; 197using Match = std::smatch;
199 198
200using DataSink = std::function<void(const char *data, size_t data_len)>;
201
202using Done = std::function<void()>;
203
204using ContentProvider =
205 std::function<void(size_t offset, size_t length, DataSink sink)>;
206
207using ContentProviderWithCloser =
208 std::function<void(size_t offset, size_t length, DataSink sink, Done done)>;
209
210using Progress = std::function<bool(uint64_t current, uint64_t total)>; 199using Progress = std::function<bool(uint64_t current, uint64_t total)>;
211 200
212struct Response; 201struct Response;
@@ -221,6 +210,22 @@ struct MultipartFormData {
221using MultipartFormDataItems = std::vector<MultipartFormData>; 210using MultipartFormDataItems = std::vector<MultipartFormData>;
222using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>; 211using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
223 212
213class DataSink {
214public:
215 DataSink() = default;
216 DataSink(const DataSink &) = delete;
217 DataSink &operator=(const DataSink &) = delete;
218 DataSink(DataSink &&) = delete;
219 DataSink &operator=(DataSink &&) = delete;
220
221 std::function<void(const char *data, size_t data_len)> write;
222 std::function<void()> done;
223 std::function<bool()> is_writable;
224};
225
226using ContentProvider =
227 std::function<void(size_t offset, size_t length, DataSink &sink)>;
228
224using ContentReceiver = 229using ContentReceiver =
225 std::function<bool(const char *data, size_t data_length)>; 230 std::function<bool(const char *data, size_t data_length)>;
226 231
@@ -296,7 +301,7 @@ struct Request {
296 301
297struct Response { 302struct Response {
298 std::string version; 303 std::string version;
299 int status; 304 int status = -1;
300 Headers headers; 305 Headers headers;
301 std::string body; 306 std::string body;
302 307
@@ -312,15 +317,19 @@ struct Response {
312 317
313 void set_content_provider( 318 void set_content_provider(
314 size_t length, 319 size_t length,
315 std::function<void(size_t offset, size_t length, DataSink sink)> provider, 320 std::function<void(size_t offset, size_t length, DataSink &sink)>
321 provider,
316 std::function<void()> resource_releaser = [] {}); 322 std::function<void()> resource_releaser = [] {});
317 323
318 void set_chunked_content_provider( 324 void set_chunked_content_provider(
319 std::function<void(size_t offset, DataSink sink, Done done)> provider, 325 std::function<void(size_t offset, DataSink &sink)> provider,
320 std::function<void()> resource_releaser = [] {}); 326 std::function<void()> resource_releaser = [] {});
321 327
322 Response() : status(-1), content_length(0) {} 328 Response() = default;
323 329 Response(const Response &) = default;
330 Response &operator=(const Response &) = default;
331 Response(Response &&) = default;
332 Response &operator=(Response &&) = default;
324 ~Response() { 333 ~Response() {
325 if (content_provider_resource_releaser) { 334 if (content_provider_resource_releaser) {
326 content_provider_resource_releaser(); 335 content_provider_resource_releaser();
@@ -328,57 +337,26 @@ struct Response {
328 } 337 }
329 338
330 // private members... 339 // private members...
331 size_t content_length; 340 size_t content_length = 0;
332 ContentProviderWithCloser content_provider; 341 ContentProvider content_provider;
333 std::function<void()> content_provider_resource_releaser; 342 std::function<void()> content_provider_resource_releaser;
334}; 343};
335 344
336class Stream { 345class Stream {
337public: 346public:
338 virtual ~Stream() = default; 347 virtual ~Stream() = default;
348
349 virtual bool is_readable() const = 0;
350 virtual bool is_writable() const = 0;
351
339 virtual int read(char *ptr, size_t size) = 0; 352 virtual int read(char *ptr, size_t size) = 0;
340 virtual int write(const char *ptr, size_t size1) = 0; 353 virtual int write(const char *ptr, size_t size) = 0;
341 virtual int write(const char *ptr) = 0;
342 virtual int write(const std::string &s) = 0;
343 virtual std::string get_remote_addr() const = 0; 354 virtual std::string get_remote_addr() const = 0;
344 355
345 template <typename... Args> 356 template <typename... Args>
346 int write_format(const char *fmt, const Args &... args); 357 int write_format(const char *fmt, const Args &... args);
347}; 358 int write(const char *ptr);
348 359 int write(const std::string &s);
349class SocketStream : public Stream {
350public:
351 SocketStream(socket_t sock, time_t read_timeout_sec,
352 time_t read_timeout_usec);
353 ~SocketStream() override;
354
355 int read(char *ptr, size_t size) override;
356 int write(const char *ptr, size_t size) override;
357 int write(const char *ptr) override;
358 int write(const std::string &s) override;
359 std::string get_remote_addr() const override;
360
361private:
362 socket_t sock_;
363 time_t read_timeout_sec_;
364 time_t read_timeout_usec_;
365};
366
367class BufferStream : public Stream {
368public:
369 BufferStream() = default;
370 ~BufferStream() override = default;
371
372 int read(char *ptr, size_t size) override;
373 int write(const char *ptr, size_t size) override;
374 int write(const char *ptr) override;
375 int write(const std::string &s) override;
376 std::string get_remote_addr() const override;
377
378 const std::string &get_buffer() const;
379
380private:
381 std::string buffer;
382}; 360};
383 361
384class TaskQueue { 362class TaskQueue {
@@ -389,7 +367,6 @@ public:
389 virtual void shutdown() = 0; 367 virtual void shutdown() = 0;
390}; 368};
391 369
392#if CPPHTTPLIB_THREAD_POOL_COUNT > 0
393class ThreadPool : public TaskQueue { 370class ThreadPool : public TaskQueue {
394public: 371public:
395 explicit ThreadPool(size_t n) : shutdown_(false) { 372 explicit ThreadPool(size_t n) : shutdown_(false) {
@@ -459,58 +436,16 @@ private:
459 std::condition_variable cond_; 436 std::condition_variable cond_;
460 std::mutex mutex_; 437 std::mutex mutex_;
461}; 438};
462#elif CPPHTTPLIB_THREAD_POOL_COUNT == 0
463class Threads : public TaskQueue {
464public:
465 Threads() : running_threads_(0) {}
466 virtual ~Threads() {}
467
468 virtual void enqueue(std::function<void()> fn) override {
469 std::thread([=]() {
470 {
471 std::lock_guard<std::mutex> guard(running_threads_mutex_);
472 running_threads_++;
473 }
474
475 fn();
476 439
477 { 440using Logger = std::function<void(const Request &, const Response &)>;
478 std::lock_guard<std::mutex> guard(running_threads_mutex_);
479 running_threads_--;
480 }
481 }).detach();
482 }
483
484 virtual void shutdown() override {
485 for (;;) {
486 std::this_thread::sleep_for(std::chrono::milliseconds(10));
487 std::lock_guard<std::mutex> guard(running_threads_mutex_);
488 if (!running_threads_) { break; }
489 }
490 }
491
492private:
493 std::mutex running_threads_mutex_;
494 int running_threads_;
495};
496#else
497class NoThread : public TaskQueue {
498public:
499 NoThread() {}
500 virtual ~NoThread() {}
501
502 virtual void enqueue(std::function<void()> fn) override { fn(); }
503
504 virtual void shutdown() override {}
505};
506#endif
507 441
508class Server { 442class Server {
509public: 443public:
510 using Handler = std::function<void(const Request &, Response &)>; 444 using Handler = std::function<void(const Request &, Response &)>;
511 using HandlerWithContentReader = std::function<void( 445 using HandlerWithContentReader = std::function<void(
512 const Request &, Response &, const ContentReader &content_reader)>; 446 const Request &, Response &, const ContentReader &content_reader)>;
513 using Logger = std::function<void(const Request &, const Response &)>; 447 using Expect100ContinueHandler =
448 std::function<int(const Request &, Response &)>;
514 449
515 Server(); 450 Server();
516 451
@@ -528,12 +463,19 @@ public:
528 Server &Delete(const char *pattern, Handler handler); 463 Server &Delete(const char *pattern, Handler handler);
529 Server &Options(const char *pattern, Handler handler); 464 Server &Options(const char *pattern, Handler handler);
530 465
531 bool set_base_dir(const char *dir, const char *mount_point = nullptr); 466 [[deprecated]] bool set_base_dir(const char *dir,
467 const char *mount_point = nullptr);
468 bool set_mount_point(const char *mount_point, const char *dir);
469 bool remove_mount_point(const char *mount_point);
470 void set_file_extension_and_mimetype_mapping(const char *ext,
471 const char *mime);
532 void set_file_request_handler(Handler handler); 472 void set_file_request_handler(Handler handler);
533 473
534 void set_error_handler(Handler handler); 474 void set_error_handler(Handler handler);
535 void set_logger(Logger logger); 475 void set_logger(Logger logger);
536 476
477 void set_expect_100_continue_handler(Expect100ContinueHandler handler);
478
537 void set_keep_alive_max_count(size_t count); 479 void set_keep_alive_max_count(size_t count);
538 void set_read_timeout(time_t sec, time_t usec); 480 void set_read_timeout(time_t sec, time_t usec);
539 void set_payload_max_length(size_t length); 481 void set_payload_max_length(size_t length);
@@ -561,7 +503,7 @@ protected:
561 503
562private: 504private:
563 using Handlers = std::vector<std::pair<std::regex, Handler>>; 505 using Handlers = std::vector<std::pair<std::regex, Handler>>;
564 using HandersForContentReader = 506 using HandlersForContentReader =
565 std::vector<std::pair<std::regex, HandlerWithContentReader>>; 507 std::vector<std::pair<std::regex, HandlerWithContentReader>>;
566 508
567 socket_t create_server_socket(const char *host, int port, 509 socket_t create_server_socket(const char *host, int port,
@@ -570,11 +512,11 @@ private:
570 bool listen_internal(); 512 bool listen_internal();
571 513
572 bool routing(Request &req, Response &res, Stream &strm, bool last_connection); 514 bool routing(Request &req, Response &res, Stream &strm, bool last_connection);
573 bool handle_file_request(Request &req, Response &res); 515 bool handle_file_request(Request &req, Response &res, bool head = false);
574 bool dispatch_request(Request &req, Response &res, Handlers &handlers); 516 bool dispatch_request(Request &req, Response &res, Handlers &handlers);
575 bool dispatch_request_for_content_reader(Request &req, Response &res, 517 bool dispatch_request_for_content_reader(Request &req, Response &res,
576 ContentReader content_reader, 518 ContentReader content_reader,
577 HandersForContentReader &handlers); 519 HandlersForContentReader &handlers);
578 520
579 bool parse_request_line(const char *s, Request &req); 521 bool parse_request_line(const char *s, Request &req);
580 bool write_response(Stream &strm, bool last_connection, const Request &req, 522 bool write_response(Stream &strm, bool last_connection, const Request &req,
@@ -598,23 +540,27 @@ private:
598 std::atomic<bool> is_running_; 540 std::atomic<bool> is_running_;
599 std::atomic<socket_t> svr_sock_; 541 std::atomic<socket_t> svr_sock_;
600 std::vector<std::pair<std::string, std::string>> base_dirs_; 542 std::vector<std::pair<std::string, std::string>> base_dirs_;
543 std::map<std::string, std::string> file_extension_and_mimetype_map_;
601 Handler file_request_handler_; 544 Handler file_request_handler_;
602 Handlers get_handlers_; 545 Handlers get_handlers_;
603 Handlers post_handlers_; 546 Handlers post_handlers_;
604 HandersForContentReader post_handlers_for_content_reader_; 547 HandlersForContentReader post_handlers_for_content_reader_;
605 Handlers put_handlers_; 548 Handlers put_handlers_;
606 HandersForContentReader put_handlers_for_content_reader_; 549 HandlersForContentReader put_handlers_for_content_reader_;
607 Handlers patch_handlers_; 550 Handlers patch_handlers_;
608 HandersForContentReader patch_handlers_for_content_reader_; 551 HandlersForContentReader patch_handlers_for_content_reader_;
609 Handlers delete_handlers_; 552 Handlers delete_handlers_;
610 Handlers options_handlers_; 553 Handlers options_handlers_;
611 Handler error_handler_; 554 Handler error_handler_;
612 Logger logger_; 555 Logger logger_;
556 Expect100ContinueHandler expect_100_continue_handler_;
613}; 557};
614 558
615class Client { 559class Client {
616public: 560public:
617 explicit Client(const char *host, int port = 80, time_t timeout_sec = 300); 561 explicit Client(const std::string &host, int port = 80,
562 const std::string &client_cert_path = std::string(),
563 const std::string &client_key_path = std::string());
618 564
619 virtual ~Client(); 565 virtual ~Client();
620 566
@@ -698,6 +644,11 @@ public:
698 ContentProvider content_provider, 644 ContentProvider content_provider,
699 const char *content_type); 645 const char *content_type);
700 646
647 std::shared_ptr<Response> Put(const char *path, const Params &params);
648
649 std::shared_ptr<Response> Put(const char *path, const Headers &headers,
650 const Params &params);
651
701 std::shared_ptr<Response> Patch(const char *path, const std::string &body, 652 std::shared_ptr<Response> Patch(const char *path, const std::string &body,
702 const char *content_type); 653 const char *content_type);
703 654
@@ -734,11 +685,17 @@ public:
734 bool send(const std::vector<Request> &requests, 685 bool send(const std::vector<Request> &requests,
735 std::vector<Response> &responses); 686 std::vector<Response> &responses);
736 687
737 void set_keep_alive_max_count(size_t count); 688 void set_timeout_sec(time_t timeout_sec);
738 689
739 void set_read_timeout(time_t sec, time_t usec); 690 void set_read_timeout(time_t sec, time_t usec);
740 691
741 void set_auth(const char *username, const char *password); 692 void set_keep_alive_max_count(size_t count);
693
694 void set_basic_auth(const char *username, const char *password);
695
696#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
697 void set_digest_auth(const char *username, const char *password);
698#endif
742 699
743 void set_follow_location(bool on); 700 void set_follow_location(bool on);
744 701
@@ -746,28 +703,96 @@ public:
746 703
747 void set_interface(const char *intf); 704 void set_interface(const char *intf);
748 705
706 void set_proxy(const char *host, int port);
707
708 void set_proxy_basic_auth(const char *username, const char *password);
709
710#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
711 void set_proxy_digest_auth(const char *username, const char *password);
712#endif
713
714 void set_logger(Logger logger);
715
749protected: 716protected:
750 bool process_request(Stream &strm, const Request &req, Response &res, 717 bool process_request(Stream &strm, const Request &req, Response &res,
751 bool last_connection, bool &connection_close); 718 bool last_connection, bool &connection_close);
752 719
753 const std::string host_; 720 const std::string host_;
754 const int port_; 721 const int port_;
755 time_t timeout_sec_;
756 const std::string host_and_port_; 722 const std::string host_and_port_;
757 size_t keep_alive_max_count_; 723
758 time_t read_timeout_sec_; 724 // Settings
759 time_t read_timeout_usec_; 725 std::string client_cert_path_;
760 bool follow_location_; 726 std::string client_key_path_;
761 std::string username_; 727
762 std::string password_; 728 time_t timeout_sec_ = 300;
763 bool compress_; 729 time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
730 time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
731
732 size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
733
734 std::string basic_auth_username_;
735 std::string basic_auth_password_;
736#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
737 std::string digest_auth_username_;
738 std::string digest_auth_password_;
739#endif
740
741 bool follow_location_ = false;
742
743 bool compress_ = false;
744
764 std::string interface_; 745 std::string interface_;
765 746
747 std::string proxy_host_;
748 int proxy_port_;
749
750 std::string proxy_basic_auth_username_;
751 std::string proxy_basic_auth_password_;
752#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
753 std::string proxy_digest_auth_username_;
754 std::string proxy_digest_auth_password_;
755#endif
756
757 Logger logger_;
758
759 void copy_settings(const Client &rhs) {
760 client_cert_path_ = rhs.client_cert_path_;
761 client_key_path_ = rhs.client_key_path_;
762 timeout_sec_ = rhs.timeout_sec_;
763 read_timeout_sec_ = rhs.read_timeout_sec_;
764 read_timeout_usec_ = rhs.read_timeout_usec_;
765 keep_alive_max_count_ = rhs.keep_alive_max_count_;
766 basic_auth_username_ = rhs.basic_auth_username_;
767 basic_auth_password_ = rhs.basic_auth_password_;
768#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
769 digest_auth_username_ = rhs.digest_auth_username_;
770 digest_auth_password_ = rhs.digest_auth_password_;
771#endif
772 follow_location_ = rhs.follow_location_;
773 compress_ = rhs.compress_;
774 interface_ = rhs.interface_;
775 proxy_host_ = rhs.proxy_host_;
776 proxy_port_ = rhs.proxy_port_;
777 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
778 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
779#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
780 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
781 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
782#endif
783 logger_ = rhs.logger_;
784 }
785
766private: 786private:
767 socket_t create_client_socket() const; 787 socket_t create_client_socket() const;
768 bool read_response_line(Stream &strm, Response &res); 788 bool read_response_line(Stream &strm, Response &res);
769 bool write_request(Stream &strm, const Request &req, bool last_connection); 789 bool write_request(Stream &strm, const Request &req, bool last_connection);
770 bool redirect(const Request &req, Response &res); 790 bool redirect(const Request &req, Response &res);
791 bool handle_request(Stream &strm, const Request &req, Response &res,
792 bool last_connection, bool &connection_close);
793#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
794 bool connect(socket_t sock, Response &res, bool &error);
795#endif
771 796
772 std::shared_ptr<Response> send_with_content_provider( 797 std::shared_ptr<Response> send_with_content_provider(
773 const char *method, const char *path, const Headers &headers, 798 const char *method, const char *path, const Headers &headers,
@@ -814,25 +839,6 @@ inline void Post(std::vector<Request> &requests, const char *path,
814} 839}
815 840
816#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 841#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
817class SSLSocketStream : public Stream {
818public:
819 SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
820 time_t read_timeout_usec);
821 virtual ~SSLSocketStream();
822
823 virtual int read(char *ptr, size_t size);
824 virtual int write(const char *ptr, size_t size);
825 virtual int write(const char *ptr);
826 virtual int write(const std::string &s);
827 virtual std::string get_remote_addr() const;
828
829private:
830 socket_t sock_;
831 SSL *ssl_;
832 time_t read_timeout_sec_;
833 time_t read_timeout_usec_;
834};
835
836class SSLServer : public Server { 842class SSLServer : public Server {
837public: 843public:
838 SSLServer(const char *cert_path, const char *private_key_path, 844 SSLServer(const char *cert_path, const char *private_key_path,
@@ -852,9 +858,9 @@ private:
852 858
853class SSLClient : public Client { 859class SSLClient : public Client {
854public: 860public:
855 SSLClient(const char *host, int port = 443, time_t timeout_sec = 300, 861 SSLClient(const std::string &host, int port = 443,
856 const char *client_cert_path = nullptr, 862 const std::string &client_cert_path = std::string(),
857 const char *client_key_path = nullptr); 863 const std::string &client_key_path = std::string());
858 864
859 virtual ~SSLClient(); 865 virtual ~SSLClient();
860 866
@@ -862,6 +868,7 @@ public:
862 868
863 void set_ca_cert_path(const char *ca_ceert_file_path, 869 void set_ca_cert_path(const char *ca_ceert_file_path,
864 const char *ca_cert_dir_path = nullptr); 870 const char *ca_cert_dir_path = nullptr);
871
865 void enable_server_certificate_verification(bool enabled); 872 void enable_server_certificate_verification(bool enabled);
866 873
867 long get_openssl_verify_result() const; 874 long get_openssl_verify_result() const;
@@ -884,6 +891,7 @@ private:
884 SSL_CTX *ctx_; 891 SSL_CTX *ctx_;
885 std::mutex ctx_mutex_; 892 std::mutex ctx_mutex_;
886 std::vector<std::string> host_components_; 893 std::vector<std::string> host_components_;
894
887 std::string ca_cert_file_path_; 895 std::string ca_cert_file_path_;
888 std::string ca_cert_dir_path_; 896 std::string ca_cert_dir_path_;
889 bool server_certificate_verification_ = false; 897 bool server_certificate_verification_ = false;
@@ -1186,6 +1194,28 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
1186#endif 1194#endif
1187} 1195}
1188 1196
1197inline int select_write(socket_t sock, time_t sec, time_t usec) {
1198#ifdef CPPHTTPLIB_USE_POLL
1199 struct pollfd pfd_read;
1200 pfd_read.fd = sock;
1201 pfd_read.events = POLLOUT;
1202
1203 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
1204
1205 return poll(&pfd_read, 1, timeout);
1206#else
1207 fd_set fds;
1208 FD_ZERO(&fds);
1209 FD_SET(sock, &fds);
1210
1211 timeval tv;
1212 tv.tv_sec = static_cast<long>(sec);
1213 tv.tv_usec = static_cast<long>(usec);
1214
1215 return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
1216#endif
1217}
1218
1189inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { 1219inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1190#ifdef CPPHTTPLIB_USE_POLL 1220#ifdef CPPHTTPLIB_USE_POLL
1191 struct pollfd pfd_read; 1221 struct pollfd pfd_read;
@@ -1227,21 +1257,77 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1227#endif 1257#endif
1228} 1258}
1229 1259
1260class SocketStream : public Stream {
1261public:
1262 SocketStream(socket_t sock, time_t read_timeout_sec,
1263 time_t read_timeout_usec);
1264 ~SocketStream() override;
1265
1266 bool is_readable() const override;
1267 bool is_writable() const override;
1268 int read(char *ptr, size_t size) override;
1269 int write(const char *ptr, size_t size) override;
1270 std::string get_remote_addr() const override;
1271
1272private:
1273 socket_t sock_;
1274 time_t read_timeout_sec_;
1275 time_t read_timeout_usec_;
1276};
1277
1278#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1279class SSLSocketStream : public Stream {
1280public:
1281 SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
1282 time_t read_timeout_usec);
1283 virtual ~SSLSocketStream();
1284
1285 bool is_readable() const override;
1286 bool is_writable() const override;
1287 int read(char *ptr, size_t size) override;
1288 int write(const char *ptr, size_t size) override;
1289 std::string get_remote_addr() const override;
1290
1291private:
1292 socket_t sock_;
1293 SSL *ssl_;
1294 time_t read_timeout_sec_;
1295 time_t read_timeout_usec_;
1296};
1297#endif
1298
1299class BufferStream : public Stream {
1300public:
1301 BufferStream() = default;
1302 ~BufferStream() override = default;
1303
1304 bool is_readable() const override;
1305 bool is_writable() const override;
1306 int read(char *ptr, size_t size) override;
1307 int write(const char *ptr, size_t size) override;
1308 std::string get_remote_addr() const override;
1309
1310 const std::string &get_buffer() const;
1311
1312private:
1313 std::string buffer;
1314 int position = 0;
1315};
1316
1230template <typename T> 1317template <typename T>
1231inline bool process_and_close_socket(bool is_client_request, socket_t sock, 1318inline bool process_socket(bool is_client_request, socket_t sock,
1232 size_t keep_alive_max_count, 1319 size_t keep_alive_max_count, time_t read_timeout_sec,
1233 time_t read_timeout_sec, 1320 time_t read_timeout_usec, T callback) {
1234 time_t read_timeout_usec, T callback) {
1235 assert(keep_alive_max_count > 0); 1321 assert(keep_alive_max_count > 0);
1236 1322
1237 bool ret = false; 1323 auto ret = false;
1238 1324
1239 if (keep_alive_max_count > 1) { 1325 if (keep_alive_max_count > 1) {
1240 auto count = keep_alive_max_count; 1326 auto count = keep_alive_max_count;
1241 while (count > 0 && 1327 while (count > 0 &&
1242 (is_client_request || 1328 (is_client_request ||
1243 detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, 1329 select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
1244 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) { 1330 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
1245 SocketStream strm(sock, read_timeout_sec, read_timeout_usec); 1331 SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
1246 auto last_connection = count == 1; 1332 auto last_connection = count == 1;
1247 auto connection_close = false; 1333 auto connection_close = false;
@@ -1251,12 +1337,22 @@ inline bool process_and_close_socket(bool is_client_request, socket_t sock,
1251 1337
1252 count--; 1338 count--;
1253 } 1339 }
1254 } else { 1340 } else { // keep_alive_max_count is 0 or 1
1255 SocketStream strm(sock, read_timeout_sec, read_timeout_usec); 1341 SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
1256 auto dummy_connection_close = false; 1342 auto dummy_connection_close = false;
1257 ret = callback(strm, true, dummy_connection_close); 1343 ret = callback(strm, true, dummy_connection_close);
1258 } 1344 }
1259 1345
1346 return ret;
1347}
1348
1349template <typename T>
1350inline bool process_and_close_socket(bool is_client_request, socket_t sock,
1351 size_t keep_alive_max_count,
1352 time_t read_timeout_sec,
1353 time_t read_timeout_usec, T callback) {
1354 auto ret = process_socket(is_client_request, sock, keep_alive_max_count,
1355 read_timeout_sec, read_timeout_usec, callback);
1260 close_socket(sock); 1356 close_socket(sock);
1261 return ret; 1357 return ret;
1262} 1358}
@@ -1302,6 +1398,23 @@ socket_t create_socket(const char *host, int port, Fn fn,
1302#ifdef _WIN32 1398#ifdef _WIN32
1303 auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, 1399 auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
1304 nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); 1400 nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT);
1401 /**
1402 * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
1403 * and above the socket creation fails on older Windows Systems.
1404 *
1405 * Let's try to create a socket the old way in this case.
1406 *
1407 * Reference:
1408 * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
1409 *
1410 * WSA_FLAG_NO_HANDLE_INHERIT:
1411 * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
1412 * SP1, and later
1413 *
1414 */
1415 if (sock == INVALID_SOCKET) {
1416 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
1417 }
1305#else 1418#else
1306 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 1419 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
1307#endif 1420#endif
@@ -1363,7 +1476,7 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
1363 1476
1364 if (getaddrinfo(host, "0", &hints, &result)) { return false; } 1477 if (getaddrinfo(host, "0", &hints, &result)) { return false; }
1365 1478
1366 bool ret = false; 1479 auto ret = false;
1367 for (auto rp = result; rp; rp = rp->ai_next) { 1480 for (auto rp = result; rp; rp = rp->ai_next) {
1368 const auto &ai = *rp; 1481 const auto &ai = *rp;
1369 if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) { 1482 if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
@@ -1440,8 +1553,14 @@ inline std::string get_remote_addr(socket_t sock) {
1440 return std::string(); 1553 return std::string();
1441} 1554}
1442 1555
1443inline const char *find_content_type(const std::string &path) { 1556inline const char *
1557find_content_type(const std::string &path,
1558 const std::map<std::string, std::string> &user_data) {
1444 auto ext = file_extension(path); 1559 auto ext = file_extension(path);
1560
1561 auto it = user_data.find(ext);
1562 if (it != user_data.end()) { return it->second.c_str(); }
1563
1445 if (ext == "txt") { 1564 if (ext == "txt") {
1446 return "text/plain"; 1565 return "text/plain";
1447 } else if (ext == "html" || ext == "htm") { 1566 } else if (ext == "html" || ext == "htm") {
@@ -1464,6 +1583,8 @@ inline const char *find_content_type(const std::string &path) {
1464 return "application/pdf"; 1583 return "application/pdf";
1465 } else if (ext == "js") { 1584 } else if (ext == "js") {
1466 return "application/javascript"; 1585 return "application/javascript";
1586 } else if (ext == "wasm") {
1587 return "application/wasm";
1467 } else if (ext == "xml") { 1588 } else if (ext == "xml") {
1468 return "application/xml"; 1589 return "application/xml";
1469 } else if (ext == "xhtml") { 1590 } else if (ext == "xhtml") {
@@ -1474,7 +1595,10 @@ inline const char *find_content_type(const std::string &path) {
1474 1595
1475inline const char *status_message(int status) { 1596inline const char *status_message(int status) {
1476 switch (status) { 1597 switch (status) {
1598 case 100: return "Continue";
1477 case 200: return "OK"; 1599 case 200: return "OK";
1600 case 202: return "Accepted";
1601 case 204: return "No Content";
1478 case 206: return "Partial Content"; 1602 case 206: return "Partial Content";
1479 case 301: return "Moved Permanently"; 1603 case 301: return "Moved Permanently";
1480 case 302: return "Found"; 1604 case 302: return "Found";
@@ -1488,6 +1612,8 @@ inline const char *status_message(int status) {
1488 case 414: return "Request-URI Too Long"; 1612 case 414: return "Request-URI Too Long";
1489 case 415: return "Unsupported Media Type"; 1613 case 415: return "Unsupported Media Type";
1490 case 416: return "Range Not Satisfiable"; 1614 case 416: return "Range Not Satisfiable";
1615 case 417: return "Expectation Failed";
1616 case 503: return "Service Unavailable";
1491 1617
1492 default: 1618 default:
1493 case 500: return "Internal Server Error"; 1619 case 500: return "Internal Server Error";
@@ -1743,7 +1869,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
1743 }; 1869 };
1744 1870
1745#ifdef CPPHTTPLIB_ZLIB_SUPPORT 1871#ifdef CPPHTTPLIB_ZLIB_SUPPORT
1746 detail::decompressor decompressor; 1872 decompressor decompressor;
1747 1873
1748 if (!decompressor.is_valid()) { 1874 if (!decompressor.is_valid()) {
1749 status = 500; 1875 status = 500;
@@ -1807,47 +1933,53 @@ inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
1807 return write_len; 1933 return write_len;
1808} 1934}
1809 1935
1810inline ssize_t write_content(Stream &strm, 1936inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
1811 ContentProviderWithCloser content_provider,
1812 size_t offset, size_t length) { 1937 size_t offset, size_t length) {
1813 size_t begin_offset = offset; 1938 size_t begin_offset = offset;
1814 size_t end_offset = offset + length; 1939 size_t end_offset = offset + length;
1815 while (offset < end_offset) { 1940 while (offset < end_offset) {
1816 ssize_t written_length = 0; 1941 ssize_t written_length = 0;
1817 content_provider( 1942
1818 offset, end_offset - offset, 1943 DataSink data_sink;
1819 [&](const char *d, size_t l) { 1944 data_sink.write = [&](const char *d, size_t l) {
1820 offset += l; 1945 offset += l;
1821 written_length = strm.write(d, l); 1946 written_length = strm.write(d, l);
1822 }, 1947 };
1823 [&](void) { written_length = -1; }); 1948 data_sink.done = [&](void) { written_length = -1; };
1949 data_sink.is_writable = [&](void) { return strm.is_writable(); };
1950
1951 content_provider(offset, end_offset - offset, data_sink);
1824 if (written_length < 0) { return written_length; } 1952 if (written_length < 0) { return written_length; }
1825 } 1953 }
1826 return static_cast<ssize_t>(offset - begin_offset); 1954 return static_cast<ssize_t>(offset - begin_offset);
1827} 1955}
1828 1956
1829inline ssize_t 1957template <typename T>
1830write_content_chunked(Stream &strm, 1958inline ssize_t write_content_chunked(Stream &strm,
1831 ContentProviderWithCloser content_provider) { 1959 ContentProvider content_provider,
1960 T is_shutting_down) {
1832 size_t offset = 0; 1961 size_t offset = 0;
1833 auto data_available = true; 1962 auto data_available = true;
1834 ssize_t total_written_length = 0; 1963 ssize_t total_written_length = 0;
1835 while (data_available) { 1964 while (data_available && !is_shutting_down()) {
1836 ssize_t written_length = 0; 1965 ssize_t written_length = 0;
1837 content_provider( 1966
1838 offset, 0, 1967 DataSink data_sink;
1839 [&](const char *d, size_t l) { 1968 data_sink.write = [&](const char *d, size_t l) {
1840 data_available = l > 0; 1969 data_available = l > 0;
1841 offset += l; 1970 offset += l;
1842 1971
1843 // Emit chunked response header and footer for each chunk 1972 // Emit chunked response header and footer for each chunk
1844 auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n"; 1973 auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n";
1845 written_length = strm.write(chunk); 1974 written_length = strm.write(chunk);
1846 }, 1975 };
1847 [&](void) { 1976 data_sink.done = [&](void) {
1848 data_available = false; 1977 data_available = false;
1849 written_length = strm.write("0\r\n\r\n"); 1978 written_length = strm.write("0\r\n\r\n");
1850 }); 1979 };
1980 data_sink.is_writable = [&](void) { return strm.is_writable(); };
1981
1982 content_provider(offset, 0, data_sink);
1851 1983
1852 if (written_length < 0) { return written_length; } 1984 if (written_length < 0) { return written_length; }
1853 total_written_length += written_length; 1985 total_written_length += written_length;
@@ -1858,17 +1990,12 @@ write_content_chunked(Stream &strm,
1858template <typename T> 1990template <typename T>
1859inline bool redirect(T &cli, const Request &req, Response &res, 1991inline bool redirect(T &cli, const Request &req, Response &res,
1860 const std::string &path) { 1992 const std::string &path) {
1861 Request new_req; 1993 Request new_req = req;
1862 new_req.method = req.method;
1863 new_req.path = path; 1994 new_req.path = path;
1864 new_req.headers = req.headers; 1995 new_req.redirect_count -= 1;
1865 new_req.body = req.body;
1866 new_req.redirect_count = req.redirect_count - 1;
1867 new_req.response_handler = req.response_handler;
1868 new_req.content_receiver = req.content_receiver;
1869 new_req.progress = req.progress;
1870 1996
1871 Response new_res; 1997 Response new_res;
1998
1872 auto ret = cli.send(new_req, new_res); 1999 auto ret = cli.send(new_req, new_res);
1873 if (ret) { res = new_res; } 2000 if (ret) { res = new_res; }
1874 return ret; 2001 return ret;
@@ -1885,7 +2012,7 @@ inline std::string encode_url(const std::string &s) {
1885 case '\n': result += "%0A"; break; 2012 case '\n': result += "%0A"; break;
1886 case '\'': result += "%27"; break; 2013 case '\'': result += "%27"; break;
1887 case ',': result += "%2C"; break; 2014 case ',': result += "%2C"; break;
1888 case ':': result += "%3A"; break; 2015 // case ':': result += "%3A"; break; // ok? probably...
1889 case ';': result += "%3B"; break; 2016 case ';': result += "%3B"; break;
1890 default: 2017 default:
1891 auto c = static_cast<uint8_t>(s[i]); 2018 auto c = static_cast<uint8_t>(s[i]);
@@ -1945,11 +2072,11 @@ inline void parse_query_text(const std::string &s, Params &params) {
1945 split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { 2072 split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) {
1946 std::string key; 2073 std::string key;
1947 std::string val; 2074 std::string val;
1948 split(b, e, '=', [&](const char *b, const char *e) { 2075 split(b, e, '=', [&](const char *b2, const char *e2) {
1949 if (key.empty()) { 2076 if (key.empty()) {
1950 key.assign(b, e); 2077 key.assign(b2, e2);
1951 } else { 2078 } else {
1952 val.assign(b, e); 2079 val.assign(b2, e2);
1953 } 2080 }
1954 }); 2081 });
1955 params.emplace(key, decode_url(val)); 2082 params.emplace(key, decode_url(val));
@@ -1972,29 +2099,28 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
1972 auto pos = m.position(1); 2099 auto pos = m.position(1);
1973 auto len = m.length(1); 2100 auto len = m.length(1);
1974 bool all_valid_ranges = true; 2101 bool all_valid_ranges = true;
1975 detail::split( 2102 split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
1976 &s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { 2103 if (!all_valid_ranges) return;
1977 if (!all_valid_ranges) return; 2104 static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
1978 static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); 2105 std::cmatch cm;
1979 std::cmatch m; 2106 if (std::regex_match(b, e, cm, re_another_range)) {
1980 if (std::regex_match(b, e, m, re_another_range)) { 2107 ssize_t first = -1;
1981 ssize_t first = -1; 2108 if (!cm.str(1).empty()) {
1982 if (!m.str(1).empty()) { 2109 first = static_cast<ssize_t>(std::stoll(cm.str(1)));
1983 first = static_cast<ssize_t>(std::stoll(m.str(1))); 2110 }
1984 }
1985 2111
1986 ssize_t last = -1; 2112 ssize_t last = -1;
1987 if (!m.str(2).empty()) { 2113 if (!cm.str(2).empty()) {
1988 last = static_cast<ssize_t>(std::stoll(m.str(2))); 2114 last = static_cast<ssize_t>(std::stoll(cm.str(2)));
1989 } 2115 }
1990 2116
1991 if (first != -1 && last != -1 && first > last) { 2117 if (first != -1 && last != -1 && first > last) {
1992 all_valid_ranges = false; 2118 all_valid_ranges = false;
1993 return; 2119 return;
1994 } 2120 }
1995 ranges.emplace_back(std::make_pair(first, last)); 2121 ranges.emplace_back(std::make_pair(first, last));
1996 } 2122 }
1997 }); 2123 });
1998 return all_valid_ranges; 2124 return all_valid_ranges;
1999 } 2125 }
2000 return false; 2126 return false;
@@ -2076,6 +2202,8 @@ public:
2076 case 3: { // Body 2202 case 3: { // Body
2077 { 2203 {
2078 auto pattern = crlf_ + dash_; 2204 auto pattern = crlf_ + dash_;
2205 if (pattern.size() > buf_.size()) { return true; }
2206
2079 auto pos = buf_.find(pattern); 2207 auto pos = buf_.find(pattern);
2080 if (pos == std::string::npos) { pos = buf_.size(); } 2208 if (pos == std::string::npos) { pos = buf_.size(); }
2081 if (!content_callback(buf_.data(), pos)) { 2209 if (!content_callback(buf_.data(), pos)) {
@@ -2238,7 +2366,7 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
2238 ctoken("\r\n"); 2366 ctoken("\r\n");
2239 } 2367 }
2240 2368
2241 auto offsets = detail::get_range_offset_and_length(req, res.body.size(), i); 2369 auto offsets = get_range_offset_and_length(req, res.body.size(), i);
2242 auto offset = offsets.first; 2370 auto offset = offsets.first;
2243 auto length = offsets.second; 2371 auto length = offsets.second;
2244 2372
@@ -2301,8 +2429,7 @@ inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
2301 [&](const std::string &token) { strm.write(token); }, 2429 [&](const std::string &token) { strm.write(token); },
2302 [&](const char *token) { strm.write(token); }, 2430 [&](const char *token) { strm.write(token); },
2303 [&](size_t offset, size_t length) { 2431 [&](size_t offset, size_t length) {
2304 return detail::write_content(strm, res.content_provider, offset, 2432 return write_content(strm, res.content_provider, offset, length) >= 0;
2305 length) >= 0;
2306 }); 2433 });
2307} 2434}
2308 2435
@@ -2346,7 +2473,6 @@ inline std::string message_digest(const std::string &s, Init init,
2346} 2473}
2347 2474
2348inline std::string MD5(const std::string &s) { 2475inline std::string MD5(const std::string &s) {
2349 using namespace detail;
2350 return message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final, 2476 return message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final,
2351 MD5_DIGEST_LENGTH); 2477 MD5_DIGEST_LENGTH);
2352} 2478}
@@ -2394,16 +2520,18 @@ inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
2394 2520
2395inline std::pair<std::string, std::string> 2521inline std::pair<std::string, std::string>
2396make_basic_authentication_header(const std::string &username, 2522make_basic_authentication_header(const std::string &username,
2397 const std::string &password) { 2523 const std::string &password,
2524 bool is_proxy = false) {
2398 auto field = "Basic " + detail::base64_encode(username + ":" + password); 2525 auto field = "Basic " + detail::base64_encode(username + ":" + password);
2399 return std::make_pair("Authorization", field); 2526 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
2527 return std::make_pair(key, field);
2400} 2528}
2401 2529
2402#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 2530#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2403inline std::pair<std::string, std::string> make_digest_authentication_header( 2531inline std::pair<std::string, std::string> make_digest_authentication_header(
2404 const Request &req, const std::map<std::string, std::string> &auth, 2532 const Request &req, const std::map<std::string, std::string> &auth,
2405 size_t cnonce_count, const std::string &cnonce, const std::string &username, 2533 size_t cnonce_count, const std::string &cnonce, const std::string &username,
2406 const std::string &password) { 2534 const std::string &password, bool is_proxy = false) {
2407 using namespace std; 2535 using namespace std;
2408 2536
2409 string nc; 2537 string nc;
@@ -2420,10 +2548,11 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
2420 qop = "auth"; 2548 qop = "auth";
2421 } 2549 }
2422 2550
2551 std::string algo = "MD5";
2552 if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
2553
2423 string response; 2554 string response;
2424 { 2555 {
2425 auto algo = auth.at("algorithm");
2426
2427 auto H = algo == "SHA-256" 2556 auto H = algo == "SHA-256"
2428 ? detail::SHA_256 2557 ? detail::SHA_256
2429 : algo == "SHA-512" ? detail::SHA_512 : detail::MD5; 2558 : algo == "SHA-512" ? detail::SHA_512 : detail::MD5;
@@ -2439,25 +2568,26 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
2439 2568
2440 auto field = "Digest username=\"hello\", realm=\"" + auth.at("realm") + 2569 auto field = "Digest username=\"hello\", realm=\"" + auth.at("realm") +
2441 "\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path + 2570 "\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path +
2442 "\", algorithm=" + auth.at("algorithm") + ", qop=" + qop + 2571 "\", algorithm=" + algo + ", qop=" + qop + ", nc=\"" + nc +
2443 ", nc=\"" + nc + "\", cnonce=\"" + cnonce + "\", response=\"" + 2572 "\", cnonce=\"" + cnonce + "\", response=\"" + response + "\"";
2444 response + "\"";
2445 2573
2446 return make_pair("Authorization", field); 2574 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
2575 return std::make_pair(key, field);
2447} 2576}
2448#endif 2577#endif
2449 2578
2450inline int 2579inline bool parse_www_authenticate(const httplib::Response &res,
2451parse_www_authenticate(const httplib::Response &res, 2580 std::map<std::string, std::string> &auth,
2452 std::map<std::string, std::string> &digest_auth) { 2581 bool is_proxy) {
2453 if (res.has_header("WWW-Authenticate")) { 2582 auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
2583 if (res.has_header(auth_key)) {
2454 static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); 2584 static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
2455 auto s = res.get_header_value("WWW-Authenticate"); 2585 auto s = res.get_header_value(auth_key);
2456 auto pos = s.find(' '); 2586 auto pos = s.find(' ');
2457 if (pos != std::string::npos) { 2587 if (pos != std::string::npos) {
2458 auto type = s.substr(0, pos); 2588 auto type = s.substr(0, pos);
2459 if (type == "Basic") { 2589 if (type == "Basic") {
2460 return 1; 2590 return false;
2461 } else if (type == "Digest") { 2591 } else if (type == "Digest") {
2462 s = s.substr(pos + 1); 2592 s = s.substr(pos + 1);
2463 auto beg = std::sregex_iterator(s.begin(), s.end(), re); 2593 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
@@ -2466,13 +2596,13 @@ parse_www_authenticate(const httplib::Response &res,
2466 auto key = s.substr(m.position(1), m.length(1)); 2596 auto key = s.substr(m.position(1), m.length(1));
2467 auto val = m.length(2) > 0 ? s.substr(m.position(2), m.length(2)) 2597 auto val = m.length(2) > 0 ? s.substr(m.position(2), m.length(2))
2468 : s.substr(m.position(3), m.length(3)); 2598 : s.substr(m.position(3), m.length(3));
2469 digest_auth[key] = val; 2599 auth[key] = val;
2470 } 2600 }
2471 return 2; 2601 return true;
2472 } 2602 }
2473 } 2603 }
2474 } 2604 }
2475 return 0; 2605 return false;
2476} 2606}
2477 2607
2478// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 2608// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240
@@ -2583,26 +2713,34 @@ inline void Response::set_content(const std::string &s,
2583} 2713}
2584 2714
2585inline void Response::set_content_provider( 2715inline void Response::set_content_provider(
2586 size_t length, 2716 size_t in_length,
2587 std::function<void(size_t offset, size_t length, DataSink sink)> provider, 2717 std::function<void(size_t offset, size_t length, DataSink &sink)> provider,
2588 std::function<void()> resource_releaser) { 2718 std::function<void()> resource_releaser) {
2589 assert(length > 0); 2719 assert(in_length > 0);
2590 content_length = length; 2720 content_length = in_length;
2591 content_provider = [provider](size_t offset, size_t length, DataSink sink, 2721 content_provider = [provider](size_t offset, size_t length, DataSink &sink) {
2592 Done) { provider(offset, length, sink); }; 2722 provider(offset, length, sink);
2723 };
2593 content_provider_resource_releaser = resource_releaser; 2724 content_provider_resource_releaser = resource_releaser;
2594} 2725}
2595 2726
2596inline void Response::set_chunked_content_provider( 2727inline void Response::set_chunked_content_provider(
2597 std::function<void(size_t offset, DataSink sink, Done done)> provider, 2728 std::function<void(size_t offset, DataSink &sink)> provider,
2598 std::function<void()> resource_releaser) { 2729 std::function<void()> resource_releaser) {
2599 content_length = 0; 2730 content_length = 0;
2600 content_provider = [provider](size_t offset, size_t, DataSink sink, 2731 content_provider = [provider](size_t offset, size_t, DataSink &sink) {
2601 Done done) { provider(offset, sink, done); }; 2732 provider(offset, sink);
2733 };
2602 content_provider_resource_releaser = resource_releaser; 2734 content_provider_resource_releaser = resource_releaser;
2603} 2735}
2604 2736
2605// Rstream implementation 2737// Rstream implementation
2738inline int Stream::write(const char *ptr) { return write(ptr, strlen(ptr)); }
2739
2740inline int Stream::write(const std::string &s) {
2741 return write(s.data(), s.size());
2742}
2743
2606template <typename... Args> 2744template <typename... Args>
2607inline int Stream::write_format(const char *fmt, const Args &... args) { 2745inline int Stream::write_format(const char *fmt, const Args &... args) {
2608 std::array<char, 2048> buf; 2746 std::array<char, 2048> buf;
@@ -2632,6 +2770,8 @@ inline int Stream::write_format(const char *fmt, const Args &... args) {
2632 } 2770 }
2633} 2771}
2634 2772
2773namespace detail {
2774
2635// Socket stream implementation 2775// Socket stream implementation
2636inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, 2776inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
2637 time_t read_timeout_usec) 2777 time_t read_timeout_usec)
@@ -2640,23 +2780,22 @@ inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
2640 2780
2641inline SocketStream::~SocketStream() {} 2781inline SocketStream::~SocketStream() {}
2642 2782
2643inline int SocketStream::read(char *ptr, size_t size) { 2783inline bool SocketStream::is_readable() const {
2644 if (detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) { 2784 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
2645 return recv(sock_, ptr, static_cast<int>(size), 0);
2646 }
2647 return -1;
2648} 2785}
2649 2786
2650inline int SocketStream::write(const char *ptr, size_t size) { 2787inline bool SocketStream::is_writable() const {
2651 return send(sock_, ptr, static_cast<int>(size), 0); 2788 return detail::select_write(sock_, 0, 0) > 0;
2652} 2789}
2653 2790
2654inline int SocketStream::write(const char *ptr) { 2791inline int SocketStream::read(char *ptr, size_t size) {
2655 return write(ptr, strlen(ptr)); 2792 if (is_readable()) { return recv(sock_, ptr, static_cast<int>(size), 0); }
2793 return -1;
2656} 2794}
2657 2795
2658inline int SocketStream::write(const std::string &s) { 2796inline int SocketStream::write(const char *ptr, size_t size) {
2659 return write(s.data(), s.size()); 2797 if (is_writable()) { return send(sock_, ptr, static_cast<int>(size), 0); }
2798 return -1;
2660} 2799}
2661 2800
2662inline std::string SocketStream::get_remote_addr() const { 2801inline std::string SocketStream::get_remote_addr() const {
@@ -2664,12 +2803,18 @@ inline std::string SocketStream::get_remote_addr() const {
2664} 2803}
2665 2804
2666// Buffer stream implementation 2805// Buffer stream implementation
2806inline bool BufferStream::is_readable() const { return true; }
2807
2808inline bool BufferStream::is_writable() const { return true; }
2809
2667inline int BufferStream::read(char *ptr, size_t size) { 2810inline int BufferStream::read(char *ptr, size_t size) {
2668#if defined(_MSC_VER) && _MSC_VER < 1900 2811#if defined(_MSC_VER) && _MSC_VER < 1900
2669 return static_cast<int>(buffer._Copy_s(ptr, size, size)); 2812 int len_read = static_cast<int>(buffer._Copy_s(ptr, size, size, position));
2670#else 2813#else
2671 return static_cast<int>(buffer.copy(ptr, size)); 2814 int len_read = static_cast<int>(buffer.copy(ptr, size, position));
2672#endif 2815#endif
2816 position += len_read;
2817 return len_read;
2673} 2818}
2674 2819
2675inline int BufferStream::write(const char *ptr, size_t size) { 2820inline int BufferStream::write(const char *ptr, size_t size) {
@@ -2677,18 +2822,12 @@ inline int BufferStream::write(const char *ptr, size_t size) {
2677 return static_cast<int>(size); 2822 return static_cast<int>(size);
2678} 2823}
2679 2824
2680inline int BufferStream::write(const char *ptr) {
2681 return write(ptr, strlen(ptr));
2682}
2683
2684inline int BufferStream::write(const std::string &s) {
2685 return write(s.data(), s.size());
2686}
2687
2688inline std::string BufferStream::get_remote_addr() const { return ""; } 2825inline std::string BufferStream::get_remote_addr() const { return ""; }
2689 2826
2690inline const std::string &BufferStream::get_buffer() const { return buffer; } 2827inline const std::string &BufferStream::get_buffer() const { return buffer; }
2691 2828
2829} // namespace detail
2830
2692// HTTP server implementation 2831// HTTP server implementation
2693inline Server::Server() 2832inline Server::Server()
2694 : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), 2833 : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT),
@@ -2699,15 +2838,7 @@ inline Server::Server()
2699#ifndef _WIN32 2838#ifndef _WIN32
2700 signal(SIGPIPE, SIG_IGN); 2839 signal(SIGPIPE, SIG_IGN);
2701#endif 2840#endif
2702 new_task_queue = [] { 2841 new_task_queue = [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); };
2703#if CPPHTTPLIB_THREAD_POOL_COUNT > 0
2704 return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT);
2705#elif CPPHTTPLIB_THREAD_POOL_COUNT == 0
2706 return new Threads();
2707#else
2708 return new NoThread();
2709#endif
2710 };
2711} 2842}
2712 2843
2713inline Server::~Server() {} 2844inline Server::~Server() {}
@@ -2764,6 +2895,10 @@ inline Server &Server::Options(const char *pattern, Handler handler) {
2764} 2895}
2765 2896
2766inline bool Server::set_base_dir(const char *dir, const char *mount_point) { 2897inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
2898 return set_mount_point(mount_point, dir);
2899}
2900
2901inline bool Server::set_mount_point(const char *mount_point, const char *dir) {
2767 if (detail::is_dir(dir)) { 2902 if (detail::is_dir(dir)) {
2768 std::string mnt = mount_point ? mount_point : "/"; 2903 std::string mnt = mount_point ? mount_point : "/";
2769 if (!mnt.empty() && mnt[0] == '/') { 2904 if (!mnt.empty() && mnt[0] == '/') {
@@ -2774,6 +2909,21 @@ inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
2774 return false; 2909 return false;
2775} 2910}
2776 2911
2912inline bool Server::remove_mount_point(const char *mount_point) {
2913 for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
2914 if (it->first == mount_point) {
2915 base_dirs_.erase(it);
2916 return true;
2917 }
2918 }
2919 return false;
2920}
2921
2922inline void Server::set_file_extension_and_mimetype_mapping(const char *ext,
2923 const char *mime) {
2924 file_extension_and_mimetype_map_[ext] = mime;
2925}
2926
2777inline void Server::set_file_request_handler(Handler handler) { 2927inline void Server::set_file_request_handler(Handler handler) {
2778 file_request_handler_ = std::move(handler); 2928 file_request_handler_ = std::move(handler);
2779} 2929}
@@ -2784,6 +2934,11 @@ inline void Server::set_error_handler(Handler handler) {
2784 2934
2785inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } 2935inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
2786 2936
2937inline void
2938Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
2939 expect_100_continue_handler_ = std::move(handler);
2940}
2941
2787inline void Server::set_keep_alive_max_count(size_t count) { 2942inline void Server::set_keep_alive_max_count(size_t count) {
2788 keep_alive_max_count_ = count; 2943 keep_alive_max_count_ = count;
2789} 2944}
@@ -2850,9 +3005,11 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
2850 3005
2851 if (400 <= res.status && error_handler_) { error_handler_(req, res); } 3006 if (400 <= res.status && error_handler_) { error_handler_(req, res); }
2852 3007
3008 detail::BufferStream bstrm;
3009
2853 // Response line 3010 // Response line
2854 if (!strm.write_format("HTTP/1.1 %d %s\r\n", res.status, 3011 if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
2855 detail::status_message(res.status))) { 3012 detail::status_message(res.status))) {
2856 return false; 3013 return false;
2857 } 3014 }
2858 3015
@@ -2865,11 +3022,12 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
2865 res.set_header("Connection", "Keep-Alive"); 3022 res.set_header("Connection", "Keep-Alive");
2866 } 3023 }
2867 3024
2868 if (!res.has_header("Content-Type")) { 3025 if (!res.has_header("Content-Type") &&
3026 (!res.body.empty() || res.content_length > 0)) {
2869 res.set_header("Content-Type", "text/plain"); 3027 res.set_header("Content-Type", "text/plain");
2870 } 3028 }
2871 3029
2872 if (!res.has_header("Accept-Ranges")) { 3030 if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {
2873 res.set_header("Accept-Ranges", "bytes"); 3031 res.set_header("Accept-Ranges", "bytes");
2874 } 3032 }
2875 3033
@@ -2932,7 +3090,7 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
2932 } 3090 }
2933 3091
2934#ifdef CPPHTTPLIB_ZLIB_SUPPORT 3092#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2935 // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0 3093 // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
2936 const auto &encodings = req.get_header_value("Accept-Encoding"); 3094 const auto &encodings = req.get_header_value("Accept-Encoding");
2937 if (encodings.find("gzip") != std::string::npos && 3095 if (encodings.find("gzip") != std::string::npos &&
2938 detail::can_compress(res.get_header_value("Content-Type"))) { 3096 detail::can_compress(res.get_header_value("Content-Type"))) {
@@ -2946,7 +3104,11 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
2946 res.set_header("Content-Length", length); 3104 res.set_header("Content-Length", length);
2947 } 3105 }
2948 3106
2949 if (!detail::write_headers(strm, res, Headers())) { return false; } 3107 if (!detail::write_headers(bstrm, res, Headers())) { return false; }
3108
3109 // Flush buffer
3110 auto &data = bstrm.get_buffer();
3111 strm.write(data.data(), data.size());
2950 3112
2951 // Body 3113 // Body
2952 if (req.method != "HEAD") { 3114 if (req.method != "HEAD") {
@@ -2992,7 +3154,11 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
2992 } 3154 }
2993 } 3155 }
2994 } else { 3156 } else {
2995 if (detail::write_content_chunked(strm, res.content_provider) < 0) { 3157 auto is_shutting_down = [this]() {
3158 return this->svr_sock_ == INVALID_SOCKET;
3159 };
3160 if (detail::write_content_chunked(strm, res.content_provider,
3161 is_shutting_down) < 0) {
2996 return false; 3162 return false;
2997 } 3163 }
2998 } 3164 }
@@ -3078,7 +3244,8 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
3078 return true; 3244 return true;
3079} 3245}
3080 3246
3081inline bool Server::handle_file_request(Request &req, Response &res) { 3247inline bool Server::handle_file_request(Request &req, Response &res,
3248 bool head) {
3082 for (const auto &kv : base_dirs_) { 3249 for (const auto &kv : base_dirs_) {
3083 const auto &mount_point = kv.first; 3250 const auto &mount_point = kv.first;
3084 const auto &base_dir = kv.second; 3251 const auto &base_dir = kv.second;
@@ -3092,10 +3259,13 @@ inline bool Server::handle_file_request(Request &req, Response &res) {
3092 3259
3093 if (detail::is_file(path)) { 3260 if (detail::is_file(path)) {
3094 detail::read_file(path, res.body); 3261 detail::read_file(path, res.body);
3095 auto type = detail::find_content_type(path); 3262 auto type =
3263 detail::find_content_type(path, file_extension_and_mimetype_map_);
3096 if (type) { res.set_header("Content-Type", type); } 3264 if (type) { res.set_header("Content-Type", type); }
3097 res.status = 200; 3265 res.status = 200;
3098 if (file_request_handler_) { file_request_handler_(req, res); } 3266 if (!head && file_request_handler_) {
3267 file_request_handler_(req, res);
3268 }
3099 return true; 3269 return true;
3100 } 3270 }
3101 } 3271 }
@@ -3196,7 +3366,11 @@ inline bool Server::listen_internal() {
3196inline bool Server::routing(Request &req, Response &res, Stream &strm, 3366inline bool Server::routing(Request &req, Response &res, Stream &strm,
3197 bool last_connection) { 3367 bool last_connection) {
3198 // File handler 3368 // File handler
3199 if (req.method == "GET" && handle_file_request(req, res)) { return true; } 3369 bool is_head_request = req.method == "HEAD";
3370 if ((req.method == "GET" || is_head_request) &&
3371 handle_file_request(req, res, is_head_request)) {
3372 return true;
3373 }
3200 3374
3201 if (detail::expect_content(req)) { 3375 if (detail::expect_content(req)) {
3202 // Content reader handler 3376 // Content reader handler
@@ -3266,10 +3440,9 @@ inline bool Server::dispatch_request(Request &req, Response &res,
3266 return false; 3440 return false;
3267} 3441}
3268 3442
3269inline bool 3443inline bool Server::dispatch_request_for_content_reader(
3270Server::dispatch_request_for_content_reader(Request &req, Response &res, 3444 Request &req, Response &res, ContentReader content_reader,
3271 ContentReader content_reader, 3445 HandlersForContentReader &handlers) {
3272 HandersForContentReader &handlers) {
3273 for (const auto &x : handlers) { 3446 for (const auto &x : handlers) {
3274 const auto &pattern = x.first; 3447 const auto &pattern = x.first;
3275 const auto &handler = x.second; 3448 const auto &handler = x.second;
@@ -3333,6 +3506,21 @@ Server::process_request(Stream &strm, bool last_connection,
3333 3506
3334 if (setup_request) { setup_request(req); } 3507 if (setup_request) { setup_request(req); }
3335 3508
3509 if (req.get_header_value("Expect") == "100-continue") {
3510 auto status = 100;
3511 if (expect_100_continue_handler_) {
3512 status = expect_100_continue_handler_(req, res);
3513 }
3514 switch (status) {
3515 case 100:
3516 case 417:
3517 strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status,
3518 detail::status_message(status));
3519 break;
3520 default: return write_response(strm, last_connection, req, res);
3521 }
3522 }
3523
3336 // Rounting 3524 // Rounting
3337 if (routing(req, res, strm, last_connection)) { 3525 if (routing(req, res, strm, last_connection)) {
3338 if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } 3526 if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
@@ -3355,19 +3543,22 @@ inline bool Server::process_and_close_socket(socket_t sock) {
3355} 3543}
3356 3544
3357// HTTP client implementation 3545// HTTP client implementation
3358inline Client::Client(const char *host, int port, time_t timeout_sec) 3546inline Client::Client(const std::string &host, int port,
3359 : host_(host), port_(port), timeout_sec_(timeout_sec), 3547 const std::string &client_cert_path,
3548 const std::string &client_key_path)
3549 : host_(host), port_(port),
3360 host_and_port_(host_ + ":" + std::to_string(port_)), 3550 host_and_port_(host_ + ":" + std::to_string(port_)),
3361 keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), 3551 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
3362 read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
3363 read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND),
3364 follow_location_(false), compress_(false) {}
3365 3552
3366inline Client::~Client() {} 3553inline Client::~Client() {}
3367 3554
3368inline bool Client::is_valid() const { return true; } 3555inline bool Client::is_valid() const { return true; }
3369 3556
3370inline socket_t Client::create_client_socket() const { 3557inline socket_t Client::create_client_socket() const {
3558 if (!proxy_host_.empty()) {
3559 return detail::create_client_socket(proxy_host_.c_str(), proxy_port_,
3560 timeout_sec_, interface_);
3561 }
3371 return detail::create_client_socket(host_.c_str(), port_, timeout_sec_, 3562 return detail::create_client_socket(host_.c_str(), port_, timeout_sec_,
3372 interface_); 3563 interface_);
3373} 3564}
@@ -3391,95 +3582,160 @@ inline bool Client::read_response_line(Stream &strm, Response &res) {
3391} 3582}
3392 3583
3393inline bool Client::send(const Request &req, Response &res) { 3584inline bool Client::send(const Request &req, Response &res) {
3394 if (req.path.empty()) { return false; }
3395
3396 auto sock = create_client_socket(); 3585 auto sock = create_client_socket();
3397 if (sock == INVALID_SOCKET) { return false; } 3586 if (sock == INVALID_SOCKET) { return false; }
3398 3587
3399 auto ret = process_and_close_socket( 3588#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3400 sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) { 3589 if (is_ssl() && !proxy_host_.empty()) {
3401 return process_request(strm, req, res, last_connection, 3590 bool error;
3402 connection_close); 3591 if (!connect(sock, res, error)) { return error; }
3403 });
3404
3405 if (ret && follow_location_ && (300 < res.status && res.status < 400)) {
3406 ret = redirect(req, res);
3407 } 3592 }
3593#endif
3408 3594
3409 if (ret && !username_.empty() && !password_.empty() && res.status == 401) { 3595 return process_and_close_socket(
3410 int type; 3596 sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) {
3411 std::map<std::string, std::string> digest_auth; 3597 return handle_request(strm, req, res, last_connection,
3598 connection_close);
3599 });
3600}
3412 3601
3413 if ((type = parse_www_authenticate(res, digest_auth)) > 0) { 3602inline bool Client::send(const std::vector<Request> &requests,
3414 std::pair<std::string, std::string> header; 3603 std::vector<Response> &responses) {
3604 size_t i = 0;
3605 while (i < requests.size()) {
3606 auto sock = create_client_socket();
3607 if (sock == INVALID_SOCKET) { return false; }
3415 3608
3416 if (type == 1) {
3417 header = make_basic_authentication_header(username_, password_);
3418 } else if (type == 2) {
3419#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 3609#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3420 size_t cnonce_count = 1; 3610 if (is_ssl() && !proxy_host_.empty()) {
3421 auto cnonce = random_string(10); 3611 Response res;
3422 3612 bool error;
3423 header = make_digest_authentication_header( 3613 if (!connect(sock, res, error)) { return false; }
3424 req, digest_auth, cnonce_count, cnonce, username_, password_); 3614 }
3425#endif 3615#endif
3426 }
3427 3616
3428 Request new_req; 3617 if (!process_and_close_socket(sock, requests.size() - i,
3429 new_req.method = req.method; 3618 [&](Stream &strm, bool last_connection,
3430 new_req.path = req.path; 3619 bool &connection_close) -> bool {
3431 new_req.headers = req.headers; 3620 auto &req = requests[i++];
3432 new_req.body = req.body; 3621 auto res = Response();
3433 new_req.response_handler = req.response_handler; 3622 auto ret = handle_request(strm, req, res,
3434 new_req.content_receiver = req.content_receiver; 3623 last_connection,
3435 new_req.progress = req.progress; 3624 connection_close);
3436 3625 if (ret) {
3437 new_req.headers.insert(header); 3626 responses.emplace_back(std::move(res));
3438 3627 }
3439 Response new_res; 3628 return ret;
3440 auto ret = send(new_req, new_res); 3629 })) {
3441 if (ret) { res = new_res; } 3630 return false;
3442 return ret;
3443 } 3631 }
3444 } 3632 }
3445 3633
3446 return ret; 3634 return true;
3447} 3635}
3448 3636
3449inline bool Client::send(const std::vector<Request> &requests, 3637inline bool Client::handle_request(Stream &strm, const Request &req,
3450 std::vector<Response> &responses) { 3638 Response &res, bool last_connection,
3451 size_t i = 0; 3639 bool &connection_close) {
3452 while (i < requests.size()) { 3640 if (req.path.empty()) { return false; }
3453 auto sock = create_client_socket();
3454 if (sock == INVALID_SOCKET) { return false; }
3455 3641
3456 if (!process_and_close_socket( 3642 bool ret;
3457 sock, requests.size() - i,
3458 [&](Stream &strm, bool last_connection,
3459 bool &connection_close) -> bool {
3460 auto &req = requests[i];
3461 auto res = Response();
3462 i++;
3463 3643
3464 if (req.path.empty()) { return false; } 3644 if (!is_ssl() && !proxy_host_.empty()) {
3465 auto ret = process_request(strm, req, res, last_connection, 3645 auto req2 = req;
3466 connection_close); 3646 req2.path = "http://" + host_and_port_ + req.path;
3647 ret = process_request(strm, req2, res, last_connection, connection_close);
3648 } else {
3649 ret = process_request(strm, req, res, last_connection, connection_close);
3650 }
3467 3651
3468 if (ret && follow_location_ && 3652 if (!ret) { return false; }
3469 (300 < res.status && res.status < 400)) {
3470 ret = redirect(req, res);
3471 }
3472 3653
3473 if (ret) { responses.emplace_back(std::move(res)); } 3654 if (300 < res.status && res.status < 400 && follow_location_) {
3655 ret = redirect(req, res);
3656 }
3474 3657
3475 return ret; 3658#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3476 })) { 3659 if (res.status == 401 || res.status == 407) {
3660 auto is_proxy = res.status == 407;
3661 const auto &username =
3662 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
3663 const auto &password =
3664 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
3665
3666 if (!username.empty() && !password.empty()) {
3667 std::map<std::string, std::string> auth;
3668 if (parse_www_authenticate(res, auth, is_proxy)) {
3669 Request new_req = req;
3670 auto key = is_proxy ? "Proxy-Authorization" : "WWW-Authorization";
3671 new_req.headers.erase(key);
3672 new_req.headers.insert(make_digest_authentication_header(
3673 req, auth, 1, random_string(10), username, password, is_proxy));
3674
3675 Response new_res;
3676
3677 ret = send(new_req, new_res);
3678 if (ret) { res = new_res; }
3679 }
3680 }
3681 }
3682#endif
3683
3684 return ret;
3685}
3686
3687#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3688inline bool Client::connect(socket_t sock, Response &res, bool &error) {
3689 error = true;
3690 Response res2;
3691
3692 if (!detail::process_socket(
3693 true, sock, 1, read_timeout_sec_, read_timeout_usec_,
3694 [&](Stream &strm, bool /*last_connection*/, bool &connection_close) {
3695 Request req2;
3696 req2.method = "CONNECT";
3697 req2.path = host_and_port_;
3698 return process_request(strm, req2, res2, false, connection_close);
3699 })) {
3700 detail::close_socket(sock);
3701 error = false;
3702 return false;
3703 }
3704
3705 if (res2.status == 407) {
3706 if (!proxy_digest_auth_username_.empty() &&
3707 !proxy_digest_auth_password_.empty()) {
3708 std::map<std::string, std::string> auth;
3709 if (parse_www_authenticate(res2, auth, true)) {
3710 Response res3;
3711 if (!detail::process_socket(
3712 true, sock, 1, read_timeout_sec_, read_timeout_usec_,
3713 [&](Stream &strm, bool /*last_connection*/,
3714 bool &connection_close) {
3715 Request req3;
3716 req3.method = "CONNECT";
3717 req3.path = host_and_port_;
3718 req3.headers.insert(make_digest_authentication_header(
3719 req3, auth, 1, random_string(10),
3720 proxy_digest_auth_username_, proxy_digest_auth_password_,
3721 true));
3722 return process_request(strm, req3, res3, false,
3723 connection_close);
3724 })) {
3725 detail::close_socket(sock);
3726 error = false;
3727 return false;
3728 }
3729 }
3730 } else {
3731 res = res2;
3477 return false; 3732 return false;
3478 } 3733 }
3479 } 3734 }
3480 3735
3481 return true; 3736 return true;
3482} 3737}
3738#endif
3483 3739
3484inline bool Client::redirect(const Request &req, Response &res) { 3740inline bool Client::redirect(const Request &req, Response &res) {
3485 if (req.redirect_count == 0) { return false; } 3741 if (req.redirect_count == 0) { return false; }
@@ -3493,28 +3749,30 @@ inline bool Client::redirect(const Request &req, Response &res) {
3493 std::smatch m; 3749 std::smatch m;
3494 if (!regex_match(location, m, re)) { return false; } 3750 if (!regex_match(location, m, re)) { return false; }
3495 3751
3752 auto scheme = is_ssl() ? "https" : "http";
3753
3496 auto next_scheme = m[1].str(); 3754 auto next_scheme = m[1].str();
3497 auto next_host = m[2].str(); 3755 auto next_host = m[2].str();
3498 auto next_path = m[3].str(); 3756 auto next_path = m[3].str();
3757 if (next_scheme.empty()) { next_scheme = scheme; }
3758 if (next_scheme.empty()) { next_scheme = scheme; }
3499 if (next_host.empty()) { next_host = host_; } 3759 if (next_host.empty()) { next_host = host_; }
3500 if (next_path.empty()) { next_path = "/"; } 3760 if (next_path.empty()) { next_path = "/"; }
3501 3761
3502 auto scheme = is_ssl() ? "https" : "http";
3503
3504 if (next_scheme == scheme && next_host == host_) { 3762 if (next_scheme == scheme && next_host == host_) {
3505 return detail::redirect(*this, req, res, next_path); 3763 return detail::redirect(*this, req, res, next_path);
3506 } else { 3764 } else {
3507 if (next_scheme == "https") { 3765 if (next_scheme == "https") {
3508#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 3766#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3509 SSLClient cli(next_host.c_str()); 3767 SSLClient cli(next_host.c_str());
3510 cli.set_follow_location(true); 3768 cli.copy_settings(*this);
3511 return detail::redirect(cli, req, res, next_path); 3769 return detail::redirect(cli, req, res, next_path);
3512#else 3770#else
3513 return false; 3771 return false;
3514#endif 3772#endif
3515 } else { 3773 } else {
3516 Client cli(next_host.c_str()); 3774 Client cli(next_host.c_str());
3517 cli.set_follow_location(true); 3775 cli.copy_settings(*this);
3518 return detail::redirect(cli, req, res, next_path); 3776 return detail::redirect(cli, req, res, next_path);
3519 } 3777 }
3520 } 3778 }
@@ -3522,16 +3780,10 @@ inline bool Client::redirect(const Request &req, Response &res) {
3522 3780
3523inline bool Client::write_request(Stream &strm, const Request &req, 3781inline bool Client::write_request(Stream &strm, const Request &req,
3524 bool last_connection) { 3782 bool last_connection) {
3525 BufferStream bstrm; 3783 detail::BufferStream bstrm;
3526 3784
3527 // Request line 3785 // Request line
3528 const static std::regex re( 3786 const auto &path = detail::encode_url(req.path);
3529 R"(^([^:/?#]+://[^/?#]*)?([^?#]*(?:\?[^#]*)?(?:#.*)?))");
3530
3531 std::smatch m;
3532 if (!regex_match(req.path, m, re)) { return false; }
3533
3534 auto path = m[1].str() + detail::encode_url(m[2].str());
3535 3787
3536 bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); 3788 bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
3537 3789
@@ -3558,7 +3810,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
3558 if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); } 3810 if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); }
3559 3811
3560 if (!req.has_header("User-Agent")) { 3812 if (!req.has_header("User-Agent")) {
3561 headers.emplace("User-Agent", "cpp-httplib/0.2"); 3813 headers.emplace("User-Agent", "cpp-httplib/0.5");
3562 } 3814 }
3563 3815
3564 if (req.body.empty()) { 3816 if (req.body.empty()) {
@@ -3579,6 +3831,17 @@ inline bool Client::write_request(Stream &strm, const Request &req,
3579 } 3831 }
3580 } 3832 }
3581 3833
3834 if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) {
3835 headers.insert(make_basic_authentication_header(
3836 basic_auth_username_, basic_auth_password_, false));
3837 }
3838
3839 if (!proxy_basic_auth_username_.empty() &&
3840 !proxy_basic_auth_password_.empty()) {
3841 headers.insert(make_basic_authentication_header(
3842 proxy_basic_auth_username_, proxy_basic_auth_password_, true));
3843 }
3844
3582 detail::write_headers(bstrm, req, headers); 3845 detail::write_headers(bstrm, req, headers);
3583 3846
3584 // Flush buffer 3847 // Flush buffer
@@ -3590,12 +3853,16 @@ inline bool Client::write_request(Stream &strm, const Request &req,
3590 if (req.content_provider) { 3853 if (req.content_provider) {
3591 size_t offset = 0; 3854 size_t offset = 0;
3592 size_t end_offset = req.content_length; 3855 size_t end_offset = req.content_length;
3856
3857 DataSink data_sink;
3858 data_sink.write = [&](const char *d, size_t l) {
3859 auto written_length = strm.write(d, l);
3860 offset += written_length;
3861 };
3862 data_sink.is_writable = [&](void) { return strm.is_writable(); };
3863
3593 while (offset < end_offset) { 3864 while (offset < end_offset) {
3594 req.content_provider(offset, end_offset - offset, 3865 req.content_provider(offset, end_offset - offset, data_sink);
3595 [&](const char *d, size_t l) {
3596 auto written_length = strm.write(d, l);
3597 offset += written_length;
3598 });
3599 } 3866 }
3600 } 3867 }
3601 } else { 3868 } else {
@@ -3620,12 +3887,16 @@ inline std::shared_ptr<Response> Client::send_with_content_provider(
3620 if (compress_) { 3887 if (compress_) {
3621 if (content_provider) { 3888 if (content_provider) {
3622 size_t offset = 0; 3889 size_t offset = 0;
3890
3891 DataSink data_sink;
3892 data_sink.write = [&](const char *data, size_t data_len) {
3893 req.body.append(data, data_len);
3894 offset += data_len;
3895 };
3896 data_sink.is_writable = [&](void) { return true; };
3897
3623 while (offset < content_length) { 3898 while (offset < content_length) {
3624 content_provider(offset, content_length - offset, 3899 content_provider(offset, content_length - offset, data_sink);
3625 [&](const char *data, size_t data_len) {
3626 req.body.append(data, data_len);
3627 offset += data_len;
3628 });
3629 } 3900 }
3630 } else { 3901 } else {
3631 req.body = body; 3902 req.body = body;
@@ -3671,7 +3942,7 @@ inline bool Client::process_request(Stream &strm, const Request &req,
3671 } 3942 }
3672 3943
3673 // Body 3944 // Body
3674 if (req.method != "HEAD") { 3945 if (req.method != "HEAD" && req.method != "CONNECT") {
3675 ContentReceiver out = [&](const char *buf, size_t n) { 3946 ContentReceiver out = [&](const char *buf, size_t n) {
3676 if (res.body.size() + n > res.body.max_size()) { return false; } 3947 if (res.body.size() + n > res.body.max_size()) { return false; }
3677 res.body.append(buf, n); 3948 res.body.append(buf, n);
@@ -3691,6 +3962,9 @@ inline bool Client::process_request(Stream &strm, const Request &req,
3691 } 3962 }
3692 } 3963 }
3693 3964
3965 // Log
3966 if (logger_) { logger_(req, res); }
3967
3694 return true; 3968 return true;
3695} 3969}
3696 3970
@@ -3708,8 +3982,7 @@ inline bool Client::process_and_close_socket(
3708inline bool Client::is_ssl() const { return false; } 3982inline bool Client::is_ssl() const { return false; }
3709 3983
3710inline std::shared_ptr<Response> Client::Get(const char *path) { 3984inline std::shared_ptr<Response> Client::Get(const char *path) {
3711 Progress dummy; 3985 return Get(path, Headers(), Progress());
3712 return Get(path, Headers(), dummy);
3713} 3986}
3714 3987
3715inline std::shared_ptr<Response> Client::Get(const char *path, 3988inline std::shared_ptr<Response> Client::Get(const char *path,
@@ -3719,8 +3992,7 @@ inline std::shared_ptr<Response> Client::Get(const char *path,
3719 3992
3720inline std::shared_ptr<Response> Client::Get(const char *path, 3993inline std::shared_ptr<Response> Client::Get(const char *path,
3721 const Headers &headers) { 3994 const Headers &headers) {
3722 Progress dummy; 3995 return Get(path, headers, Progress());
3723 return Get(path, headers, dummy);
3724} 3996}
3725 3997
3726inline std::shared_ptr<Response> 3998inline std::shared_ptr<Response>
@@ -3737,37 +4009,36 @@ Client::Get(const char *path, const Headers &headers, Progress progress) {
3737 4009
3738inline std::shared_ptr<Response> Client::Get(const char *path, 4010inline std::shared_ptr<Response> Client::Get(const char *path,
3739 ContentReceiver content_receiver) { 4011 ContentReceiver content_receiver) {
3740 Progress dummy; 4012 return Get(path, Headers(), nullptr, std::move(content_receiver), Progress());
3741 return Get(path, Headers(), nullptr, std::move(content_receiver), dummy);
3742} 4013}
3743 4014
3744inline std::shared_ptr<Response> Client::Get(const char *path, 4015inline std::shared_ptr<Response> Client::Get(const char *path,
3745 ContentReceiver content_receiver, 4016 ContentReceiver content_receiver,
3746 Progress progress) { 4017 Progress progress) {
3747 return Get(path, Headers(), nullptr, std::move(content_receiver), progress); 4018 return Get(path, Headers(), nullptr, std::move(content_receiver),
4019 std::move(progress));
3748} 4020}
3749 4021
3750inline std::shared_ptr<Response> Client::Get(const char *path, 4022inline std::shared_ptr<Response> Client::Get(const char *path,
3751 const Headers &headers, 4023 const Headers &headers,
3752 ContentReceiver content_receiver) { 4024 ContentReceiver content_receiver) {
3753 Progress dummy; 4025 return Get(path, headers, nullptr, std::move(content_receiver), Progress());
3754 return Get(path, headers, nullptr, std::move(content_receiver), dummy);
3755} 4026}
3756 4027
3757inline std::shared_ptr<Response> Client::Get(const char *path, 4028inline std::shared_ptr<Response> Client::Get(const char *path,
3758 const Headers &headers, 4029 const Headers &headers,
3759 ContentReceiver content_receiver, 4030 ContentReceiver content_receiver,
3760 Progress progress) { 4031 Progress progress) {
3761 return Get(path, headers, nullptr, std::move(content_receiver), progress); 4032 return Get(path, headers, nullptr, std::move(content_receiver),
4033 std::move(progress));
3762} 4034}
3763 4035
3764inline std::shared_ptr<Response> Client::Get(const char *path, 4036inline std::shared_ptr<Response> Client::Get(const char *path,
3765 const Headers &headers, 4037 const Headers &headers,
3766 ResponseHandler response_handler, 4038 ResponseHandler response_handler,
3767 ContentReceiver content_receiver) { 4039 ContentReceiver content_receiver) {
3768 Progress dummy;
3769 return Get(path, headers, std::move(response_handler), content_receiver, 4040 return Get(path, headers, std::move(response_handler), content_receiver,
3770 dummy); 4041 Progress());
3771} 4042}
3772 4043
3773inline std::shared_ptr<Response> Client::Get(const char *path, 4044inline std::shared_ptr<Response> Client::Get(const char *path,
@@ -3911,6 +4182,24 @@ Client::Put(const char *path, const Headers &headers, size_t content_length,
3911 content_type); 4182 content_type);
3912} 4183}
3913 4184
4185inline std::shared_ptr<Response> Client::Put(const char *path,
4186 const Params &params) {
4187 return Put(path, Headers(), params);
4188}
4189
4190inline std::shared_ptr<Response>
4191Client::Put(const char *path, const Headers &headers, const Params &params) {
4192 std::string query;
4193 for (auto it = params.begin(); it != params.end(); ++it) {
4194 if (it != params.begin()) { query += "&"; }
4195 query += it->first;
4196 query += "=";
4197 query += detail::encode_url(it->second);
4198 }
4199
4200 return Put(path, headers, query, "application/x-www-form-urlencoded");
4201}
4202
3914inline std::shared_ptr<Response> Client::Patch(const char *path, 4203inline std::shared_ptr<Response> Client::Patch(const char *path,
3915 const std::string &body, 4204 const std::string &body,
3916 const char *content_type) { 4205 const char *content_type) {
@@ -3988,8 +4277,8 @@ inline std::shared_ptr<Response> Client::Options(const char *path,
3988 return send(req, *res) ? res : nullptr; 4277 return send(req, *res) ? res : nullptr;
3989} 4278}
3990 4279
3991inline void Client::set_keep_alive_max_count(size_t count) { 4280inline void Client::set_timeout_sec(time_t timeout_sec) {
3992 keep_alive_max_count_ = count; 4281 timeout_sec_ = timeout_sec;
3993} 4282}
3994 4283
3995inline void Client::set_read_timeout(time_t sec, time_t usec) { 4284inline void Client::set_read_timeout(time_t sec, time_t usec) {
@@ -3997,17 +4286,50 @@ inline void Client::set_read_timeout(time_t sec, time_t usec) {
3997 read_timeout_usec_ = usec; 4286 read_timeout_usec_ = usec;
3998} 4287}
3999 4288
4000inline void Client::set_auth(const char *username, const char *password) { 4289inline void Client::set_keep_alive_max_count(size_t count) {
4001 username_ = username; 4290 keep_alive_max_count_ = count;
4002 password_ = password;
4003} 4291}
4004 4292
4293inline void Client::set_basic_auth(const char *username, const char *password) {
4294 basic_auth_username_ = username;
4295 basic_auth_password_ = password;
4296}
4297
4298#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4299inline void Client::set_digest_auth(const char *username,
4300 const char *password) {
4301 digest_auth_username_ = username;
4302 digest_auth_password_ = password;
4303}
4304#endif
4305
4005inline void Client::set_follow_location(bool on) { follow_location_ = on; } 4306inline void Client::set_follow_location(bool on) { follow_location_ = on; }
4006 4307
4007inline void Client::set_compress(bool on) { compress_ = on; } 4308inline void Client::set_compress(bool on) { compress_ = on; }
4008 4309
4009inline void Client::set_interface(const char *intf) { interface_ = intf; } 4310inline void Client::set_interface(const char *intf) { interface_ = intf; }
4010 4311
4312inline void Client::set_proxy(const char *host, int port) {
4313 proxy_host_ = host;
4314 proxy_port_ = port;
4315}
4316
4317inline void Client::set_proxy_basic_auth(const char *username,
4318 const char *password) {
4319 proxy_basic_auth_username_ = username;
4320 proxy_basic_auth_password_ = password;
4321}
4322
4323#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4324inline void Client::set_proxy_digest_auth(const char *username,
4325 const char *password) {
4326 proxy_digest_auth_username_ = username;
4327 proxy_digest_auth_password_ = password;
4328}
4329#endif
4330
4331inline void Client::set_logger(Logger logger) { logger_ = std::move(logger); }
4332
4011/* 4333/*
4012 * SSL Implementation 4334 * SSL Implementation
4013 */ 4335 */
@@ -4046,7 +4368,7 @@ inline bool process_and_close_socket_ssl(
4046 return false; 4368 return false;
4047 } 4369 }
4048 4370
4049 bool ret = false; 4371 auto ret = false;
4050 4372
4051 if (SSL_connect_or_accept(ssl) == 1) { 4373 if (SSL_connect_or_accept(ssl) == 1) {
4052 if (keep_alive_max_count > 1) { 4374 if (keep_alive_max_count > 1) {
@@ -4133,10 +4455,6 @@ private:
4133#endif 4455#endif
4134}; 4456};
4135 4457
4136static SSLInit sslinit_;
4137
4138} // namespace detail
4139
4140// SSL socket stream implementation 4458// SSL socket stream implementation
4141inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, 4459inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
4142 time_t read_timeout_sec, 4460 time_t read_timeout_sec,
@@ -4146,30 +4464,35 @@ inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
4146 4464
4147inline SSLSocketStream::~SSLSocketStream() {} 4465inline SSLSocketStream::~SSLSocketStream() {}
4148 4466
4467inline bool SSLSocketStream::is_readable() const {
4468 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
4469}
4470
4471inline bool SSLSocketStream::is_writable() const {
4472 return detail::select_write(sock_, 0, 0) > 0;
4473}
4474
4149inline int SSLSocketStream::read(char *ptr, size_t size) { 4475inline int SSLSocketStream::read(char *ptr, size_t size) {
4150 if (SSL_pending(ssl_) > 0 || 4476 if (SSL_pending(ssl_) > 0 ||
4151 detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) { 4477 select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) {
4152 return SSL_read(ssl_, ptr, static_cast<int>(size)); 4478 return SSL_read(ssl_, ptr, static_cast<int>(size));
4153 } 4479 }
4154 return -1; 4480 return -1;
4155} 4481}
4156 4482
4157inline int SSLSocketStream::write(const char *ptr, size_t size) { 4483inline int SSLSocketStream::write(const char *ptr, size_t size) {
4158 return SSL_write(ssl_, ptr, static_cast<int>(size)); 4484 if (is_writable()) { return SSL_write(ssl_, ptr, static_cast<int>(size)); }
4159} 4485 return -1;
4160
4161inline int SSLSocketStream::write(const char *ptr) {
4162 return write(ptr, strlen(ptr));
4163}
4164
4165inline int SSLSocketStream::write(const std::string &s) {
4166 return write(s.data(), s.size());
4167} 4486}
4168 4487
4169inline std::string SSLSocketStream::get_remote_addr() const { 4488inline std::string SSLSocketStream::get_remote_addr() const {
4170 return detail::get_remote_addr(sock_); 4489 return detail::get_remote_addr(sock_);
4171} 4490}
4172 4491
4492static SSLInit sslinit_;
4493
4494} // namespace detail
4495
4173// SSL HTTP server implementation 4496// SSL HTTP server implementation
4174inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, 4497inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
4175 const char *client_ca_cert_file_path, 4498 const char *client_ca_cert_file_path,
@@ -4227,21 +4550,21 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
4227} 4550}
4228 4551
4229// SSL HTTP client implementation 4552// SSL HTTP client implementation
4230inline SSLClient::SSLClient(const char *host, int port, time_t timeout_sec, 4553inline SSLClient::SSLClient(const std::string &host, int port,
4231 const char *client_cert_path, 4554 const std::string &client_cert_path,
4232 const char *client_key_path) 4555 const std::string &client_key_path)
4233 : Client(host, port, timeout_sec) { 4556 : Client(host, port, client_cert_path, client_key_path) {
4234 ctx_ = SSL_CTX_new(SSLv23_client_method()); 4557 ctx_ = SSL_CTX_new(SSLv23_client_method());
4235 4558
4236 detail::split(&host_[0], &host_[host_.size()], '.', 4559 detail::split(&host_[0], &host_[host_.size()], '.',
4237 [&](const char *b, const char *e) { 4560 [&](const char *b, const char *e) {
4238 host_components_.emplace_back(std::string(b, e)); 4561 host_components_.emplace_back(std::string(b, e));
4239 }); 4562 });
4240 if (client_cert_path && client_key_path) { 4563 if (!client_cert_path.empty() && !client_key_path.empty()) {
4241 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path, 4564 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
4242 SSL_FILETYPE_PEM) != 1 || 4565 SSL_FILETYPE_PEM) != 1 ||
4243 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path, SSL_FILETYPE_PEM) != 4566 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
4244 1) { 4567 SSL_FILETYPE_PEM) != 1) {
4245 SSL_CTX_free(ctx_); 4568 SSL_CTX_free(ctx_);
4246 ctx_ = nullptr; 4569 ctx_ = nullptr;
4247 } 4570 }