Path: blob/a-new-beginning/SharedDependencies/Sources/httplib/include/httplib.h
2 views
//1// httplib.h2//3// Copyright (c) 2023 Yuji Hirose. All rights reserved.4// MIT License5//67#ifndef CPPHTTPLIB_HTTPLIB_H8#define CPPHTTPLIB_HTTPLIB_H910#define CPPHTTPLIB_VERSION "0.14.3"1112/*13* Configuration14*/1516#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND17#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 518#endif1920#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT21#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 522#endif2324#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND25#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 30026#endif2728#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND29#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 030#endif3132#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND33#define CPPHTTPLIB_READ_TIMEOUT_SECOND 534#endif3536#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND37#define CPPHTTPLIB_READ_TIMEOUT_USECOND 038#endif3940#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND41#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 542#endif4344#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND45#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 046#endif4748#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND49#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 050#endif5152#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND53#ifdef _WIN3254#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000055#else56#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 057#endif58#endif5960#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH61#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 819262#endif6364#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH65#define CPPHTTPLIB_HEADER_MAX_LENGTH 819266#endif6768#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT69#define CPPHTTPLIB_REDIRECT_MAX_COUNT 2070#endif7172#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT73#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 102474#endif7576#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH77#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())78#endif7980#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH81#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 819282#endif8384#ifndef CPPHTTPLIB_TCP_NODELAY85#define CPPHTTPLIB_TCP_NODELAY false86#endif8788#ifndef CPPHTTPLIB_RECV_BUFSIZ89#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)90#endif9192#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ93#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)94#endif9596#ifndef CPPHTTPLIB_THREAD_POOL_COUNT97#define CPPHTTPLIB_THREAD_POOL_COUNT \98((std::max)(8u, std::thread::hardware_concurrency() > 0 \99? std::thread::hardware_concurrency() - 1 \100: 0))101#endif102103#ifndef CPPHTTPLIB_RECV_FLAGS104#define CPPHTTPLIB_RECV_FLAGS 0105#endif106107#ifndef CPPHTTPLIB_SEND_FLAGS108#define CPPHTTPLIB_SEND_FLAGS 0109#endif110111#ifndef CPPHTTPLIB_LISTEN_BACKLOG112#define CPPHTTPLIB_LISTEN_BACKLOG 5113#endif114115/*116* Headers117*/118119#ifdef _WIN32120#ifndef _CRT_SECURE_NO_WARNINGS121#define _CRT_SECURE_NO_WARNINGS122#endif //_CRT_SECURE_NO_WARNINGS123124#ifndef _CRT_NONSTDC_NO_DEPRECATE125#define _CRT_NONSTDC_NO_DEPRECATE126#endif //_CRT_NONSTDC_NO_DEPRECATE127128#if defined(_MSC_VER)129#if _MSC_VER < 1900130#error Sorry, Visual Studio versions prior to 2015 are not supported131#endif132133#pragma comment(lib, "ws2_32.lib")134135#ifdef _WIN64136using ssize_t = __int64;137#else138using ssize_t = long;139#endif140#endif // _MSC_VER141142#ifndef S_ISREG143#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)144#endif // S_ISREG145146#ifndef S_ISDIR147#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)148#endif // S_ISDIR149150#ifndef NOMINMAX151#define NOMINMAX152#endif // NOMINMAX153154#include <io.h>155#include <winsock2.h>156#include <ws2tcpip.h>157158#ifndef WSA_FLAG_NO_HANDLE_INHERIT159#define WSA_FLAG_NO_HANDLE_INHERIT 0x80160#endif161162using socket_t = SOCKET;163#ifdef CPPHTTPLIB_USE_POLL164#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)165#endif166167#else // not _WIN32168169#include <arpa/inet.h>170#if !defined(_AIX) && !defined(__MVS__)171#include <ifaddrs.h>172#endif173#ifdef __MVS__174#include <strings.h>175#ifndef NI_MAXHOST176#define NI_MAXHOST 1025177#endif178#endif179#include <net/if.h>180#include <netdb.h>181#include <netinet/in.h>182#ifdef __linux__183#include <resolv.h>184#endif185#include <netinet/tcp.h>186#ifdef CPPHTTPLIB_USE_POLL187#include <poll.h>188#endif189#include <csignal>190#include <pthread.h>191#include <sys/mman.h>192#include <sys/select.h>193#include <sys/socket.h>194#include <sys/un.h>195#include <unistd.h>196197using socket_t = int;198#ifndef INVALID_SOCKET199#define INVALID_SOCKET (-1)200#endif201#endif //_WIN32202203#include <algorithm>204#include <array>205#include <atomic>206#include <cassert>207#include <cctype>208#include <climits>209#include <condition_variable>210#include <cstring>211#include <errno.h>212#include <exception>213#include <fcntl.h>214#include <fstream>215#include <functional>216#include <iomanip>217#include <iostream>218#include <list>219#include <map>220#include <memory>221#include <mutex>222#include <random>223#include <regex>224#include <set>225#include <sstream>226#include <string>227#include <sys/stat.h>228#include <thread>229#include <unordered_map>230#include <unordered_set>231#include <utility>232233#ifdef CPPHTTPLIB_OPENSSL_SUPPORT234#ifdef _WIN32235#include <wincrypt.h>236237// these are defined in wincrypt.h and it breaks compilation if BoringSSL is238// used239#undef X509_NAME240#undef X509_CERT_PAIR241#undef X509_EXTENSIONS242#undef PKCS7_SIGNER_INFO243244#ifdef _MSC_VER245#pragma comment(lib, "crypt32.lib")246#endif247#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)248#include <TargetConditionals.h>249#if TARGET_OS_OSX250#include <CoreFoundation/CoreFoundation.h>251#include <Security/Security.h>252#endif // TARGET_OS_OSX253#endif // _WIN32254255#include <openssl/err.h>256#include <openssl/evp.h>257#include <openssl/ssl.h>258#include <openssl/x509v3.h>259260#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)261#include <openssl/applink.c>262#endif263264#include <iostream>265#include <sstream>266267#if OPENSSL_VERSION_NUMBER < 0x30000000L268#error Sorry, OpenSSL versions prior to 3.0.0 are not supported269#endif270271#endif272273#ifdef CPPHTTPLIB_ZLIB_SUPPORT274#include <zlib.h>275#endif276277#ifdef CPPHTTPLIB_BROTLI_SUPPORT278#include <brotli/decode.h>279#include <brotli/encode.h>280#endif281282/*283* Declaration284*/285namespace httplib {286287namespace detail {288289/*290* Backport std::make_unique from C++14.291*292* NOTE: This code came up with the following stackoverflow post:293* https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique294*295*/296297template <class T, class... Args>298typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type299make_unique(Args &&...args) {300return std::unique_ptr<T>(new T(std::forward<Args>(args)...));301}302303template <class T>304typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type305make_unique(std::size_t n) {306typedef typename std::remove_extent<T>::type RT;307return std::unique_ptr<T>(new RT[n]);308}309310struct ci {311bool operator()(const std::string &s1, const std::string &s2) const {312return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(),313s2.end(),314[](unsigned char c1, unsigned char c2) {315return ::tolower(c1) < ::tolower(c2);316});317}318};319320// This is based on321// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".322323struct scope_exit {324explicit scope_exit(std::function<void(void)> &&f)325: exit_function(std::move(f)), execute_on_destruction{true} {}326327scope_exit(scope_exit &&rhs) noexcept328: exit_function(std::move(rhs.exit_function)),329execute_on_destruction{rhs.execute_on_destruction} {330rhs.release();331}332333~scope_exit() {334if (execute_on_destruction) { this->exit_function(); }335}336337void release() { this->execute_on_destruction = false; }338339private:340scope_exit(const scope_exit &) = delete;341void operator=(const scope_exit &) = delete;342scope_exit &operator=(scope_exit &&) = delete;343344std::function<void(void)> exit_function;345bool execute_on_destruction;346};347348} // namespace detail349350enum StatusCode {351// Information responses352Continue_100 = 100,353SwitchingProtocol_101 = 101,354Processing_102 = 102,355EarlyHints_103 = 103,356357// Successful responses358OK_200 = 200,359Created_201 = 201,360Accepted_202 = 202,361NonAuthoritativeInformation_203 = 203,362NoContent_204 = 204,363ResetContent_205 = 205,364PartialContent_206 = 206,365MultiStatus_207 = 207,366AlreadyReported_208 = 208,367IMUsed_226 = 226,368369// Redirection messages370MultipleChoices_300 = 300,371MovedPermanently_301 = 301,372Found_302 = 302,373SeeOther_303 = 303,374NotModified_304 = 304,375UseProxy_305 = 305,376unused_306 = 306,377TemporaryRedirect_307 = 307,378PermanentRedirect_308 = 308,379380// Client error responses381BadRequest_400 = 400,382Unauthorized_401 = 401,383PaymentRequired_402 = 402,384Forbidden_403 = 403,385NotFound_404 = 404,386MethodNotAllowed_405 = 405,387NotAcceptable_406 = 406,388ProxyAuthenticationRequired_407 = 407,389RequestTimeout_408 = 408,390Conflict_409 = 409,391Gone_410 = 410,392LengthRequired_411 = 411,393PreconditionFailed_412 = 412,394PayloadTooLarge_413 = 413,395UriTooLong_414 = 414,396UnsupportedMediaType_415 = 415,397RangeNotSatisfiable_416 = 416,398ExpectationFailed_417 = 417,399ImATeapot_418 = 418,400MisdirectedRequest_421 = 421,401UnprocessableContent_422 = 422,402Locked_423 = 423,403FailedDependency_424 = 424,404TooEarly_425 = 425,405UpgradeRequired_426 = 426,406PreconditionRequired_428 = 428,407TooManyRequests_429 = 429,408RequestHeaderFieldsTooLarge_431 = 431,409UnavailableForLegalReasons_451 = 451,410411// Server error responses412InternalServerError_500 = 500,413NotImplemented_501 = 501,414BadGateway_502 = 502,415ServiceUnavailable_503 = 503,416GatewayTimeout_504 = 504,417HttpVersionNotSupported_505 = 505,418VariantAlsoNegotiates_506 = 506,419InsufficientStorage_507 = 507,420LoopDetected_508 = 508,421NotExtended_510 = 510,422NetworkAuthenticationRequired_511 = 511,423};424425using Headers = std::multimap<std::string, std::string, detail::ci>;426427using Params = std::multimap<std::string, std::string>;428using Match = std::smatch;429430using Progress = std::function<bool(uint64_t current, uint64_t total)>;431432struct Response;433using ResponseHandler = std::function<bool(const Response &response)>;434435struct MultipartFormData {436std::string name;437std::string content;438std::string filename;439std::string content_type;440};441using MultipartFormDataItems = std::vector<MultipartFormData>;442using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;443444class DataSink {445public:446DataSink() : os(&sb_), sb_(*this) {}447448DataSink(const DataSink &) = delete;449DataSink &operator=(const DataSink &) = delete;450DataSink(DataSink &&) = delete;451DataSink &operator=(DataSink &&) = delete;452453std::function<bool(const char *data, size_t data_len)> write;454std::function<bool()> is_writable;455std::function<void()> done;456std::function<void(const Headers &trailer)> done_with_trailer;457std::ostream os;458459private:460class data_sink_streambuf : public std::streambuf {461public:462explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}463464protected:465std::streamsize xsputn(const char *s, std::streamsize n) override {466sink_.write(s, static_cast<size_t>(n));467return n;468}469470private:471DataSink &sink_;472};473474data_sink_streambuf sb_;475};476477using ContentProvider =478std::function<bool(size_t offset, size_t length, DataSink &sink)>;479480using ContentProviderWithoutLength =481std::function<bool(size_t offset, DataSink &sink)>;482483using ContentProviderResourceReleaser = std::function<void(bool success)>;484485struct MultipartFormDataProvider {486std::string name;487ContentProviderWithoutLength provider;488std::string filename;489std::string content_type;490};491using MultipartFormDataProviderItems = std::vector<MultipartFormDataProvider>;492493using ContentReceiverWithProgress =494std::function<bool(const char *data, size_t data_length, uint64_t offset,495uint64_t total_length)>;496497using ContentReceiver =498std::function<bool(const char *data, size_t data_length)>;499500using MultipartContentHeader =501std::function<bool(const MultipartFormData &file)>;502503class ContentReader {504public:505using Reader = std::function<bool(ContentReceiver receiver)>;506using MultipartReader = std::function<bool(MultipartContentHeader header,507ContentReceiver receiver)>;508509ContentReader(Reader reader, MultipartReader multipart_reader)510: reader_(std::move(reader)),511multipart_reader_(std::move(multipart_reader)) {}512513bool operator()(MultipartContentHeader header,514ContentReceiver receiver) const {515return multipart_reader_(std::move(header), std::move(receiver));516}517518bool operator()(ContentReceiver receiver) const {519return reader_(std::move(receiver));520}521522Reader reader_;523MultipartReader multipart_reader_;524};525526using Range = std::pair<ssize_t, ssize_t>;527using Ranges = std::vector<Range>;528529struct Request {530std::string method;531std::string path;532Headers headers;533std::string body;534535std::string remote_addr;536int remote_port = -1;537std::string local_addr;538int local_port = -1;539540// for server541std::string version;542std::string target;543Params params;544MultipartFormDataMap files;545Ranges ranges;546Match matches;547std::unordered_map<std::string, std::string> path_params;548549// for client550ResponseHandler response_handler;551ContentReceiverWithProgress content_receiver;552Progress progress;553#ifdef CPPHTTPLIB_OPENSSL_SUPPORT554const SSL *ssl = nullptr;555#endif556557bool has_header(const std::string &key) const;558std::string get_header_value(const std::string &key, size_t id = 0) const;559uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const;560size_t get_header_value_count(const std::string &key) const;561void set_header(const std::string &key, const std::string &val);562563bool has_param(const std::string &key) const;564std::string get_param_value(const std::string &key, size_t id = 0) const;565size_t get_param_value_count(const std::string &key) const;566567bool is_multipart_form_data() const;568569bool has_file(const std::string &key) const;570MultipartFormData get_file_value(const std::string &key) const;571std::vector<MultipartFormData> get_file_values(const std::string &key) const;572573// private members...574size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;575size_t content_length_ = 0;576ContentProvider content_provider_;577bool is_chunked_content_provider_ = false;578size_t authorization_count_ = 0;579};580581struct Response {582std::string version;583int status = -1;584std::string reason;585Headers headers;586std::string body;587std::string location; // Redirect location588589bool has_header(const std::string &key) const;590std::string get_header_value(const std::string &key, size_t id = 0) const;591uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const;592size_t get_header_value_count(const std::string &key) const;593void set_header(const std::string &key, const std::string &val);594595void set_redirect(const std::string &url, int status = StatusCode::Found_302);596void set_content(const char *s, size_t n, const std::string &content_type);597void set_content(const std::string &s, const std::string &content_type);598599void set_content_provider(600size_t length, const std::string &content_type, ContentProvider provider,601ContentProviderResourceReleaser resource_releaser = nullptr);602603void set_content_provider(604const std::string &content_type, ContentProviderWithoutLength provider,605ContentProviderResourceReleaser resource_releaser = nullptr);606607void set_chunked_content_provider(608const std::string &content_type, ContentProviderWithoutLength provider,609ContentProviderResourceReleaser resource_releaser = nullptr);610611Response() = default;612Response(const Response &) = default;613Response &operator=(const Response &) = default;614Response(Response &&) = default;615Response &operator=(Response &&) = default;616~Response() {617if (content_provider_resource_releaser_) {618content_provider_resource_releaser_(content_provider_success_);619}620}621622// private members...623size_t content_length_ = 0;624ContentProvider content_provider_;625ContentProviderResourceReleaser content_provider_resource_releaser_;626bool is_chunked_content_provider_ = false;627bool content_provider_success_ = false;628};629630class Stream {631public:632virtual ~Stream() = default;633634virtual bool is_readable() const = 0;635virtual bool is_writable() const = 0;636637virtual ssize_t read(char *ptr, size_t size) = 0;638virtual ssize_t write(const char *ptr, size_t size) = 0;639virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;640virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;641virtual socket_t socket() const = 0;642643template <typename... Args>644ssize_t write_format(const char *fmt, const Args &...args);645ssize_t write(const char *ptr);646ssize_t write(const std::string &s);647};648649class TaskQueue {650public:651TaskQueue() = default;652virtual ~TaskQueue() = default;653654virtual bool enqueue(std::function<void()> fn) = 0;655virtual void shutdown() = 0;656657virtual void on_idle() {}658};659660class ThreadPool : public TaskQueue {661public:662explicit ThreadPool(size_t n, size_t mqr = 0)663: shutdown_(false), max_queued_requests_(mqr) {664while (n) {665threads_.emplace_back(worker(*this));666n--;667}668}669670ThreadPool(const ThreadPool &) = delete;671~ThreadPool() override = default;672673bool enqueue(std::function<void()> fn) override {674{675std::unique_lock<std::mutex> lock(mutex_);676if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {677return false;678}679jobs_.push_back(std::move(fn));680}681682cond_.notify_one();683return true;684}685686void shutdown() override {687// Stop all worker threads...688{689std::unique_lock<std::mutex> lock(mutex_);690shutdown_ = true;691}692693cond_.notify_all();694695// Join...696for (auto &t : threads_) {697t.join();698}699}700701private:702struct worker {703explicit worker(ThreadPool &pool) : pool_(pool) {}704705void operator()() {706for (;;) {707std::function<void()> fn;708{709std::unique_lock<std::mutex> lock(pool_.mutex_);710711pool_.cond_.wait(712lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });713714if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }715716fn = std::move(pool_.jobs_.front());717pool_.jobs_.pop_front();718}719720assert(true == static_cast<bool>(fn));721fn();722}723}724725ThreadPool &pool_;726};727friend struct worker;728729std::vector<std::thread> threads_;730std::list<std::function<void()>> jobs_;731732bool shutdown_;733size_t max_queued_requests_ = 0;734735std::condition_variable cond_;736std::mutex mutex_;737};738739using Logger = std::function<void(const Request &, const Response &)>;740741using SocketOptions = std::function<void(socket_t sock)>;742743void default_socket_options(socket_t sock);744745const char *status_message(int status);746747std::string get_bearer_token_auth(const Request &req);748749namespace detail {750751class MatcherBase {752public:753virtual ~MatcherBase() = default;754755// Match request path and populate its matches and756virtual bool match(Request &request) const = 0;757};758759/**760* Captures parameters in request path and stores them in Request::path_params761*762* Capture name is a substring of a pattern from : to /.763* The rest of the pattern is matched agains the request path directly764* Parameters are captured starting from the next character after765* the end of the last matched static pattern fragment until the next /.766*767* Example pattern:768* "/path/fragments/:capture/more/fragments/:second_capture"769* Static fragments:770* "/path/fragments/", "more/fragments/"771*772* Given the following request path:773* "/path/fragments/:1/more/fragments/:2"774* the resulting capture will be775* {{"capture", "1"}, {"second_capture", "2"}}776*/777class PathParamsMatcher : public MatcherBase {778public:779PathParamsMatcher(const std::string &pattern);780781bool match(Request &request) const override;782783private:784static constexpr char marker = ':';785// Treat segment separators as the end of path parameter capture786// Does not need to handle query parameters as they are parsed before path787// matching788static constexpr char separator = '/';789790// Contains static path fragments to match against, excluding the '/' after791// path params792// Fragments are separated by path params793std::vector<std::string> static_fragments_;794// Stores the names of the path parameters to be used as keys in the795// Request::path_params map796std::vector<std::string> param_names_;797};798799/**800* Performs std::regex_match on request path801* and stores the result in Request::matches802*803* Note that regex match is performed directly on the whole request.804* This means that wildcard patterns may match multiple path segments with /:805* "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end".806*/807class RegexMatcher : public MatcherBase {808public:809RegexMatcher(const std::string &pattern) : regex_(pattern) {}810811bool match(Request &request) const override;812813private:814std::regex regex_;815};816817ssize_t write_headers(Stream &strm, const Headers &headers);818819} // namespace detail820821class Server {822public:823using Handler = std::function<void(const Request &, Response &)>;824825using ExceptionHandler =826std::function<void(const Request &, Response &, std::exception_ptr ep)>;827828enum class HandlerResponse {829Handled,830Unhandled,831};832using HandlerWithResponse =833std::function<HandlerResponse(const Request &, Response &)>;834835using HandlerWithContentReader = std::function<void(836const Request &, Response &, const ContentReader &content_reader)>;837838using Expect100ContinueHandler =839std::function<int(const Request &, Response &)>;840841Server();842843virtual ~Server();844845virtual bool is_valid() const;846847Server &Get(const std::string &pattern, Handler handler);848Server &Post(const std::string &pattern, Handler handler);849Server &Post(const std::string &pattern, HandlerWithContentReader handler);850Server &Put(const std::string &pattern, Handler handler);851Server &Put(const std::string &pattern, HandlerWithContentReader handler);852Server &Patch(const std::string &pattern, Handler handler);853Server &Patch(const std::string &pattern, HandlerWithContentReader handler);854Server &Delete(const std::string &pattern, Handler handler);855Server &Delete(const std::string &pattern, HandlerWithContentReader handler);856Server &Options(const std::string &pattern, Handler handler);857858bool set_base_dir(const std::string &dir,859const std::string &mount_point = std::string());860bool set_mount_point(const std::string &mount_point, const std::string &dir,861Headers headers = Headers());862bool remove_mount_point(const std::string &mount_point);863Server &set_file_extension_and_mimetype_mapping(const std::string &ext,864const std::string &mime);865Server &set_default_file_mimetype(const std::string &mime);866Server &set_file_request_handler(Handler handler);867868Server &set_error_handler(HandlerWithResponse handler);869Server &set_error_handler(Handler handler);870Server &set_exception_handler(ExceptionHandler handler);871Server &set_pre_routing_handler(HandlerWithResponse handler);872Server &set_post_routing_handler(Handler handler);873874Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);875Server &set_logger(Logger logger);876877Server &set_address_family(int family);878Server &set_tcp_nodelay(bool on);879Server &set_socket_options(SocketOptions socket_options);880881Server &set_default_headers(Headers headers);882Server &883set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);884885Server &set_keep_alive_max_count(size_t count);886Server &set_keep_alive_timeout(time_t sec);887888Server &set_read_timeout(time_t sec, time_t usec = 0);889template <class Rep, class Period>890Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);891892Server &set_write_timeout(time_t sec, time_t usec = 0);893template <class Rep, class Period>894Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);895896Server &set_idle_interval(time_t sec, time_t usec = 0);897template <class Rep, class Period>898Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);899900Server &set_payload_max_length(size_t length);901902bool bind_to_port(const std::string &host, int port, int socket_flags = 0);903int bind_to_any_port(const std::string &host, int socket_flags = 0);904bool listen_after_bind();905906bool listen(const std::string &host, int port, int socket_flags = 0);907908bool is_running() const;909void wait_until_ready() const;910void stop();911912std::function<TaskQueue *(void)> new_task_queue;913914protected:915bool process_request(Stream &strm, bool close_connection,916bool &connection_closed,917const std::function<void(Request &)> &setup_request);918919std::atomic<socket_t> svr_sock_{INVALID_SOCKET};920size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;921time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;922time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;923time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;924time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;925time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;926time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;927time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;928size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;929930private:931using Handlers =932std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;933using HandlersForContentReader =934std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,935HandlerWithContentReader>>;936937static std::unique_ptr<detail::MatcherBase>938make_matcher(const std::string &pattern);939940socket_t create_server_socket(const std::string &host, int port,941int socket_flags,942SocketOptions socket_options) const;943int bind_internal(const std::string &host, int port, int socket_flags);944bool listen_internal();945946bool routing(Request &req, Response &res, Stream &strm);947bool handle_file_request(const Request &req, Response &res,948bool head = false);949bool dispatch_request(Request &req, Response &res,950const Handlers &handlers) const;951bool dispatch_request_for_content_reader(952Request &req, Response &res, ContentReader content_reader,953const HandlersForContentReader &handlers) const;954955bool parse_request_line(const char *s, Request &req) const;956void apply_ranges(const Request &req, Response &res,957std::string &content_type, std::string &boundary) const;958bool write_response(Stream &strm, bool close_connection, const Request &req,959Response &res);960bool write_response_with_content(Stream &strm, bool close_connection,961const Request &req, Response &res);962bool write_response_core(Stream &strm, bool close_connection,963const Request &req, Response &res,964bool need_apply_ranges);965bool write_content_with_provider(Stream &strm, const Request &req,966Response &res, const std::string &boundary,967const std::string &content_type);968bool read_content(Stream &strm, Request &req, Response &res);969bool970read_content_with_content_receiver(Stream &strm, Request &req, Response &res,971ContentReceiver receiver,972MultipartContentHeader multipart_header,973ContentReceiver multipart_receiver);974bool read_content_core(Stream &strm, Request &req, Response &res,975ContentReceiver receiver,976MultipartContentHeader multipart_header,977ContentReceiver multipart_receiver) const;978979virtual bool process_and_close_socket(socket_t sock);980981std::atomic<bool> is_running_{false};982std::atomic<bool> done_{false};983984struct MountPointEntry {985std::string mount_point;986std::string base_dir;987Headers headers;988};989std::vector<MountPointEntry> base_dirs_;990std::map<std::string, std::string> file_extension_and_mimetype_map_;991std::string default_file_mimetype_ = "application/octet-stream";992Handler file_request_handler_;993994Handlers get_handlers_;995Handlers post_handlers_;996HandlersForContentReader post_handlers_for_content_reader_;997Handlers put_handlers_;998HandlersForContentReader put_handlers_for_content_reader_;999Handlers patch_handlers_;1000HandlersForContentReader patch_handlers_for_content_reader_;1001Handlers delete_handlers_;1002HandlersForContentReader delete_handlers_for_content_reader_;1003Handlers options_handlers_;10041005HandlerWithResponse error_handler_;1006ExceptionHandler exception_handler_;1007HandlerWithResponse pre_routing_handler_;1008Handler post_routing_handler_;1009Expect100ContinueHandler expect_100_continue_handler_;10101011Logger logger_;10121013int address_family_ = AF_UNSPEC;1014bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;1015SocketOptions socket_options_ = default_socket_options;10161017Headers default_headers_;1018std::function<ssize_t(Stream &, Headers &)> header_writer_ =1019detail::write_headers;1020};10211022enum class Error {1023Success = 0,1024Unknown,1025Connection,1026BindIPAddress,1027Read,1028Write,1029ExceedRedirectCount,1030Canceled,1031SSLConnection,1032SSLLoadingCerts,1033SSLServerVerification,1034UnsupportedMultipartBoundaryChars,1035Compression,1036ConnectionTimeout,1037ProxyConnection,10381039// For internal use only1040SSLPeerCouldBeClosed_,1041};10421043std::string to_string(Error error);10441045std::ostream &operator<<(std::ostream &os, const Error &obj);10461047class Result {1048public:1049Result() = default;1050Result(std::unique_ptr<Response> &&res, Error err,1051Headers &&request_headers = Headers{})1052: res_(std::move(res)), err_(err),1053request_headers_(std::move(request_headers)) {}1054// Response1055operator bool() const { return res_ != nullptr; }1056bool operator==(std::nullptr_t) const { return res_ == nullptr; }1057bool operator!=(std::nullptr_t) const { return res_ != nullptr; }1058const Response &value() const { return *res_; }1059Response &value() { return *res_; }1060const Response &operator*() const { return *res_; }1061Response &operator*() { return *res_; }1062const Response *operator->() const { return res_.get(); }1063Response *operator->() { return res_.get(); }10641065// Error1066Error error() const { return err_; }10671068// Request Headers1069bool has_request_header(const std::string &key) const;1070std::string get_request_header_value(const std::string &key,1071size_t id = 0) const;1072uint64_t get_request_header_value_u64(const std::string &key,1073size_t id = 0) const;1074size_t get_request_header_value_count(const std::string &key) const;10751076private:1077std::unique_ptr<Response> res_;1078Error err_ = Error::Unknown;1079Headers request_headers_;1080};10811082class ClientImpl {1083public:1084explicit ClientImpl(const std::string &host);10851086explicit ClientImpl(const std::string &host, int port);10871088explicit ClientImpl(const std::string &host, int port,1089const std::string &client_cert_path,1090const std::string &client_key_path);10911092virtual ~ClientImpl();10931094virtual bool is_valid() const;10951096Result Get(const std::string &path);1097Result Get(const std::string &path, const Headers &headers);1098Result Get(const std::string &path, Progress progress);1099Result Get(const std::string &path, const Headers &headers,1100Progress progress);1101Result Get(const std::string &path, ContentReceiver content_receiver);1102Result Get(const std::string &path, const Headers &headers,1103ContentReceiver content_receiver);1104Result Get(const std::string &path, ContentReceiver content_receiver,1105Progress progress);1106Result Get(const std::string &path, const Headers &headers,1107ContentReceiver content_receiver, Progress progress);1108Result Get(const std::string &path, ResponseHandler response_handler,1109ContentReceiver content_receiver);1110Result Get(const std::string &path, const Headers &headers,1111ResponseHandler response_handler,1112ContentReceiver content_receiver);1113Result Get(const std::string &path, ResponseHandler response_handler,1114ContentReceiver content_receiver, Progress progress);1115Result Get(const std::string &path, const Headers &headers,1116ResponseHandler response_handler, ContentReceiver content_receiver,1117Progress progress);11181119Result Get(const std::string &path, const Params ¶ms,1120const Headers &headers, Progress progress = nullptr);1121Result Get(const std::string &path, const Params ¶ms,1122const Headers &headers, ContentReceiver content_receiver,1123Progress progress = nullptr);1124Result Get(const std::string &path, const Params ¶ms,1125const Headers &headers, ResponseHandler response_handler,1126ContentReceiver content_receiver, Progress progress = nullptr);11271128Result Head(const std::string &path);1129Result Head(const std::string &path, const Headers &headers);11301131Result Post(const std::string &path);1132Result Post(const std::string &path, const Headers &headers);1133Result Post(const std::string &path, const char *body, size_t content_length,1134const std::string &content_type);1135Result Post(const std::string &path, const Headers &headers, const char *body,1136size_t content_length, const std::string &content_type);1137Result Post(const std::string &path, const std::string &body,1138const std::string &content_type);1139Result Post(const std::string &path, const Headers &headers,1140const std::string &body, const std::string &content_type);1141Result Post(const std::string &path, size_t content_length,1142ContentProvider content_provider,1143const std::string &content_type);1144Result Post(const std::string &path,1145ContentProviderWithoutLength content_provider,1146const std::string &content_type);1147Result Post(const std::string &path, const Headers &headers,1148size_t content_length, ContentProvider content_provider,1149const std::string &content_type);1150Result Post(const std::string &path, const Headers &headers,1151ContentProviderWithoutLength content_provider,1152const std::string &content_type);1153Result Post(const std::string &path, const Params ¶ms);1154Result Post(const std::string &path, const Headers &headers,1155const Params ¶ms);1156Result Post(const std::string &path, const MultipartFormDataItems &items);1157Result Post(const std::string &path, const Headers &headers,1158const MultipartFormDataItems &items);1159Result Post(const std::string &path, const Headers &headers,1160const MultipartFormDataItems &items, const std::string &boundary);1161Result Post(const std::string &path, const Headers &headers,1162const MultipartFormDataItems &items,1163const MultipartFormDataProviderItems &provider_items);11641165Result Put(const std::string &path);1166Result Put(const std::string &path, const char *body, size_t content_length,1167const std::string &content_type);1168Result Put(const std::string &path, const Headers &headers, const char *body,1169size_t content_length, const std::string &content_type);1170Result Put(const std::string &path, const std::string &body,1171const std::string &content_type);1172Result Put(const std::string &path, const Headers &headers,1173const std::string &body, const std::string &content_type);1174Result Put(const std::string &path, size_t content_length,1175ContentProvider content_provider, const std::string &content_type);1176Result Put(const std::string &path,1177ContentProviderWithoutLength content_provider,1178const std::string &content_type);1179Result Put(const std::string &path, const Headers &headers,1180size_t content_length, ContentProvider content_provider,1181const std::string &content_type);1182Result Put(const std::string &path, const Headers &headers,1183ContentProviderWithoutLength content_provider,1184const std::string &content_type);1185Result Put(const std::string &path, const Params ¶ms);1186Result Put(const std::string &path, const Headers &headers,1187const Params ¶ms);1188Result Put(const std::string &path, const MultipartFormDataItems &items);1189Result Put(const std::string &path, const Headers &headers,1190const MultipartFormDataItems &items);1191Result Put(const std::string &path, const Headers &headers,1192const MultipartFormDataItems &items, const std::string &boundary);1193Result Put(const std::string &path, const Headers &headers,1194const MultipartFormDataItems &items,1195const MultipartFormDataProviderItems &provider_items);11961197Result Patch(const std::string &path);1198Result Patch(const std::string &path, const char *body, size_t content_length,1199const std::string &content_type);1200Result Patch(const std::string &path, const Headers &headers,1201const char *body, size_t content_length,1202const std::string &content_type);1203Result Patch(const std::string &path, const std::string &body,1204const std::string &content_type);1205Result Patch(const std::string &path, const Headers &headers,1206const std::string &body, const std::string &content_type);1207Result Patch(const std::string &path, size_t content_length,1208ContentProvider content_provider,1209const std::string &content_type);1210Result Patch(const std::string &path,1211ContentProviderWithoutLength content_provider,1212const std::string &content_type);1213Result Patch(const std::string &path, const Headers &headers,1214size_t content_length, ContentProvider content_provider,1215const std::string &content_type);1216Result Patch(const std::string &path, const Headers &headers,1217ContentProviderWithoutLength content_provider,1218const std::string &content_type);12191220Result Delete(const std::string &path);1221Result Delete(const std::string &path, const Headers &headers);1222Result Delete(const std::string &path, const char *body,1223size_t content_length, const std::string &content_type);1224Result Delete(const std::string &path, const Headers &headers,1225const char *body, size_t content_length,1226const std::string &content_type);1227Result Delete(const std::string &path, const std::string &body,1228const std::string &content_type);1229Result Delete(const std::string &path, const Headers &headers,1230const std::string &body, const std::string &content_type);12311232Result Options(const std::string &path);1233Result Options(const std::string &path, const Headers &headers);12341235bool send(Request &req, Response &res, Error &error);1236Result send(const Request &req);12371238void stop();12391240std::string host() const;1241int port() const;12421243size_t is_socket_open() const;1244socket_t socket() const;12451246void set_hostname_addr_map(std::map<std::string, std::string> addr_map);12471248void set_default_headers(Headers headers);12491250void1251set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);12521253void set_address_family(int family);1254void set_tcp_nodelay(bool on);1255void set_socket_options(SocketOptions socket_options);12561257void set_connection_timeout(time_t sec, time_t usec = 0);1258template <class Rep, class Period>1259void1260set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);12611262void set_read_timeout(time_t sec, time_t usec = 0);1263template <class Rep, class Period>1264void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);12651266void set_write_timeout(time_t sec, time_t usec = 0);1267template <class Rep, class Period>1268void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);12691270void set_basic_auth(const std::string &username, const std::string &password);1271void set_bearer_token_auth(const std::string &token);1272#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1273void set_digest_auth(const std::string &username,1274const std::string &password);1275#endif12761277void set_keep_alive(bool on);1278void set_follow_location(bool on);12791280void set_url_encode(bool on);12811282void set_compress(bool on);12831284void set_decompress(bool on);12851286void set_interface(const std::string &intf);12871288void set_proxy(const std::string &host, int port);1289void set_proxy_basic_auth(const std::string &username,1290const std::string &password);1291void set_proxy_bearer_token_auth(const std::string &token);1292#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1293void set_proxy_digest_auth(const std::string &username,1294const std::string &password);1295#endif12961297#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1298void set_ca_cert_path(const std::string &ca_cert_file_path,1299const std::string &ca_cert_dir_path = std::string());1300void set_ca_cert_store(X509_STORE *ca_cert_store);1301X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;1302#endif13031304#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1305void enable_server_certificate_verification(bool enabled);1306#endif13071308void set_logger(Logger logger);13091310protected:1311struct Socket {1312socket_t sock = INVALID_SOCKET;1313#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1314SSL *ssl = nullptr;1315#endif13161317bool is_open() const { return sock != INVALID_SOCKET; }1318};13191320virtual bool create_and_connect_socket(Socket &socket, Error &error);13211322// All of:1323// shutdown_ssl1324// shutdown_socket1325// close_socket1326// should ONLY be called when socket_mutex_ is locked.1327// Also, shutdown_ssl and close_socket should also NOT be called concurrently1328// with a DIFFERENT thread sending requests using that socket.1329virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);1330void shutdown_socket(Socket &socket) const;1331void close_socket(Socket &socket);13321333bool process_request(Stream &strm, Request &req, Response &res,1334bool close_connection, Error &error);13351336bool write_content_with_provider(Stream &strm, const Request &req,1337Error &error) const;13381339void copy_settings(const ClientImpl &rhs);13401341// Socket endpoint information1342const std::string host_;1343const int port_;1344const std::string host_and_port_;13451346// Current open socket1347Socket socket_;1348mutable std::mutex socket_mutex_;1349std::recursive_mutex request_mutex_;13501351// These are all protected under socket_mutex1352size_t socket_requests_in_flight_ = 0;1353std::thread::id socket_requests_are_from_thread_ = std::thread::id();1354bool socket_should_be_closed_when_request_is_done_ = false;13551356// Hostname-IP map1357std::map<std::string, std::string> addr_map_;13581359// Default headers1360Headers default_headers_;13611362// Header writer1363std::function<ssize_t(Stream &, Headers &)> header_writer_ =1364detail::write_headers;13651366// Settings1367std::string client_cert_path_;1368std::string client_key_path_;13691370time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;1371time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;1372time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;1373time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;1374time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;1375time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;13761377std::string basic_auth_username_;1378std::string basic_auth_password_;1379std::string bearer_token_auth_token_;1380#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1381std::string digest_auth_username_;1382std::string digest_auth_password_;1383#endif13841385bool keep_alive_ = false;1386bool follow_location_ = false;13871388bool url_encode_ = true;13891390int address_family_ = AF_UNSPEC;1391bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;1392SocketOptions socket_options_ = nullptr;13931394bool compress_ = false;1395bool decompress_ = true;13961397std::string interface_;13981399std::string proxy_host_;1400int proxy_port_ = -1;14011402std::string proxy_basic_auth_username_;1403std::string proxy_basic_auth_password_;1404std::string proxy_bearer_token_auth_token_;1405#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1406std::string proxy_digest_auth_username_;1407std::string proxy_digest_auth_password_;1408#endif14091410#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1411std::string ca_cert_file_path_;1412std::string ca_cert_dir_path_;14131414X509_STORE *ca_cert_store_ = nullptr;1415#endif14161417#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1418bool server_certificate_verification_ = true;1419#endif14201421Logger logger_;14221423private:1424bool send_(Request &req, Response &res, Error &error);1425Result send_(Request &&req);14261427socket_t create_client_socket(Error &error) const;1428bool read_response_line(Stream &strm, const Request &req,1429Response &res) const;1430bool write_request(Stream &strm, Request &req, bool close_connection,1431Error &error);1432bool redirect(Request &req, Response &res, Error &error);1433bool handle_request(Stream &strm, Request &req, Response &res,1434bool close_connection, Error &error);1435std::unique_ptr<Response> send_with_content_provider(1436Request &req, const char *body, size_t content_length,1437ContentProvider content_provider,1438ContentProviderWithoutLength content_provider_without_length,1439const std::string &content_type, Error &error);1440Result send_with_content_provider(1441const std::string &method, const std::string &path,1442const Headers &headers, const char *body, size_t content_length,1443ContentProvider content_provider,1444ContentProviderWithoutLength content_provider_without_length,1445const std::string &content_type);1446ContentProviderWithoutLength get_multipart_content_provider(1447const std::string &boundary, const MultipartFormDataItems &items,1448const MultipartFormDataProviderItems &provider_items) const;14491450std::string adjust_host_string(const std::string &host) const;14511452virtual bool process_socket(const Socket &socket,1453std::function<bool(Stream &strm)> callback);1454virtual bool is_ssl() const;1455};14561457class Client {1458public:1459// Universal interface1460explicit Client(const std::string &scheme_host_port);14611462explicit Client(const std::string &scheme_host_port,1463const std::string &client_cert_path,1464const std::string &client_key_path);14651466// HTTP only interface1467explicit Client(const std::string &host, int port);14681469explicit Client(const std::string &host, int port,1470const std::string &client_cert_path,1471const std::string &client_key_path);14721473Client(Client &&) = default;14741475~Client();14761477bool is_valid() const;14781479Result Get(const std::string &path);1480Result Get(const std::string &path, const Headers &headers);1481Result Get(const std::string &path, Progress progress);1482Result Get(const std::string &path, const Headers &headers,1483Progress progress);1484Result Get(const std::string &path, ContentReceiver content_receiver);1485Result Get(const std::string &path, const Headers &headers,1486ContentReceiver content_receiver);1487Result Get(const std::string &path, ContentReceiver content_receiver,1488Progress progress);1489Result Get(const std::string &path, const Headers &headers,1490ContentReceiver content_receiver, Progress progress);1491Result Get(const std::string &path, ResponseHandler response_handler,1492ContentReceiver content_receiver);1493Result Get(const std::string &path, const Headers &headers,1494ResponseHandler response_handler,1495ContentReceiver content_receiver);1496Result Get(const std::string &path, const Headers &headers,1497ResponseHandler response_handler, ContentReceiver content_receiver,1498Progress progress);1499Result Get(const std::string &path, ResponseHandler response_handler,1500ContentReceiver content_receiver, Progress progress);15011502Result Get(const std::string &path, const Params ¶ms,1503const Headers &headers, Progress progress = nullptr);1504Result Get(const std::string &path, const Params ¶ms,1505const Headers &headers, ContentReceiver content_receiver,1506Progress progress = nullptr);1507Result Get(const std::string &path, const Params ¶ms,1508const Headers &headers, ResponseHandler response_handler,1509ContentReceiver content_receiver, Progress progress = nullptr);15101511Result Head(const std::string &path);1512Result Head(const std::string &path, const Headers &headers);15131514Result Post(const std::string &path);1515Result Post(const std::string &path, const Headers &headers);1516Result Post(const std::string &path, const char *body, size_t content_length,1517const std::string &content_type);1518Result Post(const std::string &path, const Headers &headers, const char *body,1519size_t content_length, const std::string &content_type);1520Result Post(const std::string &path, const std::string &body,1521const std::string &content_type);1522Result Post(const std::string &path, const Headers &headers,1523const std::string &body, const std::string &content_type);1524Result Post(const std::string &path, size_t content_length,1525ContentProvider content_provider,1526const std::string &content_type);1527Result Post(const std::string &path,1528ContentProviderWithoutLength content_provider,1529const std::string &content_type);1530Result Post(const std::string &path, const Headers &headers,1531size_t content_length, ContentProvider content_provider,1532const std::string &content_type);1533Result Post(const std::string &path, const Headers &headers,1534ContentProviderWithoutLength content_provider,1535const std::string &content_type);1536Result Post(const std::string &path, const Params ¶ms);1537Result Post(const std::string &path, const Headers &headers,1538const Params ¶ms);1539Result Post(const std::string &path, const MultipartFormDataItems &items);1540Result Post(const std::string &path, const Headers &headers,1541const MultipartFormDataItems &items);1542Result Post(const std::string &path, const Headers &headers,1543const MultipartFormDataItems &items, const std::string &boundary);1544Result Post(const std::string &path, const Headers &headers,1545const MultipartFormDataItems &items,1546const MultipartFormDataProviderItems &provider_items);15471548Result Put(const std::string &path);1549Result Put(const std::string &path, const char *body, size_t content_length,1550const std::string &content_type);1551Result Put(const std::string &path, const Headers &headers, const char *body,1552size_t content_length, const std::string &content_type);1553Result Put(const std::string &path, const std::string &body,1554const std::string &content_type);1555Result Put(const std::string &path, const Headers &headers,1556const std::string &body, const std::string &content_type);1557Result Put(const std::string &path, size_t content_length,1558ContentProvider content_provider, const std::string &content_type);1559Result Put(const std::string &path,1560ContentProviderWithoutLength content_provider,1561const std::string &content_type);1562Result Put(const std::string &path, const Headers &headers,1563size_t content_length, ContentProvider content_provider,1564const std::string &content_type);1565Result Put(const std::string &path, const Headers &headers,1566ContentProviderWithoutLength content_provider,1567const std::string &content_type);1568Result Put(const std::string &path, const Params ¶ms);1569Result Put(const std::string &path, const Headers &headers,1570const Params ¶ms);1571Result Put(const std::string &path, const MultipartFormDataItems &items);1572Result Put(const std::string &path, const Headers &headers,1573const MultipartFormDataItems &items);1574Result Put(const std::string &path, const Headers &headers,1575const MultipartFormDataItems &items, const std::string &boundary);1576Result Put(const std::string &path, const Headers &headers,1577const MultipartFormDataItems &items,1578const MultipartFormDataProviderItems &provider_items);15791580Result Patch(const std::string &path);1581Result Patch(const std::string &path, const char *body, size_t content_length,1582const std::string &content_type);1583Result Patch(const std::string &path, const Headers &headers,1584const char *body, size_t content_length,1585const std::string &content_type);1586Result Patch(const std::string &path, const std::string &body,1587const std::string &content_type);1588Result Patch(const std::string &path, const Headers &headers,1589const std::string &body, const std::string &content_type);1590Result Patch(const std::string &path, size_t content_length,1591ContentProvider content_provider,1592const std::string &content_type);1593Result Patch(const std::string &path,1594ContentProviderWithoutLength content_provider,1595const std::string &content_type);1596Result Patch(const std::string &path, const Headers &headers,1597size_t content_length, ContentProvider content_provider,1598const std::string &content_type);1599Result Patch(const std::string &path, const Headers &headers,1600ContentProviderWithoutLength content_provider,1601const std::string &content_type);16021603Result Delete(const std::string &path);1604Result Delete(const std::string &path, const Headers &headers);1605Result Delete(const std::string &path, const char *body,1606size_t content_length, const std::string &content_type);1607Result Delete(const std::string &path, const Headers &headers,1608const char *body, size_t content_length,1609const std::string &content_type);1610Result Delete(const std::string &path, const std::string &body,1611const std::string &content_type);1612Result Delete(const std::string &path, const Headers &headers,1613const std::string &body, const std::string &content_type);16141615Result Options(const std::string &path);1616Result Options(const std::string &path, const Headers &headers);16171618bool send(Request &req, Response &res, Error &error);1619Result send(const Request &req);16201621void stop();16221623std::string host() const;1624int port() const;16251626size_t is_socket_open() const;1627socket_t socket() const;16281629void set_hostname_addr_map(std::map<std::string, std::string> addr_map);16301631void set_default_headers(Headers headers);16321633void1634set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);16351636void set_address_family(int family);1637void set_tcp_nodelay(bool on);1638void set_socket_options(SocketOptions socket_options);16391640void set_connection_timeout(time_t sec, time_t usec = 0);1641template <class Rep, class Period>1642void1643set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);16441645void set_read_timeout(time_t sec, time_t usec = 0);1646template <class Rep, class Period>1647void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);16481649void set_write_timeout(time_t sec, time_t usec = 0);1650template <class Rep, class Period>1651void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);16521653void set_basic_auth(const std::string &username, const std::string &password);1654void set_bearer_token_auth(const std::string &token);1655#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1656void set_digest_auth(const std::string &username,1657const std::string &password);1658#endif16591660void set_keep_alive(bool on);1661void set_follow_location(bool on);16621663void set_url_encode(bool on);16641665void set_compress(bool on);16661667void set_decompress(bool on);16681669void set_interface(const std::string &intf);16701671void set_proxy(const std::string &host, int port);1672void set_proxy_basic_auth(const std::string &username,1673const std::string &password);1674void set_proxy_bearer_token_auth(const std::string &token);1675#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1676void set_proxy_digest_auth(const std::string &username,1677const std::string &password);1678#endif16791680#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1681void enable_server_certificate_verification(bool enabled);1682#endif16831684void set_logger(Logger logger);16851686// SSL1687#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1688void set_ca_cert_path(const std::string &ca_cert_file_path,1689const std::string &ca_cert_dir_path = std::string());16901691void set_ca_cert_store(X509_STORE *ca_cert_store);1692void load_ca_cert_store(const char *ca_cert, std::size_t size);16931694long get_openssl_verify_result() const;16951696SSL_CTX *ssl_context() const;1697#endif16981699private:1700std::unique_ptr<ClientImpl> cli_;17011702#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1703bool is_ssl_ = false;1704#endif1705};17061707#ifdef CPPHTTPLIB_OPENSSL_SUPPORT1708class SSLServer : public Server {1709public:1710SSLServer(const char *cert_path, const char *private_key_path,1711const char *client_ca_cert_file_path = nullptr,1712const char *client_ca_cert_dir_path = nullptr,1713const char *private_key_password = nullptr);17141715SSLServer(X509 *cert, EVP_PKEY *private_key,1716X509_STORE *client_ca_cert_store = nullptr);17171718SSLServer(1719const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);17201721~SSLServer() override;17221723bool is_valid() const override;17241725SSL_CTX *ssl_context() const;17261727private:1728bool process_and_close_socket(socket_t sock) override;17291730SSL_CTX *ctx_;1731std::mutex ctx_mutex_;1732};17331734class SSLClient : public ClientImpl {1735public:1736explicit SSLClient(const std::string &host);17371738explicit SSLClient(const std::string &host, int port);17391740explicit SSLClient(const std::string &host, int port,1741const std::string &client_cert_path,1742const std::string &client_key_path);17431744explicit SSLClient(const std::string &host, int port, X509 *client_cert,1745EVP_PKEY *client_key);17461747~SSLClient() override;17481749bool is_valid() const override;17501751void set_ca_cert_store(X509_STORE *ca_cert_store);1752void load_ca_cert_store(const char *ca_cert, std::size_t size);17531754long get_openssl_verify_result() const;17551756SSL_CTX *ssl_context() const;17571758private:1759bool create_and_connect_socket(Socket &socket, Error &error) override;1760void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;1761void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);17621763bool process_socket(const Socket &socket,1764std::function<bool(Stream &strm)> callback) override;1765bool is_ssl() const override;17661767bool connect_with_proxy(Socket &sock, Response &res, bool &success,1768Error &error);1769bool initialize_ssl(Socket &socket, Error &error);17701771bool load_certs();17721773bool verify_host(X509 *server_cert) const;1774bool verify_host_with_subject_alt_name(X509 *server_cert) const;1775bool verify_host_with_common_name(X509 *server_cert) const;1776bool check_host_name(const char *pattern, size_t pattern_len) const;17771778SSL_CTX *ctx_;1779std::mutex ctx_mutex_;1780std::once_flag initialize_cert_;17811782std::vector<std::string> host_components_;17831784long verify_result_ = 0;17851786friend class ClientImpl;1787};1788#endif17891790/*1791* Implementation of template methods.1792*/17931794namespace detail {17951796template <typename T, typename U>1797inline void duration_to_sec_and_usec(const T &duration, U callback) {1798auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();1799auto usec = std::chrono::duration_cast<std::chrono::microseconds>(1800duration - std::chrono::seconds(sec))1801.count();1802callback(static_cast<time_t>(sec), static_cast<time_t>(usec));1803}18041805inline uint64_t get_header_value_u64(const Headers &headers,1806const std::string &key, size_t id,1807uint64_t def) {1808auto rng = headers.equal_range(key);1809auto it = rng.first;1810std::advance(it, static_cast<ssize_t>(id));1811if (it != rng.second) {1812return std::strtoull(it->second.data(), nullptr, 10);1813}1814return def;1815}18161817} // namespace detail18181819inline uint64_t Request::get_header_value_u64(const std::string &key,1820size_t id) const {1821return detail::get_header_value_u64(headers, key, id, 0);1822}18231824inline uint64_t Response::get_header_value_u64(const std::string &key,1825size_t id) const {1826return detail::get_header_value_u64(headers, key, id, 0);1827}18281829template <typename... Args>1830inline ssize_t Stream::write_format(const char *fmt, const Args &...args) {1831const auto bufsiz = 2048;1832std::array<char, bufsiz> buf{};18331834auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);1835if (sn <= 0) { return sn; }18361837auto n = static_cast<size_t>(sn);18381839if (n >= buf.size() - 1) {1840std::vector<char> glowable_buf(buf.size());18411842while (n >= glowable_buf.size() - 1) {1843glowable_buf.resize(glowable_buf.size() * 2);1844n = static_cast<size_t>(1845snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));1846}1847return write(&glowable_buf[0], n);1848} else {1849return write(buf.data(), n);1850}1851}18521853inline void default_socket_options(socket_t sock) {1854int yes = 1;1855#ifdef _WIN321856setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,1857reinterpret_cast<const char *>(&yes), sizeof(yes));1858setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,1859reinterpret_cast<const char *>(&yes), sizeof(yes));1860#else1861#ifdef SO_REUSEPORT1862setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,1863reinterpret_cast<const void *>(&yes), sizeof(yes));1864#else1865setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,1866reinterpret_cast<const void *>(&yes), sizeof(yes));1867#endif1868#endif1869}18701871inline const char *status_message(int status) {1872switch (status) {1873case StatusCode::Continue_100: return "Continue";1874case StatusCode::SwitchingProtocol_101: return "Switching Protocol";1875case StatusCode::Processing_102: return "Processing";1876case StatusCode::EarlyHints_103: return "Early Hints";1877case StatusCode::OK_200: return "OK";1878case StatusCode::Created_201: return "Created";1879case StatusCode::Accepted_202: return "Accepted";1880case StatusCode::NonAuthoritativeInformation_203:1881return "Non-Authoritative Information";1882case StatusCode::NoContent_204: return "No Content";1883case StatusCode::ResetContent_205: return "Reset Content";1884case StatusCode::PartialContent_206: return "Partial Content";1885case StatusCode::MultiStatus_207: return "Multi-Status";1886case StatusCode::AlreadyReported_208: return "Already Reported";1887case StatusCode::IMUsed_226: return "IM Used";1888case StatusCode::MultipleChoices_300: return "Multiple Choices";1889case StatusCode::MovedPermanently_301: return "Moved Permanently";1890case StatusCode::Found_302: return "Found";1891case StatusCode::SeeOther_303: return "See Other";1892case StatusCode::NotModified_304: return "Not Modified";1893case StatusCode::UseProxy_305: return "Use Proxy";1894case StatusCode::unused_306: return "unused";1895case StatusCode::TemporaryRedirect_307: return "Temporary Redirect";1896case StatusCode::PermanentRedirect_308: return "Permanent Redirect";1897case StatusCode::BadRequest_400: return "Bad Request";1898case StatusCode::Unauthorized_401: return "Unauthorized";1899case StatusCode::PaymentRequired_402: return "Payment Required";1900case StatusCode::Forbidden_403: return "Forbidden";1901case StatusCode::NotFound_404: return "Not Found";1902case StatusCode::MethodNotAllowed_405: return "Method Not Allowed";1903case StatusCode::NotAcceptable_406: return "Not Acceptable";1904case StatusCode::ProxyAuthenticationRequired_407:1905return "Proxy Authentication Required";1906case StatusCode::RequestTimeout_408: return "Request Timeout";1907case StatusCode::Conflict_409: return "Conflict";1908case StatusCode::Gone_410: return "Gone";1909case StatusCode::LengthRequired_411: return "Length Required";1910case StatusCode::PreconditionFailed_412: return "Precondition Failed";1911case StatusCode::PayloadTooLarge_413: return "Payload Too Large";1912case StatusCode::UriTooLong_414: return "URI Too Long";1913case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type";1914case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable";1915case StatusCode::ExpectationFailed_417: return "Expectation Failed";1916case StatusCode::ImATeapot_418: return "I'm a teapot";1917case StatusCode::MisdirectedRequest_421: return "Misdirected Request";1918case StatusCode::UnprocessableContent_422: return "Unprocessable Content";1919case StatusCode::Locked_423: return "Locked";1920case StatusCode::FailedDependency_424: return "Failed Dependency";1921case StatusCode::TooEarly_425: return "Too Early";1922case StatusCode::UpgradeRequired_426: return "Upgrade Required";1923case StatusCode::PreconditionRequired_428: return "Precondition Required";1924case StatusCode::TooManyRequests_429: return "Too Many Requests";1925case StatusCode::RequestHeaderFieldsTooLarge_431:1926return "Request Header Fields Too Large";1927case StatusCode::UnavailableForLegalReasons_451:1928return "Unavailable For Legal Reasons";1929case StatusCode::NotImplemented_501: return "Not Implemented";1930case StatusCode::BadGateway_502: return "Bad Gateway";1931case StatusCode::ServiceUnavailable_503: return "Service Unavailable";1932case StatusCode::GatewayTimeout_504: return "Gateway Timeout";1933case StatusCode::HttpVersionNotSupported_505:1934return "HTTP Version Not Supported";1935case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates";1936case StatusCode::InsufficientStorage_507: return "Insufficient Storage";1937case StatusCode::LoopDetected_508: return "Loop Detected";1938case StatusCode::NotExtended_510: return "Not Extended";1939case StatusCode::NetworkAuthenticationRequired_511:1940return "Network Authentication Required";19411942default:1943case StatusCode::InternalServerError_500: return "Internal Server Error";1944}1945}19461947inline std::string get_bearer_token_auth(const Request &req) {1948if (req.has_header("Authorization")) {1949static std::string BearerHeaderPrefix = "Bearer ";1950return req.get_header_value("Authorization")1951.substr(BearerHeaderPrefix.length());1952}1953return "";1954}19551956template <class Rep, class Period>1957inline Server &1958Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {1959detail::duration_to_sec_and_usec(1960duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });1961return *this;1962}19631964template <class Rep, class Period>1965inline Server &1966Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {1967detail::duration_to_sec_and_usec(1968duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });1969return *this;1970}19711972template <class Rep, class Period>1973inline Server &1974Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {1975detail::duration_to_sec_and_usec(1976duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });1977return *this;1978}19791980inline std::string to_string(const Error error) {1981switch (error) {1982case Error::Success: return "Success (no error)";1983case Error::Connection: return "Could not establish connection";1984case Error::BindIPAddress: return "Failed to bind IP address";1985case Error::Read: return "Failed to read connection";1986case Error::Write: return "Failed to write connection";1987case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";1988case Error::Canceled: return "Connection handling canceled";1989case Error::SSLConnection: return "SSL connection failed";1990case Error::SSLLoadingCerts: return "SSL certificate loading failed";1991case Error::SSLServerVerification: return "SSL server verification failed";1992case Error::UnsupportedMultipartBoundaryChars:1993return "Unsupported HTTP multipart boundary characters";1994case Error::Compression: return "Compression failed";1995case Error::ConnectionTimeout: return "Connection timed out";1996case Error::ProxyConnection: return "Proxy connection failed";1997case Error::Unknown: return "Unknown";1998default: break;1999}20002001return "Invalid";2002}20032004inline std::ostream &operator<<(std::ostream &os, const Error &obj) {2005os << to_string(obj);2006os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';2007return os;2008}20092010inline uint64_t Result::get_request_header_value_u64(const std::string &key,2011size_t id) const {2012return detail::get_header_value_u64(request_headers_, key, id, 0);2013}20142015template <class Rep, class Period>2016inline void ClientImpl::set_connection_timeout(2017const std::chrono::duration<Rep, Period> &duration) {2018detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {2019set_connection_timeout(sec, usec);2020});2021}20222023template <class Rep, class Period>2024inline void ClientImpl::set_read_timeout(2025const std::chrono::duration<Rep, Period> &duration) {2026detail::duration_to_sec_and_usec(2027duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });2028}20292030template <class Rep, class Period>2031inline void ClientImpl::set_write_timeout(2032const std::chrono::duration<Rep, Period> &duration) {2033detail::duration_to_sec_and_usec(2034duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });2035}20362037template <class Rep, class Period>2038inline void Client::set_connection_timeout(2039const std::chrono::duration<Rep, Period> &duration) {2040cli_->set_connection_timeout(duration);2041}20422043template <class Rep, class Period>2044inline void2045Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {2046cli_->set_read_timeout(duration);2047}20482049template <class Rep, class Period>2050inline void2051Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {2052cli_->set_write_timeout(duration);2053}20542055/*2056* Forward declarations and types that will be part of the .h file if split into2057* .h + .cc.2058*/20592060std::string hosted_at(const std::string &hostname);20612062void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);20632064std::string append_query_params(const std::string &path, const Params ¶ms);20652066std::pair<std::string, std::string> make_range_header(Ranges ranges);20672068std::pair<std::string, std::string>2069make_basic_authentication_header(const std::string &username,2070const std::string &password,2071bool is_proxy = false);20722073namespace detail {20742075std::string encode_query_param(const std::string &value);20762077std::string decode_url(const std::string &s, bool convert_plus_to_space);20782079void read_file(const std::string &path, std::string &out);20802081std::string trim_copy(const std::string &s);20822083void split(const char *b, const char *e, char d,2084std::function<void(const char *, const char *)> fn);20852086void split(const char *b, const char *e, char d, size_t m,2087std::function<void(const char *, const char *)> fn);20882089bool process_client_socket(socket_t sock, time_t read_timeout_sec,2090time_t read_timeout_usec, time_t write_timeout_sec,2091time_t write_timeout_usec,2092std::function<bool(Stream &)> callback);20932094socket_t create_client_socket(2095const std::string &host, const std::string &ip, int port,2096int address_family, bool tcp_nodelay, SocketOptions socket_options,2097time_t connection_timeout_sec, time_t connection_timeout_usec,2098time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,2099time_t write_timeout_usec, const std::string &intf, Error &error);21002101const char *get_header_value(const Headers &headers, const std::string &key,2102size_t id = 0, const char *def = nullptr);21032104std::string params_to_query_str(const Params ¶ms);21052106void parse_query_text(const std::string &s, Params ¶ms);21072108bool parse_multipart_boundary(const std::string &content_type,2109std::string &boundary);21102111bool parse_range_header(const std::string &s, Ranges &ranges);21122113int close_socket(socket_t sock);21142115ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);21162117ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);21182119enum class EncodingType { None = 0, Gzip, Brotli };21202121EncodingType encoding_type(const Request &req, const Response &res);21222123class BufferStream : public Stream {2124public:2125BufferStream() = default;2126~BufferStream() override = default;21272128bool is_readable() const override;2129bool is_writable() const override;2130ssize_t read(char *ptr, size_t size) override;2131ssize_t write(const char *ptr, size_t size) override;2132void get_remote_ip_and_port(std::string &ip, int &port) const override;2133void get_local_ip_and_port(std::string &ip, int &port) const override;2134socket_t socket() const override;21352136const std::string &get_buffer() const;21372138private:2139std::string buffer;2140size_t position = 0;2141};21422143class compressor {2144public:2145virtual ~compressor() = default;21462147typedef std::function<bool(const char *data, size_t data_len)> Callback;2148virtual bool compress(const char *data, size_t data_length, bool last,2149Callback callback) = 0;2150};21512152class decompressor {2153public:2154virtual ~decompressor() = default;21552156virtual bool is_valid() const = 0;21572158typedef std::function<bool(const char *data, size_t data_len)> Callback;2159virtual bool decompress(const char *data, size_t data_length,2160Callback callback) = 0;2161};21622163class nocompressor : public compressor {2164public:2165~nocompressor() override = default;21662167bool compress(const char *data, size_t data_length, bool /*last*/,2168Callback callback) override;2169};21702171#ifdef CPPHTTPLIB_ZLIB_SUPPORT2172class gzip_compressor : public compressor {2173public:2174gzip_compressor();2175~gzip_compressor() override;21762177bool compress(const char *data, size_t data_length, bool last,2178Callback callback) override;21792180private:2181bool is_valid_ = false;2182z_stream strm_;2183};21842185class gzip_decompressor : public decompressor {2186public:2187gzip_decompressor();2188~gzip_decompressor() override;21892190bool is_valid() const override;21912192bool decompress(const char *data, size_t data_length,2193Callback callback) override;21942195private:2196bool is_valid_ = false;2197z_stream strm_;2198};2199#endif22002201#ifdef CPPHTTPLIB_BROTLI_SUPPORT2202class brotli_compressor : public compressor {2203public:2204brotli_compressor();2205~brotli_compressor();22062207bool compress(const char *data, size_t data_length, bool last,2208Callback callback) override;22092210private:2211BrotliEncoderState *state_ = nullptr;2212};22132214class brotli_decompressor : public decompressor {2215public:2216brotli_decompressor();2217~brotli_decompressor();22182219bool is_valid() const override;22202221bool decompress(const char *data, size_t data_length,2222Callback callback) override;22232224private:2225BrotliDecoderResult decoder_r;2226BrotliDecoderState *decoder_s = nullptr;2227};2228#endif22292230// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`2231// to store data. The call can set memory on stack for performance.2232class stream_line_reader {2233public:2234stream_line_reader(Stream &strm, char *fixed_buffer,2235size_t fixed_buffer_size);2236const char *ptr() const;2237size_t size() const;2238bool end_with_crlf() const;2239bool getline();22402241private:2242void append(char c);22432244Stream &strm_;2245char *fixed_buffer_;2246const size_t fixed_buffer_size_;2247size_t fixed_buffer_used_size_ = 0;2248std::string glowable_buffer_;2249};22502251class mmap {2252public:2253mmap(const char *path);2254~mmap();22552256bool open(const char *path);2257void close();22582259bool is_open() const;2260size_t size() const;2261const char *data() const;22622263private:2264#if defined(_WIN32)2265HANDLE hFile_;2266HANDLE hMapping_;2267#else2268int fd_;2269#endif2270size_t size_;2271void *addr_;2272};22732274} // namespace detail22752276// ----------------------------------------------------------------------------22772278/*2279* Implementation that will be part of the .cc file if split into .h + .cc.2280*/22812282namespace detail {22832284inline bool is_hex(char c, int &v) {2285if (0x20 <= c && isdigit(c)) {2286v = c - '0';2287return true;2288} else if ('A' <= c && c <= 'F') {2289v = c - 'A' + 10;2290return true;2291} else if ('a' <= c && c <= 'f') {2292v = c - 'a' + 10;2293return true;2294}2295return false;2296}22972298inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,2299int &val) {2300if (i >= s.size()) { return false; }23012302val = 0;2303for (; cnt; i++, cnt--) {2304if (!s[i]) { return false; }2305auto v = 0;2306if (is_hex(s[i], v)) {2307val = val * 16 + v;2308} else {2309return false;2310}2311}2312return true;2313}23142315inline std::string from_i_to_hex(size_t n) {2316static const auto charset = "0123456789abcdef";2317std::string ret;2318do {2319ret = charset[n & 15] + ret;2320n >>= 4;2321} while (n > 0);2322return ret;2323}23242325inline size_t to_utf8(int code, char *buff) {2326if (code < 0x0080) {2327buff[0] = static_cast<char>(code & 0x7F);2328return 1;2329} else if (code < 0x0800) {2330buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));2331buff[1] = static_cast<char>(0x80 | (code & 0x3F));2332return 2;2333} else if (code < 0xD800) {2334buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));2335buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));2336buff[2] = static_cast<char>(0x80 | (code & 0x3F));2337return 3;2338} else if (code < 0xE000) { // D800 - DFFF is invalid...2339return 0;2340} else if (code < 0x10000) {2341buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));2342buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));2343buff[2] = static_cast<char>(0x80 | (code & 0x3F));2344return 3;2345} else if (code < 0x110000) {2346buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));2347buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));2348buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));2349buff[3] = static_cast<char>(0x80 | (code & 0x3F));2350return 4;2351}23522353// NOTREACHED2354return 0;2355}23562357// NOTE: This code came up with the following stackoverflow post:2358// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c2359inline std::string base64_encode(const std::string &in) {2360static const auto lookup =2361"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";23622363std::string out;2364out.reserve(in.size());23652366auto val = 0;2367auto valb = -6;23682369for (auto c : in) {2370val = (val << 8) + static_cast<uint8_t>(c);2371valb += 8;2372while (valb >= 0) {2373out.push_back(lookup[(val >> valb) & 0x3F]);2374valb -= 6;2375}2376}23772378if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }23792380while (out.size() % 4) {2381out.push_back('=');2382}23832384return out;2385}23862387inline bool is_file(const std::string &path) {2388#ifdef _WIN322389return _access_s(path.c_str(), 0) == 0;2390#else2391struct stat st;2392return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);2393#endif2394}23952396inline bool is_dir(const std::string &path) {2397struct stat st;2398return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);2399}24002401inline bool is_valid_path(const std::string &path) {2402size_t level = 0;2403size_t i = 0;24042405// Skip slash2406while (i < path.size() && path[i] == '/') {2407i++;2408}24092410while (i < path.size()) {2411// Read component2412auto beg = i;2413while (i < path.size() && path[i] != '/') {2414i++;2415}24162417auto len = i - beg;2418assert(len > 0);24192420if (!path.compare(beg, len, ".")) {2421;2422} else if (!path.compare(beg, len, "..")) {2423if (level == 0) { return false; }2424level--;2425} else {2426level++;2427}24282429// Skip slash2430while (i < path.size() && path[i] == '/') {2431i++;2432}2433}24342435return true;2436}24372438inline std::string encode_query_param(const std::string &value) {2439std::ostringstream escaped;2440escaped.fill('0');2441escaped << std::hex;24422443for (auto c : value) {2444if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||2445c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||2446c == ')') {2447escaped << c;2448} else {2449escaped << std::uppercase;2450escaped << '%' << std::setw(2)2451<< static_cast<int>(static_cast<unsigned char>(c));2452escaped << std::nouppercase;2453}2454}24552456return escaped.str();2457}24582459inline std::string encode_url(const std::string &s) {2460std::string result;2461result.reserve(s.size());24622463for (size_t i = 0; s[i]; i++) {2464switch (s[i]) {2465case ' ': result += "%20"; break;2466case '+': result += "%2B"; break;2467case '\r': result += "%0D"; break;2468case '\n': result += "%0A"; break;2469case '\'': result += "%27"; break;2470case ',': result += "%2C"; break;2471// case ':': result += "%3A"; break; // ok? probably...2472case ';': result += "%3B"; break;2473default:2474auto c = static_cast<uint8_t>(s[i]);2475if (c >= 0x80) {2476result += '%';2477char hex[4];2478auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);2479assert(len == 2);2480result.append(hex, static_cast<size_t>(len));2481} else {2482result += s[i];2483}2484break;2485}2486}24872488return result;2489}24902491inline std::string decode_url(const std::string &s,2492bool convert_plus_to_space) {2493std::string result;24942495for (size_t i = 0; i < s.size(); i++) {2496if (s[i] == '%' && i + 1 < s.size()) {2497if (s[i + 1] == 'u') {2498auto val = 0;2499if (from_hex_to_i(s, i + 2, 4, val)) {2500// 4 digits Unicode codes2501char buff[4];2502size_t len = to_utf8(val, buff);2503if (len > 0) { result.append(buff, len); }2504i += 5; // 'u0000'2505} else {2506result += s[i];2507}2508} else {2509auto val = 0;2510if (from_hex_to_i(s, i + 1, 2, val)) {2511// 2 digits hex codes2512result += static_cast<char>(val);2513i += 2; // '00'2514} else {2515result += s[i];2516}2517}2518} else if (convert_plus_to_space && s[i] == '+') {2519result += ' ';2520} else {2521result += s[i];2522}2523}25242525return result;2526}25272528inline void read_file(const std::string &path, std::string &out) {2529std::ifstream fs(path, std::ios_base::binary);2530fs.seekg(0, std::ios_base::end);2531auto size = fs.tellg();2532fs.seekg(0);2533out.resize(static_cast<size_t>(size));2534fs.read(&out[0], static_cast<std::streamsize>(size));2535}25362537inline std::string file_extension(const std::string &path) {2538std::smatch m;2539static auto re = std::regex("\\.([a-zA-Z0-9]+)$");2540if (std::regex_search(path, m, re)) { return m[1].str(); }2541return std::string();2542}25432544inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }25452546inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,2547size_t right) {2548while (b + left < e && is_space_or_tab(b[left])) {2549left++;2550}2551while (right > 0 && is_space_or_tab(b[right - 1])) {2552right--;2553}2554return std::make_pair(left, right);2555}25562557inline std::string trim_copy(const std::string &s) {2558auto r = trim(s.data(), s.data() + s.size(), 0, s.size());2559return s.substr(r.first, r.second - r.first);2560}25612562inline std::string trim_double_quotes_copy(const std::string &s) {2563if (s.length() >= 2 && s.front() == '"' && s.back() == '"') {2564return s.substr(1, s.size() - 2);2565}2566return s;2567}25682569inline void split(const char *b, const char *e, char d,2570std::function<void(const char *, const char *)> fn) {2571return split(b, e, d, (std::numeric_limits<size_t>::max)(), fn);2572}25732574inline void split(const char *b, const char *e, char d, size_t m,2575std::function<void(const char *, const char *)> fn) {2576size_t i = 0;2577size_t beg = 0;2578size_t count = 1;25792580while (e ? (b + i < e) : (b[i] != '\0')) {2581if (b[i] == d && count < m) {2582auto r = trim(b, e, beg, i);2583if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }2584beg = i + 1;2585count++;2586}2587i++;2588}25892590if (i) {2591auto r = trim(b, e, beg, i);2592if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }2593}2594}25952596inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,2597size_t fixed_buffer_size)2598: strm_(strm), fixed_buffer_(fixed_buffer),2599fixed_buffer_size_(fixed_buffer_size) {}26002601inline const char *stream_line_reader::ptr() const {2602if (glowable_buffer_.empty()) {2603return fixed_buffer_;2604} else {2605return glowable_buffer_.data();2606}2607}26082609inline size_t stream_line_reader::size() const {2610if (glowable_buffer_.empty()) {2611return fixed_buffer_used_size_;2612} else {2613return glowable_buffer_.size();2614}2615}26162617inline bool stream_line_reader::end_with_crlf() const {2618auto end = ptr() + size();2619return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';2620}26212622inline bool stream_line_reader::getline() {2623fixed_buffer_used_size_ = 0;2624glowable_buffer_.clear();26252626for (size_t i = 0;; i++) {2627char byte;2628auto n = strm_.read(&byte, 1);26292630if (n < 0) {2631return false;2632} else if (n == 0) {2633if (i == 0) {2634return false;2635} else {2636break;2637}2638}26392640append(byte);26412642if (byte == '\n') { break; }2643}26442645return true;2646}26472648inline void stream_line_reader::append(char c) {2649if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {2650fixed_buffer_[fixed_buffer_used_size_++] = c;2651fixed_buffer_[fixed_buffer_used_size_] = '\0';2652} else {2653if (glowable_buffer_.empty()) {2654assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');2655glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);2656}2657glowable_buffer_ += c;2658}2659}26602661inline mmap::mmap(const char *path)2662#if defined(_WIN32)2663: hFile_(NULL), hMapping_(NULL)2664#else2665: fd_(-1)2666#endif2667,2668size_(0), addr_(nullptr) {2669open(path);2670}26712672inline mmap::~mmap() { close(); }26732674inline bool mmap::open(const char *path) {2675close();26762677#if defined(_WIN32)2678hFile_ = ::CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,2679OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);26802681if (hFile_ == INVALID_HANDLE_VALUE) { return false; }26822683size_ = ::GetFileSize(hFile_, NULL);26842685hMapping_ = ::CreateFileMapping(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);26862687if (hMapping_ == NULL) {2688close();2689return false;2690}26912692addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);2693#else2694fd_ = ::open(path, O_RDONLY);2695if (fd_ == -1) { return false; }26962697struct stat sb;2698if (fstat(fd_, &sb) == -1) {2699close();2700return false;2701}2702size_ = static_cast<size_t>(sb.st_size);27032704addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);2705#endif27062707if (addr_ == nullptr) {2708close();2709return false;2710}27112712return true;2713}27142715inline bool mmap::is_open() const { return addr_ != nullptr; }27162717inline size_t mmap::size() const { return size_; }27182719inline const char *mmap::data() const {2720return static_cast<const char *>(addr_);2721}27222723inline void mmap::close() {2724#if defined(_WIN32)2725if (addr_) {2726::UnmapViewOfFile(addr_);2727addr_ = nullptr;2728}27292730if (hMapping_) {2731::CloseHandle(hMapping_);2732hMapping_ = NULL;2733}27342735if (hFile_ != INVALID_HANDLE_VALUE) {2736::CloseHandle(hFile_);2737hFile_ = INVALID_HANDLE_VALUE;2738}2739#else2740if (addr_ != nullptr) {2741munmap(addr_, size_);2742addr_ = nullptr;2743}27442745if (fd_ != -1) {2746::close(fd_);2747fd_ = -1;2748}2749#endif2750size_ = 0;2751}2752inline int close_socket(socket_t sock) {2753#ifdef _WIN322754return closesocket(sock);2755#else2756return close(sock);2757#endif2758}27592760template <typename T> inline ssize_t handle_EINTR(T fn) {2761ssize_t res = 0;2762while (true) {2763res = fn();2764if (res < 0 && errno == EINTR) { continue; }2765break;2766}2767return res;2768}27692770inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {2771return handle_EINTR([&]() {2772return recv(sock,2773#ifdef _WIN322774static_cast<char *>(ptr), static_cast<int>(size),2775#else2776ptr, size,2777#endif2778flags);2779});2780}27812782inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,2783int flags) {2784return handle_EINTR([&]() {2785return send(sock,2786#ifdef _WIN322787static_cast<const char *>(ptr), static_cast<int>(size),2788#else2789ptr, size,2790#endif2791flags);2792});2793}27942795inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {2796#ifdef CPPHTTPLIB_USE_POLL2797struct pollfd pfd_read;2798pfd_read.fd = sock;2799pfd_read.events = POLLIN;28002801auto timeout = static_cast<int>(sec * 1000 + usec / 1000);28022803return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });2804#else2805#ifndef _WIN322806if (sock >= FD_SETSIZE) { return -1; }2807#endif28082809fd_set fds;2810FD_ZERO(&fds);2811FD_SET(sock, &fds);28122813timeval tv;2814tv.tv_sec = static_cast<long>(sec);2815tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);28162817return handle_EINTR([&]() {2818return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);2819});2820#endif2821}28222823inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {2824#ifdef CPPHTTPLIB_USE_POLL2825struct pollfd pfd_read;2826pfd_read.fd = sock;2827pfd_read.events = POLLOUT;28282829auto timeout = static_cast<int>(sec * 1000 + usec / 1000);28302831return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });2832#else2833#ifndef _WIN322834if (sock >= FD_SETSIZE) { return -1; }2835#endif28362837fd_set fds;2838FD_ZERO(&fds);2839FD_SET(sock, &fds);28402841timeval tv;2842tv.tv_sec = static_cast<long>(sec);2843tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);28442845return handle_EINTR([&]() {2846return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);2847});2848#endif2849}28502851inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,2852time_t usec) {2853#ifdef CPPHTTPLIB_USE_POLL2854struct pollfd pfd_read;2855pfd_read.fd = sock;2856pfd_read.events = POLLIN | POLLOUT;28572858auto timeout = static_cast<int>(sec * 1000 + usec / 1000);28592860auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });28612862if (poll_res == 0) { return Error::ConnectionTimeout; }28632864if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {2865auto error = 0;2866socklen_t len = sizeof(error);2867auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,2868reinterpret_cast<char *>(&error), &len);2869auto successful = res >= 0 && !error;2870return successful ? Error::Success : Error::Connection;2871}28722873return Error::Connection;2874#else2875#ifndef _WIN322876if (sock >= FD_SETSIZE) { return Error::Connection; }2877#endif28782879fd_set fdsr;2880FD_ZERO(&fdsr);2881FD_SET(sock, &fdsr);28822883auto fdsw = fdsr;2884auto fdse = fdsr;28852886timeval tv;2887tv.tv_sec = static_cast<long>(sec);2888tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);28892890auto ret = handle_EINTR([&]() {2891return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);2892});28932894if (ret == 0) { return Error::ConnectionTimeout; }28952896if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {2897auto error = 0;2898socklen_t len = sizeof(error);2899auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,2900reinterpret_cast<char *>(&error), &len);2901auto successful = res >= 0 && !error;2902return successful ? Error::Success : Error::Connection;2903}2904return Error::Connection;2905#endif2906}29072908inline bool is_socket_alive(socket_t sock) {2909const auto val = detail::select_read(sock, 0, 0);2910if (val == 0) {2911return true;2912} else if (val < 0 && errno == EBADF) {2913return false;2914}2915char buf[1];2916return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;2917}29182919class SocketStream : public Stream {2920public:2921SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,2922time_t write_timeout_sec, time_t write_timeout_usec);2923~SocketStream() override;29242925bool is_readable() const override;2926bool is_writable() const override;2927ssize_t read(char *ptr, size_t size) override;2928ssize_t write(const char *ptr, size_t size) override;2929void get_remote_ip_and_port(std::string &ip, int &port) const override;2930void get_local_ip_and_port(std::string &ip, int &port) const override;2931socket_t socket() const override;29322933private:2934socket_t sock_;2935time_t read_timeout_sec_;2936time_t read_timeout_usec_;2937time_t write_timeout_sec_;2938time_t write_timeout_usec_;29392940std::vector<char> read_buff_;2941size_t read_buff_off_ = 0;2942size_t read_buff_content_size_ = 0;29432944static const size_t read_buff_size_ = 1024l * 4;2945};29462947#ifdef CPPHTTPLIB_OPENSSL_SUPPORT2948class SSLSocketStream : public Stream {2949public:2950SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,2951time_t read_timeout_usec, time_t write_timeout_sec,2952time_t write_timeout_usec);2953~SSLSocketStream() override;29542955bool is_readable() const override;2956bool is_writable() const override;2957ssize_t read(char *ptr, size_t size) override;2958ssize_t write(const char *ptr, size_t size) override;2959void get_remote_ip_and_port(std::string &ip, int &port) const override;2960void get_local_ip_and_port(std::string &ip, int &port) const override;2961socket_t socket() const override;29622963private:2964socket_t sock_;2965SSL *ssl_;2966time_t read_timeout_sec_;2967time_t read_timeout_usec_;2968time_t write_timeout_sec_;2969time_t write_timeout_usec_;2970};2971#endif29722973inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) {2974using namespace std::chrono;2975auto start = steady_clock::now();2976while (true) {2977auto val = select_read(sock, 0, 10000);2978if (val < 0) {2979return false;2980} else if (val == 0) {2981auto current = steady_clock::now();2982auto duration = duration_cast<milliseconds>(current - start);2983auto timeout = keep_alive_timeout_sec * 1000;2984if (duration.count() > timeout) { return false; }2985std::this_thread::sleep_for(std::chrono::milliseconds(1));2986} else {2987return true;2988}2989}2990}29912992template <typename T>2993inline bool2994process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,2995size_t keep_alive_max_count,2996time_t keep_alive_timeout_sec, T callback) {2997assert(keep_alive_max_count > 0);2998auto ret = false;2999auto count = keep_alive_max_count;3000while (svr_sock != INVALID_SOCKET && count > 0 &&3001keep_alive(sock, keep_alive_timeout_sec)) {3002auto close_connection = count == 1;3003auto connection_closed = false;3004ret = callback(close_connection, connection_closed);3005if (!ret || connection_closed) { break; }3006count--;3007}3008return ret;3009}30103011template <typename T>3012inline bool3013process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,3014size_t keep_alive_max_count,3015time_t keep_alive_timeout_sec, time_t read_timeout_sec,3016time_t read_timeout_usec, time_t write_timeout_sec,3017time_t write_timeout_usec, T callback) {3018return process_server_socket_core(3019svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,3020[&](bool close_connection, bool &connection_closed) {3021SocketStream strm(sock, read_timeout_sec, read_timeout_usec,3022write_timeout_sec, write_timeout_usec);3023return callback(strm, close_connection, connection_closed);3024});3025}30263027inline bool process_client_socket(socket_t sock, time_t read_timeout_sec,3028time_t read_timeout_usec,3029time_t write_timeout_sec,3030time_t write_timeout_usec,3031std::function<bool(Stream &)> callback) {3032SocketStream strm(sock, read_timeout_sec, read_timeout_usec,3033write_timeout_sec, write_timeout_usec);3034return callback(strm);3035}30363037inline int shutdown_socket(socket_t sock) {3038#ifdef _WIN323039return shutdown(sock, SD_BOTH);3040#else3041return shutdown(sock, SHUT_RDWR);3042#endif3043}30443045template <typename BindOrConnect>3046socket_t create_socket(const std::string &host, const std::string &ip, int port,3047int address_family, int socket_flags, bool tcp_nodelay,3048SocketOptions socket_options,3049BindOrConnect bind_or_connect) {3050// Get address info3051const char *node = nullptr;3052struct addrinfo hints;3053struct addrinfo *result;30543055memset(&hints, 0, sizeof(struct addrinfo));3056hints.ai_socktype = SOCK_STREAM;3057hints.ai_protocol = 0;30583059if (!ip.empty()) {3060node = ip.c_str();3061// Ask getaddrinfo to convert IP in c-string to address3062hints.ai_family = AF_UNSPEC;3063hints.ai_flags = AI_NUMERICHOST;3064} else {3065if (!host.empty()) { node = host.c_str(); }3066hints.ai_family = address_family;3067hints.ai_flags = socket_flags;3068}30693070#ifndef _WIN323071if (hints.ai_family == AF_UNIX) {3072const auto addrlen = host.length();3073if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }30743075auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);3076if (sock != INVALID_SOCKET) {3077sockaddr_un addr{};3078addr.sun_family = AF_UNIX;3079std::copy(host.begin(), host.end(), addr.sun_path);30803081hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);3082hints.ai_addrlen = static_cast<socklen_t>(3083sizeof(addr) - sizeof(addr.sun_path) + addrlen);30843085fcntl(sock, F_SETFD, FD_CLOEXEC);3086if (socket_options) { socket_options(sock); }30873088if (!bind_or_connect(sock, hints)) {3089close_socket(sock);3090sock = INVALID_SOCKET;3091}3092}3093return sock;3094}3095#endif30963097auto service = std::to_string(port);30983099if (getaddrinfo(node, service.c_str(), &hints, &result)) {3100#if defined __linux__ && !defined __ANDROID__3101res_init();3102#endif3103return INVALID_SOCKET;3104}31053106for (auto rp = result; rp; rp = rp->ai_next) {3107// Create a socket3108#ifdef _WIN323109auto sock =3110WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,3111WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);3112/**3113* Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP13114* and above the socket creation fails on older Windows Systems.3115*3116* Let's try to create a socket the old way in this case.3117*3118* Reference:3119* https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa3120*3121* WSA_FLAG_NO_HANDLE_INHERIT:3122* This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with3123* SP1, and later3124*3125*/3126if (sock == INVALID_SOCKET) {3127sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);3128}3129#else3130auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);3131#endif3132if (sock == INVALID_SOCKET) { continue; }31333134#ifndef _WIN323135if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {3136close_socket(sock);3137continue;3138}3139#endif31403141if (tcp_nodelay) {3142auto yes = 1;3143#ifdef _WIN323144setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,3145reinterpret_cast<const char *>(&yes), sizeof(yes));3146#else3147setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,3148reinterpret_cast<const void *>(&yes), sizeof(yes));3149#endif3150}31513152if (socket_options) { socket_options(sock); }31533154if (rp->ai_family == AF_INET6) {3155auto no = 0;3156#ifdef _WIN323157setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,3158reinterpret_cast<const char *>(&no), sizeof(no));3159#else3160setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,3161reinterpret_cast<const void *>(&no), sizeof(no));3162#endif3163}31643165// bind or connect3166if (bind_or_connect(sock, *rp)) {3167freeaddrinfo(result);3168return sock;3169}31703171close_socket(sock);3172}31733174freeaddrinfo(result);3175return INVALID_SOCKET;3176}31773178inline void set_nonblocking(socket_t sock, bool nonblocking) {3179#ifdef _WIN323180auto flags = nonblocking ? 1UL : 0UL;3181ioctlsocket(sock, FIONBIO, &flags);3182#else3183auto flags = fcntl(sock, F_GETFL, 0);3184fcntl(sock, F_SETFL,3185nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));3186#endif3187}31883189inline bool is_connection_error() {3190#ifdef _WIN323191return WSAGetLastError() != WSAEWOULDBLOCK;3192#else3193return errno != EINPROGRESS;3194#endif3195}31963197inline bool bind_ip_address(socket_t sock, const std::string &host) {3198struct addrinfo hints;3199struct addrinfo *result;32003201memset(&hints, 0, sizeof(struct addrinfo));3202hints.ai_family = AF_UNSPEC;3203hints.ai_socktype = SOCK_STREAM;3204hints.ai_protocol = 0;32053206if (getaddrinfo(host.c_str(), "0", &hints, &result)) { return false; }32073208auto ret = false;3209for (auto rp = result; rp; rp = rp->ai_next) {3210const auto &ai = *rp;3211if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {3212ret = true;3213break;3214}3215}32163217freeaddrinfo(result);3218return ret;3219}32203221#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__3222#define USE_IF2IP3223#endif32243225#ifdef USE_IF2IP3226inline std::string if2ip(int address_family, const std::string &ifn) {3227struct ifaddrs *ifap;3228getifaddrs(&ifap);3229std::string addr_candidate;3230for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {3231if (ifa->ifa_addr && ifn == ifa->ifa_name &&3232(AF_UNSPEC == address_family ||3233ifa->ifa_addr->sa_family == address_family)) {3234if (ifa->ifa_addr->sa_family == AF_INET) {3235auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);3236char buf[INET_ADDRSTRLEN];3237if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {3238freeifaddrs(ifap);3239return std::string(buf, INET_ADDRSTRLEN);3240}3241} else if (ifa->ifa_addr->sa_family == AF_INET6) {3242auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);3243if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {3244char buf[INET6_ADDRSTRLEN] = {};3245if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {3246// equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL3247auto s6_addr_head = sa->sin6_addr.s6_addr[0];3248if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {3249addr_candidate = std::string(buf, INET6_ADDRSTRLEN);3250} else {3251freeifaddrs(ifap);3252return std::string(buf, INET6_ADDRSTRLEN);3253}3254}3255}3256}3257}3258}3259freeifaddrs(ifap);3260return addr_candidate;3261}3262#endif32633264inline socket_t create_client_socket(3265const std::string &host, const std::string &ip, int port,3266int address_family, bool tcp_nodelay, SocketOptions socket_options,3267time_t connection_timeout_sec, time_t connection_timeout_usec,3268time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,3269time_t write_timeout_usec, const std::string &intf, Error &error) {3270auto sock = create_socket(3271host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options),3272[&](socket_t sock2, struct addrinfo &ai) -> bool {3273if (!intf.empty()) {3274#ifdef USE_IF2IP3275auto ip_from_if = if2ip(address_family, intf);3276if (ip_from_if.empty()) { ip_from_if = intf; }3277if (!bind_ip_address(sock2, ip_from_if)) {3278error = Error::BindIPAddress;3279return false;3280}3281#endif3282}32833284set_nonblocking(sock2, true);32853286auto ret =3287::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));32883289if (ret < 0) {3290if (is_connection_error()) {3291error = Error::Connection;3292return false;3293}3294error = wait_until_socket_is_ready(sock2, connection_timeout_sec,3295connection_timeout_usec);3296if (error != Error::Success) { return false; }3297}32983299set_nonblocking(sock2, false);33003301{3302#ifdef _WIN323303auto timeout = static_cast<uint32_t>(read_timeout_sec * 1000 +3304read_timeout_usec / 1000);3305setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,3306reinterpret_cast<const char *>(&timeout), sizeof(timeout));3307#else3308timeval tv;3309tv.tv_sec = static_cast<long>(read_timeout_sec);3310tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);3311setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,3312reinterpret_cast<const void *>(&tv), sizeof(tv));3313#endif3314}3315{33163317#ifdef _WIN323318auto timeout = static_cast<uint32_t>(write_timeout_sec * 1000 +3319write_timeout_usec / 1000);3320setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,3321reinterpret_cast<const char *>(&timeout), sizeof(timeout));3322#else3323timeval tv;3324tv.tv_sec = static_cast<long>(write_timeout_sec);3325tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);3326setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,3327reinterpret_cast<const void *>(&tv), sizeof(tv));3328#endif3329}33303331error = Error::Success;3332return true;3333});33343335if (sock != INVALID_SOCKET) {3336error = Error::Success;3337} else {3338if (error == Error::Success) { error = Error::Connection; }3339}33403341return sock;3342}33433344inline bool get_ip_and_port(const struct sockaddr_storage &addr,3345socklen_t addr_len, std::string &ip, int &port) {3346if (addr.ss_family == AF_INET) {3347port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);3348} else if (addr.ss_family == AF_INET6) {3349port =3350ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);3351} else {3352return false;3353}33543355std::array<char, NI_MAXHOST> ipstr{};3356if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,3357ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,33580, NI_NUMERICHOST)) {3359return false;3360}33613362ip = ipstr.data();3363return true;3364}33653366inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {3367struct sockaddr_storage addr;3368socklen_t addr_len = sizeof(addr);3369if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),3370&addr_len)) {3371get_ip_and_port(addr, addr_len, ip, port);3372}3373}33743375inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {3376struct sockaddr_storage addr;3377socklen_t addr_len = sizeof(addr);33783379if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),3380&addr_len)) {3381#ifndef _WIN323382if (addr.ss_family == AF_UNIX) {3383#if defined(__linux__)3384struct ucred ucred;3385socklen_t len = sizeof(ucred);3386if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {3387port = ucred.pid;3388}3389#elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__3390pid_t pid;3391socklen_t len = sizeof(pid);3392if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {3393port = pid;3394}3395#endif3396return;3397}3398#endif3399get_ip_and_port(addr, addr_len, ip, port);3400}3401}34023403inline constexpr unsigned int str2tag_core(const char *s, size_t l,3404unsigned int h) {3405return (l == 0)3406? h3407: str2tag_core(3408s + 1, l - 1,3409// Unsets the 6 high bits of h, therefore no overflow happens3410(((std::numeric_limits<unsigned int>::max)() >> 6) &3411h * 33) ^3412static_cast<unsigned char>(*s));3413}34143415inline unsigned int str2tag(const std::string &s) {3416return str2tag_core(s.data(), s.size(), 0);3417}34183419namespace udl {34203421inline constexpr unsigned int operator"" _t(const char *s, size_t l) {3422return str2tag_core(s, l, 0);3423}34243425} // namespace udl34263427inline std::string3428find_content_type(const std::string &path,3429const std::map<std::string, std::string> &user_data,3430const std::string &default_content_type) {3431auto ext = file_extension(path);34323433auto it = user_data.find(ext);3434if (it != user_data.end()) { return it->second; }34353436using udl::operator""_t;34373438switch (str2tag(ext)) {3439default: return default_content_type;34403441case "css"_t: return "text/css";3442case "csv"_t: return "text/csv";3443case "htm"_t:3444case "html"_t: return "text/html";3445case "js"_t:3446case "mjs"_t: return "text/javascript";3447case "txt"_t: return "text/plain";3448case "vtt"_t: return "text/vtt";34493450case "apng"_t: return "image/apng";3451case "avif"_t: return "image/avif";3452case "bmp"_t: return "image/bmp";3453case "gif"_t: return "image/gif";3454case "png"_t: return "image/png";3455case "svg"_t: return "image/svg+xml";3456case "webp"_t: return "image/webp";3457case "ico"_t: return "image/x-icon";3458case "tif"_t: return "image/tiff";3459case "tiff"_t: return "image/tiff";3460case "jpg"_t:3461case "jpeg"_t: return "image/jpeg";34623463case "mp4"_t: return "video/mp4";3464case "mpeg"_t: return "video/mpeg";3465case "webm"_t: return "video/webm";34663467case "mp3"_t: return "audio/mp3";3468case "mpga"_t: return "audio/mpeg";3469case "weba"_t: return "audio/webm";3470case "wav"_t: return "audio/wave";34713472case "otf"_t: return "font/otf";3473case "ttf"_t: return "font/ttf";3474case "woff"_t: return "font/woff";3475case "woff2"_t: return "font/woff2";34763477case "7z"_t: return "application/x-7z-compressed";3478case "atom"_t: return "application/atom+xml";3479case "pdf"_t: return "application/pdf";3480case "json"_t: return "application/json";3481case "rss"_t: return "application/rss+xml";3482case "tar"_t: return "application/x-tar";3483case "xht"_t:3484case "xhtml"_t: return "application/xhtml+xml";3485case "xslt"_t: return "application/xslt+xml";3486case "xml"_t: return "application/xml";3487case "gz"_t: return "application/gzip";3488case "zip"_t: return "application/zip";3489case "wasm"_t: return "application/wasm";3490}3491}34923493inline bool can_compress_content_type(const std::string &content_type) {3494using udl::operator""_t;34953496auto tag = str2tag(content_type);34973498switch (tag) {3499case "image/svg+xml"_t:3500case "application/javascript"_t:3501case "application/json"_t:3502case "application/xml"_t:3503case "application/protobuf"_t:3504case "application/xhtml+xml"_t: return true;35053506default:3507return !content_type.rfind("text/", 0) && tag != "text/event-stream"_t;3508}3509}35103511inline EncodingType encoding_type(const Request &req, const Response &res) {3512auto ret =3513detail::can_compress_content_type(res.get_header_value("Content-Type"));3514if (!ret) { return EncodingType::None; }35153516const auto &s = req.get_header_value("Accept-Encoding");3517(void)(s);35183519#ifdef CPPHTTPLIB_BROTLI_SUPPORT3520// TODO: 'Accept-Encoding' has br, not br;q=03521ret = s.find("br") != std::string::npos;3522if (ret) { return EncodingType::Brotli; }3523#endif35243525#ifdef CPPHTTPLIB_ZLIB_SUPPORT3526// TODO: 'Accept-Encoding' has gzip, not gzip;q=03527ret = s.find("gzip") != std::string::npos;3528if (ret) { return EncodingType::Gzip; }3529#endif35303531return EncodingType::None;3532}35333534inline bool nocompressor::compress(const char *data, size_t data_length,3535bool /*last*/, Callback callback) {3536if (!data_length) { return true; }3537return callback(data, data_length);3538}35393540#ifdef CPPHTTPLIB_ZLIB_SUPPORT3541inline gzip_compressor::gzip_compressor() {3542std::memset(&strm_, 0, sizeof(strm_));3543strm_.zalloc = Z_NULL;3544strm_.zfree = Z_NULL;3545strm_.opaque = Z_NULL;35463547is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,3548Z_DEFAULT_STRATEGY) == Z_OK;3549}35503551inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }35523553inline bool gzip_compressor::compress(const char *data, size_t data_length,3554bool last, Callback callback) {3555assert(is_valid_);35563557do {3558constexpr size_t max_avail_in =3559(std::numeric_limits<decltype(strm_.avail_in)>::max)();35603561strm_.avail_in = static_cast<decltype(strm_.avail_in)>(3562(std::min)(data_length, max_avail_in));3563strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));35643565data_length -= strm_.avail_in;3566data += strm_.avail_in;35673568auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;3569auto ret = Z_OK;35703571std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};3572do {3573strm_.avail_out = static_cast<uInt>(buff.size());3574strm_.next_out = reinterpret_cast<Bytef *>(buff.data());35753576ret = deflate(&strm_, flush);3577if (ret == Z_STREAM_ERROR) { return false; }35783579if (!callback(buff.data(), buff.size() - strm_.avail_out)) {3580return false;3581}3582} while (strm_.avail_out == 0);35833584assert((flush == Z_FINISH && ret == Z_STREAM_END) ||3585(flush == Z_NO_FLUSH && ret == Z_OK));3586assert(strm_.avail_in == 0);3587} while (data_length > 0);35883589return true;3590}35913592inline gzip_decompressor::gzip_decompressor() {3593std::memset(&strm_, 0, sizeof(strm_));3594strm_.zalloc = Z_NULL;3595strm_.zfree = Z_NULL;3596strm_.opaque = Z_NULL;35973598// 15 is the value of wbits, which should be at the maximum possible value3599// to ensure that any gzip stream can be decoded. The offset of 32 specifies3600// that the stream type should be automatically detected either gzip or3601// deflate.3602is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;3603}36043605inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }36063607inline bool gzip_decompressor::is_valid() const { return is_valid_; }36083609inline bool gzip_decompressor::decompress(const char *data, size_t data_length,3610Callback callback) {3611assert(is_valid_);36123613auto ret = Z_OK;36143615do {3616constexpr size_t max_avail_in =3617(std::numeric_limits<decltype(strm_.avail_in)>::max)();36183619strm_.avail_in = static_cast<decltype(strm_.avail_in)>(3620(std::min)(data_length, max_avail_in));3621strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));36223623data_length -= strm_.avail_in;3624data += strm_.avail_in;36253626std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};3627while (strm_.avail_in > 0 && ret == Z_OK) {3628strm_.avail_out = static_cast<uInt>(buff.size());3629strm_.next_out = reinterpret_cast<Bytef *>(buff.data());36303631ret = inflate(&strm_, Z_NO_FLUSH);36323633assert(ret != Z_STREAM_ERROR);3634switch (ret) {3635case Z_NEED_DICT:3636case Z_DATA_ERROR:3637case Z_MEM_ERROR: inflateEnd(&strm_); return false;3638}36393640if (!callback(buff.data(), buff.size() - strm_.avail_out)) {3641return false;3642}3643}36443645if (ret != Z_OK && ret != Z_STREAM_END) { return false; }36463647} while (data_length > 0);36483649return true;3650}3651#endif36523653#ifdef CPPHTTPLIB_BROTLI_SUPPORT3654inline brotli_compressor::brotli_compressor() {3655state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);3656}36573658inline brotli_compressor::~brotli_compressor() {3659BrotliEncoderDestroyInstance(state_);3660}36613662inline bool brotli_compressor::compress(const char *data, size_t data_length,3663bool last, Callback callback) {3664std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};36653666auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;3667auto available_in = data_length;3668auto next_in = reinterpret_cast<const uint8_t *>(data);36693670for (;;) {3671if (last) {3672if (BrotliEncoderIsFinished(state_)) { break; }3673} else {3674if (!available_in) { break; }3675}36763677auto available_out = buff.size();3678auto next_out = buff.data();36793680if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,3681&available_out, &next_out, nullptr)) {3682return false;3683}36843685auto output_bytes = buff.size() - available_out;3686if (output_bytes) {3687callback(reinterpret_cast<const char *>(buff.data()), output_bytes);3688}3689}36903691return true;3692}36933694inline brotli_decompressor::brotli_decompressor() {3695decoder_s = BrotliDecoderCreateInstance(0, 0, 0);3696decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT3697: BROTLI_DECODER_RESULT_ERROR;3698}36993700inline brotli_decompressor::~brotli_decompressor() {3701if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }3702}37033704inline bool brotli_decompressor::is_valid() const { return decoder_s; }37053706inline bool brotli_decompressor::decompress(const char *data,3707size_t data_length,3708Callback callback) {3709if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||3710decoder_r == BROTLI_DECODER_RESULT_ERROR) {3711return 0;3712}37133714auto next_in = reinterpret_cast<const uint8_t *>(data);3715size_t avail_in = data_length;3716size_t total_out;37173718decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;37193720std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};3721while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {3722char *next_out = buff.data();3723size_t avail_out = buff.size();37243725decoder_r = BrotliDecoderDecompressStream(3726decoder_s, &avail_in, &next_in, &avail_out,3727reinterpret_cast<uint8_t **>(&next_out), &total_out);37283729if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }37303731if (!callback(buff.data(), buff.size() - avail_out)) { return false; }3732}37333734return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||3735decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;3736}3737#endif37383739inline bool has_header(const Headers &headers, const std::string &key) {3740return headers.find(key) != headers.end();3741}37423743inline const char *get_header_value(const Headers &headers,3744const std::string &key, size_t id,3745const char *def) {3746auto rng = headers.equal_range(key);3747auto it = rng.first;3748std::advance(it, static_cast<ssize_t>(id));3749if (it != rng.second) { return it->second.c_str(); }3750return def;3751}37523753inline bool compare_case_ignore(const std::string &a, const std::string &b) {3754if (a.size() != b.size()) { return false; }3755for (size_t i = 0; i < b.size(); i++) {3756if (::tolower(a[i]) != ::tolower(b[i])) { return false; }3757}3758return true;3759}37603761template <typename T>3762inline bool parse_header(const char *beg, const char *end, T fn) {3763// Skip trailing spaces and tabs.3764while (beg < end && is_space_or_tab(end[-1])) {3765end--;3766}37673768auto p = beg;3769while (p < end && *p != ':') {3770p++;3771}37723773if (p == end) { return false; }37743775auto key_end = p;37763777if (*p++ != ':') { return false; }37783779while (p < end && is_space_or_tab(*p)) {3780p++;3781}37823783if (p < end) {3784auto key_len = key_end - beg;3785if (!key_len) { return false; }37863787auto key = std::string(beg, key_end);3788auto val = compare_case_ignore(key, "Location")3789? std::string(p, end)3790: decode_url(std::string(p, end), false);3791fn(std::move(key), std::move(val));3792return true;3793}37943795return false;3796}37973798inline bool read_headers(Stream &strm, Headers &headers) {3799const auto bufsiz = 2048;3800char buf[bufsiz];3801stream_line_reader line_reader(strm, buf, bufsiz);38023803for (;;) {3804if (!line_reader.getline()) { return false; }38053806// Check if the line ends with CRLF.3807auto line_terminator_len = 2;3808if (line_reader.end_with_crlf()) {3809// Blank line indicates end of headers.3810if (line_reader.size() == 2) { break; }3811#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR3812} else {3813// Blank line indicates end of headers.3814if (line_reader.size() == 1) { break; }3815line_terminator_len = 1;3816}3817#else3818} else {3819continue; // Skip invalid line.3820}3821#endif38223823if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }38243825// Exclude line terminator3826auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;38273828parse_header(line_reader.ptr(), end,3829[&](std::string &&key, std::string &&val) {3830headers.emplace(std::move(key), std::move(val));3831});3832}38333834return true;3835}38363837inline bool read_content_with_length(Stream &strm, uint64_t len,3838Progress progress,3839ContentReceiverWithProgress out) {3840char buf[CPPHTTPLIB_RECV_BUFSIZ];38413842uint64_t r = 0;3843while (r < len) {3844auto read_len = static_cast<size_t>(len - r);3845auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));3846if (n <= 0) { return false; }38473848if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }3849r += static_cast<uint64_t>(n);38503851if (progress) {3852if (!progress(r, len)) { return false; }3853}3854}38553856return true;3857}38583859inline void skip_content_with_length(Stream &strm, uint64_t len) {3860char buf[CPPHTTPLIB_RECV_BUFSIZ];3861uint64_t r = 0;3862while (r < len) {3863auto read_len = static_cast<size_t>(len - r);3864auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));3865if (n <= 0) { return; }3866r += static_cast<uint64_t>(n);3867}3868}38693870inline bool read_content_without_length(Stream &strm,3871ContentReceiverWithProgress out) {3872char buf[CPPHTTPLIB_RECV_BUFSIZ];3873uint64_t r = 0;3874for (;;) {3875auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);3876if (n <= 0) { return true; }38773878if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }3879r += static_cast<uint64_t>(n);3880}38813882return true;3883}38843885template <typename T>3886inline bool read_content_chunked(Stream &strm, T &x,3887ContentReceiverWithProgress out) {3888const auto bufsiz = 16;3889char buf[bufsiz];38903891stream_line_reader line_reader(strm, buf, bufsiz);38923893if (!line_reader.getline()) { return false; }38943895unsigned long chunk_len;3896while (true) {3897char *end_ptr;38983899chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);39003901if (end_ptr == line_reader.ptr()) { return false; }3902if (chunk_len == ULONG_MAX) { return false; }39033904if (chunk_len == 0) { break; }39053906if (!read_content_with_length(strm, chunk_len, nullptr, out)) {3907return false;3908}39093910if (!line_reader.getline()) { return false; }39113912if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; }39133914if (!line_reader.getline()) { return false; }3915}39163917assert(chunk_len == 0);39183919// Trailer3920if (!line_reader.getline()) { return false; }39213922while (strcmp(line_reader.ptr(), "\r\n") != 0) {3923if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }39243925// Exclude line terminator3926constexpr auto line_terminator_len = 2;3927auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;39283929parse_header(line_reader.ptr(), end,3930[&](std::string &&key, std::string &&val) {3931x.headers.emplace(std::move(key), std::move(val));3932});39333934if (!line_reader.getline()) { return false; }3935}39363937return true;3938}39393940inline bool is_chunked_transfer_encoding(const Headers &headers) {3941return compare_case_ignore(3942get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked");3943}39443945template <typename T, typename U>3946bool prepare_content_receiver(T &x, int &status,3947ContentReceiverWithProgress receiver,3948bool decompress, U callback) {3949if (decompress) {3950std::string encoding = x.get_header_value("Content-Encoding");3951std::unique_ptr<decompressor> decompressor;39523953if (encoding == "gzip" || encoding == "deflate") {3954#ifdef CPPHTTPLIB_ZLIB_SUPPORT3955decompressor = detail::make_unique<gzip_decompressor>();3956#else3957status = StatusCode::UnsupportedMediaType_415;3958return false;3959#endif3960} else if (encoding.find("br") != std::string::npos) {3961#ifdef CPPHTTPLIB_BROTLI_SUPPORT3962decompressor = detail::make_unique<brotli_decompressor>();3963#else3964status = StatusCode::UnsupportedMediaType_415;3965return false;3966#endif3967}39683969if (decompressor) {3970if (decompressor->is_valid()) {3971ContentReceiverWithProgress out = [&](const char *buf, size_t n,3972uint64_t off, uint64_t len) {3973return decompressor->decompress(buf, n,3974[&](const char *buf2, size_t n2) {3975return receiver(buf2, n2, off, len);3976});3977};3978return callback(std::move(out));3979} else {3980status = StatusCode::InternalServerError_500;3981return false;3982}3983}3984}39853986ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,3987uint64_t len) {3988return receiver(buf, n, off, len);3989};3990return callback(std::move(out));3991}39923993template <typename T>3994bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,3995Progress progress, ContentReceiverWithProgress receiver,3996bool decompress) {3997return prepare_content_receiver(3998x, status, std::move(receiver), decompress,3999[&](const ContentReceiverWithProgress &out) {4000auto ret = true;4001auto exceed_payload_max_length = false;40024003if (is_chunked_transfer_encoding(x.headers)) {4004ret = read_content_chunked(strm, x, out);4005} else if (!has_header(x.headers, "Content-Length")) {4006ret = read_content_without_length(strm, out);4007} else {4008auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0);4009if (len > payload_max_length) {4010exceed_payload_max_length = true;4011skip_content_with_length(strm, len);4012ret = false;4013} else if (len > 0) {4014ret = read_content_with_length(strm, len, std::move(progress), out);4015}4016}40174018if (!ret) {4019status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_4134020: StatusCode::BadRequest_400;4021}4022return ret;4023});4024} // namespace detail40254026inline ssize_t write_headers(Stream &strm, const Headers &headers) {4027ssize_t write_len = 0;4028for (const auto &x : headers) {4029auto len =4030strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());4031if (len < 0) { return len; }4032write_len += len;4033}4034auto len = strm.write("\r\n");4035if (len < 0) { return len; }4036write_len += len;4037return write_len;4038}40394040inline bool write_data(Stream &strm, const char *d, size_t l) {4041size_t offset = 0;4042while (offset < l) {4043auto length = strm.write(d + offset, l - offset);4044if (length < 0) { return false; }4045offset += static_cast<size_t>(length);4046}4047return true;4048}40494050template <typename T>4051inline bool write_content(Stream &strm, const ContentProvider &content_provider,4052size_t offset, size_t length, T is_shutting_down,4053Error &error) {4054size_t end_offset = offset + length;4055auto ok = true;4056DataSink data_sink;40574058data_sink.write = [&](const char *d, size_t l) -> bool {4059if (ok) {4060if (strm.is_writable() && write_data(strm, d, l)) {4061offset += l;4062} else {4063ok = false;4064}4065}4066return ok;4067};40684069data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };40704071while (offset < end_offset && !is_shutting_down()) {4072if (!strm.is_writable()) {4073error = Error::Write;4074return false;4075} else if (!content_provider(offset, end_offset - offset, data_sink)) {4076error = Error::Canceled;4077return false;4078} else if (!ok) {4079error = Error::Write;4080return false;4081}4082}40834084error = Error::Success;4085return true;4086}40874088template <typename T>4089inline bool write_content(Stream &strm, const ContentProvider &content_provider,4090size_t offset, size_t length,4091const T &is_shutting_down) {4092auto error = Error::Success;4093return write_content(strm, content_provider, offset, length, is_shutting_down,4094error);4095}40964097template <typename T>4098inline bool4099write_content_without_length(Stream &strm,4100const ContentProvider &content_provider,4101const T &is_shutting_down) {4102size_t offset = 0;4103auto data_available = true;4104auto ok = true;4105DataSink data_sink;41064107data_sink.write = [&](const char *d, size_t l) -> bool {4108if (ok) {4109offset += l;4110if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; }4111}4112return ok;4113};41144115data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };41164117data_sink.done = [&](void) { data_available = false; };41184119while (data_available && !is_shutting_down()) {4120if (!strm.is_writable()) {4121return false;4122} else if (!content_provider(offset, 0, data_sink)) {4123return false;4124} else if (!ok) {4125return false;4126}4127}4128return true;4129}41304131template <typename T, typename U>4132inline bool4133write_content_chunked(Stream &strm, const ContentProvider &content_provider,4134const T &is_shutting_down, U &compressor, Error &error) {4135size_t offset = 0;4136auto data_available = true;4137auto ok = true;4138DataSink data_sink;41394140data_sink.write = [&](const char *d, size_t l) -> bool {4141if (ok) {4142data_available = l > 0;4143offset += l;41444145std::string payload;4146if (compressor.compress(d, l, false,4147[&](const char *data, size_t data_len) {4148payload.append(data, data_len);4149return true;4150})) {4151if (!payload.empty()) {4152// Emit chunked response header and footer for each chunk4153auto chunk =4154from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";4155if (!strm.is_writable() ||4156!write_data(strm, chunk.data(), chunk.size())) {4157ok = false;4158}4159}4160} else {4161ok = false;4162}4163}4164return ok;4165};41664167data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };41684169auto done_with_trailer = [&](const Headers *trailer) {4170if (!ok) { return; }41714172data_available = false;41734174std::string payload;4175if (!compressor.compress(nullptr, 0, true,4176[&](const char *data, size_t data_len) {4177payload.append(data, data_len);4178return true;4179})) {4180ok = false;4181return;4182}41834184if (!payload.empty()) {4185// Emit chunked response header and footer for each chunk4186auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";4187if (!strm.is_writable() ||4188!write_data(strm, chunk.data(), chunk.size())) {4189ok = false;4190return;4191}4192}41934194static const std::string done_marker("0\r\n");4195if (!write_data(strm, done_marker.data(), done_marker.size())) {4196ok = false;4197}41984199// Trailer4200if (trailer) {4201for (const auto &kv : *trailer) {4202std::string field_line = kv.first + ": " + kv.second + "\r\n";4203if (!write_data(strm, field_line.data(), field_line.size())) {4204ok = false;4205}4206}4207}42084209static const std::string crlf("\r\n");4210if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; }4211};42124213data_sink.done = [&](void) { done_with_trailer(nullptr); };42144215data_sink.done_with_trailer = [&](const Headers &trailer) {4216done_with_trailer(&trailer);4217};42184219while (data_available && !is_shutting_down()) {4220if (!strm.is_writable()) {4221error = Error::Write;4222return false;4223} else if (!content_provider(offset, 0, data_sink)) {4224error = Error::Canceled;4225return false;4226} else if (!ok) {4227error = Error::Write;4228return false;4229}4230}42314232error = Error::Success;4233return true;4234}42354236template <typename T, typename U>4237inline bool write_content_chunked(Stream &strm,4238const ContentProvider &content_provider,4239const T &is_shutting_down, U &compressor) {4240auto error = Error::Success;4241return write_content_chunked(strm, content_provider, is_shutting_down,4242compressor, error);4243}42444245template <typename T>4246inline bool redirect(T &cli, Request &req, Response &res,4247const std::string &path, const std::string &location,4248Error &error) {4249Request new_req = req;4250new_req.path = path;4251new_req.redirect_count_ -= 1;42524253if (res.status == StatusCode::SeeOther_303 &&4254(req.method != "GET" && req.method != "HEAD")) {4255new_req.method = "GET";4256new_req.body.clear();4257new_req.headers.clear();4258}42594260Response new_res;42614262auto ret = cli.send(new_req, new_res, error);4263if (ret) {4264req = new_req;4265res = new_res;42664267if (res.location.empty()) { res.location = location; }4268}4269return ret;4270}42714272inline std::string params_to_query_str(const Params ¶ms) {4273std::string query;42744275for (auto it = params.begin(); it != params.end(); ++it) {4276if (it != params.begin()) { query += "&"; }4277query += it->first;4278query += "=";4279query += encode_query_param(it->second);4280}4281return query;4282}42834284inline void parse_query_text(const std::string &s, Params ¶ms) {4285std::set<std::string> cache;4286split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {4287std::string kv(b, e);4288if (cache.find(kv) != cache.end()) { return; }4289cache.insert(kv);42904291std::string key;4292std::string val;4293split(b, e, '=', [&](const char *b2, const char *e2) {4294if (key.empty()) {4295key.assign(b2, e2);4296} else {4297val.assign(b2, e2);4298}4299});43004301if (!key.empty()) {4302params.emplace(decode_url(key, true), decode_url(val, true));4303}4304});4305}43064307inline bool parse_multipart_boundary(const std::string &content_type,4308std::string &boundary) {4309auto boundary_keyword = "boundary=";4310auto pos = content_type.find(boundary_keyword);4311if (pos == std::string::npos) { return false; }4312auto end = content_type.find(';', pos);4313auto beg = pos + strlen(boundary_keyword);4314boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));4315return !boundary.empty();4316}43174318inline void parse_disposition_params(const std::string &s, Params ¶ms) {4319std::set<std::string> cache;4320split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {4321std::string kv(b, e);4322if (cache.find(kv) != cache.end()) { return; }4323cache.insert(kv);43244325std::string key;4326std::string val;4327split(b, e, '=', [&](const char *b2, const char *e2) {4328if (key.empty()) {4329key.assign(b2, e2);4330} else {4331val.assign(b2, e2);4332}4333});43344335if (!key.empty()) {4336params.emplace(trim_double_quotes_copy((key)),4337trim_double_quotes_copy((val)));4338}4339});4340}43414342#ifdef CPPHTTPLIB_NO_EXCEPTIONS4343inline bool parse_range_header(const std::string &s, Ranges &ranges) {4344#else4345inline bool parse_range_header(const std::string &s, Ranges &ranges) try {4346#endif4347static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");4348std::smatch m;4349if (std::regex_match(s, m, re_first_range)) {4350auto pos = static_cast<size_t>(m.position(1));4351auto len = static_cast<size_t>(m.length(1));4352auto all_valid_ranges = true;4353split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {4354if (!all_valid_ranges) { return; }4355static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");4356std::cmatch cm;4357if (std::regex_match(b, e, cm, re_another_range)) {4358ssize_t first = -1;4359if (!cm.str(1).empty()) {4360first = static_cast<ssize_t>(std::stoll(cm.str(1)));4361}43624363ssize_t last = -1;4364if (!cm.str(2).empty()) {4365last = static_cast<ssize_t>(std::stoll(cm.str(2)));4366}43674368if (first != -1 && last != -1 && first > last) {4369all_valid_ranges = false;4370return;4371}4372ranges.emplace_back(std::make_pair(first, last));4373}4374});4375return all_valid_ranges;4376}4377return false;4378#ifdef CPPHTTPLIB_NO_EXCEPTIONS4379}4380#else4381} catch (...) { return false; }4382#endif43834384class MultipartFormDataParser {4385public:4386MultipartFormDataParser() = default;43874388void set_boundary(std::string &&boundary) {4389boundary_ = boundary;4390dash_boundary_crlf_ = dash_ + boundary_ + crlf_;4391crlf_dash_boundary_ = crlf_ + dash_ + boundary_;4392}43934394bool is_valid() const { return is_valid_; }43954396bool parse(const char *buf, size_t n, const ContentReceiver &content_callback,4397const MultipartContentHeader &header_callback) {43984399buf_append(buf, n);44004401while (buf_size() > 0) {4402switch (state_) {4403case 0: { // Initial boundary4404buf_erase(buf_find(dash_boundary_crlf_));4405if (dash_boundary_crlf_.size() > buf_size()) { return true; }4406if (!buf_start_with(dash_boundary_crlf_)) { return false; }4407buf_erase(dash_boundary_crlf_.size());4408state_ = 1;4409break;4410}4411case 1: { // New entry4412clear_file_info();4413state_ = 2;4414break;4415}4416case 2: { // Headers4417auto pos = buf_find(crlf_);4418if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }4419while (pos < buf_size()) {4420// Empty line4421if (pos == 0) {4422if (!header_callback(file_)) {4423is_valid_ = false;4424return false;4425}4426buf_erase(crlf_.size());4427state_ = 3;4428break;4429}44304431const auto header = buf_head(pos);44324433if (!parse_header(header.data(), header.data() + header.size(),4434[&](std::string &&, std::string &&) {})) {4435is_valid_ = false;4436return false;4437}44384439static const std::string header_content_type = "Content-Type:";44404441if (start_with_case_ignore(header, header_content_type)) {4442file_.content_type =4443trim_copy(header.substr(header_content_type.size()));4444} else {4445static const std::regex re_content_disposition(4446R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",4447std::regex_constants::icase);44484449std::smatch m;4450if (std::regex_match(header, m, re_content_disposition)) {4451Params params;4452parse_disposition_params(m[1], params);44534454auto it = params.find("name");4455if (it != params.end()) {4456file_.name = it->second;4457} else {4458is_valid_ = false;4459return false;4460}44614462it = params.find("filename");4463if (it != params.end()) { file_.filename = it->second; }44644465it = params.find("filename*");4466if (it != params.end()) {4467// Only allow UTF-8 enconnding...4468static const std::regex re_rfc5987_encoding(4469R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);44704471std::smatch m2;4472if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {4473file_.filename = decode_url(m2[1], false); // override...4474} else {4475is_valid_ = false;4476return false;4477}4478}4479}4480}4481buf_erase(pos + crlf_.size());4482pos = buf_find(crlf_);4483}4484if (state_ != 3) { return true; }4485break;4486}4487case 3: { // Body4488if (crlf_dash_boundary_.size() > buf_size()) { return true; }4489auto pos = buf_find(crlf_dash_boundary_);4490if (pos < buf_size()) {4491if (!content_callback(buf_data(), pos)) {4492is_valid_ = false;4493return false;4494}4495buf_erase(pos + crlf_dash_boundary_.size());4496state_ = 4;4497} else {4498auto len = buf_size() - crlf_dash_boundary_.size();4499if (len > 0) {4500if (!content_callback(buf_data(), len)) {4501is_valid_ = false;4502return false;4503}4504buf_erase(len);4505}4506return true;4507}4508break;4509}4510case 4: { // Boundary4511if (crlf_.size() > buf_size()) { return true; }4512if (buf_start_with(crlf_)) {4513buf_erase(crlf_.size());4514state_ = 1;4515} else {4516if (dash_.size() > buf_size()) { return true; }4517if (buf_start_with(dash_)) {4518buf_erase(dash_.size());4519is_valid_ = true;4520buf_erase(buf_size()); // Remove epilogue4521} else {4522return true;4523}4524}4525break;4526}4527}4528}45294530return true;4531}45324533private:4534void clear_file_info() {4535file_.name.clear();4536file_.filename.clear();4537file_.content_type.clear();4538}45394540bool start_with_case_ignore(const std::string &a,4541const std::string &b) const {4542if (a.size() < b.size()) { return false; }4543for (size_t i = 0; i < b.size(); i++) {4544if (::tolower(a[i]) != ::tolower(b[i])) { return false; }4545}4546return true;4547}45484549const std::string dash_ = "--";4550const std::string crlf_ = "\r\n";4551std::string boundary_;4552std::string dash_boundary_crlf_;4553std::string crlf_dash_boundary_;45544555size_t state_ = 0;4556bool is_valid_ = false;4557MultipartFormData file_;45584559// Buffer4560bool start_with(const std::string &a, size_t spos, size_t epos,4561const std::string &b) const {4562if (epos - spos < b.size()) { return false; }4563for (size_t i = 0; i < b.size(); i++) {4564if (a[i + spos] != b[i]) { return false; }4565}4566return true;4567}45684569size_t buf_size() const { return buf_epos_ - buf_spos_; }45704571const char *buf_data() const { return &buf_[buf_spos_]; }45724573std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }45744575bool buf_start_with(const std::string &s) const {4576return start_with(buf_, buf_spos_, buf_epos_, s);4577}45784579size_t buf_find(const std::string &s) const {4580auto c = s.front();45814582size_t off = buf_spos_;4583while (off < buf_epos_) {4584auto pos = off;4585while (true) {4586if (pos == buf_epos_) { return buf_size(); }4587if (buf_[pos] == c) { break; }4588pos++;4589}45904591auto remaining_size = buf_epos_ - pos;4592if (s.size() > remaining_size) { return buf_size(); }45934594if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }45954596off = pos + 1;4597}45984599return buf_size();4600}46014602void buf_append(const char *data, size_t n) {4603auto remaining_size = buf_size();4604if (remaining_size > 0 && buf_spos_ > 0) {4605for (size_t i = 0; i < remaining_size; i++) {4606buf_[i] = buf_[buf_spos_ + i];4607}4608}4609buf_spos_ = 0;4610buf_epos_ = remaining_size;46114612if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }46134614for (size_t i = 0; i < n; i++) {4615buf_[buf_epos_ + i] = data[i];4616}4617buf_epos_ += n;4618}46194620void buf_erase(size_t size) { buf_spos_ += size; }46214622std::string buf_;4623size_t buf_spos_ = 0;4624size_t buf_epos_ = 0;4625};46264627inline std::string to_lower(const char *beg, const char *end) {4628std::string out;4629auto it = beg;4630while (it != end) {4631out += static_cast<char>(::tolower(*it));4632it++;4633}4634return out;4635}46364637inline std::string make_multipart_data_boundary() {4638static const char data[] =4639"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";46404641// std::random_device might actually be deterministic on some4642// platforms, but due to lack of support in the c++ standard library,4643// doing better requires either some ugly hacks or breaking portability.4644std::random_device seed_gen;46454646// Request 128 bits of entropy for initialization4647std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};4648std::mt19937 engine(seed_sequence);46494650std::string result = "--cpp-httplib-multipart-data-";46514652for (auto i = 0; i < 16; i++) {4653result += data[engine() % (sizeof(data) - 1)];4654}46554656return result;4657}46584659inline bool is_multipart_boundary_chars_valid(const std::string &boundary) {4660auto valid = true;4661for (size_t i = 0; i < boundary.size(); i++) {4662auto c = boundary[i];4663if (!std::isalnum(c) && c != '-' && c != '_') {4664valid = false;4665break;4666}4667}4668return valid;4669}46704671template <typename T>4672inline std::string4673serialize_multipart_formdata_item_begin(const T &item,4674const std::string &boundary) {4675std::string body = "--" + boundary + "\r\n";4676body += "Content-Disposition: form-data; name=\"" + item.name + "\"";4677if (!item.filename.empty()) {4678body += "; filename=\"" + item.filename + "\"";4679}4680body += "\r\n";4681if (!item.content_type.empty()) {4682body += "Content-Type: " + item.content_type + "\r\n";4683}4684body += "\r\n";46854686return body;4687}46884689inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; }46904691inline std::string4692serialize_multipart_formdata_finish(const std::string &boundary) {4693return "--" + boundary + "--\r\n";4694}46954696inline std::string4697serialize_multipart_formdata_get_content_type(const std::string &boundary) {4698return "multipart/form-data; boundary=" + boundary;4699}47004701inline std::string4702serialize_multipart_formdata(const MultipartFormDataItems &items,4703const std::string &boundary, bool finish = true) {4704std::string body;47054706for (const auto &item : items) {4707body += serialize_multipart_formdata_item_begin(item, boundary);4708body += item.content + serialize_multipart_formdata_item_end();4709}47104711if (finish) { body += serialize_multipart_formdata_finish(boundary); }47124713return body;4714}47154716inline std::pair<size_t, size_t>4717get_range_offset_and_length(const Request &req, size_t content_length,4718size_t index) {4719auto r = req.ranges[index];47204721if (r.first == -1 && r.second == -1) {4722return std::make_pair(0, content_length);4723}47244725auto slen = static_cast<ssize_t>(content_length);47264727if (r.first == -1) {4728r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);4729r.second = slen - 1;4730}47314732if (r.second == -1) { r.second = slen - 1; }4733return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);4734}47354736inline std::string4737make_content_range_header_field(const std::pair<ssize_t, ssize_t> &range,4738size_t content_length) {4739std::string field = "bytes ";4740if (range.first != -1) { field += std::to_string(range.first); }4741field += "-";4742if (range.second != -1) { field += std::to_string(range.second); }4743field += "/";4744field += std::to_string(content_length);4745return field;4746}47474748template <typename SToken, typename CToken, typename Content>4749bool process_multipart_ranges_data(const Request &req, Response &res,4750const std::string &boundary,4751const std::string &content_type,4752SToken stoken, CToken ctoken,4753Content content) {4754for (size_t i = 0; i < req.ranges.size(); i++) {4755ctoken("--");4756stoken(boundary);4757ctoken("\r\n");4758if (!content_type.empty()) {4759ctoken("Content-Type: ");4760stoken(content_type);4761ctoken("\r\n");4762}47634764ctoken("Content-Range: ");4765const auto &range = req.ranges[i];4766stoken(make_content_range_header_field(range, res.content_length_));4767ctoken("\r\n");4768ctoken("\r\n");47694770auto offsets = get_range_offset_and_length(req, res.content_length_, i);4771auto offset = offsets.first;4772auto length = offsets.second;4773if (!content(offset, length)) { return false; }4774ctoken("\r\n");4775}47764777ctoken("--");4778stoken(boundary);4779ctoken("--");47804781return true;4782}47834784inline bool make_multipart_ranges_data(const Request &req, Response &res,4785const std::string &boundary,4786const std::string &content_type,4787std::string &data) {4788return process_multipart_ranges_data(4789req, res, boundary, content_type,4790[&](const std::string &token) { data += token; },4791[&](const std::string &token) { data += token; },4792[&](size_t offset, size_t length) {4793if (offset < res.body.size()) {4794data += res.body.substr(offset, length);4795return true;4796}4797return false;4798});4799}48004801inline size_t4802get_multipart_ranges_data_length(const Request &req, Response &res,4803const std::string &boundary,4804const std::string &content_type) {4805size_t data_length = 0;48064807process_multipart_ranges_data(4808req, res, boundary, content_type,4809[&](const std::string &token) { data_length += token.size(); },4810[&](const std::string &token) { data_length += token.size(); },4811[&](size_t /*offset*/, size_t length) {4812data_length += length;4813return true;4814});48154816return data_length;4817}48184819template <typename T>4820inline bool write_multipart_ranges_data(Stream &strm, const Request &req,4821Response &res,4822const std::string &boundary,4823const std::string &content_type,4824const T &is_shutting_down) {4825return process_multipart_ranges_data(4826req, res, boundary, content_type,4827[&](const std::string &token) { strm.write(token); },4828[&](const std::string &token) { strm.write(token); },4829[&](size_t offset, size_t length) {4830return write_content(strm, res.content_provider_, offset, length,4831is_shutting_down);4832});4833}48344835inline std::pair<size_t, size_t>4836get_range_offset_and_length(const Request &req, const Response &res,4837size_t index) {4838auto r = req.ranges[index];48394840if (r.second == -1) {4841r.second = static_cast<ssize_t>(res.content_length_) - 1;4842}48434844return std::make_pair(r.first, r.second - r.first + 1);4845}48464847inline bool expect_content(const Request &req) {4848if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||4849req.method == "PRI" || req.method == "DELETE") {4850return true;4851}4852// TODO: check if Content-Length is set4853return false;4854}48554856inline bool has_crlf(const std::string &s) {4857auto p = s.c_str();4858while (*p) {4859if (*p == '\r' || *p == '\n') { return true; }4860p++;4861}4862return false;4863}48644865#ifdef CPPHTTPLIB_OPENSSL_SUPPORT4866inline std::string message_digest(const std::string &s, const EVP_MD *algo) {4867auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(4868EVP_MD_CTX_new(), EVP_MD_CTX_free);48694870unsigned int hash_length = 0;4871unsigned char hash[EVP_MAX_MD_SIZE];48724873EVP_DigestInit_ex(context.get(), algo, nullptr);4874EVP_DigestUpdate(context.get(), s.c_str(), s.size());4875EVP_DigestFinal_ex(context.get(), hash, &hash_length);48764877std::stringstream ss;4878for (auto i = 0u; i < hash_length; ++i) {4879ss << std::hex << std::setw(2) << std::setfill('0')4880<< static_cast<unsigned int>(hash[i]);4881}48824883return ss.str();4884}48854886inline std::string MD5(const std::string &s) {4887return message_digest(s, EVP_md5());4888}48894890inline std::string SHA_256(const std::string &s) {4891return message_digest(s, EVP_sha256());4892}48934894inline std::string SHA_512(const std::string &s) {4895return message_digest(s, EVP_sha512());4896}4897#endif48984899#ifdef CPPHTTPLIB_OPENSSL_SUPPORT4900#ifdef _WIN324901// NOTE: This code came up with the following stackoverflow post:4902// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store4903inline bool load_system_certs_on_windows(X509_STORE *store) {4904auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");4905if (!hStore) { return false; }49064907auto result = false;4908PCCERT_CONTEXT pContext = NULL;4909while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=4910nullptr) {4911auto encoded_cert =4912static_cast<const unsigned char *>(pContext->pbCertEncoded);49134914auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);4915if (x509) {4916X509_STORE_add_cert(store, x509);4917X509_free(x509);4918result = true;4919}4920}49214922CertFreeCertificateContext(pContext);4923CertCloseStore(hStore, 0);49244925return result;4926}4927#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)4928#if TARGET_OS_OSX4929template <typename T>4930using CFObjectPtr =4931std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;49324933inline void cf_object_ptr_deleter(CFTypeRef obj) {4934if (obj) { CFRelease(obj); }4935}49364937inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {4938CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};4939CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,4940kCFBooleanTrue};49414942CFObjectPtr<CFDictionaryRef> query(4943CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,4944sizeof(keys) / sizeof(keys[0]),4945&kCFTypeDictionaryKeyCallBacks,4946&kCFTypeDictionaryValueCallBacks),4947cf_object_ptr_deleter);49484949if (!query) { return false; }49504951CFTypeRef security_items = nullptr;4952if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||4953CFArrayGetTypeID() != CFGetTypeID(security_items)) {4954return false;4955}49564957certs.reset(reinterpret_cast<CFArrayRef>(security_items));4958return true;4959}49604961inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {4962CFArrayRef root_security_items = nullptr;4963if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {4964return false;4965}49664967certs.reset(root_security_items);4968return true;4969}49704971inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {4972auto result = false;4973for (auto i = 0; i < CFArrayGetCount(certs); ++i) {4974const auto cert = reinterpret_cast<const __SecCertificate *>(4975CFArrayGetValueAtIndex(certs, i));49764977if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }49784979CFDataRef cert_data = nullptr;4980if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=4981errSecSuccess) {4982continue;4983}49844985CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);49864987auto encoded_cert = static_cast<const unsigned char *>(4988CFDataGetBytePtr(cert_data_ptr.get()));49894990auto x509 =4991d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));49924993if (x509) {4994X509_STORE_add_cert(store, x509);4995X509_free(x509);4996result = true;4997}4998}49995000return result;5001}50025003inline bool load_system_certs_on_macos(X509_STORE *store) {5004auto result = false;5005CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);5006if (retrieve_certs_from_keychain(certs) && certs) {5007result = add_certs_to_x509_store(certs.get(), store);5008}50095010if (retrieve_root_certs_from_keychain(certs) && certs) {5011result = add_certs_to_x509_store(certs.get(), store) || result;5012}50135014return result;5015}5016#endif // TARGET_OS_OSX5017#endif // _WIN325018#endif // CPPHTTPLIB_OPENSSL_SUPPORT50195020#ifdef _WIN325021class WSInit {5022public:5023WSInit() {5024WSADATA wsaData;5025if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;5026}50275028~WSInit() {5029if (is_valid_) WSACleanup();5030}50315032bool is_valid_ = false;5033};50345035static WSInit wsinit_;5036#endif50375038#ifdef CPPHTTPLIB_OPENSSL_SUPPORT5039inline std::pair<std::string, std::string> make_digest_authentication_header(5040const Request &req, const std::map<std::string, std::string> &auth,5041size_t cnonce_count, const std::string &cnonce, const std::string &username,5042const std::string &password, bool is_proxy = false) {5043std::string nc;5044{5045std::stringstream ss;5046ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;5047nc = ss.str();5048}50495050std::string qop;5051if (auth.find("qop") != auth.end()) {5052qop = auth.at("qop");5053if (qop.find("auth-int") != std::string::npos) {5054qop = "auth-int";5055} else if (qop.find("auth") != std::string::npos) {5056qop = "auth";5057} else {5058qop.clear();5059}5060}50615062std::string algo = "MD5";5063if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }50645065std::string response;5066{5067auto H = algo == "SHA-256" ? detail::SHA_2565068: algo == "SHA-512" ? detail::SHA_5125069: detail::MD5;50705071auto A1 = username + ":" + auth.at("realm") + ":" + password;50725073auto A2 = req.method + ":" + req.path;5074if (qop == "auth-int") { A2 += ":" + H(req.body); }50755076if (qop.empty()) {5077response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));5078} else {5079response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +5080":" + qop + ":" + H(A2));5081}5082}50835084auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";50855086auto field = "Digest username=\"" + username + "\", realm=\"" +5087auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +5088"\", uri=\"" + req.path + "\", algorithm=" + algo +5089(qop.empty() ? ", response=\""5090: ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +5091cnonce + "\", response=\"") +5092response + "\"" +5093(opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");50945095auto key = is_proxy ? "Proxy-Authorization" : "Authorization";5096return std::make_pair(key, field);5097}5098#endif50995100inline bool parse_www_authenticate(const Response &res,5101std::map<std::string, std::string> &auth,5102bool is_proxy) {5103auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";5104if (res.has_header(auth_key)) {5105static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");5106auto s = res.get_header_value(auth_key);5107auto pos = s.find(' ');5108if (pos != std::string::npos) {5109auto type = s.substr(0, pos);5110if (type == "Basic") {5111return false;5112} else if (type == "Digest") {5113s = s.substr(pos + 1);5114auto beg = std::sregex_iterator(s.begin(), s.end(), re);5115for (auto i = beg; i != std::sregex_iterator(); ++i) {5116const auto &m = *i;5117auto key = s.substr(static_cast<size_t>(m.position(1)),5118static_cast<size_t>(m.length(1)));5119auto val = m.length(2) > 05120? s.substr(static_cast<size_t>(m.position(2)),5121static_cast<size_t>(m.length(2)))5122: s.substr(static_cast<size_t>(m.position(3)),5123static_cast<size_t>(m.length(3)));5124auth[key] = val;5125}5126return true;5127}5128}5129}5130return false;5131}51325133// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-4402405134inline std::string random_string(size_t length) {5135auto randchar = []() -> char {5136const char charset[] = "0123456789"5137"ABCDEFGHIJKLMNOPQRSTUVWXYZ"5138"abcdefghijklmnopqrstuvwxyz";5139const size_t max_index = (sizeof(charset) - 1);5140return charset[static_cast<size_t>(std::rand()) % max_index];5141};5142std::string str(length, 0);5143std::generate_n(str.begin(), length, randchar);5144return str;5145}51465147class ContentProviderAdapter {5148public:5149explicit ContentProviderAdapter(5150ContentProviderWithoutLength &&content_provider)5151: content_provider_(content_provider) {}51525153bool operator()(size_t offset, size_t, DataSink &sink) {5154return content_provider_(offset, sink);5155}51565157private:5158ContentProviderWithoutLength content_provider_;5159};51605161} // namespace detail51625163inline std::string hosted_at(const std::string &hostname) {5164std::vector<std::string> addrs;5165hosted_at(hostname, addrs);5166if (addrs.empty()) { return std::string(); }5167return addrs[0];5168}51695170inline void hosted_at(const std::string &hostname,5171std::vector<std::string> &addrs) {5172struct addrinfo hints;5173struct addrinfo *result;51745175memset(&hints, 0, sizeof(struct addrinfo));5176hints.ai_family = AF_UNSPEC;5177hints.ai_socktype = SOCK_STREAM;5178hints.ai_protocol = 0;51795180if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) {5181#if defined __linux__ && !defined __ANDROID__5182res_init();5183#endif5184return;5185}51865187for (auto rp = result; rp; rp = rp->ai_next) {5188const auto &addr =5189*reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);5190std::string ip;5191auto dummy = -1;5192if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,5193dummy)) {5194addrs.push_back(ip);5195}5196}51975198freeaddrinfo(result);5199}52005201inline std::string append_query_params(const std::string &path,5202const Params ¶ms) {5203std::string path_with_query = path;5204const static std::regex re("[^?]+\\?.*");5205auto delm = std::regex_match(path, re) ? '&' : '?';5206path_with_query += delm + detail::params_to_query_str(params);5207return path_with_query;5208}52095210// Header utilities5211inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {5212std::string field = "bytes=";5213auto i = 0;5214for (auto r : ranges) {5215if (i != 0) { field += ", "; }5216if (r.first != -1) { field += std::to_string(r.first); }5217field += '-';5218if (r.second != -1) { field += std::to_string(r.second); }5219i++;5220}5221return std::make_pair("Range", std::move(field));5222}52235224inline std::pair<std::string, std::string>5225make_basic_authentication_header(const std::string &username,5226const std::string &password, bool is_proxy) {5227auto field = "Basic " + detail::base64_encode(username + ":" + password);5228auto key = is_proxy ? "Proxy-Authorization" : "Authorization";5229return std::make_pair(key, std::move(field));5230}52315232inline std::pair<std::string, std::string>5233make_bearer_token_authentication_header(const std::string &token,5234bool is_proxy = false) {5235auto field = "Bearer " + token;5236auto key = is_proxy ? "Proxy-Authorization" : "Authorization";5237return std::make_pair(key, std::move(field));5238}52395240// Request implementation5241inline bool Request::has_header(const std::string &key) const {5242return detail::has_header(headers, key);5243}52445245inline std::string Request::get_header_value(const std::string &key,5246size_t id) const {5247return detail::get_header_value(headers, key, id, "");5248}52495250inline size_t Request::get_header_value_count(const std::string &key) const {5251auto r = headers.equal_range(key);5252return static_cast<size_t>(std::distance(r.first, r.second));5253}52545255inline void Request::set_header(const std::string &key,5256const std::string &val) {5257if (!detail::has_crlf(key) && !detail::has_crlf(val)) {5258headers.emplace(key, val);5259}5260}52615262inline bool Request::has_param(const std::string &key) const {5263return params.find(key) != params.end();5264}52655266inline std::string Request::get_param_value(const std::string &key,5267size_t id) const {5268auto rng = params.equal_range(key);5269auto it = rng.first;5270std::advance(it, static_cast<ssize_t>(id));5271if (it != rng.second) { return it->second; }5272return std::string();5273}52745275inline size_t Request::get_param_value_count(const std::string &key) const {5276auto r = params.equal_range(key);5277return static_cast<size_t>(std::distance(r.first, r.second));5278}52795280inline bool Request::is_multipart_form_data() const {5281const auto &content_type = get_header_value("Content-Type");5282return !content_type.rfind("multipart/form-data", 0);5283}52845285inline bool Request::has_file(const std::string &key) const {5286return files.find(key) != files.end();5287}52885289inline MultipartFormData Request::get_file_value(const std::string &key) const {5290auto it = files.find(key);5291if (it != files.end()) { return it->second; }5292return MultipartFormData();5293}52945295inline std::vector<MultipartFormData>5296Request::get_file_values(const std::string &key) const {5297std::vector<MultipartFormData> values;5298auto rng = files.equal_range(key);5299for (auto it = rng.first; it != rng.second; it++) {5300values.push_back(it->second);5301}5302return values;5303}53045305// Response implementation5306inline bool Response::has_header(const std::string &key) const {5307return headers.find(key) != headers.end();5308}53095310inline std::string Response::get_header_value(const std::string &key,5311size_t id) const {5312return detail::get_header_value(headers, key, id, "");5313}53145315inline size_t Response::get_header_value_count(const std::string &key) const {5316auto r = headers.equal_range(key);5317return static_cast<size_t>(std::distance(r.first, r.second));5318}53195320inline void Response::set_header(const std::string &key,5321const std::string &val) {5322if (!detail::has_crlf(key) && !detail::has_crlf(val)) {5323headers.emplace(key, val);5324}5325}53265327inline void Response::set_redirect(const std::string &url, int stat) {5328if (!detail::has_crlf(url)) {5329set_header("Location", url);5330if (300 <= stat && stat < 400) {5331this->status = stat;5332} else {5333this->status = StatusCode::Found_302;5334}5335}5336}53375338inline void Response::set_content(const char *s, size_t n,5339const std::string &content_type) {5340body.assign(s, n);53415342auto rng = headers.equal_range("Content-Type");5343headers.erase(rng.first, rng.second);5344set_header("Content-Type", content_type);5345}53465347inline void Response::set_content(const std::string &s,5348const std::string &content_type) {5349set_content(s.data(), s.size(), content_type);5350}53515352inline void Response::set_content_provider(5353size_t in_length, const std::string &content_type, ContentProvider provider,5354ContentProviderResourceReleaser resource_releaser) {5355set_header("Content-Type", content_type);5356content_length_ = in_length;5357if (in_length > 0) { content_provider_ = std::move(provider); }5358content_provider_resource_releaser_ = resource_releaser;5359is_chunked_content_provider_ = false;5360}53615362inline void Response::set_content_provider(5363const std::string &content_type, ContentProviderWithoutLength provider,5364ContentProviderResourceReleaser resource_releaser) {5365set_header("Content-Type", content_type);5366content_length_ = 0;5367content_provider_ = detail::ContentProviderAdapter(std::move(provider));5368content_provider_resource_releaser_ = resource_releaser;5369is_chunked_content_provider_ = false;5370}53715372inline void Response::set_chunked_content_provider(5373const std::string &content_type, ContentProviderWithoutLength provider,5374ContentProviderResourceReleaser resource_releaser) {5375set_header("Content-Type", content_type);5376content_length_ = 0;5377content_provider_ = detail::ContentProviderAdapter(std::move(provider));5378content_provider_resource_releaser_ = resource_releaser;5379is_chunked_content_provider_ = true;5380}53815382// Result implementation5383inline bool Result::has_request_header(const std::string &key) const {5384return request_headers_.find(key) != request_headers_.end();5385}53865387inline std::string Result::get_request_header_value(const std::string &key,5388size_t id) const {5389return detail::get_header_value(request_headers_, key, id, "");5390}53915392inline size_t5393Result::get_request_header_value_count(const std::string &key) const {5394auto r = request_headers_.equal_range(key);5395return static_cast<size_t>(std::distance(r.first, r.second));5396}53975398// Stream implementation5399inline ssize_t Stream::write(const char *ptr) {5400return write(ptr, strlen(ptr));5401}54025403inline ssize_t Stream::write(const std::string &s) {5404return write(s.data(), s.size());5405}54065407namespace detail {54085409// Socket stream implementation5410inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,5411time_t read_timeout_usec,5412time_t write_timeout_sec,5413time_t write_timeout_usec)5414: sock_(sock), read_timeout_sec_(read_timeout_sec),5415read_timeout_usec_(read_timeout_usec),5416write_timeout_sec_(write_timeout_sec),5417write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {}54185419inline SocketStream::~SocketStream() = default;54205421inline bool SocketStream::is_readable() const {5422return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;5423}54245425inline bool SocketStream::is_writable() const {5426return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&5427is_socket_alive(sock_);5428}54295430inline ssize_t SocketStream::read(char *ptr, size_t size) {5431#ifdef _WIN325432size =5433(std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));5434#else5435size = (std::min)(size,5436static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));5437#endif54385439if (read_buff_off_ < read_buff_content_size_) {5440auto remaining_size = read_buff_content_size_ - read_buff_off_;5441if (size <= remaining_size) {5442memcpy(ptr, read_buff_.data() + read_buff_off_, size);5443read_buff_off_ += size;5444return static_cast<ssize_t>(size);5445} else {5446memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);5447read_buff_off_ += remaining_size;5448return static_cast<ssize_t>(remaining_size);5449}5450}54515452if (!is_readable()) { return -1; }54535454read_buff_off_ = 0;5455read_buff_content_size_ = 0;54565457if (size < read_buff_size_) {5458auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,5459CPPHTTPLIB_RECV_FLAGS);5460if (n <= 0) {5461return n;5462} else if (n <= static_cast<ssize_t>(size)) {5463memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));5464return n;5465} else {5466memcpy(ptr, read_buff_.data(), size);5467read_buff_off_ = size;5468read_buff_content_size_ = static_cast<size_t>(n);5469return static_cast<ssize_t>(size);5470}5471} else {5472return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);5473}5474}54755476inline ssize_t SocketStream::write(const char *ptr, size_t size) {5477if (!is_writable()) { return -1; }54785479#if defined(_WIN32) && !defined(_WIN64)5480size =5481(std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));5482#endif54835484return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);5485}54865487inline void SocketStream::get_remote_ip_and_port(std::string &ip,5488int &port) const {5489return detail::get_remote_ip_and_port(sock_, ip, port);5490}54915492inline void SocketStream::get_local_ip_and_port(std::string &ip,5493int &port) const {5494return detail::get_local_ip_and_port(sock_, ip, port);5495}54965497inline socket_t SocketStream::socket() const { return sock_; }54985499// Buffer stream implementation5500inline bool BufferStream::is_readable() const { return true; }55015502inline bool BufferStream::is_writable() const { return true; }55035504inline ssize_t BufferStream::read(char *ptr, size_t size) {5505#if defined(_MSC_VER) && _MSC_VER < 19105506auto len_read = buffer._Copy_s(ptr, size, size, position);5507#else5508auto len_read = buffer.copy(ptr, size, position);5509#endif5510position += static_cast<size_t>(len_read);5511return static_cast<ssize_t>(len_read);5512}55135514inline ssize_t BufferStream::write(const char *ptr, size_t size) {5515buffer.append(ptr, size);5516return static_cast<ssize_t>(size);5517}55185519inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,5520int & /*port*/) const {}55215522inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,5523int & /*port*/) const {}55245525inline socket_t BufferStream::socket() const { return 0; }55265527inline const std::string &BufferStream::get_buffer() const { return buffer; }55285529inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {5530// One past the last ending position of a path param substring5531std::size_t last_param_end = 0;55325533#ifndef CPPHTTPLIB_NO_EXCEPTIONS5534// Needed to ensure that parameter names are unique during matcher5535// construction5536// If exceptions are disabled, only last duplicate path5537// parameter will be set5538std::unordered_set<std::string> param_name_set;5539#endif55405541while (true) {5542const auto marker_pos = pattern.find(marker, last_param_end);5543if (marker_pos == std::string::npos) { break; }55445545static_fragments_.push_back(5546pattern.substr(last_param_end, marker_pos - last_param_end));55475548const auto param_name_start = marker_pos + 1;55495550auto sep_pos = pattern.find(separator, param_name_start);5551if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }55525553auto param_name =5554pattern.substr(param_name_start, sep_pos - param_name_start);55555556#ifndef CPPHTTPLIB_NO_EXCEPTIONS5557if (param_name_set.find(param_name) != param_name_set.cend()) {5558std::string msg = "Encountered path parameter '" + param_name +5559"' multiple times in route pattern '" + pattern + "'.";5560throw std::invalid_argument(msg);5561}5562#endif55635564param_names_.push_back(std::move(param_name));55655566last_param_end = sep_pos + 1;5567}55685569if (last_param_end < pattern.length()) {5570static_fragments_.push_back(pattern.substr(last_param_end));5571}5572}55735574inline bool PathParamsMatcher::match(Request &request) const {5575request.matches = std::smatch();5576request.path_params.clear();5577request.path_params.reserve(param_names_.size());55785579// One past the position at which the path matched the pattern last time5580std::size_t starting_pos = 0;5581for (size_t i = 0; i < static_fragments_.size(); ++i) {5582const auto &fragment = static_fragments_[i];55835584if (starting_pos + fragment.length() > request.path.length()) {5585return false;5586}55875588// Avoid unnecessary allocation by using strncmp instead of substr +5589// comparison5590if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),5591fragment.length()) != 0) {5592return false;5593}55945595starting_pos += fragment.length();55965597// Should only happen when we have a static fragment after a param5598// Example: '/users/:id/subscriptions'5599// The 'subscriptions' fragment here does not have a corresponding param5600if (i >= param_names_.size()) { continue; }56015602auto sep_pos = request.path.find(separator, starting_pos);5603if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }56045605const auto ¶m_name = param_names_[i];56065607request.path_params.emplace(5608param_name, request.path.substr(starting_pos, sep_pos - starting_pos));56095610// Mark everythin up to '/' as matched5611starting_pos = sep_pos + 1;5612}5613// Returns false if the path is longer than the pattern5614return starting_pos >= request.path.length();5615}56165617inline bool RegexMatcher::match(Request &request) const {5618request.path_params.clear();5619return std::regex_match(request.path, request.matches, regex_);5620}56215622} // namespace detail56235624// HTTP server implementation5625inline Server::Server()5626: new_task_queue(5627[] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {5628#ifndef _WIN325629signal(SIGPIPE, SIG_IGN);5630#endif5631}56325633inline Server::~Server() = default;56345635inline std::unique_ptr<detail::MatcherBase>5636Server::make_matcher(const std::string &pattern) {5637if (pattern.find("/:") != std::string::npos) {5638return detail::make_unique<detail::PathParamsMatcher>(pattern);5639} else {5640return detail::make_unique<detail::RegexMatcher>(pattern);5641}5642}56435644inline Server &Server::Get(const std::string &pattern, Handler handler) {5645get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));5646return *this;5647}56485649inline Server &Server::Post(const std::string &pattern, Handler handler) {5650post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));5651return *this;5652}56535654inline Server &Server::Post(const std::string &pattern,5655HandlerWithContentReader handler) {5656post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),5657std::move(handler));5658return *this;5659}56605661inline Server &Server::Put(const std::string &pattern, Handler handler) {5662put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));5663return *this;5664}56655666inline Server &Server::Put(const std::string &pattern,5667HandlerWithContentReader handler) {5668put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),5669std::move(handler));5670return *this;5671}56725673inline Server &Server::Patch(const std::string &pattern, Handler handler) {5674patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));5675return *this;5676}56775678inline Server &Server::Patch(const std::string &pattern,5679HandlerWithContentReader handler) {5680patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),5681std::move(handler));5682return *this;5683}56845685inline Server &Server::Delete(const std::string &pattern, Handler handler) {5686delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));5687return *this;5688}56895690inline Server &Server::Delete(const std::string &pattern,5691HandlerWithContentReader handler) {5692delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),5693std::move(handler));5694return *this;5695}56965697inline Server &Server::Options(const std::string &pattern, Handler handler) {5698options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));5699return *this;5700}57015702inline bool Server::set_base_dir(const std::string &dir,5703const std::string &mount_point) {5704return set_mount_point(mount_point, dir);5705}57065707inline bool Server::set_mount_point(const std::string &mount_point,5708const std::string &dir, Headers headers) {5709if (detail::is_dir(dir)) {5710std::string mnt = !mount_point.empty() ? mount_point : "/";5711if (!mnt.empty() && mnt[0] == '/') {5712base_dirs_.push_back({mnt, dir, std::move(headers)});5713return true;5714}5715}5716return false;5717}57185719inline bool Server::remove_mount_point(const std::string &mount_point) {5720for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {5721if (it->mount_point == mount_point) {5722base_dirs_.erase(it);5723return true;5724}5725}5726return false;5727}57285729inline Server &5730Server::set_file_extension_and_mimetype_mapping(const std::string &ext,5731const std::string &mime) {5732file_extension_and_mimetype_map_[ext] = mime;5733return *this;5734}57355736inline Server &Server::set_default_file_mimetype(const std::string &mime) {5737default_file_mimetype_ = mime;5738return *this;5739}57405741inline Server &Server::set_file_request_handler(Handler handler) {5742file_request_handler_ = std::move(handler);5743return *this;5744}57455746inline Server &Server::set_error_handler(HandlerWithResponse handler) {5747error_handler_ = std::move(handler);5748return *this;5749}57505751inline Server &Server::set_error_handler(Handler handler) {5752error_handler_ = [handler](const Request &req, Response &res) {5753handler(req, res);5754return HandlerResponse::Handled;5755};5756return *this;5757}57585759inline Server &Server::set_exception_handler(ExceptionHandler handler) {5760exception_handler_ = std::move(handler);5761return *this;5762}57635764inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {5765pre_routing_handler_ = std::move(handler);5766return *this;5767}57685769inline Server &Server::set_post_routing_handler(Handler handler) {5770post_routing_handler_ = std::move(handler);5771return *this;5772}57735774inline Server &Server::set_logger(Logger logger) {5775logger_ = std::move(logger);5776return *this;5777}57785779inline Server &5780Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {5781expect_100_continue_handler_ = std::move(handler);5782return *this;5783}57845785inline Server &Server::set_address_family(int family) {5786address_family_ = family;5787return *this;5788}57895790inline Server &Server::set_tcp_nodelay(bool on) {5791tcp_nodelay_ = on;5792return *this;5793}57945795inline Server &Server::set_socket_options(SocketOptions socket_options) {5796socket_options_ = std::move(socket_options);5797return *this;5798}57995800inline Server &Server::set_default_headers(Headers headers) {5801default_headers_ = std::move(headers);5802return *this;5803}58045805inline Server &Server::set_header_writer(5806std::function<ssize_t(Stream &, Headers &)> const &writer) {5807header_writer_ = writer;5808return *this;5809}58105811inline Server &Server::set_keep_alive_max_count(size_t count) {5812keep_alive_max_count_ = count;5813return *this;5814}58155816inline Server &Server::set_keep_alive_timeout(time_t sec) {5817keep_alive_timeout_sec_ = sec;5818return *this;5819}58205821inline Server &Server::set_read_timeout(time_t sec, time_t usec) {5822read_timeout_sec_ = sec;5823read_timeout_usec_ = usec;5824return *this;5825}58265827inline Server &Server::set_write_timeout(time_t sec, time_t usec) {5828write_timeout_sec_ = sec;5829write_timeout_usec_ = usec;5830return *this;5831}58325833inline Server &Server::set_idle_interval(time_t sec, time_t usec) {5834idle_interval_sec_ = sec;5835idle_interval_usec_ = usec;5836return *this;5837}58385839inline Server &Server::set_payload_max_length(size_t length) {5840payload_max_length_ = length;5841return *this;5842}58435844inline bool Server::bind_to_port(const std::string &host, int port,5845int socket_flags) {5846return bind_internal(host, port, socket_flags) >= 0;5847}5848inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {5849return bind_internal(host, 0, socket_flags);5850}58515852inline bool Server::listen_after_bind() {5853auto se = detail::scope_exit([&]() { done_ = true; });5854return listen_internal();5855}58565857inline bool Server::listen(const std::string &host, int port,5858int socket_flags) {5859auto se = detail::scope_exit([&]() { done_ = true; });5860return bind_to_port(host, port, socket_flags) && listen_internal();5861}58625863inline bool Server::is_running() const { return is_running_; }58645865inline void Server::wait_until_ready() const {5866while (!is_running() && !done_) {5867std::this_thread::sleep_for(std::chrono::milliseconds{1});5868}5869}58705871inline void Server::stop() {5872if (is_running_) {5873assert(svr_sock_ != INVALID_SOCKET);5874std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));5875detail::shutdown_socket(sock);5876detail::close_socket(sock);5877}5878}58795880inline bool Server::parse_request_line(const char *s, Request &req) const {5881auto len = strlen(s);5882if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }5883len -= 2;58845885{5886size_t count = 0;58875888detail::split(s, s + len, ' ', [&](const char *b, const char *e) {5889switch (count) {5890case 0: req.method = std::string(b, e); break;5891case 1: req.target = std::string(b, e); break;5892case 2: req.version = std::string(b, e); break;5893default: break;5894}5895count++;5896});58975898if (count != 3) { return false; }5899}59005901static const std::set<std::string> methods{5902"GET", "HEAD", "POST", "PUT", "DELETE",5903"CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};59045905if (methods.find(req.method) == methods.end()) { return false; }59065907if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; }59085909{5910// Skip URL fragment5911for (size_t i = 0; i < req.target.size(); i++) {5912if (req.target[i] == '#') {5913req.target.erase(i);5914break;5915}5916}59175918size_t count = 0;59195920detail::split(req.target.data(), req.target.data() + req.target.size(), '?',59212, [&](const char *b, const char *e) {5922switch (count) {5923case 0:5924req.path = detail::decode_url(std::string(b, e), false);5925break;5926case 1: {5927if (e - b > 0) {5928detail::parse_query_text(std::string(b, e), req.params);5929}5930break;5931}5932default: break;5933}5934count++;5935});59365937if (count > 2) { return false; }5938}59395940return true;5941}59425943inline bool Server::write_response(Stream &strm, bool close_connection,5944const Request &req, Response &res) {5945return write_response_core(strm, close_connection, req, res, false);5946}59475948inline bool Server::write_response_with_content(Stream &strm,5949bool close_connection,5950const Request &req,5951Response &res) {5952return write_response_core(strm, close_connection, req, res, true);5953}59545955inline bool Server::write_response_core(Stream &strm, bool close_connection,5956const Request &req, Response &res,5957bool need_apply_ranges) {5958assert(res.status != -1);59595960if (400 <= res.status && error_handler_ &&5961error_handler_(req, res) == HandlerResponse::Handled) {5962need_apply_ranges = true;5963}59645965std::string content_type;5966std::string boundary;5967if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }59685969// Prepare additional headers5970if (close_connection || req.get_header_value("Connection") == "close") {5971res.set_header("Connection", "close");5972} else {5973std::stringstream ss;5974ss << "timeout=" << keep_alive_timeout_sec_5975<< ", max=" << keep_alive_max_count_;5976res.set_header("Keep-Alive", ss.str());5977}59785979if (!res.has_header("Content-Type") &&5980(!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) {5981res.set_header("Content-Type", "text/plain");5982}59835984if (!res.has_header("Content-Length") && res.body.empty() &&5985!res.content_length_ && !res.content_provider_) {5986res.set_header("Content-Length", "0");5987}59885989if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {5990res.set_header("Accept-Ranges", "bytes");5991}59925993if (post_routing_handler_) { post_routing_handler_(req, res); }59945995// Response line and headers5996{5997detail::BufferStream bstrm;59985999if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,6000status_message(res.status))) {6001return false;6002}60036004if (!header_writer_(bstrm, res.headers)) { return false; }60056006// Flush buffer6007auto &data = bstrm.get_buffer();6008detail::write_data(strm, data.data(), data.size());6009}60106011// Body6012auto ret = true;6013if (req.method != "HEAD") {6014if (!res.body.empty()) {6015if (!detail::write_data(strm, res.body.data(), res.body.size())) {6016ret = false;6017}6018} else if (res.content_provider_) {6019if (write_content_with_provider(strm, req, res, boundary, content_type)) {6020res.content_provider_success_ = true;6021} else {6022res.content_provider_success_ = false;6023ret = false;6024}6025}6026}60276028// Log6029if (logger_) { logger_(req, res); }60306031return ret;6032}60336034inline bool6035Server::write_content_with_provider(Stream &strm, const Request &req,6036Response &res, const std::string &boundary,6037const std::string &content_type) {6038auto is_shutting_down = [this]() {6039return this->svr_sock_ == INVALID_SOCKET;6040};60416042if (res.content_length_ > 0) {6043if (req.ranges.empty()) {6044return detail::write_content(strm, res.content_provider_, 0,6045res.content_length_, is_shutting_down);6046} else if (req.ranges.size() == 1) {6047auto offsets =6048detail::get_range_offset_and_length(req, res.content_length_, 0);6049auto offset = offsets.first;6050auto length = offsets.second;6051return detail::write_content(strm, res.content_provider_, offset, length,6052is_shutting_down);6053} else {6054return detail::write_multipart_ranges_data(6055strm, req, res, boundary, content_type, is_shutting_down);6056}6057} else {6058if (res.is_chunked_content_provider_) {6059auto type = detail::encoding_type(req, res);60606061std::unique_ptr<detail::compressor> compressor;6062if (type == detail::EncodingType::Gzip) {6063#ifdef CPPHTTPLIB_ZLIB_SUPPORT6064compressor = detail::make_unique<detail::gzip_compressor>();6065#endif6066} else if (type == detail::EncodingType::Brotli) {6067#ifdef CPPHTTPLIB_BROTLI_SUPPORT6068compressor = detail::make_unique<detail::brotli_compressor>();6069#endif6070} else {6071compressor = detail::make_unique<detail::nocompressor>();6072}6073assert(compressor != nullptr);60746075return detail::write_content_chunked(strm, res.content_provider_,6076is_shutting_down, *compressor);6077} else {6078return detail::write_content_without_length(strm, res.content_provider_,6079is_shutting_down);6080}6081}6082}60836084inline bool Server::read_content(Stream &strm, Request &req, Response &res) {6085MultipartFormDataMap::iterator cur;6086auto file_count = 0;6087if (read_content_core(6088strm, req, res,6089// Regular6090[&](const char *buf, size_t n) {6091if (req.body.size() + n > req.body.max_size()) { return false; }6092req.body.append(buf, n);6093return true;6094},6095// Multipart6096[&](const MultipartFormData &file) {6097if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {6098return false;6099}6100cur = req.files.emplace(file.name, file);6101return true;6102},6103[&](const char *buf, size_t n) {6104auto &content = cur->second.content;6105if (content.size() + n > content.max_size()) { return false; }6106content.append(buf, n);6107return true;6108})) {6109const auto &content_type = req.get_header_value("Content-Type");6110if (!content_type.find("application/x-www-form-urlencoded")) {6111if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {6112res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?6113return false;6114}6115detail::parse_query_text(req.body, req.params);6116}6117return true;6118}6119return false;6120}61216122inline bool Server::read_content_with_content_receiver(6123Stream &strm, Request &req, Response &res, ContentReceiver receiver,6124MultipartContentHeader multipart_header,6125ContentReceiver multipart_receiver) {6126return read_content_core(strm, req, res, std::move(receiver),6127std::move(multipart_header),6128std::move(multipart_receiver));6129}61306131inline bool6132Server::read_content_core(Stream &strm, Request &req, Response &res,6133ContentReceiver receiver,6134MultipartContentHeader multipart_header,6135ContentReceiver multipart_receiver) const {6136detail::MultipartFormDataParser multipart_form_data_parser;6137ContentReceiverWithProgress out;61386139if (req.is_multipart_form_data()) {6140const auto &content_type = req.get_header_value("Content-Type");6141std::string boundary;6142if (!detail::parse_multipart_boundary(content_type, boundary)) {6143res.status = StatusCode::BadRequest_400;6144return false;6145}61466147multipart_form_data_parser.set_boundary(std::move(boundary));6148out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {6149/* For debug6150size_t pos = 0;6151while (pos < n) {6152auto read_size = (std::min)<size_t>(1, n - pos);6153auto ret = multipart_form_data_parser.parse(6154buf + pos, read_size, multipart_receiver, multipart_header);6155if (!ret) { return false; }6156pos += read_size;6157}6158return true;6159*/6160return multipart_form_data_parser.parse(buf, n, multipart_receiver,6161multipart_header);6162};6163} else {6164out = [receiver](const char *buf, size_t n, uint64_t /*off*/,6165uint64_t /*len*/) { return receiver(buf, n); };6166}61676168if (req.method == "DELETE" && !req.has_header("Content-Length")) {6169return true;6170}61716172if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,6173out, true)) {6174return false;6175}61766177if (req.is_multipart_form_data()) {6178if (!multipart_form_data_parser.is_valid()) {6179res.status = StatusCode::BadRequest_400;6180return false;6181}6182}61836184return true;6185}61866187inline bool Server::handle_file_request(const Request &req, Response &res,6188bool head) {6189for (const auto &entry : base_dirs_) {6190// Prefix match6191if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {6192std::string sub_path = "/" + req.path.substr(entry.mount_point.size());6193if (detail::is_valid_path(sub_path)) {6194auto path = entry.base_dir + sub_path;6195if (path.back() == '/') { path += "index.html"; }61966197if (detail::is_file(path)) {6198for (const auto &kv : entry.headers) {6199res.set_header(kv.first, kv.second);6200}62016202auto mm = std::make_shared<detail::mmap>(path.c_str());6203if (!mm->is_open()) { return false; }62046205res.set_content_provider(6206mm->size(),6207detail::find_content_type(path, file_extension_and_mimetype_map_,6208default_file_mimetype_),6209[mm](size_t offset, size_t length, DataSink &sink) -> bool {6210sink.write(mm->data() + offset, length);6211return true;6212});62136214if (!head && file_request_handler_) {6215file_request_handler_(req, res);6216}62176218return true;6219}6220}6221}6222}6223return false;6224}62256226inline socket_t6227Server::create_server_socket(const std::string &host, int port,6228int socket_flags,6229SocketOptions socket_options) const {6230return detail::create_socket(6231host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,6232std::move(socket_options),6233[](socket_t sock, struct addrinfo &ai) -> bool {6234if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {6235return false;6236}6237if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; }6238return true;6239});6240}62416242inline int Server::bind_internal(const std::string &host, int port,6243int socket_flags) {6244if (!is_valid()) { return -1; }62456246svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);6247if (svr_sock_ == INVALID_SOCKET) { return -1; }62486249if (port == 0) {6250struct sockaddr_storage addr;6251socklen_t addr_len = sizeof(addr);6252if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),6253&addr_len) == -1) {6254return -1;6255}6256if (addr.ss_family == AF_INET) {6257return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);6258} else if (addr.ss_family == AF_INET6) {6259return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);6260} else {6261return -1;6262}6263} else {6264return port;6265}6266}62676268inline bool Server::listen_internal() {6269auto ret = true;6270is_running_ = true;6271auto se = detail::scope_exit([&]() { is_running_ = false; });62726273{6274std::unique_ptr<TaskQueue> task_queue(new_task_queue());62756276while (svr_sock_ != INVALID_SOCKET) {6277#ifndef _WIN326278if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {6279#endif6280auto val = detail::select_read(svr_sock_, idle_interval_sec_,6281idle_interval_usec_);6282if (val == 0) { // Timeout6283task_queue->on_idle();6284continue;6285}6286#ifndef _WIN326287}6288#endif6289socket_t sock = accept(svr_sock_, nullptr, nullptr);62906291if (sock == INVALID_SOCKET) {6292if (errno == EMFILE) {6293// The per-process limit of open file descriptors has been reached.6294// Try to accept new connections after a short sleep.6295std::this_thread::sleep_for(std::chrono::milliseconds(1));6296continue;6297} else if (errno == EINTR || errno == EAGAIN) {6298continue;6299}6300if (svr_sock_ != INVALID_SOCKET) {6301detail::close_socket(svr_sock_);6302ret = false;6303} else {6304; // The server socket was closed by user.6305}6306break;6307}63086309{6310#ifdef _WIN326311auto timeout = static_cast<uint32_t>(read_timeout_sec_ * 1000 +6312read_timeout_usec_ / 1000);6313setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,6314reinterpret_cast<const char *>(&timeout), sizeof(timeout));6315#else6316timeval tv;6317tv.tv_sec = static_cast<long>(read_timeout_sec_);6318tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec_);6319setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,6320reinterpret_cast<const void *>(&tv), sizeof(tv));6321#endif6322}6323{63246325#ifdef _WIN326326auto timeout = static_cast<uint32_t>(write_timeout_sec_ * 1000 +6327write_timeout_usec_ / 1000);6328setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,6329reinterpret_cast<const char *>(&timeout), sizeof(timeout));6330#else6331timeval tv;6332tv.tv_sec = static_cast<long>(write_timeout_sec_);6333tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec_);6334setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,6335reinterpret_cast<const void *>(&tv), sizeof(tv));6336#endif6337}63386339if (!task_queue->enqueue(6340[this, sock]() { process_and_close_socket(sock); })) {6341detail::shutdown_socket(sock);6342detail::close_socket(sock);6343}6344}63456346task_queue->shutdown();6347}63486349return ret;6350}63516352inline bool Server::routing(Request &req, Response &res, Stream &strm) {6353if (pre_routing_handler_ &&6354pre_routing_handler_(req, res) == HandlerResponse::Handled) {6355return true;6356}63576358// File handler6359auto is_head_request = req.method == "HEAD";6360if ((req.method == "GET" || is_head_request) &&6361handle_file_request(req, res, is_head_request)) {6362return true;6363}63646365if (detail::expect_content(req)) {6366// Content reader handler6367{6368ContentReader reader(6369[&](ContentReceiver receiver) {6370return read_content_with_content_receiver(6371strm, req, res, std::move(receiver), nullptr, nullptr);6372},6373[&](MultipartContentHeader header, ContentReceiver receiver) {6374return read_content_with_content_receiver(strm, req, res, nullptr,6375std::move(header),6376std::move(receiver));6377});63786379if (req.method == "POST") {6380if (dispatch_request_for_content_reader(6381req, res, std::move(reader),6382post_handlers_for_content_reader_)) {6383return true;6384}6385} else if (req.method == "PUT") {6386if (dispatch_request_for_content_reader(6387req, res, std::move(reader),6388put_handlers_for_content_reader_)) {6389return true;6390}6391} else if (req.method == "PATCH") {6392if (dispatch_request_for_content_reader(6393req, res, std::move(reader),6394patch_handlers_for_content_reader_)) {6395return true;6396}6397} else if (req.method == "DELETE") {6398if (dispatch_request_for_content_reader(6399req, res, std::move(reader),6400delete_handlers_for_content_reader_)) {6401return true;6402}6403}6404}64056406// Read content into `req.body`6407if (!read_content(strm, req, res)) { return false; }6408}64096410// Regular handler6411if (req.method == "GET" || req.method == "HEAD") {6412return dispatch_request(req, res, get_handlers_);6413} else if (req.method == "POST") {6414return dispatch_request(req, res, post_handlers_);6415} else if (req.method == "PUT") {6416return dispatch_request(req, res, put_handlers_);6417} else if (req.method == "DELETE") {6418return dispatch_request(req, res, delete_handlers_);6419} else if (req.method == "OPTIONS") {6420return dispatch_request(req, res, options_handlers_);6421} else if (req.method == "PATCH") {6422return dispatch_request(req, res, patch_handlers_);6423}64246425res.status = StatusCode::BadRequest_400;6426return false;6427}64286429inline bool Server::dispatch_request(Request &req, Response &res,6430const Handlers &handlers) const {6431for (const auto &x : handlers) {6432const auto &matcher = x.first;6433const auto &handler = x.second;64346435if (matcher->match(req)) {6436handler(req, res);6437return true;6438}6439}6440return false;6441}64426443inline void Server::apply_ranges(const Request &req, Response &res,6444std::string &content_type,6445std::string &boundary) const {6446if (req.ranges.size() > 1) {6447boundary = detail::make_multipart_data_boundary();64486449auto it = res.headers.find("Content-Type");6450if (it != res.headers.end()) {6451content_type = it->second;6452res.headers.erase(it);6453}64546455res.set_header("Content-Type",6456"multipart/byteranges; boundary=" + boundary);6457}64586459auto type = detail::encoding_type(req, res);64606461if (res.body.empty()) {6462if (res.content_length_ > 0) {6463size_t length = 0;6464if (req.ranges.empty()) {6465length = res.content_length_;6466} else if (req.ranges.size() == 1) {6467auto offsets =6468detail::get_range_offset_and_length(req, res.content_length_, 0);6469length = offsets.second;64706471auto content_range = detail::make_content_range_header_field(6472req.ranges[0], res.content_length_);6473res.set_header("Content-Range", content_range);6474} else {6475length = detail::get_multipart_ranges_data_length(req, res, boundary,6476content_type);6477}6478res.set_header("Content-Length", std::to_string(length));6479} else {6480if (res.content_provider_) {6481if (res.is_chunked_content_provider_) {6482res.set_header("Transfer-Encoding", "chunked");6483if (type == detail::EncodingType::Gzip) {6484res.set_header("Content-Encoding", "gzip");6485} else if (type == detail::EncodingType::Brotli) {6486res.set_header("Content-Encoding", "br");6487}6488}6489}6490}6491} else {6492if (req.ranges.empty()) {6493;6494} else if (req.ranges.size() == 1) {6495auto content_range = detail::make_content_range_header_field(6496req.ranges[0], res.body.size());6497res.set_header("Content-Range", content_range);64986499auto offsets =6500detail::get_range_offset_and_length(req, res.body.size(), 0);6501auto offset = offsets.first;6502auto length = offsets.second;65036504if (offset < res.body.size()) {6505res.body = res.body.substr(offset, length);6506} else {6507res.body.clear();6508res.status = StatusCode::RangeNotSatisfiable_416;6509}6510} else {6511std::string data;6512if (detail::make_multipart_ranges_data(req, res, boundary, content_type,6513data)) {6514res.body.swap(data);6515} else {6516res.body.clear();6517res.status = StatusCode::RangeNotSatisfiable_416;6518}6519}65206521if (type != detail::EncodingType::None) {6522std::unique_ptr<detail::compressor> compressor;6523std::string content_encoding;65246525if (type == detail::EncodingType::Gzip) {6526#ifdef CPPHTTPLIB_ZLIB_SUPPORT6527compressor = detail::make_unique<detail::gzip_compressor>();6528content_encoding = "gzip";6529#endif6530} else if (type == detail::EncodingType::Brotli) {6531#ifdef CPPHTTPLIB_BROTLI_SUPPORT6532compressor = detail::make_unique<detail::brotli_compressor>();6533content_encoding = "br";6534#endif6535}65366537if (compressor) {6538std::string compressed;6539if (compressor->compress(res.body.data(), res.body.size(), true,6540[&](const char *data, size_t data_len) {6541compressed.append(data, data_len);6542return true;6543})) {6544res.body.swap(compressed);6545res.set_header("Content-Encoding", content_encoding);6546}6547}6548}65496550auto length = std::to_string(res.body.size());6551res.set_header("Content-Length", length);6552}6553}65546555inline bool Server::dispatch_request_for_content_reader(6556Request &req, Response &res, ContentReader content_reader,6557const HandlersForContentReader &handlers) const {6558for (const auto &x : handlers) {6559const auto &matcher = x.first;6560const auto &handler = x.second;65616562if (matcher->match(req)) {6563handler(req, res, content_reader);6564return true;6565}6566}6567return false;6568}65696570inline bool6571Server::process_request(Stream &strm, bool close_connection,6572bool &connection_closed,6573const std::function<void(Request &)> &setup_request) {6574std::array<char, 2048> buf{};65756576detail::stream_line_reader line_reader(strm, buf.data(), buf.size());65776578// Connection has been closed on client6579if (!line_reader.getline()) { return false; }65806581Request req;65826583Response res;6584res.version = "HTTP/1.1";6585res.headers = default_headers_;65866587#ifdef _WIN326588// TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL).6589#else6590#ifndef CPPHTTPLIB_USE_POLL6591// Socket file descriptor exceeded FD_SETSIZE...6592if (strm.socket() >= FD_SETSIZE) {6593Headers dummy;6594detail::read_headers(strm, dummy);6595res.status = StatusCode::InternalServerError_500;6596return write_response(strm, close_connection, req, res);6597}6598#endif6599#endif66006601// Check if the request URI doesn't exceed the limit6602if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {6603Headers dummy;6604detail::read_headers(strm, dummy);6605res.status = StatusCode::UriTooLong_414;6606return write_response(strm, close_connection, req, res);6607}66086609// Request line and headers6610if (!parse_request_line(line_reader.ptr(), req) ||6611!detail::read_headers(strm, req.headers)) {6612res.status = StatusCode::BadRequest_400;6613return write_response(strm, close_connection, req, res);6614}66156616if (req.get_header_value("Connection") == "close") {6617connection_closed = true;6618}66196620if (req.version == "HTTP/1.0" &&6621req.get_header_value("Connection") != "Keep-Alive") {6622connection_closed = true;6623}66246625strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);6626req.set_header("REMOTE_ADDR", req.remote_addr);6627req.set_header("REMOTE_PORT", std::to_string(req.remote_port));66286629strm.get_local_ip_and_port(req.local_addr, req.local_port);6630req.set_header("LOCAL_ADDR", req.local_addr);6631req.set_header("LOCAL_PORT", std::to_string(req.local_port));66326633if (req.has_header("Range")) {6634const auto &range_header_value = req.get_header_value("Range");6635if (!detail::parse_range_header(range_header_value, req.ranges)) {6636res.status = StatusCode::RangeNotSatisfiable_416;6637return write_response(strm, close_connection, req, res);6638}6639}66406641if (setup_request) { setup_request(req); }66426643if (req.get_header_value("Expect") == "100-continue") {6644int status = StatusCode::Continue_100;6645if (expect_100_continue_handler_) {6646status = expect_100_continue_handler_(req, res);6647}6648switch (status) {6649case StatusCode::Continue_100:6650case StatusCode::ExpectationFailed_417:6651strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status,6652status_message(status));6653break;6654default: return write_response(strm, close_connection, req, res);6655}6656}66576658// Rounting6659auto routed = false;6660#ifdef CPPHTTPLIB_NO_EXCEPTIONS6661routed = routing(req, res, strm);6662#else6663try {6664routed = routing(req, res, strm);6665} catch (std::exception &e) {6666if (exception_handler_) {6667auto ep = std::current_exception();6668exception_handler_(req, res, ep);6669routed = true;6670} else {6671res.status = StatusCode::InternalServerError_500;6672std::string val;6673auto s = e.what();6674for (size_t i = 0; s[i]; i++) {6675switch (s[i]) {6676case '\r': val += "\\r"; break;6677case '\n': val += "\\n"; break;6678default: val += s[i]; break;6679}6680}6681res.set_header("EXCEPTION_WHAT", val);6682}6683} catch (...) {6684if (exception_handler_) {6685auto ep = std::current_exception();6686exception_handler_(req, res, ep);6687routed = true;6688} else {6689res.status = StatusCode::InternalServerError_500;6690res.set_header("EXCEPTION_WHAT", "UNKNOWN");6691}6692}6693#endif66946695if (routed) {6696if (res.status == -1) {6697res.status = req.ranges.empty() ? StatusCode::OK_2006698: StatusCode::PartialContent_206;6699}6700return write_response_with_content(strm, close_connection, req, res);6701} else {6702if (res.status == -1) { res.status = StatusCode::NotFound_404; }6703return write_response(strm, close_connection, req, res);6704}6705}67066707inline bool Server::is_valid() const { return true; }67086709inline bool Server::process_and_close_socket(socket_t sock) {6710auto ret = detail::process_server_socket(6711svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,6712read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,6713write_timeout_usec_,6714[this](Stream &strm, bool close_connection, bool &connection_closed) {6715return process_request(strm, close_connection, connection_closed,6716nullptr);6717});67186719detail::shutdown_socket(sock);6720detail::close_socket(sock);6721return ret;6722}67236724// HTTP client implementation6725inline ClientImpl::ClientImpl(const std::string &host)6726: ClientImpl(host, 80, std::string(), std::string()) {}67276728inline ClientImpl::ClientImpl(const std::string &host, int port)6729: ClientImpl(host, port, std::string(), std::string()) {}67306731inline ClientImpl::ClientImpl(const std::string &host, int port,6732const std::string &client_cert_path,6733const std::string &client_key_path)6734: host_(host), port_(port),6735host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)),6736client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}67376738inline ClientImpl::~ClientImpl() {6739std::lock_guard<std::mutex> guard(socket_mutex_);6740shutdown_socket(socket_);6741close_socket(socket_);6742}67436744inline bool ClientImpl::is_valid() const { return true; }67456746inline void ClientImpl::copy_settings(const ClientImpl &rhs) {6747client_cert_path_ = rhs.client_cert_path_;6748client_key_path_ = rhs.client_key_path_;6749connection_timeout_sec_ = rhs.connection_timeout_sec_;6750read_timeout_sec_ = rhs.read_timeout_sec_;6751read_timeout_usec_ = rhs.read_timeout_usec_;6752write_timeout_sec_ = rhs.write_timeout_sec_;6753write_timeout_usec_ = rhs.write_timeout_usec_;6754basic_auth_username_ = rhs.basic_auth_username_;6755basic_auth_password_ = rhs.basic_auth_password_;6756bearer_token_auth_token_ = rhs.bearer_token_auth_token_;6757#ifdef CPPHTTPLIB_OPENSSL_SUPPORT6758digest_auth_username_ = rhs.digest_auth_username_;6759digest_auth_password_ = rhs.digest_auth_password_;6760#endif6761keep_alive_ = rhs.keep_alive_;6762follow_location_ = rhs.follow_location_;6763url_encode_ = rhs.url_encode_;6764address_family_ = rhs.address_family_;6765tcp_nodelay_ = rhs.tcp_nodelay_;6766socket_options_ = rhs.socket_options_;6767compress_ = rhs.compress_;6768decompress_ = rhs.decompress_;6769interface_ = rhs.interface_;6770proxy_host_ = rhs.proxy_host_;6771proxy_port_ = rhs.proxy_port_;6772proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;6773proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;6774proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;6775#ifdef CPPHTTPLIB_OPENSSL_SUPPORT6776proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;6777proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;6778#endif6779#ifdef CPPHTTPLIB_OPENSSL_SUPPORT6780ca_cert_file_path_ = rhs.ca_cert_file_path_;6781ca_cert_dir_path_ = rhs.ca_cert_dir_path_;6782ca_cert_store_ = rhs.ca_cert_store_;6783#endif6784#ifdef CPPHTTPLIB_OPENSSL_SUPPORT6785server_certificate_verification_ = rhs.server_certificate_verification_;6786#endif6787logger_ = rhs.logger_;6788}67896790inline socket_t ClientImpl::create_client_socket(Error &error) const {6791if (!proxy_host_.empty() && proxy_port_ != -1) {6792return detail::create_client_socket(6793proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,6794socket_options_, connection_timeout_sec_, connection_timeout_usec_,6795read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,6796write_timeout_usec_, interface_, error);6797}67986799// Check is custom IP specified for host_6800std::string ip;6801auto it = addr_map_.find(host_);6802if (it != addr_map_.end()) { ip = it->second; }68036804return detail::create_client_socket(6805host_, ip, port_, address_family_, tcp_nodelay_, socket_options_,6806connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_,6807read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_,6808error);6809}68106811inline bool ClientImpl::create_and_connect_socket(Socket &socket,6812Error &error) {6813auto sock = create_client_socket(error);6814if (sock == INVALID_SOCKET) { return false; }6815socket.sock = sock;6816return true;6817}68186819inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,6820bool /*shutdown_gracefully*/) {6821// If there are any requests in flight from threads other than us, then it's6822// a thread-unsafe race because individual ssl* objects are not thread-safe.6823assert(socket_requests_in_flight_ == 0 ||6824socket_requests_are_from_thread_ == std::this_thread::get_id());6825}68266827inline void ClientImpl::shutdown_socket(Socket &socket) const {6828if (socket.sock == INVALID_SOCKET) { return; }6829detail::shutdown_socket(socket.sock);6830}68316832inline void ClientImpl::close_socket(Socket &socket) {6833// If there are requests in flight in another thread, usually closing6834// the socket will be fine and they will simply receive an error when6835// using the closed socket, but it is still a bug since rarely the OS6836// may reassign the socket id to be used for a new socket, and then6837// suddenly they will be operating on a live socket that is different6838// than the one they intended!6839assert(socket_requests_in_flight_ == 0 ||6840socket_requests_are_from_thread_ == std::this_thread::get_id());68416842// It is also a bug if this happens while SSL is still active6843#ifdef CPPHTTPLIB_OPENSSL_SUPPORT6844assert(socket.ssl == nullptr);6845#endif6846if (socket.sock == INVALID_SOCKET) { return; }6847detail::close_socket(socket.sock);6848socket.sock = INVALID_SOCKET;6849}68506851inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,6852Response &res) const {6853std::array<char, 2048> buf{};68546855detail::stream_line_reader line_reader(strm, buf.data(), buf.size());68566857if (!line_reader.getline()) { return false; }68586859#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR6860const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");6861#else6862const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");6863#endif68646865std::cmatch m;6866if (!std::regex_match(line_reader.ptr(), m, re)) {6867return req.method == "CONNECT";6868}6869res.version = std::string(m[1]);6870res.status = std::stoi(std::string(m[2]));6871res.reason = std::string(m[3]);68726873// Ignore '100 Continue'6874while (res.status == StatusCode::Continue_100) {6875if (!line_reader.getline()) { return false; } // CRLF6876if (!line_reader.getline()) { return false; } // next response line68776878if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }6879res.version = std::string(m[1]);6880res.status = std::stoi(std::string(m[2]));6881res.reason = std::string(m[3]);6882}68836884return true;6885}68866887inline bool ClientImpl::send(Request &req, Response &res, Error &error) {6888std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);6889auto ret = send_(req, res, error);6890if (error == Error::SSLPeerCouldBeClosed_) {6891assert(!ret);6892ret = send_(req, res, error);6893}6894return ret;6895}68966897inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {6898{6899std::lock_guard<std::mutex> guard(socket_mutex_);69006901// Set this to false immediately - if it ever gets set to true by the end of6902// the request, we know another thread instructed us to close the socket.6903socket_should_be_closed_when_request_is_done_ = false;69046905auto is_alive = false;6906if (socket_.is_open()) {6907is_alive = detail::is_socket_alive(socket_.sock);6908if (!is_alive) {6909// Attempt to avoid sigpipe by shutting down nongracefully if it seems6910// like the other side has already closed the connection Also, there6911// cannot be any requests in flight from other threads since we locked6912// request_mutex_, so safe to close everything immediately6913const bool shutdown_gracefully = false;6914shutdown_ssl(socket_, shutdown_gracefully);6915shutdown_socket(socket_);6916close_socket(socket_);6917}6918}69196920if (!is_alive) {6921if (!create_and_connect_socket(socket_, error)) { return false; }69226923#ifdef CPPHTTPLIB_OPENSSL_SUPPORT6924// TODO: refactoring6925if (is_ssl()) {6926auto &scli = static_cast<SSLClient &>(*this);6927if (!proxy_host_.empty() && proxy_port_ != -1) {6928auto success = false;6929if (!scli.connect_with_proxy(socket_, res, success, error)) {6930return success;6931}6932}69336934if (!scli.initialize_ssl(socket_, error)) { return false; }6935}6936#endif6937}69386939// Mark the current socket as being in use so that it cannot be closed by6940// anyone else while this request is ongoing, even though we will be6941// releasing the mutex.6942if (socket_requests_in_flight_ > 1) {6943assert(socket_requests_are_from_thread_ == std::this_thread::get_id());6944}6945socket_requests_in_flight_ += 1;6946socket_requests_are_from_thread_ = std::this_thread::get_id();6947}69486949for (const auto &header : default_headers_) {6950if (req.headers.find(header.first) == req.headers.end()) {6951req.headers.insert(header);6952}6953}69546955auto ret = false;6956auto close_connection = !keep_alive_;69576958auto se = detail::scope_exit([&]() {6959// Briefly lock mutex in order to mark that a request is no longer ongoing6960std::lock_guard<std::mutex> guard(socket_mutex_);6961socket_requests_in_flight_ -= 1;6962if (socket_requests_in_flight_ <= 0) {6963assert(socket_requests_in_flight_ == 0);6964socket_requests_are_from_thread_ = std::thread::id();6965}69666967if (socket_should_be_closed_when_request_is_done_ || close_connection ||6968!ret) {6969shutdown_ssl(socket_, true);6970shutdown_socket(socket_);6971close_socket(socket_);6972}6973});69746975ret = process_socket(socket_, [&](Stream &strm) {6976return handle_request(strm, req, res, close_connection, error);6977});69786979if (!ret) {6980if (error == Error::Success) { error = Error::Unknown; }6981}69826983return ret;6984}69856986inline Result ClientImpl::send(const Request &req) {6987auto req2 = req;6988return send_(std::move(req2));6989}69906991inline Result ClientImpl::send_(Request &&req) {6992auto res = detail::make_unique<Response>();6993auto error = Error::Success;6994auto ret = send(req, *res, error);6995return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};6996}69976998inline bool ClientImpl::handle_request(Stream &strm, Request &req,6999Response &res, bool close_connection,7000Error &error) {7001if (req.path.empty()) {7002error = Error::Connection;7003return false;7004}70057006auto req_save = req;70077008bool ret;70097010if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {7011auto req2 = req;7012req2.path = "http://" + host_and_port_ + req.path;7013ret = process_request(strm, req2, res, close_connection, error);7014req = req2;7015req.path = req_save.path;7016} else {7017ret = process_request(strm, req, res, close_connection, error);7018}70197020if (!ret) { return false; }70217022if (res.get_header_value("Connection") == "close" ||7023(res.version == "HTTP/1.0" && res.reason != "Connection established")) {7024// TODO this requires a not-entirely-obvious chain of calls to be correct7025// for this to be safe.70267027// This is safe to call because handle_request is only called by send_7028// which locks the request mutex during the process. It would be a bug7029// to call it from a different thread since it's a thread-safety issue7030// to do these things to the socket if another thread is using the socket.7031std::lock_guard<std::mutex> guard(socket_mutex_);7032shutdown_ssl(socket_, true);7033shutdown_socket(socket_);7034close_socket(socket_);7035}70367037if (300 < res.status && res.status < 400 && follow_location_) {7038req = req_save;7039ret = redirect(req, res, error);7040}70417042#ifdef CPPHTTPLIB_OPENSSL_SUPPORT7043if ((res.status == StatusCode::Unauthorized_401 ||7044res.status == StatusCode::ProxyAuthenticationRequired_407) &&7045req.authorization_count_ < 5) {7046auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;7047const auto &username =7048is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;7049const auto &password =7050is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;70517052if (!username.empty() && !password.empty()) {7053std::map<std::string, std::string> auth;7054if (detail::parse_www_authenticate(res, auth, is_proxy)) {7055Request new_req = req;7056new_req.authorization_count_ += 1;7057new_req.headers.erase(is_proxy ? "Proxy-Authorization"7058: "Authorization");7059new_req.headers.insert(detail::make_digest_authentication_header(7060req, auth, new_req.authorization_count_, detail::random_string(10),7061username, password, is_proxy));70627063Response new_res;70647065ret = send(new_req, new_res, error);7066if (ret) { res = new_res; }7067}7068}7069}7070#endif70717072return ret;7073}70747075inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {7076if (req.redirect_count_ == 0) {7077error = Error::ExceedRedirectCount;7078return false;7079}70807081auto location = res.get_header_value("location");7082if (location.empty()) { return false; }70837084const static std::regex re(7085R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");70867087std::smatch m;7088if (!std::regex_match(location, m, re)) { return false; }70897090auto scheme = is_ssl() ? "https" : "http";70917092auto next_scheme = m[1].str();7093auto next_host = m[2].str();7094if (next_host.empty()) { next_host = m[3].str(); }7095auto port_str = m[4].str();7096auto next_path = m[5].str();7097auto next_query = m[6].str();70987099auto next_port = port_;7100if (!port_str.empty()) {7101next_port = std::stoi(port_str);7102} else if (!next_scheme.empty()) {7103next_port = next_scheme == "https" ? 443 : 80;7104}71057106if (next_scheme.empty()) { next_scheme = scheme; }7107if (next_host.empty()) { next_host = host_; }7108if (next_path.empty()) { next_path = "/"; }71097110auto path = detail::decode_url(next_path, true) + next_query;71117112if (next_scheme == scheme && next_host == host_ && next_port == port_) {7113return detail::redirect(*this, req, res, path, location, error);7114} else {7115if (next_scheme == "https") {7116#ifdef CPPHTTPLIB_OPENSSL_SUPPORT7117SSLClient cli(next_host, next_port);7118cli.copy_settings(*this);7119if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }7120return detail::redirect(cli, req, res, path, location, error);7121#else7122return false;7123#endif7124} else {7125ClientImpl cli(next_host, next_port);7126cli.copy_settings(*this);7127return detail::redirect(cli, req, res, path, location, error);7128}7129}7130}71317132inline bool ClientImpl::write_content_with_provider(Stream &strm,7133const Request &req,7134Error &error) const {7135auto is_shutting_down = []() { return false; };71367137if (req.is_chunked_content_provider_) {7138// TODO: Brotli support7139std::unique_ptr<detail::compressor> compressor;7140#ifdef CPPHTTPLIB_ZLIB_SUPPORT7141if (compress_) {7142compressor = detail::make_unique<detail::gzip_compressor>();7143} else7144#endif7145{7146compressor = detail::make_unique<detail::nocompressor>();7147}71487149return detail::write_content_chunked(strm, req.content_provider_,7150is_shutting_down, *compressor, error);7151} else {7152return detail::write_content(strm, req.content_provider_, 0,7153req.content_length_, is_shutting_down, error);7154}7155}71567157inline bool ClientImpl::write_request(Stream &strm, Request &req,7158bool close_connection, Error &error) {7159// Prepare additional headers7160if (close_connection) {7161if (!req.has_header("Connection")) {7162req.set_header("Connection", "close");7163}7164}71657166if (!req.has_header("Host")) {7167if (is_ssl()) {7168if (port_ == 443) {7169req.set_header("Host", host_);7170} else {7171req.set_header("Host", host_and_port_);7172}7173} else {7174if (port_ == 80) {7175req.set_header("Host", host_);7176} else {7177req.set_header("Host", host_and_port_);7178}7179}7180}71817182if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); }71837184#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT7185if (!req.has_header("User-Agent")) {7186auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;7187req.set_header("User-Agent", agent);7188}7189#endif71907191if (req.body.empty()) {7192if (req.content_provider_) {7193if (!req.is_chunked_content_provider_) {7194if (!req.has_header("Content-Length")) {7195auto length = std::to_string(req.content_length_);7196req.set_header("Content-Length", length);7197}7198}7199} else {7200if (req.method == "POST" || req.method == "PUT" ||7201req.method == "PATCH") {7202req.set_header("Content-Length", "0");7203}7204}7205} else {7206if (!req.has_header("Content-Type")) {7207req.set_header("Content-Type", "text/plain");7208}72097210if (!req.has_header("Content-Length")) {7211auto length = std::to_string(req.body.size());7212req.set_header("Content-Length", length);7213}7214}72157216if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {7217if (!req.has_header("Authorization")) {7218req.headers.insert(make_basic_authentication_header(7219basic_auth_username_, basic_auth_password_, false));7220}7221}72227223if (!proxy_basic_auth_username_.empty() &&7224!proxy_basic_auth_password_.empty()) {7225if (!req.has_header("Proxy-Authorization")) {7226req.headers.insert(make_basic_authentication_header(7227proxy_basic_auth_username_, proxy_basic_auth_password_, true));7228}7229}72307231if (!bearer_token_auth_token_.empty()) {7232if (!req.has_header("Authorization")) {7233req.headers.insert(make_bearer_token_authentication_header(7234bearer_token_auth_token_, false));7235}7236}72377238if (!proxy_bearer_token_auth_token_.empty()) {7239if (!req.has_header("Proxy-Authorization")) {7240req.headers.insert(make_bearer_token_authentication_header(7241proxy_bearer_token_auth_token_, true));7242}7243}72447245// Request line and headers7246{7247detail::BufferStream bstrm;72487249const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path;7250bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());72517252header_writer_(bstrm, req.headers);72537254// Flush buffer7255auto &data = bstrm.get_buffer();7256if (!detail::write_data(strm, data.data(), data.size())) {7257error = Error::Write;7258return false;7259}7260}72617262// Body7263if (req.body.empty()) {7264return write_content_with_provider(strm, req, error);7265}72667267if (!detail::write_data(strm, req.body.data(), req.body.size())) {7268error = Error::Write;7269return false;7270}72717272return true;7273}72747275inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(7276Request &req, const char *body, size_t content_length,7277ContentProvider content_provider,7278ContentProviderWithoutLength content_provider_without_length,7279const std::string &content_type, Error &error) {7280if (!content_type.empty()) { req.set_header("Content-Type", content_type); }72817282#ifdef CPPHTTPLIB_ZLIB_SUPPORT7283if (compress_) { req.set_header("Content-Encoding", "gzip"); }7284#endif72857286#ifdef CPPHTTPLIB_ZLIB_SUPPORT7287if (compress_ && !content_provider_without_length) {7288// TODO: Brotli support7289detail::gzip_compressor compressor;72907291if (content_provider) {7292auto ok = true;7293size_t offset = 0;7294DataSink data_sink;72957296data_sink.write = [&](const char *data, size_t data_len) -> bool {7297if (ok) {7298auto last = offset + data_len == content_length;72997300auto ret = compressor.compress(7301data, data_len, last,7302[&](const char *compressed_data, size_t compressed_data_len) {7303req.body.append(compressed_data, compressed_data_len);7304return true;7305});73067307if (ret) {7308offset += data_len;7309} else {7310ok = false;7311}7312}7313return ok;7314};73157316while (ok && offset < content_length) {7317if (!content_provider(offset, content_length - offset, data_sink)) {7318error = Error::Canceled;7319return nullptr;7320}7321}7322} else {7323if (!compressor.compress(body, content_length, true,7324[&](const char *data, size_t data_len) {7325req.body.append(data, data_len);7326return true;7327})) {7328error = Error::Compression;7329return nullptr;7330}7331}7332} else7333#endif7334{7335if (content_provider) {7336req.content_length_ = content_length;7337req.content_provider_ = std::move(content_provider);7338req.is_chunked_content_provider_ = false;7339} else if (content_provider_without_length) {7340req.content_length_ = 0;7341req.content_provider_ = detail::ContentProviderAdapter(7342std::move(content_provider_without_length));7343req.is_chunked_content_provider_ = true;7344req.set_header("Transfer-Encoding", "chunked");7345} else {7346req.body.assign(body, content_length);7347}7348}73497350auto res = detail::make_unique<Response>();7351return send(req, *res, error) ? std::move(res) : nullptr;7352}73537354inline Result ClientImpl::send_with_content_provider(7355const std::string &method, const std::string &path, const Headers &headers,7356const char *body, size_t content_length, ContentProvider content_provider,7357ContentProviderWithoutLength content_provider_without_length,7358const std::string &content_type) {7359Request req;7360req.method = method;7361req.headers = headers;7362req.path = path;73637364auto error = Error::Success;73657366auto res = send_with_content_provider(7367req, body, content_length, std::move(content_provider),7368std::move(content_provider_without_length), content_type, error);73697370return Result{std::move(res), error, std::move(req.headers)};7371}73727373inline std::string7374ClientImpl::adjust_host_string(const std::string &host) const {7375if (host.find(':') != std::string::npos) { return "[" + host + "]"; }7376return host;7377}73787379inline bool ClientImpl::process_request(Stream &strm, Request &req,7380Response &res, bool close_connection,7381Error &error) {7382// Send request7383if (!write_request(strm, req, close_connection, error)) { return false; }73847385#ifdef CPPHTTPLIB_OPENSSL_SUPPORT7386if (is_ssl()) {7387auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;7388if (!is_proxy_enabled) {7389char buf[1];7390if (SSL_peek(socket_.ssl, buf, 1) == 0 &&7391SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) {7392error = Error::SSLPeerCouldBeClosed_;7393return false;7394}7395}7396}7397#endif73987399// Receive response and headers7400if (!read_response_line(strm, req, res) ||7401!detail::read_headers(strm, res.headers)) {7402error = Error::Read;7403return false;7404}74057406// Body7407if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&7408req.method != "CONNECT") {7409auto redirect = 300 < res.status && res.status < 400 && follow_location_;74107411if (req.response_handler && !redirect) {7412if (!req.response_handler(res)) {7413error = Error::Canceled;7414return false;7415}7416}74177418auto out =7419req.content_receiver7420? static_cast<ContentReceiverWithProgress>(7421[&](const char *buf, size_t n, uint64_t off, uint64_t len) {7422if (redirect) { return true; }7423auto ret = req.content_receiver(buf, n, off, len);7424if (!ret) { error = Error::Canceled; }7425return ret;7426})7427: static_cast<ContentReceiverWithProgress>(7428[&](const char *buf, size_t n, uint64_t /*off*/,7429uint64_t /*len*/) {7430if (res.body.size() + n > res.body.max_size()) {7431return false;7432}7433res.body.append(buf, n);7434return true;7435});74367437auto progress = [&](uint64_t current, uint64_t total) {7438if (!req.progress || redirect) { return true; }7439auto ret = req.progress(current, total);7440if (!ret) { error = Error::Canceled; }7441return ret;7442};74437444int dummy_status;7445if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),7446dummy_status, std::move(progress), std::move(out),7447decompress_)) {7448if (error != Error::Canceled) { error = Error::Read; }7449return false;7450}7451}74527453// Log7454if (logger_) { logger_(req, res); }74557456return true;7457}74587459inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(7460const std::string &boundary, const MultipartFormDataItems &items,7461const MultipartFormDataProviderItems &provider_items) const {7462size_t cur_item = 0;7463size_t cur_start = 0;7464// cur_item and cur_start are copied to within the std::function and maintain7465// state between successive calls7466return [&, cur_item, cur_start](size_t offset,7467DataSink &sink) mutable -> bool {7468if (!offset && !items.empty()) {7469sink.os << detail::serialize_multipart_formdata(items, boundary, false);7470return true;7471} else if (cur_item < provider_items.size()) {7472if (!cur_start) {7473const auto &begin = detail::serialize_multipart_formdata_item_begin(7474provider_items[cur_item], boundary);7475offset += begin.size();7476cur_start = offset;7477sink.os << begin;7478}74797480DataSink cur_sink;7481auto has_data = true;7482cur_sink.write = sink.write;7483cur_sink.done = [&]() { has_data = false; };74847485if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {7486return false;7487}74887489if (!has_data) {7490sink.os << detail::serialize_multipart_formdata_item_end();7491cur_item++;7492cur_start = 0;7493}7494return true;7495} else {7496sink.os << detail::serialize_multipart_formdata_finish(boundary);7497sink.done();7498return true;7499}7500};7501}75027503inline bool7504ClientImpl::process_socket(const Socket &socket,7505std::function<bool(Stream &strm)> callback) {7506return detail::process_client_socket(7507socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,7508write_timeout_usec_, std::move(callback));7509}75107511inline bool ClientImpl::is_ssl() const { return false; }75127513inline Result ClientImpl::Get(const std::string &path) {7514return Get(path, Headers(), Progress());7515}75167517inline Result ClientImpl::Get(const std::string &path, Progress progress) {7518return Get(path, Headers(), std::move(progress));7519}75207521inline Result ClientImpl::Get(const std::string &path, const Headers &headers) {7522return Get(path, headers, Progress());7523}75247525inline Result ClientImpl::Get(const std::string &path, const Headers &headers,7526Progress progress) {7527Request req;7528req.method = "GET";7529req.path = path;7530req.headers = headers;7531req.progress = std::move(progress);75327533return send_(std::move(req));7534}75357536inline Result ClientImpl::Get(const std::string &path,7537ContentReceiver content_receiver) {7538return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr);7539}75407541inline Result ClientImpl::Get(const std::string &path,7542ContentReceiver content_receiver,7543Progress progress) {7544return Get(path, Headers(), nullptr, std::move(content_receiver),7545std::move(progress));7546}75477548inline Result ClientImpl::Get(const std::string &path, const Headers &headers,7549ContentReceiver content_receiver) {7550return Get(path, headers, nullptr, std::move(content_receiver), nullptr);7551}75527553inline Result ClientImpl::Get(const std::string &path, const Headers &headers,7554ContentReceiver content_receiver,7555Progress progress) {7556return Get(path, headers, nullptr, std::move(content_receiver),7557std::move(progress));7558}75597560inline Result ClientImpl::Get(const std::string &path,7561ResponseHandler response_handler,7562ContentReceiver content_receiver) {7563return Get(path, Headers(), std::move(response_handler),7564std::move(content_receiver), nullptr);7565}75667567inline Result ClientImpl::Get(const std::string &path, const Headers &headers,7568ResponseHandler response_handler,7569ContentReceiver content_receiver) {7570return Get(path, headers, std::move(response_handler),7571std::move(content_receiver), nullptr);7572}75737574inline Result ClientImpl::Get(const std::string &path,7575ResponseHandler response_handler,7576ContentReceiver content_receiver,7577Progress progress) {7578return Get(path, Headers(), std::move(response_handler),7579std::move(content_receiver), std::move(progress));7580}75817582inline Result ClientImpl::Get(const std::string &path, const Headers &headers,7583ResponseHandler response_handler,7584ContentReceiver content_receiver,7585Progress progress) {7586Request req;7587req.method = "GET";7588req.path = path;7589req.headers = headers;7590req.response_handler = std::move(response_handler);7591req.content_receiver =7592[content_receiver](const char *data, size_t data_length,7593uint64_t /*offset*/, uint64_t /*total_length*/) {7594return content_receiver(data, data_length);7595};7596req.progress = std::move(progress);75977598return send_(std::move(req));7599}76007601inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,7602const Headers &headers, Progress progress) {7603if (params.empty()) { return Get(path, headers); }76047605std::string path_with_query = append_query_params(path, params);7606return Get(path_with_query, headers, progress);7607}76087609inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,7610const Headers &headers,7611ContentReceiver content_receiver,7612Progress progress) {7613return Get(path, params, headers, nullptr, content_receiver, progress);7614}76157616inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,7617const Headers &headers,7618ResponseHandler response_handler,7619ContentReceiver content_receiver,7620Progress progress) {7621if (params.empty()) {7622return Get(path, headers, response_handler, content_receiver, progress);7623}76247625std::string path_with_query = append_query_params(path, params);7626return Get(path_with_query, headers, response_handler, content_receiver,7627progress);7628}76297630inline Result ClientImpl::Head(const std::string &path) {7631return Head(path, Headers());7632}76337634inline Result ClientImpl::Head(const std::string &path,7635const Headers &headers) {7636Request req;7637req.method = "HEAD";7638req.headers = headers;7639req.path = path;76407641return send_(std::move(req));7642}76437644inline Result ClientImpl::Post(const std::string &path) {7645return Post(path, std::string(), std::string());7646}76477648inline Result ClientImpl::Post(const std::string &path,7649const Headers &headers) {7650return Post(path, headers, nullptr, 0, std::string());7651}76527653inline Result ClientImpl::Post(const std::string &path, const char *body,7654size_t content_length,7655const std::string &content_type) {7656return Post(path, Headers(), body, content_length, content_type);7657}76587659inline Result ClientImpl::Post(const std::string &path, const Headers &headers,7660const char *body, size_t content_length,7661const std::string &content_type) {7662return send_with_content_provider("POST", path, headers, body, content_length,7663nullptr, nullptr, content_type);7664}76657666inline Result ClientImpl::Post(const std::string &path, const std::string &body,7667const std::string &content_type) {7668return Post(path, Headers(), body, content_type);7669}76707671inline Result ClientImpl::Post(const std::string &path, const Headers &headers,7672const std::string &body,7673const std::string &content_type) {7674return send_with_content_provider("POST", path, headers, body.data(),7675body.size(), nullptr, nullptr,7676content_type);7677}76787679inline Result ClientImpl::Post(const std::string &path, const Params ¶ms) {7680return Post(path, Headers(), params);7681}76827683inline Result ClientImpl::Post(const std::string &path, size_t content_length,7684ContentProvider content_provider,7685const std::string &content_type) {7686return Post(path, Headers(), content_length, std::move(content_provider),7687content_type);7688}76897690inline Result ClientImpl::Post(const std::string &path,7691ContentProviderWithoutLength content_provider,7692const std::string &content_type) {7693return Post(path, Headers(), std::move(content_provider), content_type);7694}76957696inline Result ClientImpl::Post(const std::string &path, const Headers &headers,7697size_t content_length,7698ContentProvider content_provider,7699const std::string &content_type) {7700return send_with_content_provider("POST", path, headers, nullptr,7701content_length, std::move(content_provider),7702nullptr, content_type);7703}77047705inline Result ClientImpl::Post(const std::string &path, const Headers &headers,7706ContentProviderWithoutLength content_provider,7707const std::string &content_type) {7708return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr,7709std::move(content_provider), content_type);7710}77117712inline Result ClientImpl::Post(const std::string &path, const Headers &headers,7713const Params ¶ms) {7714auto query = detail::params_to_query_str(params);7715return Post(path, headers, query, "application/x-www-form-urlencoded");7716}77177718inline Result ClientImpl::Post(const std::string &path,7719const MultipartFormDataItems &items) {7720return Post(path, Headers(), items);7721}77227723inline Result ClientImpl::Post(const std::string &path, const Headers &headers,7724const MultipartFormDataItems &items) {7725const auto &boundary = detail::make_multipart_data_boundary();7726const auto &content_type =7727detail::serialize_multipart_formdata_get_content_type(boundary);7728const auto &body = detail::serialize_multipart_formdata(items, boundary);7729return Post(path, headers, body, content_type);7730}77317732inline Result ClientImpl::Post(const std::string &path, const Headers &headers,7733const MultipartFormDataItems &items,7734const std::string &boundary) {7735if (!detail::is_multipart_boundary_chars_valid(boundary)) {7736return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};7737}77387739const auto &content_type =7740detail::serialize_multipart_formdata_get_content_type(boundary);7741const auto &body = detail::serialize_multipart_formdata(items, boundary);7742return Post(path, headers, body, content_type);7743}77447745inline Result7746ClientImpl::Post(const std::string &path, const Headers &headers,7747const MultipartFormDataItems &items,7748const MultipartFormDataProviderItems &provider_items) {7749const auto &boundary = detail::make_multipart_data_boundary();7750const auto &content_type =7751detail::serialize_multipart_formdata_get_content_type(boundary);7752return send_with_content_provider(7753"POST", path, headers, nullptr, 0, nullptr,7754get_multipart_content_provider(boundary, items, provider_items),7755content_type);7756}77577758inline Result ClientImpl::Put(const std::string &path) {7759return Put(path, std::string(), std::string());7760}77617762inline Result ClientImpl::Put(const std::string &path, const char *body,7763size_t content_length,7764const std::string &content_type) {7765return Put(path, Headers(), body, content_length, content_type);7766}77677768inline Result ClientImpl::Put(const std::string &path, const Headers &headers,7769const char *body, size_t content_length,7770const std::string &content_type) {7771return send_with_content_provider("PUT", path, headers, body, content_length,7772nullptr, nullptr, content_type);7773}77747775inline Result ClientImpl::Put(const std::string &path, const std::string &body,7776const std::string &content_type) {7777return Put(path, Headers(), body, content_type);7778}77797780inline Result ClientImpl::Put(const std::string &path, const Headers &headers,7781const std::string &body,7782const std::string &content_type) {7783return send_with_content_provider("PUT", path, headers, body.data(),7784body.size(), nullptr, nullptr,7785content_type);7786}77877788inline Result ClientImpl::Put(const std::string &path, size_t content_length,7789ContentProvider content_provider,7790const std::string &content_type) {7791return Put(path, Headers(), content_length, std::move(content_provider),7792content_type);7793}77947795inline Result ClientImpl::Put(const std::string &path,7796ContentProviderWithoutLength content_provider,7797const std::string &content_type) {7798return Put(path, Headers(), std::move(content_provider), content_type);7799}78007801inline Result ClientImpl::Put(const std::string &path, const Headers &headers,7802size_t content_length,7803ContentProvider content_provider,7804const std::string &content_type) {7805return send_with_content_provider("PUT", path, headers, nullptr,7806content_length, std::move(content_provider),7807nullptr, content_type);7808}78097810inline Result ClientImpl::Put(const std::string &path, const Headers &headers,7811ContentProviderWithoutLength content_provider,7812const std::string &content_type) {7813return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr,7814std::move(content_provider), content_type);7815}78167817inline Result ClientImpl::Put(const std::string &path, const Params ¶ms) {7818return Put(path, Headers(), params);7819}78207821inline Result ClientImpl::Put(const std::string &path, const Headers &headers,7822const Params ¶ms) {7823auto query = detail::params_to_query_str(params);7824return Put(path, headers, query, "application/x-www-form-urlencoded");7825}78267827inline Result ClientImpl::Put(const std::string &path,7828const MultipartFormDataItems &items) {7829return Put(path, Headers(), items);7830}78317832inline Result ClientImpl::Put(const std::string &path, const Headers &headers,7833const MultipartFormDataItems &items) {7834const auto &boundary = detail::make_multipart_data_boundary();7835const auto &content_type =7836detail::serialize_multipart_formdata_get_content_type(boundary);7837const auto &body = detail::serialize_multipart_formdata(items, boundary);7838return Put(path, headers, body, content_type);7839}78407841inline Result ClientImpl::Put(const std::string &path, const Headers &headers,7842const MultipartFormDataItems &items,7843const std::string &boundary) {7844if (!detail::is_multipart_boundary_chars_valid(boundary)) {7845return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};7846}78477848const auto &content_type =7849detail::serialize_multipart_formdata_get_content_type(boundary);7850const auto &body = detail::serialize_multipart_formdata(items, boundary);7851return Put(path, headers, body, content_type);7852}78537854inline Result7855ClientImpl::Put(const std::string &path, const Headers &headers,7856const MultipartFormDataItems &items,7857const MultipartFormDataProviderItems &provider_items) {7858const auto &boundary = detail::make_multipart_data_boundary();7859const auto &content_type =7860detail::serialize_multipart_formdata_get_content_type(boundary);7861return send_with_content_provider(7862"PUT", path, headers, nullptr, 0, nullptr,7863get_multipart_content_provider(boundary, items, provider_items),7864content_type);7865}7866inline Result ClientImpl::Patch(const std::string &path) {7867return Patch(path, std::string(), std::string());7868}78697870inline Result ClientImpl::Patch(const std::string &path, const char *body,7871size_t content_length,7872const std::string &content_type) {7873return Patch(path, Headers(), body, content_length, content_type);7874}78757876inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,7877const char *body, size_t content_length,7878const std::string &content_type) {7879return send_with_content_provider("PATCH", path, headers, body,7880content_length, nullptr, nullptr,7881content_type);7882}78837884inline Result ClientImpl::Patch(const std::string &path,7885const std::string &body,7886const std::string &content_type) {7887return Patch(path, Headers(), body, content_type);7888}78897890inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,7891const std::string &body,7892const std::string &content_type) {7893return send_with_content_provider("PATCH", path, headers, body.data(),7894body.size(), nullptr, nullptr,7895content_type);7896}78977898inline Result ClientImpl::Patch(const std::string &path, size_t content_length,7899ContentProvider content_provider,7900const std::string &content_type) {7901return Patch(path, Headers(), content_length, std::move(content_provider),7902content_type);7903}79047905inline Result ClientImpl::Patch(const std::string &path,7906ContentProviderWithoutLength content_provider,7907const std::string &content_type) {7908return Patch(path, Headers(), std::move(content_provider), content_type);7909}79107911inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,7912size_t content_length,7913ContentProvider content_provider,7914const std::string &content_type) {7915return send_with_content_provider("PATCH", path, headers, nullptr,7916content_length, std::move(content_provider),7917nullptr, content_type);7918}79197920inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,7921ContentProviderWithoutLength content_provider,7922const std::string &content_type) {7923return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr,7924std::move(content_provider), content_type);7925}79267927inline Result ClientImpl::Delete(const std::string &path) {7928return Delete(path, Headers(), std::string(), std::string());7929}79307931inline Result ClientImpl::Delete(const std::string &path,7932const Headers &headers) {7933return Delete(path, headers, std::string(), std::string());7934}79357936inline Result ClientImpl::Delete(const std::string &path, const char *body,7937size_t content_length,7938const std::string &content_type) {7939return Delete(path, Headers(), body, content_length, content_type);7940}79417942inline Result ClientImpl::Delete(const std::string &path,7943const Headers &headers, const char *body,7944size_t content_length,7945const std::string &content_type) {7946Request req;7947req.method = "DELETE";7948req.headers = headers;7949req.path = path;79507951if (!content_type.empty()) { req.set_header("Content-Type", content_type); }7952req.body.assign(body, content_length);79537954return send_(std::move(req));7955}79567957inline Result ClientImpl::Delete(const std::string &path,7958const std::string &body,7959const std::string &content_type) {7960return Delete(path, Headers(), body.data(), body.size(), content_type);7961}79627963inline Result ClientImpl::Delete(const std::string &path,7964const Headers &headers,7965const std::string &body,7966const std::string &content_type) {7967return Delete(path, headers, body.data(), body.size(), content_type);7968}79697970inline Result ClientImpl::Options(const std::string &path) {7971return Options(path, Headers());7972}79737974inline Result ClientImpl::Options(const std::string &path,7975const Headers &headers) {7976Request req;7977req.method = "OPTIONS";7978req.headers = headers;7979req.path = path;79807981return send_(std::move(req));7982}79837984inline void ClientImpl::stop() {7985std::lock_guard<std::mutex> guard(socket_mutex_);79867987// If there is anything ongoing right now, the ONLY thread-safe thing we can7988// do is to shutdown_socket, so that threads using this socket suddenly7989// discover they can't read/write any more and error out. Everything else7990// (closing the socket, shutting ssl down) is unsafe because these actions are7991// not thread-safe.7992if (socket_requests_in_flight_ > 0) {7993shutdown_socket(socket_);79947995// Aside from that, we set a flag for the socket to be closed when we're7996// done.7997socket_should_be_closed_when_request_is_done_ = true;7998return;7999}80008001// Otherwise, still holding the mutex, we can shut everything down ourselves8002shutdown_ssl(socket_, true);8003shutdown_socket(socket_);8004close_socket(socket_);8005}80068007inline std::string ClientImpl::host() const { return host_; }80088009inline int ClientImpl::port() const { return port_; }80108011inline size_t ClientImpl::is_socket_open() const {8012std::lock_guard<std::mutex> guard(socket_mutex_);8013return socket_.is_open();8014}80158016inline socket_t ClientImpl::socket() const { return socket_.sock; }80178018inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {8019connection_timeout_sec_ = sec;8020connection_timeout_usec_ = usec;8021}80228023inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {8024read_timeout_sec_ = sec;8025read_timeout_usec_ = usec;8026}80278028inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {8029write_timeout_sec_ = sec;8030write_timeout_usec_ = usec;8031}80328033inline void ClientImpl::set_basic_auth(const std::string &username,8034const std::string &password) {8035basic_auth_username_ = username;8036basic_auth_password_ = password;8037}80388039inline void ClientImpl::set_bearer_token_auth(const std::string &token) {8040bearer_token_auth_token_ = token;8041}80428043#ifdef CPPHTTPLIB_OPENSSL_SUPPORT8044inline void ClientImpl::set_digest_auth(const std::string &username,8045const std::string &password) {8046digest_auth_username_ = username;8047digest_auth_password_ = password;8048}8049#endif80508051inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }80528053inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }80548055inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }80568057inline void8058ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {8059addr_map_ = std::move(addr_map);8060}80618062inline void ClientImpl::set_default_headers(Headers headers) {8063default_headers_ = std::move(headers);8064}80658066inline void ClientImpl::set_header_writer(8067std::function<ssize_t(Stream &, Headers &)> const &writer) {8068header_writer_ = writer;8069}80708071inline void ClientImpl::set_address_family(int family) {8072address_family_ = family;8073}80748075inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }80768077inline void ClientImpl::set_socket_options(SocketOptions socket_options) {8078socket_options_ = std::move(socket_options);8079}80808081inline void ClientImpl::set_compress(bool on) { compress_ = on; }80828083inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }80848085inline void ClientImpl::set_interface(const std::string &intf) {8086interface_ = intf;8087}80888089inline void ClientImpl::set_proxy(const std::string &host, int port) {8090proxy_host_ = host;8091proxy_port_ = port;8092}80938094inline void ClientImpl::set_proxy_basic_auth(const std::string &username,8095const std::string &password) {8096proxy_basic_auth_username_ = username;8097proxy_basic_auth_password_ = password;8098}80998100inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {8101proxy_bearer_token_auth_token_ = token;8102}81038104#ifdef CPPHTTPLIB_OPENSSL_SUPPORT8105inline void ClientImpl::set_proxy_digest_auth(const std::string &username,8106const std::string &password) {8107proxy_digest_auth_username_ = username;8108proxy_digest_auth_password_ = password;8109}81108111inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,8112const std::string &ca_cert_dir_path) {8113ca_cert_file_path_ = ca_cert_file_path;8114ca_cert_dir_path_ = ca_cert_dir_path;8115}81168117inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {8118if (ca_cert_store && ca_cert_store != ca_cert_store_) {8119ca_cert_store_ = ca_cert_store;8120}8121}81228123inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,8124std::size_t size) const {8125auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));8126if (!mem) { return nullptr; }81278128auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);8129if (!inf) {8130BIO_free_all(mem);8131return nullptr;8132}81338134auto cts = X509_STORE_new();8135if (cts) {8136for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {8137auto itmp = sk_X509_INFO_value(inf, i);8138if (!itmp) { continue; }81398140if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }8141if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }8142}8143}81448145sk_X509_INFO_pop_free(inf, X509_INFO_free);8146BIO_free_all(mem);8147return cts;8148}81498150inline void ClientImpl::enable_server_certificate_verification(bool enabled) {8151server_certificate_verification_ = enabled;8152}8153#endif81548155inline void ClientImpl::set_logger(Logger logger) {8156logger_ = std::move(logger);8157}81588159/*8160* SSL Implementation8161*/8162#ifdef CPPHTTPLIB_OPENSSL_SUPPORT8163namespace detail {81648165template <typename U, typename V>8166inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,8167U SSL_connect_or_accept, V setup) {8168SSL *ssl = nullptr;8169{8170std::lock_guard<std::mutex> guard(ctx_mutex);8171ssl = SSL_new(ctx);8172}81738174if (ssl) {8175set_nonblocking(sock, true);8176auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);8177BIO_set_nbio(bio, 1);8178SSL_set_bio(ssl, bio, bio);81798180if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {8181SSL_shutdown(ssl);8182{8183std::lock_guard<std::mutex> guard(ctx_mutex);8184SSL_free(ssl);8185}8186set_nonblocking(sock, false);8187return nullptr;8188}8189BIO_set_nbio(bio, 0);8190set_nonblocking(sock, false);8191}81928193return ssl;8194}81958196inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,8197bool shutdown_gracefully) {8198// sometimes we may want to skip this to try to avoid SIGPIPE if we know8199// the remote has closed the network connection8200// Note that it is not always possible to avoid SIGPIPE, this is merely a8201// best-efforts.8202if (shutdown_gracefully) { SSL_shutdown(ssl); }82038204std::lock_guard<std::mutex> guard(ctx_mutex);8205SSL_free(ssl);8206}82078208template <typename U>8209bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,8210U ssl_connect_or_accept,8211time_t timeout_sec,8212time_t timeout_usec) {8213auto res = 0;8214while ((res = ssl_connect_or_accept(ssl)) != 1) {8215auto err = SSL_get_error(ssl, res);8216switch (err) {8217case SSL_ERROR_WANT_READ:8218if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }8219break;8220case SSL_ERROR_WANT_WRITE:8221if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }8222break;8223default: break;8224}8225return false;8226}8227return true;8228}82298230template <typename T>8231inline bool process_server_socket_ssl(8232const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,8233size_t keep_alive_max_count, time_t keep_alive_timeout_sec,8234time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,8235time_t write_timeout_usec, T callback) {8236return process_server_socket_core(8237svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,8238[&](bool close_connection, bool &connection_closed) {8239SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,8240write_timeout_sec, write_timeout_usec);8241return callback(strm, close_connection, connection_closed);8242});8243}82448245template <typename T>8246inline bool8247process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec,8248time_t read_timeout_usec, time_t write_timeout_sec,8249time_t write_timeout_usec, T callback) {8250SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,8251write_timeout_sec, write_timeout_usec);8252return callback(strm);8253}82548255class SSLInit {8256public:8257SSLInit() {8258OPENSSL_init_ssl(8259OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);8260}8261};82628263// SSL socket stream implementation8264inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,8265time_t read_timeout_sec,8266time_t read_timeout_usec,8267time_t write_timeout_sec,8268time_t write_timeout_usec)8269: sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),8270read_timeout_usec_(read_timeout_usec),8271write_timeout_sec_(write_timeout_sec),8272write_timeout_usec_(write_timeout_usec) {8273SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);8274}82758276inline SSLSocketStream::~SSLSocketStream() = default;82778278inline bool SSLSocketStream::is_readable() const {8279return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;8280}82818282inline bool SSLSocketStream::is_writable() const {8283return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&8284is_socket_alive(sock_);8285}82868287inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {8288if (SSL_pending(ssl_) > 0) {8289return SSL_read(ssl_, ptr, static_cast<int>(size));8290} else if (is_readable()) {8291auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));8292if (ret < 0) {8293auto err = SSL_get_error(ssl_, ret);8294auto n = 1000;8295#ifdef _WIN328296while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||8297(err == SSL_ERROR_SYSCALL &&8298WSAGetLastError() == WSAETIMEDOUT))) {8299#else8300while (--n >= 0 && err == SSL_ERROR_WANT_READ) {8301#endif8302if (SSL_pending(ssl_) > 0) {8303return SSL_read(ssl_, ptr, static_cast<int>(size));8304} else if (is_readable()) {8305std::this_thread::sleep_for(std::chrono::milliseconds(1));8306ret = SSL_read(ssl_, ptr, static_cast<int>(size));8307if (ret >= 0) { return ret; }8308err = SSL_get_error(ssl_, ret);8309} else {8310return -1;8311}8312}8313}8314return ret;8315}8316return -1;8317}83188319inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {8320if (is_writable()) {8321auto handle_size = static_cast<int>(8322std::min<size_t>(size, (std::numeric_limits<int>::max)()));83238324auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));8325if (ret < 0) {8326auto err = SSL_get_error(ssl_, ret);8327auto n = 1000;8328#ifdef _WIN328329while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||8330(err == SSL_ERROR_SYSCALL &&8331WSAGetLastError() == WSAETIMEDOUT))) {8332#else8333while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {8334#endif8335if (is_writable()) {8336std::this_thread::sleep_for(std::chrono::milliseconds(1));8337ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));8338if (ret >= 0) { return ret; }8339err = SSL_get_error(ssl_, ret);8340} else {8341return -1;8342}8343}8344}8345return ret;8346}8347return -1;8348}83498350inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,8351int &port) const {8352detail::get_remote_ip_and_port(sock_, ip, port);8353}83548355inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,8356int &port) const {8357detail::get_local_ip_and_port(sock_, ip, port);8358}83598360inline socket_t SSLSocketStream::socket() const { return sock_; }83618362static SSLInit sslinit_;83638364} // namespace detail83658366// SSL HTTP server implementation8367inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,8368const char *client_ca_cert_file_path,8369const char *client_ca_cert_dir_path,8370const char *private_key_password) {8371ctx_ = SSL_CTX_new(TLS_server_method());83728373if (ctx_) {8374SSL_CTX_set_options(ctx_,8375SSL_OP_NO_COMPRESSION |8376SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);83778378SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);83798380// add default password callback before opening encrypted private key8381if (private_key_password != nullptr && (private_key_password[0] != '\0')) {8382SSL_CTX_set_default_passwd_cb_userdata(8383ctx_,8384reinterpret_cast<void *>(const_cast<char *>(private_key_password)));8385}83868387if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||8388SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=83891) {8390SSL_CTX_free(ctx_);8391ctx_ = nullptr;8392} else if (client_ca_cert_file_path || client_ca_cert_dir_path) {8393SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,8394client_ca_cert_dir_path);83958396SSL_CTX_set_verify(8397ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);8398}8399}8400}84018402inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,8403X509_STORE *client_ca_cert_store) {8404ctx_ = SSL_CTX_new(TLS_server_method());84058406if (ctx_) {8407SSL_CTX_set_options(ctx_,8408SSL_OP_NO_COMPRESSION |8409SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);84108411SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);84128413if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||8414SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {8415SSL_CTX_free(ctx_);8416ctx_ = nullptr;8417} else if (client_ca_cert_store) {8418SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);84198420SSL_CTX_set_verify(8421ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);8422}8423}8424}84258426inline SSLServer::SSLServer(8427const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {8428ctx_ = SSL_CTX_new(TLS_method());8429if (ctx_) {8430if (!setup_ssl_ctx_callback(*ctx_)) {8431SSL_CTX_free(ctx_);8432ctx_ = nullptr;8433}8434}8435}84368437inline SSLServer::~SSLServer() {8438if (ctx_) { SSL_CTX_free(ctx_); }8439}84408441inline bool SSLServer::is_valid() const { return ctx_; }84428443inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }84448445inline bool SSLServer::process_and_close_socket(socket_t sock) {8446auto ssl = detail::ssl_new(8447sock, ctx_, ctx_mutex_,8448[&](SSL *ssl2) {8449return detail::ssl_connect_or_accept_nonblocking(8450sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);8451},8452[](SSL * /*ssl2*/) { return true; });84538454auto ret = false;8455if (ssl) {8456ret = detail::process_server_socket_ssl(8457svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,8458read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,8459write_timeout_usec_,8460[this, ssl](Stream &strm, bool close_connection,8461bool &connection_closed) {8462return process_request(strm, close_connection, connection_closed,8463[&](Request &req) { req.ssl = ssl; });8464});84658466// Shutdown gracefully if the result seemed successful, non-gracefully if8467// the connection appeared to be closed.8468const bool shutdown_gracefully = ret;8469detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully);8470}84718472detail::shutdown_socket(sock);8473detail::close_socket(sock);8474return ret;8475}84768477// SSL HTTP client implementation8478inline SSLClient::SSLClient(const std::string &host)8479: SSLClient(host, 443, std::string(), std::string()) {}84808481inline SSLClient::SSLClient(const std::string &host, int port)8482: SSLClient(host, port, std::string(), std::string()) {}84838484inline SSLClient::SSLClient(const std::string &host, int port,8485const std::string &client_cert_path,8486const std::string &client_key_path)8487: ClientImpl(host, port, client_cert_path, client_key_path) {8488ctx_ = SSL_CTX_new(TLS_client_method());84898490detail::split(&host_[0], &host_[host_.size()], '.',8491[&](const char *b, const char *e) {8492host_components_.emplace_back(b, e);8493});84948495if (!client_cert_path.empty() && !client_key_path.empty()) {8496if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),8497SSL_FILETYPE_PEM) != 1 ||8498SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),8499SSL_FILETYPE_PEM) != 1) {8500SSL_CTX_free(ctx_);8501ctx_ = nullptr;8502}8503}8504}85058506inline SSLClient::SSLClient(const std::string &host, int port,8507X509 *client_cert, EVP_PKEY *client_key)8508: ClientImpl(host, port) {8509ctx_ = SSL_CTX_new(TLS_client_method());85108511detail::split(&host_[0], &host_[host_.size()], '.',8512[&](const char *b, const char *e) {8513host_components_.emplace_back(b, e);8514});85158516if (client_cert != nullptr && client_key != nullptr) {8517if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||8518SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {8519SSL_CTX_free(ctx_);8520ctx_ = nullptr;8521}8522}8523}85248525inline SSLClient::~SSLClient() {8526if (ctx_) { SSL_CTX_free(ctx_); }8527// Make sure to shut down SSL since shutdown_ssl will resolve to the8528// base function rather than the derived function once we get to the8529// base class destructor, and won't free the SSL (causing a leak).8530shutdown_ssl_impl(socket_, true);8531}85328533inline bool SSLClient::is_valid() const { return ctx_; }85348535inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {8536if (ca_cert_store) {8537if (ctx_) {8538if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {8539// Free memory allocated for old cert and use new store `ca_cert_store`8540SSL_CTX_set_cert_store(ctx_, ca_cert_store);8541}8542} else {8543X509_STORE_free(ca_cert_store);8544}8545}8546}85478548inline void SSLClient::load_ca_cert_store(const char *ca_cert,8549std::size_t size) {8550set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));8551}85528553inline long SSLClient::get_openssl_verify_result() const {8554return verify_result_;8555}85568557inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }85588559inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {8560return is_valid() && ClientImpl::create_and_connect_socket(socket, error);8561}85628563// Assumes that socket_mutex_ is locked and that there are no requests in flight8564inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,8565bool &success, Error &error) {8566success = true;8567Response proxy_res;8568if (!detail::process_client_socket(8569socket.sock, read_timeout_sec_, read_timeout_usec_,8570write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {8571Request req2;8572req2.method = "CONNECT";8573req2.path = host_and_port_;8574return process_request(strm, req2, proxy_res, false, error);8575})) {8576// Thread-safe to close everything because we are assuming there are no8577// requests in flight8578shutdown_ssl(socket, true);8579shutdown_socket(socket);8580close_socket(socket);8581success = false;8582return false;8583}85848585if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {8586if (!proxy_digest_auth_username_.empty() &&8587!proxy_digest_auth_password_.empty()) {8588std::map<std::string, std::string> auth;8589if (detail::parse_www_authenticate(proxy_res, auth, true)) {8590proxy_res = Response();8591if (!detail::process_client_socket(8592socket.sock, read_timeout_sec_, read_timeout_usec_,8593write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {8594Request req3;8595req3.method = "CONNECT";8596req3.path = host_and_port_;8597req3.headers.insert(detail::make_digest_authentication_header(8598req3, auth, 1, detail::random_string(10),8599proxy_digest_auth_username_, proxy_digest_auth_password_,8600true));8601return process_request(strm, req3, proxy_res, false, error);8602})) {8603// Thread-safe to close everything because we are assuming there are8604// no requests in flight8605shutdown_ssl(socket, true);8606shutdown_socket(socket);8607close_socket(socket);8608success = false;8609return false;8610}8611}8612}8613}86148615// If status code is not 200, proxy request is failed.8616// Set error to ProxyConnection and return proxy response8617// as the response of the request8618if (proxy_res.status != StatusCode::OK_200) {8619error = Error::ProxyConnection;8620res = std::move(proxy_res);8621// Thread-safe to close everything because we are assuming there are8622// no requests in flight8623shutdown_ssl(socket, true);8624shutdown_socket(socket);8625close_socket(socket);8626return false;8627}86288629return true;8630}86318632inline bool SSLClient::load_certs() {8633auto ret = true;86348635std::call_once(initialize_cert_, [&]() {8636std::lock_guard<std::mutex> guard(ctx_mutex_);8637if (!ca_cert_file_path_.empty()) {8638if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),8639nullptr)) {8640ret = false;8641}8642} else if (!ca_cert_dir_path_.empty()) {8643if (!SSL_CTX_load_verify_locations(ctx_, nullptr,8644ca_cert_dir_path_.c_str())) {8645ret = false;8646}8647} else {8648auto loaded = false;8649#ifdef _WIN328650loaded =8651detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));8652#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)8653#if TARGET_OS_OSX8654loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));8655#endif // TARGET_OS_OSX8656#endif // _WIN328657if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }8658}8659});86608661return ret;8662}86638664inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {8665auto ssl = detail::ssl_new(8666socket.sock, ctx_, ctx_mutex_,8667[&](SSL *ssl2) {8668if (server_certificate_verification_) {8669if (!load_certs()) {8670error = Error::SSLLoadingCerts;8671return false;8672}8673SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);8674}86758676if (!detail::ssl_connect_or_accept_nonblocking(8677socket.sock, ssl2, SSL_connect, connection_timeout_sec_,8678connection_timeout_usec_)) {8679error = Error::SSLConnection;8680return false;8681}86828683if (server_certificate_verification_) {8684verify_result_ = SSL_get_verify_result(ssl2);86858686if (verify_result_ != X509_V_OK) {8687error = Error::SSLServerVerification;8688return false;8689}86908691auto server_cert = SSL_get1_peer_certificate(ssl2);86928693if (server_cert == nullptr) {8694error = Error::SSLServerVerification;8695return false;8696}86978698if (!verify_host(server_cert)) {8699X509_free(server_cert);8700error = Error::SSLServerVerification;8701return false;8702}8703X509_free(server_cert);8704}87058706return true;8707},8708[&](SSL *ssl2) {8709// NOTE: Direct call instead of using the OpenSSL macro to suppress8710// -Wold-style-cast warning8711// SSL_set_tlsext_host_name(ssl2, host_.c_str());8712SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,8713static_cast<void *>(const_cast<char *>(host_.c_str())));8714return true;8715});87168717if (ssl) {8718socket.ssl = ssl;8719return true;8720}87218722shutdown_socket(socket);8723close_socket(socket);8724return false;8725}87268727inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {8728shutdown_ssl_impl(socket, shutdown_gracefully);8729}87308731inline void SSLClient::shutdown_ssl_impl(Socket &socket,8732bool shutdown_gracefully) {8733if (socket.sock == INVALID_SOCKET) {8734assert(socket.ssl == nullptr);8735return;8736}8737if (socket.ssl) {8738detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);8739socket.ssl = nullptr;8740}8741assert(socket.ssl == nullptr);8742}87438744inline bool8745SSLClient::process_socket(const Socket &socket,8746std::function<bool(Stream &strm)> callback) {8747assert(socket.ssl);8748return detail::process_client_socket_ssl(8749socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,8750write_timeout_sec_, write_timeout_usec_, std::move(callback));8751}87528753inline bool SSLClient::is_ssl() const { return true; }87548755inline bool SSLClient::verify_host(X509 *server_cert) const {8756/* Quote from RFC2818 section 3.1 "Server Identity"87578758If a subjectAltName extension of type dNSName is present, that MUST8759be used as the identity. Otherwise, the (most specific) Common Name8760field in the Subject field of the certificate MUST be used. Although8761the use of the Common Name is existing practice, it is deprecated and8762Certification Authorities are encouraged to use the dNSName instead.87638764Matching is performed using the matching rules specified by8765[RFC2459]. If more than one identity of a given type is present in8766the certificate (e.g., more than one dNSName name, a match in any one8767of the set is considered acceptable.) Names may contain the wildcard8768character * which is considered to match any single domain name8769component or component fragment. E.g., *.a.com matches foo.a.com but8770not bar.foo.a.com. f*.com matches foo.com but not bar.com.87718772In some cases, the URI is specified as an IP address rather than a8773hostname. In this case, the iPAddress subjectAltName must be present8774in the certificate and must exactly match the IP in the URI.87758776*/8777return verify_host_with_subject_alt_name(server_cert) ||8778verify_host_with_common_name(server_cert);8779}87808781inline bool8782SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {8783auto ret = false;87848785auto type = GEN_DNS;87868787struct in6_addr addr6 {};8788struct in_addr addr {};8789size_t addr_len = 0;87908791#ifndef __MINGW32__8792if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {8793type = GEN_IPADD;8794addr_len = sizeof(struct in6_addr);8795} else if (inet_pton(AF_INET, host_.c_str(), &addr)) {8796type = GEN_IPADD;8797addr_len = sizeof(struct in_addr);8798}8799#endif88008801auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(8802X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));88038804if (alt_names) {8805auto dsn_matched = false;8806auto ip_matched = false;88078808auto count = sk_GENERAL_NAME_num(alt_names);88098810for (decltype(count) i = 0; i < count && !dsn_matched; i++) {8811auto val = sk_GENERAL_NAME_value(alt_names, i);8812if (val->type == type) {8813auto name =8814reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));8815auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));88168817switch (type) {8818case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;88198820case GEN_IPADD:8821if (!memcmp(&addr6, name, addr_len) ||8822!memcmp(&addr, name, addr_len)) {8823ip_matched = true;8824}8825break;8826}8827}8828}88298830if (dsn_matched || ip_matched) { ret = true; }8831}88328833GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(8834reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));8835return ret;8836}88378838inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {8839const auto subject_name = X509_get_subject_name(server_cert);88408841if (subject_name != nullptr) {8842char name[BUFSIZ];8843auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,8844name, sizeof(name));88458846if (name_len != -1) {8847return check_host_name(name, static_cast<size_t>(name_len));8848}8849}88508851return false;8852}88538854inline bool SSLClient::check_host_name(const char *pattern,8855size_t pattern_len) const {8856if (host_.size() == pattern_len && host_ == pattern) { return true; }88578858// Wildcard match8859// https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/3764848860std::vector<std::string> pattern_components;8861detail::split(&pattern[0], &pattern[pattern_len], '.',8862[&](const char *b, const char *e) {8863pattern_components.emplace_back(b, e);8864});88658866if (host_components_.size() != pattern_components.size()) { return false; }88678868auto itr = pattern_components.begin();8869for (const auto &h : host_components_) {8870auto &p = *itr;8871if (p != h && p != "*") {8872auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&8873!p.compare(0, p.size() - 1, h));8874if (!partial_match) { return false; }8875}8876++itr;8877}88788879return true;8880}8881#endif88828883// Universal client implementation8884inline Client::Client(const std::string &scheme_host_port)8885: Client(scheme_host_port, std::string(), std::string()) {}88868887inline Client::Client(const std::string &scheme_host_port,8888const std::string &client_cert_path,8889const std::string &client_key_path) {8890const static std::regex re(8891R"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)");88928893std::smatch m;8894if (std::regex_match(scheme_host_port, m, re)) {8895auto scheme = m[1].str();88968897#ifdef CPPHTTPLIB_OPENSSL_SUPPORT8898if (!scheme.empty() && (scheme != "http" && scheme != "https")) {8899#else8900if (!scheme.empty() && scheme != "http") {8901#endif8902#ifndef CPPHTTPLIB_NO_EXCEPTIONS8903std::string msg = "'" + scheme + "' scheme is not supported.";8904throw std::invalid_argument(msg);8905#endif8906return;8907}89088909auto is_ssl = scheme == "https";89108911auto host = m[2].str();8912if (host.empty()) { host = m[3].str(); }89138914auto port_str = m[4].str();8915auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);89168917if (is_ssl) {8918#ifdef CPPHTTPLIB_OPENSSL_SUPPORT8919cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,8920client_key_path);8921is_ssl_ = is_ssl;8922#endif8923} else {8924cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,8925client_key_path);8926}8927} else {8928cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,8929client_cert_path, client_key_path);8930}8931}89328933inline Client::Client(const std::string &host, int port)8934: cli_(detail::make_unique<ClientImpl>(host, port)) {}89358936inline Client::Client(const std::string &host, int port,8937const std::string &client_cert_path,8938const std::string &client_key_path)8939: cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,8940client_key_path)) {}89418942inline Client::~Client() = default;89438944inline bool Client::is_valid() const {8945return cli_ != nullptr && cli_->is_valid();8946}89478948inline Result Client::Get(const std::string &path) { return cli_->Get(path); }8949inline Result Client::Get(const std::string &path, const Headers &headers) {8950return cli_->Get(path, headers);8951}8952inline Result Client::Get(const std::string &path, Progress progress) {8953return cli_->Get(path, std::move(progress));8954}8955inline Result Client::Get(const std::string &path, const Headers &headers,8956Progress progress) {8957return cli_->Get(path, headers, std::move(progress));8958}8959inline Result Client::Get(const std::string &path,8960ContentReceiver content_receiver) {8961return cli_->Get(path, std::move(content_receiver));8962}8963inline Result Client::Get(const std::string &path, const Headers &headers,8964ContentReceiver content_receiver) {8965return cli_->Get(path, headers, std::move(content_receiver));8966}8967inline Result Client::Get(const std::string &path,8968ContentReceiver content_receiver, Progress progress) {8969return cli_->Get(path, std::move(content_receiver), std::move(progress));8970}8971inline Result Client::Get(const std::string &path, const Headers &headers,8972ContentReceiver content_receiver, Progress progress) {8973return cli_->Get(path, headers, std::move(content_receiver),8974std::move(progress));8975}8976inline Result Client::Get(const std::string &path,8977ResponseHandler response_handler,8978ContentReceiver content_receiver) {8979return cli_->Get(path, std::move(response_handler),8980std::move(content_receiver));8981}8982inline Result Client::Get(const std::string &path, const Headers &headers,8983ResponseHandler response_handler,8984ContentReceiver content_receiver) {8985return cli_->Get(path, headers, std::move(response_handler),8986std::move(content_receiver));8987}8988inline Result Client::Get(const std::string &path,8989ResponseHandler response_handler,8990ContentReceiver content_receiver, Progress progress) {8991return cli_->Get(path, std::move(response_handler),8992std::move(content_receiver), std::move(progress));8993}8994inline Result Client::Get(const std::string &path, const Headers &headers,8995ResponseHandler response_handler,8996ContentReceiver content_receiver, Progress progress) {8997return cli_->Get(path, headers, std::move(response_handler),8998std::move(content_receiver), std::move(progress));8999}9000inline Result Client::Get(const std::string &path, const Params ¶ms,9001const Headers &headers, Progress progress) {9002return cli_->Get(path, params, headers, progress);9003}9004inline Result Client::Get(const std::string &path, const Params ¶ms,9005const Headers &headers,9006ContentReceiver content_receiver, Progress progress) {9007return cli_->Get(path, params, headers, content_receiver, progress);9008}9009inline Result Client::Get(const std::string &path, const Params ¶ms,9010const Headers &headers,9011ResponseHandler response_handler,9012ContentReceiver content_receiver, Progress progress) {9013return cli_->Get(path, params, headers, response_handler, content_receiver,9014progress);9015}90169017inline Result Client::Head(const std::string &path) { return cli_->Head(path); }9018inline Result Client::Head(const std::string &path, const Headers &headers) {9019return cli_->Head(path, headers);9020}90219022inline Result Client::Post(const std::string &path) { return cli_->Post(path); }9023inline Result Client::Post(const std::string &path, const Headers &headers) {9024return cli_->Post(path, headers);9025}9026inline Result Client::Post(const std::string &path, const char *body,9027size_t content_length,9028const std::string &content_type) {9029return cli_->Post(path, body, content_length, content_type);9030}9031inline Result Client::Post(const std::string &path, const Headers &headers,9032const char *body, size_t content_length,9033const std::string &content_type) {9034return cli_->Post(path, headers, body, content_length, content_type);9035}9036inline Result Client::Post(const std::string &path, const std::string &body,9037const std::string &content_type) {9038return cli_->Post(path, body, content_type);9039}9040inline Result Client::Post(const std::string &path, const Headers &headers,9041const std::string &body,9042const std::string &content_type) {9043return cli_->Post(path, headers, body, content_type);9044}9045inline Result Client::Post(const std::string &path, size_t content_length,9046ContentProvider content_provider,9047const std::string &content_type) {9048return cli_->Post(path, content_length, std::move(content_provider),9049content_type);9050}9051inline Result Client::Post(const std::string &path,9052ContentProviderWithoutLength content_provider,9053const std::string &content_type) {9054return cli_->Post(path, std::move(content_provider), content_type);9055}9056inline Result Client::Post(const std::string &path, const Headers &headers,9057size_t content_length,9058ContentProvider content_provider,9059const std::string &content_type) {9060return cli_->Post(path, headers, content_length, std::move(content_provider),9061content_type);9062}9063inline Result Client::Post(const std::string &path, const Headers &headers,9064ContentProviderWithoutLength content_provider,9065const std::string &content_type) {9066return cli_->Post(path, headers, std::move(content_provider), content_type);9067}9068inline Result Client::Post(const std::string &path, const Params ¶ms) {9069return cli_->Post(path, params);9070}9071inline Result Client::Post(const std::string &path, const Headers &headers,9072const Params ¶ms) {9073return cli_->Post(path, headers, params);9074}9075inline Result Client::Post(const std::string &path,9076const MultipartFormDataItems &items) {9077return cli_->Post(path, items);9078}9079inline Result Client::Post(const std::string &path, const Headers &headers,9080const MultipartFormDataItems &items) {9081return cli_->Post(path, headers, items);9082}9083inline Result Client::Post(const std::string &path, const Headers &headers,9084const MultipartFormDataItems &items,9085const std::string &boundary) {9086return cli_->Post(path, headers, items, boundary);9087}9088inline Result9089Client::Post(const std::string &path, const Headers &headers,9090const MultipartFormDataItems &items,9091const MultipartFormDataProviderItems &provider_items) {9092return cli_->Post(path, headers, items, provider_items);9093}9094inline Result Client::Put(const std::string &path) { return cli_->Put(path); }9095inline Result Client::Put(const std::string &path, const char *body,9096size_t content_length,9097const std::string &content_type) {9098return cli_->Put(path, body, content_length, content_type);9099}9100inline Result Client::Put(const std::string &path, const Headers &headers,9101const char *body, size_t content_length,9102const std::string &content_type) {9103return cli_->Put(path, headers, body, content_length, content_type);9104}9105inline Result Client::Put(const std::string &path, const std::string &body,9106const std::string &content_type) {9107return cli_->Put(path, body, content_type);9108}9109inline Result Client::Put(const std::string &path, const Headers &headers,9110const std::string &body,9111const std::string &content_type) {9112return cli_->Put(path, headers, body, content_type);9113}9114inline Result Client::Put(const std::string &path, size_t content_length,9115ContentProvider content_provider,9116const std::string &content_type) {9117return cli_->Put(path, content_length, std::move(content_provider),9118content_type);9119}9120inline Result Client::Put(const std::string &path,9121ContentProviderWithoutLength content_provider,9122const std::string &content_type) {9123return cli_->Put(path, std::move(content_provider), content_type);9124}9125inline Result Client::Put(const std::string &path, const Headers &headers,9126size_t content_length,9127ContentProvider content_provider,9128const std::string &content_type) {9129return cli_->Put(path, headers, content_length, std::move(content_provider),9130content_type);9131}9132inline Result Client::Put(const std::string &path, const Headers &headers,9133ContentProviderWithoutLength content_provider,9134const std::string &content_type) {9135return cli_->Put(path, headers, std::move(content_provider), content_type);9136}9137inline Result Client::Put(const std::string &path, const Params ¶ms) {9138return cli_->Put(path, params);9139}9140inline Result Client::Put(const std::string &path, const Headers &headers,9141const Params ¶ms) {9142return cli_->Put(path, headers, params);9143}9144inline Result Client::Put(const std::string &path,9145const MultipartFormDataItems &items) {9146return cli_->Put(path, items);9147}9148inline Result Client::Put(const std::string &path, const Headers &headers,9149const MultipartFormDataItems &items) {9150return cli_->Put(path, headers, items);9151}9152inline Result Client::Put(const std::string &path, const Headers &headers,9153const MultipartFormDataItems &items,9154const std::string &boundary) {9155return cli_->Put(path, headers, items, boundary);9156}9157inline Result9158Client::Put(const std::string &path, const Headers &headers,9159const MultipartFormDataItems &items,9160const MultipartFormDataProviderItems &provider_items) {9161return cli_->Put(path, headers, items, provider_items);9162}9163inline Result Client::Patch(const std::string &path) {9164return cli_->Patch(path);9165}9166inline Result Client::Patch(const std::string &path, const char *body,9167size_t content_length,9168const std::string &content_type) {9169return cli_->Patch(path, body, content_length, content_type);9170}9171inline Result Client::Patch(const std::string &path, const Headers &headers,9172const char *body, size_t content_length,9173const std::string &content_type) {9174return cli_->Patch(path, headers, body, content_length, content_type);9175}9176inline Result Client::Patch(const std::string &path, const std::string &body,9177const std::string &content_type) {9178return cli_->Patch(path, body, content_type);9179}9180inline Result Client::Patch(const std::string &path, const Headers &headers,9181const std::string &body,9182const std::string &content_type) {9183return cli_->Patch(path, headers, body, content_type);9184}9185inline Result Client::Patch(const std::string &path, size_t content_length,9186ContentProvider content_provider,9187const std::string &content_type) {9188return cli_->Patch(path, content_length, std::move(content_provider),9189content_type);9190}9191inline Result Client::Patch(const std::string &path,9192ContentProviderWithoutLength content_provider,9193const std::string &content_type) {9194return cli_->Patch(path, std::move(content_provider), content_type);9195}9196inline Result Client::Patch(const std::string &path, const Headers &headers,9197size_t content_length,9198ContentProvider content_provider,9199const std::string &content_type) {9200return cli_->Patch(path, headers, content_length, std::move(content_provider),9201content_type);9202}9203inline Result Client::Patch(const std::string &path, const Headers &headers,9204ContentProviderWithoutLength content_provider,9205const std::string &content_type) {9206return cli_->Patch(path, headers, std::move(content_provider), content_type);9207}9208inline Result Client::Delete(const std::string &path) {9209return cli_->Delete(path);9210}9211inline Result Client::Delete(const std::string &path, const Headers &headers) {9212return cli_->Delete(path, headers);9213}9214inline Result Client::Delete(const std::string &path, const char *body,9215size_t content_length,9216const std::string &content_type) {9217return cli_->Delete(path, body, content_length, content_type);9218}9219inline Result Client::Delete(const std::string &path, const Headers &headers,9220const char *body, size_t content_length,9221const std::string &content_type) {9222return cli_->Delete(path, headers, body, content_length, content_type);9223}9224inline Result Client::Delete(const std::string &path, const std::string &body,9225const std::string &content_type) {9226return cli_->Delete(path, body, content_type);9227}9228inline Result Client::Delete(const std::string &path, const Headers &headers,9229const std::string &body,9230const std::string &content_type) {9231return cli_->Delete(path, headers, body, content_type);9232}9233inline Result Client::Options(const std::string &path) {9234return cli_->Options(path);9235}9236inline Result Client::Options(const std::string &path, const Headers &headers) {9237return cli_->Options(path, headers);9238}92399240inline bool Client::send(Request &req, Response &res, Error &error) {9241return cli_->send(req, res, error);9242}92439244inline Result Client::send(const Request &req) { return cli_->send(req); }92459246inline void Client::stop() { cli_->stop(); }92479248inline std::string Client::host() const { return cli_->host(); }92499250inline int Client::port() const { return cli_->port(); }92519252inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }92539254inline socket_t Client::socket() const { return cli_->socket(); }92559256inline void9257Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {9258cli_->set_hostname_addr_map(std::move(addr_map));9259}92609261inline void Client::set_default_headers(Headers headers) {9262cli_->set_default_headers(std::move(headers));9263}92649265inline void Client::set_header_writer(9266std::function<ssize_t(Stream &, Headers &)> const &writer) {9267cli_->set_header_writer(writer);9268}92699270inline void Client::set_address_family(int family) {9271cli_->set_address_family(family);9272}92739274inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }92759276inline void Client::set_socket_options(SocketOptions socket_options) {9277cli_->set_socket_options(std::move(socket_options));9278}92799280inline void Client::set_connection_timeout(time_t sec, time_t usec) {9281cli_->set_connection_timeout(sec, usec);9282}92839284inline void Client::set_read_timeout(time_t sec, time_t usec) {9285cli_->set_read_timeout(sec, usec);9286}92879288inline void Client::set_write_timeout(time_t sec, time_t usec) {9289cli_->set_write_timeout(sec, usec);9290}92919292inline void Client::set_basic_auth(const std::string &username,9293const std::string &password) {9294cli_->set_basic_auth(username, password);9295}9296inline void Client::set_bearer_token_auth(const std::string &token) {9297cli_->set_bearer_token_auth(token);9298}9299#ifdef CPPHTTPLIB_OPENSSL_SUPPORT9300inline void Client::set_digest_auth(const std::string &username,9301const std::string &password) {9302cli_->set_digest_auth(username, password);9303}9304#endif93059306inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }9307inline void Client::set_follow_location(bool on) {9308cli_->set_follow_location(on);9309}93109311inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); }93129313inline void Client::set_compress(bool on) { cli_->set_compress(on); }93149315inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }93169317inline void Client::set_interface(const std::string &intf) {9318cli_->set_interface(intf);9319}93209321inline void Client::set_proxy(const std::string &host, int port) {9322cli_->set_proxy(host, port);9323}9324inline void Client::set_proxy_basic_auth(const std::string &username,9325const std::string &password) {9326cli_->set_proxy_basic_auth(username, password);9327}9328inline void Client::set_proxy_bearer_token_auth(const std::string &token) {9329cli_->set_proxy_bearer_token_auth(token);9330}9331#ifdef CPPHTTPLIB_OPENSSL_SUPPORT9332inline void Client::set_proxy_digest_auth(const std::string &username,9333const std::string &password) {9334cli_->set_proxy_digest_auth(username, password);9335}9336#endif93379338#ifdef CPPHTTPLIB_OPENSSL_SUPPORT9339inline void Client::enable_server_certificate_verification(bool enabled) {9340cli_->enable_server_certificate_verification(enabled);9341}9342#endif93439344inline void Client::set_logger(Logger logger) {9345cli_->set_logger(std::move(logger));9346}93479348#ifdef CPPHTTPLIB_OPENSSL_SUPPORT9349inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,9350const std::string &ca_cert_dir_path) {9351cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);9352}93539354inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {9355if (is_ssl_) {9356static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);9357} else {9358cli_->set_ca_cert_store(ca_cert_store);9359}9360}93619362inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {9363set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));9364}93659366inline long Client::get_openssl_verify_result() const {9367if (is_ssl_) {9368return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();9369}9370return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???9371}93729373inline SSL_CTX *Client::ssl_context() const {9374if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }9375return nullptr;9376}9377#endif93789379// ----------------------------------------------------------------------------93809381} // namespace httplib93829383#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)9384#undef poll9385#endif93869387#endif // CPPHTTPLIB_HTTPLIB_H938893899390