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