LCOV - code coverage report
Current view: top level - app/libs/cpp-httplib - httplib.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 1731 0.0 %
Date: 2023-08-17 16:45:52 Functions: 0 170 0.0 %

          Line data    Source code
       1             : //
       2             : //  httplib.h
       3             : //
       4             : //  Copyright (c) 2023 Yuji Hirose. All rights reserved.
       5             : //  MIT License
       6             : //
       7             : 
       8             : #ifndef CPPHTTPLIB_HTTPLIB_H
       9             : #define CPPHTTPLIB_HTTPLIB_H
      10             : 
      11             : #define CPPHTTPLIB_VERSION "0.12.2"
      12             : 
      13             : /*
      14             :  * Configuration
      15             :  */
      16             : 
      17             : #ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
      18             : #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
      19             : #endif
      20             : 
      21             : #ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
      22             : #define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
      23             : #endif
      24             : 
      25             : #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
      26             : #define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
      27             : #endif
      28             : 
      29             : #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
      30             : #define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
      31             : #endif
      32             : 
      33             : #ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
      34             : #define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
      35             : #endif
      36             : 
      37             : #ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
      38             : #define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
      39             : #endif
      40             : 
      41             : #ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND
      42             : #define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5
      43             : #endif
      44             : 
      45             : #ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND
      46             : #define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0
      47             : #endif
      48             : 
      49             : #ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
      50             : #define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
      51             : #endif
      52             : 
      53             : #ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
      54             : #ifdef _WIN32
      55             : #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
      56             : #else
      57             : #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
      58             : #endif
      59             : #endif
      60             : 
      61             : #ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
      62             : #define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
      63             : #endif
      64             : 
      65             : #ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
      66             : #define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
      67             : #endif
      68             : 
      69             : #ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
      70             : #define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
      71             : #endif
      72             : 
      73             : #ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
      74             : #define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
      75             : #endif
      76             : 
      77             : #ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
      78             : #define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
      79             : #endif
      80             : 
      81             : #ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
      82             : #define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
      83             : #endif
      84             : 
      85             : #ifndef CPPHTTPLIB_TCP_NODELAY
      86             : #define CPPHTTPLIB_TCP_NODELAY false
      87             : #endif
      88             : 
      89             : #ifndef CPPHTTPLIB_RECV_BUFSIZ
      90             : #define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
      91             : #endif
      92             : 
      93             : #ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
      94             : #define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
      95             : #endif
      96             : 
      97             : #ifndef CPPHTTPLIB_THREAD_POOL_COUNT
      98             : #define CPPHTTPLIB_THREAD_POOL_COUNT                                           \
      99             :   ((std::max)(8u, std::thread::hardware_concurrency() > 0                      \
     100             :                       ? std::thread::hardware_concurrency() - 1                \
     101             :                       : 0))
     102             : #endif
     103             : 
     104             : #ifndef CPPHTTPLIB_RECV_FLAGS
     105             : #define CPPHTTPLIB_RECV_FLAGS 0
     106             : #endif
     107             : 
     108             : #ifndef CPPHTTPLIB_SEND_FLAGS
     109             : #define CPPHTTPLIB_SEND_FLAGS 0
     110             : #endif
     111             : 
     112             : #ifndef CPPHTTPLIB_LISTEN_BACKLOG
     113             : #define CPPHTTPLIB_LISTEN_BACKLOG 5
     114             : #endif
     115             : 
     116             : /*
     117             :  * Headers
     118             :  */
     119             : 
     120             : #ifdef _WIN32
     121             : #ifndef _CRT_SECURE_NO_WARNINGS
     122             : #define _CRT_SECURE_NO_WARNINGS
     123             : #endif //_CRT_SECURE_NO_WARNINGS
     124             : 
     125             : #ifndef _CRT_NONSTDC_NO_DEPRECATE
     126             : #define _CRT_NONSTDC_NO_DEPRECATE
     127             : #endif //_CRT_NONSTDC_NO_DEPRECATE
     128             : 
     129             : #if defined(_MSC_VER)
     130             : #if _MSC_VER < 1900
     131             : #error Sorry, Visual Studio versions prior to 2015 are not supported
     132             : #endif
     133             : 
     134             : #pragma comment(lib, "ws2_32.lib")
     135             : 
     136             : #ifdef _WIN64
     137             : using ssize_t = __int64;
     138             : #else
     139             : using ssize_t = long;
     140             : #endif
     141             : #endif // _MSC_VER
     142             : 
     143             : #ifndef S_ISREG
     144             : #define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
     145             : #endif // S_ISREG
     146             : 
     147             : #ifndef S_ISDIR
     148             : #define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
     149             : #endif // S_ISDIR
     150             : 
     151             : #ifndef NOMINMAX
     152             : #define NOMINMAX
     153             : #endif // NOMINMAX
     154             : 
     155             : #include <io.h>
     156             : #include <winsock2.h>
     157             : #include <ws2tcpip.h>
     158             : 
     159             : #ifndef WSA_FLAG_NO_HANDLE_INHERIT
     160             : #define WSA_FLAG_NO_HANDLE_INHERIT 0x80
     161             : #endif
     162             : 
     163             : #ifndef strcasecmp
     164             : #define strcasecmp _stricmp
     165             : #endif // strcasecmp
     166             : 
     167             : using socket_t = SOCKET;
     168             : #ifdef CPPHTTPLIB_USE_POLL
     169             : #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
     170             : #endif
     171             : 
     172             : #else // not _WIN32
     173             : 
     174             : #include <arpa/inet.h>
     175             : #ifndef _AIX
     176             : #include <ifaddrs.h>
     177             : #endif
     178             : #include <net/if.h>
     179             : #include <netdb.h>
     180             : #include <netinet/in.h>
     181             : #ifdef __linux__
     182             : #include <resolv.h>
     183             : #endif
     184             : #include <netinet/tcp.h>
     185             : #ifdef CPPHTTPLIB_USE_POLL
     186             : #include <poll.h>
     187             : #endif
     188             : #include <csignal>
     189             : #include <pthread.h>
     190             : #include <sys/select.h>
     191             : #include <sys/socket.h>
     192             : #include <sys/un.h>
     193             : #include <unistd.h>
     194             : 
     195             : using socket_t = int;
     196             : #ifndef INVALID_SOCKET
     197             : #define INVALID_SOCKET (-1)
     198             : #endif
     199             : #endif //_WIN32
     200             : 
     201             : #include <algorithm>
     202             : #include <array>
     203             : #include <atomic>
     204             : #include <cassert>
     205             : #include <cctype>
     206             : #include <climits>
     207             : #include <condition_variable>
     208             : #include <cstring>
     209             : #include <errno.h>
     210             : #include <fcntl.h>
     211             : #include <fstream>
     212             : #include <functional>
     213             : #include <iomanip>
     214             : #include <iostream>
     215             : #include <list>
     216             : #include <map>
     217             : #include <memory>
     218             : #include <mutex>
     219             : #include <random>
     220             : #include <regex>
     221             : #include <set>
     222             : #include <sstream>
     223             : #include <string>
     224             : #include <sys/stat.h>
     225             : #include <thread>
     226             : 
     227             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
     228             : #ifdef _WIN32
     229             : #include <wincrypt.h>
     230             : 
     231             : // these are defined in wincrypt.h and it breaks compilation if BoringSSL is
     232             : // used
     233             : #undef X509_NAME
     234             : #undef X509_CERT_PAIR
     235             : #undef X509_EXTENSIONS
     236             : #undef PKCS7_SIGNER_INFO
     237             : 
     238             : #ifdef _MSC_VER
     239             : #pragma comment(lib, "crypt32.lib")
     240             : #pragma comment(lib, "cryptui.lib")
     241             : #endif
     242             : #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
     243             : #include <TargetConditionals.h>
     244             : #if TARGET_OS_OSX
     245             : #include <CoreFoundation/CoreFoundation.h>
     246             : #include <Security/Security.h>
     247             : #endif // TARGET_OS_OSX
     248             : #endif // _WIN32
     249             : 
     250             : #include <openssl/err.h>
     251             : #include <openssl/evp.h>
     252             : #include <openssl/ssl.h>
     253             : #include <openssl/x509v3.h>
     254             : 
     255             : #if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
     256             : #include <openssl/applink.c>
     257             : #endif
     258             : 
     259             : #include <iostream>
     260             : #include <sstream>
     261             : 
     262             : #if OPENSSL_VERSION_NUMBER < 0x1010100fL
     263             : #error Sorry, OpenSSL versions prior to 1.1.1 are not supported
     264             : #elif OPENSSL_VERSION_NUMBER < 0x30000000L
     265             : #define SSL_get1_peer_certificate SSL_get_peer_certificate
     266             : #endif
     267             : 
     268             : #endif
     269             : 
     270             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
     271             : #include <zlib.h>
     272             : #endif
     273             : 
     274             : #ifdef CPPHTTPLIB_BROTLI_SUPPORT
     275             : #include <brotli/decode.h>
     276             : #include <brotli/encode.h>
     277             : #endif
     278             : 
     279             : /*
     280             :  * Declaration
     281             :  */
     282             : namespace httplib {
     283             : 
     284             : namespace detail {
     285             : 
     286             : /*
     287             :  * Backport std::make_unique from C++14.
     288             :  *
     289             :  * NOTE: This code came up with the following stackoverflow post:
     290             :  * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
     291             :  *
     292             :  */
     293             : 
     294             : template <class T, class... Args>
     295             : typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
     296           0 : make_unique(Args &&...args) {
     297           0 :   return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
     298             : }
     299             : 
     300             : template <class T>
     301             : typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
     302             : make_unique(std::size_t n) {
     303             :   typedef typename std::remove_extent<T>::type RT;
     304             :   return std::unique_ptr<T>(new RT[n]);
     305             : }
     306             : 
     307             : struct ci {
     308             :   bool operator()(const std::string &s1, const std::string &s2) const {
     309             :     return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(),
     310             :                                         s2.end(),
     311             :                                         [](unsigned char c1, unsigned char c2) {
     312             :                                           return ::tolower(c1) < ::tolower(c2);
     313             :                                         });
     314             :   }
     315             : };
     316             : 
     317             : // This is based on
     318             : // "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
     319             : 
     320             : struct scope_exit {
     321           0 :   explicit scope_exit(std::function<void(void)> &&f)
     322           0 :       : exit_function(std::move(f)), execute_on_destruction{true} {}
     323             : 
     324             :   scope_exit(scope_exit &&rhs)
     325             :       : exit_function(std::move(rhs.exit_function)),
     326             :         execute_on_destruction{rhs.execute_on_destruction} {
     327             :     rhs.release();
     328             :   }
     329             : 
     330           0 :   ~scope_exit() {
     331           0 :     if (execute_on_destruction) { this->exit_function(); }
     332           0 :   }
     333             : 
     334             :   void release() { this->execute_on_destruction = false; }
     335             : 
     336             : private:
     337             :   scope_exit(const scope_exit &) = delete;
     338             :   void operator=(const scope_exit &) = delete;
     339             :   scope_exit &operator=(scope_exit &&) = delete;
     340             : 
     341             :   std::function<void(void)> exit_function;
     342             :   bool execute_on_destruction;
     343             : };
     344             : 
     345             : } // namespace detail
     346             : 
     347             : using Headers = std::multimap<std::string, std::string, detail::ci>;
     348             : 
     349             : using Params = std::multimap<std::string, std::string>;
     350             : using Match = std::smatch;
     351             : 
     352             : using Progress = std::function<bool(uint64_t current, uint64_t total)>;
     353             : 
     354             : struct Response;
     355             : using ResponseHandler = std::function<bool(const Response &response)>;
     356             : 
     357           0 : struct MultipartFormData {
     358             :   std::string name;
     359             :   std::string content;
     360             :   std::string filename;
     361             :   std::string content_type;
     362             : };
     363             : using MultipartFormDataItems = std::vector<MultipartFormData>;
     364             : using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
     365             : 
     366             : class DataSink {
     367             : public:
     368           0 :   DataSink() : os(&sb_), sb_(*this) {}
     369             : 
     370             :   DataSink(const DataSink &) = delete;
     371             :   DataSink &operator=(const DataSink &) = delete;
     372             :   DataSink(DataSink &&) = delete;
     373             :   DataSink &operator=(DataSink &&) = delete;
     374             : 
     375             :   std::function<bool(const char *data, size_t data_len)> write;
     376             :   std::function<void()> done;
     377             :   std::function<void(const Headers &trailer)> done_with_trailer;
     378             :   std::ostream os;
     379             : 
     380             : private:
     381             :   class data_sink_streambuf : public std::streambuf {
     382             :   public:
     383           0 :     explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
     384             : 
     385             :   protected:
     386           0 :     std::streamsize xsputn(const char *s, std::streamsize n) {
     387           0 :       sink_.write(s, static_cast<size_t>(n));
     388           0 :       return n;
     389             :     }
     390             : 
     391             :   private:
     392             :     DataSink &sink_;
     393             :   };
     394             : 
     395             :   data_sink_streambuf sb_;
     396             : };
     397             : 
     398             : using ContentProvider =
     399             :     std::function<bool(size_t offset, size_t length, DataSink &sink)>;
     400             : 
     401             : using ContentProviderWithoutLength =
     402             :     std::function<bool(size_t offset, DataSink &sink)>;
     403             : 
     404             : using ContentProviderResourceReleaser = std::function<void(bool success)>;
     405             : 
     406             : struct MultipartFormDataProvider {
     407             :   std::string name;
     408             :   ContentProviderWithoutLength provider;
     409             :   std::string filename;
     410             :   std::string content_type;
     411             : };
     412             : using MultipartFormDataProviderItems = std::vector<MultipartFormDataProvider>;
     413             : 
     414             : using ContentReceiverWithProgress =
     415             :     std::function<bool(const char *data, size_t data_length, uint64_t offset,
     416             :                        uint64_t total_length)>;
     417             : 
     418             : using ContentReceiver =
     419             :     std::function<bool(const char *data, size_t data_length)>;
     420             : 
     421             : using MultipartContentHeader =
     422             :     std::function<bool(const MultipartFormData &file)>;
     423             : 
     424             : class ContentReader {
     425             : public:
     426             :   using Reader = std::function<bool(ContentReceiver receiver)>;
     427             :   using MultipartReader = std::function<bool(MultipartContentHeader header,
     428             :                                              ContentReceiver receiver)>;
     429             : 
     430           0 :   ContentReader(Reader reader, MultipartReader multipart_reader)
     431           0 :       : reader_(std::move(reader)),
     432           0 :         multipart_reader_(std::move(multipart_reader)) {}
     433             : 
     434             :   bool operator()(MultipartContentHeader header,
     435             :                   ContentReceiver receiver) const {
     436             :     return multipart_reader_(std::move(header), std::move(receiver));
     437             :   }
     438             : 
     439             :   bool operator()(ContentReceiver receiver) const {
     440             :     return reader_(std::move(receiver));
     441             :   }
     442             : 
     443             :   Reader reader_;
     444             :   MultipartReader multipart_reader_;
     445             : };
     446             : 
     447             : using Range = std::pair<ssize_t, ssize_t>;
     448             : using Ranges = std::vector<Range>;
     449             : 
     450             : struct Request {
     451             :   std::string method;
     452             :   std::string path;
     453             :   Headers headers;
     454             :   std::string body;
     455             : 
     456             :   std::string remote_addr;
     457             :   int remote_port = -1;
     458             :   std::string local_addr;
     459             :   int local_port = -1;
     460             : 
     461             :   // for server
     462             :   std::string version;
     463             :   std::string target;
     464             :   Params params;
     465             :   MultipartFormDataMap files;
     466             :   Ranges ranges;
     467             :   Match matches;
     468             : 
     469             :   // for client
     470             :   ResponseHandler response_handler;
     471             :   ContentReceiverWithProgress content_receiver;
     472             :   Progress progress;
     473             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
     474             :   const SSL *ssl = nullptr;
     475             : #endif
     476             : 
     477             :   bool has_header(const std::string &key) const;
     478             :   std::string get_header_value(const std::string &key, size_t id = 0) const;
     479             :   template <typename T>
     480             :   T get_header_value(const std::string &key, size_t id = 0) const;
     481             :   size_t get_header_value_count(const std::string &key) const;
     482             :   void set_header(const std::string &key, const std::string &val);
     483             : 
     484             :   bool has_param(const std::string &key) const;
     485             :   std::string get_param_value(const std::string &key, size_t id = 0) const;
     486             :   size_t get_param_value_count(const std::string &key) const;
     487             : 
     488             :   bool is_multipart_form_data() const;
     489             : 
     490             :   bool has_file(const std::string &key) const;
     491             :   MultipartFormData get_file_value(const std::string &key) const;
     492             :   std::vector<MultipartFormData> get_file_values(const std::string &key) const;
     493             : 
     494             :   // private members...
     495             :   size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
     496             :   size_t content_length_ = 0;
     497             :   ContentProvider content_provider_;
     498             :   bool is_chunked_content_provider_ = false;
     499             :   size_t authorization_count_ = 0;
     500             : };
     501             : 
     502             : struct Response {
     503             :   std::string version;
     504             :   int status = -1;
     505             :   std::string reason;
     506             :   Headers headers;
     507             :   std::string body;
     508             :   std::string location; // Redirect location
     509             : 
     510             :   bool has_header(const std::string &key) const;
     511             :   std::string get_header_value(const std::string &key, size_t id = 0) const;
     512             :   template <typename T>
     513             :   T get_header_value(const std::string &key, size_t id = 0) const;
     514             :   size_t get_header_value_count(const std::string &key) const;
     515             :   void set_header(const std::string &key, const std::string &val);
     516             : 
     517             :   void set_redirect(const std::string &url, int status = 302);
     518             :   void set_content(const char *s, size_t n, const std::string &content_type);
     519             :   void set_content(const std::string &s, const std::string &content_type);
     520             : 
     521             :   void set_content_provider(
     522             :       size_t length, const std::string &content_type, ContentProvider provider,
     523             :       ContentProviderResourceReleaser resource_releaser = nullptr);
     524             : 
     525             :   void set_content_provider(
     526             :       const std::string &content_type, ContentProviderWithoutLength provider,
     527             :       ContentProviderResourceReleaser resource_releaser = nullptr);
     528             : 
     529             :   void set_chunked_content_provider(
     530             :       const std::string &content_type, ContentProviderWithoutLength provider,
     531             :       ContentProviderResourceReleaser resource_releaser = nullptr);
     532             : 
     533           0 :   Response() = default;
     534             :   Response(const Response &) = default;
     535             :   Response &operator=(const Response &) = default;
     536             :   Response(Response &&) = default;
     537             :   Response &operator=(Response &&) = default;
     538           0 :   ~Response() {
     539           0 :     if (content_provider_resource_releaser_) {
     540           0 :       content_provider_resource_releaser_(content_provider_success_);
     541             :     }
     542           0 :   }
     543             : 
     544             :   // private members...
     545             :   size_t content_length_ = 0;
     546             :   ContentProvider content_provider_;
     547             :   ContentProviderResourceReleaser content_provider_resource_releaser_;
     548             :   bool is_chunked_content_provider_ = false;
     549             :   bool content_provider_success_ = false;
     550             : };
     551             : 
     552           0 : class Stream {
     553             : public:
     554           0 :   virtual ~Stream() = default;
     555             : 
     556             :   virtual bool is_readable() const = 0;
     557             :   virtual bool is_writable() const = 0;
     558             : 
     559             :   virtual ssize_t read(char *ptr, size_t size) = 0;
     560             :   virtual ssize_t write(const char *ptr, size_t size) = 0;
     561             :   virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
     562             :   virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
     563             :   virtual socket_t socket() const = 0;
     564             : 
     565             :   template <typename... Args>
     566             :   ssize_t write_format(const char *fmt, const Args &...args);
     567             :   ssize_t write(const char *ptr);
     568             :   ssize_t write(const std::string &s);
     569             : };
     570             : 
     571             : class TaskQueue {
     572             : public:
     573           0 :   TaskQueue() = default;
     574           0 :   virtual ~TaskQueue() = default;
     575             : 
     576             :   virtual void enqueue(std::function<void()> fn) = 0;
     577             :   virtual void shutdown() = 0;
     578             : 
     579           0 :   virtual void on_idle() {}
     580             : };
     581             : 
     582             : class ThreadPool : public TaskQueue {
     583             : public:
     584           0 :   explicit ThreadPool(size_t n) : shutdown_(false) {
     585           0 :     while (n) {
     586           0 :       threads_.emplace_back(worker(*this));
     587           0 :       n--;
     588             :     }
     589           0 :   }
     590             : 
     591             :   ThreadPool(const ThreadPool &) = delete;
     592           0 :   ~ThreadPool() override = default;
     593             : 
     594           0 :   void enqueue(std::function<void()> fn) override {
     595           0 :     {
     596           0 :       std::unique_lock<std::mutex> lock(mutex_);
     597           0 :       jobs_.push_back(std::move(fn));
     598             :     }
     599             : 
     600           0 :     cond_.notify_one();
     601           0 :   }
     602             : 
     603           0 :   void shutdown() override {
     604             :     // Stop all worker threads...
     605           0 :     {
     606           0 :       std::unique_lock<std::mutex> lock(mutex_);
     607           0 :       shutdown_ = true;
     608             :     }
     609             : 
     610           0 :     cond_.notify_all();
     611             : 
     612             :     // Join...
     613           0 :     for (auto &t : threads_) {
     614           0 :       t.join();
     615             :     }
     616           0 :   }
     617             : 
     618             : private:
     619             :   struct worker {
     620           0 :     explicit worker(ThreadPool &pool) : pool_(pool) {}
     621             : 
     622           0 :     void operator()() {
     623           0 :       for (;;) {
     624           0 :         std::function<void()> fn;
     625           0 :         {
     626           0 :           std::unique_lock<std::mutex> lock(pool_.mutex_);
     627             : 
     628           0 :           pool_.cond_.wait(
     629           0 :               lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
     630             : 
     631           0 :           if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
     632             : 
     633           0 :           fn = std::move(pool_.jobs_.front());
     634           0 :           pool_.jobs_.pop_front();
     635             :         }
     636             : 
     637           0 :         assert(true == static_cast<bool>(fn));
     638           0 :         fn();
     639             :       }
     640           0 :     }
     641             : 
     642             :     ThreadPool &pool_;
     643             :   };
     644             :   friend struct worker;
     645             : 
     646             :   std::vector<std::thread> threads_;
     647             :   std::list<std::function<void()>> jobs_;
     648             : 
     649             :   bool shutdown_;
     650             : 
     651             :   std::condition_variable cond_;
     652             :   std::mutex mutex_;
     653             : };
     654             : 
     655             : using Logger = std::function<void(const Request &, const Response &)>;
     656             : 
     657             : using SocketOptions = std::function<void(socket_t sock)>;
     658             : 
     659             : void default_socket_options(socket_t sock);
     660             : 
     661             : class Server {
     662             : public:
     663             :   using Handler = std::function<void(const Request &, Response &)>;
     664             : 
     665             :   using ExceptionHandler =
     666             :       std::function<void(const Request &, Response &, std::exception_ptr ep)>;
     667             : 
     668             :   enum class HandlerResponse {
     669             :     Handled,
     670             :     Unhandled,
     671             :   };
     672             :   using HandlerWithResponse =
     673             :       std::function<HandlerResponse(const Request &, Response &)>;
     674             : 
     675             :   using HandlerWithContentReader = std::function<void(
     676             :       const Request &, Response &, const ContentReader &content_reader)>;
     677             : 
     678             :   using Expect100ContinueHandler =
     679             :       std::function<int(const Request &, Response &)>;
     680             : 
     681             :   Server();
     682             : 
     683             :   virtual ~Server();
     684             : 
     685             :   virtual bool is_valid() const;
     686             : 
     687             :   Server &Get(const std::string &pattern, Handler handler);
     688             :   Server &Post(const std::string &pattern, Handler handler);
     689             :   Server &Post(const std::string &pattern, HandlerWithContentReader handler);
     690             :   Server &Put(const std::string &pattern, Handler handler);
     691             :   Server &Put(const std::string &pattern, HandlerWithContentReader handler);
     692             :   Server &Patch(const std::string &pattern, Handler handler);
     693             :   Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
     694             :   Server &Delete(const std::string &pattern, Handler handler);
     695             :   Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
     696             :   Server &Options(const std::string &pattern, Handler handler);
     697             : 
     698             :   bool set_base_dir(const std::string &dir,
     699             :                     const std::string &mount_point = std::string());
     700             :   bool set_mount_point(const std::string &mount_point, const std::string &dir,
     701             :                        Headers headers = Headers());
     702             :   bool remove_mount_point(const std::string &mount_point);
     703             :   Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
     704             :                                                   const std::string &mime);
     705             :   Server &set_file_request_handler(Handler handler);
     706             : 
     707             :   Server &set_error_handler(HandlerWithResponse handler);
     708             :   Server &set_error_handler(Handler handler);
     709             :   Server &set_exception_handler(ExceptionHandler handler);
     710             :   Server &set_pre_routing_handler(HandlerWithResponse handler);
     711             :   Server &set_post_routing_handler(Handler handler);
     712             : 
     713             :   Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
     714             :   Server &set_logger(Logger logger);
     715             : 
     716             :   Server &set_address_family(int family);
     717             :   Server &set_tcp_nodelay(bool on);
     718             :   Server &set_socket_options(SocketOptions socket_options);
     719             : 
     720             :   Server &set_default_headers(Headers headers);
     721             : 
     722             :   Server &set_keep_alive_max_count(size_t count);
     723             :   Server &set_keep_alive_timeout(time_t sec);
     724             : 
     725             :   Server &set_read_timeout(time_t sec, time_t usec = 0);
     726             :   template <class Rep, class Period>
     727             :   Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
     728             : 
     729             :   Server &set_write_timeout(time_t sec, time_t usec = 0);
     730             :   template <class Rep, class Period>
     731             :   Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
     732             : 
     733             :   Server &set_idle_interval(time_t sec, time_t usec = 0);
     734             :   template <class Rep, class Period>
     735             :   Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
     736             : 
     737             :   Server &set_payload_max_length(size_t length);
     738             : 
     739             :   bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
     740             :   int bind_to_any_port(const std::string &host, int socket_flags = 0);
     741             :   bool listen_after_bind();
     742             : 
     743             :   bool listen(const std::string &host, int port, int socket_flags = 0);
     744             : 
     745             :   bool is_running() const;
     746             :   void wait_until_ready() const;
     747             :   void stop();
     748             : 
     749             :   std::function<TaskQueue *(void)> new_task_queue;
     750             : 
     751             : protected:
     752             :   bool process_request(Stream &strm, bool close_connection,
     753             :                        bool &connection_closed,
     754             :                        const std::function<void(Request &)> &setup_request);
     755             : 
     756             :   std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
     757             :   size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
     758             :   time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
     759             :   time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
     760             :   time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
     761             :   time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
     762             :   time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
     763             :   time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
     764             :   time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
     765             :   size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
     766             : 
     767             : private:
     768             :   using Handlers = std::vector<std::pair<std::regex, Handler>>;
     769             :   using HandlersForContentReader =
     770             :       std::vector<std::pair<std::regex, HandlerWithContentReader>>;
     771             : 
     772             :   socket_t create_server_socket(const std::string &host, int port,
     773             :                                 int socket_flags,
     774             :                                 SocketOptions socket_options) const;
     775             :   int bind_internal(const std::string &host, int port, int socket_flags);
     776             :   bool listen_internal();
     777             : 
     778             :   bool routing(Request &req, Response &res, Stream &strm);
     779             :   bool handle_file_request(const Request &req, Response &res,
     780             :                            bool head = false);
     781             :   bool dispatch_request(Request &req, Response &res, const Handlers &handlers);
     782             :   bool
     783             :   dispatch_request_for_content_reader(Request &req, Response &res,
     784             :                                       ContentReader content_reader,
     785             :                                       const HandlersForContentReader &handlers);
     786             : 
     787             :   bool parse_request_line(const char *s, Request &req);
     788             :   void apply_ranges(const Request &req, Response &res,
     789             :                     std::string &content_type, std::string &boundary);
     790             :   bool write_response(Stream &strm, bool close_connection, const Request &req,
     791             :                       Response &res);
     792             :   bool write_response_with_content(Stream &strm, bool close_connection,
     793             :                                    const Request &req, Response &res);
     794             :   bool write_response_core(Stream &strm, bool close_connection,
     795             :                            const Request &req, Response &res,
     796             :                            bool need_apply_ranges);
     797             :   bool write_content_with_provider(Stream &strm, const Request &req,
     798             :                                    Response &res, const std::string &boundary,
     799             :                                    const std::string &content_type);
     800             :   bool read_content(Stream &strm, Request &req, Response &res);
     801             :   bool
     802             :   read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
     803             :                                      ContentReceiver receiver,
     804             :                                      MultipartContentHeader multipart_header,
     805             :                                      ContentReceiver multipart_receiver);
     806             :   bool read_content_core(Stream &strm, Request &req, Response &res,
     807             :                          ContentReceiver receiver,
     808             :                          MultipartContentHeader multipart_header,
     809             :                          ContentReceiver multipart_receiver);
     810             : 
     811             :   virtual bool process_and_close_socket(socket_t sock);
     812             : 
     813             :   struct MountPointEntry {
     814             :     std::string mount_point;
     815             :     std::string base_dir;
     816             :     Headers headers;
     817             :   };
     818             :   std::vector<MountPointEntry> base_dirs_;
     819             : 
     820             :   std::atomic<bool> is_running_{false};
     821             :   std::atomic<bool> done_{false};
     822             :   std::map<std::string, std::string> file_extension_and_mimetype_map_;
     823             :   Handler file_request_handler_;
     824             :   Handlers get_handlers_;
     825             :   Handlers post_handlers_;
     826             :   HandlersForContentReader post_handlers_for_content_reader_;
     827             :   Handlers put_handlers_;
     828             :   HandlersForContentReader put_handlers_for_content_reader_;
     829             :   Handlers patch_handlers_;
     830             :   HandlersForContentReader patch_handlers_for_content_reader_;
     831             :   Handlers delete_handlers_;
     832             :   HandlersForContentReader delete_handlers_for_content_reader_;
     833             :   Handlers options_handlers_;
     834             :   HandlerWithResponse error_handler_;
     835             :   ExceptionHandler exception_handler_;
     836             :   HandlerWithResponse pre_routing_handler_;
     837             :   Handler post_routing_handler_;
     838             :   Logger logger_;
     839             :   Expect100ContinueHandler expect_100_continue_handler_;
     840             : 
     841             :   int address_family_ = AF_UNSPEC;
     842             :   bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
     843             :   SocketOptions socket_options_ = default_socket_options;
     844             : 
     845             :   Headers default_headers_;
     846             : };
     847             : 
     848             : enum class Error {
     849             :   Success = 0,
     850             :   Unknown,
     851             :   Connection,
     852             :   BindIPAddress,
     853             :   Read,
     854             :   Write,
     855             :   ExceedRedirectCount,
     856             :   Canceled,
     857             :   SSLConnection,
     858             :   SSLLoadingCerts,
     859             :   SSLServerVerification,
     860             :   UnsupportedMultipartBoundaryChars,
     861             :   Compression,
     862             :   ConnectionTimeout,
     863             : 
     864             :   // For internal use only
     865             :   SSLPeerCouldBeClosed_,
     866             : };
     867             : 
     868             : std::string to_string(const Error error);
     869             : 
     870             : std::ostream &operator<<(std::ostream &os, const Error &obj);
     871             : 
     872             : class Result {
     873             : public:
     874             :   Result(std::unique_ptr<Response> &&res, Error err,
     875             :          Headers &&request_headers = Headers{})
     876             :       : res_(std::move(res)), err_(err),
     877             :         request_headers_(std::move(request_headers)) {}
     878             :   // Response
     879             :   operator bool() const { return res_ != nullptr; }
     880             :   bool operator==(std::nullptr_t) const { return res_ == nullptr; }
     881             :   bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
     882             :   const Response &value() const { return *res_; }
     883             :   Response &value() { return *res_; }
     884             :   const Response &operator*() const { return *res_; }
     885             :   Response &operator*() { return *res_; }
     886             :   const Response *operator->() const { return res_.get(); }
     887             :   Response *operator->() { return res_.get(); }
     888             : 
     889             :   // Error
     890             :   Error error() const { return err_; }
     891             : 
     892             :   // Request Headers
     893             :   bool has_request_header(const std::string &key) const;
     894             :   std::string get_request_header_value(const std::string &key,
     895             :                                        size_t id = 0) const;
     896             :   template <typename T>
     897             :   T get_request_header_value(const std::string &key, size_t id = 0) const;
     898             :   size_t get_request_header_value_count(const std::string &key) const;
     899             : 
     900             : private:
     901             :   std::unique_ptr<Response> res_;
     902             :   Error err_;
     903             :   Headers request_headers_;
     904             : };
     905             : 
     906             : class ClientImpl {
     907             : public:
     908             :   explicit ClientImpl(const std::string &host);
     909             : 
     910             :   explicit ClientImpl(const std::string &host, int port);
     911             : 
     912             :   explicit ClientImpl(const std::string &host, int port,
     913             :                       const std::string &client_cert_path,
     914             :                       const std::string &client_key_path);
     915             : 
     916             :   virtual ~ClientImpl();
     917             : 
     918             :   virtual bool is_valid() const;
     919             : 
     920             :   Result Get(const std::string &path);
     921             :   Result Get(const std::string &path, const Headers &headers);
     922             :   Result Get(const std::string &path, Progress progress);
     923             :   Result Get(const std::string &path, const Headers &headers,
     924             :              Progress progress);
     925             :   Result Get(const std::string &path, ContentReceiver content_receiver);
     926             :   Result Get(const std::string &path, const Headers &headers,
     927             :              ContentReceiver content_receiver);
     928             :   Result Get(const std::string &path, ContentReceiver content_receiver,
     929             :              Progress progress);
     930             :   Result Get(const std::string &path, const Headers &headers,
     931             :              ContentReceiver content_receiver, Progress progress);
     932             :   Result Get(const std::string &path, ResponseHandler response_handler,
     933             :              ContentReceiver content_receiver);
     934             :   Result Get(const std::string &path, const Headers &headers,
     935             :              ResponseHandler response_handler,
     936             :              ContentReceiver content_receiver);
     937             :   Result Get(const std::string &path, ResponseHandler response_handler,
     938             :              ContentReceiver content_receiver, Progress progress);
     939             :   Result Get(const std::string &path, const Headers &headers,
     940             :              ResponseHandler response_handler, ContentReceiver content_receiver,
     941             :              Progress progress);
     942             : 
     943             :   Result Get(const std::string &path, const Params &params,
     944             :              const Headers &headers, Progress progress = nullptr);
     945             :   Result Get(const std::string &path, const Params &params,
     946             :              const Headers &headers, ContentReceiver content_receiver,
     947             :              Progress progress = nullptr);
     948             :   Result Get(const std::string &path, const Params &params,
     949             :              const Headers &headers, ResponseHandler response_handler,
     950             :              ContentReceiver content_receiver, Progress progress = nullptr);
     951             : 
     952             :   Result Head(const std::string &path);
     953             :   Result Head(const std::string &path, const Headers &headers);
     954             : 
     955             :   Result Post(const std::string &path);
     956             :   Result Post(const std::string &path, const Headers &headers);
     957             :   Result Post(const std::string &path, const char *body, size_t content_length,
     958             :               const std::string &content_type);
     959             :   Result Post(const std::string &path, const Headers &headers, const char *body,
     960             :               size_t content_length, const std::string &content_type);
     961             :   Result Post(const std::string &path, const std::string &body,
     962             :               const std::string &content_type);
     963             :   Result Post(const std::string &path, const Headers &headers,
     964             :               const std::string &body, const std::string &content_type);
     965             :   Result Post(const std::string &path, size_t content_length,
     966             :               ContentProvider content_provider,
     967             :               const std::string &content_type);
     968             :   Result Post(const std::string &path,
     969             :               ContentProviderWithoutLength content_provider,
     970             :               const std::string &content_type);
     971             :   Result Post(const std::string &path, const Headers &headers,
     972             :               size_t content_length, ContentProvider content_provider,
     973             :               const std::string &content_type);
     974             :   Result Post(const std::string &path, const Headers &headers,
     975             :               ContentProviderWithoutLength content_provider,
     976             :               const std::string &content_type);
     977             :   Result Post(const std::string &path, const Params &params);
     978             :   Result Post(const std::string &path, const Headers &headers,
     979             :               const Params &params);
     980             :   Result Post(const std::string &path, const MultipartFormDataItems &items);
     981             :   Result Post(const std::string &path, const Headers &headers,
     982             :               const MultipartFormDataItems &items);
     983             :   Result Post(const std::string &path, const Headers &headers,
     984             :               const MultipartFormDataItems &items, const std::string &boundary);
     985             :   Result Post(const std::string &path, const Headers &headers,
     986             :               const MultipartFormDataItems &items,
     987             :               const MultipartFormDataProviderItems &provider_items);
     988             : 
     989             :   Result Put(const std::string &path);
     990             :   Result Put(const std::string &path, const char *body, size_t content_length,
     991             :              const std::string &content_type);
     992             :   Result Put(const std::string &path, const Headers &headers, const char *body,
     993             :              size_t content_length, const std::string &content_type);
     994             :   Result Put(const std::string &path, const std::string &body,
     995             :              const std::string &content_type);
     996             :   Result Put(const std::string &path, const Headers &headers,
     997             :              const std::string &body, const std::string &content_type);
     998             :   Result Put(const std::string &path, size_t content_length,
     999             :              ContentProvider content_provider, const std::string &content_type);
    1000             :   Result Put(const std::string &path,
    1001             :              ContentProviderWithoutLength content_provider,
    1002             :              const std::string &content_type);
    1003             :   Result Put(const std::string &path, const Headers &headers,
    1004             :              size_t content_length, ContentProvider content_provider,
    1005             :              const std::string &content_type);
    1006             :   Result Put(const std::string &path, const Headers &headers,
    1007             :              ContentProviderWithoutLength content_provider,
    1008             :              const std::string &content_type);
    1009             :   Result Put(const std::string &path, const Params &params);
    1010             :   Result Put(const std::string &path, const Headers &headers,
    1011             :              const Params &params);
    1012             :   Result Put(const std::string &path, const MultipartFormDataItems &items);
    1013             :   Result Put(const std::string &path, const Headers &headers,
    1014             :              const MultipartFormDataItems &items);
    1015             :   Result Put(const std::string &path, const Headers &headers,
    1016             :              const MultipartFormDataItems &items, const std::string &boundary);
    1017             :   Result Put(const std::string &path, const Headers &headers,
    1018             :              const MultipartFormDataItems &items,
    1019             :              const MultipartFormDataProviderItems &provider_items);
    1020             : 
    1021             :   Result Patch(const std::string &path);
    1022             :   Result Patch(const std::string &path, const char *body, size_t content_length,
    1023             :                const std::string &content_type);
    1024             :   Result Patch(const std::string &path, const Headers &headers,
    1025             :                const char *body, size_t content_length,
    1026             :                const std::string &content_type);
    1027             :   Result Patch(const std::string &path, const std::string &body,
    1028             :                const std::string &content_type);
    1029             :   Result Patch(const std::string &path, const Headers &headers,
    1030             :                const std::string &body, const std::string &content_type);
    1031             :   Result Patch(const std::string &path, size_t content_length,
    1032             :                ContentProvider content_provider,
    1033             :                const std::string &content_type);
    1034             :   Result Patch(const std::string &path,
    1035             :                ContentProviderWithoutLength content_provider,
    1036             :                const std::string &content_type);
    1037             :   Result Patch(const std::string &path, const Headers &headers,
    1038             :                size_t content_length, ContentProvider content_provider,
    1039             :                const std::string &content_type);
    1040             :   Result Patch(const std::string &path, const Headers &headers,
    1041             :                ContentProviderWithoutLength content_provider,
    1042             :                const std::string &content_type);
    1043             : 
    1044             :   Result Delete(const std::string &path);
    1045             :   Result Delete(const std::string &path, const Headers &headers);
    1046             :   Result Delete(const std::string &path, const char *body,
    1047             :                 size_t content_length, const std::string &content_type);
    1048             :   Result Delete(const std::string &path, const Headers &headers,
    1049             :                 const char *body, size_t content_length,
    1050             :                 const std::string &content_type);
    1051             :   Result Delete(const std::string &path, const std::string &body,
    1052             :                 const std::string &content_type);
    1053             :   Result Delete(const std::string &path, const Headers &headers,
    1054             :                 const std::string &body, const std::string &content_type);
    1055             : 
    1056             :   Result Options(const std::string &path);
    1057             :   Result Options(const std::string &path, const Headers &headers);
    1058             : 
    1059             :   bool send(Request &req, Response &res, Error &error);
    1060             :   Result send(const Request &req);
    1061             : 
    1062             :   size_t is_socket_open() const;
    1063             : 
    1064             :   socket_t socket() const;
    1065             : 
    1066             :   void stop();
    1067             : 
    1068             :   void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
    1069             : 
    1070             :   void set_default_headers(Headers headers);
    1071             : 
    1072             :   void set_address_family(int family);
    1073             :   void set_tcp_nodelay(bool on);
    1074             :   void set_socket_options(SocketOptions socket_options);
    1075             : 
    1076             :   void set_connection_timeout(time_t sec, time_t usec = 0);
    1077             :   template <class Rep, class Period>
    1078             :   void
    1079             :   set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
    1080             : 
    1081             :   void set_read_timeout(time_t sec, time_t usec = 0);
    1082             :   template <class Rep, class Period>
    1083             :   void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
    1084             : 
    1085             :   void set_write_timeout(time_t sec, time_t usec = 0);
    1086             :   template <class Rep, class Period>
    1087             :   void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
    1088             : 
    1089             :   void set_basic_auth(const std::string &username, const std::string &password);
    1090             :   void set_bearer_token_auth(const std::string &token);
    1091             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1092             :   void set_digest_auth(const std::string &username,
    1093             :                        const std::string &password);
    1094             : #endif
    1095             : 
    1096             :   void set_keep_alive(bool on);
    1097             :   void set_follow_location(bool on);
    1098             : 
    1099             :   void set_url_encode(bool on);
    1100             : 
    1101             :   void set_compress(bool on);
    1102             : 
    1103             :   void set_decompress(bool on);
    1104             : 
    1105             :   void set_interface(const std::string &intf);
    1106             : 
    1107             :   void set_proxy(const std::string &host, int port);
    1108             :   void set_proxy_basic_auth(const std::string &username,
    1109             :                             const std::string &password);
    1110             :   void set_proxy_bearer_token_auth(const std::string &token);
    1111             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1112             :   void set_proxy_digest_auth(const std::string &username,
    1113             :                              const std::string &password);
    1114             : #endif
    1115             : 
    1116             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1117             :   void set_ca_cert_path(const std::string &ca_cert_file_path,
    1118             :                         const std::string &ca_cert_dir_path = std::string());
    1119             :   void set_ca_cert_store(X509_STORE *ca_cert_store);
    1120             : #endif
    1121             : 
    1122             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1123             :   void enable_server_certificate_verification(bool enabled);
    1124             : #endif
    1125             : 
    1126             :   void set_logger(Logger logger);
    1127             : 
    1128             : protected:
    1129             :   struct Socket {
    1130             :     socket_t sock = INVALID_SOCKET;
    1131             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1132             :     SSL *ssl = nullptr;
    1133             : #endif
    1134             : 
    1135             :     bool is_open() const { return sock != INVALID_SOCKET; }
    1136             :   };
    1137             : 
    1138             :   virtual bool create_and_connect_socket(Socket &socket, Error &error);
    1139             : 
    1140             :   // All of:
    1141             :   //   shutdown_ssl
    1142             :   //   shutdown_socket
    1143             :   //   close_socket
    1144             :   // should ONLY be called when socket_mutex_ is locked.
    1145             :   // Also, shutdown_ssl and close_socket should also NOT be called concurrently
    1146             :   // with a DIFFERENT thread sending requests using that socket.
    1147             :   virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
    1148             :   void shutdown_socket(Socket &socket);
    1149             :   void close_socket(Socket &socket);
    1150             : 
    1151             :   bool process_request(Stream &strm, Request &req, Response &res,
    1152             :                        bool close_connection, Error &error);
    1153             : 
    1154             :   bool write_content_with_provider(Stream &strm, const Request &req,
    1155             :                                    Error &error);
    1156             : 
    1157             :   void copy_settings(const ClientImpl &rhs);
    1158             : 
    1159             :   // Socket endpoint information
    1160             :   const std::string host_;
    1161             :   const int port_;
    1162             :   const std::string host_and_port_;
    1163             : 
    1164             :   // Current open socket
    1165             :   Socket socket_;
    1166             :   mutable std::mutex socket_mutex_;
    1167             :   std::recursive_mutex request_mutex_;
    1168             : 
    1169             :   // These are all protected under socket_mutex
    1170             :   size_t socket_requests_in_flight_ = 0;
    1171             :   std::thread::id socket_requests_are_from_thread_ = std::thread::id();
    1172             :   bool socket_should_be_closed_when_request_is_done_ = false;
    1173             : 
    1174             :   // Hostname-IP map
    1175             :   std::map<std::string, std::string> addr_map_;
    1176             : 
    1177             :   // Default headers
    1178             :   Headers default_headers_;
    1179             : 
    1180             :   // Settings
    1181             :   std::string client_cert_path_;
    1182             :   std::string client_key_path_;
    1183             : 
    1184             :   time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
    1185             :   time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
    1186             :   time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
    1187             :   time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
    1188             :   time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
    1189             :   time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
    1190             : 
    1191             :   std::string basic_auth_username_;
    1192             :   std::string basic_auth_password_;
    1193             :   std::string bearer_token_auth_token_;
    1194             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1195             :   std::string digest_auth_username_;
    1196             :   std::string digest_auth_password_;
    1197             : #endif
    1198             : 
    1199             :   bool keep_alive_ = false;
    1200             :   bool follow_location_ = false;
    1201             : 
    1202             :   bool url_encode_ = true;
    1203             : 
    1204             :   int address_family_ = AF_UNSPEC;
    1205             :   bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
    1206             :   SocketOptions socket_options_ = nullptr;
    1207             : 
    1208             :   bool compress_ = false;
    1209             :   bool decompress_ = true;
    1210             : 
    1211             :   std::string interface_;
    1212             : 
    1213             :   std::string proxy_host_;
    1214             :   int proxy_port_ = -1;
    1215             : 
    1216             :   std::string proxy_basic_auth_username_;
    1217             :   std::string proxy_basic_auth_password_;
    1218             :   std::string proxy_bearer_token_auth_token_;
    1219             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1220             :   std::string proxy_digest_auth_username_;
    1221             :   std::string proxy_digest_auth_password_;
    1222             : #endif
    1223             : 
    1224             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1225             :   std::string ca_cert_file_path_;
    1226             :   std::string ca_cert_dir_path_;
    1227             : 
    1228             :   X509_STORE *ca_cert_store_ = nullptr;
    1229             : #endif
    1230             : 
    1231             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1232             :   bool server_certificate_verification_ = true;
    1233             : #endif
    1234             : 
    1235             :   Logger logger_;
    1236             : 
    1237             : private:
    1238             :   bool send_(Request &req, Response &res, Error &error);
    1239             :   Result send_(Request &&req);
    1240             : 
    1241             :   socket_t create_client_socket(Error &error) const;
    1242             :   bool read_response_line(Stream &strm, const Request &req, Response &res);
    1243             :   bool write_request(Stream &strm, Request &req, bool close_connection,
    1244             :                      Error &error);
    1245             :   bool redirect(Request &req, Response &res, Error &error);
    1246             :   bool handle_request(Stream &strm, Request &req, Response &res,
    1247             :                       bool close_connection, Error &error);
    1248             :   std::unique_ptr<Response> send_with_content_provider(
    1249             :       Request &req, const char *body, size_t content_length,
    1250             :       ContentProvider content_provider,
    1251             :       ContentProviderWithoutLength content_provider_without_length,
    1252             :       const std::string &content_type, Error &error);
    1253             :   Result send_with_content_provider(
    1254             :       const std::string &method, const std::string &path,
    1255             :       const Headers &headers, const char *body, size_t content_length,
    1256             :       ContentProvider content_provider,
    1257             :       ContentProviderWithoutLength content_provider_without_length,
    1258             :       const std::string &content_type);
    1259             :   ContentProviderWithoutLength get_multipart_content_provider(
    1260             :       const std::string &boundary, const MultipartFormDataItems &items,
    1261             :       const MultipartFormDataProviderItems &provider_items);
    1262             : 
    1263             :   std::string adjust_host_string(const std::string &host) const;
    1264             : 
    1265             :   virtual bool process_socket(const Socket &socket,
    1266             :                               std::function<bool(Stream &strm)> callback);
    1267             :   virtual bool is_ssl() const;
    1268             : };
    1269             : 
    1270             : class Client {
    1271             : public:
    1272             :   // Universal interface
    1273             :   explicit Client(const std::string &scheme_host_port);
    1274             : 
    1275             :   explicit Client(const std::string &scheme_host_port,
    1276             :                   const std::string &client_cert_path,
    1277             :                   const std::string &client_key_path);
    1278             : 
    1279             :   // HTTP only interface
    1280             :   explicit Client(const std::string &host, int port);
    1281             : 
    1282             :   explicit Client(const std::string &host, int port,
    1283             :                   const std::string &client_cert_path,
    1284             :                   const std::string &client_key_path);
    1285             : 
    1286             :   Client(Client &&) = default;
    1287             : 
    1288             :   ~Client();
    1289             : 
    1290             :   bool is_valid() const;
    1291             : 
    1292             :   Result Get(const std::string &path);
    1293             :   Result Get(const std::string &path, const Headers &headers);
    1294             :   Result Get(const std::string &path, Progress progress);
    1295             :   Result Get(const std::string &path, const Headers &headers,
    1296             :              Progress progress);
    1297             :   Result Get(const std::string &path, ContentReceiver content_receiver);
    1298             :   Result Get(const std::string &path, const Headers &headers,
    1299             :              ContentReceiver content_receiver);
    1300             :   Result Get(const std::string &path, ContentReceiver content_receiver,
    1301             :              Progress progress);
    1302             :   Result Get(const std::string &path, const Headers &headers,
    1303             :              ContentReceiver content_receiver, Progress progress);
    1304             :   Result Get(const std::string &path, ResponseHandler response_handler,
    1305             :              ContentReceiver content_receiver);
    1306             :   Result Get(const std::string &path, const Headers &headers,
    1307             :              ResponseHandler response_handler,
    1308             :              ContentReceiver content_receiver);
    1309             :   Result Get(const std::string &path, const Headers &headers,
    1310             :              ResponseHandler response_handler, ContentReceiver content_receiver,
    1311             :              Progress progress);
    1312             :   Result Get(const std::string &path, ResponseHandler response_handler,
    1313             :              ContentReceiver content_receiver, Progress progress);
    1314             : 
    1315             :   Result Get(const std::string &path, const Params &params,
    1316             :              const Headers &headers, Progress progress = nullptr);
    1317             :   Result Get(const std::string &path, const Params &params,
    1318             :              const Headers &headers, ContentReceiver content_receiver,
    1319             :              Progress progress = nullptr);
    1320             :   Result Get(const std::string &path, const Params &params,
    1321             :              const Headers &headers, ResponseHandler response_handler,
    1322             :              ContentReceiver content_receiver, Progress progress = nullptr);
    1323             : 
    1324             :   Result Head(const std::string &path);
    1325             :   Result Head(const std::string &path, const Headers &headers);
    1326             : 
    1327             :   Result Post(const std::string &path);
    1328             :   Result Post(const std::string &path, const Headers &headers);
    1329             :   Result Post(const std::string &path, const char *body, size_t content_length,
    1330             :               const std::string &content_type);
    1331             :   Result Post(const std::string &path, const Headers &headers, const char *body,
    1332             :               size_t content_length, const std::string &content_type);
    1333             :   Result Post(const std::string &path, const std::string &body,
    1334             :               const std::string &content_type);
    1335             :   Result Post(const std::string &path, const Headers &headers,
    1336             :               const std::string &body, const std::string &content_type);
    1337             :   Result Post(const std::string &path, size_t content_length,
    1338             :               ContentProvider content_provider,
    1339             :               const std::string &content_type);
    1340             :   Result Post(const std::string &path,
    1341             :               ContentProviderWithoutLength content_provider,
    1342             :               const std::string &content_type);
    1343             :   Result Post(const std::string &path, const Headers &headers,
    1344             :               size_t content_length, ContentProvider content_provider,
    1345             :               const std::string &content_type);
    1346             :   Result Post(const std::string &path, const Headers &headers,
    1347             :               ContentProviderWithoutLength content_provider,
    1348             :               const std::string &content_type);
    1349             :   Result Post(const std::string &path, const Params &params);
    1350             :   Result Post(const std::string &path, const Headers &headers,
    1351             :               const Params &params);
    1352             :   Result Post(const std::string &path, const MultipartFormDataItems &items);
    1353             :   Result Post(const std::string &path, const Headers &headers,
    1354             :               const MultipartFormDataItems &items);
    1355             :   Result Post(const std::string &path, const Headers &headers,
    1356             :               const MultipartFormDataItems &items, const std::string &boundary);
    1357             :   Result Post(const std::string &path, const Headers &headers,
    1358             :               const MultipartFormDataItems &items,
    1359             :               const MultipartFormDataProviderItems &provider_items);
    1360             : 
    1361             :   Result Put(const std::string &path);
    1362             :   Result Put(const std::string &path, const char *body, size_t content_length,
    1363             :              const std::string &content_type);
    1364             :   Result Put(const std::string &path, const Headers &headers, const char *body,
    1365             :              size_t content_length, const std::string &content_type);
    1366             :   Result Put(const std::string &path, const std::string &body,
    1367             :              const std::string &content_type);
    1368             :   Result Put(const std::string &path, const Headers &headers,
    1369             :              const std::string &body, const std::string &content_type);
    1370             :   Result Put(const std::string &path, size_t content_length,
    1371             :              ContentProvider content_provider, const std::string &content_type);
    1372             :   Result Put(const std::string &path,
    1373             :              ContentProviderWithoutLength content_provider,
    1374             :              const std::string &content_type);
    1375             :   Result Put(const std::string &path, const Headers &headers,
    1376             :              size_t content_length, ContentProvider content_provider,
    1377             :              const std::string &content_type);
    1378             :   Result Put(const std::string &path, const Headers &headers,
    1379             :              ContentProviderWithoutLength content_provider,
    1380             :              const std::string &content_type);
    1381             :   Result Put(const std::string &path, const Params &params);
    1382             :   Result Put(const std::string &path, const Headers &headers,
    1383             :              const Params &params);
    1384             :   Result Put(const std::string &path, const MultipartFormDataItems &items);
    1385             :   Result Put(const std::string &path, const Headers &headers,
    1386             :              const MultipartFormDataItems &items);
    1387             :   Result Put(const std::string &path, const Headers &headers,
    1388             :              const MultipartFormDataItems &items, const std::string &boundary);
    1389             :   Result Put(const std::string &path, const Headers &headers,
    1390             :              const MultipartFormDataItems &items,
    1391             :              const MultipartFormDataProviderItems &provider_items);
    1392             : 
    1393             :   Result Patch(const std::string &path);
    1394             :   Result Patch(const std::string &path, const char *body, size_t content_length,
    1395             :                const std::string &content_type);
    1396             :   Result Patch(const std::string &path, const Headers &headers,
    1397             :                const char *body, size_t content_length,
    1398             :                const std::string &content_type);
    1399             :   Result Patch(const std::string &path, const std::string &body,
    1400             :                const std::string &content_type);
    1401             :   Result Patch(const std::string &path, const Headers &headers,
    1402             :                const std::string &body, const std::string &content_type);
    1403             :   Result Patch(const std::string &path, size_t content_length,
    1404             :                ContentProvider content_provider,
    1405             :                const std::string &content_type);
    1406             :   Result Patch(const std::string &path,
    1407             :                ContentProviderWithoutLength content_provider,
    1408             :                const std::string &content_type);
    1409             :   Result Patch(const std::string &path, const Headers &headers,
    1410             :                size_t content_length, ContentProvider content_provider,
    1411             :                const std::string &content_type);
    1412             :   Result Patch(const std::string &path, const Headers &headers,
    1413             :                ContentProviderWithoutLength content_provider,
    1414             :                const std::string &content_type);
    1415             : 
    1416             :   Result Delete(const std::string &path);
    1417             :   Result Delete(const std::string &path, const Headers &headers);
    1418             :   Result Delete(const std::string &path, const char *body,
    1419             :                 size_t content_length, const std::string &content_type);
    1420             :   Result Delete(const std::string &path, const Headers &headers,
    1421             :                 const char *body, size_t content_length,
    1422             :                 const std::string &content_type);
    1423             :   Result Delete(const std::string &path, const std::string &body,
    1424             :                 const std::string &content_type);
    1425             :   Result Delete(const std::string &path, const Headers &headers,
    1426             :                 const std::string &body, const std::string &content_type);
    1427             : 
    1428             :   Result Options(const std::string &path);
    1429             :   Result Options(const std::string &path, const Headers &headers);
    1430             : 
    1431             :   bool send(Request &req, Response &res, Error &error);
    1432             :   Result send(const Request &req);
    1433             : 
    1434             :   size_t is_socket_open() const;
    1435             : 
    1436             :   socket_t socket() const;
    1437             : 
    1438             :   void stop();
    1439             : 
    1440             :   void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
    1441             : 
    1442             :   void set_default_headers(Headers headers);
    1443             : 
    1444             :   void set_address_family(int family);
    1445             :   void set_tcp_nodelay(bool on);
    1446             :   void set_socket_options(SocketOptions socket_options);
    1447             : 
    1448             :   void set_connection_timeout(time_t sec, time_t usec = 0);
    1449             :   template <class Rep, class Period>
    1450             :   void
    1451             :   set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
    1452             : 
    1453             :   void set_read_timeout(time_t sec, time_t usec = 0);
    1454             :   template <class Rep, class Period>
    1455             :   void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
    1456             : 
    1457             :   void set_write_timeout(time_t sec, time_t usec = 0);
    1458             :   template <class Rep, class Period>
    1459             :   void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
    1460             : 
    1461             :   void set_basic_auth(const std::string &username, const std::string &password);
    1462             :   void set_bearer_token_auth(const std::string &token);
    1463             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1464             :   void set_digest_auth(const std::string &username,
    1465             :                        const std::string &password);
    1466             : #endif
    1467             : 
    1468             :   void set_keep_alive(bool on);
    1469             :   void set_follow_location(bool on);
    1470             : 
    1471             :   void set_url_encode(bool on);
    1472             : 
    1473             :   void set_compress(bool on);
    1474             : 
    1475             :   void set_decompress(bool on);
    1476             : 
    1477             :   void set_interface(const std::string &intf);
    1478             : 
    1479             :   void set_proxy(const std::string &host, int port);
    1480             :   void set_proxy_basic_auth(const std::string &username,
    1481             :                             const std::string &password);
    1482             :   void set_proxy_bearer_token_auth(const std::string &token);
    1483             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1484             :   void set_proxy_digest_auth(const std::string &username,
    1485             :                              const std::string &password);
    1486             : #endif
    1487             : 
    1488             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1489             :   void enable_server_certificate_verification(bool enabled);
    1490             : #endif
    1491             : 
    1492             :   void set_logger(Logger logger);
    1493             : 
    1494             :   // SSL
    1495             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1496             :   void set_ca_cert_path(const std::string &ca_cert_file_path,
    1497             :                         const std::string &ca_cert_dir_path = std::string());
    1498             : 
    1499             :   void set_ca_cert_store(X509_STORE *ca_cert_store);
    1500             : 
    1501             :   long get_openssl_verify_result() const;
    1502             : 
    1503             :   SSL_CTX *ssl_context() const;
    1504             : #endif
    1505             : 
    1506             : private:
    1507             :   std::unique_ptr<ClientImpl> cli_;
    1508             : 
    1509             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1510             :   bool is_ssl_ = false;
    1511             : #endif
    1512             : };
    1513             : 
    1514             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    1515             : class SSLServer : public Server {
    1516             : public:
    1517             :   SSLServer(const char *cert_path, const char *private_key_path,
    1518             :             const char *client_ca_cert_file_path = nullptr,
    1519             :             const char *client_ca_cert_dir_path = nullptr,
    1520             :             const char *private_key_password = nullptr);
    1521             : 
    1522             :   SSLServer(X509 *cert, EVP_PKEY *private_key,
    1523             :             X509_STORE *client_ca_cert_store = nullptr);
    1524             : 
    1525             :   SSLServer(
    1526             :       const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
    1527             : 
    1528             :   ~SSLServer() override;
    1529             : 
    1530             :   bool is_valid() const override;
    1531             : 
    1532             :   SSL_CTX *ssl_context() const;
    1533             : 
    1534             : private:
    1535             :   bool process_and_close_socket(socket_t sock) override;
    1536             : 
    1537             :   SSL_CTX *ctx_;
    1538             :   std::mutex ctx_mutex_;
    1539             : };
    1540             : 
    1541             : class SSLClient : public ClientImpl {
    1542             : public:
    1543             :   explicit SSLClient(const std::string &host);
    1544             : 
    1545             :   explicit SSLClient(const std::string &host, int port);
    1546             : 
    1547             :   explicit SSLClient(const std::string &host, int port,
    1548             :                      const std::string &client_cert_path,
    1549             :                      const std::string &client_key_path);
    1550             : 
    1551             :   explicit SSLClient(const std::string &host, int port, X509 *client_cert,
    1552             :                      EVP_PKEY *client_key);
    1553             : 
    1554             :   ~SSLClient() override;
    1555             : 
    1556             :   bool is_valid() const override;
    1557             : 
    1558             :   void set_ca_cert_store(X509_STORE *ca_cert_store);
    1559             : 
    1560             :   long get_openssl_verify_result() const;
    1561             : 
    1562             :   SSL_CTX *ssl_context() const;
    1563             : 
    1564             : private:
    1565             :   bool create_and_connect_socket(Socket &socket, Error &error) override;
    1566             :   void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
    1567             :   void shutdown_ssl_impl(Socket &socket, bool shutdown_socket);
    1568             : 
    1569             :   bool process_socket(const Socket &socket,
    1570             :                       std::function<bool(Stream &strm)> callback) override;
    1571             :   bool is_ssl() const override;
    1572             : 
    1573             :   bool connect_with_proxy(Socket &sock, Response &res, bool &success,
    1574             :                           Error &error);
    1575             :   bool initialize_ssl(Socket &socket, Error &error);
    1576             : 
    1577             :   bool load_certs();
    1578             : 
    1579             :   bool verify_host(X509 *server_cert) const;
    1580             :   bool verify_host_with_subject_alt_name(X509 *server_cert) const;
    1581             :   bool verify_host_with_common_name(X509 *server_cert) const;
    1582             :   bool check_host_name(const char *pattern, size_t pattern_len) const;
    1583             : 
    1584             :   SSL_CTX *ctx_;
    1585             :   std::mutex ctx_mutex_;
    1586             :   std::once_flag initialize_cert_;
    1587             : 
    1588             :   std::vector<std::string> host_components_;
    1589             : 
    1590             :   long verify_result_ = 0;
    1591             : 
    1592             :   friend class ClientImpl;
    1593             : };
    1594             : #endif
    1595             : 
    1596             : /*
    1597             :  * Implementation of template methods.
    1598             :  */
    1599             : 
    1600             : namespace detail {
    1601             : 
    1602             : template <typename T, typename U>
    1603             : inline void duration_to_sec_and_usec(const T &duration, U callback) {
    1604             :   auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
    1605             :   auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
    1606             :                   duration - std::chrono::seconds(sec))
    1607             :                   .count();
    1608             :   callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
    1609             : }
    1610             : 
    1611             : template <typename T>
    1612             : inline T get_header_value(const Headers & /*headers*/,
    1613             :                           const std::string & /*key*/, size_t /*id*/ = 0,
    1614             :                           uint64_t /*def*/ = 0) {}
    1615             : 
    1616             : template <>
    1617           0 : inline uint64_t get_header_value<uint64_t>(const Headers &headers,
    1618             :                                            const std::string &key, size_t id,
    1619             :                                            uint64_t def) {
    1620           0 :   auto rng = headers.equal_range(key);
    1621           0 :   auto it = rng.first;
    1622           0 :   std::advance(it, static_cast<ssize_t>(id));
    1623           0 :   if (it != rng.second) {
    1624           0 :     return std::strtoull(it->second.data(), nullptr, 10);
    1625             :   }
    1626             :   return def;
    1627             : }
    1628             : 
    1629             : } // namespace detail
    1630             : 
    1631             : template <typename T>
    1632             : inline T Request::get_header_value(const std::string &key, size_t id) const {
    1633             :   return detail::get_header_value<T>(headers, key, id, 0);
    1634             : }
    1635             : 
    1636             : template <typename T>
    1637             : inline T Response::get_header_value(const std::string &key, size_t id) const {
    1638             :   return detail::get_header_value<T>(headers, key, id, 0);
    1639             : }
    1640             : 
    1641             : template <typename... Args>
    1642           0 : inline ssize_t Stream::write_format(const char *fmt, const Args &...args) {
    1643           0 :   const auto bufsiz = 2048;
    1644           0 :   std::array<char, bufsiz> buf{};
    1645             : 
    1646           0 :   auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
    1647           0 :   if (sn <= 0) { return sn; }
    1648             : 
    1649           0 :   auto n = static_cast<size_t>(sn);
    1650             : 
    1651           0 :   if (n >= buf.size() - 1) {
    1652           0 :     std::vector<char> glowable_buf(buf.size());
    1653             : 
    1654           0 :     while (n >= glowable_buf.size() - 1) {
    1655           0 :       glowable_buf.resize(glowable_buf.size() * 2);
    1656           0 :       n = static_cast<size_t>(
    1657           0 :           snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
    1658             :     }
    1659           0 :     return write(&glowable_buf[0], n);
    1660             :   } else {
    1661           0 :     return write(buf.data(), n);
    1662             :   }
    1663             : }
    1664             : 
    1665           0 : inline void default_socket_options(socket_t sock) {
    1666           0 :   int yes = 1;
    1667             : #ifdef _WIN32
    1668             :   setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
    1669             :              sizeof(yes));
    1670             :   setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
    1671             :              reinterpret_cast<char *>(&yes), sizeof(yes));
    1672             : #else
    1673             : #ifdef SO_REUSEPORT
    1674           0 :   setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<void *>(&yes),
    1675             :              sizeof(yes));
    1676             : #else
    1677             :   setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void *>(&yes),
    1678             :              sizeof(yes));
    1679             : #endif
    1680             : #endif
    1681           0 : }
    1682             : 
    1683             : template <class Rep, class Period>
    1684             : inline Server &
    1685             : Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
    1686             :   detail::duration_to_sec_and_usec(
    1687             :       duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
    1688             :   return *this;
    1689             : }
    1690             : 
    1691             : template <class Rep, class Period>
    1692             : inline Server &
    1693             : Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
    1694             :   detail::duration_to_sec_and_usec(
    1695             :       duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
    1696             :   return *this;
    1697             : }
    1698             : 
    1699             : template <class Rep, class Period>
    1700             : inline Server &
    1701             : Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
    1702             :   detail::duration_to_sec_and_usec(
    1703             :       duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
    1704             :   return *this;
    1705             : }
    1706             : 
    1707             : inline std::string to_string(const Error error) {
    1708             :   switch (error) {
    1709             :   case Error::Success: return "Success (no error)";
    1710             :   case Error::Connection: return "Could not establish connection";
    1711             :   case Error::BindIPAddress: return "Failed to bind IP address";
    1712             :   case Error::Read: return "Failed to read connection";
    1713             :   case Error::Write: return "Failed to write connection";
    1714             :   case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";
    1715             :   case Error::Canceled: return "Connection handling canceled";
    1716             :   case Error::SSLConnection: return "SSL connection failed";
    1717             :   case Error::SSLLoadingCerts: return "SSL certificate loading failed";
    1718             :   case Error::SSLServerVerification: return "SSL server verification failed";
    1719             :   case Error::UnsupportedMultipartBoundaryChars:
    1720             :     return "Unsupported HTTP multipart boundary characters";
    1721             :   case Error::Compression: return "Compression failed";
    1722             :   case Error::ConnectionTimeout: return "Connection timed out";
    1723             :   case Error::Unknown: return "Unknown";
    1724             :   default: break;
    1725             :   }
    1726             : 
    1727             :   return "Invalid";
    1728             : }
    1729             : 
    1730             : inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
    1731             :   os << to_string(obj);
    1732             :   os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
    1733             :   return os;
    1734             : }
    1735             : 
    1736             : template <typename T>
    1737             : inline T Result::get_request_header_value(const std::string &key,
    1738             :                                           size_t id) const {
    1739             :   return detail::get_header_value<T>(request_headers_, key, id, 0);
    1740             : }
    1741             : 
    1742             : template <class Rep, class Period>
    1743             : inline void ClientImpl::set_connection_timeout(
    1744             :     const std::chrono::duration<Rep, Period> &duration) {
    1745             :   detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
    1746             :     set_connection_timeout(sec, usec);
    1747             :   });
    1748             : }
    1749             : 
    1750             : template <class Rep, class Period>
    1751             : inline void ClientImpl::set_read_timeout(
    1752             :     const std::chrono::duration<Rep, Period> &duration) {
    1753             :   detail::duration_to_sec_and_usec(
    1754             :       duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
    1755             : }
    1756             : 
    1757             : template <class Rep, class Period>
    1758             : inline void ClientImpl::set_write_timeout(
    1759             :     const std::chrono::duration<Rep, Period> &duration) {
    1760             :   detail::duration_to_sec_and_usec(
    1761             :       duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
    1762             : }
    1763             : 
    1764             : template <class Rep, class Period>
    1765             : inline void Client::set_connection_timeout(
    1766             :     const std::chrono::duration<Rep, Period> &duration) {
    1767             :   cli_->set_connection_timeout(duration);
    1768             : }
    1769             : 
    1770             : template <class Rep, class Period>
    1771             : inline void
    1772             : Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
    1773             :   cli_->set_read_timeout(duration);
    1774             : }
    1775             : 
    1776             : template <class Rep, class Period>
    1777             : inline void
    1778             : Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
    1779             :   cli_->set_write_timeout(duration);
    1780             : }
    1781             : 
    1782             : /*
    1783             :  * Forward declarations and types that will be part of the .h file if split into
    1784             :  * .h + .cc.
    1785             :  */
    1786             : 
    1787             : std::string hosted_at(const std::string &hostname);
    1788             : 
    1789             : void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
    1790             : 
    1791             : std::string append_query_params(const std::string &path, const Params &params);
    1792             : 
    1793             : std::pair<std::string, std::string> make_range_header(Ranges ranges);
    1794             : 
    1795             : std::pair<std::string, std::string>
    1796             : make_basic_authentication_header(const std::string &username,
    1797             :                                  const std::string &password,
    1798             :                                  bool is_proxy = false);
    1799             : 
    1800             : namespace detail {
    1801             : 
    1802             : std::string encode_query_param(const std::string &value);
    1803             : 
    1804             : std::string decode_url(const std::string &s, bool convert_plus_to_space);
    1805             : 
    1806             : void read_file(const std::string &path, std::string &out);
    1807             : 
    1808             : std::string trim_copy(const std::string &s);
    1809             : 
    1810             : void split(const char *b, const char *e, char d,
    1811             :            std::function<void(const char *, const char *)> fn);
    1812             : 
    1813             : bool process_client_socket(socket_t sock, time_t read_timeout_sec,
    1814             :                            time_t read_timeout_usec, time_t write_timeout_sec,
    1815             :                            time_t write_timeout_usec,
    1816             :                            std::function<bool(Stream &)> callback);
    1817             : 
    1818             : socket_t create_client_socket(
    1819             :     const std::string &host, const std::string &ip, int port,
    1820             :     int address_family, bool tcp_nodelay, SocketOptions socket_options,
    1821             :     time_t connection_timeout_sec, time_t connection_timeout_usec,
    1822             :     time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
    1823             :     time_t write_timeout_usec, const std::string &intf, Error &error);
    1824             : 
    1825             : const char *get_header_value(const Headers &headers, const std::string &key,
    1826             :                              size_t id = 0, const char *def = nullptr);
    1827             : 
    1828             : std::string params_to_query_str(const Params &params);
    1829             : 
    1830             : void parse_query_text(const std::string &s, Params &params);
    1831             : 
    1832             : bool parse_multipart_boundary(const std::string &content_type,
    1833             :                               std::string &boundary);
    1834             : 
    1835             : bool parse_range_header(const std::string &s, Ranges &ranges);
    1836             : 
    1837             : int close_socket(socket_t sock);
    1838             : 
    1839             : ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
    1840             : 
    1841             : ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
    1842             : 
    1843             : enum class EncodingType { None = 0, Gzip, Brotli };
    1844             : 
    1845             : EncodingType encoding_type(const Request &req, const Response &res);
    1846             : 
    1847             : class BufferStream : public Stream {
    1848             : public:
    1849           0 :   BufferStream() = default;
    1850           0 :   ~BufferStream() override = default;
    1851             : 
    1852             :   bool is_readable() const override;
    1853             :   bool is_writable() const override;
    1854             :   ssize_t read(char *ptr, size_t size) override;
    1855             :   ssize_t write(const char *ptr, size_t size) override;
    1856             :   void get_remote_ip_and_port(std::string &ip, int &port) const override;
    1857             :   void get_local_ip_and_port(std::string &ip, int &port) const override;
    1858             :   socket_t socket() const override;
    1859             : 
    1860             :   const std::string &get_buffer() const;
    1861             : 
    1862             : private:
    1863             :   std::string buffer;
    1864             :   size_t position = 0;
    1865             : };
    1866             : 
    1867           0 : class compressor {
    1868             : public:
    1869           0 :   virtual ~compressor() = default;
    1870             : 
    1871             :   typedef std::function<bool(const char *data, size_t data_len)> Callback;
    1872             :   virtual bool compress(const char *data, size_t data_length, bool last,
    1873             :                         Callback callback) = 0;
    1874             : };
    1875             : 
    1876             : class decompressor {
    1877             : public:
    1878             :   virtual ~decompressor() = default;
    1879             : 
    1880             :   virtual bool is_valid() const = 0;
    1881             : 
    1882             :   typedef std::function<bool(const char *data, size_t data_len)> Callback;
    1883             :   virtual bool decompress(const char *data, size_t data_length,
    1884             :                           Callback callback) = 0;
    1885             : };
    1886             : 
    1887           0 : class nocompressor : public compressor {
    1888             : public:
    1889           0 :   virtual ~nocompressor() = default;
    1890             : 
    1891             :   bool compress(const char *data, size_t data_length, bool /*last*/,
    1892             :                 Callback callback) override;
    1893             : };
    1894             : 
    1895             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
    1896             : class gzip_compressor : public compressor {
    1897             : public:
    1898             :   gzip_compressor();
    1899             :   ~gzip_compressor();
    1900             : 
    1901             :   bool compress(const char *data, size_t data_length, bool last,
    1902             :                 Callback callback) override;
    1903             : 
    1904             : private:
    1905             :   bool is_valid_ = false;
    1906             :   z_stream strm_;
    1907             : };
    1908             : 
    1909             : class gzip_decompressor : public decompressor {
    1910             : public:
    1911             :   gzip_decompressor();
    1912             :   ~gzip_decompressor();
    1913             : 
    1914             :   bool is_valid() const override;
    1915             : 
    1916             :   bool decompress(const char *data, size_t data_length,
    1917             :                   Callback callback) override;
    1918             : 
    1919             : private:
    1920             :   bool is_valid_ = false;
    1921             :   z_stream strm_;
    1922             : };
    1923             : #endif
    1924             : 
    1925             : #ifdef CPPHTTPLIB_BROTLI_SUPPORT
    1926             : class brotli_compressor : public compressor {
    1927             : public:
    1928             :   brotli_compressor();
    1929             :   ~brotli_compressor();
    1930             : 
    1931             :   bool compress(const char *data, size_t data_length, bool last,
    1932             :                 Callback callback) override;
    1933             : 
    1934             : private:
    1935             :   BrotliEncoderState *state_ = nullptr;
    1936             : };
    1937             : 
    1938             : class brotli_decompressor : public decompressor {
    1939             : public:
    1940             :   brotli_decompressor();
    1941             :   ~brotli_decompressor();
    1942             : 
    1943             :   bool is_valid() const override;
    1944             : 
    1945             :   bool decompress(const char *data, size_t data_length,
    1946             :                   Callback callback) override;
    1947             : 
    1948             : private:
    1949             :   BrotliDecoderResult decoder_r;
    1950             :   BrotliDecoderState *decoder_s = nullptr;
    1951             : };
    1952             : #endif
    1953             : 
    1954             : // NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
    1955             : // to store data. The call can set memory on stack for performance.
    1956           0 : class stream_line_reader {
    1957             : public:
    1958             :   stream_line_reader(Stream &strm, char *fixed_buffer,
    1959             :                      size_t fixed_buffer_size);
    1960             :   const char *ptr() const;
    1961             :   size_t size() const;
    1962             :   bool end_with_crlf() const;
    1963             :   bool getline();
    1964             : 
    1965             : private:
    1966             :   void append(char c);
    1967             : 
    1968             :   Stream &strm_;
    1969             :   char *fixed_buffer_;
    1970             :   const size_t fixed_buffer_size_;
    1971             :   size_t fixed_buffer_used_size_ = 0;
    1972             :   std::string glowable_buffer_;
    1973             : };
    1974             : 
    1975             : } // namespace detail
    1976             : 
    1977             : // ----------------------------------------------------------------------------
    1978             : 
    1979             : /*
    1980             :  * Implementation that will be part of the .cc file if split into .h + .cc.
    1981             :  */
    1982             : 
    1983             : namespace detail {
    1984             : 
    1985           0 : inline bool is_hex(char c, int &v) {
    1986           0 :   if (0x20 <= c && isdigit(c)) {
    1987           0 :     v = c - '0';
    1988           0 :     return true;
    1989           0 :   } else if ('A' <= c && c <= 'F') {
    1990           0 :     v = c - 'A' + 10;
    1991           0 :     return true;
    1992           0 :   } else if ('a' <= c && c <= 'f') {
    1993           0 :     v = c - 'a' + 10;
    1994           0 :     return true;
    1995             :   }
    1996             :   return false;
    1997             : }
    1998             : 
    1999           0 : inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
    2000             :                           int &val) {
    2001           0 :   if (i >= s.size()) { return false; }
    2002             : 
    2003           0 :   val = 0;
    2004           0 :   for (; cnt; i++, cnt--) {
    2005           0 :     if (!s[i]) { return false; }
    2006           0 :     int v = 0;
    2007           0 :     if (is_hex(s[i], v)) {
    2008           0 :       val = val * 16 + v;
    2009             :     } else {
    2010             :       return false;
    2011             :     }
    2012             :   }
    2013             :   return true;
    2014             : }
    2015             : 
    2016           0 : inline std::string from_i_to_hex(size_t n) {
    2017           0 :   const char *charset = "0123456789abcdef";
    2018           0 :   std::string ret;
    2019           0 :   do {
    2020           0 :     ret = charset[n & 15] + ret;
    2021           0 :     n >>= 4;
    2022           0 :   } while (n > 0);
    2023           0 :   return ret;
    2024             : }
    2025             : 
    2026           0 : inline size_t to_utf8(int code, char *buff) {
    2027           0 :   if (code < 0x0080) {
    2028           0 :     buff[0] = (code & 0x7F);
    2029           0 :     return 1;
    2030           0 :   } else if (code < 0x0800) {
    2031           0 :     buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
    2032           0 :     buff[1] = static_cast<char>(0x80 | (code & 0x3F));
    2033           0 :     return 2;
    2034           0 :   } else if (code < 0xD800) {
    2035           0 :     buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
    2036           0 :     buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
    2037           0 :     buff[2] = static_cast<char>(0x80 | (code & 0x3F));
    2038           0 :     return 3;
    2039           0 :   } else if (code < 0xE000) { // D800 - DFFF is invalid...
    2040             :     return 0;
    2041           0 :   } else if (code < 0x10000) {
    2042           0 :     buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
    2043           0 :     buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
    2044           0 :     buff[2] = static_cast<char>(0x80 | (code & 0x3F));
    2045           0 :     return 3;
    2046           0 :   } else if (code < 0x110000) {
    2047           0 :     buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
    2048           0 :     buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
    2049           0 :     buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
    2050           0 :     buff[3] = static_cast<char>(0x80 | (code & 0x3F));
    2051           0 :     return 4;
    2052             :   }
    2053             : 
    2054             :   // NOTREACHED
    2055             :   return 0;
    2056             : }
    2057             : 
    2058             : // NOTE: This code came up with the following stackoverflow post:
    2059             : // https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
    2060             : inline std::string base64_encode(const std::string &in) {
    2061             :   static const auto lookup =
    2062             :       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    2063             : 
    2064             :   std::string out;
    2065             :   out.reserve(in.size());
    2066             : 
    2067             :   int val = 0;
    2068             :   int valb = -6;
    2069             : 
    2070             :   for (auto c : in) {
    2071             :     val = (val << 8) + static_cast<uint8_t>(c);
    2072             :     valb += 8;
    2073             :     while (valb >= 0) {
    2074             :       out.push_back(lookup[(val >> valb) & 0x3F]);
    2075             :       valb -= 6;
    2076             :     }
    2077             :   }
    2078             : 
    2079             :   if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
    2080             : 
    2081             :   while (out.size() % 4) {
    2082             :     out.push_back('=');
    2083             :   }
    2084             : 
    2085             :   return out;
    2086             : }
    2087             : 
    2088           0 : inline bool is_file(const std::string &path) {
    2089             : #ifdef _WIN32
    2090             :   return _access_s(path.c_str(), 0) == 0;
    2091             : #else
    2092           0 :   struct stat st;
    2093           0 :   return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
    2094             : #endif
    2095             : }
    2096             : 
    2097           0 : inline bool is_dir(const std::string &path) {
    2098           0 :   struct stat st;
    2099           0 :   return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
    2100             : }
    2101             : 
    2102           0 : inline bool is_valid_path(const std::string &path) {
    2103           0 :   size_t level = 0;
    2104           0 :   size_t i = 0;
    2105             : 
    2106             :   // Skip slash
    2107           0 :   while (i < path.size() && path[i] == '/') {
    2108           0 :     i++;
    2109             :   }
    2110             : 
    2111           0 :   while (i < path.size()) {
    2112             :     // Read component
    2113           0 :     auto beg = i;
    2114           0 :     while (i < path.size() && path[i] != '/') {
    2115           0 :       i++;
    2116             :     }
    2117             : 
    2118           0 :     auto len = i - beg;
    2119           0 :     assert(len > 0);
    2120             : 
    2121           0 :     if (!path.compare(beg, len, ".")) {
    2122             :       ;
    2123           0 :     } else if (!path.compare(beg, len, "..")) {
    2124           0 :       if (level == 0) { return false; }
    2125           0 :       level--;
    2126             :     } else {
    2127           0 :       level++;
    2128             :     }
    2129             : 
    2130             :     // Skip slash
    2131           0 :     while (i < path.size() && path[i] == '/') {
    2132           0 :       i++;
    2133             :     }
    2134             :   }
    2135             : 
    2136             :   return true;
    2137             : }
    2138             : 
    2139             : inline std::string encode_query_param(const std::string &value) {
    2140             :   std::ostringstream escaped;
    2141             :   escaped.fill('0');
    2142             :   escaped << std::hex;
    2143             : 
    2144             :   for (auto c : value) {
    2145             :     if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
    2146             :         c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
    2147             :         c == ')') {
    2148             :       escaped << c;
    2149             :     } else {
    2150             :       escaped << std::uppercase;
    2151             :       escaped << '%' << std::setw(2)
    2152             :               << static_cast<int>(static_cast<unsigned char>(c));
    2153             :       escaped << std::nouppercase;
    2154             :     }
    2155             :   }
    2156             : 
    2157             :   return escaped.str();
    2158             : }
    2159             : 
    2160             : inline std::string encode_url(const std::string &s) {
    2161             :   std::string result;
    2162             :   result.reserve(s.size());
    2163             : 
    2164             :   for (size_t i = 0; s[i]; i++) {
    2165             :     switch (s[i]) {
    2166             :     case ' ': result += "%20"; break;
    2167             :     case '+': result += "%2B"; break;
    2168             :     case '\r': result += "%0D"; break;
    2169             :     case '\n': result += "%0A"; break;
    2170             :     case '\'': result += "%27"; break;
    2171             :     case ',': result += "%2C"; break;
    2172             :     // case ':': result += "%3A"; break; // ok? probably...
    2173             :     case ';': result += "%3B"; break;
    2174             :     default:
    2175             :       auto c = static_cast<uint8_t>(s[i]);
    2176             :       if (c >= 0x80) {
    2177             :         result += '%';
    2178             :         char hex[4];
    2179             :         auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
    2180             :         assert(len == 2);
    2181             :         result.append(hex, static_cast<size_t>(len));
    2182             :       } else {
    2183             :         result += s[i];
    2184             :       }
    2185             :       break;
    2186             :     }
    2187             :   }
    2188             : 
    2189             :   return result;
    2190             : }
    2191             : 
    2192           0 : inline std::string decode_url(const std::string &s,
    2193             :                               bool convert_plus_to_space) {
    2194           0 :   std::string result;
    2195             : 
    2196           0 :   for (size_t i = 0; i < s.size(); i++) {
    2197           0 :     if (s[i] == '%' && i + 1 < s.size()) {
    2198           0 :       if (s[i + 1] == 'u') {
    2199           0 :         int val = 0;
    2200           0 :         if (from_hex_to_i(s, i + 2, 4, val)) {
    2201             :           // 4 digits Unicode codes
    2202           0 :           char buff[4];
    2203           0 :           size_t len = to_utf8(val, buff);
    2204           0 :           if (len > 0) { result.append(buff, len); }
    2205           0 :           i += 5; // 'u0000'
    2206             :         } else {
    2207           0 :           result += s[i];
    2208             :         }
    2209             :       } else {
    2210           0 :         int val = 0;
    2211           0 :         if (from_hex_to_i(s, i + 1, 2, val)) {
    2212             :           // 2 digits hex codes
    2213           0 :           result += static_cast<char>(val);
    2214           0 :           i += 2; // '00'
    2215             :         } else {
    2216           0 :           result += s[i];
    2217             :         }
    2218             :       }
    2219           0 :     } else if (convert_plus_to_space && s[i] == '+') {
    2220           0 :       result += ' ';
    2221             :     } else {
    2222           0 :       result += s[i];
    2223             :     }
    2224             :   }
    2225             : 
    2226           0 :   return result;
    2227             : }
    2228             : 
    2229           0 : inline void read_file(const std::string &path, std::string &out) {
    2230           0 :   std::ifstream fs(path, std::ios_base::binary);
    2231           0 :   fs.seekg(0, std::ios_base::end);
    2232           0 :   auto size = fs.tellg();
    2233           0 :   fs.seekg(0);
    2234           0 :   out.resize(static_cast<size_t>(size));
    2235           0 :   fs.read(&out[0], static_cast<std::streamsize>(size));
    2236           0 : }
    2237             : 
    2238           0 : inline std::string file_extension(const std::string &path) {
    2239           0 :   std::smatch m;
    2240           0 :   static auto re = std::regex("\\.([a-zA-Z0-9]+)$");
    2241           0 :   if (std::regex_search(path, m, re)) { return m[1].str(); }
    2242           0 :   return std::string();
    2243             : }
    2244             : 
    2245           0 : inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
    2246             : 
    2247           0 : inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
    2248             :                                       size_t right) {
    2249           0 :   while (b + left < e && is_space_or_tab(b[left])) {
    2250           0 :     left++;
    2251             :   }
    2252           0 :   while (right > 0 && is_space_or_tab(b[right - 1])) {
    2253             :     right--;
    2254             :   }
    2255           0 :   return std::make_pair(left, right);
    2256             : }
    2257             : 
    2258           0 : inline std::string trim_copy(const std::string &s) {
    2259           0 :   auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
    2260           0 :   return s.substr(r.first, r.second - r.first);
    2261             : }
    2262             : 
    2263           0 : inline void split(const char *b, const char *e, char d,
    2264             :                   std::function<void(const char *, const char *)> fn) {
    2265           0 :   size_t i = 0;
    2266           0 :   size_t beg = 0;
    2267             : 
    2268           0 :   while (e ? (b + i < e) : (b[i] != '\0')) {
    2269           0 :     if (b[i] == d) {
    2270           0 :       auto r = trim(b, e, beg, i);
    2271           0 :       if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
    2272           0 :       beg = i + 1;
    2273             :     }
    2274           0 :     i++;
    2275             :   }
    2276             : 
    2277           0 :   if (i) {
    2278           0 :     auto r = trim(b, e, beg, i);
    2279           0 :     if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
    2280             :   }
    2281           0 : }
    2282             : 
    2283           0 : inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
    2284             :                                               size_t fixed_buffer_size)
    2285             :     : strm_(strm), fixed_buffer_(fixed_buffer),
    2286           0 :       fixed_buffer_size_(fixed_buffer_size) {}
    2287             : 
    2288           0 : inline const char *stream_line_reader::ptr() const {
    2289           0 :   if (glowable_buffer_.empty()) {
    2290           0 :     return fixed_buffer_;
    2291             :   } else {
    2292           0 :     return glowable_buffer_.data();
    2293             :   }
    2294             : }
    2295             : 
    2296           0 : inline size_t stream_line_reader::size() const {
    2297           0 :   if (glowable_buffer_.empty()) {
    2298           0 :     return fixed_buffer_used_size_;
    2299             :   } else {
    2300             :     return glowable_buffer_.size();
    2301             :   }
    2302             : }
    2303             : 
    2304           0 : inline bool stream_line_reader::end_with_crlf() const {
    2305           0 :   auto end = ptr() + size();
    2306           0 :   return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
    2307             : }
    2308             : 
    2309           0 : inline bool stream_line_reader::getline() {
    2310           0 :   fixed_buffer_used_size_ = 0;
    2311           0 :   glowable_buffer_.clear();
    2312             : 
    2313           0 :   for (size_t i = 0;; i++) {
    2314           0 :     char byte;
    2315           0 :     auto n = strm_.read(&byte, 1);
    2316             : 
    2317           0 :     if (n < 0) {
    2318           0 :       return false;
    2319           0 :     } else if (n == 0) {
    2320           0 :       if (i == 0) {
    2321             :         return false;
    2322             :       } else {
    2323             :         break;
    2324             :       }
    2325             :     }
    2326             : 
    2327           0 :     append(byte);
    2328             : 
    2329           0 :     if (byte == '\n') { break; }
    2330           0 :   }
    2331             : 
    2332           0 :   return true;
    2333             : }
    2334             : 
    2335           0 : inline void stream_line_reader::append(char c) {
    2336           0 :   if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
    2337           0 :     fixed_buffer_[fixed_buffer_used_size_++] = c;
    2338           0 :     fixed_buffer_[fixed_buffer_used_size_] = '\0';
    2339             :   } else {
    2340           0 :     if (glowable_buffer_.empty()) {
    2341           0 :       assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
    2342           0 :       glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
    2343             :     }
    2344           0 :     glowable_buffer_ += c;
    2345             :   }
    2346           0 : }
    2347             : 
    2348           0 : inline int close_socket(socket_t sock) {
    2349             : #ifdef _WIN32
    2350             :   return closesocket(sock);
    2351             : #else
    2352           0 :   return close(sock);
    2353             : #endif
    2354             : }
    2355             : 
    2356           0 : template <typename T> inline ssize_t handle_EINTR(T fn) {
    2357           0 :   ssize_t res = false;
    2358           0 :   while (true) {
    2359           0 :     res = fn();
    2360           0 :     if (res < 0 && errno == EINTR) { continue; }
    2361             :     break;
    2362             :   }
    2363           0 :   return res;
    2364             : }
    2365             : 
    2366           0 : inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
    2367           0 :   return handle_EINTR([&]() {
    2368           0 :     return recv(sock,
    2369             : #ifdef _WIN32
    2370             :                 static_cast<char *>(ptr), static_cast<int>(size),
    2371             : #else
    2372           0 :                 ptr, size,
    2373             : #endif
    2374           0 :                 flags);
    2375             :   });
    2376             : }
    2377             : 
    2378           0 : inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
    2379             :                            int flags) {
    2380           0 :   return handle_EINTR([&]() {
    2381           0 :     return send(sock,
    2382             : #ifdef _WIN32
    2383             :                 static_cast<const char *>(ptr), static_cast<int>(size),
    2384             : #else
    2385           0 :                 ptr, size,
    2386             : #endif
    2387           0 :                 flags);
    2388             :   });
    2389             : }
    2390             : 
    2391           0 : inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
    2392             : #ifdef CPPHTTPLIB_USE_POLL
    2393             :   struct pollfd pfd_read;
    2394             :   pfd_read.fd = sock;
    2395             :   pfd_read.events = POLLIN;
    2396             : 
    2397             :   auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
    2398             : 
    2399             :   return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
    2400             : #else
    2401             : #ifndef _WIN32
    2402           0 :   if (sock >= FD_SETSIZE) { return 1; }
    2403             : #endif
    2404             : 
    2405           0 :   fd_set fds;
    2406           0 :   FD_ZERO(&fds);
    2407           0 :   FD_SET(sock, &fds);
    2408             : 
    2409           0 :   timeval tv;
    2410           0 :   tv.tv_sec = static_cast<long>(sec);
    2411           0 :   tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
    2412             : 
    2413           0 :   return handle_EINTR([&]() {
    2414           0 :     return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
    2415             :   });
    2416             : #endif
    2417             : }
    2418             : 
    2419           0 : inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
    2420             : #ifdef CPPHTTPLIB_USE_POLL
    2421             :   struct pollfd pfd_read;
    2422             :   pfd_read.fd = sock;
    2423             :   pfd_read.events = POLLOUT;
    2424             : 
    2425             :   auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
    2426             : 
    2427             :   return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
    2428             : #else
    2429             : #ifndef _WIN32
    2430           0 :   if (sock >= FD_SETSIZE) { return 1; }
    2431             : #endif
    2432             : 
    2433           0 :   fd_set fds;
    2434           0 :   FD_ZERO(&fds);
    2435           0 :   FD_SET(sock, &fds);
    2436             : 
    2437           0 :   timeval tv;
    2438           0 :   tv.tv_sec = static_cast<long>(sec);
    2439           0 :   tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
    2440             : 
    2441           0 :   return handle_EINTR([&]() {
    2442           0 :     return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
    2443             :   });
    2444             : #endif
    2445             : }
    2446             : 
    2447           0 : inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
    2448             :                                         time_t usec) {
    2449             : #ifdef CPPHTTPLIB_USE_POLL
    2450             :   struct pollfd pfd_read;
    2451             :   pfd_read.fd = sock;
    2452             :   pfd_read.events = POLLIN | POLLOUT;
    2453             : 
    2454             :   auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
    2455             : 
    2456             :   auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
    2457             : 
    2458             :   if (poll_res == 0) { return Error::ConnectionTimeout; }
    2459             : 
    2460             :   if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
    2461             :     int error = 0;
    2462             :     socklen_t len = sizeof(error);
    2463             :     auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
    2464             :                           reinterpret_cast<char *>(&error), &len);
    2465             :     auto successful = res >= 0 && !error;
    2466             :     return successful ? Error::Success : Error::Connection;
    2467             :   }
    2468             : 
    2469             :   return Error::Connection;
    2470             : #else
    2471             : #ifndef _WIN32
    2472           0 :   if (sock >= FD_SETSIZE) { return Error::Connection; }
    2473             : #endif
    2474             : 
    2475           0 :   fd_set fdsr;
    2476           0 :   FD_ZERO(&fdsr);
    2477           0 :   FD_SET(sock, &fdsr);
    2478             : 
    2479           0 :   auto fdsw = fdsr;
    2480           0 :   auto fdse = fdsr;
    2481             : 
    2482           0 :   timeval tv;
    2483           0 :   tv.tv_sec = static_cast<long>(sec);
    2484           0 :   tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
    2485             : 
    2486           0 :   auto ret = handle_EINTR([&]() {
    2487           0 :     return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
    2488             :   });
    2489             : 
    2490           0 :   if (ret == 0) { return Error::ConnectionTimeout; }
    2491             : 
    2492           0 :   if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
    2493           0 :     int error = 0;
    2494           0 :     socklen_t len = sizeof(error);
    2495           0 :     auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
    2496             :                           reinterpret_cast<char *>(&error), &len);
    2497           0 :     auto successful = res >= 0 && !error;
    2498           0 :     return successful ? Error::Success : Error::Connection;
    2499             :   }
    2500             :   return Error::Connection;
    2501             : #endif
    2502             : }
    2503             : 
    2504           0 : inline bool is_socket_alive(socket_t sock) {
    2505           0 :   const auto val = detail::select_read(sock, 0, 0);
    2506           0 :   if (val == 0) {
    2507             :     return true;
    2508           0 :   } else if (val < 0 && errno == EBADF) {
    2509             :     return false;
    2510             :   }
    2511           0 :   char buf[1];
    2512           0 :   return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
    2513             : }
    2514             : 
    2515             : class SocketStream : public Stream {
    2516             : public:
    2517             :   SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
    2518             :                time_t write_timeout_sec, time_t write_timeout_usec);
    2519             :   ~SocketStream() override;
    2520             : 
    2521             :   bool is_readable() const override;
    2522             :   bool is_writable() const override;
    2523             :   ssize_t read(char *ptr, size_t size) override;
    2524             :   ssize_t write(const char *ptr, size_t size) override;
    2525             :   void get_remote_ip_and_port(std::string &ip, int &port) const override;
    2526             :   void get_local_ip_and_port(std::string &ip, int &port) const override;
    2527             :   socket_t socket() const override;
    2528             : 
    2529             : private:
    2530             :   socket_t sock_;
    2531             :   time_t read_timeout_sec_;
    2532             :   time_t read_timeout_usec_;
    2533             :   time_t write_timeout_sec_;
    2534             :   time_t write_timeout_usec_;
    2535             : 
    2536             :   std::vector<char> read_buff_;
    2537             :   size_t read_buff_off_ = 0;
    2538             :   size_t read_buff_content_size_ = 0;
    2539             : 
    2540             :   static const size_t read_buff_size_ = 1024 * 4;
    2541             : };
    2542             : 
    2543             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    2544             : class SSLSocketStream : public Stream {
    2545             : public:
    2546             :   SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
    2547             :                   time_t read_timeout_usec, time_t write_timeout_sec,
    2548             :                   time_t write_timeout_usec);
    2549             :   ~SSLSocketStream() override;
    2550             : 
    2551             :   bool is_readable() const override;
    2552             :   bool is_writable() const override;
    2553             :   ssize_t read(char *ptr, size_t size) override;
    2554             :   ssize_t write(const char *ptr, size_t size) override;
    2555             :   void get_remote_ip_and_port(std::string &ip, int &port) const override;
    2556             :   void get_local_ip_and_port(std::string &ip, int &port) const override;
    2557             :   socket_t socket() const override;
    2558             : 
    2559             : private:
    2560             :   socket_t sock_;
    2561             :   SSL *ssl_;
    2562             :   time_t read_timeout_sec_;
    2563             :   time_t read_timeout_usec_;
    2564             :   time_t write_timeout_sec_;
    2565             :   time_t write_timeout_usec_;
    2566             : };
    2567             : #endif
    2568             : 
    2569           0 : inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) {
    2570           0 :   using namespace std::chrono;
    2571           0 :   auto start = steady_clock::now();
    2572           0 :   while (true) {
    2573           0 :     auto val = select_read(sock, 0, 10000);
    2574           0 :     if (val < 0) {
    2575             :       return false;
    2576           0 :     } else if (val == 0) {
    2577           0 :       auto current = steady_clock::now();
    2578           0 :       auto duration = duration_cast<milliseconds>(current - start);
    2579           0 :       auto timeout = keep_alive_timeout_sec * 1000;
    2580           0 :       if (duration.count() > timeout) { return false; }
    2581           0 :       std::this_thread::sleep_for(std::chrono::milliseconds(1));
    2582             :     } else {
    2583             :       return true;
    2584             :     }
    2585             :   }
    2586             : }
    2587             : 
    2588             : template <typename T>
    2589             : inline bool
    2590           0 : process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
    2591             :                            size_t keep_alive_max_count,
    2592             :                            time_t keep_alive_timeout_sec, T callback) {
    2593           0 :   assert(keep_alive_max_count > 0);
    2594             :   auto ret = false;
    2595             :   auto count = keep_alive_max_count;
    2596           0 :   while (svr_sock != INVALID_SOCKET && count > 0 &&
    2597           0 :          keep_alive(sock, keep_alive_timeout_sec)) {
    2598           0 :     auto close_connection = count == 1;
    2599           0 :     auto connection_closed = false;
    2600           0 :     ret = callback(close_connection, connection_closed);
    2601           0 :     if (!ret || connection_closed) { break; }
    2602           0 :     count--;
    2603             :   }
    2604           0 :   return ret;
    2605             : }
    2606             : 
    2607             : template <typename T>
    2608             : inline bool
    2609           0 : process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
    2610             :                       size_t keep_alive_max_count,
    2611             :                       time_t keep_alive_timeout_sec, time_t read_timeout_sec,
    2612             :                       time_t read_timeout_usec, time_t write_timeout_sec,
    2613             :                       time_t write_timeout_usec, T callback) {
    2614           0 :   return process_server_socket_core(
    2615             :       svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
    2616           0 :       [&](bool close_connection, bool &connection_closed) {
    2617           0 :         SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
    2618             :                           write_timeout_sec, write_timeout_usec);
    2619           0 :         return callback(strm, close_connection, connection_closed);
    2620             :       });
    2621             : }
    2622             : 
    2623           0 : inline bool process_client_socket(socket_t sock, time_t read_timeout_sec,
    2624             :                                   time_t read_timeout_usec,
    2625             :                                   time_t write_timeout_sec,
    2626             :                                   time_t write_timeout_usec,
    2627             :                                   std::function<bool(Stream &)> callback) {
    2628           0 :   SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
    2629           0 :                     write_timeout_sec, write_timeout_usec);
    2630           0 :   return callback(strm);
    2631             : }
    2632             : 
    2633           0 : inline int shutdown_socket(socket_t sock) {
    2634             : #ifdef _WIN32
    2635             :   return shutdown(sock, SD_BOTH);
    2636             : #else
    2637           0 :   return shutdown(sock, SHUT_RDWR);
    2638             : #endif
    2639             : }
    2640             : 
    2641             : template <typename BindOrConnect>
    2642           0 : socket_t create_socket(const std::string &host, const std::string &ip, int port,
    2643             :                        int address_family, int socket_flags, bool tcp_nodelay,
    2644             :                        SocketOptions socket_options,
    2645             :                        BindOrConnect bind_or_connect) {
    2646             :   // Get address info
    2647           0 :   const char *node = nullptr;
    2648             :   struct addrinfo hints;
    2649             :   struct addrinfo *result;
    2650             : 
    2651           0 :   memset(&hints, 0, sizeof(struct addrinfo));
    2652           0 :   hints.ai_socktype = SOCK_STREAM;
    2653           0 :   hints.ai_protocol = 0;
    2654             : 
    2655           0 :   if (!ip.empty()) {
    2656           0 :     node = ip.c_str();
    2657             :     // Ask getaddrinfo to convert IP in c-string to address
    2658           0 :     hints.ai_family = AF_UNSPEC;
    2659           0 :     hints.ai_flags = AI_NUMERICHOST;
    2660             :   } else {
    2661           0 :     if (!host.empty()) { node = host.c_str(); }
    2662           0 :     hints.ai_family = address_family;
    2663           0 :     hints.ai_flags = socket_flags;
    2664             :   }
    2665             : 
    2666             : #ifndef _WIN32
    2667           0 :   if (hints.ai_family == AF_UNIX) {
    2668           0 :     const auto addrlen = host.length();
    2669           0 :     if (addrlen > sizeof(sockaddr_un::sun_path)) return INVALID_SOCKET;
    2670             : 
    2671           0 :     auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
    2672           0 :     if (sock != INVALID_SOCKET) {
    2673           0 :       sockaddr_un addr{};
    2674           0 :       addr.sun_family = AF_UNIX;
    2675           0 :       std::copy(host.begin(), host.end(), addr.sun_path);
    2676             : 
    2677           0 :       hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
    2678           0 :       hints.ai_addrlen = static_cast<socklen_t>(
    2679           0 :           sizeof(addr) - sizeof(addr.sun_path) + addrlen);
    2680             : 
    2681           0 :       fcntl(sock, F_SETFD, FD_CLOEXEC);
    2682           0 :       if (socket_options) { socket_options(sock); }
    2683             : 
    2684           0 :       if (!bind_or_connect(sock, hints)) {
    2685           0 :         close_socket(sock);
    2686             :         sock = INVALID_SOCKET;
    2687             :       }
    2688             :     }
    2689           0 :     return sock;
    2690             :   }
    2691             : #endif
    2692             : 
    2693           0 :   auto service = std::to_string(port);
    2694             : 
    2695           0 :   if (getaddrinfo(node, service.c_str(), &hints, &result)) {
    2696             : #if defined __linux__ && !defined __ANDROID__
    2697           0 :     res_init();
    2698             : #endif
    2699           0 :     return INVALID_SOCKET;
    2700             :   }
    2701             : 
    2702           0 :   for (auto rp = result; rp; rp = rp->ai_next) {
    2703             :     // Create a socket
    2704             : #ifdef _WIN32
    2705             :     auto sock =
    2706             :         WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
    2707             :                    WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
    2708             :     /**
    2709             :      * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
    2710             :      * and above the socket creation fails on older Windows Systems.
    2711             :      *
    2712             :      * Let's try to create a socket the old way in this case.
    2713             :      *
    2714             :      * Reference:
    2715             :      * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
    2716             :      *
    2717             :      * WSA_FLAG_NO_HANDLE_INHERIT:
    2718             :      * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
    2719             :      * SP1, and later
    2720             :      *
    2721             :      */
    2722             :     if (sock == INVALID_SOCKET) {
    2723             :       sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
    2724             :     }
    2725             : #else
    2726           0 :     auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
    2727             : #endif
    2728           0 :     if (sock == INVALID_SOCKET) { continue; }
    2729             : 
    2730             : #ifndef _WIN32
    2731           0 :     if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
    2732           0 :       close_socket(sock);
    2733           0 :       continue;
    2734             :     }
    2735             : #endif
    2736             : 
    2737           0 :     if (tcp_nodelay) {
    2738           0 :       int yes = 1;
    2739           0 :       setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&yes),
    2740             :                  sizeof(yes));
    2741             :     }
    2742             : 
    2743           0 :     if (socket_options) { socket_options(sock); }
    2744             : 
    2745           0 :     if (rp->ai_family == AF_INET6) {
    2746           0 :       int no = 0;
    2747           0 :       setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char *>(&no),
    2748             :                  sizeof(no));
    2749             :     }
    2750             : 
    2751             :     // bind or connect
    2752           0 :     if (bind_or_connect(sock, *rp)) {
    2753           0 :       freeaddrinfo(result);
    2754           0 :       return sock;
    2755             :     }
    2756             : 
    2757           0 :     close_socket(sock);
    2758             :   }
    2759             : 
    2760           0 :   freeaddrinfo(result);
    2761           0 :   return INVALID_SOCKET;
    2762             : }
    2763             : 
    2764           0 : inline void set_nonblocking(socket_t sock, bool nonblocking) {
    2765             : #ifdef _WIN32
    2766             :   auto flags = nonblocking ? 1UL : 0UL;
    2767             :   ioctlsocket(sock, FIONBIO, &flags);
    2768             : #else
    2769           0 :   auto flags = fcntl(sock, F_GETFL, 0);
    2770           0 :   fcntl(sock, F_SETFL,
    2771             :         nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
    2772             : #endif
    2773           0 : }
    2774             : 
    2775           0 : inline bool is_connection_error() {
    2776             : #ifdef _WIN32
    2777             :   return WSAGetLastError() != WSAEWOULDBLOCK;
    2778             : #else
    2779           0 :   return errno != EINPROGRESS;
    2780             : #endif
    2781             : }
    2782             : 
    2783           0 : inline bool bind_ip_address(socket_t sock, const std::string &host) {
    2784           0 :   struct addrinfo hints;
    2785           0 :   struct addrinfo *result;
    2786             : 
    2787           0 :   memset(&hints, 0, sizeof(struct addrinfo));
    2788           0 :   hints.ai_family = AF_UNSPEC;
    2789           0 :   hints.ai_socktype = SOCK_STREAM;
    2790           0 :   hints.ai_protocol = 0;
    2791             : 
    2792           0 :   if (getaddrinfo(host.c_str(), "0", &hints, &result)) { return false; }
    2793             : 
    2794           0 :   auto ret = false;
    2795           0 :   for (auto rp = result; rp; rp = rp->ai_next) {
    2796           0 :     const auto &ai = *rp;
    2797           0 :     if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
    2798             :       ret = true;
    2799             :       break;
    2800             :     }
    2801             :   }
    2802             : 
    2803           0 :   freeaddrinfo(result);
    2804           0 :   return ret;
    2805             : }
    2806             : 
    2807             : #if !defined _WIN32 && !defined ANDROID && !defined _AIX
    2808             : #define USE_IF2IP
    2809             : #endif
    2810             : 
    2811             : #ifdef USE_IF2IP
    2812           0 : inline std::string if2ip(int address_family, const std::string &ifn) {
    2813           0 :   struct ifaddrs *ifap;
    2814           0 :   getifaddrs(&ifap);
    2815           0 :   std::string addr_candidate;
    2816           0 :   for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
    2817           0 :     if (ifa->ifa_addr && ifn == ifa->ifa_name &&
    2818           0 :         (AF_UNSPEC == address_family ||
    2819           0 :          ifa->ifa_addr->sa_family == address_family)) {
    2820           0 :       if (ifa->ifa_addr->sa_family == AF_INET) {
    2821           0 :         auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
    2822           0 :         char buf[INET_ADDRSTRLEN];
    2823           0 :         if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
    2824           0 :           freeifaddrs(ifap);
    2825           0 :           return std::string(buf, INET_ADDRSTRLEN);
    2826             :         }
    2827           0 :       } else if (ifa->ifa_addr->sa_family == AF_INET6) {
    2828           0 :         auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
    2829           0 :         if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
    2830           0 :           char buf[INET6_ADDRSTRLEN] = {};
    2831           0 :           if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
    2832             :             // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
    2833           0 :             auto s6_addr_head = sa->sin6_addr.s6_addr[0];
    2834           0 :             if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
    2835           0 :               addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
    2836             :             } else {
    2837           0 :               freeifaddrs(ifap);
    2838           0 :               return std::string(buf, INET6_ADDRSTRLEN);
    2839             :             }
    2840             :           }
    2841             :         }
    2842             :       }
    2843             :     }
    2844             :   }
    2845           0 :   freeifaddrs(ifap);
    2846           0 :   return addr_candidate;
    2847             : }
    2848             : #endif
    2849             : 
    2850           0 : inline socket_t create_client_socket(
    2851             :     const std::string &host, const std::string &ip, int port,
    2852             :     int address_family, bool tcp_nodelay, SocketOptions socket_options,
    2853             :     time_t connection_timeout_sec, time_t connection_timeout_usec,
    2854             :     time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
    2855             :     time_t write_timeout_usec, const std::string &intf, Error &error) {
    2856           0 :   auto sock = create_socket(
    2857           0 :       host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options),
    2858           0 :       [&](socket_t sock2, struct addrinfo &ai) -> bool {
    2859           0 :         if (!intf.empty()) {
    2860             : #ifdef USE_IF2IP
    2861           0 :           auto ip_from_if = if2ip(address_family, intf);
    2862           0 :           if (ip_from_if.empty()) { ip_from_if = intf; }
    2863           0 :           if (!bind_ip_address(sock2, ip_from_if.c_str())) {
    2864           0 :             error = Error::BindIPAddress;
    2865           0 :             return false;
    2866             :           }
    2867             : #endif
    2868             :         }
    2869             : 
    2870           0 :         set_nonblocking(sock2, true);
    2871             : 
    2872           0 :         auto ret =
    2873           0 :             ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
    2874             : 
    2875           0 :         if (ret < 0) {
    2876           0 :           if (is_connection_error()) {
    2877           0 :             error = Error::Connection;
    2878           0 :             return false;
    2879             :           }
    2880           0 :           error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
    2881           0 :                                              connection_timeout_usec);
    2882           0 :           if (error != Error::Success) { return false; }
    2883             :         }
    2884             : 
    2885           0 :         set_nonblocking(sock2, false);
    2886             : 
    2887           0 :         {
    2888             : #ifdef _WIN32
    2889             :           auto timeout = static_cast<uint32_t>(read_timeout_sec * 1000 +
    2890             :                                                read_timeout_usec / 1000);
    2891             :           setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
    2892             :                      sizeof(timeout));
    2893             : #else
    2894           0 :           timeval tv;
    2895           0 :           tv.tv_sec = static_cast<long>(read_timeout_sec);
    2896           0 :           tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
    2897           0 :           setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
    2898             : #endif
    2899             :         }
    2900           0 :         {
    2901             : 
    2902             : #ifdef _WIN32
    2903             :           auto timeout = static_cast<uint32_t>(write_timeout_sec * 1000 +
    2904             :                                                write_timeout_usec / 1000);
    2905             :           setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
    2906             :                      sizeof(timeout));
    2907             : #else
    2908           0 :           timeval tv;
    2909           0 :           tv.tv_sec = static_cast<long>(write_timeout_sec);
    2910           0 :           tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
    2911           0 :           setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
    2912             : #endif
    2913             :         }
    2914             : 
    2915           0 :         error = Error::Success;
    2916           0 :         return true;
    2917             :       });
    2918             : 
    2919           0 :   if (sock != INVALID_SOCKET) {
    2920           0 :     error = Error::Success;
    2921             :   } else {
    2922           0 :     if (error == Error::Success) { error = Error::Connection; }
    2923             :   }
    2924             : 
    2925           0 :   return sock;
    2926             : }
    2927             : 
    2928           0 : inline bool get_ip_and_port(const struct sockaddr_storage &addr,
    2929             :                             socklen_t addr_len, std::string &ip, int &port) {
    2930           0 :   if (addr.ss_family == AF_INET) {
    2931           0 :     port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
    2932           0 :   } else if (addr.ss_family == AF_INET6) {
    2933           0 :     port =
    2934           0 :         ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
    2935             :   } else {
    2936             :     return false;
    2937             :   }
    2938             : 
    2939           0 :   std::array<char, NI_MAXHOST> ipstr{};
    2940           0 :   if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
    2941             :                   ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
    2942             :                   0, NI_NUMERICHOST)) {
    2943             :     return false;
    2944             :   }
    2945             : 
    2946           0 :   ip = ipstr.data();
    2947             :   return true;
    2948             : }
    2949             : 
    2950           0 : inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {
    2951           0 :   struct sockaddr_storage addr;
    2952           0 :   socklen_t addr_len = sizeof(addr);
    2953           0 :   if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),
    2954             :                    &addr_len)) {
    2955           0 :     get_ip_and_port(addr, addr_len, ip, port);
    2956             :   }
    2957           0 : }
    2958             : 
    2959           0 : inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
    2960           0 :   struct sockaddr_storage addr;
    2961           0 :   socklen_t addr_len = sizeof(addr);
    2962             : 
    2963           0 :   if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
    2964             :                    &addr_len)) {
    2965             : #ifndef _WIN32
    2966           0 :     if (addr.ss_family == AF_UNIX) {
    2967             : #if defined(__linux__)
    2968           0 :       struct ucred ucred;
    2969           0 :       socklen_t len = sizeof(ucred);
    2970           0 :       if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
    2971           0 :         port = ucred.pid;
    2972             :       }
    2973             : #elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__
    2974             :       pid_t pid;
    2975             :       socklen_t len = sizeof(pid);
    2976             :       if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
    2977             :         port = pid;
    2978             :       }
    2979             : #endif
    2980           0 :       return;
    2981             :     }
    2982             : #endif
    2983           0 :     get_ip_and_port(addr, addr_len, ip, port);
    2984             :   }
    2985             : }
    2986             : 
    2987           0 : inline constexpr unsigned int str2tag_core(const char *s, size_t l,
    2988             :                                            unsigned int h) {
    2989           0 :   return (l == 0)
    2990           0 :              ? h
    2991           0 :              : str2tag_core(
    2992             :                    s + 1, l - 1,
    2993             :                    // Unsets the 6 high bits of h, therefore no overflow happens
    2994           0 :                    (((std::numeric_limits<unsigned int>::max)() >> 6) &
    2995           0 :                     h * 33) ^
    2996           0 :                        static_cast<unsigned char>(*s));
    2997             : }
    2998             : 
    2999           0 : inline unsigned int str2tag(const std::string &s) {
    3000           0 :   return str2tag_core(s.data(), s.size(), 0);
    3001             : }
    3002             : 
    3003             : namespace udl {
    3004             : 
    3005             : inline constexpr unsigned int operator"" _t(const char *s, size_t l) {
    3006             :   return str2tag_core(s, l, 0);
    3007             : }
    3008             : 
    3009             : } // namespace udl
    3010             : 
    3011             : inline const char *
    3012           0 : find_content_type(const std::string &path,
    3013             :                   const std::map<std::string, std::string> &user_data) {
    3014           0 :   auto ext = file_extension(path);
    3015             : 
    3016           0 :   auto it = user_data.find(ext);
    3017           0 :   if (it != user_data.end()) { return it->second.c_str(); }
    3018             : 
    3019           0 :   using udl::operator""_t;
    3020             : 
    3021           0 :   switch (str2tag(ext)) {
    3022             :   default: return nullptr;
    3023           0 :   case "css"_t: return "text/css";
    3024           0 :   case "csv"_t: return "text/csv";
    3025           0 :   case "htm"_t:
    3026           0 :   case "html"_t: return "text/html";
    3027           0 :   case "js"_t:
    3028           0 :   case "mjs"_t: return "text/javascript";
    3029           0 :   case "txt"_t: return "text/plain";
    3030           0 :   case "vtt"_t: return "text/vtt";
    3031             : 
    3032           0 :   case "apng"_t: return "image/apng";
    3033           0 :   case "avif"_t: return "image/avif";
    3034           0 :   case "bmp"_t: return "image/bmp";
    3035           0 :   case "gif"_t: return "image/gif";
    3036           0 :   case "png"_t: return "image/png";
    3037           0 :   case "svg"_t: return "image/svg+xml";
    3038           0 :   case "webp"_t: return "image/webp";
    3039           0 :   case "ico"_t: return "image/x-icon";
    3040           0 :   case "tif"_t: return "image/tiff";
    3041           0 :   case "tiff"_t: return "image/tiff";
    3042           0 :   case "jpg"_t:
    3043           0 :   case "jpeg"_t: return "image/jpeg";
    3044             : 
    3045           0 :   case "mp4"_t: return "video/mp4";
    3046           0 :   case "mpeg"_t: return "video/mpeg";
    3047           0 :   case "webm"_t: return "video/webm";
    3048             : 
    3049           0 :   case "mp3"_t: return "audio/mp3";
    3050           0 :   case "mpga"_t: return "audio/mpeg";
    3051           0 :   case "weba"_t: return "audio/webm";
    3052           0 :   case "wav"_t: return "audio/wave";
    3053             : 
    3054           0 :   case "otf"_t: return "font/otf";
    3055           0 :   case "ttf"_t: return "font/ttf";
    3056           0 :   case "woff"_t: return "font/woff";
    3057           0 :   case "woff2"_t: return "font/woff2";
    3058             : 
    3059           0 :   case "7z"_t: return "application/x-7z-compressed";
    3060           0 :   case "atom"_t: return "application/atom+xml";
    3061           0 :   case "pdf"_t: return "application/pdf";
    3062           0 :   case "json"_t: return "application/json";
    3063           0 :   case "rss"_t: return "application/rss+xml";
    3064           0 :   case "tar"_t: return "application/x-tar";
    3065           0 :   case "xht"_t:
    3066           0 :   case "xhtml"_t: return "application/xhtml+xml";
    3067           0 :   case "xslt"_t: return "application/xslt+xml";
    3068           0 :   case "xml"_t: return "application/xml";
    3069           0 :   case "gz"_t: return "application/gzip";
    3070           0 :   case "zip"_t: return "application/zip";
    3071           0 :   case "wasm"_t: return "application/wasm";
    3072             :   }
    3073             : }
    3074             : 
    3075           0 : inline const char *status_message(int status) {
    3076           0 :   switch (status) {
    3077             :   case 100: return "Continue";
    3078           0 :   case 101: return "Switching Protocol";
    3079           0 :   case 102: return "Processing";
    3080           0 :   case 103: return "Early Hints";
    3081           0 :   case 200: return "OK";
    3082           0 :   case 201: return "Created";
    3083           0 :   case 202: return "Accepted";
    3084           0 :   case 203: return "Non-Authoritative Information";
    3085           0 :   case 204: return "No Content";
    3086           0 :   case 205: return "Reset Content";
    3087           0 :   case 206: return "Partial Content";
    3088           0 :   case 207: return "Multi-Status";
    3089           0 :   case 208: return "Already Reported";
    3090           0 :   case 226: return "IM Used";
    3091           0 :   case 300: return "Multiple Choice";
    3092           0 :   case 301: return "Moved Permanently";
    3093           0 :   case 302: return "Found";
    3094           0 :   case 303: return "See Other";
    3095           0 :   case 304: return "Not Modified";
    3096           0 :   case 305: return "Use Proxy";
    3097           0 :   case 306: return "unused";
    3098           0 :   case 307: return "Temporary Redirect";
    3099           0 :   case 308: return "Permanent Redirect";
    3100           0 :   case 400: return "Bad Request";
    3101           0 :   case 401: return "Unauthorized";
    3102           0 :   case 402: return "Payment Required";
    3103           0 :   case 403: return "Forbidden";
    3104           0 :   case 404: return "Not Found";
    3105           0 :   case 405: return "Method Not Allowed";
    3106           0 :   case 406: return "Not Acceptable";
    3107           0 :   case 407: return "Proxy Authentication Required";
    3108           0 :   case 408: return "Request Timeout";
    3109           0 :   case 409: return "Conflict";
    3110           0 :   case 410: return "Gone";
    3111           0 :   case 411: return "Length Required";
    3112           0 :   case 412: return "Precondition Failed";
    3113           0 :   case 413: return "Payload Too Large";
    3114           0 :   case 414: return "URI Too Long";
    3115           0 :   case 415: return "Unsupported Media Type";
    3116           0 :   case 416: return "Range Not Satisfiable";
    3117           0 :   case 417: return "Expectation Failed";
    3118           0 :   case 418: return "I'm a teapot";
    3119           0 :   case 421: return "Misdirected Request";
    3120           0 :   case 422: return "Unprocessable Entity";
    3121           0 :   case 423: return "Locked";
    3122           0 :   case 424: return "Failed Dependency";
    3123           0 :   case 425: return "Too Early";
    3124           0 :   case 426: return "Upgrade Required";
    3125           0 :   case 428: return "Precondition Required";
    3126           0 :   case 429: return "Too Many Requests";
    3127           0 :   case 431: return "Request Header Fields Too Large";
    3128           0 :   case 451: return "Unavailable For Legal Reasons";
    3129           0 :   case 501: return "Not Implemented";
    3130           0 :   case 502: return "Bad Gateway";
    3131           0 :   case 503: return "Service Unavailable";
    3132           0 :   case 504: return "Gateway Timeout";
    3133           0 :   case 505: return "HTTP Version Not Supported";
    3134           0 :   case 506: return "Variant Also Negotiates";
    3135           0 :   case 507: return "Insufficient Storage";
    3136           0 :   case 508: return "Loop Detected";
    3137           0 :   case 510: return "Not Extended";
    3138           0 :   case 511: return "Network Authentication Required";
    3139             : 
    3140           0 :   default:
    3141           0 :   case 500: return "Internal Server Error";
    3142             :   }
    3143             : }
    3144             : 
    3145           0 : inline bool can_compress_content_type(const std::string &content_type) {
    3146           0 :   using udl::operator""_t;
    3147             : 
    3148           0 :   auto tag = str2tag(content_type);
    3149             : 
    3150           0 :   switch (tag) {
    3151             :   case "image/svg+xml"_t:
    3152             :   case "application/javascript"_t:
    3153             :   case "application/json"_t:
    3154             :   case "application/xml"_t:
    3155             :   case "application/protobuf"_t:
    3156             :   case "application/xhtml+xml"_t: return true;
    3157             : 
    3158           0 :   default:
    3159           0 :     return !content_type.rfind("text/", 0) && tag != "text/event-stream"_t;
    3160             :   }
    3161             : }
    3162             : 
    3163           0 : inline EncodingType encoding_type(const Request &req, const Response &res) {
    3164           0 :   auto ret =
    3165           0 :       detail::can_compress_content_type(res.get_header_value("Content-Type"));
    3166           0 :   if (!ret) { return EncodingType::None; }
    3167             : 
    3168           0 :   const auto &s = req.get_header_value("Accept-Encoding");
    3169           0 :   (void)(s);
    3170             : 
    3171             : #ifdef CPPHTTPLIB_BROTLI_SUPPORT
    3172             :   // TODO: 'Accept-Encoding' has br, not br;q=0
    3173             :   ret = s.find("br") != std::string::npos;
    3174             :   if (ret) { return EncodingType::Brotli; }
    3175             : #endif
    3176             : 
    3177             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
    3178             :   // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
    3179             :   ret = s.find("gzip") != std::string::npos;
    3180             :   if (ret) { return EncodingType::Gzip; }
    3181             : #endif
    3182             : 
    3183           0 :   return EncodingType::None;
    3184             : }
    3185             : 
    3186           0 : inline bool nocompressor::compress(const char *data, size_t data_length,
    3187             :                                    bool /*last*/, Callback callback) {
    3188           0 :   if (!data_length) { return true; }
    3189           0 :   return callback(data, data_length);
    3190             : }
    3191             : 
    3192             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
    3193             : inline gzip_compressor::gzip_compressor() {
    3194             :   std::memset(&strm_, 0, sizeof(strm_));
    3195             :   strm_.zalloc = Z_NULL;
    3196             :   strm_.zfree = Z_NULL;
    3197             :   strm_.opaque = Z_NULL;
    3198             : 
    3199             :   is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
    3200             :                            Z_DEFAULT_STRATEGY) == Z_OK;
    3201             : }
    3202             : 
    3203             : inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
    3204             : 
    3205             : inline bool gzip_compressor::compress(const char *data, size_t data_length,
    3206             :                                       bool last, Callback callback) {
    3207             :   assert(is_valid_);
    3208             : 
    3209             :   do {
    3210             :     constexpr size_t max_avail_in =
    3211             :         (std::numeric_limits<decltype(strm_.avail_in)>::max)();
    3212             : 
    3213             :     strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
    3214             :         (std::min)(data_length, max_avail_in));
    3215             :     strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
    3216             : 
    3217             :     data_length -= strm_.avail_in;
    3218             :     data += strm_.avail_in;
    3219             : 
    3220             :     auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
    3221             :     int ret = Z_OK;
    3222             : 
    3223             :     std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
    3224             :     do {
    3225             :       strm_.avail_out = static_cast<uInt>(buff.size());
    3226             :       strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
    3227             : 
    3228             :       ret = deflate(&strm_, flush);
    3229             :       if (ret == Z_STREAM_ERROR) { return false; }
    3230             : 
    3231             :       if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
    3232             :         return false;
    3233             :       }
    3234             :     } while (strm_.avail_out == 0);
    3235             : 
    3236             :     assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
    3237             :            (flush == Z_NO_FLUSH && ret == Z_OK));
    3238             :     assert(strm_.avail_in == 0);
    3239             :   } while (data_length > 0);
    3240             : 
    3241             :   return true;
    3242             : }
    3243             : 
    3244             : inline gzip_decompressor::gzip_decompressor() {
    3245             :   std::memset(&strm_, 0, sizeof(strm_));
    3246             :   strm_.zalloc = Z_NULL;
    3247             :   strm_.zfree = Z_NULL;
    3248             :   strm_.opaque = Z_NULL;
    3249             : 
    3250             :   // 15 is the value of wbits, which should be at the maximum possible value
    3251             :   // to ensure that any gzip stream can be decoded. The offset of 32 specifies
    3252             :   // that the stream type should be automatically detected either gzip or
    3253             :   // deflate.
    3254             :   is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
    3255             : }
    3256             : 
    3257             : inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
    3258             : 
    3259             : inline bool gzip_decompressor::is_valid() const { return is_valid_; }
    3260             : 
    3261             : inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
    3262             :                                           Callback callback) {
    3263             :   assert(is_valid_);
    3264             : 
    3265             :   int ret = Z_OK;
    3266             : 
    3267             :   do {
    3268             :     constexpr size_t max_avail_in =
    3269             :         (std::numeric_limits<decltype(strm_.avail_in)>::max)();
    3270             : 
    3271             :     strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
    3272             :         (std::min)(data_length, max_avail_in));
    3273             :     strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
    3274             : 
    3275             :     data_length -= strm_.avail_in;
    3276             :     data += strm_.avail_in;
    3277             : 
    3278             :     std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
    3279             :     while (strm_.avail_in > 0) {
    3280             :       strm_.avail_out = static_cast<uInt>(buff.size());
    3281             :       strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
    3282             : 
    3283             :       auto prev_avail_in = strm_.avail_in;
    3284             : 
    3285             :       ret = inflate(&strm_, Z_NO_FLUSH);
    3286             : 
    3287             :       if (prev_avail_in - strm_.avail_in == 0) { return false; }
    3288             : 
    3289             :       assert(ret != Z_STREAM_ERROR);
    3290             :       switch (ret) {
    3291             :       case Z_NEED_DICT:
    3292             :       case Z_DATA_ERROR:
    3293             :       case Z_MEM_ERROR: inflateEnd(&strm_); return false;
    3294             :       }
    3295             : 
    3296             :       if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
    3297             :         return false;
    3298             :       }
    3299             :     }
    3300             : 
    3301             :     if (ret != Z_OK && ret != Z_STREAM_END) return false;
    3302             : 
    3303             :   } while (data_length > 0);
    3304             : 
    3305             :   return true;
    3306             : }
    3307             : #endif
    3308             : 
    3309             : #ifdef CPPHTTPLIB_BROTLI_SUPPORT
    3310             : inline brotli_compressor::brotli_compressor() {
    3311             :   state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
    3312             : }
    3313             : 
    3314             : inline brotli_compressor::~brotli_compressor() {
    3315             :   BrotliEncoderDestroyInstance(state_);
    3316             : }
    3317             : 
    3318             : inline bool brotli_compressor::compress(const char *data, size_t data_length,
    3319             :                                         bool last, Callback callback) {
    3320             :   std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
    3321             : 
    3322             :   auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
    3323             :   auto available_in = data_length;
    3324             :   auto next_in = reinterpret_cast<const uint8_t *>(data);
    3325             : 
    3326             :   for (;;) {
    3327             :     if (last) {
    3328             :       if (BrotliEncoderIsFinished(state_)) { break; }
    3329             :     } else {
    3330             :       if (!available_in) { break; }
    3331             :     }
    3332             : 
    3333             :     auto available_out = buff.size();
    3334             :     auto next_out = buff.data();
    3335             : 
    3336             :     if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
    3337             :                                      &available_out, &next_out, nullptr)) {
    3338             :       return false;
    3339             :     }
    3340             : 
    3341             :     auto output_bytes = buff.size() - available_out;
    3342             :     if (output_bytes) {
    3343             :       callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
    3344             :     }
    3345             :   }
    3346             : 
    3347             :   return true;
    3348             : }
    3349             : 
    3350             : inline brotli_decompressor::brotli_decompressor() {
    3351             :   decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
    3352             :   decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
    3353             :                         : BROTLI_DECODER_RESULT_ERROR;
    3354             : }
    3355             : 
    3356             : inline brotli_decompressor::~brotli_decompressor() {
    3357             :   if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
    3358             : }
    3359             : 
    3360             : inline bool brotli_decompressor::is_valid() const { return decoder_s; }
    3361             : 
    3362             : inline bool brotli_decompressor::decompress(const char *data,
    3363             :                                             size_t data_length,
    3364             :                                             Callback callback) {
    3365             :   if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
    3366             :       decoder_r == BROTLI_DECODER_RESULT_ERROR) {
    3367             :     return 0;
    3368             :   }
    3369             : 
    3370             :   const uint8_t *next_in = (const uint8_t *)data;
    3371             :   size_t avail_in = data_length;
    3372             :   size_t total_out;
    3373             : 
    3374             :   decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
    3375             : 
    3376             :   std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
    3377             :   while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
    3378             :     char *next_out = buff.data();
    3379             :     size_t avail_out = buff.size();
    3380             : 
    3381             :     decoder_r = BrotliDecoderDecompressStream(
    3382             :         decoder_s, &avail_in, &next_in, &avail_out,
    3383             :         reinterpret_cast<uint8_t **>(&next_out), &total_out);
    3384             : 
    3385             :     if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
    3386             : 
    3387             :     if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
    3388             :   }
    3389             : 
    3390             :   return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
    3391             :          decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
    3392             : }
    3393             : #endif
    3394             : 
    3395           0 : inline bool has_header(const Headers &headers, const std::string &key) {
    3396           0 :   return headers.find(key) != headers.end();
    3397             : }
    3398             : 
    3399           0 : inline const char *get_header_value(const Headers &headers,
    3400             :                                     const std::string &key, size_t id,
    3401             :                                     const char *def) {
    3402           0 :   auto rng = headers.equal_range(key);
    3403           0 :   auto it = rng.first;
    3404           0 :   std::advance(it, static_cast<ssize_t>(id));
    3405           0 :   if (it != rng.second) { return it->second.c_str(); }
    3406             :   return def;
    3407             : }
    3408             : 
    3409           0 : inline bool compare_case_ignore(const std::string &a, const std::string &b) {
    3410           0 :   if (a.size() != b.size()) { return false; }
    3411           0 :   for (size_t i = 0; i < b.size(); i++) {
    3412           0 :     if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
    3413             :   }
    3414             :   return true;
    3415             : }
    3416             : 
    3417             : template <typename T>
    3418           0 : inline bool parse_header(const char *beg, const char *end, T fn) {
    3419             :   // Skip trailing spaces and tabs.
    3420           0 :   while (beg < end && is_space_or_tab(end[-1])) {
    3421           0 :     end--;
    3422             :   }
    3423             : 
    3424             :   auto p = beg;
    3425           0 :   while (p < end && *p != ':') {
    3426           0 :     p++;
    3427             :   }
    3428             : 
    3429           0 :   if (p == end) { return false; }
    3430             : 
    3431           0 :   auto key_end = p;
    3432             : 
    3433           0 :   if (*p++ != ':') { return false; }
    3434             : 
    3435           0 :   while (p < end && is_space_or_tab(*p)) {
    3436           0 :     p++;
    3437             :   }
    3438             : 
    3439           0 :   if (p < end) {
    3440           0 :     auto key = std::string(beg, key_end);
    3441           0 :     auto val = compare_case_ignore(key, "Location")
    3442             :                    ? std::string(p, end)
    3443             :                    : decode_url(std::string(p, end), false);
    3444           0 :     fn(std::move(key), std::move(val));
    3445           0 :     return true;
    3446             :   }
    3447             : 
    3448             :   return false;
    3449             : }
    3450             : 
    3451           0 : inline bool read_headers(Stream &strm, Headers &headers) {
    3452           0 :   const auto bufsiz = 2048;
    3453           0 :   char buf[bufsiz];
    3454           0 :   stream_line_reader line_reader(strm, buf, bufsiz);
    3455             : 
    3456           0 :   for (;;) {
    3457           0 :     if (!line_reader.getline()) { return false; }
    3458             : 
    3459             :     // Check if the line ends with CRLF.
    3460           0 :     auto line_terminator_len = 2;
    3461           0 :     if (line_reader.end_with_crlf()) {
    3462             :       // Blank line indicates end of headers.
    3463           0 :       if (line_reader.size() == 2) { break; }
    3464             : #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
    3465             :     } else {
    3466             :       // Blank line indicates end of headers.
    3467             :       if (line_reader.size() == 1) { break; }
    3468             :       line_terminator_len = 1;
    3469             :     }
    3470             : #else
    3471             :     } else {
    3472           0 :       continue; // Skip invalid line.
    3473             :     }
    3474             : #endif
    3475             : 
    3476           0 :     if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
    3477             : 
    3478             :     // Exclude line terminator
    3479           0 :     auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
    3480             : 
    3481           0 :     parse_header(line_reader.ptr(), end,
    3482           0 :                  [&](std::string &&key, std::string &&val) {
    3483           0 :                    headers.emplace(std::move(key), std::move(val));
    3484           0 :                  });
    3485             :   }
    3486             : 
    3487             :   return true;
    3488             : }
    3489             : 
    3490           0 : inline bool read_content_with_length(Stream &strm, uint64_t len,
    3491             :                                      Progress progress,
    3492             :                                      ContentReceiverWithProgress out) {
    3493           0 :   char buf[CPPHTTPLIB_RECV_BUFSIZ];
    3494             : 
    3495           0 :   uint64_t r = 0;
    3496           0 :   while (r < len) {
    3497           0 :     auto read_len = static_cast<size_t>(len - r);
    3498           0 :     auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
    3499           0 :     if (n <= 0) { return false; }
    3500             : 
    3501           0 :     if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
    3502           0 :     r += static_cast<uint64_t>(n);
    3503             : 
    3504           0 :     if (progress) {
    3505           0 :       if (!progress(r, len)) { return false; }
    3506             :     }
    3507             :   }
    3508             : 
    3509             :   return true;
    3510             : }
    3511             : 
    3512           0 : inline void skip_content_with_length(Stream &strm, uint64_t len) {
    3513           0 :   char buf[CPPHTTPLIB_RECV_BUFSIZ];
    3514           0 :   uint64_t r = 0;
    3515           0 :   while (r < len) {
    3516           0 :     auto read_len = static_cast<size_t>(len - r);
    3517           0 :     auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
    3518           0 :     if (n <= 0) { return; }
    3519           0 :     r += static_cast<uint64_t>(n);
    3520             :   }
    3521             : }
    3522             : 
    3523           0 : inline bool read_content_without_length(Stream &strm,
    3524             :                                         ContentReceiverWithProgress out) {
    3525           0 :   char buf[CPPHTTPLIB_RECV_BUFSIZ];
    3526           0 :   uint64_t r = 0;
    3527           0 :   for (;;) {
    3528           0 :     auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
    3529           0 :     if (n < 0) {
    3530             :       return false;
    3531           0 :     } else if (n == 0) {
    3532             :       return true;
    3533             :     }
    3534             : 
    3535           0 :     if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
    3536           0 :     r += static_cast<uint64_t>(n);
    3537           0 :   }
    3538             : 
    3539             :   return true;
    3540             : }
    3541             : 
    3542             : template <typename T>
    3543           0 : inline bool read_content_chunked(Stream &strm, T &x,
    3544             :                                  ContentReceiverWithProgress out) {
    3545           0 :   const auto bufsiz = 16;
    3546             :   char buf[bufsiz];
    3547             : 
    3548           0 :   stream_line_reader line_reader(strm, buf, bufsiz);
    3549             : 
    3550           0 :   if (!line_reader.getline()) { return false; }
    3551             : 
    3552             :   unsigned long chunk_len;
    3553           0 :   while (true) {
    3554             :     char *end_ptr;
    3555             : 
    3556           0 :     chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
    3557             : 
    3558           0 :     if (end_ptr == line_reader.ptr()) { return false; }
    3559           0 :     if (chunk_len == ULONG_MAX) { return false; }
    3560             : 
    3561           0 :     if (chunk_len == 0) { break; }
    3562             : 
    3563           0 :     if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
    3564             :       return false;
    3565             :     }
    3566             : 
    3567           0 :     if (!line_reader.getline()) { return false; }
    3568             : 
    3569           0 :     if (strcmp(line_reader.ptr(), "\r\n")) { return false; }
    3570             : 
    3571           0 :     if (!line_reader.getline()) { return false; }
    3572             :   }
    3573             : 
    3574             :   assert(chunk_len == 0);
    3575             : 
    3576             :   // Trailer
    3577           0 :   if (!line_reader.getline()) { return false; }
    3578             : 
    3579           0 :   while (strcmp(line_reader.ptr(), "\r\n")) {
    3580           0 :     if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
    3581             : 
    3582             :     // Exclude line terminator
    3583           0 :     constexpr auto line_terminator_len = 2;
    3584           0 :     auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
    3585             : 
    3586           0 :     parse_header(line_reader.ptr(), end,
    3587           0 :                  [&](std::string &&key, std::string &&val) {
    3588           0 :                    x.headers.emplace(std::move(key), std::move(val));
    3589             :                  });
    3590             : 
    3591           0 :     if (!line_reader.getline()) { return false; }
    3592             :   }
    3593             : 
    3594             :   return true;
    3595             : }
    3596             : 
    3597           0 : inline bool is_chunked_transfer_encoding(const Headers &headers) {
    3598           0 :   return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""),
    3599           0 :                      "chunked");
    3600             : }
    3601             : 
    3602             : template <typename T, typename U>
    3603           0 : bool prepare_content_receiver(T &x, int &status,
    3604             :                               ContentReceiverWithProgress receiver,
    3605             :                               bool decompress, U callback) {
    3606           0 :   if (decompress) {
    3607           0 :     std::string encoding = x.get_header_value("Content-Encoding");
    3608           0 :     std::unique_ptr<decompressor> decompressor;
    3609             : 
    3610           0 :     if (encoding == "gzip" || encoding == "deflate") {
    3611             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
    3612             :       decompressor = detail::make_unique<gzip_decompressor>();
    3613             : #else
    3614           0 :       status = 415;
    3615           0 :       return false;
    3616             : #endif
    3617           0 :     } else if (encoding.find("br") != std::string::npos) {
    3618             : #ifdef CPPHTTPLIB_BROTLI_SUPPORT
    3619             :       decompressor = detail::make_unique<brotli_decompressor>();
    3620             : #else
    3621           0 :       status = 415;
    3622           0 :       return false;
    3623             : #endif
    3624             :     }
    3625             : 
    3626           0 :     if (decompressor) {
    3627             :       if (decompressor->is_valid()) {
    3628             :         ContentReceiverWithProgress out = [&](const char *buf, size_t n,
    3629             :                                               uint64_t off, uint64_t len) {
    3630             :           return decompressor->decompress(buf, n,
    3631             :                                           [&](const char *buf2, size_t n2) {
    3632             :                                             return receiver(buf2, n2, off, len);
    3633             :                                           });
    3634             :         };
    3635             :         return callback(std::move(out));
    3636             :       } else {
    3637             :         status = 500;
    3638             :         return false;
    3639             :       }
    3640             :     }
    3641             :   }
    3642             : 
    3643           0 :   ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
    3644             :                                         uint64_t len) {
    3645           0 :     return receiver(buf, n, off, len);
    3646             :   };
    3647           0 :   return callback(std::move(out));
    3648             : }
    3649             : 
    3650             : template <typename T>
    3651           0 : bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
    3652             :                   Progress progress, ContentReceiverWithProgress receiver,
    3653             :                   bool decompress) {
    3654           0 :   return prepare_content_receiver(
    3655           0 :       x, status, std::move(receiver), decompress,
    3656           0 :       [&](const ContentReceiverWithProgress &out) {
    3657           0 :         auto ret = true;
    3658           0 :         auto exceed_payload_max_length = false;
    3659             : 
    3660           0 :         if (is_chunked_transfer_encoding(x.headers)) {
    3661           0 :           ret = read_content_chunked(strm, x, out);
    3662           0 :         } else if (!has_header(x.headers, "Content-Length")) {
    3663           0 :           ret = read_content_without_length(strm, out);
    3664             :         } else {
    3665           0 :           auto len = get_header_value<uint64_t>(x.headers, "Content-Length");
    3666           0 :           if (len > payload_max_length) {
    3667           0 :             exceed_payload_max_length = true;
    3668           0 :             skip_content_with_length(strm, len);
    3669           0 :             ret = false;
    3670           0 :           } else if (len > 0) {
    3671           0 :             ret = read_content_with_length(strm, len, std::move(progress), out);
    3672             :           }
    3673             :         }
    3674             : 
    3675           0 :         if (!ret) { status = exceed_payload_max_length ? 413 : 400; }
    3676           0 :         return ret;
    3677           0 :       });
    3678             : } // namespace detail
    3679             : 
    3680           0 : inline ssize_t write_headers(Stream &strm, const Headers &headers) {
    3681           0 :   ssize_t write_len = 0;
    3682           0 :   for (const auto &x : headers) {
    3683           0 :     auto len =
    3684           0 :         strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
    3685           0 :     if (len < 0) { return len; }
    3686           0 :     write_len += len;
    3687             :   }
    3688           0 :   auto len = strm.write("\r\n");
    3689           0 :   if (len < 0) { return len; }
    3690           0 :   write_len += len;
    3691           0 :   return write_len;
    3692             : }
    3693             : 
    3694           0 : inline bool write_data(Stream &strm, const char *d, size_t l) {
    3695           0 :   size_t offset = 0;
    3696           0 :   while (offset < l) {
    3697           0 :     auto length = strm.write(d + offset, l - offset);
    3698           0 :     if (length < 0) { return false; }
    3699           0 :     offset += static_cast<size_t>(length);
    3700             :   }
    3701             :   return true;
    3702             : }
    3703             : 
    3704             : template <typename T>
    3705           0 : inline bool write_content(Stream &strm, const ContentProvider &content_provider,
    3706             :                           size_t offset, size_t length, T is_shutting_down,
    3707             :                           Error &error) {
    3708           0 :   size_t end_offset = offset + length;
    3709           0 :   auto ok = true;
    3710           0 :   DataSink data_sink;
    3711             : 
    3712           0 :   data_sink.write = [&](const char *d, size_t l) -> bool {
    3713           0 :     if (ok) {
    3714           0 :       if (strm.is_writable() && write_data(strm, d, l)) {
    3715           0 :         offset += l;
    3716             :       } else {
    3717           0 :         ok = false;
    3718             :       }
    3719             :     }
    3720           0 :     return ok;
    3721             :   };
    3722             : 
    3723           0 :   while (offset < end_offset && !is_shutting_down()) {
    3724           0 :     if (!strm.is_writable()) {
    3725           0 :       error = Error::Write;
    3726           0 :       return false;
    3727           0 :     } else if (!content_provider(offset, end_offset - offset, data_sink)) {
    3728           0 :       error = Error::Canceled;
    3729           0 :       return false;
    3730           0 :     } else if (!ok) {
    3731           0 :       error = Error::Write;
    3732           0 :       return false;
    3733             :     }
    3734             :   }
    3735             : 
    3736           0 :   error = Error::Success;
    3737           0 :   return true;
    3738             : }
    3739             : 
    3740             : template <typename T>
    3741           0 : inline bool write_content(Stream &strm, const ContentProvider &content_provider,
    3742             :                           size_t offset, size_t length,
    3743             :                           const T &is_shutting_down) {
    3744           0 :   auto error = Error::Success;
    3745           0 :   return write_content(strm, content_provider, offset, length, is_shutting_down,
    3746             :                        error);
    3747             : }
    3748             : 
    3749             : template <typename T>
    3750             : inline bool
    3751           0 : write_content_without_length(Stream &strm,
    3752             :                              const ContentProvider &content_provider,
    3753             :                              const T &is_shutting_down) {
    3754           0 :   size_t offset = 0;
    3755           0 :   auto data_available = true;
    3756           0 :   auto ok = true;
    3757           0 :   DataSink data_sink;
    3758             : 
    3759           0 :   data_sink.write = [&](const char *d, size_t l) -> bool {
    3760           0 :     if (ok) {
    3761           0 :       offset += l;
    3762           0 :       if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; }
    3763             :     }
    3764           0 :     return ok;
    3765             :   };
    3766             : 
    3767           0 :   data_sink.done = [&](void) { data_available = false; };
    3768             : 
    3769           0 :   while (data_available && !is_shutting_down()) {
    3770           0 :     if (!strm.is_writable()) {
    3771             :       return false;
    3772           0 :     } else if (!content_provider(offset, 0, data_sink)) {
    3773             :       return false;
    3774           0 :     } else if (!ok) {
    3775             :       return false;
    3776             :     }
    3777             :   }
    3778             :   return true;
    3779             : }
    3780             : 
    3781             : template <typename T, typename U>
    3782             : inline bool
    3783           0 : write_content_chunked(Stream &strm, const ContentProvider &content_provider,
    3784             :                       const T &is_shutting_down, U &compressor, Error &error) {
    3785           0 :   size_t offset = 0;
    3786           0 :   auto data_available = true;
    3787           0 :   auto ok = true;
    3788           0 :   DataSink data_sink;
    3789             : 
    3790           0 :   data_sink.write = [&](const char *d, size_t l) -> bool {
    3791           0 :     if (ok) {
    3792           0 :       data_available = l > 0;
    3793           0 :       offset += l;
    3794             : 
    3795           0 :       std::string payload;
    3796           0 :       if (compressor.compress(d, l, false,
    3797           0 :                               [&](const char *data, size_t data_len) {
    3798           0 :                                 payload.append(data, data_len);
    3799             :                                 return true;
    3800             :                               })) {
    3801           0 :         if (!payload.empty()) {
    3802             :           // Emit chunked response header and footer for each chunk
    3803           0 :           auto chunk =
    3804             :               from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
    3805           0 :           if (!strm.is_writable() ||
    3806           0 :               !write_data(strm, chunk.data(), chunk.size())) {
    3807           0 :             ok = false;
    3808             :           }
    3809             :         }
    3810             :       } else {
    3811           0 :         ok = false;
    3812             :       }
    3813             :     }
    3814           0 :     return ok;
    3815             :   };
    3816             : 
    3817           0 :   auto done_with_trailer = [&](const Headers *trailer) {
    3818           0 :     if (!ok) { return; }
    3819             : 
    3820           0 :     data_available = false;
    3821             : 
    3822           0 :     std::string payload;
    3823           0 :     if (!compressor.compress(nullptr, 0, true,
    3824           0 :                              [&](const char *data, size_t data_len) {
    3825           0 :                                payload.append(data, data_len);
    3826             :                                return true;
    3827             :                              })) {
    3828           0 :       ok = false;
    3829           0 :       return;
    3830             :     }
    3831             : 
    3832           0 :     if (!payload.empty()) {
    3833             :       // Emit chunked response header and footer for each chunk
    3834           0 :       auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
    3835           0 :       if (!strm.is_writable() ||
    3836           0 :           !write_data(strm, chunk.data(), chunk.size())) {
    3837           0 :         ok = false;
    3838           0 :         return;
    3839             :       }
    3840             :     }
    3841             : 
    3842           0 :     static const std::string done_marker("0\r\n");
    3843           0 :     if (!write_data(strm, done_marker.data(), done_marker.size())) {
    3844           0 :       ok = false;
    3845             :     }
    3846             : 
    3847             :     // Trailer
    3848           0 :     if (trailer) {
    3849           0 :       for (const auto &kv : *trailer) {
    3850           0 :         std::string field_line = kv.first + ": " + kv.second + "\r\n";
    3851           0 :         if (!write_data(strm, field_line.data(), field_line.size())) {
    3852           0 :           ok = false;
    3853             :         }
    3854             :       }
    3855             :     }
    3856             : 
    3857           0 :     static const std::string crlf("\r\n");
    3858           0 :     if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; }
    3859             :   };
    3860             : 
    3861           0 :   data_sink.done = [&](void) { done_with_trailer(nullptr); };
    3862             : 
    3863           0 :   data_sink.done_with_trailer = [&](const Headers &trailer) {
    3864           0 :     done_with_trailer(&trailer);
    3865             :   };
    3866             : 
    3867           0 :   while (data_available && !is_shutting_down()) {
    3868           0 :     if (!strm.is_writable()) {
    3869           0 :       error = Error::Write;
    3870           0 :       return false;
    3871           0 :     } else if (!content_provider(offset, 0, data_sink)) {
    3872           0 :       error = Error::Canceled;
    3873           0 :       return false;
    3874           0 :     } else if (!ok) {
    3875           0 :       error = Error::Write;
    3876           0 :       return false;
    3877             :     }
    3878             :   }
    3879             : 
    3880           0 :   error = Error::Success;
    3881           0 :   return true;
    3882             : }
    3883             : 
    3884             : template <typename T, typename U>
    3885           0 : inline bool write_content_chunked(Stream &strm,
    3886             :                                   const ContentProvider &content_provider,
    3887             :                                   const T &is_shutting_down, U &compressor) {
    3888           0 :   auto error = Error::Success;
    3889           0 :   return write_content_chunked(strm, content_provider, is_shutting_down,
    3890             :                                compressor, error);
    3891             : }
    3892             : 
    3893             : template <typename T>
    3894             : inline bool redirect(T &cli, Request &req, Response &res,
    3895             :                      const std::string &path, const std::string &location,
    3896             :                      Error &error) {
    3897             :   Request new_req = req;
    3898             :   new_req.path = path;
    3899             :   new_req.redirect_count_ -= 1;
    3900             : 
    3901             :   if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) {
    3902             :     new_req.method = "GET";
    3903             :     new_req.body.clear();
    3904             :     new_req.headers.clear();
    3905             :   }
    3906             : 
    3907             :   Response new_res;
    3908             : 
    3909             :   auto ret = cli.send(new_req, new_res, error);
    3910             :   if (ret) {
    3911             :     req = new_req;
    3912             :     res = new_res;
    3913             :     res.location = location;
    3914             :   }
    3915             :   return ret;
    3916             : }
    3917             : 
    3918             : inline std::string params_to_query_str(const Params &params) {
    3919             :   std::string query;
    3920             : 
    3921             :   for (auto it = params.begin(); it != params.end(); ++it) {
    3922             :     if (it != params.begin()) { query += "&"; }
    3923             :     query += it->first;
    3924             :     query += "=";
    3925             :     query += encode_query_param(it->second);
    3926             :   }
    3927             :   return query;
    3928             : }
    3929             : 
    3930           0 : inline void parse_query_text(const std::string &s, Params &params) {
    3931           0 :   std::set<std::string> cache;
    3932           0 :   split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
    3933           0 :     std::string kv(b, e);
    3934           0 :     if (cache.find(kv) != cache.end()) { return; }
    3935           0 :     cache.insert(kv);
    3936             : 
    3937           0 :     std::string key;
    3938           0 :     std::string val;
    3939           0 :     split(b, e, '=', [&](const char *b2, const char *e2) {
    3940           0 :       if (key.empty()) {
    3941           0 :         key.assign(b2, e2);
    3942             :       } else {
    3943           0 :         val.assign(b2, e2);
    3944             :       }
    3945           0 :     });
    3946             : 
    3947           0 :     if (!key.empty()) {
    3948           0 :       params.emplace(decode_url(key, true), decode_url(val, true));
    3949             :     }
    3950             :   });
    3951           0 : }
    3952             : 
    3953           0 : inline bool parse_multipart_boundary(const std::string &content_type,
    3954             :                                      std::string &boundary) {
    3955           0 :   auto boundary_keyword = "boundary=";
    3956           0 :   auto pos = content_type.find(boundary_keyword);
    3957           0 :   if (pos == std::string::npos) { return false; }
    3958           0 :   auto end = content_type.find(';', pos);
    3959           0 :   auto beg = pos + strlen(boundary_keyword);
    3960           0 :   boundary = content_type.substr(beg, end - beg);
    3961           0 :   if (boundary.length() >= 2 && boundary.front() == '"' &&
    3962           0 :       boundary.back() == '"') {
    3963           0 :     boundary = boundary.substr(1, boundary.size() - 2);
    3964             :   }
    3965           0 :   return !boundary.empty();
    3966             : }
    3967             : 
    3968             : #ifdef CPPHTTPLIB_NO_EXCEPTIONS
    3969             : inline bool parse_range_header(const std::string &s, Ranges &ranges) {
    3970             : #else
    3971           0 : inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
    3972             : #endif
    3973           0 :   static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
    3974           0 :   std::smatch m;
    3975           0 :   if (std::regex_match(s, m, re_first_range)) {
    3976           0 :     auto pos = static_cast<size_t>(m.position(1));
    3977           0 :     auto len = static_cast<size_t>(m.length(1));
    3978           0 :     bool all_valid_ranges = true;
    3979           0 :     split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
    3980           0 :       if (!all_valid_ranges) return;
    3981           0 :       static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
    3982           0 :       std::cmatch cm;
    3983           0 :       if (std::regex_match(b, e, cm, re_another_range)) {
    3984           0 :         ssize_t first = -1;
    3985           0 :         if (!cm.str(1).empty()) {
    3986           0 :           first = static_cast<ssize_t>(std::stoll(cm.str(1)));
    3987             :         }
    3988             : 
    3989           0 :         ssize_t last = -1;
    3990           0 :         if (!cm.str(2).empty()) {
    3991           0 :           last = static_cast<ssize_t>(std::stoll(cm.str(2)));
    3992             :         }
    3993             : 
    3994           0 :         if (first != -1 && last != -1 && first > last) {
    3995           0 :           all_valid_ranges = false;
    3996           0 :           return;
    3997             :         }
    3998           0 :         ranges.emplace_back(std::make_pair(first, last));
    3999             :       }
    4000             :     });
    4001           0 :     return all_valid_ranges;
    4002             :   }
    4003             :   return false;
    4004             : #ifdef CPPHTTPLIB_NO_EXCEPTIONS
    4005             : }
    4006             : #else
    4007           0 : } catch (...) { return false; }
    4008             : #endif
    4009             : 
    4010             : class MultipartFormDataParser {
    4011             : public:
    4012           0 :   MultipartFormDataParser() = default;
    4013             : 
    4014           0 :   void set_boundary(std::string &&boundary) {
    4015           0 :     boundary_ = boundary;
    4016           0 :     dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
    4017           0 :     crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
    4018           0 :   }
    4019             : 
    4020           0 :   bool is_valid() const { return is_valid_; }
    4021             : 
    4022           0 :   bool parse(const char *buf, size_t n, const ContentReceiver &content_callback,
    4023             :              const MultipartContentHeader &header_callback) {
    4024             : 
    4025             :     // TODO: support 'filename*'
    4026           0 :     static const std::regex re_content_disposition(
    4027             :         R"~(^Content-Disposition:\s*form-data;\s*name="(.*?)"(?:;\s*filename="(.*?)")?(?:;\s*filename\*=\S+)?\s*$)~",
    4028           0 :         std::regex_constants::icase);
    4029             : 
    4030           0 :     buf_append(buf, n);
    4031             : 
    4032           0 :     while (buf_size() > 0) {
    4033           0 :       switch (state_) {
    4034           0 :       case 0: { // Initial boundary
    4035           0 :         buf_erase(buf_find(dash_boundary_crlf_));
    4036           0 :         if (dash_boundary_crlf_.size() > buf_size()) { return true; }
    4037           0 :         if (!buf_start_with(dash_boundary_crlf_)) { return false; }
    4038           0 :         buf_erase(dash_boundary_crlf_.size());
    4039           0 :         state_ = 1;
    4040           0 :         break;
    4041             :       }
    4042           0 :       case 1: { // New entry
    4043           0 :         clear_file_info();
    4044           0 :         state_ = 2;
    4045           0 :         break;
    4046             :       }
    4047           0 :       case 2: { // Headers
    4048           0 :         auto pos = buf_find(crlf_);
    4049           0 :         if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
    4050           0 :         while (pos < buf_size()) {
    4051             :           // Empty line
    4052           0 :           if (pos == 0) {
    4053           0 :             if (!header_callback(file_)) {
    4054           0 :               is_valid_ = false;
    4055           0 :               return false;
    4056             :             }
    4057           0 :             buf_erase(crlf_.size());
    4058           0 :             state_ = 3;
    4059           0 :             break;
    4060             :           }
    4061             : 
    4062           0 :           static const std::string header_name = "content-type:";
    4063           0 :           const auto header = buf_head(pos);
    4064           0 :           if (start_with_case_ignore(header, header_name)) {
    4065           0 :             file_.content_type = trim_copy(header.substr(header_name.size()));
    4066             :           } else {
    4067           0 :             std::smatch m;
    4068           0 :             if (std::regex_match(header, m, re_content_disposition)) {
    4069           0 :               file_.name = m[1];
    4070           0 :               file_.filename = m[2];
    4071             :             } else {
    4072           0 :               is_valid_ = false;
    4073           0 :               return false;
    4074             :             }
    4075             :           }
    4076           0 :           buf_erase(pos + crlf_.size());
    4077           0 :           pos = buf_find(crlf_);
    4078             :         }
    4079           0 :         if (state_ != 3) { return true; }
    4080             :         break;
    4081             :       }
    4082           0 :       case 3: { // Body
    4083           0 :         if (crlf_dash_boundary_.size() > buf_size()) { return true; }
    4084           0 :         auto pos = buf_find(crlf_dash_boundary_);
    4085           0 :         if (pos < buf_size()) {
    4086           0 :           if (!content_callback(buf_data(), pos)) {
    4087           0 :             is_valid_ = false;
    4088           0 :             return false;
    4089             :           }
    4090           0 :           buf_erase(pos + crlf_dash_boundary_.size());
    4091           0 :           state_ = 4;
    4092             :         } else {
    4093           0 :           auto len = buf_size() - crlf_dash_boundary_.size();
    4094           0 :           if (len > 0) {
    4095           0 :             if (!content_callback(buf_data(), len)) {
    4096           0 :               is_valid_ = false;
    4097           0 :               return false;
    4098             :             }
    4099           0 :             buf_erase(len);
    4100             :           }
    4101           0 :           return true;
    4102             :         }
    4103           0 :         break;
    4104             :       }
    4105           0 :       case 4: { // Boundary
    4106           0 :         if (crlf_.size() > buf_size()) { return true; }
    4107           0 :         if (buf_start_with(crlf_)) {
    4108           0 :           buf_erase(crlf_.size());
    4109           0 :           state_ = 1;
    4110             :         } else {
    4111           0 :           if (dash_crlf_.size() > buf_size()) { return true; }
    4112           0 :           if (buf_start_with(dash_crlf_)) {
    4113           0 :             buf_erase(dash_crlf_.size());
    4114           0 :             is_valid_ = true;
    4115           0 :             buf_erase(buf_size()); // Remove epilogue
    4116             :           } else {
    4117             :             return true;
    4118             :           }
    4119             :         }
    4120             :         break;
    4121             :       }
    4122             :       }
    4123             :     }
    4124             : 
    4125             :     return true;
    4126             :   }
    4127             : 
    4128             : private:
    4129           0 :   void clear_file_info() {
    4130           0 :     file_.name.clear();
    4131           0 :     file_.filename.clear();
    4132           0 :     file_.content_type.clear();
    4133             :   }
    4134             : 
    4135             :   bool start_with_case_ignore(const std::string &a,
    4136             :                               const std::string &b) const {
    4137             :     if (a.size() < b.size()) { return false; }
    4138             :     for (size_t i = 0; i < b.size(); i++) {
    4139             :       if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
    4140             :     }
    4141             :     return true;
    4142             :   }
    4143             : 
    4144             :   const std::string dash_ = "--";
    4145             :   const std::string crlf_ = "\r\n";
    4146             :   const std::string dash_crlf_ = "--\r\n";
    4147             :   std::string boundary_;
    4148             :   std::string dash_boundary_crlf_;
    4149             :   std::string crlf_dash_boundary_;
    4150             : 
    4151             :   size_t state_ = 0;
    4152             :   bool is_valid_ = false;
    4153             :   MultipartFormData file_;
    4154             : 
    4155             :   // Buffer
    4156             :   bool start_with(const std::string &a, size_t spos, size_t epos,
    4157             :                   const std::string &b) const {
    4158             :     if (epos - spos < b.size()) { return false; }
    4159           0 :     for (size_t i = 0; i < b.size(); i++) {
    4160           0 :       if (a[i + spos] != b[i]) { return false; }
    4161             :     }
    4162             :     return true;
    4163             :   }
    4164             : 
    4165           0 :   size_t buf_size() const { return buf_epos_ - buf_spos_; }
    4166             : 
    4167           0 :   const char *buf_data() const { return &buf_[buf_spos_]; }
    4168             : 
    4169           0 :   std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
    4170             : 
    4171             :   bool buf_start_with(const std::string &s) const {
    4172           0 :     return start_with(buf_, buf_spos_, buf_epos_, s);
    4173             :   }
    4174             : 
    4175           0 :   size_t buf_find(const std::string &s) const {
    4176           0 :     auto c = s.front();
    4177             : 
    4178           0 :     size_t off = buf_spos_;
    4179           0 :     while (off < buf_epos_) {
    4180             :       auto pos = off;
    4181           0 :       while (true) {
    4182           0 :         if (pos == buf_epos_) { return buf_size(); }
    4183           0 :         if (buf_[pos] == c) { break; }
    4184           0 :         pos++;
    4185             :       }
    4186             : 
    4187           0 :       auto remaining_size = buf_epos_ - pos;
    4188           0 :       if (s.size() > remaining_size) { return buf_size(); }
    4189             : 
    4190           0 :       if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
    4191             : 
    4192           0 :       off = pos + 1;
    4193             :     }
    4194             : 
    4195           0 :     return buf_size();
    4196             :   }
    4197             : 
    4198           0 :   void buf_append(const char *data, size_t n) {
    4199           0 :     auto remaining_size = buf_size();
    4200           0 :     if (remaining_size > 0 && buf_spos_ > 0) {
    4201           0 :       for (size_t i = 0; i < remaining_size; i++) {
    4202           0 :         buf_[i] = buf_[buf_spos_ + i];
    4203             :       }
    4204             :     }
    4205           0 :     buf_spos_ = 0;
    4206           0 :     buf_epos_ = remaining_size;
    4207             : 
    4208           0 :     if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
    4209             : 
    4210           0 :     for (size_t i = 0; i < n; i++) {
    4211           0 :       buf_[buf_epos_ + i] = data[i];
    4212             :     }
    4213           0 :     buf_epos_ += n;
    4214           0 :   }
    4215             : 
    4216           0 :   void buf_erase(size_t size) { buf_spos_ += size; }
    4217             : 
    4218             :   std::string buf_;
    4219             :   size_t buf_spos_ = 0;
    4220             :   size_t buf_epos_ = 0;
    4221             : };
    4222             : 
    4223             : inline std::string to_lower(const char *beg, const char *end) {
    4224             :   std::string out;
    4225             :   auto it = beg;
    4226             :   while (it != end) {
    4227             :     out += static_cast<char>(::tolower(*it));
    4228             :     it++;
    4229             :   }
    4230             :   return out;
    4231             : }
    4232             : 
    4233           0 : inline std::string make_multipart_data_boundary() {
    4234           0 :   static const char data[] =
    4235             :       "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    4236             : 
    4237             :   // std::random_device might actually be deterministic on some
    4238             :   // platforms, but due to lack of support in the c++ standard library,
    4239             :   // doing better requires either some ugly hacks or breaking portability.
    4240           0 :   std::random_device seed_gen;
    4241             : 
    4242             :   // Request 128 bits of entropy for initialization
    4243           0 :   std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
    4244           0 :   std::mt19937 engine(seed_sequence);
    4245             : 
    4246           0 :   std::string result = "--cpp-httplib-multipart-data-";
    4247             : 
    4248           0 :   for (auto i = 0; i < 16; i++) {
    4249           0 :     result += data[engine() % (sizeof(data) - 1)];
    4250             :   }
    4251             : 
    4252           0 :   return result;
    4253             : }
    4254             : 
    4255             : inline bool is_multipart_boundary_chars_valid(const std::string &boundary) {
    4256             :   auto valid = true;
    4257             :   for (size_t i = 0; i < boundary.size(); i++) {
    4258             :     auto c = boundary[i];
    4259             :     if (!std::isalnum(c) && c != '-' && c != '_') {
    4260             :       valid = false;
    4261             :       break;
    4262             :     }
    4263             :   }
    4264             :   return valid;
    4265             : }
    4266             : 
    4267             : template <typename T>
    4268             : inline std::string
    4269             : serialize_multipart_formdata_item_begin(const T &item,
    4270             :                                         const std::string &boundary) {
    4271             :   std::string body = "--" + boundary + "\r\n";
    4272             :   body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
    4273             :   if (!item.filename.empty()) {
    4274             :     body += "; filename=\"" + item.filename + "\"";
    4275             :   }
    4276             :   body += "\r\n";
    4277             :   if (!item.content_type.empty()) {
    4278             :     body += "Content-Type: " + item.content_type + "\r\n";
    4279             :   }
    4280             :   body += "\r\n";
    4281             : 
    4282             :   return body;
    4283             : }
    4284             : 
    4285             : inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; }
    4286             : 
    4287             : inline std::string
    4288             : serialize_multipart_formdata_finish(const std::string &boundary) {
    4289             :   return "--" + boundary + "--\r\n";
    4290             : }
    4291             : 
    4292             : inline std::string
    4293             : serialize_multipart_formdata_get_content_type(const std::string &boundary) {
    4294             :   return "multipart/form-data; boundary=" + boundary;
    4295             : }
    4296             : 
    4297             : inline std::string
    4298             : serialize_multipart_formdata(const MultipartFormDataItems &items,
    4299             :                              const std::string &boundary, bool finish = true) {
    4300             :   std::string body;
    4301             : 
    4302             :   for (const auto &item : items) {
    4303             :     body += serialize_multipart_formdata_item_begin(item, boundary);
    4304             :     body += item.content + serialize_multipart_formdata_item_end();
    4305             :   }
    4306             : 
    4307             :   if (finish) body += serialize_multipart_formdata_finish(boundary);
    4308             : 
    4309             :   return body;
    4310             : }
    4311             : 
    4312             : inline std::pair<size_t, size_t>
    4313           0 : get_range_offset_and_length(const Request &req, size_t content_length,
    4314             :                             size_t index) {
    4315           0 :   auto r = req.ranges[index];
    4316             : 
    4317           0 :   if (r.first == -1 && r.second == -1) {
    4318           0 :     return std::make_pair(0, content_length);
    4319             :   }
    4320             : 
    4321           0 :   auto slen = static_cast<ssize_t>(content_length);
    4322             : 
    4323           0 :   if (r.first == -1) {
    4324           0 :     r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);
    4325           0 :     r.second = slen - 1;
    4326             :   }
    4327             : 
    4328           0 :   if (r.second == -1) { r.second = slen - 1; }
    4329           0 :   return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
    4330             : }
    4331             : 
    4332           0 : inline std::string make_content_range_header_field(size_t offset, size_t length,
    4333             :                                                    size_t content_length) {
    4334           0 :   std::string field = "bytes ";
    4335           0 :   field += std::to_string(offset);
    4336           0 :   field += "-";
    4337           0 :   field += std::to_string(offset + length - 1);
    4338           0 :   field += "/";
    4339           0 :   field += std::to_string(content_length);
    4340           0 :   return field;
    4341             : }
    4342             : 
    4343             : template <typename SToken, typename CToken, typename Content>
    4344           0 : bool process_multipart_ranges_data(const Request &req, Response &res,
    4345             :                                    const std::string &boundary,
    4346             :                                    const std::string &content_type,
    4347             :                                    SToken stoken, CToken ctoken,
    4348             :                                    Content content) {
    4349           0 :   for (size_t i = 0; i < req.ranges.size(); i++) {
    4350           0 :     ctoken("--");
    4351           0 :     stoken(boundary);
    4352           0 :     ctoken("\r\n");
    4353           0 :     if (!content_type.empty()) {
    4354           0 :       ctoken("Content-Type: ");
    4355           0 :       stoken(content_type);
    4356           0 :       ctoken("\r\n");
    4357             :     }
    4358             : 
    4359           0 :     auto offsets = get_range_offset_and_length(req, res.body.size(), i);
    4360           0 :     auto offset = offsets.first;
    4361           0 :     auto length = offsets.second;
    4362             : 
    4363           0 :     ctoken("Content-Range: ");
    4364           0 :     stoken(make_content_range_header_field(offset, length, res.body.size()));
    4365           0 :     ctoken("\r\n");
    4366           0 :     ctoken("\r\n");
    4367           0 :     if (!content(offset, length)) { return false; }
    4368           0 :     ctoken("\r\n");
    4369             :   }
    4370             : 
    4371           0 :   ctoken("--");
    4372           0 :   stoken(boundary);
    4373           0 :   ctoken("--\r\n");
    4374             : 
    4375           0 :   return true;
    4376             : }
    4377             : 
    4378           0 : inline bool make_multipart_ranges_data(const Request &req, Response &res,
    4379             :                                        const std::string &boundary,
    4380             :                                        const std::string &content_type,
    4381             :                                        std::string &data) {
    4382           0 :   return process_multipart_ranges_data(
    4383             :       req, res, boundary, content_type,
    4384           0 :       [&](const std::string &token) { data += token; },
    4385           0 :       [&](const std::string &token) { data += token; },
    4386           0 :       [&](size_t offset, size_t length) {
    4387           0 :         if (offset < res.body.size()) {
    4388           0 :           data += res.body.substr(offset, length);
    4389           0 :           return true;
    4390             :         }
    4391             :         return false;
    4392             :       });
    4393             : }
    4394             : 
    4395             : inline size_t
    4396           0 : get_multipart_ranges_data_length(const Request &req, Response &res,
    4397             :                                  const std::string &boundary,
    4398             :                                  const std::string &content_type) {
    4399           0 :   size_t data_length = 0;
    4400             : 
    4401           0 :   process_multipart_ranges_data(
    4402             :       req, res, boundary, content_type,
    4403           0 :       [&](const std::string &token) { data_length += token.size(); },
    4404           0 :       [&](const std::string &token) { data_length += token.size(); },
    4405           0 :       [&](size_t /*offset*/, size_t length) {
    4406           0 :         data_length += length;
    4407           0 :         return true;
    4408             :       });
    4409             : 
    4410           0 :   return data_length;
    4411             : }
    4412             : 
    4413             : template <typename T>
    4414           0 : inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
    4415             :                                         Response &res,
    4416             :                                         const std::string &boundary,
    4417             :                                         const std::string &content_type,
    4418             :                                         const T &is_shutting_down) {
    4419           0 :   return process_multipart_ranges_data(
    4420             :       req, res, boundary, content_type,
    4421           0 :       [&](const std::string &token) { strm.write(token); },
    4422           0 :       [&](const std::string &token) { strm.write(token); },
    4423           0 :       [&](size_t offset, size_t length) {
    4424           0 :         return write_content(strm, res.content_provider_, offset, length,
    4425           0 :                              is_shutting_down);
    4426             :       });
    4427             : }
    4428             : 
    4429             : inline std::pair<size_t, size_t>
    4430             : get_range_offset_and_length(const Request &req, const Response &res,
    4431             :                             size_t index) {
    4432             :   auto r = req.ranges[index];
    4433             : 
    4434             :   if (r.second == -1) {
    4435             :     r.second = static_cast<ssize_t>(res.content_length_) - 1;
    4436             :   }
    4437             : 
    4438             :   return std::make_pair(r.first, r.second - r.first + 1);
    4439             : }
    4440             : 
    4441           0 : inline bool expect_content(const Request &req) {
    4442           0 :   if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
    4443           0 :       req.method == "PRI" || req.method == "DELETE") {
    4444           0 :     return true;
    4445             :   }
    4446             :   // TODO: check if Content-Length is set
    4447             :   return false;
    4448             : }
    4449             : 
    4450           0 : inline bool has_crlf(const std::string &s) {
    4451           0 :   auto p = s.c_str();
    4452           0 :   while (*p) {
    4453           0 :     if (*p == '\r' || *p == '\n') { return true; }
    4454           0 :     p++;
    4455             :   }
    4456             :   return false;
    4457             : }
    4458             : 
    4459             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    4460             : inline std::string message_digest(const std::string &s, const EVP_MD *algo) {
    4461             :   auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
    4462             :       EVP_MD_CTX_new(), EVP_MD_CTX_free);
    4463             : 
    4464             :   unsigned int hash_length = 0;
    4465             :   unsigned char hash[EVP_MAX_MD_SIZE];
    4466             : 
    4467             :   EVP_DigestInit_ex(context.get(), algo, nullptr);
    4468             :   EVP_DigestUpdate(context.get(), s.c_str(), s.size());
    4469             :   EVP_DigestFinal_ex(context.get(), hash, &hash_length);
    4470             : 
    4471             :   std::stringstream ss;
    4472             :   for (auto i = 0u; i < hash_length; ++i) {
    4473             :     ss << std::hex << std::setw(2) << std::setfill('0')
    4474             :        << (unsigned int)hash[i];
    4475             :   }
    4476             : 
    4477             :   return ss.str();
    4478             : }
    4479             : 
    4480             : inline std::string MD5(const std::string &s) {
    4481             :   return message_digest(s, EVP_md5());
    4482             : }
    4483             : 
    4484             : inline std::string SHA_256(const std::string &s) {
    4485             :   return message_digest(s, EVP_sha256());
    4486             : }
    4487             : 
    4488             : inline std::string SHA_512(const std::string &s) {
    4489             :   return message_digest(s, EVP_sha512());
    4490             : }
    4491             : #endif
    4492             : 
    4493             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    4494             : #ifdef _WIN32
    4495             : // NOTE: This code came up with the following stackoverflow post:
    4496             : // https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
    4497             : inline bool load_system_certs_on_windows(X509_STORE *store) {
    4498             :   auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
    4499             :   if (!hStore) { return false; }
    4500             : 
    4501             :   auto result = false;
    4502             :   PCCERT_CONTEXT pContext = NULL;
    4503             :   while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
    4504             :          nullptr) {
    4505             :     auto encoded_cert =
    4506             :         static_cast<const unsigned char *>(pContext->pbCertEncoded);
    4507             : 
    4508             :     auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
    4509             :     if (x509) {
    4510             :       X509_STORE_add_cert(store, x509);
    4511             :       X509_free(x509);
    4512             :       result = true;
    4513             :     }
    4514             :   }
    4515             : 
    4516             :   CertFreeCertificateContext(pContext);
    4517             :   CertCloseStore(hStore, 0);
    4518             : 
    4519             :   return result;
    4520             : }
    4521             : #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
    4522             : #if TARGET_OS_OSX
    4523             : template <typename T>
    4524             : using CFObjectPtr =
    4525             :     std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
    4526             : 
    4527             : inline void cf_object_ptr_deleter(CFTypeRef obj) {
    4528             :   if (obj) { CFRelease(obj); }
    4529             : }
    4530             : 
    4531             : inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
    4532             :   CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
    4533             :   CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
    4534             :                         kCFBooleanTrue};
    4535             : 
    4536             :   CFObjectPtr<CFDictionaryRef> query(
    4537             :       CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
    4538             :                          sizeof(keys) / sizeof(keys[0]),
    4539             :                          &kCFTypeDictionaryKeyCallBacks,
    4540             :                          &kCFTypeDictionaryValueCallBacks),
    4541             :       cf_object_ptr_deleter);
    4542             : 
    4543             :   if (!query) { return false; }
    4544             : 
    4545             :   CFTypeRef security_items = nullptr;
    4546             :   if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
    4547             :       CFArrayGetTypeID() != CFGetTypeID(security_items)) {
    4548             :     return false;
    4549             :   }
    4550             : 
    4551             :   certs.reset(reinterpret_cast<CFArrayRef>(security_items));
    4552             :   return true;
    4553             : }
    4554             : 
    4555             : inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
    4556             :   CFArrayRef root_security_items = nullptr;
    4557             :   if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
    4558             :     return false;
    4559             :   }
    4560             : 
    4561             :   certs.reset(root_security_items);
    4562             :   return true;
    4563             : }
    4564             : 
    4565             : inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
    4566             :   auto result = false;
    4567             :   for (int i = 0; i < CFArrayGetCount(certs); ++i) {
    4568             :     const auto cert = reinterpret_cast<const __SecCertificate *>(
    4569             :         CFArrayGetValueAtIndex(certs, i));
    4570             : 
    4571             :     if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
    4572             : 
    4573             :     CFDataRef cert_data = nullptr;
    4574             :     if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
    4575             :         errSecSuccess) {
    4576             :       continue;
    4577             :     }
    4578             : 
    4579             :     CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
    4580             : 
    4581             :     auto encoded_cert = static_cast<const unsigned char *>(
    4582             :         CFDataGetBytePtr(cert_data_ptr.get()));
    4583             : 
    4584             :     auto x509 =
    4585             :         d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
    4586             : 
    4587             :     if (x509) {
    4588             :       X509_STORE_add_cert(store, x509);
    4589             :       X509_free(x509);
    4590             :       result = true;
    4591             :     }
    4592             :   }
    4593             : 
    4594             :   return result;
    4595             : }
    4596             : 
    4597             : inline bool load_system_certs_on_macos(X509_STORE *store) {
    4598             :   auto result = false;
    4599             :   CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
    4600             :   if (retrieve_certs_from_keychain(certs) && certs) {
    4601             :     result = add_certs_to_x509_store(certs.get(), store);
    4602             :   }
    4603             : 
    4604             :   if (retrieve_root_certs_from_keychain(certs) && certs) {
    4605             :     result = add_certs_to_x509_store(certs.get(), store) || result;
    4606             :   }
    4607             : 
    4608             :   return result;
    4609             : }
    4610             : #endif // TARGET_OS_OSX
    4611             : #endif // _WIN32
    4612             : #endif // CPPHTTPLIB_OPENSSL_SUPPORT
    4613             : 
    4614             : #ifdef _WIN32
    4615             : class WSInit {
    4616             : public:
    4617             :   WSInit() {
    4618             :     WSADATA wsaData;
    4619             :     if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
    4620             :   }
    4621             : 
    4622             :   ~WSInit() {
    4623             :     if (is_valid_) WSACleanup();
    4624             :   }
    4625             : 
    4626             :   bool is_valid_ = false;
    4627             : };
    4628             : 
    4629             : static WSInit wsinit_;
    4630             : #endif
    4631             : 
    4632             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    4633             : inline std::pair<std::string, std::string> make_digest_authentication_header(
    4634             :     const Request &req, const std::map<std::string, std::string> &auth,
    4635             :     size_t cnonce_count, const std::string &cnonce, const std::string &username,
    4636             :     const std::string &password, bool is_proxy = false) {
    4637             :   std::string nc;
    4638             :   {
    4639             :     std::stringstream ss;
    4640             :     ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
    4641             :     nc = ss.str();
    4642             :   }
    4643             : 
    4644             :   std::string qop;
    4645             :   if (auth.find("qop") != auth.end()) {
    4646             :     qop = auth.at("qop");
    4647             :     if (qop.find("auth-int") != std::string::npos) {
    4648             :       qop = "auth-int";
    4649             :     } else if (qop.find("auth") != std::string::npos) {
    4650             :       qop = "auth";
    4651             :     } else {
    4652             :       qop.clear();
    4653             :     }
    4654             :   }
    4655             : 
    4656             :   std::string algo = "MD5";
    4657             :   if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
    4658             : 
    4659             :   std::string response;
    4660             :   {
    4661             :     auto H = algo == "SHA-256"   ? detail::SHA_256
    4662             :              : algo == "SHA-512" ? detail::SHA_512
    4663             :                                  : detail::MD5;
    4664             : 
    4665             :     auto A1 = username + ":" + auth.at("realm") + ":" + password;
    4666             : 
    4667             :     auto A2 = req.method + ":" + req.path;
    4668             :     if (qop == "auth-int") { A2 += ":" + H(req.body); }
    4669             : 
    4670             :     if (qop.empty()) {
    4671             :       response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
    4672             :     } else {
    4673             :       response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
    4674             :                    ":" + qop + ":" + H(A2));
    4675             :     }
    4676             :   }
    4677             : 
    4678             :   auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
    4679             : 
    4680             :   auto field = "Digest username=\"" + username + "\", realm=\"" +
    4681             :                auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
    4682             :                "\", uri=\"" + req.path + "\", algorithm=" + algo +
    4683             :                (qop.empty() ? ", response=\""
    4684             :                             : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
    4685             :                                   cnonce + "\", response=\"") +
    4686             :                response + "\"" +
    4687             :                (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
    4688             : 
    4689             :   auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
    4690             :   return std::make_pair(key, field);
    4691             : }
    4692             : #endif
    4693             : 
    4694             : inline bool parse_www_authenticate(const Response &res,
    4695             :                                    std::map<std::string, std::string> &auth,
    4696             :                                    bool is_proxy) {
    4697             :   auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
    4698             :   if (res.has_header(auth_key)) {
    4699             :     static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
    4700             :     auto s = res.get_header_value(auth_key);
    4701             :     auto pos = s.find(' ');
    4702             :     if (pos != std::string::npos) {
    4703             :       auto type = s.substr(0, pos);
    4704             :       if (type == "Basic") {
    4705             :         return false;
    4706             :       } else if (type == "Digest") {
    4707             :         s = s.substr(pos + 1);
    4708             :         auto beg = std::sregex_iterator(s.begin(), s.end(), re);
    4709             :         for (auto i = beg; i != std::sregex_iterator(); ++i) {
    4710             :           auto m = *i;
    4711             :           auto key = s.substr(static_cast<size_t>(m.position(1)),
    4712             :                               static_cast<size_t>(m.length(1)));
    4713             :           auto val = m.length(2) > 0
    4714             :                          ? s.substr(static_cast<size_t>(m.position(2)),
    4715             :                                     static_cast<size_t>(m.length(2)))
    4716             :                          : s.substr(static_cast<size_t>(m.position(3)),
    4717             :                                     static_cast<size_t>(m.length(3)));
    4718             :           auth[key] = val;
    4719             :         }
    4720             :         return true;
    4721             :       }
    4722             :     }
    4723             :   }
    4724             :   return false;
    4725             : }
    4726             : 
    4727             : // https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240
    4728             : inline std::string random_string(size_t length) {
    4729             :   auto randchar = []() -> char {
    4730             :     const char charset[] = "0123456789"
    4731             :                            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    4732             :                            "abcdefghijklmnopqrstuvwxyz";
    4733             :     const size_t max_index = (sizeof(charset) - 1);
    4734             :     return charset[static_cast<size_t>(std::rand()) % max_index];
    4735             :   };
    4736             :   std::string str(length, 0);
    4737             :   std::generate_n(str.begin(), length, randchar);
    4738             :   return str;
    4739             : }
    4740             : 
    4741             : class ContentProviderAdapter {
    4742             : public:
    4743             :   explicit ContentProviderAdapter(
    4744             :       ContentProviderWithoutLength &&content_provider)
    4745             :       : content_provider_(content_provider) {}
    4746             : 
    4747             :   bool operator()(size_t offset, size_t, DataSink &sink) {
    4748             :     return content_provider_(offset, sink);
    4749             :   }
    4750             : 
    4751             : private:
    4752             :   ContentProviderWithoutLength content_provider_;
    4753             : };
    4754             : 
    4755             : } // namespace detail
    4756             : 
    4757             : inline std::string hosted_at(const std::string &hostname) {
    4758             :   std::vector<std::string> addrs;
    4759             :   hosted_at(hostname, addrs);
    4760             :   if (addrs.empty()) { return std::string(); }
    4761             :   return addrs[0];
    4762             : }
    4763             : 
    4764             : inline void hosted_at(const std::string &hostname,
    4765             :                       std::vector<std::string> &addrs) {
    4766             :   struct addrinfo hints;
    4767             :   struct addrinfo *result;
    4768             : 
    4769             :   memset(&hints, 0, sizeof(struct addrinfo));
    4770             :   hints.ai_family = AF_UNSPEC;
    4771             :   hints.ai_socktype = SOCK_STREAM;
    4772             :   hints.ai_protocol = 0;
    4773             : 
    4774             :   if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) {
    4775             : #if defined __linux__ && !defined __ANDROID__
    4776             :     res_init();
    4777             : #endif
    4778             :     return;
    4779             :   }
    4780             : 
    4781             :   for (auto rp = result; rp; rp = rp->ai_next) {
    4782             :     const auto &addr =
    4783             :         *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
    4784             :     std::string ip;
    4785             :     int dummy = -1;
    4786             :     if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,
    4787             :                                 dummy)) {
    4788             :       addrs.push_back(ip);
    4789             :     }
    4790             :   }
    4791             : 
    4792             :   freeaddrinfo(result);
    4793             : }
    4794             : 
    4795             : inline std::string append_query_params(const std::string &path,
    4796             :                                        const Params &params) {
    4797             :   std::string path_with_query = path;
    4798             :   const static std::regex re("[^?]+\\?.*");
    4799             :   auto delm = std::regex_match(path, re) ? '&' : '?';
    4800             :   path_with_query += delm + detail::params_to_query_str(params);
    4801             :   return path_with_query;
    4802             : }
    4803             : 
    4804             : // Header utilities
    4805             : inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
    4806             :   std::string field = "bytes=";
    4807             :   auto i = 0;
    4808             :   for (auto r : ranges) {
    4809             :     if (i != 0) { field += ", "; }
    4810             :     if (r.first != -1) { field += std::to_string(r.first); }
    4811             :     field += '-';
    4812             :     if (r.second != -1) { field += std::to_string(r.second); }
    4813             :     i++;
    4814             :   }
    4815             :   return std::make_pair("Range", std::move(field));
    4816             : }
    4817             : 
    4818             : inline std::pair<std::string, std::string>
    4819             : make_basic_authentication_header(const std::string &username,
    4820             :                                  const std::string &password, bool is_proxy) {
    4821             :   auto field = "Basic " + detail::base64_encode(username + ":" + password);
    4822             :   auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
    4823             :   return std::make_pair(key, std::move(field));
    4824             : }
    4825             : 
    4826             : inline std::pair<std::string, std::string>
    4827             : make_bearer_token_authentication_header(const std::string &token,
    4828             :                                         bool is_proxy = false) {
    4829             :   auto field = "Bearer " + token;
    4830             :   auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
    4831             :   return std::make_pair(key, std::move(field));
    4832             : }
    4833             : 
    4834             : // Request implementation
    4835           0 : inline bool Request::has_header(const std::string &key) const {
    4836           0 :   return detail::has_header(headers, key);
    4837             : }
    4838             : 
    4839           0 : inline std::string Request::get_header_value(const std::string &key,
    4840             :                                              size_t id) const {
    4841           0 :   return detail::get_header_value(headers, key, id, "");
    4842             : }
    4843             : 
    4844             : inline size_t Request::get_header_value_count(const std::string &key) const {
    4845             :   auto r = headers.equal_range(key);
    4846             :   return static_cast<size_t>(std::distance(r.first, r.second));
    4847             : }
    4848             : 
    4849           0 : inline void Request::set_header(const std::string &key,
    4850             :                                 const std::string &val) {
    4851           0 :   if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
    4852           0 :     headers.emplace(key, val);
    4853             :   }
    4854           0 : }
    4855             : 
    4856             : inline bool Request::has_param(const std::string &key) const {
    4857             :   return params.find(key) != params.end();
    4858             : }
    4859             : 
    4860             : inline std::string Request::get_param_value(const std::string &key,
    4861             :                                             size_t id) const {
    4862             :   auto rng = params.equal_range(key);
    4863             :   auto it = rng.first;
    4864             :   std::advance(it, static_cast<ssize_t>(id));
    4865             :   if (it != rng.second) { return it->second; }
    4866             :   return std::string();
    4867             : }
    4868             : 
    4869             : inline size_t Request::get_param_value_count(const std::string &key) const {
    4870             :   auto r = params.equal_range(key);
    4871             :   return static_cast<size_t>(std::distance(r.first, r.second));
    4872             : }
    4873             : 
    4874           0 : inline bool Request::is_multipart_form_data() const {
    4875           0 :   const auto &content_type = get_header_value("Content-Type");
    4876           0 :   return !content_type.rfind("multipart/form-data", 0);
    4877             : }
    4878             : 
    4879             : inline bool Request::has_file(const std::string &key) const {
    4880             :   return files.find(key) != files.end();
    4881             : }
    4882             : 
    4883             : inline MultipartFormData Request::get_file_value(const std::string &key) const {
    4884             :   auto it = files.find(key);
    4885             :   if (it != files.end()) { return it->second; }
    4886             :   return MultipartFormData();
    4887             : }
    4888             : 
    4889             : inline std::vector<MultipartFormData>
    4890             : Request::get_file_values(const std::string &key) const {
    4891             :   std::vector<MultipartFormData> values;
    4892             :   auto rng = files.equal_range(key);
    4893             :   for (auto it = rng.first; it != rng.second; it++) {
    4894             :     values.push_back(it->second);
    4895             :   }
    4896             :   return values;
    4897             : }
    4898             : 
    4899             : // Response implementation
    4900           0 : inline bool Response::has_header(const std::string &key) const {
    4901           0 :   return headers.find(key) != headers.end();
    4902             : }
    4903             : 
    4904           0 : inline std::string Response::get_header_value(const std::string &key,
    4905             :                                               size_t id) const {
    4906           0 :   return detail::get_header_value(headers, key, id, "");
    4907             : }
    4908             : 
    4909             : inline size_t Response::get_header_value_count(const std::string &key) const {
    4910             :   auto r = headers.equal_range(key);
    4911             :   return static_cast<size_t>(std::distance(r.first, r.second));
    4912             : }
    4913             : 
    4914           0 : inline void Response::set_header(const std::string &key,
    4915             :                                  const std::string &val) {
    4916           0 :   if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
    4917           0 :     headers.emplace(key, val);
    4918             :   }
    4919           0 : }
    4920             : 
    4921             : inline void Response::set_redirect(const std::string &url, int stat) {
    4922             :   if (!detail::has_crlf(url)) {
    4923             :     set_header("Location", url);
    4924             :     if (300 <= stat && stat < 400) {
    4925             :       this->status = stat;
    4926             :     } else {
    4927             :       this->status = 302;
    4928             :     }
    4929             :   }
    4930             : }
    4931             : 
    4932           0 : inline void Response::set_content(const char *s, size_t n,
    4933             :                                   const std::string &content_type) {
    4934           0 :   body.assign(s, n);
    4935             : 
    4936           0 :   auto rng = headers.equal_range("Content-Type");
    4937           0 :   headers.erase(rng.first, rng.second);
    4938           0 :   set_header("Content-Type", content_type);
    4939           0 : }
    4940             : 
    4941           0 : inline void Response::set_content(const std::string &s,
    4942             :                                   const std::string &content_type) {
    4943           0 :   set_content(s.data(), s.size(), content_type);
    4944           0 : }
    4945             : 
    4946             : inline void Response::set_content_provider(
    4947             :     size_t in_length, const std::string &content_type, ContentProvider provider,
    4948             :     ContentProviderResourceReleaser resource_releaser) {
    4949             :   set_header("Content-Type", content_type);
    4950             :   content_length_ = in_length;
    4951             :   if (in_length > 0) { content_provider_ = std::move(provider); }
    4952             :   content_provider_resource_releaser_ = resource_releaser;
    4953             :   is_chunked_content_provider_ = false;
    4954             : }
    4955             : 
    4956             : inline void Response::set_content_provider(
    4957             :     const std::string &content_type, ContentProviderWithoutLength provider,
    4958             :     ContentProviderResourceReleaser resource_releaser) {
    4959             :   set_header("Content-Type", content_type);
    4960             :   content_length_ = 0;
    4961             :   content_provider_ = detail::ContentProviderAdapter(std::move(provider));
    4962             :   content_provider_resource_releaser_ = resource_releaser;
    4963             :   is_chunked_content_provider_ = false;
    4964             : }
    4965             : 
    4966             : inline void Response::set_chunked_content_provider(
    4967             :     const std::string &content_type, ContentProviderWithoutLength provider,
    4968             :     ContentProviderResourceReleaser resource_releaser) {
    4969             :   set_header("Content-Type", content_type);
    4970             :   content_length_ = 0;
    4971             :   content_provider_ = detail::ContentProviderAdapter(std::move(provider));
    4972             :   content_provider_resource_releaser_ = resource_releaser;
    4973             :   is_chunked_content_provider_ = true;
    4974             : }
    4975             : 
    4976             : // Result implementation
    4977             : inline bool Result::has_request_header(const std::string &key) const {
    4978             :   return request_headers_.find(key) != request_headers_.end();
    4979             : }
    4980             : 
    4981             : inline std::string Result::get_request_header_value(const std::string &key,
    4982             :                                                     size_t id) const {
    4983             :   return detail::get_header_value(request_headers_, key, id, "");
    4984             : }
    4985             : 
    4986             : inline size_t
    4987             : Result::get_request_header_value_count(const std::string &key) const {
    4988             :   auto r = request_headers_.equal_range(key);
    4989             :   return static_cast<size_t>(std::distance(r.first, r.second));
    4990             : }
    4991             : 
    4992             : // Stream implementation
    4993           0 : inline ssize_t Stream::write(const char *ptr) {
    4994           0 :   return write(ptr, strlen(ptr));
    4995             : }
    4996             : 
    4997           0 : inline ssize_t Stream::write(const std::string &s) {
    4998           0 :   return write(s.data(), s.size());
    4999             : }
    5000             : 
    5001             : namespace detail {
    5002             : 
    5003             : // Socket stream implementation
    5004           0 : inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
    5005             :                                   time_t read_timeout_usec,
    5006             :                                   time_t write_timeout_sec,
    5007           0 :                                   time_t write_timeout_usec)
    5008             :     : sock_(sock), read_timeout_sec_(read_timeout_sec),
    5009             :       read_timeout_usec_(read_timeout_usec),
    5010             :       write_timeout_sec_(write_timeout_sec),
    5011           0 :       write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {}
    5012             : 
    5013           0 : inline SocketStream::~SocketStream() {}
    5014             : 
    5015           0 : inline bool SocketStream::is_readable() const {
    5016           0 :   return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
    5017             : }
    5018             : 
    5019           0 : inline bool SocketStream::is_writable() const {
    5020           0 :   return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
    5021           0 :          is_socket_alive(sock_);
    5022             : }
    5023             : 
    5024           0 : inline ssize_t SocketStream::read(char *ptr, size_t size) {
    5025             : #ifdef _WIN32
    5026             :   size =
    5027             :       (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
    5028             : #else
    5029           0 :   size = (std::min)(size,
    5030           0 :                     static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
    5031             : #endif
    5032             : 
    5033           0 :   if (read_buff_off_ < read_buff_content_size_) {
    5034           0 :     auto remaining_size = read_buff_content_size_ - read_buff_off_;
    5035           0 :     if (size <= remaining_size) {
    5036           0 :       memcpy(ptr, read_buff_.data() + read_buff_off_, size);
    5037           0 :       read_buff_off_ += size;
    5038           0 :       return static_cast<ssize_t>(size);
    5039             :     } else {
    5040           0 :       memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
    5041           0 :       read_buff_off_ += remaining_size;
    5042           0 :       return static_cast<ssize_t>(remaining_size);
    5043             :     }
    5044             :   }
    5045             : 
    5046           0 :   if (!is_readable()) { return -1; }
    5047             : 
    5048           0 :   read_buff_off_ = 0;
    5049           0 :   read_buff_content_size_ = 0;
    5050             : 
    5051           0 :   if (size < read_buff_size_) {
    5052           0 :     auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
    5053             :                          CPPHTTPLIB_RECV_FLAGS);
    5054           0 :     if (n <= 0) {
    5055             :       return n;
    5056           0 :     } else if (n <= static_cast<ssize_t>(size)) {
    5057           0 :       memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
    5058           0 :       return n;
    5059             :     } else {
    5060           0 :       memcpy(ptr, read_buff_.data(), size);
    5061           0 :       read_buff_off_ = size;
    5062           0 :       read_buff_content_size_ = static_cast<size_t>(n);
    5063           0 :       return static_cast<ssize_t>(size);
    5064             :     }
    5065             :   } else {
    5066           0 :     return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
    5067             :   }
    5068             : }
    5069             : 
    5070           0 : inline ssize_t SocketStream::write(const char *ptr, size_t size) {
    5071           0 :   if (!is_writable()) { return -1; }
    5072             : 
    5073             : #if defined(_WIN32) && !defined(_WIN64)
    5074             :   size =
    5075             :       (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
    5076             : #endif
    5077             : 
    5078           0 :   return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
    5079             : }
    5080             : 
    5081           0 : inline void SocketStream::get_remote_ip_and_port(std::string &ip,
    5082             :                                                  int &port) const {
    5083           0 :   return detail::get_remote_ip_and_port(sock_, ip, port);
    5084             : }
    5085             : 
    5086           0 : inline void SocketStream::get_local_ip_and_port(std::string &ip,
    5087             :                                                 int &port) const {
    5088           0 :   return detail::get_local_ip_and_port(sock_, ip, port);
    5089             : }
    5090             : 
    5091           0 : inline socket_t SocketStream::socket() const { return sock_; }
    5092             : 
    5093             : // Buffer stream implementation
    5094           0 : inline bool BufferStream::is_readable() const { return true; }
    5095             : 
    5096           0 : inline bool BufferStream::is_writable() const { return true; }
    5097             : 
    5098           0 : inline ssize_t BufferStream::read(char *ptr, size_t size) {
    5099             : #if defined(_MSC_VER) && _MSC_VER < 1910
    5100             :   auto len_read = buffer._Copy_s(ptr, size, size, position);
    5101             : #else
    5102           0 :   auto len_read = buffer.copy(ptr, size, position);
    5103             : #endif
    5104           0 :   position += static_cast<size_t>(len_read);
    5105           0 :   return static_cast<ssize_t>(len_read);
    5106             : }
    5107             : 
    5108           0 : inline ssize_t BufferStream::write(const char *ptr, size_t size) {
    5109           0 :   buffer.append(ptr, size);
    5110           0 :   return static_cast<ssize_t>(size);
    5111             : }
    5112             : 
    5113           0 : inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
    5114           0 :                                                  int & /*port*/) const {}
    5115             : 
    5116           0 : inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
    5117           0 :                                                 int & /*port*/) const {}
    5118             : 
    5119           0 : inline socket_t BufferStream::socket() const { return 0; }
    5120             : 
    5121           0 : inline const std::string &BufferStream::get_buffer() const { return buffer; }
    5122             : 
    5123             : } // namespace detail
    5124             : 
    5125             : // HTTP server implementation
    5126           0 : inline Server::Server()
    5127             :     : new_task_queue(
    5128           0 :           [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
    5129             : #ifndef _WIN32
    5130           0 :   signal(SIGPIPE, SIG_IGN);
    5131             : #endif
    5132           0 : }
    5133             : 
    5134           0 : inline Server::~Server() {}
    5135             : 
    5136           0 : inline Server &Server::Get(const std::string &pattern, Handler handler) {
    5137           0 :   get_handlers_.push_back(
    5138           0 :       std::make_pair(std::regex(pattern), std::move(handler)));
    5139           0 :   return *this;
    5140             : }
    5141             : 
    5142           0 : inline Server &Server::Post(const std::string &pattern, Handler handler) {
    5143           0 :   post_handlers_.push_back(
    5144           0 :       std::make_pair(std::regex(pattern), std::move(handler)));
    5145           0 :   return *this;
    5146             : }
    5147             : 
    5148             : inline Server &Server::Post(const std::string &pattern,
    5149             :                             HandlerWithContentReader handler) {
    5150             :   post_handlers_for_content_reader_.push_back(
    5151             :       std::make_pair(std::regex(pattern), std::move(handler)));
    5152             :   return *this;
    5153             : }
    5154             : 
    5155             : inline Server &Server::Put(const std::string &pattern, Handler handler) {
    5156             :   put_handlers_.push_back(
    5157             :       std::make_pair(std::regex(pattern), std::move(handler)));
    5158             :   return *this;
    5159             : }
    5160             : 
    5161             : inline Server &Server::Put(const std::string &pattern,
    5162             :                            HandlerWithContentReader handler) {
    5163             :   put_handlers_for_content_reader_.push_back(
    5164             :       std::make_pair(std::regex(pattern), std::move(handler)));
    5165             :   return *this;
    5166             : }
    5167             : 
    5168             : inline Server &Server::Patch(const std::string &pattern, Handler handler) {
    5169             :   patch_handlers_.push_back(
    5170             :       std::make_pair(std::regex(pattern), std::move(handler)));
    5171             :   return *this;
    5172             : }
    5173             : 
    5174             : inline Server &Server::Patch(const std::string &pattern,
    5175             :                              HandlerWithContentReader handler) {
    5176             :   patch_handlers_for_content_reader_.push_back(
    5177             :       std::make_pair(std::regex(pattern), std::move(handler)));
    5178             :   return *this;
    5179             : }
    5180             : 
    5181             : inline Server &Server::Delete(const std::string &pattern, Handler handler) {
    5182             :   delete_handlers_.push_back(
    5183             :       std::make_pair(std::regex(pattern), std::move(handler)));
    5184             :   return *this;
    5185             : }
    5186             : 
    5187             : inline Server &Server::Delete(const std::string &pattern,
    5188             :                               HandlerWithContentReader handler) {
    5189             :   delete_handlers_for_content_reader_.push_back(
    5190             :       std::make_pair(std::regex(pattern), std::move(handler)));
    5191             :   return *this;
    5192             : }
    5193             : 
    5194             : inline Server &Server::Options(const std::string &pattern, Handler handler) {
    5195             :   options_handlers_.push_back(
    5196             :       std::make_pair(std::regex(pattern), std::move(handler)));
    5197             :   return *this;
    5198             : }
    5199             : 
    5200             : inline bool Server::set_base_dir(const std::string &dir,
    5201             :                                  const std::string &mount_point) {
    5202             :   return set_mount_point(mount_point, dir);
    5203             : }
    5204             : 
    5205           0 : inline bool Server::set_mount_point(const std::string &mount_point,
    5206             :                                     const std::string &dir, Headers headers) {
    5207           0 :   if (detail::is_dir(dir)) {
    5208           0 :     std::string mnt = !mount_point.empty() ? mount_point : "/";
    5209           0 :     if (!mnt.empty() && mnt[0] == '/') {
    5210           0 :       base_dirs_.push_back({mnt, dir, std::move(headers)});
    5211           0 :       return true;
    5212             :     }
    5213             :   }
    5214             :   return false;
    5215             : }
    5216             : 
    5217             : inline bool Server::remove_mount_point(const std::string &mount_point) {
    5218             :   for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
    5219             :     if (it->mount_point == mount_point) {
    5220             :       base_dirs_.erase(it);
    5221             :       return true;
    5222             :     }
    5223             :   }
    5224             :   return false;
    5225             : }
    5226             : 
    5227             : inline Server &
    5228             : Server::set_file_extension_and_mimetype_mapping(const std::string &ext,
    5229             :                                                 const std::string &mime) {
    5230             :   file_extension_and_mimetype_map_[ext] = mime;
    5231             :   return *this;
    5232             : }
    5233             : 
    5234             : inline Server &Server::set_file_request_handler(Handler handler) {
    5235             :   file_request_handler_ = std::move(handler);
    5236             :   return *this;
    5237             : }
    5238             : 
    5239             : inline Server &Server::set_error_handler(HandlerWithResponse handler) {
    5240             :   error_handler_ = std::move(handler);
    5241             :   return *this;
    5242             : }
    5243             : 
    5244             : inline Server &Server::set_error_handler(Handler handler) {
    5245             :   error_handler_ = [handler](const Request &req, Response &res) {
    5246             :     handler(req, res);
    5247             :     return HandlerResponse::Handled;
    5248             :   };
    5249             :   return *this;
    5250             : }
    5251             : 
    5252             : inline Server &Server::set_exception_handler(ExceptionHandler handler) {
    5253             :   exception_handler_ = std::move(handler);
    5254             :   return *this;
    5255             : }
    5256             : 
    5257             : inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
    5258             :   pre_routing_handler_ = std::move(handler);
    5259             :   return *this;
    5260             : }
    5261             : 
    5262             : inline Server &Server::set_post_routing_handler(Handler handler) {
    5263             :   post_routing_handler_ = std::move(handler);
    5264             :   return *this;
    5265             : }
    5266             : 
    5267             : inline Server &Server::set_logger(Logger logger) {
    5268             :   logger_ = std::move(logger);
    5269             :   return *this;
    5270             : }
    5271             : 
    5272             : inline Server &
    5273             : Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
    5274             :   expect_100_continue_handler_ = std::move(handler);
    5275             : 
    5276             :   return *this;
    5277             : }
    5278             : 
    5279             : inline Server &Server::set_address_family(int family) {
    5280             :   address_family_ = family;
    5281             :   return *this;
    5282             : }
    5283             : 
    5284             : inline Server &Server::set_tcp_nodelay(bool on) {
    5285             :   tcp_nodelay_ = on;
    5286             :   return *this;
    5287             : }
    5288             : 
    5289             : inline Server &Server::set_socket_options(SocketOptions socket_options) {
    5290             :   socket_options_ = std::move(socket_options);
    5291             :   return *this;
    5292             : }
    5293             : 
    5294             : inline Server &Server::set_default_headers(Headers headers) {
    5295             :   default_headers_ = std::move(headers);
    5296             :   return *this;
    5297             : }
    5298             : 
    5299             : inline Server &Server::set_keep_alive_max_count(size_t count) {
    5300             :   keep_alive_max_count_ = count;
    5301             :   return *this;
    5302             : }
    5303             : 
    5304             : inline Server &Server::set_keep_alive_timeout(time_t sec) {
    5305             :   keep_alive_timeout_sec_ = sec;
    5306             :   return *this;
    5307             : }
    5308             : 
    5309             : inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
    5310             :   read_timeout_sec_ = sec;
    5311             :   read_timeout_usec_ = usec;
    5312             :   return *this;
    5313             : }
    5314             : 
    5315             : inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
    5316             :   write_timeout_sec_ = sec;
    5317             :   write_timeout_usec_ = usec;
    5318             :   return *this;
    5319             : }
    5320             : 
    5321             : inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
    5322             :   idle_interval_sec_ = sec;
    5323             :   idle_interval_usec_ = usec;
    5324             :   return *this;
    5325             : }
    5326             : 
    5327             : inline Server &Server::set_payload_max_length(size_t length) {
    5328             :   payload_max_length_ = length;
    5329             :   return *this;
    5330             : }
    5331             : 
    5332           0 : inline bool Server::bind_to_port(const std::string &host, int port,
    5333             :                                  int socket_flags) {
    5334           0 :   if (bind_internal(host, port, socket_flags) < 0) return false;
    5335             :   return true;
    5336             : }
    5337             : inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
    5338             :   return bind_internal(host, 0, socket_flags);
    5339             : }
    5340             : 
    5341             : inline bool Server::listen_after_bind() {
    5342             :   auto se = detail::scope_exit([&]() { done_ = true; });
    5343             :   return listen_internal();
    5344             : }
    5345             : 
    5346           0 : inline bool Server::listen(const std::string &host, int port,
    5347             :                            int socket_flags) {
    5348           0 :   auto se = detail::scope_exit([&]() { done_ = true; });
    5349           0 :   return bind_to_port(host, port, socket_flags) && listen_internal();
    5350             : }
    5351             : 
    5352             : inline bool Server::is_running() const { return is_running_; }
    5353             : 
    5354             : inline void Server::wait_until_ready() const {
    5355             :   while (!is_running() && !done_) {
    5356             :     std::this_thread::sleep_for(std::chrono::milliseconds{1});
    5357             :   }
    5358             : }
    5359             : 
    5360             : inline void Server::stop() {
    5361             :   if (is_running_) {
    5362             :     assert(svr_sock_ != INVALID_SOCKET);
    5363             :     std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
    5364             :     detail::shutdown_socket(sock);
    5365             :     detail::close_socket(sock);
    5366             :   }
    5367             : }
    5368             : 
    5369           0 : inline bool Server::parse_request_line(const char *s, Request &req) {
    5370           0 :   auto len = strlen(s);
    5371           0 :   if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
    5372           0 :   len -= 2;
    5373             : 
    5374           0 :   {
    5375           0 :     size_t count = 0;
    5376             : 
    5377           0 :     detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
    5378           0 :       switch (count) {
    5379           0 :       case 0: req.method = std::string(b, e); break;
    5380           0 :       case 1: req.target = std::string(b, e); break;
    5381           0 :       case 2: req.version = std::string(b, e); break;
    5382             :       default: break;
    5383             :       }
    5384           0 :       count++;
    5385           0 :     });
    5386             : 
    5387           0 :     if (count != 3) { return false; }
    5388             :   }
    5389             : 
    5390           0 :   static const std::set<std::string> methods{
    5391             :       "GET",     "HEAD",    "POST",  "PUT",   "DELETE",
    5392           0 :       "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
    5393             : 
    5394           0 :   if (methods.find(req.method) == methods.end()) { return false; }
    5395             : 
    5396           0 :   if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; }
    5397             : 
    5398             :   {
    5399             :     // Skip URL fragment
    5400           0 :     for (size_t i = 0; i < req.target.size(); i++) {
    5401           0 :       if (req.target[i] == '#') {
    5402           0 :         req.target.erase(i);
    5403             :         break;
    5404             :       }
    5405             :     }
    5406             : 
    5407           0 :     size_t count = 0;
    5408             : 
    5409           0 :     detail::split(req.target.data(), req.target.data() + req.target.size(), '?',
    5410           0 :                   [&](const char *b, const char *e) {
    5411           0 :                     switch (count) {
    5412           0 :                     case 0:
    5413           0 :                       req.path = detail::decode_url(std::string(b, e), false);
    5414           0 :                       break;
    5415           0 :                     case 1: {
    5416           0 :                       if (e - b > 0) {
    5417           0 :                         detail::parse_query_text(std::string(b, e), req.params);
    5418             :                       }
    5419             :                       break;
    5420             :                     }
    5421             :                     default: break;
    5422             :                     }
    5423           0 :                     count++;
    5424           0 :                   });
    5425             : 
    5426           0 :     if (count > 2) { return false; }
    5427             :   }
    5428             : 
    5429           0 :   return true;
    5430             : }
    5431             : 
    5432           0 : inline bool Server::write_response(Stream &strm, bool close_connection,
    5433             :                                    const Request &req, Response &res) {
    5434           0 :   return write_response_core(strm, close_connection, req, res, false);
    5435             : }
    5436             : 
    5437           0 : inline bool Server::write_response_with_content(Stream &strm,
    5438             :                                                 bool close_connection,
    5439             :                                                 const Request &req,
    5440             :                                                 Response &res) {
    5441           0 :   return write_response_core(strm, close_connection, req, res, true);
    5442             : }
    5443             : 
    5444           0 : inline bool Server::write_response_core(Stream &strm, bool close_connection,
    5445             :                                         const Request &req, Response &res,
    5446             :                                         bool need_apply_ranges) {
    5447           0 :   assert(res.status != -1);
    5448             : 
    5449           0 :   if (400 <= res.status && error_handler_ &&
    5450           0 :       error_handler_(req, res) == HandlerResponse::Handled) {
    5451             :     need_apply_ranges = true;
    5452             :   }
    5453             : 
    5454           0 :   std::string content_type;
    5455           0 :   std::string boundary;
    5456           0 :   if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
    5457             : 
    5458             :   // Prepare additional headers
    5459           0 :   if (close_connection || req.get_header_value("Connection") == "close") {
    5460           0 :     res.set_header("Connection", "close");
    5461             :   } else {
    5462           0 :     std::stringstream ss;
    5463           0 :     ss << "timeout=" << keep_alive_timeout_sec_
    5464           0 :        << ", max=" << keep_alive_max_count_;
    5465           0 :     res.set_header("Keep-Alive", ss.str());
    5466             :   }
    5467             : 
    5468           0 :   if (!res.has_header("Content-Type") &&
    5469           0 :       (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) {
    5470           0 :     res.set_header("Content-Type", "text/plain");
    5471             :   }
    5472             : 
    5473           0 :   if (!res.has_header("Content-Length") && res.body.empty() &&
    5474           0 :       !res.content_length_ && !res.content_provider_) {
    5475           0 :     res.set_header("Content-Length", "0");
    5476             :   }
    5477             : 
    5478           0 :   if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {
    5479           0 :     res.set_header("Accept-Ranges", "bytes");
    5480             :   }
    5481             : 
    5482           0 :   if (post_routing_handler_) { post_routing_handler_(req, res); }
    5483             : 
    5484             :   // Response line and headers
    5485           0 :   {
    5486           0 :     detail::BufferStream bstrm;
    5487             : 
    5488           0 :     if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
    5489           0 :                             detail::status_message(res.status))) {
    5490           0 :       return false;
    5491             :     }
    5492             : 
    5493           0 :     if (!detail::write_headers(bstrm, res.headers)) { return false; }
    5494             : 
    5495             :     // Flush buffer
    5496           0 :     auto &data = bstrm.get_buffer();
    5497           0 :     detail::write_data(strm, data.data(), data.size());
    5498             :   }
    5499             : 
    5500             :   // Body
    5501           0 :   auto ret = true;
    5502           0 :   if (req.method != "HEAD") {
    5503           0 :     if (!res.body.empty()) {
    5504           0 :       if (!detail::write_data(strm, res.body.data(), res.body.size())) {
    5505           0 :         ret = false;
    5506             :       }
    5507           0 :     } else if (res.content_provider_) {
    5508           0 :       if (write_content_with_provider(strm, req, res, boundary, content_type)) {
    5509           0 :         res.content_provider_success_ = true;
    5510             :       } else {
    5511           0 :         res.content_provider_success_ = false;
    5512           0 :         ret = false;
    5513             :       }
    5514             :     }
    5515             :   }
    5516             : 
    5517             :   // Log
    5518           0 :   if (logger_) { logger_(req, res); }
    5519             : 
    5520             :   return ret;
    5521             : }
    5522             : 
    5523             : inline bool
    5524           0 : Server::write_content_with_provider(Stream &strm, const Request &req,
    5525             :                                     Response &res, const std::string &boundary,
    5526             :                                     const std::string &content_type) {
    5527           0 :   auto is_shutting_down = [this]() {
    5528           0 :     return this->svr_sock_ == INVALID_SOCKET;
    5529           0 :   };
    5530             : 
    5531           0 :   if (res.content_length_ > 0) {
    5532           0 :     if (req.ranges.empty()) {
    5533           0 :       return detail::write_content(strm, res.content_provider_, 0,
    5534           0 :                                    res.content_length_, is_shutting_down);
    5535           0 :     } else if (req.ranges.size() == 1) {
    5536           0 :       auto offsets =
    5537           0 :           detail::get_range_offset_and_length(req, res.content_length_, 0);
    5538           0 :       auto offset = offsets.first;
    5539           0 :       auto length = offsets.second;
    5540           0 :       return detail::write_content(strm, res.content_provider_, offset, length,
    5541             :                                    is_shutting_down);
    5542             :     } else {
    5543           0 :       return detail::write_multipart_ranges_data(
    5544             :           strm, req, res, boundary, content_type, is_shutting_down);
    5545             :     }
    5546             :   } else {
    5547           0 :     if (res.is_chunked_content_provider_) {
    5548           0 :       auto type = detail::encoding_type(req, res);
    5549             : 
    5550           0 :       std::unique_ptr<detail::compressor> compressor;
    5551           0 :       if (type == detail::EncodingType::Gzip) {
    5552             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
    5553             :         compressor = detail::make_unique<detail::gzip_compressor>();
    5554             : #endif
    5555           0 :       } else if (type == detail::EncodingType::Brotli) {
    5556             : #ifdef CPPHTTPLIB_BROTLI_SUPPORT
    5557             :         compressor = detail::make_unique<detail::brotli_compressor>();
    5558             : #endif
    5559             :       } else {
    5560           0 :         compressor = detail::make_unique<detail::nocompressor>();
    5561             :       }
    5562           0 :       assert(compressor != nullptr);
    5563             : 
    5564           0 :       return detail::write_content_chunked(strm, res.content_provider_,
    5565           0 :                                            is_shutting_down, *compressor);
    5566             :     } else {
    5567           0 :       return detail::write_content_without_length(strm, res.content_provider_,
    5568             :                                                   is_shutting_down);
    5569             :     }
    5570             :   }
    5571             : }
    5572             : 
    5573           0 : inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
    5574           0 :   MultipartFormDataMap::iterator cur;
    5575           0 :   auto file_count = 0;
    5576           0 :   if (read_content_core(
    5577             :           strm, req, res,
    5578             :           // Regular
    5579           0 :           [&](const char *buf, size_t n) {
    5580           0 :             if (req.body.size() + n > req.body.max_size()) { return false; }
    5581           0 :             req.body.append(buf, n);
    5582           0 :             return true;
    5583             :           },
    5584             :           // Multipart
    5585           0 :           [&](const MultipartFormData &file) {
    5586           0 :             if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
    5587             :               return false;
    5588             :             }
    5589           0 :             cur = req.files.emplace(file.name, file);
    5590           0 :             return true;
    5591             :           },
    5592           0 :           [&](const char *buf, size_t n) {
    5593           0 :             auto &content = cur->second.content;
    5594           0 :             if (content.size() + n > content.max_size()) { return false; }
    5595           0 :             content.append(buf, n);
    5596           0 :             return true;
    5597             :           })) {
    5598           0 :     const auto &content_type = req.get_header_value("Content-Type");
    5599           0 :     if (!content_type.find("application/x-www-form-urlencoded")) {
    5600           0 :       if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
    5601           0 :         res.status = 413; // NOTE: should be 414?
    5602           0 :         return false;
    5603             :       }
    5604           0 :       detail::parse_query_text(req.body, req.params);
    5605             :     }
    5606           0 :     return true;
    5607             :   }
    5608             :   return false;
    5609             : }
    5610             : 
    5611           0 : inline bool Server::read_content_with_content_receiver(
    5612             :     Stream &strm, Request &req, Response &res, ContentReceiver receiver,
    5613             :     MultipartContentHeader multipart_header,
    5614             :     ContentReceiver multipart_receiver) {
    5615           0 :   return read_content_core(strm, req, res, std::move(receiver),
    5616           0 :                            std::move(multipart_header),
    5617           0 :                            std::move(multipart_receiver));
    5618             : }
    5619             : 
    5620           0 : inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
    5621             :                                       ContentReceiver receiver,
    5622             :                                       MultipartContentHeader multipart_header,
    5623             :                                       ContentReceiver multipart_receiver) {
    5624           0 :   detail::MultipartFormDataParser multipart_form_data_parser;
    5625           0 :   ContentReceiverWithProgress out;
    5626             : 
    5627           0 :   if (req.is_multipart_form_data()) {
    5628           0 :     const auto &content_type = req.get_header_value("Content-Type");
    5629           0 :     std::string boundary;
    5630           0 :     if (!detail::parse_multipart_boundary(content_type, boundary)) {
    5631           0 :       res.status = 400;
    5632           0 :       return false;
    5633             :     }
    5634             : 
    5635           0 :     multipart_form_data_parser.set_boundary(std::move(boundary));
    5636           0 :     out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
    5637             :       /* For debug
    5638             :       size_t pos = 0;
    5639             :       while (pos < n) {
    5640             :         auto read_size = (std::min)<size_t>(1, n - pos);
    5641             :         auto ret = multipart_form_data_parser.parse(
    5642             :             buf + pos, read_size, multipart_receiver, multipart_header);
    5643             :         if (!ret) { return false; }
    5644             :         pos += read_size;
    5645             :       }
    5646             :       return true;
    5647             :       */
    5648           0 :       return multipart_form_data_parser.parse(buf, n, multipart_receiver,
    5649           0 :                                               multipart_header);
    5650           0 :     };
    5651             :   } else {
    5652           0 :     out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
    5653           0 :                      uint64_t /*len*/) { return receiver(buf, n); };
    5654             :   }
    5655             : 
    5656           0 :   if (req.method == "DELETE" && !req.has_header("Content-Length")) {
    5657             :     return true;
    5658             :   }
    5659             : 
    5660           0 :   if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
    5661             :                             out, true)) {
    5662             :     return false;
    5663             :   }
    5664             : 
    5665           0 :   if (req.is_multipart_form_data()) {
    5666           0 :     if (!multipart_form_data_parser.is_valid()) {
    5667           0 :       res.status = 400;
    5668           0 :       return false;
    5669             :     }
    5670             :   }
    5671             : 
    5672             :   return true;
    5673             : }
    5674             : 
    5675           0 : inline bool Server::handle_file_request(const Request &req, Response &res,
    5676             :                                         bool head) {
    5677           0 :   for (const auto &entry : base_dirs_) {
    5678             :     // Prefix match
    5679           0 :     if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
    5680           0 :       std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
    5681           0 :       if (detail::is_valid_path(sub_path)) {
    5682           0 :         auto path = entry.base_dir + sub_path;
    5683           0 :         if (path.back() == '/') { path += "index.html"; }
    5684             : 
    5685           0 :         if (detail::is_file(path)) {
    5686           0 :           detail::read_file(path, res.body);
    5687           0 :           auto type =
    5688           0 :               detail::find_content_type(path, file_extension_and_mimetype_map_);
    5689           0 :           if (type) { res.set_header("Content-Type", type); }
    5690           0 :           for (const auto &kv : entry.headers) {
    5691           0 :             res.set_header(kv.first.c_str(), kv.second);
    5692             :           }
    5693           0 :           res.status = req.has_header("Range") ? 206 : 200;
    5694           0 :           if (!head && file_request_handler_) {
    5695           0 :             file_request_handler_(req, res);
    5696             :           }
    5697           0 :           return true;
    5698             :         }
    5699             :       }
    5700             :     }
    5701             :   }
    5702             :   return false;
    5703             : }
    5704             : 
    5705             : inline socket_t
    5706           0 : Server::create_server_socket(const std::string &host, int port,
    5707             :                              int socket_flags,
    5708             :                              SocketOptions socket_options) const {
    5709           0 :   return detail::create_socket(
    5710           0 :       host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
    5711           0 :       std::move(socket_options),
    5712           0 :       [](socket_t sock, struct addrinfo &ai) -> bool {
    5713           0 :         if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
    5714             :           return false;
    5715             :         }
    5716           0 :         if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; }
    5717             :         return true;
    5718           0 :       });
    5719             : }
    5720             : 
    5721           0 : inline int Server::bind_internal(const std::string &host, int port,
    5722             :                                  int socket_flags) {
    5723           0 :   if (!is_valid()) { return -1; }
    5724             : 
    5725           0 :   svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
    5726           0 :   if (svr_sock_ == INVALID_SOCKET) { return -1; }
    5727             : 
    5728           0 :   if (port == 0) {
    5729           0 :     struct sockaddr_storage addr;
    5730           0 :     socklen_t addr_len = sizeof(addr);
    5731           0 :     if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
    5732             :                     &addr_len) == -1) {
    5733             :       return -1;
    5734             :     }
    5735           0 :     if (addr.ss_family == AF_INET) {
    5736           0 :       return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
    5737           0 :     } else if (addr.ss_family == AF_INET6) {
    5738           0 :       return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
    5739             :     } else {
    5740             :       return -1;
    5741             :     }
    5742             :   } else {
    5743             :     return port;
    5744             :   }
    5745             : }
    5746             : 
    5747           0 : inline bool Server::listen_internal() {
    5748           0 :   auto ret = true;
    5749           0 :   is_running_ = true;
    5750           0 :   auto se = detail::scope_exit([&]() { is_running_ = false; });
    5751             : 
    5752           0 :   {
    5753           0 :     std::unique_ptr<TaskQueue> task_queue(new_task_queue());
    5754             : 
    5755           0 :     while (svr_sock_ != INVALID_SOCKET) {
    5756             : #ifndef _WIN32
    5757           0 :       if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
    5758             : #endif
    5759           0 :         auto val = detail::select_read(svr_sock_, idle_interval_sec_,
    5760             :                                        idle_interval_usec_);
    5761           0 :         if (val == 0) { // Timeout
    5762           0 :           task_queue->on_idle();
    5763           0 :           continue;
    5764             :         }
    5765             : #ifndef _WIN32
    5766             :       }
    5767             : #endif
    5768           0 :       socket_t sock = accept(svr_sock_, nullptr, nullptr);
    5769             : 
    5770           0 :       if (sock == INVALID_SOCKET) {
    5771           0 :         if (errno == EMFILE) {
    5772             :           // The per-process limit of open file descriptors has been reached.
    5773             :           // Try to accept new connections after a short sleep.
    5774           0 :           std::this_thread::sleep_for(std::chrono::milliseconds(1));
    5775           0 :           continue;
    5776           0 :         } else if (errno == EINTR || errno == EAGAIN) {
    5777           0 :           continue;
    5778             :         }
    5779           0 :         if (svr_sock_ != INVALID_SOCKET) {
    5780           0 :           detail::close_socket(svr_sock_);
    5781             :           ret = false;
    5782             :         } else {
    5783             :           ; // The server socket was closed by user.
    5784             :         }
    5785             :         break;
    5786             :       }
    5787             : 
    5788           0 :       {
    5789             : #ifdef _WIN32
    5790             :         auto timeout = static_cast<uint32_t>(read_timeout_sec_ * 1000 +
    5791             :                                              read_timeout_usec_ / 1000);
    5792             :         setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
    5793             :                    sizeof(timeout));
    5794             : #else
    5795           0 :         timeval tv;
    5796           0 :         tv.tv_sec = static_cast<long>(read_timeout_sec_);
    5797           0 :         tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec_);
    5798           0 :         setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
    5799             : #endif
    5800             :       }
    5801           0 :       {
    5802             : 
    5803             : #ifdef _WIN32
    5804             :         auto timeout = static_cast<uint32_t>(write_timeout_sec_ * 1000 +
    5805             :                                              write_timeout_usec_ / 1000);
    5806             :         setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
    5807             :                    sizeof(timeout));
    5808             : #else
    5809           0 :         timeval tv;
    5810           0 :         tv.tv_sec = static_cast<long>(write_timeout_sec_);
    5811           0 :         tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec_);
    5812           0 :         setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
    5813             : #endif
    5814             :       }
    5815             : 
    5816           0 :       task_queue->enqueue([this, sock]() { process_and_close_socket(sock); });
    5817             :     }
    5818             : 
    5819           0 :     task_queue->shutdown();
    5820             :   }
    5821             : 
    5822           0 :   return ret;
    5823             : }
    5824             : 
    5825           0 : inline bool Server::routing(Request &req, Response &res, Stream &strm) {
    5826           0 :   if (pre_routing_handler_ &&
    5827           0 :       pre_routing_handler_(req, res) == HandlerResponse::Handled) {
    5828             :     return true;
    5829             :   }
    5830             : 
    5831             :   // File handler
    5832           0 :   bool is_head_request = req.method == "HEAD";
    5833           0 :   if ((req.method == "GET" || is_head_request) &&
    5834           0 :       handle_file_request(req, res, is_head_request)) {
    5835             :     return true;
    5836             :   }
    5837             : 
    5838           0 :   if (detail::expect_content(req)) {
    5839             :     // Content reader handler
    5840           0 :     {
    5841           0 :       ContentReader reader(
    5842           0 :           [&](ContentReceiver receiver) {
    5843           0 :             return read_content_with_content_receiver(
    5844           0 :                 strm, req, res, std::move(receiver), nullptr, nullptr);
    5845             :           },
    5846           0 :           [&](MultipartContentHeader header, ContentReceiver receiver) {
    5847           0 :             return read_content_with_content_receiver(strm, req, res, nullptr,
    5848           0 :                                                       std::move(header),
    5849           0 :                                                       std::move(receiver));
    5850           0 :           });
    5851             : 
    5852           0 :       if (req.method == "POST") {
    5853           0 :         if (dispatch_request_for_content_reader(
    5854           0 :                 req, res, std::move(reader),
    5855           0 :                 post_handlers_for_content_reader_)) {
    5856           0 :           return true;
    5857             :         }
    5858           0 :       } else if (req.method == "PUT") {
    5859           0 :         if (dispatch_request_for_content_reader(
    5860           0 :                 req, res, std::move(reader),
    5861           0 :                 put_handlers_for_content_reader_)) {
    5862             :           return true;
    5863             :         }
    5864           0 :       } else if (req.method == "PATCH") {
    5865           0 :         if (dispatch_request_for_content_reader(
    5866           0 :                 req, res, std::move(reader),
    5867           0 :                 patch_handlers_for_content_reader_)) {
    5868             :           return true;
    5869             :         }
    5870           0 :       } else if (req.method == "DELETE") {
    5871           0 :         if (dispatch_request_for_content_reader(
    5872           0 :                 req, res, std::move(reader),
    5873           0 :                 delete_handlers_for_content_reader_)) {
    5874             :           return true;
    5875             :         }
    5876             :       }
    5877             :     }
    5878             : 
    5879             :     // Read content into `req.body`
    5880           0 :     if (!read_content(strm, req, res)) { return false; }
    5881             :   }
    5882             : 
    5883             :   // Regular handler
    5884           0 :   if (req.method == "GET" || req.method == "HEAD") {
    5885           0 :     return dispatch_request(req, res, get_handlers_);
    5886           0 :   } else if (req.method == "POST") {
    5887           0 :     return dispatch_request(req, res, post_handlers_);
    5888           0 :   } else if (req.method == "PUT") {
    5889           0 :     return dispatch_request(req, res, put_handlers_);
    5890           0 :   } else if (req.method == "DELETE") {
    5891           0 :     return dispatch_request(req, res, delete_handlers_);
    5892           0 :   } else if (req.method == "OPTIONS") {
    5893           0 :     return dispatch_request(req, res, options_handlers_);
    5894           0 :   } else if (req.method == "PATCH") {
    5895           0 :     return dispatch_request(req, res, patch_handlers_);
    5896             :   }
    5897             : 
    5898           0 :   res.status = 400;
    5899           0 :   return false;
    5900             : }
    5901             : 
    5902           0 : inline bool Server::dispatch_request(Request &req, Response &res,
    5903             :                                      const Handlers &handlers) {
    5904           0 :   for (const auto &x : handlers) {
    5905           0 :     const auto &pattern = x.first;
    5906           0 :     const auto &handler = x.second;
    5907             : 
    5908           0 :     if (std::regex_match(req.path, req.matches, pattern)) {
    5909           0 :       handler(req, res);
    5910           0 :       return true;
    5911             :     }
    5912             :   }
    5913             :   return false;
    5914             : }
    5915             : 
    5916           0 : inline void Server::apply_ranges(const Request &req, Response &res,
    5917             :                                  std::string &content_type,
    5918             :                                  std::string &boundary) {
    5919           0 :   if (req.ranges.size() > 1) {
    5920           0 :     boundary = detail::make_multipart_data_boundary();
    5921             : 
    5922           0 :     auto it = res.headers.find("Content-Type");
    5923           0 :     if (it != res.headers.end()) {
    5924           0 :       content_type = it->second;
    5925           0 :       res.headers.erase(it);
    5926             :     }
    5927             : 
    5928           0 :     res.headers.emplace("Content-Type",
    5929           0 :                         "multipart/byteranges; boundary=" + boundary);
    5930             :   }
    5931             : 
    5932           0 :   auto type = detail::encoding_type(req, res);
    5933             : 
    5934           0 :   if (res.body.empty()) {
    5935           0 :     if (res.content_length_ > 0) {
    5936           0 :       size_t length = 0;
    5937           0 :       if (req.ranges.empty()) {
    5938             :         length = res.content_length_;
    5939           0 :       } else if (req.ranges.size() == 1) {
    5940           0 :         auto offsets =
    5941           0 :             detail::get_range_offset_and_length(req, res.content_length_, 0);
    5942           0 :         auto offset = offsets.first;
    5943           0 :         length = offsets.second;
    5944           0 :         auto content_range = detail::make_content_range_header_field(
    5945           0 :             offset, length, res.content_length_);
    5946           0 :         res.set_header("Content-Range", content_range);
    5947             :       } else {
    5948           0 :         length = detail::get_multipart_ranges_data_length(req, res, boundary,
    5949             :                                                           content_type);
    5950             :       }
    5951           0 :       res.set_header("Content-Length", std::to_string(length));
    5952             :     } else {
    5953           0 :       if (res.content_provider_) {
    5954           0 :         if (res.is_chunked_content_provider_) {
    5955           0 :           res.set_header("Transfer-Encoding", "chunked");
    5956           0 :           if (type == detail::EncodingType::Gzip) {
    5957           0 :             res.set_header("Content-Encoding", "gzip");
    5958           0 :           } else if (type == detail::EncodingType::Brotli) {
    5959           0 :             res.set_header("Content-Encoding", "br");
    5960             :           }
    5961             :         }
    5962             :       }
    5963             :     }
    5964             :   } else {
    5965           0 :     if (req.ranges.empty()) {
    5966             :       ;
    5967           0 :     } else if (req.ranges.size() == 1) {
    5968           0 :       auto offsets =
    5969           0 :           detail::get_range_offset_and_length(req, res.body.size(), 0);
    5970           0 :       auto offset = offsets.first;
    5971           0 :       auto length = offsets.second;
    5972           0 :       auto content_range = detail::make_content_range_header_field(
    5973           0 :           offset, length, res.body.size());
    5974           0 :       res.set_header("Content-Range", content_range);
    5975           0 :       if (offset < res.body.size()) {
    5976           0 :         res.body = res.body.substr(offset, length);
    5977             :       } else {
    5978           0 :         res.body.clear();
    5979           0 :         res.status = 416;
    5980             :       }
    5981             :     } else {
    5982           0 :       std::string data;
    5983           0 :       if (detail::make_multipart_ranges_data(req, res, boundary, content_type,
    5984             :                                              data)) {
    5985           0 :         res.body.swap(data);
    5986             :       } else {
    5987           0 :         res.body.clear();
    5988           0 :         res.status = 416;
    5989             :       }
    5990             :     }
    5991             : 
    5992           0 :     if (type != detail::EncodingType::None) {
    5993             :       std::unique_ptr<detail::compressor> compressor;
    5994             :       std::string content_encoding;
    5995             : 
    5996             :       if (type == detail::EncodingType::Gzip) {
    5997             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
    5998             :         compressor = detail::make_unique<detail::gzip_compressor>();
    5999             :         content_encoding = "gzip";
    6000             : #endif
    6001             :       } else if (type == detail::EncodingType::Brotli) {
    6002             : #ifdef CPPHTTPLIB_BROTLI_SUPPORT
    6003             :         compressor = detail::make_unique<detail::brotli_compressor>();
    6004             :         content_encoding = "br";
    6005             : #endif
    6006             :       }
    6007             : 
    6008             :       if (compressor) {
    6009             :         std::string compressed;
    6010             :         if (compressor->compress(res.body.data(), res.body.size(), true,
    6011             :                                  [&](const char *data, size_t data_len) {
    6012             :                                    compressed.append(data, data_len);
    6013             :                                    return true;
    6014             :                                  })) {
    6015             :           res.body.swap(compressed);
    6016             :           res.set_header("Content-Encoding", content_encoding);
    6017             :         }
    6018             :       }
    6019             :     }
    6020             : 
    6021           0 :     auto length = std::to_string(res.body.size());
    6022           0 :     res.set_header("Content-Length", length);
    6023             :   }
    6024           0 : }
    6025             : 
    6026           0 : inline bool Server::dispatch_request_for_content_reader(
    6027             :     Request &req, Response &res, ContentReader content_reader,
    6028             :     const HandlersForContentReader &handlers) {
    6029           0 :   for (const auto &x : handlers) {
    6030           0 :     const auto &pattern = x.first;
    6031           0 :     const auto &handler = x.second;
    6032             : 
    6033           0 :     if (std::regex_match(req.path, req.matches, pattern)) {
    6034           0 :       handler(req, res, content_reader);
    6035           0 :       return true;
    6036             :     }
    6037             :   }
    6038             :   return false;
    6039             : }
    6040             : 
    6041             : inline bool
    6042           0 : Server::process_request(Stream &strm, bool close_connection,
    6043             :                         bool &connection_closed,
    6044             :                         const std::function<void(Request &)> &setup_request) {
    6045           0 :   std::array<char, 2048> buf{};
    6046             : 
    6047           0 :   detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
    6048             : 
    6049             :   // Connection has been closed on client
    6050           0 :   if (!line_reader.getline()) { return false; }
    6051             : 
    6052           0 :   Request req;
    6053           0 :   Response res;
    6054             : 
    6055           0 :   res.version = "HTTP/1.1";
    6056             : 
    6057           0 :   for (const auto &header : default_headers_) {
    6058           0 :     if (res.headers.find(header.first) == res.headers.end()) {
    6059           0 :       res.headers.insert(header);
    6060             :     }
    6061             :   }
    6062             : 
    6063             : #ifdef _WIN32
    6064             :   // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL).
    6065             : #else
    6066             : #ifndef CPPHTTPLIB_USE_POLL
    6067             :   // Socket file descriptor exceeded FD_SETSIZE...
    6068           0 :   if (strm.socket() >= FD_SETSIZE) {
    6069           0 :     Headers dummy;
    6070           0 :     detail::read_headers(strm, dummy);
    6071           0 :     res.status = 500;
    6072           0 :     return write_response(strm, close_connection, req, res);
    6073             :   }
    6074             : #endif
    6075             : #endif
    6076             : 
    6077             :   // Check if the request URI doesn't exceed the limit
    6078           0 :   if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
    6079           0 :     Headers dummy;
    6080           0 :     detail::read_headers(strm, dummy);
    6081           0 :     res.status = 414;
    6082           0 :     return write_response(strm, close_connection, req, res);
    6083             :   }
    6084             : 
    6085             :   // Request line and headers
    6086           0 :   if (!parse_request_line(line_reader.ptr(), req) ||
    6087           0 :       !detail::read_headers(strm, req.headers)) {
    6088           0 :     res.status = 400;
    6089           0 :     return write_response(strm, close_connection, req, res);
    6090             :   }
    6091             : 
    6092           0 :   if (req.get_header_value("Connection") == "close") {
    6093           0 :     connection_closed = true;
    6094             :   }
    6095             : 
    6096           0 :   if (req.version == "HTTP/1.0" &&
    6097           0 :       req.get_header_value("Connection") != "Keep-Alive") {
    6098           0 :     connection_closed = true;
    6099             :   }
    6100             : 
    6101           0 :   strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);
    6102           0 :   req.set_header("REMOTE_ADDR", req.remote_addr);
    6103           0 :   req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
    6104             : 
    6105           0 :   strm.get_local_ip_and_port(req.local_addr, req.local_port);
    6106           0 :   req.set_header("LOCAL_ADDR", req.local_addr);
    6107           0 :   req.set_header("LOCAL_PORT", std::to_string(req.local_port));
    6108             : 
    6109           0 :   if (req.has_header("Range")) {
    6110           0 :     const auto &range_header_value = req.get_header_value("Range");
    6111           0 :     if (!detail::parse_range_header(range_header_value, req.ranges)) {
    6112           0 :       res.status = 416;
    6113           0 :       return write_response(strm, close_connection, req, res);
    6114             :     }
    6115             :   }
    6116             : 
    6117           0 :   if (setup_request) { setup_request(req); }
    6118             : 
    6119           0 :   if (req.get_header_value("Expect") == "100-continue") {
    6120           0 :     auto status = 100;
    6121           0 :     if (expect_100_continue_handler_) {
    6122           0 :       status = expect_100_continue_handler_(req, res);
    6123             :     }
    6124           0 :     switch (status) {
    6125           0 :     case 100:
    6126           0 :     case 417:
    6127           0 :       strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status,
    6128           0 :                         detail::status_message(status));
    6129           0 :       break;
    6130           0 :     default: return write_response(strm, close_connection, req, res);
    6131             :     }
    6132             :   }
    6133             : 
    6134             :   // Rounting
    6135           0 :   bool routed = false;
    6136             : #ifdef CPPHTTPLIB_NO_EXCEPTIONS
    6137             :   routed = routing(req, res, strm);
    6138             : #else
    6139           0 :   try {
    6140           0 :     routed = routing(req, res, strm);
    6141           0 :   } catch (std::exception &e) {
    6142           0 :     if (exception_handler_) {
    6143           0 :       auto ep = std::current_exception();
    6144           0 :       exception_handler_(req, res, ep);
    6145           0 :       routed = true;
    6146             :     } else {
    6147           0 :       res.status = 500;
    6148           0 :       std::string val;
    6149           0 :       auto s = e.what();
    6150           0 :       for (size_t i = 0; s[i]; i++) {
    6151           0 :         switch (s[i]) {
    6152           0 :         case '\r': val += "\\r"; break;
    6153           0 :         case '\n': val += "\\n"; break;
    6154           0 :         default: val += s[i]; break;
    6155             :         }
    6156             :       }
    6157           0 :       res.set_header("EXCEPTION_WHAT", val);
    6158             :     }
    6159           0 :   } catch (...) {
    6160           0 :     if (exception_handler_) {
    6161           0 :       auto ep = std::current_exception();
    6162           0 :       exception_handler_(req, res, ep);
    6163           0 :       routed = true;
    6164             :     } else {
    6165           0 :       res.status = 500;
    6166           0 :       res.set_header("EXCEPTION_WHAT", "UNKNOWN");
    6167             :     }
    6168             :   }
    6169             : #endif
    6170             : 
    6171           0 :   if (routed) {
    6172           0 :     if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
    6173           0 :     return write_response_with_content(strm, close_connection, req, res);
    6174             :   } else {
    6175           0 :     if (res.status == -1) { res.status = 404; }
    6176           0 :     return write_response(strm, close_connection, req, res);
    6177             :   }
    6178             : }
    6179             : 
    6180           0 : inline bool Server::is_valid() const { return true; }
    6181             : 
    6182           0 : inline bool Server::process_and_close_socket(socket_t sock) {
    6183           0 :   auto ret = detail::process_server_socket(
    6184           0 :       svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
    6185             :       read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
    6186             :       write_timeout_usec_,
    6187           0 :       [this](Stream &strm, bool close_connection, bool &connection_closed) {
    6188           0 :         return process_request(strm, close_connection, connection_closed,
    6189           0 :                                nullptr);
    6190             :       });
    6191             : 
    6192           0 :   detail::shutdown_socket(sock);
    6193           0 :   detail::close_socket(sock);
    6194           0 :   return ret;
    6195             : }
    6196             : 
    6197             : // HTTP client implementation
    6198             : inline ClientImpl::ClientImpl(const std::string &host)
    6199             :     : ClientImpl(host, 80, std::string(), std::string()) {}
    6200             : 
    6201             : inline ClientImpl::ClientImpl(const std::string &host, int port)
    6202             :     : ClientImpl(host, port, std::string(), std::string()) {}
    6203             : 
    6204             : inline ClientImpl::ClientImpl(const std::string &host, int port,
    6205             :                               const std::string &client_cert_path,
    6206             :                               const std::string &client_key_path)
    6207             :     : host_(host), port_(port),
    6208             :       host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)),
    6209             :       client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
    6210             : 
    6211           0 : inline ClientImpl::~ClientImpl() {
    6212           0 :   std::lock_guard<std::mutex> guard(socket_mutex_);
    6213           0 :   shutdown_socket(socket_);
    6214           0 :   close_socket(socket_);
    6215           0 : }
    6216             : 
    6217           0 : inline bool ClientImpl::is_valid() const { return true; }
    6218             : 
    6219             : inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
    6220             :   client_cert_path_ = rhs.client_cert_path_;
    6221             :   client_key_path_ = rhs.client_key_path_;
    6222             :   connection_timeout_sec_ = rhs.connection_timeout_sec_;
    6223             :   read_timeout_sec_ = rhs.read_timeout_sec_;
    6224             :   read_timeout_usec_ = rhs.read_timeout_usec_;
    6225             :   write_timeout_sec_ = rhs.write_timeout_sec_;
    6226             :   write_timeout_usec_ = rhs.write_timeout_usec_;
    6227             :   basic_auth_username_ = rhs.basic_auth_username_;
    6228             :   basic_auth_password_ = rhs.basic_auth_password_;
    6229             :   bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
    6230             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    6231             :   digest_auth_username_ = rhs.digest_auth_username_;
    6232             :   digest_auth_password_ = rhs.digest_auth_password_;
    6233             : #endif
    6234             :   keep_alive_ = rhs.keep_alive_;
    6235             :   follow_location_ = rhs.follow_location_;
    6236             :   url_encode_ = rhs.url_encode_;
    6237             :   address_family_ = rhs.address_family_;
    6238             :   tcp_nodelay_ = rhs.tcp_nodelay_;
    6239             :   socket_options_ = rhs.socket_options_;
    6240             :   compress_ = rhs.compress_;
    6241             :   decompress_ = rhs.decompress_;
    6242             :   interface_ = rhs.interface_;
    6243             :   proxy_host_ = rhs.proxy_host_;
    6244             :   proxy_port_ = rhs.proxy_port_;
    6245             :   proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
    6246             :   proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
    6247             :   proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
    6248             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    6249             :   proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
    6250             :   proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
    6251             : #endif
    6252             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    6253             :   ca_cert_file_path_ = rhs.ca_cert_file_path_;
    6254             :   ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
    6255             :   ca_cert_store_ = rhs.ca_cert_store_;
    6256             : #endif
    6257             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    6258             :   server_certificate_verification_ = rhs.server_certificate_verification_;
    6259             : #endif
    6260             :   logger_ = rhs.logger_;
    6261             : }
    6262             : 
    6263           0 : inline socket_t ClientImpl::create_client_socket(Error &error) const {
    6264           0 :   if (!proxy_host_.empty() && proxy_port_ != -1) {
    6265           0 :     return detail::create_client_socket(
    6266           0 :         proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
    6267           0 :         socket_options_, connection_timeout_sec_, connection_timeout_usec_,
    6268           0 :         read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
    6269           0 :         write_timeout_usec_, interface_, error);
    6270             :   }
    6271             : 
    6272             :   // Check is custom IP specified for host_
    6273           0 :   std::string ip;
    6274           0 :   auto it = addr_map_.find(host_);
    6275           0 :   if (it != addr_map_.end()) ip = it->second;
    6276             : 
    6277           0 :   return detail::create_client_socket(
    6278           0 :       host_, ip, port_, address_family_, tcp_nodelay_, socket_options_,
    6279           0 :       connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_,
    6280           0 :       read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_,
    6281             :       error);
    6282             : }
    6283             : 
    6284           0 : inline bool ClientImpl::create_and_connect_socket(Socket &socket,
    6285             :                                                   Error &error) {
    6286           0 :   auto sock = create_client_socket(error);
    6287           0 :   if (sock == INVALID_SOCKET) { return false; }
    6288           0 :   socket.sock = sock;
    6289           0 :   return true;
    6290             : }
    6291             : 
    6292           0 : inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,
    6293             :                                      bool /*shutdown_gracefully*/) {
    6294             :   // If there are any requests in flight from threads other than us, then it's
    6295             :   // a thread-unsafe race because individual ssl* objects are not thread-safe.
    6296           0 :   assert(socket_requests_in_flight_ == 0 ||
    6297             :          socket_requests_are_from_thread_ == std::this_thread::get_id());
    6298           0 : }
    6299             : 
    6300           0 : inline void ClientImpl::shutdown_socket(Socket &socket) {
    6301           0 :   if (socket.sock == INVALID_SOCKET) { return; }
    6302           0 :   detail::shutdown_socket(socket.sock);
    6303             : }
    6304             : 
    6305             : inline void ClientImpl::close_socket(Socket &socket) {
    6306             :   // If there are requests in flight in another thread, usually closing
    6307             :   // the socket will be fine and they will simply receive an error when
    6308             :   // using the closed socket, but it is still a bug since rarely the OS
    6309             :   // may reassign the socket id to be used for a new socket, and then
    6310             :   // suddenly they will be operating on a live socket that is different
    6311             :   // than the one they intended!
    6312             :   assert(socket_requests_in_flight_ == 0 ||
    6313             :          socket_requests_are_from_thread_ == std::this_thread::get_id());
    6314             : 
    6315             :   // It is also a bug if this happens while SSL is still active
    6316             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    6317             :   assert(socket.ssl == nullptr);
    6318             : #endif
    6319             :   if (socket.sock == INVALID_SOCKET) { return; }
    6320             :   detail::close_socket(socket.sock);
    6321             :   socket.sock = INVALID_SOCKET;
    6322             : }
    6323             : 
    6324             : inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
    6325             :                                            Response &res) {
    6326             :   std::array<char, 2048> buf{};
    6327             : 
    6328             :   detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
    6329             : 
    6330             :   if (!line_reader.getline()) { return false; }
    6331             : 
    6332             : #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
    6333             :   const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
    6334             : #else
    6335             :   const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
    6336             : #endif
    6337             : 
    6338             :   std::cmatch m;
    6339             :   if (!std::regex_match(line_reader.ptr(), m, re)) {
    6340             :     return req.method == "CONNECT";
    6341             :   }
    6342             :   res.version = std::string(m[1]);
    6343             :   res.status = std::stoi(std::string(m[2]));
    6344             :   res.reason = std::string(m[3]);
    6345             : 
    6346             :   // Ignore '100 Continue'
    6347             :   while (res.status == 100) {
    6348             :     if (!line_reader.getline()) { return false; } // CRLF
    6349             :     if (!line_reader.getline()) { return false; } // next response line
    6350             : 
    6351             :     if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
    6352             :     res.version = std::string(m[1]);
    6353             :     res.status = std::stoi(std::string(m[2]));
    6354             :     res.reason = std::string(m[3]);
    6355             :   }
    6356             : 
    6357             :   return true;
    6358             : }
    6359             : 
    6360             : inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
    6361             :   std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
    6362             :   auto ret = send_(req, res, error);
    6363             :   if (error == Error::SSLPeerCouldBeClosed_) {
    6364             :     assert(!ret);
    6365             :     ret = send_(req, res, error);
    6366             :   }
    6367             :   return ret;
    6368             : }
    6369             : 
    6370             : inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
    6371             :   {
    6372             :     std::lock_guard<std::mutex> guard(socket_mutex_);
    6373             : 
    6374             :     // Set this to false immediately - if it ever gets set to true by the end of
    6375             :     // the request, we know another thread instructed us to close the socket.
    6376             :     socket_should_be_closed_when_request_is_done_ = false;
    6377             : 
    6378             :     auto is_alive = false;
    6379             :     if (socket_.is_open()) {
    6380             :       is_alive = detail::is_socket_alive(socket_.sock);
    6381             :       if (!is_alive) {
    6382             :         // Attempt to avoid sigpipe by shutting down nongracefully if it seems
    6383             :         // like the other side has already closed the connection Also, there
    6384             :         // cannot be any requests in flight from other threads since we locked
    6385             :         // request_mutex_, so safe to close everything immediately
    6386             :         const bool shutdown_gracefully = false;
    6387             :         shutdown_ssl(socket_, shutdown_gracefully);
    6388             :         shutdown_socket(socket_);
    6389             :         close_socket(socket_);
    6390             :       }
    6391             :     }
    6392             : 
    6393             :     if (!is_alive) {
    6394             :       if (!create_and_connect_socket(socket_, error)) { return false; }
    6395             : 
    6396             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    6397             :       // TODO: refactoring
    6398             :       if (is_ssl()) {
    6399             :         auto &scli = static_cast<SSLClient &>(*this);
    6400             :         if (!proxy_host_.empty() && proxy_port_ != -1) {
    6401             :           auto success = false;
    6402             :           if (!scli.connect_with_proxy(socket_, res, success, error)) {
    6403             :             return success;
    6404             :           }
    6405             :         }
    6406             : 
    6407             :         if (!scli.initialize_ssl(socket_, error)) { return false; }
    6408             :       }
    6409             : #endif
    6410             :     }
    6411             : 
    6412             :     // Mark the current socket as being in use so that it cannot be closed by
    6413             :     // anyone else while this request is ongoing, even though we will be
    6414             :     // releasing the mutex.
    6415             :     if (socket_requests_in_flight_ > 1) {
    6416             :       assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
    6417             :     }
    6418             :     socket_requests_in_flight_ += 1;
    6419             :     socket_requests_are_from_thread_ = std::this_thread::get_id();
    6420             :   }
    6421             : 
    6422             :   for (const auto &header : default_headers_) {
    6423             :     if (req.headers.find(header.first) == req.headers.end()) {
    6424             :       req.headers.insert(header);
    6425             :     }
    6426             :   }
    6427             : 
    6428             :   auto ret = false;
    6429             :   auto close_connection = !keep_alive_;
    6430             : 
    6431             :   auto se = detail::scope_exit([&]() {
    6432             :     // Briefly lock mutex in order to mark that a request is no longer ongoing
    6433             :     std::lock_guard<std::mutex> guard(socket_mutex_);
    6434             :     socket_requests_in_flight_ -= 1;
    6435             :     if (socket_requests_in_flight_ <= 0) {
    6436             :       assert(socket_requests_in_flight_ == 0);
    6437             :       socket_requests_are_from_thread_ = std::thread::id();
    6438             :     }
    6439             : 
    6440             :     if (socket_should_be_closed_when_request_is_done_ || close_connection ||
    6441             :         !ret) {
    6442             :       shutdown_ssl(socket_, true);
    6443             :       shutdown_socket(socket_);
    6444             :       close_socket(socket_);
    6445             :     }
    6446             :   });
    6447             : 
    6448             :   ret = process_socket(socket_, [&](Stream &strm) {
    6449             :     return handle_request(strm, req, res, close_connection, error);
    6450             :   });
    6451             : 
    6452             :   if (!ret) {
    6453             :     if (error == Error::Success) { error = Error::Unknown; }
    6454             :   }
    6455             : 
    6456             :   return ret;
    6457             : }
    6458             : 
    6459             : inline Result ClientImpl::send(const Request &req) {
    6460             :   auto req2 = req;
    6461             :   return send_(std::move(req2));
    6462             : }
    6463             : 
    6464             : inline Result ClientImpl::send_(Request &&req) {
    6465             :   auto res = detail::make_unique<Response>();
    6466             :   auto error = Error::Success;
    6467             :   auto ret = send(req, *res, error);
    6468             :   return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
    6469             : }
    6470             : 
    6471             : inline bool ClientImpl::handle_request(Stream &strm, Request &req,
    6472             :                                        Response &res, bool close_connection,
    6473             :                                        Error &error) {
    6474             :   if (req.path.empty()) {
    6475             :     error = Error::Connection;
    6476             :     return false;
    6477             :   }
    6478             : 
    6479             :   auto req_save = req;
    6480             : 
    6481             :   bool ret;
    6482             : 
    6483             :   if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
    6484             :     auto req2 = req;
    6485             :     req2.path = "http://" + host_and_port_ + req.path;
    6486             :     ret = process_request(strm, req2, res, close_connection, error);
    6487             :     req = req2;
    6488             :     req.path = req_save.path;
    6489             :   } else {
    6490             :     ret = process_request(strm, req, res, close_connection, error);
    6491             :   }
    6492             : 
    6493             :   if (!ret) { return false; }
    6494             : 
    6495             :   if (300 < res.status && res.status < 400 && follow_location_) {
    6496             :     req = req_save;
    6497             :     ret = redirect(req, res, error);
    6498             :   }
    6499             : 
    6500             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    6501             :   if ((res.status == 401 || res.status == 407) &&
    6502             :       req.authorization_count_ < 5) {
    6503             :     auto is_proxy = res.status == 407;
    6504             :     const auto &username =
    6505             :         is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
    6506             :     const auto &password =
    6507             :         is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
    6508             : 
    6509             :     if (!username.empty() && !password.empty()) {
    6510             :       std::map<std::string, std::string> auth;
    6511             :       if (detail::parse_www_authenticate(res, auth, is_proxy)) {
    6512             :         Request new_req = req;
    6513             :         new_req.authorization_count_ += 1;
    6514             :         new_req.headers.erase(is_proxy ? "Proxy-Authorization"
    6515             :                                        : "Authorization");
    6516             :         new_req.headers.insert(detail::make_digest_authentication_header(
    6517             :             req, auth, new_req.authorization_count_, detail::random_string(10),
    6518             :             username, password, is_proxy));
    6519             : 
    6520             :         Response new_res;
    6521             : 
    6522             :         ret = send(new_req, new_res, error);
    6523             :         if (ret) { res = new_res; }
    6524             :       }
    6525             :     }
    6526             :   }
    6527             : #endif
    6528             : 
    6529             :   return ret;
    6530             : }
    6531             : 
    6532             : inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
    6533             :   if (req.redirect_count_ == 0) {
    6534             :     error = Error::ExceedRedirectCount;
    6535             :     return false;
    6536             :   }
    6537             : 
    6538             :   auto location = res.get_header_value("location");
    6539             :   if (location.empty()) { return false; }
    6540             : 
    6541             :   const static std::regex re(
    6542             :       R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
    6543             : 
    6544             :   std::smatch m;
    6545             :   if (!std::regex_match(location, m, re)) { return false; }
    6546             : 
    6547             :   auto scheme = is_ssl() ? "https" : "http";
    6548             : 
    6549             :   auto next_scheme = m[1].str();
    6550             :   auto next_host = m[2].str();
    6551             :   if (next_host.empty()) { next_host = m[3].str(); }
    6552             :   auto port_str = m[4].str();
    6553             :   auto next_path = m[5].str();
    6554             :   auto next_query = m[6].str();
    6555             : 
    6556             :   auto next_port = port_;
    6557             :   if (!port_str.empty()) {
    6558             :     next_port = std::stoi(port_str);
    6559             :   } else if (!next_scheme.empty()) {
    6560             :     next_port = next_scheme == "https" ? 443 : 80;
    6561             :   }
    6562             : 
    6563             :   if (next_scheme.empty()) { next_scheme = scheme; }
    6564             :   if (next_host.empty()) { next_host = host_; }
    6565             :   if (next_path.empty()) { next_path = "/"; }
    6566             : 
    6567             :   auto path = detail::decode_url(next_path, true) + next_query;
    6568             : 
    6569             :   if (next_scheme == scheme && next_host == host_ && next_port == port_) {
    6570             :     return detail::redirect(*this, req, res, path, location, error);
    6571             :   } else {
    6572             :     if (next_scheme == "https") {
    6573             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    6574             :       SSLClient cli(next_host.c_str(), next_port);
    6575             :       cli.copy_settings(*this);
    6576             :       if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
    6577             :       return detail::redirect(cli, req, res, path, location, error);
    6578             : #else
    6579             :       return false;
    6580             : #endif
    6581             :     } else {
    6582             :       ClientImpl cli(next_host.c_str(), next_port);
    6583             :       cli.copy_settings(*this);
    6584             :       return detail::redirect(cli, req, res, path, location, error);
    6585             :     }
    6586             :   }
    6587             : }
    6588             : 
    6589             : inline bool ClientImpl::write_content_with_provider(Stream &strm,
    6590             :                                                     const Request &req,
    6591             :                                                     Error &error) {
    6592             :   auto is_shutting_down = []() { return false; };
    6593             : 
    6594             :   if (req.is_chunked_content_provider_) {
    6595             :     // TODO: Brotli support
    6596             :     std::unique_ptr<detail::compressor> compressor;
    6597             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
    6598             :     if (compress_) {
    6599             :       compressor = detail::make_unique<detail::gzip_compressor>();
    6600             :     } else
    6601             : #endif
    6602             :     {
    6603             :       compressor = detail::make_unique<detail::nocompressor>();
    6604             :     }
    6605             : 
    6606             :     return detail::write_content_chunked(strm, req.content_provider_,
    6607             :                                          is_shutting_down, *compressor, error);
    6608             :   } else {
    6609             :     return detail::write_content(strm, req.content_provider_, 0,
    6610             :                                  req.content_length_, is_shutting_down, error);
    6611             :   }
    6612             : }
    6613             : 
    6614             : inline bool ClientImpl::write_request(Stream &strm, Request &req,
    6615             :                                       bool close_connection, Error &error) {
    6616             :   // Prepare additional headers
    6617             :   if (close_connection) {
    6618             :     if (!req.has_header("Connection")) {
    6619             :       req.headers.emplace("Connection", "close");
    6620             :     }
    6621             :   }
    6622             : 
    6623             :   if (!req.has_header("Host")) {
    6624             :     if (is_ssl()) {
    6625             :       if (port_ == 443) {
    6626             :         req.headers.emplace("Host", host_);
    6627             :       } else {
    6628             :         req.headers.emplace("Host", host_and_port_);
    6629             :       }
    6630             :     } else {
    6631             :       if (port_ == 80) {
    6632             :         req.headers.emplace("Host", host_);
    6633             :       } else {
    6634             :         req.headers.emplace("Host", host_and_port_);
    6635             :       }
    6636             :     }
    6637             :   }
    6638             : 
    6639             :   if (!req.has_header("Accept")) { req.headers.emplace("Accept", "*/*"); }
    6640             : 
    6641             : #ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
    6642             :   if (!req.has_header("User-Agent")) {
    6643             :     auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
    6644             :     req.headers.emplace("User-Agent", agent);
    6645             :   }
    6646             : #endif
    6647             : 
    6648             :   if (req.body.empty()) {
    6649             :     if (req.content_provider_) {
    6650             :       if (!req.is_chunked_content_provider_) {
    6651             :         if (!req.has_header("Content-Length")) {
    6652             :           auto length = std::to_string(req.content_length_);
    6653             :           req.headers.emplace("Content-Length", length);
    6654             :         }
    6655             :       }
    6656             :     } else {
    6657             :       if (req.method == "POST" || req.method == "PUT" ||
    6658             :           req.method == "PATCH") {
    6659             :         req.headers.emplace("Content-Length", "0");
    6660             :       }
    6661             :     }
    6662             :   } else {
    6663             :     if (!req.has_header("Content-Type")) {
    6664             :       req.headers.emplace("Content-Type", "text/plain");
    6665             :     }
    6666             : 
    6667             :     if (!req.has_header("Content-Length")) {
    6668             :       auto length = std::to_string(req.body.size());
    6669             :       req.headers.emplace("Content-Length", length);
    6670             :     }
    6671             :   }
    6672             : 
    6673             :   if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
    6674             :     if (!req.has_header("Authorization")) {
    6675             :       req.headers.insert(make_basic_authentication_header(
    6676             :           basic_auth_username_, basic_auth_password_, false));
    6677             :     }
    6678             :   }
    6679             : 
    6680             :   if (!proxy_basic_auth_username_.empty() &&
    6681             :       !proxy_basic_auth_password_.empty()) {
    6682             :     if (!req.has_header("Proxy-Authorization")) {
    6683             :       req.headers.insert(make_basic_authentication_header(
    6684             :           proxy_basic_auth_username_, proxy_basic_auth_password_, true));
    6685             :     }
    6686             :   }
    6687             : 
    6688             :   if (!bearer_token_auth_token_.empty()) {
    6689             :     if (!req.has_header("Authorization")) {
    6690             :       req.headers.insert(make_bearer_token_authentication_header(
    6691             :           bearer_token_auth_token_, false));
    6692             :     }
    6693             :   }
    6694             : 
    6695             :   if (!proxy_bearer_token_auth_token_.empty()) {
    6696             :     if (!req.has_header("Proxy-Authorization")) {
    6697             :       req.headers.insert(make_bearer_token_authentication_header(
    6698             :           proxy_bearer_token_auth_token_, true));
    6699             :     }
    6700             :   }
    6701             : 
    6702             :   // Request line and headers
    6703             :   {
    6704             :     detail::BufferStream bstrm;
    6705             : 
    6706             :     const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path;
    6707             :     bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
    6708             : 
    6709             :     detail::write_headers(bstrm, req.headers);
    6710             : 
    6711             :     // Flush buffer
    6712             :     auto &data = bstrm.get_buffer();
    6713             :     if (!detail::write_data(strm, data.data(), data.size())) {
    6714             :       error = Error::Write;
    6715             :       return false;
    6716             :     }
    6717             :   }
    6718             : 
    6719             :   // Body
    6720             :   if (req.body.empty()) {
    6721             :     return write_content_with_provider(strm, req, error);
    6722             :   }
    6723             : 
    6724             :   if (!detail::write_data(strm, req.body.data(), req.body.size())) {
    6725             :     error = Error::Write;
    6726             :     return false;
    6727             :   }
    6728             : 
    6729             :   return true;
    6730             : }
    6731             : 
    6732             : inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
    6733             :     Request &req, const char *body, size_t content_length,
    6734             :     ContentProvider content_provider,
    6735             :     ContentProviderWithoutLength content_provider_without_length,
    6736             :     const std::string &content_type, Error &error) {
    6737             :   if (!content_type.empty()) {
    6738             :     req.headers.emplace("Content-Type", content_type);
    6739             :   }
    6740             : 
    6741             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
    6742             :   if (compress_) { req.headers.emplace("Content-Encoding", "gzip"); }
    6743             : #endif
    6744             : 
    6745             : #ifdef CPPHTTPLIB_ZLIB_SUPPORT
    6746             :   if (compress_ && !content_provider_without_length) {
    6747             :     // TODO: Brotli support
    6748             :     detail::gzip_compressor compressor;
    6749             : 
    6750             :     if (content_provider) {
    6751             :       auto ok = true;
    6752             :       size_t offset = 0;
    6753             :       DataSink data_sink;
    6754             : 
    6755             :       data_sink.write = [&](const char *data, size_t data_len) -> bool {
    6756             :         if (ok) {
    6757             :           auto last = offset + data_len == content_length;
    6758             : 
    6759             :           auto ret = compressor.compress(
    6760             :               data, data_len, last,
    6761             :               [&](const char *compressed_data, size_t compressed_data_len) {
    6762             :                 req.body.append(compressed_data, compressed_data_len);
    6763             :                 return true;
    6764             :               });
    6765             : 
    6766             :           if (ret) {
    6767             :             offset += data_len;
    6768             :           } else {
    6769             :             ok = false;
    6770             :           }
    6771             :         }
    6772             :         return ok;
    6773             :       };
    6774             : 
    6775             :       while (ok && offset < content_length) {
    6776             :         if (!content_provider(offset, content_length - offset, data_sink)) {
    6777             :           error = Error::Canceled;
    6778             :           return nullptr;
    6779             :         }
    6780             :       }
    6781             :     } else {
    6782             :       if (!compressor.compress(body, content_length, true,
    6783             :                                [&](const char *data, size_t data_len) {
    6784             :                                  req.body.append(data, data_len);
    6785             :                                  return true;
    6786             :                                })) {
    6787             :         error = Error::Compression;
    6788             :         return nullptr;
    6789             :       }
    6790             :     }
    6791             :   } else
    6792             : #endif
    6793             :   {
    6794             :     if (content_provider) {
    6795             :       req.content_length_ = content_length;
    6796             :       req.content_provider_ = std::move(content_provider);
    6797             :       req.is_chunked_content_provider_ = false;
    6798             :     } else if (content_provider_without_length) {
    6799             :       req.content_length_ = 0;
    6800             :       req.content_provider_ = detail::ContentProviderAdapter(
    6801             :           std::move(content_provider_without_length));
    6802             :       req.is_chunked_content_provider_ = true;
    6803             :       req.headers.emplace("Transfer-Encoding", "chunked");
    6804             :     } else {
    6805             :       req.body.assign(body, content_length);
    6806             :       ;
    6807             :     }
    6808             :   }
    6809             : 
    6810             :   auto res = detail::make_unique<Response>();
    6811             :   return send(req, *res, error) ? std::move(res) : nullptr;
    6812             : }
    6813             : 
    6814             : inline Result ClientImpl::send_with_content_provider(
    6815             :     const std::string &method, const std::string &path, const Headers &headers,
    6816             :     const char *body, size_t content_length, ContentProvider content_provider,
    6817             :     ContentProviderWithoutLength content_provider_without_length,
    6818             :     const std::string &content_type) {
    6819             :   Request req;
    6820             :   req.method = method;
    6821             :   req.headers = headers;
    6822             :   req.path = path;
    6823             : 
    6824             :   auto error = Error::Success;
    6825             : 
    6826             :   auto res = send_with_content_provider(
    6827             :       req, body, content_length, std::move(content_provider),
    6828             :       std::move(content_provider_without_length), content_type, error);
    6829             : 
    6830             :   return Result{std::move(res), error, std::move(req.headers)};
    6831             : }
    6832             : 
    6833             : inline std::string
    6834             : ClientImpl::adjust_host_string(const std::string &host) const {
    6835             :   if (host.find(':') != std::string::npos) { return "[" + host + "]"; }
    6836             :   return host;
    6837             : }
    6838             : 
    6839             : inline bool ClientImpl::process_request(Stream &strm, Request &req,
    6840             :                                         Response &res, bool close_connection,
    6841             :                                         Error &error) {
    6842             :   // Send request
    6843             :   if (!write_request(strm, req, close_connection, error)) { return false; }
    6844             : 
    6845             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    6846             :   if (is_ssl()) {
    6847             :     auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
    6848             :     if (!is_proxy_enabled) {
    6849             :       char buf[1];
    6850             :       if (SSL_peek(socket_.ssl, buf, 1) == 0 &&
    6851             :           SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) {
    6852             :         error = Error::SSLPeerCouldBeClosed_;
    6853             :         return false;
    6854             :       }
    6855             :     }
    6856             :   }
    6857             : #endif
    6858             : 
    6859             :   // Receive response and headers
    6860             :   if (!read_response_line(strm, req, res) ||
    6861             :       !detail::read_headers(strm, res.headers)) {
    6862             :     error = Error::Read;
    6863             :     return false;
    6864             :   }
    6865             : 
    6866             :   // Body
    6867             :   if ((res.status != 204) && req.method != "HEAD" && req.method != "CONNECT") {
    6868             :     auto redirect = 300 < res.status && res.status < 400 && follow_location_;
    6869             : 
    6870             :     if (req.response_handler && !redirect) {
    6871             :       if (!req.response_handler(res)) {
    6872             :         error = Error::Canceled;
    6873             :         return false;
    6874             :       }
    6875             :     }
    6876             : 
    6877             :     auto out =
    6878             :         req.content_receiver
    6879             :             ? static_cast<ContentReceiverWithProgress>(
    6880             :                   [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
    6881             :                     if (redirect) { return true; }
    6882             :                     auto ret = req.content_receiver(buf, n, off, len);
    6883             :                     if (!ret) { error = Error::Canceled; }
    6884             :                     return ret;
    6885             :                   })
    6886             :             : static_cast<ContentReceiverWithProgress>(
    6887             :                   [&](const char *buf, size_t n, uint64_t /*off*/,
    6888             :                       uint64_t /*len*/) {
    6889             :                     if (res.body.size() + n > res.body.max_size()) {
    6890             :                       return false;
    6891             :                     }
    6892             :                     res.body.append(buf, n);
    6893             :                     return true;
    6894             :                   });
    6895             : 
    6896             :     auto progress = [&](uint64_t current, uint64_t total) {
    6897             :       if (!req.progress || redirect) { return true; }
    6898             :       auto ret = req.progress(current, total);
    6899             :       if (!ret) { error = Error::Canceled; }
    6900             :       return ret;
    6901             :     };
    6902             : 
    6903             :     int dummy_status;
    6904             :     if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
    6905             :                               dummy_status, std::move(progress), std::move(out),
    6906             :                               decompress_)) {
    6907             :       if (error != Error::Canceled) { error = Error::Read; }
    6908             :       return false;
    6909             :     }
    6910             :   }
    6911             : 
    6912             :   if (res.get_header_value("Connection") == "close" ||
    6913             :       (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
    6914             :     // TODO this requires a not-entirely-obvious chain of calls to be correct
    6915             :     // for this to be safe. Maybe a code refactor (such as moving this out to
    6916             :     // the send function and getting rid of the recursiveness of the mutex)
    6917             :     // could make this more obvious.
    6918             : 
    6919             :     // This is safe to call because process_request is only called by
    6920             :     // handle_request which is only called by send, which locks the request
    6921             :     // mutex during the process. It would be a bug to call it from a different
    6922             :     // thread since it's a thread-safety issue to do these things to the socket
    6923             :     // if another thread is using the socket.
    6924             :     std::lock_guard<std::mutex> guard(socket_mutex_);
    6925             :     shutdown_ssl(socket_, true);
    6926             :     shutdown_socket(socket_);
    6927             :     close_socket(socket_);
    6928             :   }
    6929             : 
    6930             :   // Log
    6931             :   if (logger_) { logger_(req, res); }
    6932             : 
    6933             :   return true;
    6934             : }
    6935             : 
    6936             : inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
    6937             :     const std::string &boundary, const MultipartFormDataItems &items,
    6938             :     const MultipartFormDataProviderItems &provider_items) {
    6939             :   size_t cur_item = 0, cur_start = 0;
    6940             :   // cur_item and cur_start are copied to within the std::function and maintain
    6941             :   // state between successive calls
    6942             :   return [&, cur_item, cur_start](size_t offset,
    6943             :                                   DataSink &sink) mutable -> bool {
    6944             :     if (!offset && items.size()) {
    6945             :       sink.os << detail::serialize_multipart_formdata(items, boundary, false);
    6946             :       return true;
    6947             :     } else if (cur_item < provider_items.size()) {
    6948             :       if (!cur_start) {
    6949             :         const auto &begin = detail::serialize_multipart_formdata_item_begin(
    6950             :             provider_items[cur_item], boundary);
    6951             :         offset += begin.size();
    6952             :         cur_start = offset;
    6953             :         sink.os << begin;
    6954             :       }
    6955             : 
    6956             :       DataSink cur_sink;
    6957             :       bool has_data = true;
    6958             :       cur_sink.write = sink.write;
    6959             :       cur_sink.done = [&]() { has_data = false; };
    6960             : 
    6961             :       if (!provider_items[cur_item].provider(offset - cur_start, cur_sink))
    6962             :         return false;
    6963             : 
    6964             :       if (!has_data) {
    6965             :         sink.os << detail::serialize_multipart_formdata_item_end();
    6966             :         cur_item++;
    6967             :         cur_start = 0;
    6968             :       }
    6969             :       return true;
    6970             :     } else {
    6971             :       sink.os << detail::serialize_multipart_formdata_finish(boundary);
    6972             :       sink.done();
    6973             :       return true;
    6974             :     }
    6975             :   };
    6976             : }
    6977             : 
    6978             : inline bool
    6979           0 : ClientImpl::process_socket(const Socket &socket,
    6980             :                            std::function<bool(Stream &strm)> callback) {
    6981           0 :   return detail::process_client_socket(
    6982           0 :       socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
    6983           0 :       write_timeout_usec_, std::move(callback));
    6984             : }
    6985             : 
    6986           0 : inline bool ClientImpl::is_ssl() const { return false; }
    6987             : 
    6988             : inline Result ClientImpl::Get(const std::string &path) {
    6989             :   return Get(path, Headers(), Progress());
    6990             : }
    6991             : 
    6992             : inline Result ClientImpl::Get(const std::string &path, Progress progress) {
    6993             :   return Get(path, Headers(), std::move(progress));
    6994             : }
    6995             : 
    6996             : inline Result ClientImpl::Get(const std::string &path, const Headers &headers) {
    6997             :   return Get(path, headers, Progress());
    6998             : }
    6999             : 
    7000             : inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
    7001             :                               Progress progress) {
    7002             :   Request req;
    7003             :   req.method = "GET";
    7004             :   req.path = path;
    7005             :   req.headers = headers;
    7006             :   req.progress = std::move(progress);
    7007             : 
    7008             :   return send_(std::move(req));
    7009             : }
    7010             : 
    7011             : inline Result ClientImpl::Get(const std::string &path,
    7012             :                               ContentReceiver content_receiver) {
    7013             :   return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr);
    7014             : }
    7015             : 
    7016             : inline Result ClientImpl::Get(const std::string &path,
    7017             :                               ContentReceiver content_receiver,
    7018             :                               Progress progress) {
    7019             :   return Get(path, Headers(), nullptr, std::move(content_receiver),
    7020             :              std::move(progress));
    7021             : }
    7022             : 
    7023             : inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
    7024             :                               ContentReceiver content_receiver) {
    7025             :   return Get(path, headers, nullptr, std::move(content_receiver), nullptr);
    7026             : }
    7027             : 
    7028             : inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
    7029             :                               ContentReceiver content_receiver,
    7030             :                               Progress progress) {
    7031             :   return Get(path, headers, nullptr, std::move(content_receiver),
    7032             :              std::move(progress));
    7033             : }
    7034             : 
    7035             : inline Result ClientImpl::Get(const std::string &path,
    7036             :                               ResponseHandler response_handler,
    7037             :                               ContentReceiver content_receiver) {
    7038             :   return Get(path, Headers(), std::move(response_handler),
    7039             :              std::move(content_receiver), nullptr);
    7040             : }
    7041             : 
    7042             : inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
    7043             :                               ResponseHandler response_handler,
    7044             :                               ContentReceiver content_receiver) {
    7045             :   return Get(path, headers, std::move(response_handler),
    7046             :              std::move(content_receiver), nullptr);
    7047             : }
    7048             : 
    7049             : inline Result ClientImpl::Get(const std::string &path,
    7050             :                               ResponseHandler response_handler,
    7051             :                               ContentReceiver content_receiver,
    7052             :                               Progress progress) {
    7053             :   return Get(path, Headers(), std::move(response_handler),
    7054             :              std::move(content_receiver), std::move(progress));
    7055             : }
    7056             : 
    7057             : inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
    7058             :                               ResponseHandler response_handler,
    7059             :                               ContentReceiver content_receiver,
    7060             :                               Progress progress) {
    7061             :   Request req;
    7062             :   req.method = "GET";
    7063             :   req.path = path;
    7064             :   req.headers = headers;
    7065             :   req.response_handler = std::move(response_handler);
    7066             :   req.content_receiver =
    7067             :       [content_receiver](const char *data, size_t data_length,
    7068             :                          uint64_t /*offset*/, uint64_t /*total_length*/) {
    7069             :         return content_receiver(data, data_length);
    7070             :       };
    7071             :   req.progress = std::move(progress);
    7072             : 
    7073             :   return send_(std::move(req));
    7074             : }
    7075             : 
    7076             : inline Result ClientImpl::Get(const std::string &path, const Params &params,
    7077             :                               const Headers &headers, Progress progress) {
    7078             :   if (params.empty()) { return Get(path, headers); }
    7079             : 
    7080             :   std::string path_with_query = append_query_params(path, params);
    7081             :   return Get(path_with_query.c_str(), headers, progress);
    7082             : }
    7083             : 
    7084             : inline Result ClientImpl::Get(const std::string &path, const Params &params,
    7085             :                               const Headers &headers,
    7086             :                               ContentReceiver content_receiver,
    7087             :                               Progress progress) {
    7088             :   return Get(path, params, headers, nullptr, content_receiver, progress);
    7089             : }
    7090             : 
    7091             : inline Result ClientImpl::Get(const std::string &path, const Params &params,
    7092             :                               const Headers &headers,
    7093             :                               ResponseHandler response_handler,
    7094             :                               ContentReceiver content_receiver,
    7095             :                               Progress progress) {
    7096             :   if (params.empty()) {
    7097             :     return Get(path, headers, response_handler, content_receiver, progress);
    7098             :   }
    7099             : 
    7100             :   std::string path_with_query = append_query_params(path, params);
    7101             :   return Get(path_with_query.c_str(), headers, response_handler,
    7102             :              content_receiver, progress);
    7103             : }
    7104             : 
    7105             : inline Result ClientImpl::Head(const std::string &path) {
    7106             :   return Head(path, Headers());
    7107             : }
    7108             : 
    7109             : inline Result ClientImpl::Head(const std::string &path,
    7110             :                                const Headers &headers) {
    7111             :   Request req;
    7112             :   req.method = "HEAD";
    7113             :   req.headers = headers;
    7114             :   req.path = path;
    7115             : 
    7116             :   return send_(std::move(req));
    7117             : }
    7118             : 
    7119             : inline Result ClientImpl::Post(const std::string &path) {
    7120             :   return Post(path, std::string(), std::string());
    7121             : }
    7122             : 
    7123             : inline Result ClientImpl::Post(const std::string &path,
    7124             :                                const Headers &headers) {
    7125             :   return Post(path, headers, nullptr, 0, std::string());
    7126             : }
    7127             : 
    7128             : inline Result ClientImpl::Post(const std::string &path, const char *body,
    7129             :                                size_t content_length,
    7130             :                                const std::string &content_type) {
    7131             :   return Post(path, Headers(), body, content_length, content_type);
    7132             : }
    7133             : 
    7134             : inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
    7135             :                                const char *body, size_t content_length,
    7136             :                                const std::string &content_type) {
    7137             :   return send_with_content_provider("POST", path, headers, body, content_length,
    7138             :                                     nullptr, nullptr, content_type);
    7139             : }
    7140             : 
    7141             : inline Result ClientImpl::Post(const std::string &path, const std::string &body,
    7142             :                                const std::string &content_type) {
    7143             :   return Post(path, Headers(), body, content_type);
    7144             : }
    7145             : 
    7146             : inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
    7147             :                                const std::string &body,
    7148             :                                const std::string &content_type) {
    7149             :   return send_with_content_provider("POST", path, headers, body.data(),
    7150             :                                     body.size(), nullptr, nullptr,
    7151             :                                     content_type);
    7152             : }
    7153             : 
    7154             : inline Result ClientImpl::Post(const std::string &path, const Params &params) {
    7155             :   return Post(path, Headers(), params);
    7156             : }
    7157             : 
    7158             : inline Result ClientImpl::Post(const std::string &path, size_t content_length,
    7159             :                                ContentProvider content_provider,
    7160             :                                const std::string &content_type) {
    7161             :   return Post(path, Headers(), content_length, std::move(content_provider),
    7162             :               content_type);
    7163             : }
    7164             : 
    7165             : inline Result ClientImpl::Post(const std::string &path,
    7166             :                                ContentProviderWithoutLength content_provider,
    7167             :                                const std::string &content_type) {
    7168             :   return Post(path, Headers(), std::move(content_provider), content_type);
    7169             : }
    7170             : 
    7171             : inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
    7172             :                                size_t content_length,
    7173             :                                ContentProvider content_provider,
    7174             :                                const std::string &content_type) {
    7175             :   return send_with_content_provider("POST", path, headers, nullptr,
    7176             :                                     content_length, std::move(content_provider),
    7177             :                                     nullptr, content_type);
    7178             : }
    7179             : 
    7180             : inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
    7181             :                                ContentProviderWithoutLength content_provider,
    7182             :                                const std::string &content_type) {
    7183             :   return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr,
    7184             :                                     std::move(content_provider), content_type);
    7185             : }
    7186             : 
    7187             : inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
    7188             :                                const Params &params) {
    7189             :   auto query = detail::params_to_query_str(params);
    7190             :   return Post(path, headers, query, "application/x-www-form-urlencoded");
    7191             : }
    7192             : 
    7193             : inline Result ClientImpl::Post(const std::string &path,
    7194             :                                const MultipartFormDataItems &items) {
    7195             :   return Post(path, Headers(), items);
    7196             : }
    7197             : 
    7198             : inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
    7199             :                                const MultipartFormDataItems &items) {
    7200             :   const auto &boundary = detail::make_multipart_data_boundary();
    7201             :   const auto &content_type =
    7202             :       detail::serialize_multipart_formdata_get_content_type(boundary);
    7203             :   const auto &body = detail::serialize_multipart_formdata(items, boundary);
    7204             :   return Post(path, headers, body, content_type.c_str());
    7205             : }
    7206             : 
    7207             : inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
    7208             :                                const MultipartFormDataItems &items,
    7209             :                                const std::string &boundary) {
    7210             :   if (!detail::is_multipart_boundary_chars_valid(boundary)) {
    7211             :     return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
    7212             :   }
    7213             : 
    7214             :   const auto &content_type =
    7215             :       detail::serialize_multipart_formdata_get_content_type(boundary);
    7216             :   const auto &body = detail::serialize_multipart_formdata(items, boundary);
    7217             :   return Post(path, headers, body, content_type.c_str());
    7218             : }
    7219             : 
    7220             : inline Result
    7221             : ClientImpl::Post(const std::string &path, const Headers &headers,
    7222             :                  const MultipartFormDataItems &items,
    7223             :                  const MultipartFormDataProviderItems &provider_items) {
    7224             :   const auto &boundary = detail::make_multipart_data_boundary();
    7225             :   const auto &content_type =
    7226             :       detail::serialize_multipart_formdata_get_content_type(boundary);
    7227             :   return send_with_content_provider(
    7228             :       "POST", path, headers, nullptr, 0, nullptr,
    7229             :       get_multipart_content_provider(boundary, items, provider_items),
    7230             :       content_type);
    7231             : }
    7232             : 
    7233             : inline Result ClientImpl::Put(const std::string &path) {
    7234             :   return Put(path, std::string(), std::string());
    7235             : }
    7236             : 
    7237             : inline Result ClientImpl::Put(const std::string &path, const char *body,
    7238             :                               size_t content_length,
    7239             :                               const std::string &content_type) {
    7240             :   return Put(path, Headers(), body, content_length, content_type);
    7241             : }
    7242             : 
    7243             : inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
    7244             :                               const char *body, size_t content_length,
    7245             :                               const std::string &content_type) {
    7246             :   return send_with_content_provider("PUT", path, headers, body, content_length,
    7247             :                                     nullptr, nullptr, content_type);
    7248             : }
    7249             : 
    7250             : inline Result ClientImpl::Put(const std::string &path, const std::string &body,
    7251             :                               const std::string &content_type) {
    7252             :   return Put(path, Headers(), body, content_type);
    7253             : }
    7254             : 
    7255             : inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
    7256             :                               const std::string &body,
    7257             :                               const std::string &content_type) {
    7258             :   return send_with_content_provider("PUT", path, headers, body.data(),
    7259             :                                     body.size(), nullptr, nullptr,
    7260             :                                     content_type);
    7261             : }
    7262             : 
    7263             : inline Result ClientImpl::Put(const std::string &path, size_t content_length,
    7264             :                               ContentProvider content_provider,
    7265             :                               const std::string &content_type) {
    7266             :   return Put(path, Headers(), content_length, std::move(content_provider),
    7267             :              content_type);
    7268             : }
    7269             : 
    7270             : inline Result ClientImpl::Put(const std::string &path,
    7271             :                               ContentProviderWithoutLength content_provider,
    7272             :                               const std::string &content_type) {
    7273             :   return Put(path, Headers(), std::move(content_provider), content_type);
    7274             : }
    7275             : 
    7276             : inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
    7277             :                               size_t content_length,
    7278             :                               ContentProvider content_provider,
    7279             :                               const std::string &content_type) {
    7280             :   return send_with_content_provider("PUT", path, headers, nullptr,
    7281             :                                     content_length, std::move(content_provider),
    7282             :                                     nullptr, content_type);
    7283             : }
    7284             : 
    7285             : inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
    7286             :                               ContentProviderWithoutLength content_provider,
    7287             :                               const std::string &content_type) {
    7288             :   return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr,
    7289             :                                     std::move(content_provider), content_type);
    7290             : }
    7291             : 
    7292             : inline Result ClientImpl::Put(const std::string &path, const Params &params) {
    7293             :   return Put(path, Headers(), params);
    7294             : }
    7295             : 
    7296             : inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
    7297             :                               const Params &params) {
    7298             :   auto query = detail::params_to_query_str(params);
    7299             :   return Put(path, headers, query, "application/x-www-form-urlencoded");
    7300             : }
    7301             : 
    7302             : inline Result ClientImpl::Put(const std::string &path,
    7303             :                               const MultipartFormDataItems &items) {
    7304             :   return Put(path, Headers(), items);
    7305             : }
    7306             : 
    7307             : inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
    7308             :                               const MultipartFormDataItems &items) {
    7309             :   const auto &boundary = detail::make_multipart_data_boundary();
    7310             :   const auto &content_type =
    7311             :       detail::serialize_multipart_formdata_get_content_type(boundary);
    7312             :   const auto &body = detail::serialize_multipart_formdata(items, boundary);
    7313             :   return Put(path, headers, body, content_type);
    7314             : }
    7315             : 
    7316             : inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
    7317             :                               const MultipartFormDataItems &items,
    7318             :                               const std::string &boundary) {
    7319             :   if (!detail::is_multipart_boundary_chars_valid(boundary)) {
    7320             :     return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
    7321             :   }
    7322             : 
    7323             :   const auto &content_type =
    7324             :       detail::serialize_multipart_formdata_get_content_type(boundary);
    7325             :   const auto &body = detail::serialize_multipart_formdata(items, boundary);
    7326             :   return Put(path, headers, body, content_type);
    7327             : }
    7328             : 
    7329             : inline Result
    7330             : ClientImpl::Put(const std::string &path, const Headers &headers,
    7331             :                 const MultipartFormDataItems &items,
    7332             :                 const MultipartFormDataProviderItems &provider_items) {
    7333             :   const auto &boundary = detail::make_multipart_data_boundary();
    7334             :   const auto &content_type =
    7335             :       detail::serialize_multipart_formdata_get_content_type(boundary);
    7336             :   return send_with_content_provider(
    7337             :       "PUT", path, headers, nullptr, 0, nullptr,
    7338             :       get_multipart_content_provider(boundary, items, provider_items),
    7339             :       content_type);
    7340             : }
    7341             : inline Result ClientImpl::Patch(const std::string &path) {
    7342             :   return Patch(path, std::string(), std::string());
    7343             : }
    7344             : 
    7345             : inline Result ClientImpl::Patch(const std::string &path, const char *body,
    7346             :                                 size_t content_length,
    7347             :                                 const std::string &content_type) {
    7348             :   return Patch(path, Headers(), body, content_length, content_type);
    7349             : }
    7350             : 
    7351             : inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
    7352             :                                 const char *body, size_t content_length,
    7353             :                                 const std::string &content_type) {
    7354             :   return send_with_content_provider("PATCH", path, headers, body,
    7355             :                                     content_length, nullptr, nullptr,
    7356             :                                     content_type);
    7357             : }
    7358             : 
    7359             : inline Result ClientImpl::Patch(const std::string &path,
    7360             :                                 const std::string &body,
    7361             :                                 const std::string &content_type) {
    7362             :   return Patch(path, Headers(), body, content_type);
    7363             : }
    7364             : 
    7365             : inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
    7366             :                                 const std::string &body,
    7367             :                                 const std::string &content_type) {
    7368             :   return send_with_content_provider("PATCH", path, headers, body.data(),
    7369             :                                     body.size(), nullptr, nullptr,
    7370             :                                     content_type);
    7371             : }
    7372             : 
    7373             : inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
    7374             :                                 ContentProvider content_provider,
    7375             :                                 const std::string &content_type) {
    7376             :   return Patch(path, Headers(), content_length, std::move(content_provider),
    7377             :                content_type);
    7378             : }
    7379             : 
    7380             : inline Result ClientImpl::Patch(const std::string &path,
    7381             :                                 ContentProviderWithoutLength content_provider,
    7382             :                                 const std::string &content_type) {
    7383             :   return Patch(path, Headers(), std::move(content_provider), content_type);
    7384             : }
    7385             : 
    7386             : inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
    7387             :                                 size_t content_length,
    7388             :                                 ContentProvider content_provider,
    7389             :                                 const std::string &content_type) {
    7390             :   return send_with_content_provider("PATCH", path, headers, nullptr,
    7391             :                                     content_length, std::move(content_provider),
    7392             :                                     nullptr, content_type);
    7393             : }
    7394             : 
    7395             : inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
    7396             :                                 ContentProviderWithoutLength content_provider,
    7397             :                                 const std::string &content_type) {
    7398             :   return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr,
    7399             :                                     std::move(content_provider), content_type);
    7400             : }
    7401             : 
    7402             : inline Result ClientImpl::Delete(const std::string &path) {
    7403             :   return Delete(path, Headers(), std::string(), std::string());
    7404             : }
    7405             : 
    7406             : inline Result ClientImpl::Delete(const std::string &path,
    7407             :                                  const Headers &headers) {
    7408             :   return Delete(path, headers, std::string(), std::string());
    7409             : }
    7410             : 
    7411             : inline Result ClientImpl::Delete(const std::string &path, const char *body,
    7412             :                                  size_t content_length,
    7413             :                                  const std::string &content_type) {
    7414             :   return Delete(path, Headers(), body, content_length, content_type);
    7415             : }
    7416             : 
    7417             : inline Result ClientImpl::Delete(const std::string &path,
    7418             :                                  const Headers &headers, const char *body,
    7419             :                                  size_t content_length,
    7420             :                                  const std::string &content_type) {
    7421             :   Request req;
    7422             :   req.method = "DELETE";
    7423             :   req.headers = headers;
    7424             :   req.path = path;
    7425             : 
    7426             :   if (!content_type.empty()) {
    7427             :     req.headers.emplace("Content-Type", content_type);
    7428             :   }
    7429             :   req.body.assign(body, content_length);
    7430             : 
    7431             :   return send_(std::move(req));
    7432             : }
    7433             : 
    7434             : inline Result ClientImpl::Delete(const std::string &path,
    7435             :                                  const std::string &body,
    7436             :                                  const std::string &content_type) {
    7437             :   return Delete(path, Headers(), body.data(), body.size(), content_type);
    7438             : }
    7439             : 
    7440             : inline Result ClientImpl::Delete(const std::string &path,
    7441             :                                  const Headers &headers,
    7442             :                                  const std::string &body,
    7443             :                                  const std::string &content_type) {
    7444             :   return Delete(path, headers, body.data(), body.size(), content_type);
    7445             : }
    7446             : 
    7447             : inline Result ClientImpl::Options(const std::string &path) {
    7448             :   return Options(path, Headers());
    7449             : }
    7450             : 
    7451             : inline Result ClientImpl::Options(const std::string &path,
    7452             :                                   const Headers &headers) {
    7453             :   Request req;
    7454             :   req.method = "OPTIONS";
    7455             :   req.headers = headers;
    7456             :   req.path = path;
    7457             : 
    7458             :   return send_(std::move(req));
    7459             : }
    7460             : 
    7461             : inline size_t ClientImpl::is_socket_open() const {
    7462             :   std::lock_guard<std::mutex> guard(socket_mutex_);
    7463             :   return socket_.is_open();
    7464             : }
    7465             : 
    7466             : inline socket_t ClientImpl::socket() const { return socket_.sock; }
    7467             : 
    7468             : inline void ClientImpl::stop() {
    7469             :   std::lock_guard<std::mutex> guard(socket_mutex_);
    7470             : 
    7471             :   // If there is anything ongoing right now, the ONLY thread-safe thing we can
    7472             :   // do is to shutdown_socket, so that threads using this socket suddenly
    7473             :   // discover they can't read/write any more and error out. Everything else
    7474             :   // (closing the socket, shutting ssl down) is unsafe because these actions are
    7475             :   // not thread-safe.
    7476             :   if (socket_requests_in_flight_ > 0) {
    7477             :     shutdown_socket(socket_);
    7478             : 
    7479             :     // Aside from that, we set a flag for the socket to be closed when we're
    7480             :     // done.
    7481             :     socket_should_be_closed_when_request_is_done_ = true;
    7482             :     return;
    7483             :   }
    7484             : 
    7485             :   // Otherwise, still holding the mutex, we can shut everything down ourselves
    7486             :   shutdown_ssl(socket_, true);
    7487             :   shutdown_socket(socket_);
    7488             :   close_socket(socket_);
    7489             : }
    7490             : 
    7491             : inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
    7492             :   connection_timeout_sec_ = sec;
    7493             :   connection_timeout_usec_ = usec;
    7494             : }
    7495             : 
    7496             : inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
    7497             :   read_timeout_sec_ = sec;
    7498             :   read_timeout_usec_ = usec;
    7499             : }
    7500             : 
    7501             : inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
    7502             :   write_timeout_sec_ = sec;
    7503             :   write_timeout_usec_ = usec;
    7504             : }
    7505             : 
    7506             : inline void ClientImpl::set_basic_auth(const std::string &username,
    7507             :                                        const std::string &password) {
    7508             :   basic_auth_username_ = username;
    7509             :   basic_auth_password_ = password;
    7510             : }
    7511             : 
    7512             : inline void ClientImpl::set_bearer_token_auth(const std::string &token) {
    7513             :   bearer_token_auth_token_ = token;
    7514             : }
    7515             : 
    7516             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    7517             : inline void ClientImpl::set_digest_auth(const std::string &username,
    7518             :                                         const std::string &password) {
    7519             :   digest_auth_username_ = username;
    7520             :   digest_auth_password_ = password;
    7521             : }
    7522             : #endif
    7523             : 
    7524             : inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
    7525             : 
    7526             : inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
    7527             : 
    7528             : inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }
    7529             : 
    7530             : inline void
    7531             : ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
    7532             :   addr_map_ = std::move(addr_map);
    7533             : }
    7534             : 
    7535             : inline void ClientImpl::set_default_headers(Headers headers) {
    7536             :   default_headers_ = std::move(headers);
    7537             : }
    7538             : 
    7539             : inline void ClientImpl::set_address_family(int family) {
    7540             :   address_family_ = family;
    7541             : }
    7542             : 
    7543             : inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
    7544             : 
    7545             : inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
    7546             :   socket_options_ = std::move(socket_options);
    7547             : }
    7548             : 
    7549             : inline void ClientImpl::set_compress(bool on) { compress_ = on; }
    7550             : 
    7551             : inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
    7552             : 
    7553             : inline void ClientImpl::set_interface(const std::string &intf) {
    7554             :   interface_ = intf;
    7555             : }
    7556             : 
    7557             : inline void ClientImpl::set_proxy(const std::string &host, int port) {
    7558             :   proxy_host_ = host;
    7559             :   proxy_port_ = port;
    7560             : }
    7561             : 
    7562             : inline void ClientImpl::set_proxy_basic_auth(const std::string &username,
    7563             :                                              const std::string &password) {
    7564             :   proxy_basic_auth_username_ = username;
    7565             :   proxy_basic_auth_password_ = password;
    7566             : }
    7567             : 
    7568             : inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {
    7569             :   proxy_bearer_token_auth_token_ = token;
    7570             : }
    7571             : 
    7572             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    7573             : inline void ClientImpl::set_proxy_digest_auth(const std::string &username,
    7574             :                                               const std::string &password) {
    7575             :   proxy_digest_auth_username_ = username;
    7576             :   proxy_digest_auth_password_ = password;
    7577             : }
    7578             : #endif
    7579             : 
    7580             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    7581             : inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,
    7582             :                                          const std::string &ca_cert_dir_path) {
    7583             :   ca_cert_file_path_ = ca_cert_file_path;
    7584             :   ca_cert_dir_path_ = ca_cert_dir_path;
    7585             : }
    7586             : 
    7587             : inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
    7588             :   if (ca_cert_store && ca_cert_store != ca_cert_store_) {
    7589             :     ca_cert_store_ = ca_cert_store;
    7590             :   }
    7591             : }
    7592             : #endif
    7593             : 
    7594             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    7595             : inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
    7596             :   server_certificate_verification_ = enabled;
    7597             : }
    7598             : #endif
    7599             : 
    7600             : inline void ClientImpl::set_logger(Logger logger) {
    7601             :   logger_ = std::move(logger);
    7602             : }
    7603             : 
    7604             : /*
    7605             :  * SSL Implementation
    7606             :  */
    7607             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    7608             : namespace detail {
    7609             : 
    7610             : template <typename U, typename V>
    7611             : inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
    7612             :                     U SSL_connect_or_accept, V setup) {
    7613             :   SSL *ssl = nullptr;
    7614             :   {
    7615             :     std::lock_guard<std::mutex> guard(ctx_mutex);
    7616             :     ssl = SSL_new(ctx);
    7617             :   }
    7618             : 
    7619             :   if (ssl) {
    7620             :     set_nonblocking(sock, true);
    7621             :     auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
    7622             :     BIO_set_nbio(bio, 1);
    7623             :     SSL_set_bio(ssl, bio, bio);
    7624             : 
    7625             :     if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
    7626             :       SSL_shutdown(ssl);
    7627             :       {
    7628             :         std::lock_guard<std::mutex> guard(ctx_mutex);
    7629             :         SSL_free(ssl);
    7630             :       }
    7631             :       set_nonblocking(sock, false);
    7632             :       return nullptr;
    7633             :     }
    7634             :     BIO_set_nbio(bio, 0);
    7635             :     set_nonblocking(sock, false);
    7636             :   }
    7637             : 
    7638             :   return ssl;
    7639             : }
    7640             : 
    7641             : inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
    7642             :                        bool shutdown_gracefully) {
    7643             :   // sometimes we may want to skip this to try to avoid SIGPIPE if we know
    7644             :   // the remote has closed the network connection
    7645             :   // Note that it is not always possible to avoid SIGPIPE, this is merely a
    7646             :   // best-efforts.
    7647             :   if (shutdown_gracefully) { SSL_shutdown(ssl); }
    7648             : 
    7649             :   std::lock_guard<std::mutex> guard(ctx_mutex);
    7650             :   SSL_free(ssl);
    7651             : }
    7652             : 
    7653             : template <typename U>
    7654             : bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
    7655             :                                        U ssl_connect_or_accept,
    7656             :                                        time_t timeout_sec,
    7657             :                                        time_t timeout_usec) {
    7658             :   int res = 0;
    7659             :   while ((res = ssl_connect_or_accept(ssl)) != 1) {
    7660             :     auto err = SSL_get_error(ssl, res);
    7661             :     switch (err) {
    7662             :     case SSL_ERROR_WANT_READ:
    7663             :       if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
    7664             :       break;
    7665             :     case SSL_ERROR_WANT_WRITE:
    7666             :       if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
    7667             :       break;
    7668             :     default: break;
    7669             :     }
    7670             :     return false;
    7671             :   }
    7672             :   return true;
    7673             : }
    7674             : 
    7675             : template <typename T>
    7676             : inline bool process_server_socket_ssl(
    7677             :     const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,
    7678             :     size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
    7679             :     time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
    7680             :     time_t write_timeout_usec, T callback) {
    7681             :   return process_server_socket_core(
    7682             :       svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
    7683             :       [&](bool close_connection, bool &connection_closed) {
    7684             :         SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
    7685             :                              write_timeout_sec, write_timeout_usec);
    7686             :         return callback(strm, close_connection, connection_closed);
    7687             :       });
    7688             : }
    7689             : 
    7690             : template <typename T>
    7691             : inline bool
    7692             : process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec,
    7693             :                           time_t read_timeout_usec, time_t write_timeout_sec,
    7694             :                           time_t write_timeout_usec, T callback) {
    7695             :   SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
    7696             :                        write_timeout_sec, write_timeout_usec);
    7697             :   return callback(strm);
    7698             : }
    7699             : 
    7700             : class SSLInit {
    7701             : public:
    7702             :   SSLInit() {
    7703             :     OPENSSL_init_ssl(
    7704             :         OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
    7705             :   }
    7706             : };
    7707             : 
    7708             : // SSL socket stream implementation
    7709             : inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
    7710             :                                         time_t read_timeout_sec,
    7711             :                                         time_t read_timeout_usec,
    7712             :                                         time_t write_timeout_sec,
    7713             :                                         time_t write_timeout_usec)
    7714             :     : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
    7715             :       read_timeout_usec_(read_timeout_usec),
    7716             :       write_timeout_sec_(write_timeout_sec),
    7717             :       write_timeout_usec_(write_timeout_usec) {
    7718             :   SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
    7719             : }
    7720             : 
    7721             : inline SSLSocketStream::~SSLSocketStream() {}
    7722             : 
    7723             : inline bool SSLSocketStream::is_readable() const {
    7724             :   return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
    7725             : }
    7726             : 
    7727             : inline bool SSLSocketStream::is_writable() const {
    7728             :   return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
    7729             :          is_socket_alive(sock_);
    7730             : }
    7731             : 
    7732             : inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
    7733             :   if (SSL_pending(ssl_) > 0) {
    7734             :     return SSL_read(ssl_, ptr, static_cast<int>(size));
    7735             :   } else if (is_readable()) {
    7736             :     auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
    7737             :     if (ret < 0) {
    7738             :       auto err = SSL_get_error(ssl_, ret);
    7739             :       int n = 1000;
    7740             : #ifdef _WIN32
    7741             :       while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
    7742             :                           (err == SSL_ERROR_SYSCALL &&
    7743             :                            WSAGetLastError() == WSAETIMEDOUT))) {
    7744             : #else
    7745             :       while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
    7746             : #endif
    7747             :         if (SSL_pending(ssl_) > 0) {
    7748             :           return SSL_read(ssl_, ptr, static_cast<int>(size));
    7749             :         } else if (is_readable()) {
    7750             :           std::this_thread::sleep_for(std::chrono::milliseconds(1));
    7751             :           ret = SSL_read(ssl_, ptr, static_cast<int>(size));
    7752             :           if (ret >= 0) { return ret; }
    7753             :           err = SSL_get_error(ssl_, ret);
    7754             :         } else {
    7755             :           return -1;
    7756             :         }
    7757             :       }
    7758             :     }
    7759             :     return ret;
    7760             :   }
    7761             :   return -1;
    7762             : }
    7763             : 
    7764             : inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
    7765             :   if (is_writable()) {
    7766             :     auto handle_size = static_cast<int>(
    7767             :         std::min<size_t>(size, (std::numeric_limits<int>::max)()));
    7768             : 
    7769             :     auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
    7770             :     if (ret < 0) {
    7771             :       auto err = SSL_get_error(ssl_, ret);
    7772             :       int n = 1000;
    7773             : #ifdef _WIN32
    7774             :       while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
    7775             :                           (err == SSL_ERROR_SYSCALL &&
    7776             :                            WSAGetLastError() == WSAETIMEDOUT))) {
    7777             : #else
    7778             :       while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
    7779             : #endif
    7780             :         if (is_writable()) {
    7781             :           std::this_thread::sleep_for(std::chrono::milliseconds(1));
    7782             :           ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
    7783             :           if (ret >= 0) { return ret; }
    7784             :           err = SSL_get_error(ssl_, ret);
    7785             :         } else {
    7786             :           return -1;
    7787             :         }
    7788             :       }
    7789             :     }
    7790             :     return ret;
    7791             :   }
    7792             :   return -1;
    7793             : }
    7794             : 
    7795             : inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
    7796             :                                                     int &port) const {
    7797             :   detail::get_remote_ip_and_port(sock_, ip, port);
    7798             : }
    7799             : 
    7800             : inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
    7801             :                                                    int &port) const {
    7802             :   detail::get_local_ip_and_port(sock_, ip, port);
    7803             : }
    7804             : 
    7805             : inline socket_t SSLSocketStream::socket() const { return sock_; }
    7806             : 
    7807             : static SSLInit sslinit_;
    7808             : 
    7809             : } // namespace detail
    7810             : 
    7811             : // SSL HTTP server implementation
    7812             : inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
    7813             :                             const char *client_ca_cert_file_path,
    7814             :                             const char *client_ca_cert_dir_path,
    7815             :                             const char *private_key_password) {
    7816             :   ctx_ = SSL_CTX_new(TLS_server_method());
    7817             : 
    7818             :   if (ctx_) {
    7819             :     SSL_CTX_set_options(ctx_,
    7820             :                         SSL_OP_NO_COMPRESSION |
    7821             :                             SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
    7822             : 
    7823             :     SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);
    7824             : 
    7825             :     // add default password callback before opening encrypted private key
    7826             :     if (private_key_password != nullptr && (private_key_password[0] != '\0')) {
    7827             :       SSL_CTX_set_default_passwd_cb_userdata(ctx_,
    7828             :                                              (char *)private_key_password);
    7829             :     }
    7830             : 
    7831             :     if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
    7832             :         SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
    7833             :             1) {
    7834             :       SSL_CTX_free(ctx_);
    7835             :       ctx_ = nullptr;
    7836             :     } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
    7837             :       SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
    7838             :                                     client_ca_cert_dir_path);
    7839             : 
    7840             :       SSL_CTX_set_verify(
    7841             :           ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
    7842             :     }
    7843             :   }
    7844             : }
    7845             : 
    7846             : inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
    7847             :                             X509_STORE *client_ca_cert_store) {
    7848             :   ctx_ = SSL_CTX_new(TLS_server_method());
    7849             : 
    7850             :   if (ctx_) {
    7851             :     SSL_CTX_set_options(ctx_,
    7852             :                         SSL_OP_NO_COMPRESSION |
    7853             :                             SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
    7854             : 
    7855             :     SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);
    7856             : 
    7857             :     if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
    7858             :         SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
    7859             :       SSL_CTX_free(ctx_);
    7860             :       ctx_ = nullptr;
    7861             :     } else if (client_ca_cert_store) {
    7862             :       SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
    7863             : 
    7864             :       SSL_CTX_set_verify(
    7865             :           ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
    7866             :     }
    7867             :   }
    7868             : }
    7869             : 
    7870             : inline SSLServer::SSLServer(
    7871             :     const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
    7872             :   ctx_ = SSL_CTX_new(TLS_method());
    7873             :   if (ctx_) {
    7874             :     if (!setup_ssl_ctx_callback(*ctx_)) {
    7875             :       SSL_CTX_free(ctx_);
    7876             :       ctx_ = nullptr;
    7877             :     }
    7878             :   }
    7879             : }
    7880             : 
    7881             : inline SSLServer::~SSLServer() {
    7882             :   if (ctx_) { SSL_CTX_free(ctx_); }
    7883             : }
    7884             : 
    7885             : inline bool SSLServer::is_valid() const { return ctx_; }
    7886             : 
    7887             : inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
    7888             : 
    7889             : inline bool SSLServer::process_and_close_socket(socket_t sock) {
    7890             :   auto ssl = detail::ssl_new(
    7891             :       sock, ctx_, ctx_mutex_,
    7892             :       [&](SSL *ssl2) {
    7893             :         return detail::ssl_connect_or_accept_nonblocking(
    7894             :             sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
    7895             :       },
    7896             :       [](SSL * /*ssl2*/) { return true; });
    7897             : 
    7898             :   auto ret = false;
    7899             :   if (ssl) {
    7900             :     ret = detail::process_server_socket_ssl(
    7901             :         svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
    7902             :         read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
    7903             :         write_timeout_usec_,
    7904             :         [this, ssl](Stream &strm, bool close_connection,
    7905             :                     bool &connection_closed) {
    7906             :           return process_request(strm, close_connection, connection_closed,
    7907             :                                  [&](Request &req) { req.ssl = ssl; });
    7908             :         });
    7909             : 
    7910             :     // Shutdown gracefully if the result seemed successful, non-gracefully if
    7911             :     // the connection appeared to be closed.
    7912             :     const bool shutdown_gracefully = ret;
    7913             :     detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully);
    7914             :   }
    7915             : 
    7916             :   detail::shutdown_socket(sock);
    7917             :   detail::close_socket(sock);
    7918             :   return ret;
    7919             : }
    7920             : 
    7921             : // SSL HTTP client implementation
    7922             : inline SSLClient::SSLClient(const std::string &host)
    7923             :     : SSLClient(host, 443, std::string(), std::string()) {}
    7924             : 
    7925             : inline SSLClient::SSLClient(const std::string &host, int port)
    7926             :     : SSLClient(host, port, std::string(), std::string()) {}
    7927             : 
    7928             : inline SSLClient::SSLClient(const std::string &host, int port,
    7929             :                             const std::string &client_cert_path,
    7930             :                             const std::string &client_key_path)
    7931             :     : ClientImpl(host, port, client_cert_path, client_key_path) {
    7932             :   ctx_ = SSL_CTX_new(TLS_client_method());
    7933             : 
    7934             :   detail::split(&host_[0], &host_[host_.size()], '.',
    7935             :                 [&](const char *b, const char *e) {
    7936             :                   host_components_.emplace_back(std::string(b, e));
    7937             :                 });
    7938             : 
    7939             :   if (!client_cert_path.empty() && !client_key_path.empty()) {
    7940             :     if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
    7941             :                                      SSL_FILETYPE_PEM) != 1 ||
    7942             :         SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
    7943             :                                     SSL_FILETYPE_PEM) != 1) {
    7944             :       SSL_CTX_free(ctx_);
    7945             :       ctx_ = nullptr;
    7946             :     }
    7947             :   }
    7948             : }
    7949             : 
    7950             : inline SSLClient::SSLClient(const std::string &host, int port,
    7951             :                             X509 *client_cert, EVP_PKEY *client_key)
    7952             :     : ClientImpl(host, port) {
    7953             :   ctx_ = SSL_CTX_new(TLS_client_method());
    7954             : 
    7955             :   detail::split(&host_[0], &host_[host_.size()], '.',
    7956             :                 [&](const char *b, const char *e) {
    7957             :                   host_components_.emplace_back(std::string(b, e));
    7958             :                 });
    7959             : 
    7960             :   if (client_cert != nullptr && client_key != nullptr) {
    7961             :     if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
    7962             :         SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
    7963             :       SSL_CTX_free(ctx_);
    7964             :       ctx_ = nullptr;
    7965             :     }
    7966             :   }
    7967             : }
    7968             : 
    7969             : inline SSLClient::~SSLClient() {
    7970             :   if (ctx_) { SSL_CTX_free(ctx_); }
    7971             :   // Make sure to shut down SSL since shutdown_ssl will resolve to the
    7972             :   // base function rather than the derived function once we get to the
    7973             :   // base class destructor, and won't free the SSL (causing a leak).
    7974             :   shutdown_ssl_impl(socket_, true);
    7975             : }
    7976             : 
    7977             : inline bool SSLClient::is_valid() const { return ctx_; }
    7978             : 
    7979             : inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
    7980             :   if (ca_cert_store) {
    7981             :     if (ctx_) {
    7982             :       if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
    7983             :         // Free memory allocated for old cert and use new store `ca_cert_store`
    7984             :         SSL_CTX_set_cert_store(ctx_, ca_cert_store);
    7985             :       }
    7986             :     } else {
    7987             :       X509_STORE_free(ca_cert_store);
    7988             :     }
    7989             :   }
    7990             : }
    7991             : 
    7992             : inline long SSLClient::get_openssl_verify_result() const {
    7993             :   return verify_result_;
    7994             : }
    7995             : 
    7996             : inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
    7997             : 
    7998             : inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
    7999             :   return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
    8000             : }
    8001             : 
    8002             : // Assumes that socket_mutex_ is locked and that there are no requests in flight
    8003             : inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
    8004             :                                           bool &success, Error &error) {
    8005             :   success = true;
    8006             :   Response res2;
    8007             :   if (!detail::process_client_socket(
    8008             :           socket.sock, read_timeout_sec_, read_timeout_usec_,
    8009             :           write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
    8010             :             Request req2;
    8011             :             req2.method = "CONNECT";
    8012             :             req2.path = host_and_port_;
    8013             :             return process_request(strm, req2, res2, false, error);
    8014             :           })) {
    8015             :     // Thread-safe to close everything because we are assuming there are no
    8016             :     // requests in flight
    8017             :     shutdown_ssl(socket, true);
    8018             :     shutdown_socket(socket);
    8019             :     close_socket(socket);
    8020             :     success = false;
    8021             :     return false;
    8022             :   }
    8023             : 
    8024             :   if (res2.status == 407) {
    8025             :     if (!proxy_digest_auth_username_.empty() &&
    8026             :         !proxy_digest_auth_password_.empty()) {
    8027             :       std::map<std::string, std::string> auth;
    8028             :       if (detail::parse_www_authenticate(res2, auth, true)) {
    8029             :         Response res3;
    8030             :         if (!detail::process_client_socket(
    8031             :                 socket.sock, read_timeout_sec_, read_timeout_usec_,
    8032             :                 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
    8033             :                   Request req3;
    8034             :                   req3.method = "CONNECT";
    8035             :                   req3.path = host_and_port_;
    8036             :                   req3.headers.insert(detail::make_digest_authentication_header(
    8037             :                       req3, auth, 1, detail::random_string(10),
    8038             :                       proxy_digest_auth_username_, proxy_digest_auth_password_,
    8039             :                       true));
    8040             :                   return process_request(strm, req3, res3, false, error);
    8041             :                 })) {
    8042             :           // Thread-safe to close everything because we are assuming there are
    8043             :           // no requests in flight
    8044             :           shutdown_ssl(socket, true);
    8045             :           shutdown_socket(socket);
    8046             :           close_socket(socket);
    8047             :           success = false;
    8048             :           return false;
    8049             :         }
    8050             :       }
    8051             :     } else {
    8052             :       res = res2;
    8053             :       return false;
    8054             :     }
    8055             :   }
    8056             : 
    8057             :   return true;
    8058             : }
    8059             : 
    8060             : inline bool SSLClient::load_certs() {
    8061             :   bool ret = true;
    8062             : 
    8063             :   std::call_once(initialize_cert_, [&]() {
    8064             :     std::lock_guard<std::mutex> guard(ctx_mutex_);
    8065             :     if (!ca_cert_file_path_.empty()) {
    8066             :       if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
    8067             :                                          nullptr)) {
    8068             :         ret = false;
    8069             :       }
    8070             :     } else if (!ca_cert_dir_path_.empty()) {
    8071             :       if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
    8072             :                                          ca_cert_dir_path_.c_str())) {
    8073             :         ret = false;
    8074             :       }
    8075             :     } else {
    8076             :       auto loaded = false;
    8077             : #ifdef _WIN32
    8078             :       loaded =
    8079             :           detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
    8080             : #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
    8081             : #if TARGET_OS_OSX
    8082             :       loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
    8083             : #endif // TARGET_OS_OSX
    8084             : #endif // _WIN32
    8085             :       if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
    8086             :     }
    8087             :   });
    8088             : 
    8089             :   return ret;
    8090             : }
    8091             : 
    8092             : inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
    8093             :   auto ssl = detail::ssl_new(
    8094             :       socket.sock, ctx_, ctx_mutex_,
    8095             :       [&](SSL *ssl2) {
    8096             :         if (server_certificate_verification_) {
    8097             :           if (!load_certs()) {
    8098             :             error = Error::SSLLoadingCerts;
    8099             :             return false;
    8100             :           }
    8101             :           SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
    8102             :         }
    8103             : 
    8104             :         if (!detail::ssl_connect_or_accept_nonblocking(
    8105             :                 socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
    8106             :                 connection_timeout_usec_)) {
    8107             :           error = Error::SSLConnection;
    8108             :           return false;
    8109             :         }
    8110             : 
    8111             :         if (server_certificate_verification_) {
    8112             :           verify_result_ = SSL_get_verify_result(ssl2);
    8113             : 
    8114             :           if (verify_result_ != X509_V_OK) {
    8115             :             error = Error::SSLServerVerification;
    8116             :             return false;
    8117             :           }
    8118             : 
    8119             :           auto server_cert = SSL_get1_peer_certificate(ssl2);
    8120             : 
    8121             :           if (server_cert == nullptr) {
    8122             :             error = Error::SSLServerVerification;
    8123             :             return false;
    8124             :           }
    8125             : 
    8126             :           if (!verify_host(server_cert)) {
    8127             :             X509_free(server_cert);
    8128             :             error = Error::SSLServerVerification;
    8129             :             return false;
    8130             :           }
    8131             :           X509_free(server_cert);
    8132             :         }
    8133             : 
    8134             :         return true;
    8135             :       },
    8136             :       [&](SSL *ssl2) {
    8137             :         SSL_set_tlsext_host_name(ssl2, host_.c_str());
    8138             :         return true;
    8139             :       });
    8140             : 
    8141             :   if (ssl) {
    8142             :     socket.ssl = ssl;
    8143             :     return true;
    8144             :   }
    8145             : 
    8146             :   shutdown_socket(socket);
    8147             :   close_socket(socket);
    8148             :   return false;
    8149             : }
    8150             : 
    8151             : inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
    8152             :   shutdown_ssl_impl(socket, shutdown_gracefully);
    8153             : }
    8154             : 
    8155             : inline void SSLClient::shutdown_ssl_impl(Socket &socket,
    8156             :                                          bool shutdown_gracefully) {
    8157             :   if (socket.sock == INVALID_SOCKET) {
    8158             :     assert(socket.ssl == nullptr);
    8159             :     return;
    8160             :   }
    8161             :   if (socket.ssl) {
    8162             :     detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
    8163             :     socket.ssl = nullptr;
    8164             :   }
    8165             :   assert(socket.ssl == nullptr);
    8166             : }
    8167             : 
    8168             : inline bool
    8169             : SSLClient::process_socket(const Socket &socket,
    8170             :                           std::function<bool(Stream &strm)> callback) {
    8171             :   assert(socket.ssl);
    8172             :   return detail::process_client_socket_ssl(
    8173             :       socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
    8174             :       write_timeout_sec_, write_timeout_usec_, std::move(callback));
    8175             : }
    8176             : 
    8177             : inline bool SSLClient::is_ssl() const { return true; }
    8178             : 
    8179             : inline bool SSLClient::verify_host(X509 *server_cert) const {
    8180             :   /* Quote from RFC2818 section 3.1 "Server Identity"
    8181             : 
    8182             :      If a subjectAltName extension of type dNSName is present, that MUST
    8183             :      be used as the identity. Otherwise, the (most specific) Common Name
    8184             :      field in the Subject field of the certificate MUST be used. Although
    8185             :      the use of the Common Name is existing practice, it is deprecated and
    8186             :      Certification Authorities are encouraged to use the dNSName instead.
    8187             : 
    8188             :      Matching is performed using the matching rules specified by
    8189             :      [RFC2459].  If more than one identity of a given type is present in
    8190             :      the certificate (e.g., more than one dNSName name, a match in any one
    8191             :      of the set is considered acceptable.) Names may contain the wildcard
    8192             :      character * which is considered to match any single domain name
    8193             :      component or component fragment. E.g., *.a.com matches foo.a.com but
    8194             :      not bar.foo.a.com. f*.com matches foo.com but not bar.com.
    8195             : 
    8196             :      In some cases, the URI is specified as an IP address rather than a
    8197             :      hostname. In this case, the iPAddress subjectAltName must be present
    8198             :      in the certificate and must exactly match the IP in the URI.
    8199             : 
    8200             :   */
    8201             :   return verify_host_with_subject_alt_name(server_cert) ||
    8202             :          verify_host_with_common_name(server_cert);
    8203             : }
    8204             : 
    8205             : inline bool
    8206             : SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
    8207             :   auto ret = false;
    8208             : 
    8209             :   auto type = GEN_DNS;
    8210             : 
    8211             :   struct in6_addr addr6;
    8212             :   struct in_addr addr;
    8213             :   size_t addr_len = 0;
    8214             : 
    8215             : #ifndef __MINGW32__
    8216             :   if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
    8217             :     type = GEN_IPADD;
    8218             :     addr_len = sizeof(struct in6_addr);
    8219             :   } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
    8220             :     type = GEN_IPADD;
    8221             :     addr_len = sizeof(struct in_addr);
    8222             :   }
    8223             : #endif
    8224             : 
    8225             :   auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
    8226             :       X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
    8227             : 
    8228             :   if (alt_names) {
    8229             :     auto dsn_matched = false;
    8230             :     auto ip_matched = false;
    8231             : 
    8232             :     auto count = sk_GENERAL_NAME_num(alt_names);
    8233             : 
    8234             :     for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
    8235             :       auto val = sk_GENERAL_NAME_value(alt_names, i);
    8236             :       if (val->type == type) {
    8237             :         auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5);
    8238             :         auto name_len = (size_t)ASN1_STRING_length(val->d.ia5);
    8239             : 
    8240             :         switch (type) {
    8241             :         case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
    8242             : 
    8243             :         case GEN_IPADD:
    8244             :           if (!memcmp(&addr6, name, addr_len) ||
    8245             :               !memcmp(&addr, name, addr_len)) {
    8246             :             ip_matched = true;
    8247             :           }
    8248             :           break;
    8249             :         }
    8250             :       }
    8251             :     }
    8252             : 
    8253             :     if (dsn_matched || ip_matched) { ret = true; }
    8254             :   }
    8255             : 
    8256             :   GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
    8257             :   return ret;
    8258             : }
    8259             : 
    8260             : inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
    8261             :   const auto subject_name = X509_get_subject_name(server_cert);
    8262             : 
    8263             :   if (subject_name != nullptr) {
    8264             :     char name[BUFSIZ];
    8265             :     auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
    8266             :                                               name, sizeof(name));
    8267             : 
    8268             :     if (name_len != -1) {
    8269             :       return check_host_name(name, static_cast<size_t>(name_len));
    8270             :     }
    8271             :   }
    8272             : 
    8273             :   return false;
    8274             : }
    8275             : 
    8276             : inline bool SSLClient::check_host_name(const char *pattern,
    8277             :                                        size_t pattern_len) const {
    8278             :   if (host_.size() == pattern_len && host_ == pattern) { return true; }
    8279             : 
    8280             :   // Wildcard match
    8281             :   // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
    8282             :   std::vector<std::string> pattern_components;
    8283             :   detail::split(&pattern[0], &pattern[pattern_len], '.',
    8284             :                 [&](const char *b, const char *e) {
    8285             :                   pattern_components.emplace_back(std::string(b, e));
    8286             :                 });
    8287             : 
    8288             :   if (host_components_.size() != pattern_components.size()) { return false; }
    8289             : 
    8290             :   auto itr = pattern_components.begin();
    8291             :   for (const auto &h : host_components_) {
    8292             :     auto &p = *itr;
    8293             :     if (p != h && p != "*") {
    8294             :       auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
    8295             :                             !p.compare(0, p.size() - 1, h));
    8296             :       if (!partial_match) { return false; }
    8297             :     }
    8298             :     ++itr;
    8299             :   }
    8300             : 
    8301             :   return true;
    8302             : }
    8303             : #endif
    8304             : 
    8305             : // Universal client implementation
    8306             : inline Client::Client(const std::string &scheme_host_port)
    8307             :     : Client(scheme_host_port, std::string(), std::string()) {}
    8308             : 
    8309             : inline Client::Client(const std::string &scheme_host_port,
    8310             :                       const std::string &client_cert_path,
    8311             :                       const std::string &client_key_path) {
    8312             :   const static std::regex re(
    8313             :       R"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
    8314             : 
    8315             :   std::smatch m;
    8316             :   if (std::regex_match(scheme_host_port, m, re)) {
    8317             :     auto scheme = m[1].str();
    8318             : 
    8319             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    8320             :     if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
    8321             : #else
    8322             :     if (!scheme.empty() && scheme != "http") {
    8323             : #endif
    8324             : #ifndef CPPHTTPLIB_NO_EXCEPTIONS
    8325             :       std::string msg = "'" + scheme + "' scheme is not supported.";
    8326             :       throw std::invalid_argument(msg);
    8327             : #endif
    8328             :       return;
    8329             :     }
    8330             : 
    8331             :     auto is_ssl = scheme == "https";
    8332             : 
    8333             :     auto host = m[2].str();
    8334             :     if (host.empty()) { host = m[3].str(); }
    8335             : 
    8336             :     auto port_str = m[4].str();
    8337             :     auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
    8338             : 
    8339             :     if (is_ssl) {
    8340             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    8341             :       cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
    8342             :                                             client_key_path);
    8343             :       is_ssl_ = is_ssl;
    8344             : #endif
    8345             :     } else {
    8346             :       cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
    8347             :                                              client_key_path);
    8348             :     }
    8349             :   } else {
    8350             :     cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
    8351             :                                            client_cert_path, client_key_path);
    8352             :   }
    8353             : }
    8354             : 
    8355             : inline Client::Client(const std::string &host, int port)
    8356             :     : cli_(detail::make_unique<ClientImpl>(host, port)) {}
    8357             : 
    8358             : inline Client::Client(const std::string &host, int port,
    8359             :                       const std::string &client_cert_path,
    8360             :                       const std::string &client_key_path)
    8361             :     : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
    8362             :                                            client_key_path)) {}
    8363             : 
    8364             : inline Client::~Client() {}
    8365             : 
    8366             : inline bool Client::is_valid() const {
    8367             :   return cli_ != nullptr && cli_->is_valid();
    8368             : }
    8369             : 
    8370             : inline Result Client::Get(const std::string &path) { return cli_->Get(path); }
    8371             : inline Result Client::Get(const std::string &path, const Headers &headers) {
    8372             :   return cli_->Get(path, headers);
    8373             : }
    8374             : inline Result Client::Get(const std::string &path, Progress progress) {
    8375             :   return cli_->Get(path, std::move(progress));
    8376             : }
    8377             : inline Result Client::Get(const std::string &path, const Headers &headers,
    8378             :                           Progress progress) {
    8379             :   return cli_->Get(path, headers, std::move(progress));
    8380             : }
    8381             : inline Result Client::Get(const std::string &path,
    8382             :                           ContentReceiver content_receiver) {
    8383             :   return cli_->Get(path, std::move(content_receiver));
    8384             : }
    8385             : inline Result Client::Get(const std::string &path, const Headers &headers,
    8386             :                           ContentReceiver content_receiver) {
    8387             :   return cli_->Get(path, headers, std::move(content_receiver));
    8388             : }
    8389             : inline Result Client::Get(const std::string &path,
    8390             :                           ContentReceiver content_receiver, Progress progress) {
    8391             :   return cli_->Get(path, std::move(content_receiver), std::move(progress));
    8392             : }
    8393             : inline Result Client::Get(const std::string &path, const Headers &headers,
    8394             :                           ContentReceiver content_receiver, Progress progress) {
    8395             :   return cli_->Get(path, headers, std::move(content_receiver),
    8396             :                    std::move(progress));
    8397             : }
    8398             : inline Result Client::Get(const std::string &path,
    8399             :                           ResponseHandler response_handler,
    8400             :                           ContentReceiver content_receiver) {
    8401             :   return cli_->Get(path, std::move(response_handler),
    8402             :                    std::move(content_receiver));
    8403             : }
    8404             : inline Result Client::Get(const std::string &path, const Headers &headers,
    8405             :                           ResponseHandler response_handler,
    8406             :                           ContentReceiver content_receiver) {
    8407             :   return cli_->Get(path, headers, std::move(response_handler),
    8408             :                    std::move(content_receiver));
    8409             : }
    8410             : inline Result Client::Get(const std::string &path,
    8411             :                           ResponseHandler response_handler,
    8412             :                           ContentReceiver content_receiver, Progress progress) {
    8413             :   return cli_->Get(path, std::move(response_handler),
    8414             :                    std::move(content_receiver), std::move(progress));
    8415             : }
    8416             : inline Result Client::Get(const std::string &path, const Headers &headers,
    8417             :                           ResponseHandler response_handler,
    8418             :                           ContentReceiver content_receiver, Progress progress) {
    8419             :   return cli_->Get(path, headers, std::move(response_handler),
    8420             :                    std::move(content_receiver), std::move(progress));
    8421             : }
    8422             : inline Result Client::Get(const std::string &path, const Params &params,
    8423             :                           const Headers &headers, Progress progress) {
    8424             :   return cli_->Get(path, params, headers, progress);
    8425             : }
    8426             : inline Result Client::Get(const std::string &path, const Params &params,
    8427             :                           const Headers &headers,
    8428             :                           ContentReceiver content_receiver, Progress progress) {
    8429             :   return cli_->Get(path, params, headers, content_receiver, progress);
    8430             : }
    8431             : inline Result Client::Get(const std::string &path, const Params &params,
    8432             :                           const Headers &headers,
    8433             :                           ResponseHandler response_handler,
    8434             :                           ContentReceiver content_receiver, Progress progress) {
    8435             :   return cli_->Get(path, params, headers, response_handler, content_receiver,
    8436             :                    progress);
    8437             : }
    8438             : 
    8439             : inline Result Client::Head(const std::string &path) { return cli_->Head(path); }
    8440             : inline Result Client::Head(const std::string &path, const Headers &headers) {
    8441             :   return cli_->Head(path, headers);
    8442             : }
    8443             : 
    8444             : inline Result Client::Post(const std::string &path) { return cli_->Post(path); }
    8445             : inline Result Client::Post(const std::string &path, const Headers &headers) {
    8446             :   return cli_->Post(path, headers);
    8447             : }
    8448             : inline Result Client::Post(const std::string &path, const char *body,
    8449             :                            size_t content_length,
    8450             :                            const std::string &content_type) {
    8451             :   return cli_->Post(path, body, content_length, content_type);
    8452             : }
    8453             : inline Result Client::Post(const std::string &path, const Headers &headers,
    8454             :                            const char *body, size_t content_length,
    8455             :                            const std::string &content_type) {
    8456             :   return cli_->Post(path, headers, body, content_length, content_type);
    8457             : }
    8458             : inline Result Client::Post(const std::string &path, const std::string &body,
    8459             :                            const std::string &content_type) {
    8460             :   return cli_->Post(path, body, content_type);
    8461             : }
    8462             : inline Result Client::Post(const std::string &path, const Headers &headers,
    8463             :                            const std::string &body,
    8464             :                            const std::string &content_type) {
    8465             :   return cli_->Post(path, headers, body, content_type);
    8466             : }
    8467             : inline Result Client::Post(const std::string &path, size_t content_length,
    8468             :                            ContentProvider content_provider,
    8469             :                            const std::string &content_type) {
    8470             :   return cli_->Post(path, content_length, std::move(content_provider),
    8471             :                     content_type);
    8472             : }
    8473             : inline Result Client::Post(const std::string &path,
    8474             :                            ContentProviderWithoutLength content_provider,
    8475             :                            const std::string &content_type) {
    8476             :   return cli_->Post(path, std::move(content_provider), content_type);
    8477             : }
    8478             : inline Result Client::Post(const std::string &path, const Headers &headers,
    8479             :                            size_t content_length,
    8480             :                            ContentProvider content_provider,
    8481             :                            const std::string &content_type) {
    8482             :   return cli_->Post(path, headers, content_length, std::move(content_provider),
    8483             :                     content_type);
    8484             : }
    8485             : inline Result Client::Post(const std::string &path, const Headers &headers,
    8486             :                            ContentProviderWithoutLength content_provider,
    8487             :                            const std::string &content_type) {
    8488             :   return cli_->Post(path, headers, std::move(content_provider), content_type);
    8489             : }
    8490             : inline Result Client::Post(const std::string &path, const Params &params) {
    8491             :   return cli_->Post(path, params);
    8492             : }
    8493             : inline Result Client::Post(const std::string &path, const Headers &headers,
    8494             :                            const Params &params) {
    8495             :   return cli_->Post(path, headers, params);
    8496             : }
    8497             : inline Result Client::Post(const std::string &path,
    8498             :                            const MultipartFormDataItems &items) {
    8499             :   return cli_->Post(path, items);
    8500             : }
    8501             : inline Result Client::Post(const std::string &path, const Headers &headers,
    8502             :                            const MultipartFormDataItems &items) {
    8503             :   return cli_->Post(path, headers, items);
    8504             : }
    8505             : inline Result Client::Post(const std::string &path, const Headers &headers,
    8506             :                            const MultipartFormDataItems &items,
    8507             :                            const std::string &boundary) {
    8508             :   return cli_->Post(path, headers, items, boundary);
    8509             : }
    8510             : inline Result
    8511             : Client::Post(const std::string &path, const Headers &headers,
    8512             :              const MultipartFormDataItems &items,
    8513             :              const MultipartFormDataProviderItems &provider_items) {
    8514             :   return cli_->Post(path, headers, items, provider_items);
    8515             : }
    8516             : inline Result Client::Put(const std::string &path) { return cli_->Put(path); }
    8517             : inline Result Client::Put(const std::string &path, const char *body,
    8518             :                           size_t content_length,
    8519             :                           const std::string &content_type) {
    8520             :   return cli_->Put(path, body, content_length, content_type);
    8521             : }
    8522             : inline Result Client::Put(const std::string &path, const Headers &headers,
    8523             :                           const char *body, size_t content_length,
    8524             :                           const std::string &content_type) {
    8525             :   return cli_->Put(path, headers, body, content_length, content_type);
    8526             : }
    8527             : inline Result Client::Put(const std::string &path, const std::string &body,
    8528             :                           const std::string &content_type) {
    8529             :   return cli_->Put(path, body, content_type);
    8530             : }
    8531             : inline Result Client::Put(const std::string &path, const Headers &headers,
    8532             :                           const std::string &body,
    8533             :                           const std::string &content_type) {
    8534             :   return cli_->Put(path, headers, body, content_type);
    8535             : }
    8536             : inline Result Client::Put(const std::string &path, size_t content_length,
    8537             :                           ContentProvider content_provider,
    8538             :                           const std::string &content_type) {
    8539             :   return cli_->Put(path, content_length, std::move(content_provider),
    8540             :                    content_type);
    8541             : }
    8542             : inline Result Client::Put(const std::string &path,
    8543             :                           ContentProviderWithoutLength content_provider,
    8544             :                           const std::string &content_type) {
    8545             :   return cli_->Put(path, std::move(content_provider), content_type);
    8546             : }
    8547             : inline Result Client::Put(const std::string &path, const Headers &headers,
    8548             :                           size_t content_length,
    8549             :                           ContentProvider content_provider,
    8550             :                           const std::string &content_type) {
    8551             :   return cli_->Put(path, headers, content_length, std::move(content_provider),
    8552             :                    content_type);
    8553             : }
    8554             : inline Result Client::Put(const std::string &path, const Headers &headers,
    8555             :                           ContentProviderWithoutLength content_provider,
    8556             :                           const std::string &content_type) {
    8557             :   return cli_->Put(path, headers, std::move(content_provider), content_type);
    8558             : }
    8559             : inline Result Client::Put(const std::string &path, const Params &params) {
    8560             :   return cli_->Put(path, params);
    8561             : }
    8562             : inline Result Client::Put(const std::string &path, const Headers &headers,
    8563             :                           const Params &params) {
    8564             :   return cli_->Put(path, headers, params);
    8565             : }
    8566             : inline Result Client::Put(const std::string &path,
    8567             :                           const MultipartFormDataItems &items) {
    8568             :   return cli_->Put(path, items);
    8569             : }
    8570             : inline Result Client::Put(const std::string &path, const Headers &headers,
    8571             :                           const MultipartFormDataItems &items) {
    8572             :   return cli_->Put(path, headers, items);
    8573             : }
    8574             : inline Result Client::Put(const std::string &path, const Headers &headers,
    8575             :                           const MultipartFormDataItems &items,
    8576             :                           const std::string &boundary) {
    8577             :   return cli_->Put(path, headers, items, boundary);
    8578             : }
    8579             : inline Result
    8580             : Client::Put(const std::string &path, const Headers &headers,
    8581             :             const MultipartFormDataItems &items,
    8582             :             const MultipartFormDataProviderItems &provider_items) {
    8583             :   return cli_->Put(path, headers, items, provider_items);
    8584             : }
    8585             : inline Result Client::Patch(const std::string &path) {
    8586             :   return cli_->Patch(path);
    8587             : }
    8588             : inline Result Client::Patch(const std::string &path, const char *body,
    8589             :                             size_t content_length,
    8590             :                             const std::string &content_type) {
    8591             :   return cli_->Patch(path, body, content_length, content_type);
    8592             : }
    8593             : inline Result Client::Patch(const std::string &path, const Headers &headers,
    8594             :                             const char *body, size_t content_length,
    8595             :                             const std::string &content_type) {
    8596             :   return cli_->Patch(path, headers, body, content_length, content_type);
    8597             : }
    8598             : inline Result Client::Patch(const std::string &path, const std::string &body,
    8599             :                             const std::string &content_type) {
    8600             :   return cli_->Patch(path, body, content_type);
    8601             : }
    8602             : inline Result Client::Patch(const std::string &path, const Headers &headers,
    8603             :                             const std::string &body,
    8604             :                             const std::string &content_type) {
    8605             :   return cli_->Patch(path, headers, body, content_type);
    8606             : }
    8607             : inline Result Client::Patch(const std::string &path, size_t content_length,
    8608             :                             ContentProvider content_provider,
    8609             :                             const std::string &content_type) {
    8610             :   return cli_->Patch(path, content_length, std::move(content_provider),
    8611             :                      content_type);
    8612             : }
    8613             : inline Result Client::Patch(const std::string &path,
    8614             :                             ContentProviderWithoutLength content_provider,
    8615             :                             const std::string &content_type) {
    8616             :   return cli_->Patch(path, std::move(content_provider), content_type);
    8617             : }
    8618             : inline Result Client::Patch(const std::string &path, const Headers &headers,
    8619             :                             size_t content_length,
    8620             :                             ContentProvider content_provider,
    8621             :                             const std::string &content_type) {
    8622             :   return cli_->Patch(path, headers, content_length, std::move(content_provider),
    8623             :                      content_type);
    8624             : }
    8625             : inline Result Client::Patch(const std::string &path, const Headers &headers,
    8626             :                             ContentProviderWithoutLength content_provider,
    8627             :                             const std::string &content_type) {
    8628             :   return cli_->Patch(path, headers, std::move(content_provider), content_type);
    8629             : }
    8630             : inline Result Client::Delete(const std::string &path) {
    8631             :   return cli_->Delete(path);
    8632             : }
    8633             : inline Result Client::Delete(const std::string &path, const Headers &headers) {
    8634             :   return cli_->Delete(path, headers);
    8635             : }
    8636             : inline Result Client::Delete(const std::string &path, const char *body,
    8637             :                              size_t content_length,
    8638             :                              const std::string &content_type) {
    8639             :   return cli_->Delete(path, body, content_length, content_type);
    8640             : }
    8641             : inline Result Client::Delete(const std::string &path, const Headers &headers,
    8642             :                              const char *body, size_t content_length,
    8643             :                              const std::string &content_type) {
    8644             :   return cli_->Delete(path, headers, body, content_length, content_type);
    8645             : }
    8646             : inline Result Client::Delete(const std::string &path, const std::string &body,
    8647             :                              const std::string &content_type) {
    8648             :   return cli_->Delete(path, body, content_type);
    8649             : }
    8650             : inline Result Client::Delete(const std::string &path, const Headers &headers,
    8651             :                              const std::string &body,
    8652             :                              const std::string &content_type) {
    8653             :   return cli_->Delete(path, headers, body, content_type);
    8654             : }
    8655             : inline Result Client::Options(const std::string &path) {
    8656             :   return cli_->Options(path);
    8657             : }
    8658             : inline Result Client::Options(const std::string &path, const Headers &headers) {
    8659             :   return cli_->Options(path, headers);
    8660             : }
    8661             : 
    8662             : inline bool Client::send(Request &req, Response &res, Error &error) {
    8663             :   return cli_->send(req, res, error);
    8664             : }
    8665             : 
    8666             : inline Result Client::send(const Request &req) { return cli_->send(req); }
    8667             : 
    8668             : inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
    8669             : 
    8670             : inline socket_t Client::socket() const { return cli_->socket(); }
    8671             : 
    8672             : inline void Client::stop() { cli_->stop(); }
    8673             : 
    8674             : inline void
    8675             : Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
    8676             :   cli_->set_hostname_addr_map(std::move(addr_map));
    8677             : }
    8678             : 
    8679             : inline void Client::set_default_headers(Headers headers) {
    8680             :   cli_->set_default_headers(std::move(headers));
    8681             : }
    8682             : 
    8683             : inline void Client::set_address_family(int family) {
    8684             :   cli_->set_address_family(family);
    8685             : }
    8686             : 
    8687             : inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
    8688             : 
    8689             : inline void Client::set_socket_options(SocketOptions socket_options) {
    8690             :   cli_->set_socket_options(std::move(socket_options));
    8691             : }
    8692             : 
    8693             : inline void Client::set_connection_timeout(time_t sec, time_t usec) {
    8694             :   cli_->set_connection_timeout(sec, usec);
    8695             : }
    8696             : 
    8697             : inline void Client::set_read_timeout(time_t sec, time_t usec) {
    8698             :   cli_->set_read_timeout(sec, usec);
    8699             : }
    8700             : 
    8701             : inline void Client::set_write_timeout(time_t sec, time_t usec) {
    8702             :   cli_->set_write_timeout(sec, usec);
    8703             : }
    8704             : 
    8705             : inline void Client::set_basic_auth(const std::string &username,
    8706             :                                    const std::string &password) {
    8707             :   cli_->set_basic_auth(username, password);
    8708             : }
    8709             : inline void Client::set_bearer_token_auth(const std::string &token) {
    8710             :   cli_->set_bearer_token_auth(token);
    8711             : }
    8712             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    8713             : inline void Client::set_digest_auth(const std::string &username,
    8714             :                                     const std::string &password) {
    8715             :   cli_->set_digest_auth(username, password);
    8716             : }
    8717             : #endif
    8718             : 
    8719             : inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
    8720             : inline void Client::set_follow_location(bool on) {
    8721             :   cli_->set_follow_location(on);
    8722             : }
    8723             : 
    8724             : inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); }
    8725             : 
    8726             : inline void Client::set_compress(bool on) { cli_->set_compress(on); }
    8727             : 
    8728             : inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
    8729             : 
    8730             : inline void Client::set_interface(const std::string &intf) {
    8731             :   cli_->set_interface(intf);
    8732             : }
    8733             : 
    8734             : inline void Client::set_proxy(const std::string &host, int port) {
    8735             :   cli_->set_proxy(host, port);
    8736             : }
    8737             : inline void Client::set_proxy_basic_auth(const std::string &username,
    8738             :                                          const std::string &password) {
    8739             :   cli_->set_proxy_basic_auth(username, password);
    8740             : }
    8741             : inline void Client::set_proxy_bearer_token_auth(const std::string &token) {
    8742             :   cli_->set_proxy_bearer_token_auth(token);
    8743             : }
    8744             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    8745             : inline void Client::set_proxy_digest_auth(const std::string &username,
    8746             :                                           const std::string &password) {
    8747             :   cli_->set_proxy_digest_auth(username, password);
    8748             : }
    8749             : #endif
    8750             : 
    8751             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    8752             : inline void Client::enable_server_certificate_verification(bool enabled) {
    8753             :   cli_->enable_server_certificate_verification(enabled);
    8754             : }
    8755             : #endif
    8756             : 
    8757             : inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); }
    8758             : 
    8759             : #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    8760             : inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
    8761             :                                      const std::string &ca_cert_dir_path) {
    8762             :   cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
    8763             : }
    8764             : 
    8765             : inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
    8766             :   if (is_ssl_) {
    8767             :     static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
    8768             :   } else {
    8769             :     cli_->set_ca_cert_store(ca_cert_store);
    8770             :   }
    8771             : }
    8772             : 
    8773             : inline long Client::get_openssl_verify_result() const {
    8774             :   if (is_ssl_) {
    8775             :     return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
    8776             :   }
    8777             :   return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
    8778             : }
    8779             : 
    8780             : inline SSL_CTX *Client::ssl_context() const {
    8781             :   if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
    8782             :   return nullptr;
    8783             : }
    8784             : #endif
    8785             : 
    8786             : // ----------------------------------------------------------------------------
    8787             : 
    8788             : } // namespace httplib
    8789             : 
    8790             : #if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)
    8791             : #undef poll
    8792             : #endif
    8793             : 
    8794             : #endif // CPPHTTPLIB_HTTPLIB_H

Generated by: LCOV version 1.14