提交 f56e060bb864e1f7ce73d649bd7dc24ac56f8189

作者 LJH 李佳桓
1 个父辈 f8a6c580

1

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