summaryrefslogtreecommitdiff
path: root/externals/httplib/httplib.h
diff options
context:
space:
mode:
Diffstat (limited to 'externals/httplib/httplib.h')
-rw-r--r--externals/httplib/httplib.h651
1 files changed, 440 insertions, 211 deletions
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
index 5139b7f05..8982054e2 100644
--- a/externals/httplib/httplib.h
+++ b/externals/httplib/httplib.h
@@ -173,6 +173,7 @@ using socket_t = int;
173#define INVALID_SOCKET (-1) 173#define INVALID_SOCKET (-1)
174#endif //_WIN32 174#endif //_WIN32
175 175
176#include <algorithm>
176#include <array> 177#include <array>
177#include <atomic> 178#include <atomic>
178#include <cassert> 179#include <cassert>
@@ -237,6 +238,27 @@ namespace httplib {
237 238
238namespace detail { 239namespace detail {
239 240
241/*
242 * Backport std::make_unique from C++14.
243 *
244 * NOTE: This code came up with the following stackoverflow post:
245 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
246 *
247 */
248
249template <class T, class... Args>
250typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
251make_unique(Args &&... args) {
252 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
253}
254
255template <class T>
256typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
257make_unique(std::size_t n) {
258 typedef typename std::remove_extent<T>::type RT;
259 return std::unique_ptr<T>(new RT[n]);
260}
261
240struct ci { 262struct ci {
241 bool operator()(const std::string &s1, const std::string &s2) const { 263 bool operator()(const std::string &s1, const std::string &s2) const {
242 return std::lexicographical_compare( 264 return std::lexicographical_compare(
@@ -304,6 +326,10 @@ using ContentProvider =
304using ContentProviderWithoutLength = 326using ContentProviderWithoutLength =
305 std::function<bool(size_t offset, DataSink &sink)>; 327 std::function<bool(size_t offset, DataSink &sink)>;
306 328
329using ContentReceiverWithProgress =
330 std::function<bool(const char *data, size_t data_length, uint64_t offset,
331 uint64_t total_length)>;
332
307using ContentReceiver = 333using ContentReceiver =
308 std::function<bool(const char *data, size_t data_length)>; 334 std::function<bool(const char *data, size_t data_length)>;
309 335
@@ -317,14 +343,17 @@ public:
317 ContentReceiver receiver)>; 343 ContentReceiver receiver)>;
318 344
319 ContentReader(Reader reader, MultipartReader multipart_reader) 345 ContentReader(Reader reader, MultipartReader multipart_reader)
320 : reader_(reader), multipart_reader_(multipart_reader) {} 346 : reader_(std::move(reader)),
347 multipart_reader_(std::move(multipart_reader)) {}
321 348
322 bool operator()(MultipartContentHeader header, 349 bool operator()(MultipartContentHeader header,
323 ContentReceiver receiver) const { 350 ContentReceiver receiver) const {
324 return multipart_reader_(header, receiver); 351 return multipart_reader_(std::move(header), std::move(receiver));
325 } 352 }
326 353
327 bool operator()(ContentReceiver receiver) const { return reader_(receiver); } 354 bool operator()(ContentReceiver receiver) const {
355 return reader_(std::move(receiver));
356 }
328 357
329 Reader reader_; 358 Reader reader_;
330 MultipartReader multipart_reader_; 359 MultipartReader multipart_reader_;
@@ -353,7 +382,7 @@ struct Request {
353 // for client 382 // for client
354 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; 383 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
355 ResponseHandler response_handler; 384 ResponseHandler response_handler;
356 ContentReceiver content_receiver; 385 ContentReceiverWithProgress content_receiver;
357 size_t content_length = 0; 386 size_t content_length = 0;
358 ContentProvider content_provider; 387 ContentProvider content_provider;
359 Progress progress; 388 Progress progress;
@@ -475,7 +504,7 @@ public:
475 504
476 void enqueue(std::function<void()> fn) override { 505 void enqueue(std::function<void()> fn) override {
477 std::unique_lock<std::mutex> lock(mutex_); 506 std::unique_lock<std::mutex> lock(mutex_);
478 jobs_.push_back(fn); 507 jobs_.push_back(std::move(fn));
479 cond_.notify_one(); 508 cond_.notify_one();
480 } 509 }
481 510
@@ -664,8 +693,8 @@ private:
664 ContentReceiver multipart_receiver); 693 ContentReceiver multipart_receiver);
665 694
666 virtual bool process_and_close_socket(socket_t sock); 695 virtual bool process_and_close_socket(socket_t sock);
667 696
668 struct MountPointEntry { 697 struct MountPointEntry {
669 std::string mount_point; 698 std::string mount_point;
670 std::string base_dir; 699 std::string base_dir;
671 Headers headers; 700 Headers headers;
@@ -704,23 +733,27 @@ enum Error {
704 Canceled, 733 Canceled,
705 SSLConnection, 734 SSLConnection,
706 SSLLoadingCerts, 735 SSLLoadingCerts,
707 SSLServerVerification 736 SSLServerVerification,
737 UnsupportedMultipartBoundaryChars
708}; 738};
709 739
710class Result { 740class Result {
711public: 741public:
712 Result(const std::shared_ptr<Response> &res, Error err) 742 Result(std::unique_ptr<Response> res, Error err)
713 : res_(res), err_(err) {} 743 : res_(std::move(res)), err_(err) {}
714 operator bool() const { return res_ != nullptr; } 744 operator bool() const { return res_ != nullptr; }
715 bool operator==(std::nullptr_t) const { return res_ == nullptr; } 745 bool operator==(std::nullptr_t) const { return res_ == nullptr; }
716 bool operator!=(std::nullptr_t) const { return res_ != nullptr; } 746 bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
717 const Response &value() const { return *res_; } 747 const Response &value() const { return *res_; }
748 Response &value() { return *res_; }
718 const Response &operator*() const { return *res_; } 749 const Response &operator*() const { return *res_; }
750 Response &operator*() { return *res_; }
719 const Response *operator->() const { return res_.get(); } 751 const Response *operator->() const { return res_.get(); }
752 Response *operator->() { return res_.get(); }
720 Error error() const { return err_; } 753 Error error() const { return err_; }
721 754
722private: 755private:
723 std::shared_ptr<Response> res_; 756 std::unique_ptr<Response> res_;
724 Error err_; 757 Error err_;
725}; 758};
726 759
@@ -777,6 +810,8 @@ public:
777 Result Post(const char *path, const MultipartFormDataItems &items); 810 Result Post(const char *path, const MultipartFormDataItems &items);
778 Result Post(const char *path, const Headers &headers, 811 Result Post(const char *path, const Headers &headers,
779 const MultipartFormDataItems &items); 812 const MultipartFormDataItems &items);
813 Result Post(const char *path, const Headers &headers,
814 const MultipartFormDataItems &items, const std::string &boundary);
780 815
781 Result Put(const char *path); 816 Result Put(const char *path);
782 Result Put(const char *path, const std::string &body, 817 Result Put(const char *path, const std::string &body,
@@ -863,7 +898,21 @@ protected:
863 }; 898 };
864 899
865 virtual bool create_and_connect_socket(Socket &socket); 900 virtual bool create_and_connect_socket(Socket &socket);
866 virtual void close_socket(Socket &socket, bool process_socket_ret); 901
902 // All of:
903 // shutdown_ssl
904 // shutdown_socket
905 // close_socket
906 // should ONLY be called when socket_mutex_ is locked.
907 // Also, shutdown_ssl and close_socket should also NOT be called concurrently
908 // with a DIFFERENT thread sending requests using that socket.
909 virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
910 void shutdown_socket(Socket &socket);
911 void close_socket(Socket &socket);
912
913 // Similar to shutdown_ssl and close_socket, this should NOT be called
914 // concurrently with a DIFFERENT thread sending requests from the socket
915 void lock_socket_and_shutdown_and_close();
867 916
868 bool process_request(Stream &strm, const Request &req, Response &res, 917 bool process_request(Stream &strm, const Request &req, Response &res,
869 bool close_connection); 918 bool close_connection);
@@ -873,7 +922,7 @@ protected:
873 void copy_settings(const ClientImpl &rhs); 922 void copy_settings(const ClientImpl &rhs);
874 923
875 // Error state 924 // Error state
876 mutable Error error_ = Error::Success; 925 mutable std::atomic<Error> error_;
877 926
878 // Socket endoint information 927 // Socket endoint information
879 const std::string host_; 928 const std::string host_;
@@ -885,6 +934,11 @@ protected:
885 mutable std::mutex socket_mutex_; 934 mutable std::mutex socket_mutex_;
886 std::recursive_mutex request_mutex_; 935 std::recursive_mutex request_mutex_;
887 936
937 // These are all protected under socket_mutex
938 int socket_requests_in_flight_ = 0;
939 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
940 bool socket_should_be_closed_when_request_is_done_ = false;
941
888 // Default headers 942 // Default headers
889 Headers default_headers_; 943 Headers default_headers_;
890 944
@@ -942,13 +996,13 @@ private:
942 bool redirect(const Request &req, Response &res); 996 bool redirect(const Request &req, Response &res);
943 bool handle_request(Stream &strm, const Request &req, Response &res, 997 bool handle_request(Stream &strm, const Request &req, Response &res,
944 bool close_connection); 998 bool close_connection);
945 void stop_core(); 999 std::unique_ptr<Response> send_with_content_provider(
946 std::shared_ptr<Response> send_with_content_provider(
947 const char *method, const char *path, const Headers &headers, 1000 const char *method, const char *path, const Headers &headers,
948 const std::string &body, size_t content_length, 1001 const std::string &body, size_t content_length,
949 ContentProvider content_provider, const char *content_type); 1002 ContentProvider content_provider, const char *content_type);
950 1003
951 virtual bool process_socket(Socket &socket, 1004 // socket is const because this function is called when socket_mutex_ is not locked
1005 virtual bool process_socket(const Socket &socket,
952 std::function<bool(Stream &strm)> callback); 1006 std::function<bool(Stream &strm)> callback);
953 virtual bool is_ssl() const; 1007 virtual bool is_ssl() const;
954}; 1008};
@@ -1012,6 +1066,8 @@ public:
1012 Result Post(const char *path, const MultipartFormDataItems &items); 1066 Result Post(const char *path, const MultipartFormDataItems &items);
1013 Result Post(const char *path, const Headers &headers, 1067 Result Post(const char *path, const Headers &headers,
1014 const MultipartFormDataItems &items); 1068 const MultipartFormDataItems &items);
1069 Result Post(const char *path, const Headers &headers,
1070 const MultipartFormDataItems &items, const std::string &boundary);
1015 Result Put(const char *path); 1071 Result Put(const char *path);
1016 Result Put(const char *path, const std::string &body, 1072 Result Put(const char *path, const std::string &body,
1017 const char *content_type); 1073 const char *content_type);
@@ -1098,7 +1154,7 @@ public:
1098#endif 1154#endif
1099 1155
1100private: 1156private:
1101 std::shared_ptr<ClientImpl> cli_; 1157 std::unique_ptr<ClientImpl> cli_;
1102 1158
1103#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 1159#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1104 bool is_ssl_ = false; 1160 bool is_ssl_ = false;
@@ -1154,9 +1210,9 @@ public:
1154 1210
1155private: 1211private:
1156 bool create_and_connect_socket(Socket &socket) override; 1212 bool create_and_connect_socket(Socket &socket) override;
1157 void close_socket(Socket &socket, bool process_socket_ret) override; 1213 void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
1158 1214
1159 bool process_socket(Socket &socket, 1215 bool process_socket(const Socket &socket,
1160 std::function<bool(Stream &strm)> callback) override; 1216 std::function<bool(Stream &strm)> callback) override;
1161 bool is_ssl() const override; 1217 bool is_ssl() const override;
1162 1218
@@ -1942,9 +1998,9 @@ inline socket_t create_client_socket(const char *host, int port,
1942 bool tcp_nodelay, 1998 bool tcp_nodelay,
1943 SocketOptions socket_options, 1999 SocketOptions socket_options,
1944 time_t timeout_sec, time_t timeout_usec, 2000 time_t timeout_sec, time_t timeout_usec,
1945 const std::string &intf, Error &error) { 2001 const std::string &intf, std::atomic<Error> &error) {
1946 auto sock = create_socket( 2002 auto sock = create_socket(
1947 host, port, 0, tcp_nodelay, socket_options, 2003 host, port, 0, tcp_nodelay, std::move(socket_options),
1948 [&](socket_t sock, struct addrinfo &ai) -> bool { 2004 [&](socket_t sock, struct addrinfo &ai) -> bool {
1949 if (!intf.empty()) { 2005 if (!intf.empty()) {
1950#ifdef USE_IF2IP 2006#ifdef USE_IF2IP
@@ -2478,7 +2534,8 @@ inline bool read_headers(Stream &strm, Headers &headers) {
2478} 2534}
2479 2535
2480inline bool read_content_with_length(Stream &strm, uint64_t len, 2536inline bool read_content_with_length(Stream &strm, uint64_t len,
2481 Progress progress, ContentReceiver out) { 2537 Progress progress,
2538 ContentReceiverWithProgress out) {
2482 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 2539 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2483 2540
2484 uint64_t r = 0; 2541 uint64_t r = 0;
@@ -2487,8 +2544,7 @@ inline bool read_content_with_length(Stream &strm, uint64_t len,
2487 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); 2544 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
2488 if (n <= 0) { return false; } 2545 if (n <= 0) { return false; }
2489 2546
2490 if (!out(buf, static_cast<size_t>(n))) { return false; } 2547 if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
2491
2492 r += static_cast<uint64_t>(n); 2548 r += static_cast<uint64_t>(n);
2493 2549
2494 if (progress) { 2550 if (progress) {
@@ -2510,8 +2566,10 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
2510 } 2566 }
2511} 2567}
2512 2568
2513inline bool read_content_without_length(Stream &strm, ContentReceiver out) { 2569inline bool read_content_without_length(Stream &strm,
2570 ContentReceiverWithProgress out) {
2514 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 2571 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2572 uint64_t r = 0;
2515 for (;;) { 2573 for (;;) {
2516 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); 2574 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
2517 if (n < 0) { 2575 if (n < 0) {
@@ -2519,13 +2577,16 @@ inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
2519 } else if (n == 0) { 2577 } else if (n == 0) {
2520 return true; 2578 return true;
2521 } 2579 }
2522 if (!out(buf, static_cast<size_t>(n))) { return false; } 2580
2581 if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
2582 r += static_cast<uint64_t>(n);
2523 } 2583 }
2524 2584
2525 return true; 2585 return true;
2526} 2586}
2527 2587
2528inline bool read_content_chunked(Stream &strm, ContentReceiver out) { 2588inline bool read_content_chunked(Stream &strm,
2589 ContentReceiverWithProgress out) {
2529 const auto bufsiz = 16; 2590 const auto bufsiz = 16;
2530 char buf[bufsiz]; 2591 char buf[bufsiz];
2531 2592
@@ -2570,23 +2631,24 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
2570} 2631}
2571 2632
2572template <typename T, typename U> 2633template <typename T, typename U>
2573bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver, 2634bool prepare_content_receiver(T &x, int &status,
2635 ContentReceiverWithProgress receiver,
2574 bool decompress, U callback) { 2636 bool decompress, U callback) {
2575 if (decompress) { 2637 if (decompress) {
2576 std::string encoding = x.get_header_value("Content-Encoding"); 2638 std::string encoding = x.get_header_value("Content-Encoding");
2577 std::shared_ptr<decompressor> decompressor; 2639 std::unique_ptr<decompressor> decompressor;
2578 2640
2579 if (encoding.find("gzip") != std::string::npos || 2641 if (encoding.find("gzip") != std::string::npos ||
2580 encoding.find("deflate") != std::string::npos) { 2642 encoding.find("deflate") != std::string::npos) {
2581#ifdef CPPHTTPLIB_ZLIB_SUPPORT 2643#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2582 decompressor = std::make_shared<gzip_decompressor>(); 2644 decompressor = detail::make_unique<gzip_decompressor>();
2583#else 2645#else
2584 status = 415; 2646 status = 415;
2585 return false; 2647 return false;
2586#endif 2648#endif
2587 } else if (encoding.find("br") != std::string::npos) { 2649 } else if (encoding.find("br") != std::string::npos) {
2588#ifdef CPPHTTPLIB_BROTLI_SUPPORT 2650#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2589 decompressor = std::make_shared<brotli_decompressor>(); 2651 decompressor = detail::make_unique<brotli_decompressor>();
2590#else 2652#else
2591 status = 415; 2653 status = 415;
2592 return false; 2654 return false;
@@ -2595,12 +2657,14 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2595 2657
2596 if (decompressor) { 2658 if (decompressor) {
2597 if (decompressor->is_valid()) { 2659 if (decompressor->is_valid()) {
2598 ContentReceiver out = [&](const char *buf, size_t n) { 2660 ContentReceiverWithProgress out = [&](const char *buf, size_t n,
2599 return decompressor->decompress( 2661 uint64_t off, uint64_t len) {
2600 buf, n, 2662 return decompressor->decompress(buf, n,
2601 [&](const char *buf, size_t n) { return receiver(buf, n); }); 2663 [&](const char *buf, size_t n) {
2664 return receiver(buf, n, off, len);
2665 });
2602 }; 2666 };
2603 return callback(out); 2667 return callback(std::move(out));
2604 } else { 2668 } else {
2605 status = 500; 2669 status = 500;
2606 return false; 2670 return false;
@@ -2608,18 +2672,20 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2608 } 2672 }
2609 } 2673 }
2610 2674
2611 ContentReceiver out = [&](const char *buf, size_t n) { 2675 ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
2612 return receiver(buf, n); 2676 uint64_t len) {
2677 return receiver(buf, n, off, len);
2613 }; 2678 };
2614 return callback(out); 2679 return callback(std::move(out));
2615} 2680}
2616 2681
2617template <typename T> 2682template <typename T>
2618bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, 2683bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2619 Progress progress, ContentReceiver receiver, 2684 Progress progress, ContentReceiverWithProgress receiver,
2620 bool decompress) { 2685 bool decompress) {
2621 return prepare_content_receiver( 2686 return prepare_content_receiver(
2622 x, status, receiver, decompress, [&](const ContentReceiver &out) { 2687 x, status, std::move(receiver), decompress,
2688 [&](const ContentReceiverWithProgress &out) {
2623 auto ret = true; 2689 auto ret = true;
2624 auto exceed_payload_max_length = false; 2690 auto exceed_payload_max_length = false;
2625 2691
@@ -2634,7 +2700,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2634 skip_content_with_length(strm, len); 2700 skip_content_with_length(strm, len);
2635 ret = false; 2701 ret = false;
2636 } else if (len > 0) { 2702 } else if (len > 0) {
2637 ret = read_content_with_length(strm, len, progress, out); 2703 ret = read_content_with_length(strm, len, std::move(progress), out);
2638 } 2704 }
2639 } 2705 }
2640 2706
@@ -2875,7 +2941,7 @@ inline bool parse_multipart_boundary(const std::string &content_type,
2875 return !boundary.empty(); 2941 return !boundary.empty();
2876} 2942}
2877 2943
2878inline bool parse_range_header(const std::string &s, Ranges &ranges) { 2944inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
2879 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); 2945 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
2880 std::smatch m; 2946 std::smatch m;
2881 if (std::regex_match(s, m, re_first_range)) { 2947 if (std::regex_match(s, m, re_first_range)) {
@@ -2907,7 +2973,7 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
2907 return all_valid_ranges; 2973 return all_valid_ranges;
2908 } 2974 }
2909 return false; 2975 return false;
2910} 2976} catch (...) { return false; }
2911 2977
2912class MultipartFormDataParser { 2978class MultipartFormDataParser {
2913public: 2979public:
@@ -2918,7 +2984,8 @@ public:
2918 bool is_valid() const { return is_valid_; } 2984 bool is_valid() const { return is_valid_; }
2919 2985
2920 template <typename T, typename U> 2986 template <typename T, typename U>
2921 bool parse(const char *buf, size_t n, T content_callback, U header_callback) { 2987 bool parse(const char *buf, size_t n, const T &content_callback,
2988 const U &header_callback) {
2922 2989
2923 static const std::regex re_content_disposition( 2990 static const std::regex re_content_disposition(
2924 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" 2991 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
@@ -3090,8 +3157,13 @@ inline std::string make_multipart_data_boundary() {
3090 static const char data[] = 3157 static const char data[] =
3091 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 3158 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3092 3159
3160 // std::random_device might actually be deterministic on some
3161 // platforms, but due to lack of support in the c++ standard library,
3162 // doing better requires either some ugly hacks or breaking portability.
3093 std::random_device seed_gen; 3163 std::random_device seed_gen;
3094 std::mt19937 engine(seed_gen()); 3164 // Request 128 bits of entropy for initialization
3165 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
3166 std::mt19937 engine(seed_sequence);
3095 3167
3096 std::string result = "--cpp-httplib-multipart-data-"; 3168 std::string result = "--cpp-httplib-multipart-data-";
3097 3169
@@ -3114,7 +3186,7 @@ get_range_offset_and_length(const Request &req, size_t content_length,
3114 auto slen = static_cast<ssize_t>(content_length); 3186 auto slen = static_cast<ssize_t>(content_length);
3115 3187
3116 if (r.first == -1) { 3188 if (r.first == -1) {
3117 r.first = slen - r.second; 3189 r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);
3118 r.second = slen - 1; 3190 r.second = slen - 1;
3119 } 3191 }
3120 3192
@@ -3451,7 +3523,7 @@ inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
3451 if (r.second != -1) { field += std::to_string(r.second); } 3523 if (r.second != -1) { field += std::to_string(r.second); }
3452 i++; 3524 i++;
3453 } 3525 }
3454 return std::make_pair("Range", field); 3526 return std::make_pair("Range", std::move(field));
3455} 3527}
3456 3528
3457inline std::pair<std::string, std::string> 3529inline std::pair<std::string, std::string>
@@ -3460,7 +3532,7 @@ make_basic_authentication_header(const std::string &username,
3460 bool is_proxy = false) { 3532 bool is_proxy = false) {
3461 auto field = "Basic " + detail::base64_encode(username + ":" + password); 3533 auto field = "Basic " + detail::base64_encode(username + ":" + password);
3462 auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; 3534 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3463 return std::make_pair(key, field); 3535 return std::make_pair(key, std::move(field));
3464} 3536}
3465 3537
3466inline std::pair<std::string, std::string> 3538inline std::pair<std::string, std::string>
@@ -3468,7 +3540,7 @@ make_bearer_token_authentication_header(const std::string &token,
3468 bool is_proxy = false) { 3540 bool is_proxy = false) {
3469 auto field = "Bearer " + token; 3541 auto field = "Bearer " + token;
3470 auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; 3542 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3471 return std::make_pair(key, field); 3543 return std::make_pair(key, std::move(field));
3472} 3544}
3473 3545
3474// Request implementation 3546// Request implementation
@@ -3761,60 +3833,66 @@ inline Server::Server()
3761inline Server::~Server() {} 3833inline Server::~Server() {}
3762 3834
3763inline Server &Server::Get(const char *pattern, Handler handler) { 3835inline Server &Server::Get(const char *pattern, Handler handler) {
3764 get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3836 get_handlers_.push_back(
3837 std::make_pair(std::regex(pattern), std::move(handler)));
3765 return *this; 3838 return *this;
3766} 3839}
3767 3840
3768inline Server &Server::Post(const char *pattern, Handler handler) { 3841inline Server &Server::Post(const char *pattern, Handler handler) {
3769 post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3842 post_handlers_.push_back(
3843 std::make_pair(std::regex(pattern), std::move(handler)));
3770 return *this; 3844 return *this;
3771} 3845}
3772 3846
3773inline Server &Server::Post(const char *pattern, 3847inline Server &Server::Post(const char *pattern,
3774 HandlerWithContentReader handler) { 3848 HandlerWithContentReader handler) {
3775 post_handlers_for_content_reader_.push_back( 3849 post_handlers_for_content_reader_.push_back(
3776 std::make_pair(std::regex(pattern), handler)); 3850 std::make_pair(std::regex(pattern), std::move(handler)));
3777 return *this; 3851 return *this;
3778} 3852}
3779 3853
3780inline Server &Server::Put(const char *pattern, Handler handler) { 3854inline Server &Server::Put(const char *pattern, Handler handler) {
3781 put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3855 put_handlers_.push_back(
3856 std::make_pair(std::regex(pattern), std::move(handler)));
3782 return *this; 3857 return *this;
3783} 3858}
3784 3859
3785inline Server &Server::Put(const char *pattern, 3860inline Server &Server::Put(const char *pattern,
3786 HandlerWithContentReader handler) { 3861 HandlerWithContentReader handler) {
3787 put_handlers_for_content_reader_.push_back( 3862 put_handlers_for_content_reader_.push_back(
3788 std::make_pair(std::regex(pattern), handler)); 3863 std::make_pair(std::regex(pattern), std::move(handler)));
3789 return *this; 3864 return *this;
3790} 3865}
3791 3866
3792inline Server &Server::Patch(const char *pattern, Handler handler) { 3867inline Server &Server::Patch(const char *pattern, Handler handler) {
3793 patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3868 patch_handlers_.push_back(
3869 std::make_pair(std::regex(pattern), std::move(handler)));
3794 return *this; 3870 return *this;
3795} 3871}
3796 3872
3797inline Server &Server::Patch(const char *pattern, 3873inline Server &Server::Patch(const char *pattern,
3798 HandlerWithContentReader handler) { 3874 HandlerWithContentReader handler) {
3799 patch_handlers_for_content_reader_.push_back( 3875 patch_handlers_for_content_reader_.push_back(
3800 std::make_pair(std::regex(pattern), handler)); 3876 std::make_pair(std::regex(pattern), std::move(handler)));
3801 return *this; 3877 return *this;
3802} 3878}
3803 3879
3804inline Server &Server::Delete(const char *pattern, Handler handler) { 3880inline Server &Server::Delete(const char *pattern, Handler handler) {
3805 delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3881 delete_handlers_.push_back(
3882 std::make_pair(std::regex(pattern), std::move(handler)));
3806 return *this; 3883 return *this;
3807} 3884}
3808 3885
3809inline Server &Server::Delete(const char *pattern, 3886inline Server &Server::Delete(const char *pattern,
3810 HandlerWithContentReader handler) { 3887 HandlerWithContentReader handler) {
3811 delete_handlers_for_content_reader_.push_back( 3888 delete_handlers_for_content_reader_.push_back(
3812 std::make_pair(std::regex(pattern), handler)); 3889 std::make_pair(std::regex(pattern), std::move(handler)));
3813 return *this; 3890 return *this;
3814} 3891}
3815 3892
3816inline Server &Server::Options(const char *pattern, Handler handler) { 3893inline Server &Server::Options(const char *pattern, Handler handler) {
3817 options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3894 options_handlers_.push_back(
3895 std::make_pair(std::regex(pattern), std::move(handler)));
3818 return *this; 3896 return *this;
3819} 3897}
3820 3898
@@ -3860,7 +3938,7 @@ inline void Server::set_error_handler(Handler handler) {
3860inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } 3938inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
3861 3939
3862inline void Server::set_socket_options(SocketOptions socket_options) { 3940inline void Server::set_socket_options(SocketOptions socket_options) {
3863 socket_options_ = socket_options; 3941 socket_options_ = std::move(socket_options);
3864} 3942}
3865 3943
3866inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } 3944inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
@@ -4045,16 +4123,16 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
4045 } 4123 }
4046 4124
4047 if (type != detail::EncodingType::None) { 4125 if (type != detail::EncodingType::None) {
4048 std::shared_ptr<detail::compressor> compressor; 4126 std::unique_ptr<detail::compressor> compressor;
4049 4127
4050 if (type == detail::EncodingType::Gzip) { 4128 if (type == detail::EncodingType::Gzip) {
4051#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4129#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4052 compressor = std::make_shared<detail::gzip_compressor>(); 4130 compressor = detail::make_unique<detail::gzip_compressor>();
4053 res.set_header("Content-Encoding", "gzip"); 4131 res.set_header("Content-Encoding", "gzip");
4054#endif 4132#endif
4055 } else if (type == detail::EncodingType::Brotli) { 4133 } else if (type == detail::EncodingType::Brotli) {
4056#ifdef CPPHTTPLIB_BROTLI_SUPPORT 4134#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4057 compressor = std::make_shared<detail::brotli_compressor>(); 4135 compressor = detail::make_unique<detail::brotli_compressor>();
4058 res.set_header("Content-Encoding", "brotli"); 4136 res.set_header("Content-Encoding", "brotli");
4059#endif 4137#endif
4060 } 4138 }
@@ -4136,17 +4214,17 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
4136 if (res.is_chunked_content_provider) { 4214 if (res.is_chunked_content_provider) {
4137 auto type = detail::encoding_type(req, res); 4215 auto type = detail::encoding_type(req, res);
4138 4216
4139 std::shared_ptr<detail::compressor> compressor; 4217 std::unique_ptr<detail::compressor> compressor;
4140 if (type == detail::EncodingType::Gzip) { 4218 if (type == detail::EncodingType::Gzip) {
4141#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4219#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4142 compressor = std::make_shared<detail::gzip_compressor>(); 4220 compressor = detail::make_unique<detail::gzip_compressor>();
4143#endif 4221#endif
4144 } else if (type == detail::EncodingType::Brotli) { 4222 } else if (type == detail::EncodingType::Brotli) {
4145#ifdef CPPHTTPLIB_BROTLI_SUPPORT 4223#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4146 compressor = std::make_shared<detail::brotli_compressor>(); 4224 compressor = detail::make_unique<detail::brotli_compressor>();
4147#endif 4225#endif
4148 } else { 4226 } else {
4149 compressor = std::make_shared<detail::nocompressor>(); 4227 compressor = detail::make_unique<detail::nocompressor>();
4150 } 4228 }
4151 assert(compressor != nullptr); 4229 assert(compressor != nullptr);
4152 4230
@@ -4198,8 +4276,9 @@ inline bool Server::read_content_with_content_receiver(
4198 Stream &strm, Request &req, Response &res, ContentReceiver receiver, 4276 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
4199 MultipartContentHeader multipart_header, 4277 MultipartContentHeader multipart_header,
4200 ContentReceiver multipart_receiver) { 4278 ContentReceiver multipart_receiver) {
4201 return read_content_core(strm, req, res, receiver, multipart_header, 4279 return read_content_core(strm, req, res, std::move(receiver),
4202 multipart_receiver); 4280 std::move(multipart_header),
4281 std::move(multipart_receiver));
4203} 4282}
4204 4283
4205inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, 4284inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
@@ -4207,7 +4286,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4207 MultipartContentHeader mulitpart_header, 4286 MultipartContentHeader mulitpart_header,
4208 ContentReceiver multipart_receiver) { 4287 ContentReceiver multipart_receiver) {
4209 detail::MultipartFormDataParser multipart_form_data_parser; 4288 detail::MultipartFormDataParser multipart_form_data_parser;
4210 ContentReceiver out; 4289 ContentReceiverWithProgress out;
4211 4290
4212 if (req.is_multipart_form_data()) { 4291 if (req.is_multipart_form_data()) {
4213 const auto &content_type = req.get_header_value("Content-Type"); 4292 const auto &content_type = req.get_header_value("Content-Type");
@@ -4218,7 +4297,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4218 } 4297 }
4219 4298
4220 multipart_form_data_parser.set_boundary(std::move(boundary)); 4299 multipart_form_data_parser.set_boundary(std::move(boundary));
4221 out = [&](const char *buf, size_t n) { 4300 out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
4222 /* For debug 4301 /* For debug
4223 size_t pos = 0; 4302 size_t pos = 0;
4224 while (pos < n) { 4303 while (pos < n) {
@@ -4234,7 +4313,8 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4234 mulitpart_header); 4313 mulitpart_header);
4235 }; 4314 };
4236 } else { 4315 } else {
4237 out = receiver; 4316 out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
4317 uint64_t /*len*/) { return receiver(buf, n); };
4238 } 4318 }
4239 4319
4240 if (req.method == "DELETE" && !req.has_header("Content-Length")) { 4320 if (req.method == "DELETE" && !req.has_header("Content-Length")) {
@@ -4271,7 +4351,7 @@ inline bool Server::handle_file_request(Request &req, Response &res,
4271 auto type = 4351 auto type =
4272 detail::find_content_type(path, file_extension_and_mimetype_map_); 4352 detail::find_content_type(path, file_extension_and_mimetype_map_);
4273 if (type) { res.set_header("Content-Type", type); } 4353 if (type) { res.set_header("Content-Type", type); }
4274 for (const auto& kv : entry.headers) { 4354 for (const auto &kv : entry.headers) {
4275 res.set_header(kv.first.c_str(), kv.second); 4355 res.set_header(kv.first.c_str(), kv.second);
4276 } 4356 }
4277 res.status = 200; 4357 res.status = 200;
@@ -4290,7 +4370,7 @@ inline socket_t
4290Server::create_server_socket(const char *host, int port, int socket_flags, 4370Server::create_server_socket(const char *host, int port, int socket_flags,
4291 SocketOptions socket_options) const { 4371 SocketOptions socket_options) const {
4292 return detail::create_socket( 4372 return detail::create_socket(
4293 host, port, socket_flags, tcp_nodelay_, socket_options, 4373 host, port, socket_flags, tcp_nodelay_, std::move(socket_options),
4294 [](socket_t sock, struct addrinfo &ai) -> bool { 4374 [](socket_t sock, struct addrinfo &ai) -> bool {
4295 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) { 4375 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
4296 return false; 4376 return false;
@@ -4392,32 +4472,37 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
4392 { 4472 {
4393 ContentReader reader( 4473 ContentReader reader(
4394 [&](ContentReceiver receiver) { 4474 [&](ContentReceiver receiver) {
4395 return read_content_with_content_receiver(strm, req, res, receiver, 4475 return read_content_with_content_receiver(
4396 nullptr, nullptr); 4476 strm, req, res, std::move(receiver), nullptr, nullptr);
4397 }, 4477 },
4398 [&](MultipartContentHeader header, ContentReceiver receiver) { 4478 [&](MultipartContentHeader header, ContentReceiver receiver) {
4399 return read_content_with_content_receiver(strm, req, res, nullptr, 4479 return read_content_with_content_receiver(strm, req, res, nullptr,
4400 header, receiver); 4480 std::move(header),
4481 std::move(receiver));
4401 }); 4482 });
4402 4483
4403 if (req.method == "POST") { 4484 if (req.method == "POST") {
4404 if (dispatch_request_for_content_reader( 4485 if (dispatch_request_for_content_reader(
4405 req, res, reader, post_handlers_for_content_reader_)) { 4486 req, res, std::move(reader),
4487 post_handlers_for_content_reader_)) {
4406 return true; 4488 return true;
4407 } 4489 }
4408 } else if (req.method == "PUT") { 4490 } else if (req.method == "PUT") {
4409 if (dispatch_request_for_content_reader( 4491 if (dispatch_request_for_content_reader(
4410 req, res, reader, put_handlers_for_content_reader_)) { 4492 req, res, std::move(reader),
4493 put_handlers_for_content_reader_)) {
4411 return true; 4494 return true;
4412 } 4495 }
4413 } else if (req.method == "PATCH") { 4496 } else if (req.method == "PATCH") {
4414 if (dispatch_request_for_content_reader( 4497 if (dispatch_request_for_content_reader(
4415 req, res, reader, patch_handlers_for_content_reader_)) { 4498 req, res, std::move(reader),
4499 patch_handlers_for_content_reader_)) {
4416 return true; 4500 return true;
4417 } 4501 }
4418 } else if (req.method == "DELETE") { 4502 } else if (req.method == "DELETE") {
4419 if (dispatch_request_for_content_reader( 4503 if (dispatch_request_for_content_reader(
4420 req, res, reader, delete_handlers_for_content_reader_)) { 4504 req, res, std::move(reader),
4505 delete_handlers_for_content_reader_)) {
4421 return true; 4506 return true;
4422 } 4507 }
4423 } 4508 }
@@ -4448,7 +4533,6 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
4448 4533
4449inline bool Server::dispatch_request(Request &req, Response &res, 4534inline bool Server::dispatch_request(Request &req, Response &res,
4450 const Handlers &handlers) { 4535 const Handlers &handlers) {
4451
4452 try { 4536 try {
4453 for (const auto &x : handlers) { 4537 for (const auto &x : handlers) {
4454 const auto &pattern = x.first; 4538 const auto &pattern = x.first;
@@ -4531,7 +4615,8 @@ Server::process_request(Stream &strm, bool close_connection,
4531 if (req.has_header("Range")) { 4615 if (req.has_header("Range")) {
4532 const auto &range_header_value = req.get_header_value("Range"); 4616 const auto &range_header_value = req.get_header_value("Range");
4533 if (!detail::parse_range_header(range_header_value, req.ranges)) { 4617 if (!detail::parse_range_header(range_header_value, req.ranges)) {
4534 // TODO: error 4618 res.status = 416;
4619 return write_response(strm, close_connection, req, res);
4535 } 4620 }
4536 } 4621 }
4537 4622
@@ -4588,11 +4673,11 @@ inline ClientImpl::ClientImpl(const std::string &host, int port)
4588inline ClientImpl::ClientImpl(const std::string &host, int port, 4673inline ClientImpl::ClientImpl(const std::string &host, int port,
4589 const std::string &client_cert_path, 4674 const std::string &client_cert_path,
4590 const std::string &client_key_path) 4675 const std::string &client_key_path)
4591 : host_(host), port_(port), 4676 : error_(Error::Success), host_(host), port_(port),
4592 host_and_port_(host_ + ":" + std::to_string(port_)), 4677 host_and_port_(host_ + ":" + std::to_string(port_)),
4593 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} 4678 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
4594 4679
4595inline ClientImpl::~ClientImpl() { stop_core(); } 4680inline ClientImpl::~ClientImpl() { lock_socket_and_shutdown_and_close(); }
4596 4681
4597inline bool ClientImpl::is_valid() const { return true; } 4682inline bool ClientImpl::is_valid() const { return true; }
4598 4683
@@ -4653,15 +4738,47 @@ inline bool ClientImpl::create_and_connect_socket(Socket &socket) {
4653 return true; 4738 return true;
4654} 4739}
4655 4740
4656inline void ClientImpl::close_socket(Socket &socket, 4741inline void ClientImpl::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
4657 bool /*process_socket_ret*/) { 4742 (void)socket;
4658 detail::close_socket(socket.sock); 4743 (void)shutdown_gracefully;
4659 socket_.sock = INVALID_SOCKET; 4744 //If there are any requests in flight from threads other than us, then it's
4745 //a thread-unsafe race because individual ssl* objects are not thread-safe.
4746 assert(socket_requests_in_flight_ == 0 ||
4747 socket_requests_are_from_thread_ == std::this_thread::get_id());
4748}
4749
4750inline void ClientImpl::shutdown_socket(Socket &socket) {
4751 if (socket.sock == INVALID_SOCKET)
4752 return;
4753 detail::shutdown_socket(socket.sock);
4754}
4755
4756inline void ClientImpl::close_socket(Socket &socket) {
4757 // If there are requests in flight in another thread, usually closing
4758 // the socket will be fine and they will simply receive an error when
4759 // using the closed socket, but it is still a bug since rarely the OS
4760 // may reassign the socket id to be used for a new socket, and then
4761 // suddenly they will be operating on a live socket that is different
4762 // than the one they intended!
4763 assert(socket_requests_in_flight_ == 0 ||
4764 socket_requests_are_from_thread_ == std::this_thread::get_id());
4765 // It is also a bug if this happens while SSL is still active
4660#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 4766#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4661 socket_.ssl = nullptr; 4767 assert(socket.ssl == nullptr);
4662#endif 4768#endif
4769 if (socket.sock == INVALID_SOCKET)
4770 return;
4771 detail::close_socket(socket.sock);
4772 socket.sock = INVALID_SOCKET;
4663} 4773}
4664 4774
4775inline void ClientImpl::lock_socket_and_shutdown_and_close() {
4776 std::lock_guard<std::mutex> guard(socket_mutex_);
4777 shutdown_ssl(socket_, true);
4778 shutdown_socket(socket_);
4779 close_socket(socket_);
4780}
4781
4665inline bool ClientImpl::read_response_line(Stream &strm, Response &res) { 4782inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
4666 std::array<char, 2048> buf; 4783 std::array<char, 2048> buf;
4667 4784
@@ -4696,11 +4813,23 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
4696 4813
4697 { 4814 {
4698 std::lock_guard<std::mutex> guard(socket_mutex_); 4815 std::lock_guard<std::mutex> guard(socket_mutex_);
4816 // Set this to false immediately - if it ever gets set to true by the end of the
4817 // request, we know another thread instructed us to close the socket.
4818 socket_should_be_closed_when_request_is_done_ = false;
4699 4819
4700 auto is_alive = false; 4820 auto is_alive = false;
4701 if (socket_.is_open()) { 4821 if (socket_.is_open()) {
4702 is_alive = detail::select_write(socket_.sock, 0, 0) > 0; 4822 is_alive = detail::select_write(socket_.sock, 0, 0) > 0;
4703 if (!is_alive) { close_socket(socket_, false); } 4823 if (!is_alive) {
4824 // Attempt to avoid sigpipe by shutting down nongracefully if it seems like
4825 // the other side has already closed the connection
4826 // Also, there cannot be any requests in flight from other threads since we locked
4827 // request_mutex_, so safe to close everything immediately
4828 const bool shutdown_gracefully = false;
4829 shutdown_ssl(socket_, shutdown_gracefully);
4830 shutdown_socket(socket_);
4831 close_socket(socket_);
4832 }
4704 } 4833 }
4705 4834
4706 if (!is_alive) { 4835 if (!is_alive) {
@@ -4721,15 +4850,38 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
4721 } 4850 }
4722#endif 4851#endif
4723 } 4852 }
4853
4854 // Mark the current socket as being in use so that it cannot be closed by anyone
4855 // else while this request is ongoing, even though we will be releasing the mutex.
4856 if (socket_requests_in_flight_ > 1) {
4857 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
4858 }
4859 socket_requests_in_flight_ += 1;
4860 socket_requests_are_from_thread_ = std::this_thread::get_id();
4724 } 4861 }
4725 4862
4726 auto close_connection = !keep_alive_; 4863 auto close_connection = !keep_alive_;
4727
4728 auto ret = process_socket(socket_, [&](Stream &strm) { 4864 auto ret = process_socket(socket_, [&](Stream &strm) {
4729 return handle_request(strm, req, res, close_connection); 4865 return handle_request(strm, req, res, close_connection);
4730 }); 4866 });
4731 4867
4732 if (close_connection || !ret) { stop_core(); } 4868 //Briefly lock mutex in order to mark that a request is no longer ongoing
4869 {
4870 std::lock_guard<std::mutex> guard(socket_mutex_);
4871 socket_requests_in_flight_ -= 1;
4872 if (socket_requests_in_flight_ <= 0) {
4873 assert(socket_requests_in_flight_ == 0);
4874 socket_requests_are_from_thread_ = std::thread::id();
4875 }
4876
4877 if (socket_should_be_closed_when_request_is_done_ ||
4878 close_connection ||
4879 !ret ) {
4880 shutdown_ssl(socket_, true);
4881 shutdown_socket(socket_);
4882 close_socket(socket_);
4883 }
4884 }
4733 4885
4734 if (!ret) { 4886 if (!ret) {
4735 if (error_ == Error::Success) { error_ = Error::Unknown; } 4887 if (error_ == Error::Success) { error_ = Error::Unknown; }
@@ -4973,7 +5125,7 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
4973 return true; 5125 return true;
4974} 5126}
4975 5127
4976inline std::shared_ptr<Response> ClientImpl::send_with_content_provider( 5128inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
4977 const char *method, const char *path, const Headers &headers, 5129 const char *method, const char *path, const Headers &headers,
4978 const std::string &body, size_t content_length, 5130 const std::string &body, size_t content_length,
4979 ContentProvider content_provider, const char *content_type) { 5131 ContentProvider content_provider, const char *content_type) {
@@ -5036,15 +5188,15 @@ inline std::shared_ptr<Response> ClientImpl::send_with_content_provider(
5036 { 5188 {
5037 if (content_provider) { 5189 if (content_provider) {
5038 req.content_length = content_length; 5190 req.content_length = content_length;
5039 req.content_provider = content_provider; 5191 req.content_provider = std::move(content_provider);
5040 } else { 5192 } else {
5041 req.body = body; 5193 req.body = body;
5042 } 5194 }
5043 } 5195 }
5044 5196
5045 auto res = std::make_shared<Response>(); 5197 auto res = detail::make_unique<Response>();
5046 5198
5047 return send(req, *res) ? res : nullptr; 5199 return send(req, *res) ? std::move(res) : nullptr;
5048} 5200}
5049 5201
5050inline bool ClientImpl::process_request(Stream &strm, const Request &req, 5202inline bool ClientImpl::process_request(Stream &strm, const Request &req,
@@ -5070,16 +5222,21 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5070 if (req.method != "HEAD" && req.method != "CONNECT") { 5222 if (req.method != "HEAD" && req.method != "CONNECT") {
5071 auto out = 5223 auto out =
5072 req.content_receiver 5224 req.content_receiver
5073 ? static_cast<ContentReceiver>([&](const char *buf, size_t n) { 5225 ? static_cast<ContentReceiverWithProgress>(
5074 auto ret = req.content_receiver(buf, n); 5226 [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
5075 if (!ret) { error_ = Error::Canceled; } 5227 auto ret = req.content_receiver(buf, n, off, len);
5076 return ret; 5228 if (!ret) { error_ = Error::Canceled; }
5077 }) 5229 return ret;
5078 : static_cast<ContentReceiver>([&](const char *buf, size_t n) { 5230 })
5079 if (res.body.size() + n > res.body.max_size()) { return false; } 5231 : static_cast<ContentReceiverWithProgress>(
5080 res.body.append(buf, n); 5232 [&](const char *buf, size_t n, uint64_t /*off*/,
5081 return true; 5233 uint64_t /*len*/) {
5082 }); 5234 if (res.body.size() + n > res.body.max_size()) {
5235 return false;
5236 }
5237 res.body.append(buf, n);
5238 return true;
5239 });
5083 5240
5084 auto progress = [&](uint64_t current, uint64_t total) { 5241 auto progress = [&](uint64_t current, uint64_t total) {
5085 if (!req.progress) { return true; } 5242 if (!req.progress) { return true; }
@@ -5090,7 +5247,8 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5090 5247
5091 int dummy_status; 5248 int dummy_status;
5092 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(), 5249 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
5093 dummy_status, progress, out, decompress_)) { 5250 dummy_status, std::move(progress), std::move(out),
5251 decompress_)) {
5094 if (error_ != Error::Canceled) { error_ = Error::Read; } 5252 if (error_ != Error::Canceled) { error_ = Error::Read; }
5095 return false; 5253 return false;
5096 } 5254 }
@@ -5098,7 +5256,16 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5098 5256
5099 if (res.get_header_value("Connection") == "close" || 5257 if (res.get_header_value("Connection") == "close" ||
5100 (res.version == "HTTP/1.0" && res.reason != "Connection established")) { 5258 (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
5101 stop_core(); 5259 // TODO this requires a not-entirely-obvious chain of calls to be correct
5260 // for this to be safe. Maybe a code refactor (such as moving this out to
5261 // the send function and getting rid of the recursiveness of the mutex)
5262 // could make this more obvious.
5263
5264 // This is safe to call because process_request is only called by handle_request
5265 // which is only called by send, which locks the request mutex during the process.
5266 // It would be a bug to call it from a different thread since it's a thread-safety
5267 // issue to do these things to the socket if another thread is using the socket.
5268 lock_socket_and_shutdown_and_close();
5102 } 5269 }
5103 5270
5104 // Log 5271 // Log
@@ -5108,11 +5275,11 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5108} 5275}
5109 5276
5110inline bool 5277inline bool
5111ClientImpl::process_socket(Socket &socket, 5278ClientImpl::process_socket(const Socket &socket,
5112 std::function<bool(Stream &strm)> callback) { 5279 std::function<bool(Stream &strm)> callback) {
5113 return detail::process_client_socket(socket.sock, read_timeout_sec_, 5280 return detail::process_client_socket(
5114 read_timeout_usec_, write_timeout_sec_, 5281 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
5115 write_timeout_usec_, callback); 5282 write_timeout_usec_, std::move(callback));
5116} 5283}
5117 5284
5118inline bool ClientImpl::is_ssl() const { return false; } 5285inline bool ClientImpl::is_ssl() const { return false; }
@@ -5138,9 +5305,9 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5138 req.headers.insert(headers.begin(), headers.end()); 5305 req.headers.insert(headers.begin(), headers.end());
5139 req.progress = std::move(progress); 5306 req.progress = std::move(progress);
5140 5307
5141 auto res = std::make_shared<Response>(); 5308 auto res = detail::make_unique<Response>();
5142 auto ret = send(req, *res); 5309 auto ret = send(req, *res);
5143 return Result{ret ? res : nullptr, get_last_error()}; 5310 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5144} 5311}
5145 5312
5146inline Result ClientImpl::Get(const char *path, 5313inline Result ClientImpl::Get(const char *path,
@@ -5170,23 +5337,23 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5170inline Result ClientImpl::Get(const char *path, 5337inline Result ClientImpl::Get(const char *path,
5171 ResponseHandler response_handler, 5338 ResponseHandler response_handler,
5172 ContentReceiver content_receiver) { 5339 ContentReceiver content_receiver) {
5173 return Get(path, Headers(), std::move(response_handler), content_receiver, 5340 return Get(path, Headers(), std::move(response_handler),
5174 nullptr); 5341 std::move(content_receiver), nullptr);
5175} 5342}
5176 5343
5177inline Result ClientImpl::Get(const char *path, const Headers &headers, 5344inline Result ClientImpl::Get(const char *path, const Headers &headers,
5178 ResponseHandler response_handler, 5345 ResponseHandler response_handler,
5179 ContentReceiver content_receiver) { 5346 ContentReceiver content_receiver) {
5180 return Get(path, headers, std::move(response_handler), content_receiver, 5347 return Get(path, headers, std::move(response_handler),
5181 nullptr); 5348 std::move(content_receiver), nullptr);
5182} 5349}
5183 5350
5184inline Result ClientImpl::Get(const char *path, 5351inline Result ClientImpl::Get(const char *path,
5185 ResponseHandler response_handler, 5352 ResponseHandler response_handler,
5186 ContentReceiver content_receiver, 5353 ContentReceiver content_receiver,
5187 Progress progress) { 5354 Progress progress) {
5188 return Get(path, Headers(), std::move(response_handler), content_receiver, 5355 return Get(path, Headers(), std::move(response_handler),
5189 progress); 5356 std::move(content_receiver), std::move(progress));
5190} 5357}
5191 5358
5192inline Result ClientImpl::Get(const char *path, const Headers &headers, 5359inline Result ClientImpl::Get(const char *path, const Headers &headers,
@@ -5199,12 +5366,16 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5199 req.headers = default_headers_; 5366 req.headers = default_headers_;
5200 req.headers.insert(headers.begin(), headers.end()); 5367 req.headers.insert(headers.begin(), headers.end());
5201 req.response_handler = std::move(response_handler); 5368 req.response_handler = std::move(response_handler);
5202 req.content_receiver = std::move(content_receiver); 5369 req.content_receiver =
5370 [content_receiver](const char *data, size_t data_length,
5371 uint64_t /*offset*/, uint64_t /*total_length*/) {
5372 return content_receiver(data, data_length);
5373 };
5203 req.progress = std::move(progress); 5374 req.progress = std::move(progress);
5204 5375
5205 auto res = std::make_shared<Response>(); 5376 auto res = detail::make_unique<Response>();
5206 auto ret = send(req, *res); 5377 auto ret = send(req, *res);
5207 return Result{ret ? res : nullptr, get_last_error()}; 5378 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5208} 5379}
5209 5380
5210inline Result ClientImpl::Head(const char *path) { 5381inline Result ClientImpl::Head(const char *path) {
@@ -5218,9 +5389,9 @@ inline Result ClientImpl::Head(const char *path, const Headers &headers) {
5218 req.headers.insert(headers.begin(), headers.end()); 5389 req.headers.insert(headers.begin(), headers.end());
5219 req.path = path; 5390 req.path = path;
5220 5391
5221 auto res = std::make_shared<Response>(); 5392 auto res = detail::make_unique<Response>();
5222 auto ret = send(req, *res); 5393 auto ret = send(req, *res);
5223 return Result{ret ? res : nullptr, get_last_error()}; 5394 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5224} 5395}
5225 5396
5226inline Result ClientImpl::Post(const char *path) { 5397inline Result ClientImpl::Post(const char *path) {
@@ -5237,7 +5408,7 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
5237 const char *content_type) { 5408 const char *content_type) {
5238 auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr, 5409 auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr,
5239 content_type); 5410 content_type);
5240 return Result{ret, get_last_error()}; 5411 return Result{std::move(ret), get_last_error()};
5241} 5412}
5242 5413
5243inline Result ClientImpl::Post(const char *path, const Params &params) { 5414inline Result ClientImpl::Post(const char *path, const Params &params) {
@@ -5247,17 +5418,18 @@ inline Result ClientImpl::Post(const char *path, const Params &params) {
5247inline Result ClientImpl::Post(const char *path, size_t content_length, 5418inline Result ClientImpl::Post(const char *path, size_t content_length,
5248 ContentProvider content_provider, 5419 ContentProvider content_provider,
5249 const char *content_type) { 5420 const char *content_type) {
5250 return Post(path, Headers(), content_length, content_provider, content_type); 5421 return Post(path, Headers(), content_length, std::move(content_provider),
5422 content_type);
5251} 5423}
5252 5424
5253inline Result ClientImpl::Post(const char *path, const Headers &headers, 5425inline Result ClientImpl::Post(const char *path, const Headers &headers,
5254 size_t content_length, 5426 size_t content_length,
5255 ContentProvider content_provider, 5427 ContentProvider content_provider,
5256 const char *content_type) { 5428 const char *content_type) {
5257 auto ret = send_with_content_provider("POST", path, headers, std::string(), 5429 auto ret = send_with_content_provider(
5258 content_length, content_provider, 5430 "POST", path, headers, std::string(), content_length,
5259 content_type); 5431 std::move(content_provider), content_type);
5260 return Result{ret, get_last_error()}; 5432 return Result{std::move(ret), get_last_error()};
5261} 5433}
5262 5434
5263inline Result ClientImpl::Post(const char *path, const Headers &headers, 5435inline Result ClientImpl::Post(const char *path, const Headers &headers,
@@ -5273,7 +5445,18 @@ inline Result ClientImpl::Post(const char *path,
5273 5445
5274inline Result ClientImpl::Post(const char *path, const Headers &headers, 5446inline Result ClientImpl::Post(const char *path, const Headers &headers,
5275 const MultipartFormDataItems &items) { 5447 const MultipartFormDataItems &items) {
5276 auto boundary = detail::make_multipart_data_boundary(); 5448 return Post(path, headers, items, detail::make_multipart_data_boundary());
5449}
5450inline Result ClientImpl::Post(const char *path, const Headers &headers,
5451 const MultipartFormDataItems &items,
5452 const std::string &boundary) {
5453 for (size_t i = 0; i < boundary.size(); i++) {
5454 char c = boundary[i];
5455 if (!std::isalnum(c) && c != '-' && c != '_') {
5456 error_ = Error::UnsupportedMultipartBoundaryChars;
5457 return Result{nullptr, error_};
5458 }
5459 }
5277 5460
5278 std::string body; 5461 std::string body;
5279 5462
@@ -5311,23 +5494,24 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
5311 const char *content_type) { 5494 const char *content_type) {
5312 auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr, 5495 auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr,
5313 content_type); 5496 content_type);
5314 return Result{ret, get_last_error()}; 5497 return Result{std::move(ret), get_last_error()};
5315} 5498}
5316 5499
5317inline Result ClientImpl::Put(const char *path, size_t content_length, 5500inline Result ClientImpl::Put(const char *path, size_t content_length,
5318 ContentProvider content_provider, 5501 ContentProvider content_provider,
5319 const char *content_type) { 5502 const char *content_type) {
5320 return Put(path, Headers(), content_length, content_provider, content_type); 5503 return Put(path, Headers(), content_length, std::move(content_provider),
5504 content_type);
5321} 5505}
5322 5506
5323inline Result ClientImpl::Put(const char *path, const Headers &headers, 5507inline Result ClientImpl::Put(const char *path, const Headers &headers,
5324 size_t content_length, 5508 size_t content_length,
5325 ContentProvider content_provider, 5509 ContentProvider content_provider,
5326 const char *content_type) { 5510 const char *content_type) {
5327 auto ret = send_with_content_provider("PUT", path, headers, std::string(), 5511 auto ret = send_with_content_provider(
5328 content_length, content_provider, 5512 "PUT", path, headers, std::string(), content_length,
5329 content_type); 5513 std::move(content_provider), content_type);
5330 return Result{ret, get_last_error()}; 5514 return Result{std::move(ret), get_last_error()};
5331} 5515}
5332 5516
5333inline Result ClientImpl::Put(const char *path, const Params &params) { 5517inline Result ClientImpl::Put(const char *path, const Params &params) {
@@ -5350,23 +5534,24 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5350 const char *content_type) { 5534 const char *content_type) {
5351 auto ret = send_with_content_provider("PATCH", path, headers, body, 0, 5535 auto ret = send_with_content_provider("PATCH", path, headers, body, 0,
5352 nullptr, content_type); 5536 nullptr, content_type);
5353 return Result{ret, get_last_error()}; 5537 return Result{std::move(ret), get_last_error()};
5354} 5538}
5355 5539
5356inline Result ClientImpl::Patch(const char *path, size_t content_length, 5540inline Result ClientImpl::Patch(const char *path, size_t content_length,
5357 ContentProvider content_provider, 5541 ContentProvider content_provider,
5358 const char *content_type) { 5542 const char *content_type) {
5359 return Patch(path, Headers(), content_length, content_provider, content_type); 5543 return Patch(path, Headers(), content_length, std::move(content_provider),
5544 content_type);
5360} 5545}
5361 5546
5362inline Result ClientImpl::Patch(const char *path, const Headers &headers, 5547inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5363 size_t content_length, 5548 size_t content_length,
5364 ContentProvider content_provider, 5549 ContentProvider content_provider,
5365 const char *content_type) { 5550 const char *content_type) {
5366 auto ret = send_with_content_provider("PATCH", path, headers, std::string(), 5551 auto ret = send_with_content_provider(
5367 content_length, content_provider, 5552 "PATCH", path, headers, std::string(), content_length,
5368 content_type); 5553 std::move(content_provider), content_type);
5369 return Result{ret, get_last_error()}; 5554 return Result{std::move(ret), get_last_error()};
5370} 5555}
5371 5556
5372inline Result ClientImpl::Delete(const char *path) { 5557inline Result ClientImpl::Delete(const char *path) {
@@ -5394,9 +5579,9 @@ inline Result ClientImpl::Delete(const char *path, const Headers &headers,
5394 if (content_type) { req.headers.emplace("Content-Type", content_type); } 5579 if (content_type) { req.headers.emplace("Content-Type", content_type); }
5395 req.body = body; 5580 req.body = body;
5396 5581
5397 auto res = std::make_shared<Response>(); 5582 auto res = detail::make_unique<Response>();
5398 auto ret = send(req, *res); 5583 auto ret = send(req, *res);
5399 return Result{ret ? res : nullptr, get_last_error()}; 5584 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5400} 5585}
5401 5586
5402inline Result ClientImpl::Options(const char *path) { 5587inline Result ClientImpl::Options(const char *path) {
@@ -5410,9 +5595,9 @@ inline Result ClientImpl::Options(const char *path, const Headers &headers) {
5410 req.headers.insert(headers.begin(), headers.end()); 5595 req.headers.insert(headers.begin(), headers.end());
5411 req.path = path; 5596 req.path = path;
5412 5597
5413 auto res = std::make_shared<Response>(); 5598 auto res = detail::make_unique<Response>();
5414 auto ret = send(req, *res); 5599 auto ret = send(req, *res);
5415 return Result{ret ? res : nullptr, get_last_error()}; 5600 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5416} 5601}
5417 5602
5418inline size_t ClientImpl::is_socket_open() const { 5603inline size_t ClientImpl::is_socket_open() const {
@@ -5421,18 +5606,27 @@ inline size_t ClientImpl::is_socket_open() const {
5421} 5606}
5422 5607
5423inline void ClientImpl::stop() { 5608inline void ClientImpl::stop() {
5424 stop_core();
5425 error_ = Error::Canceled;
5426}
5427
5428inline void ClientImpl::stop_core() {
5429 std::lock_guard<std::mutex> guard(socket_mutex_); 5609 std::lock_guard<std::mutex> guard(socket_mutex_);
5430 if (socket_.is_open()) { 5610 // There is no guarantee that this doesn't get overwritten later, but set it so that
5431 detail::shutdown_socket(socket_.sock); 5611 // there is a good chance that any threads stopping as a result pick up this error.
5432 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 5612 error_ = Error::Canceled;
5433 close_socket(socket_, true); 5613
5434 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 5614 // If there is anything ongoing right now, the ONLY thread-safe thing we can do
5615 // is to shutdown_socket, so that threads using this socket suddenly discover
5616 // they can't read/write any more and error out.
5617 // Everything else (closing the socket, shutting ssl down) is unsafe because these
5618 // actions are not thread-safe.
5619 if (socket_requests_in_flight_ > 0) {
5620 shutdown_socket(socket_);
5621 // Aside from that, we set a flag for the socket to be closed when we're done.
5622 socket_should_be_closed_when_request_is_done_ = true;
5623 return;
5435 } 5624 }
5625
5626 //Otherwise, sitll holding the mutex, we can shut everything down ourselves
5627 shutdown_ssl(socket_, true);
5628 shutdown_socket(socket_);
5629 close_socket(socket_);
5436} 5630}
5437 5631
5438inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { 5632inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
@@ -5479,7 +5673,7 @@ inline void ClientImpl::set_default_headers(Headers headers) {
5479inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } 5673inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
5480 5674
5481inline void ClientImpl::set_socket_options(SocketOptions socket_options) { 5675inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
5482 socket_options_ = socket_options; 5676 socket_options_ = std::move(socket_options);
5483} 5677}
5484 5678
5485inline void ClientImpl::set_compress(bool on) { compress_ = on; } 5679inline void ClientImpl::set_compress(bool on) { compress_ = on; }
@@ -5554,9 +5748,12 @@ inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
5554} 5748}
5555 5749
5556inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, 5750inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
5557 bool process_socket_ret) { 5751 bool shutdown_gracefully) {
5558 if (process_socket_ret) { 5752 // sometimes we may want to skip this to try to avoid SIGPIPE if we know
5559 SSL_shutdown(ssl); // shutdown only if not already closed by remote 5753 // the remote has closed the network connection
5754 // Note that it is not always possible to avoid SIGPIPE, this is merely a best-efforts.
5755 if (shutdown_gracefully) {
5756 SSL_shutdown(ssl);
5560 } 5757 }
5561 5758
5562 std::lock_guard<std::mutex> guard(ctx_mutex); 5759 std::lock_guard<std::mutex> guard(ctx_mutex);
@@ -5650,22 +5847,7 @@ inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
5650 read_timeout_usec_(read_timeout_usec), 5847 read_timeout_usec_(read_timeout_usec),
5651 write_timeout_sec_(write_timeout_sec), 5848 write_timeout_sec_(write_timeout_sec),
5652 write_timeout_usec_(write_timeout_usec) { 5849 write_timeout_usec_(write_timeout_usec) {
5653 { 5850 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
5654 timeval tv;
5655 tv.tv_sec = static_cast<long>(read_timeout_sec);
5656 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
5657
5658 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&tv),
5659 sizeof(tv));
5660 }
5661 {
5662 timeval tv;
5663 tv.tv_sec = static_cast<long>(write_timeout_sec);
5664 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
5665
5666 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&tv),
5667 sizeof(tv));
5668 }
5669} 5851}
5670 5852
5671inline SSLSocketStream::~SSLSocketStream() {} 5853inline SSLSocketStream::~SSLSocketStream() {}
@@ -5680,8 +5862,27 @@ inline bool SSLSocketStream::is_writable() const {
5680} 5862}
5681 5863
5682inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { 5864inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
5683 if (SSL_pending(ssl_) > 0 || is_readable()) { 5865 if (SSL_pending(ssl_) > 0) {
5684 return SSL_read(ssl_, ptr, static_cast<int>(size)); 5866 return SSL_read(ssl_, ptr, static_cast<int>(size));
5867 } else if (is_readable()) {
5868 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5869 if (ret < 0) {
5870 auto err = SSL_get_error(ssl_, ret);
5871 while (err == SSL_ERROR_WANT_READ) {
5872 if (SSL_pending(ssl_) > 0) {
5873 return SSL_read(ssl_, ptr, static_cast<int>(size));
5874 } else if (is_readable()) {
5875 ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5876 if (ret >= 0) {
5877 return ret;
5878 }
5879 err = SSL_get_error(ssl_, ret);
5880 } else {
5881 return -1;
5882 }
5883 }
5884 }
5885 return ret;
5685 } 5886 }
5686 return -1; 5887 return -1;
5687} 5888}
@@ -5788,9 +5989,12 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
5788 }); 5989 });
5789 5990
5790 detail::ssl_delete(ctx_mutex_, ssl, ret); 5991 detail::ssl_delete(ctx_mutex_, ssl, ret);
5992 detail::shutdown_socket(sock);
5993 detail::close_socket(sock);
5791 return ret; 5994 return ret;
5792 } 5995 }
5793 5996
5997 detail::shutdown_socket(sock);
5794 detail::close_socket(sock); 5998 detail::close_socket(sock);
5795 return false; 5999 return false;
5796} 6000}
@@ -5843,6 +6047,10 @@ inline SSLClient::SSLClient(const std::string &host, int port,
5843 6047
5844inline SSLClient::~SSLClient() { 6048inline SSLClient::~SSLClient() {
5845 if (ctx_) { SSL_CTX_free(ctx_); } 6049 if (ctx_) { SSL_CTX_free(ctx_); }
6050 // Make sure to shut down SSL since shutdown_ssl will resolve to the
6051 // base function rather than the derived function once we get to the
6052 // base class destructor, and won't free the SSL (causing a leak).
6053 SSLClient::shutdown_ssl(socket_, true);
5846} 6054}
5847 6055
5848inline bool SSLClient::is_valid() const { return ctx_; } 6056inline bool SSLClient::is_valid() const { return ctx_; }
@@ -5876,11 +6084,11 @@ inline bool SSLClient::create_and_connect_socket(Socket &socket) {
5876 return is_valid() && ClientImpl::create_and_connect_socket(socket); 6084 return is_valid() && ClientImpl::create_and_connect_socket(socket);
5877} 6085}
5878 6086
6087// Assumes that socket_mutex_ is locked and that there are no requests in flight
5879inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, 6088inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5880 bool &success) { 6089 bool &success) {
5881 success = true; 6090 success = true;
5882 Response res2; 6091 Response res2;
5883
5884 if (!detail::process_client_socket( 6092 if (!detail::process_client_socket(
5885 socket.sock, read_timeout_sec_, read_timeout_usec_, 6093 socket.sock, read_timeout_sec_, read_timeout_usec_,
5886 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { 6094 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
@@ -5889,7 +6097,10 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5889 req2.path = host_and_port_; 6097 req2.path = host_and_port_;
5890 return process_request(strm, req2, res2, false); 6098 return process_request(strm, req2, res2, false);
5891 })) { 6099 })) {
5892 close_socket(socket, true); 6100 // Thread-safe to close everything because we are assuming there are no requests in flight
6101 shutdown_ssl(socket, true);
6102 shutdown_socket(socket);
6103 close_socket(socket);
5893 success = false; 6104 success = false;
5894 return false; 6105 return false;
5895 } 6106 }
@@ -5912,7 +6123,10 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5912 true)); 6123 true));
5913 return process_request(strm, req3, res3, false); 6124 return process_request(strm, req3, res3, false);
5914 })) { 6125 })) {
5915 close_socket(socket, true); 6126 // Thread-safe to close everything because we are assuming there are no requests in flight
6127 shutdown_ssl(socket, true);
6128 shutdown_socket(socket);
6129 close_socket(socket);
5916 success = false; 6130 success = false;
5917 return false; 6131 return false;
5918 } 6132 }
@@ -6005,26 +6219,30 @@ inline bool SSLClient::initialize_ssl(Socket &socket) {
6005 return true; 6219 return true;
6006 } 6220 }
6007 6221
6008 close_socket(socket, false); 6222 shutdown_socket(socket);
6223 close_socket(socket);
6009 return false; 6224 return false;
6010} 6225}
6011 6226
6012inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) { 6227inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
6013 detail::close_socket(socket.sock); 6228 if (socket.sock == INVALID_SOCKET) {
6014 socket_.sock = INVALID_SOCKET; 6229 assert(socket.ssl == nullptr);
6230 return;
6231 }
6015 if (socket.ssl) { 6232 if (socket.ssl) {
6016 detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret); 6233 detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
6017 socket_.ssl = nullptr; 6234 socket.ssl = nullptr;
6018 } 6235 }
6236 assert(socket.ssl == nullptr);
6019} 6237}
6020 6238
6021inline bool 6239inline bool
6022SSLClient::process_socket(Socket &socket, 6240SSLClient::process_socket(const Socket &socket,
6023 std::function<bool(Stream &strm)> callback) { 6241 std::function<bool(Stream &strm)> callback) {
6024 assert(socket.ssl); 6242 assert(socket.ssl);
6025 return detail::process_client_socket_ssl( 6243 return detail::process_client_socket_ssl(
6026 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, 6244 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
6027 write_timeout_sec_, write_timeout_usec_, callback); 6245 write_timeout_sec_, write_timeout_usec_, std::move(callback));
6028} 6246}
6029 6247
6030inline bool SSLClient::is_ssl() const { return true; } 6248inline bool SSLClient::is_ssl() const { return true; }
@@ -6175,6 +6393,8 @@ inline Client::Client(const char *scheme_host_port,
6175#else 6393#else
6176 if (!scheme.empty() && scheme != "http") { 6394 if (!scheme.empty() && scheme != "http") {
6177#endif 6395#endif
6396 std::string msg = "'" + scheme + "' scheme is not supported.";
6397 throw std::invalid_argument(msg);
6178 return; 6398 return;
6179 } 6399 }
6180 6400
@@ -6187,28 +6407,28 @@ inline Client::Client(const char *scheme_host_port,
6187 6407
6188 if (is_ssl) { 6408 if (is_ssl) {
6189#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 6409#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6190 cli_ = std::make_shared<SSLClient>(host.c_str(), port, client_cert_path, 6410 cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
6191 client_key_path); 6411 client_cert_path, client_key_path);
6192 is_ssl_ = is_ssl; 6412 is_ssl_ = is_ssl;
6193#endif 6413#endif
6194 } else { 6414 } else {
6195 cli_ = std::make_shared<ClientImpl>(host.c_str(), port, client_cert_path, 6415 cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
6196 client_key_path); 6416 client_cert_path, client_key_path);
6197 } 6417 }
6198 } else { 6418 } else {
6199 cli_ = std::make_shared<ClientImpl>(scheme_host_port, 80, client_cert_path, 6419 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
6200 client_key_path); 6420 client_cert_path, client_key_path);
6201 } 6421 }
6202} 6422}
6203 6423
6204inline Client::Client(const std::string &host, int port) 6424inline Client::Client(const std::string &host, int port)
6205 : cli_(std::make_shared<ClientImpl>(host, port)) {} 6425 : cli_(detail::make_unique<ClientImpl>(host, port)) {}
6206 6426
6207inline Client::Client(const std::string &host, int port, 6427inline Client::Client(const std::string &host, int port,
6208 const std::string &client_cert_path, 6428 const std::string &client_cert_path,
6209 const std::string &client_key_path) 6429 const std::string &client_key_path)
6210 : cli_(std::make_shared<ClientImpl>(host, port, client_cert_path, 6430 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
6211 client_key_path)) {} 6431 client_key_path)) {}
6212 6432
6213inline Client::~Client() {} 6433inline Client::~Client() {}
6214 6434
@@ -6221,11 +6441,11 @@ inline Result Client::Get(const char *path, const Headers &headers) {
6221 return cli_->Get(path, headers); 6441 return cli_->Get(path, headers);
6222} 6442}
6223inline Result Client::Get(const char *path, Progress progress) { 6443inline Result Client::Get(const char *path, Progress progress) {
6224 return cli_->Get(path, progress); 6444 return cli_->Get(path, std::move(progress));
6225} 6445}
6226inline Result Client::Get(const char *path, const Headers &headers, 6446inline Result Client::Get(const char *path, const Headers &headers,
6227 Progress progress) { 6447 Progress progress) {
6228 return cli_->Get(path, headers, progress); 6448 return cli_->Get(path, headers, std::move(progress));
6229} 6449}
6230inline Result Client::Get(const char *path, ContentReceiver content_receiver) { 6450inline Result Client::Get(const char *path, ContentReceiver content_receiver) {
6231 return cli_->Get(path, std::move(content_receiver)); 6451 return cli_->Get(path, std::move(content_receiver));
@@ -6262,7 +6482,8 @@ inline Result Client::Get(const char *path, ResponseHandler response_handler,
6262inline Result Client::Get(const char *path, const Headers &headers, 6482inline Result Client::Get(const char *path, const Headers &headers,
6263 ResponseHandler response_handler, 6483 ResponseHandler response_handler,
6264 ContentReceiver content_receiver, Progress progress) { 6484 ContentReceiver content_receiver, Progress progress) {
6265 return cli_->Get(path, headers, response_handler, content_receiver, progress); 6485 return cli_->Get(path, headers, std::move(response_handler),
6486 std::move(content_receiver), std::move(progress));
6266} 6487}
6267 6488
6268inline Result Client::Head(const char *path) { return cli_->Head(path); } 6489inline Result Client::Head(const char *path) { return cli_->Head(path); }
@@ -6282,13 +6503,14 @@ inline Result Client::Post(const char *path, const Headers &headers,
6282inline Result Client::Post(const char *path, size_t content_length, 6503inline Result Client::Post(const char *path, size_t content_length,
6283 ContentProvider content_provider, 6504 ContentProvider content_provider,
6284 const char *content_type) { 6505 const char *content_type) {
6285 return cli_->Post(path, content_length, content_provider, content_type); 6506 return cli_->Post(path, content_length, std::move(content_provider),
6507 content_type);
6286} 6508}
6287inline Result Client::Post(const char *path, const Headers &headers, 6509inline Result Client::Post(const char *path, const Headers &headers,
6288 size_t content_length, 6510 size_t content_length,
6289 ContentProvider content_provider, 6511 ContentProvider content_provider,
6290 const char *content_type) { 6512 const char *content_type) {
6291 return cli_->Post(path, headers, content_length, content_provider, 6513 return cli_->Post(path, headers, content_length, std::move(content_provider),
6292 content_type); 6514 content_type);
6293} 6515}
6294inline Result Client::Post(const char *path, const Params &params) { 6516inline Result Client::Post(const char *path, const Params &params) {
@@ -6306,6 +6528,11 @@ inline Result Client::Post(const char *path, const Headers &headers,
6306 const MultipartFormDataItems &items) { 6528 const MultipartFormDataItems &items) {
6307 return cli_->Post(path, headers, items); 6529 return cli_->Post(path, headers, items);
6308} 6530}
6531inline Result Client::Post(const char *path, const Headers &headers,
6532 const MultipartFormDataItems &items,
6533 const std::string &boundary) {
6534 return cli_->Post(path, headers, items, boundary);
6535}
6309inline Result Client::Put(const char *path) { return cli_->Put(path); } 6536inline Result Client::Put(const char *path) { return cli_->Put(path); }
6310inline Result Client::Put(const char *path, const std::string &body, 6537inline Result Client::Put(const char *path, const std::string &body,
6311 const char *content_type) { 6538 const char *content_type) {
@@ -6318,13 +6545,14 @@ inline Result Client::Put(const char *path, const Headers &headers,
6318inline Result Client::Put(const char *path, size_t content_length, 6545inline Result Client::Put(const char *path, size_t content_length,
6319 ContentProvider content_provider, 6546 ContentProvider content_provider,
6320 const char *content_type) { 6547 const char *content_type) {
6321 return cli_->Put(path, content_length, content_provider, content_type); 6548 return cli_->Put(path, content_length, std::move(content_provider),
6549 content_type);
6322} 6550}
6323inline Result Client::Put(const char *path, const Headers &headers, 6551inline Result Client::Put(const char *path, const Headers &headers,
6324 size_t content_length, 6552 size_t content_length,
6325 ContentProvider content_provider, 6553 ContentProvider content_provider,
6326 const char *content_type) { 6554 const char *content_type) {
6327 return cli_->Put(path, headers, content_length, content_provider, 6555 return cli_->Put(path, headers, content_length, std::move(content_provider),
6328 content_type); 6556 content_type);
6329} 6557}
6330inline Result Client::Put(const char *path, const Params &params) { 6558inline Result Client::Put(const char *path, const Params &params) {
@@ -6345,13 +6573,14 @@ inline Result Client::Patch(const char *path, const Headers &headers,
6345inline Result Client::Patch(const char *path, size_t content_length, 6573inline Result Client::Patch(const char *path, size_t content_length,
6346 ContentProvider content_provider, 6574 ContentProvider content_provider,
6347 const char *content_type) { 6575 const char *content_type) {
6348 return cli_->Patch(path, content_length, content_provider, content_type); 6576 return cli_->Patch(path, content_length, std::move(content_provider),
6577 content_type);
6349} 6578}
6350inline Result Client::Patch(const char *path, const Headers &headers, 6579inline Result Client::Patch(const char *path, const Headers &headers,
6351 size_t content_length, 6580 size_t content_length,
6352 ContentProvider content_provider, 6581 ContentProvider content_provider,
6353 const char *content_type) { 6582 const char *content_type) {
6354 return cli_->Patch(path, headers, content_length, content_provider, 6583 return cli_->Patch(path, headers, content_length, std::move(content_provider),
6355 content_type); 6584 content_type);
6356} 6585}
6357inline Result Client::Delete(const char *path) { return cli_->Delete(path); } 6586inline Result Client::Delete(const char *path) { return cli_->Delete(path); }
@@ -6386,7 +6615,7 @@ inline void Client::set_default_headers(Headers headers) {
6386 6615
6387inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } 6616inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
6388inline void Client::set_socket_options(SocketOptions socket_options) { 6617inline void Client::set_socket_options(SocketOptions socket_options) {
6389 cli_->set_socket_options(socket_options); 6618 cli_->set_socket_options(std::move(socket_options));
6390} 6619}
6391 6620
6392inline void Client::set_connection_timeout(time_t sec, time_t usec) { 6621inline void Client::set_connection_timeout(time_t sec, time_t usec) {