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