From: Joel Rosdahl Date: Wed, 20 Mar 2024 20:45:10 +0000 (+0100) Subject: build: Add support for using system cpp-httplib X-Git-Tag: v4.10~42^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=66e650e5f783a88d9a12a5bf34a9cba7c9bdbddc;p=thirdparty%2Fccache.git build: Add support for using system cpp-httplib --- diff --git a/.clang-format b/.clang-format index 5857583f7..e35a0140c 100644 --- a/.clang-format +++ b/.clang-format @@ -31,7 +31,7 @@ IncludeCategories: - Regex: '^$' + - Regex: '^<(doctest/.*|fmt/.*|hiredis/.*|httplib\.h|zstd\.h)>$' Priority: 3 # System headers: - Regex: '\.h.*>$' diff --git a/CMakeLists.txt b/CMakeLists.txt index d22c05c32..acedac4a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,8 +98,8 @@ include(CodeAnalysis) # # Source code # -add_subdirectory(src/ccache) add_subdirectory(src/third_party) +add_subdirectory(src/ccache) # # ccache executable diff --git a/LICENSE.adoc b/LICENSE.adoc index da8cfa0c8..98b8e18cd 100644 --- a/LICENSE.adoc +++ b/LICENSE.adoc @@ -387,10 +387,10 @@ express Statement of Purpose. ---- -=== src/third_party/httplib.* +=== src/third_party/cpp-httplib/httplib.h cpp-httplib - A C++11 cross-platform HTTP/HTTPS library. Copied from cpp-httplib -v0.15.2. downloaded from https://github.com/yhirose/cpp-httplib[cpp-httplib]. The +v0.15.3 downloaded from https://github.com/yhirose/cpp-httplib[cpp-httplib]. The library has the following license: ---- diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index c951e7598..9d47567a2 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -19,6 +19,7 @@ endif() set(DEPS AUTO CACHE STRING "How to retrieve third party dependencies") set_property(CACHE DEPS PROPERTY STRINGS AUTO DOWNLOAD LOCAL) +find_package(CppHttplib 0.10.6 MODULE REQUIRED) find_package(Fmt 8.0.0 MODULE REQUIRED) find_package(Zstd 1.3.4 MODULE REQUIRED) diff --git a/cmake/FindCppHttplib.cmake b/cmake/FindCppHttplib.cmake new file mode 100644 index 000000000..d74ad3237 --- /dev/null +++ b/cmake/FindCppHttplib.cmake @@ -0,0 +1,33 @@ +mark_as_advanced(CPPHTTPLIB_INCLUDE_DIR) +mark_as_advanced(CPPHTTPLIB_LIBRARY) + +if(DEP_CPPHTTPLIB STREQUAL "BUNDLED") + message(STATUS "Using bundled CppHttplib as requested") +else() + find_path(CPPHTTPLIB_INCLUDE_DIR httplib.h) + if(CPPHTTPLIB_INCLUDE_DIR) + file(READ "${CPPHTTPLIB_INCLUDE_DIR}/httplib.h" _httplib_h) + string(REGEX MATCH "#define CPPHTTPLIB_VERSION \"([0-9]+).([0-9]+).*([0-9]+)\"" _ "${_httplib_h}") + set(_cpphttplib_version_string "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") + if(NOT "${CMAKE_MATCH_0}" STREQUAL "" AND "${_cpphttplib_version_string}" VERSION_GREATER_EQUAL "${CppHttplib_FIND_VERSION}") + # Some dists like Fedora package cpp-httplib as a single header while some + # dists like Debian package it as a traditional library. + find_library(CPPHTTPLIB_LIBRARY cpp-httplib) + if(CPPHTTPLIB_LIBRARY) + message(STATUS "Using system CppHttplib (${CPPHTTPLIB_LIBRARY})") + add_library(dep_cpphttplib UNKNOWN IMPORTED) + set_target_properties(dep_cpphttplib PROPERTIES IMPORTED_LOCATION "${CPPHTTPLIB_LIBRARY}") + set(_cpphttplib_origin "SYSTEM (${CPPHTTPLIB_LIBRARY})") + else() + message(STATUS "Using system CppHttplib (${CPPHTTPLIB_INCLUDE_DIR}/httplib.h)") + add_library(dep_cpphttplib INTERFACE IMPORTED) + set(_cpphttplib_origin "SYSTEM (${CPPHTTPLIB_INCLUDE_DIR}/httplib.h)") + endif() + set_target_properties(dep_cpphttplib PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CPPHTTPLIB_INCLUDE_DIR}") + register_dependency(CppHttplib "${_cpphttplib_origin}" "${_cpphttplib_version_string}") + endif() + endif() + if(NOT TARGET dep_cpphttplib) + message(STATUS "Using bundled CppHttplib since CppHttplib>=${CppHttplib_FIND_VERSION} was not found locally") + endif() +endif() diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 7dd37ba02..45032dae0 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -39,14 +39,16 @@ should be located or retrieved: ### Dependencies -- [doctest](https://github.com/doctest/doctest)[^1] (optional, disable with `-D +- [cpp-httplib](https://github.com/yhirose/cpp-httplib)[^1] +- [doctest](https://github.com/doctest/doctest)[^2] (optional, disable with `-D ENABLE_TESTING=OFF`) -- [fmt](https://fmt.dev)[^1] -- [hiredis](https://github.com/redis/hiredis)[^1] (optional, disable with `-D +- [fmt](https://fmt.dev)[^2] +- [hiredis](https://github.com/redis/hiredis)[^2] (optional, disable with `-D REDIS_STORAGE_BACKEND=OFF`) -- [Zstandard](https://github.com/facebook/zstd)[^1] +- [Zstandard](https://github.com/facebook/zstd)[^2] -[^1]: A downloaded version will be used if missing locally. +[^1]: A bundled version will be used if missing locally. +[^2]: A downloaded version will be used if missing locally. ### Tips diff --git a/src/ccache/CMakeLists.txt b/src/ccache/CMakeLists.txt index 68a831659..b17540539 100644 --- a/src/ccache/CMakeLists.txt +++ b/src/ccache/CMakeLists.txt @@ -48,6 +48,7 @@ target_link_libraries( dep_fmt third_party PRIVATE + dep_cpphttplib dep_zstd standard_settings standard_warnings diff --git a/src/ccache/storage/remote/HttpStorage.cpp b/src/ccache/storage/remote/HttpStorage.cpp index cf469f99e..a74da0ea1 100644 --- a/src/ccache/storage/remote/HttpStorage.cpp +++ b/src/ccache/storage/remote/HttpStorage.cpp @@ -30,6 +30,7 @@ #include #include + #include #include diff --git a/src/third_party/CMakeLists.txt b/src/third_party/CMakeLists.txt index 7e14efbe0..baea1d76c 100644 --- a/src/third_party/CMakeLists.txt +++ b/src/third_party/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(third_party STATIC httplib.cpp url.cpp xxhash.c) +add_library(third_party STATIC url.cpp xxhash.c) target_include_directories(third_party INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) if(MSVC) @@ -55,3 +55,7 @@ if(MSVC) endif() add_subdirectory(blake3) + +if(NOT TARGET dep_cpphttplib) + add_subdirectory(cpp-httplib) +endif() diff --git a/src/third_party/cpp-httplib/CMakeLists.txt b/src/third_party/cpp-httplib/CMakeLists.txt new file mode 100644 index 000000000..a601808f7 --- /dev/null +++ b/src/third_party/cpp-httplib/CMakeLists.txt @@ -0,0 +1,2 @@ +register_dependency(CppHttplib BUNDLED 0.15.3) +add_header_only_library(cpphttplib DIR "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/src/third_party/httplib.cpp b/src/third_party/cpp-httplib/httplib.h similarity index 64% rename from src/third_party/httplib.cpp rename to src/third_party/cpp-httplib/httplib.h index 8f0b7d28f..dfdd260ab 100644 --- a/src/third_party/httplib.cpp +++ b/src/third_party/cpp-httplib/httplib.h @@ -1,13 +1,2293 @@ -#include "httplib.h" +// +// httplib.h +// +// Copyright (c) 2024 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef CPPHTTPLIB_HTTPLIB_H +#define CPPHTTPLIB_HTTPLIB_H + +#define CPPHTTPLIB_VERSION "0.15.3" + +/* + * Configuration + */ + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND +#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND +#ifdef _WIN32 +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 +#else +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 +#endif +#endif + +#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH +#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT +#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 +#endif + +#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT +#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024 +#endif + +#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) +#endif + +#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_RANGE_MAX_COUNT +#define CPPHTTPLIB_RANGE_MAX_COUNT 1024 +#endif + +#ifndef CPPHTTPLIB_TCP_NODELAY +#define CPPHTTPLIB_TCP_NODELAY false +#endif + +#ifndef CPPHTTPLIB_RECV_BUFSIZ +#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) +#endif + +#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ +#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) +#endif + +#ifndef CPPHTTPLIB_THREAD_POOL_COUNT +#define CPPHTTPLIB_THREAD_POOL_COUNT \ + ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ + ? std::thread::hardware_concurrency() - 1 \ + : 0)) +#endif + +#ifndef CPPHTTPLIB_RECV_FLAGS +#define CPPHTTPLIB_RECV_FLAGS 0 +#endif + +#ifndef CPPHTTPLIB_SEND_FLAGS +#define CPPHTTPLIB_SEND_FLAGS 0 +#endif + +#ifndef CPPHTTPLIB_LISTEN_BACKLOG +#define CPPHTTPLIB_LISTEN_BACKLOG 5 +#endif + +/* + * Headers + */ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif //_CRT_SECURE_NO_WARNINGS + +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif //_CRT_NONSTDC_NO_DEPRECATE + +#if defined(_MSC_VER) +#if _MSC_VER < 1900 +#error Sorry, Visual Studio versions prior to 2015 are not supported +#endif + +#pragma comment(lib, "ws2_32.lib") + +#ifdef _WIN64 +using ssize_t = __int64; +#else +using ssize_t = long; +#endif +#endif // _MSC_VER + +#ifndef S_ISREG +#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG) +#endif // S_ISREG + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) +#endif // S_ISDIR + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +#include +#include +#include + +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +using socket_t = SOCKET; +#ifdef CPPHTTPLIB_USE_POLL +#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) +#endif + +#else // not _WIN32 + +#include +#if !defined(_AIX) && !defined(__MVS__) +#include +#endif +#ifdef __MVS__ +#include +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#endif +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include +#ifdef CPPHTTPLIB_USE_POLL +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +using socket_t = int; +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#endif //_WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#ifdef _WIN32 +#include + +// these are defined in wincrypt.h and it breaks compilation if BoringSSL is +// used +#undef X509_NAME +#undef X509_CERT_PAIR +#undef X509_EXTENSIONS +#undef PKCS7_SIGNER_INFO + +#ifdef _MSC_VER +#pragma comment(lib, "crypt32.lib") +#endif +#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) +#include +#if TARGET_OS_OSX +#include +#include +#endif // TARGET_OS_OSX +#endif // _WIN32 + +#include +#include +#include +#include + +#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK) +#include +#endif + +#include +#include + +#if OPENSSL_VERSION_NUMBER < 0x30000000L +#error Sorry, OpenSSL versions prior to 3.0.0 are not supported +#endif + +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +#include +#include +#endif + +/* + * Declaration + */ namespace httplib { +namespace detail { + +/* + * Backport std::make_unique from C++14. + * + * NOTE: This code came up with the following stackoverflow post: + * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique + * + */ + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(Args &&...args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(std::size_t n) { + typedef typename std::remove_extent::type RT; + return std::unique_ptr(new RT[n]); +} + +struct ci { + bool operator()(const std::string &s1, const std::string &s2) const { + return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), + s2.end(), + [](unsigned char c1, unsigned char c2) { + return ::tolower(c1) < ::tolower(c2); + }); + } +}; + +// This is based on +// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". + +struct scope_exit { + explicit scope_exit(std::function &&f) + : exit_function(std::move(f)), execute_on_destruction{true} {} + + scope_exit(scope_exit &&rhs) noexcept + : exit_function(std::move(rhs.exit_function)), + execute_on_destruction{rhs.execute_on_destruction} { + rhs.release(); + } + + ~scope_exit() { + if (execute_on_destruction) { this->exit_function(); } + } + + void release() { this->execute_on_destruction = false; } + +private: + scope_exit(const scope_exit &) = delete; + void operator=(const scope_exit &) = delete; + scope_exit &operator=(scope_exit &&) = delete; + + std::function exit_function; + bool execute_on_destruction; +}; + +} // namespace detail + +enum StatusCode { + // Information responses + Continue_100 = 100, + SwitchingProtocol_101 = 101, + Processing_102 = 102, + EarlyHints_103 = 103, + + // Successful responses + OK_200 = 200, + Created_201 = 201, + Accepted_202 = 202, + NonAuthoritativeInformation_203 = 203, + NoContent_204 = 204, + ResetContent_205 = 205, + PartialContent_206 = 206, + MultiStatus_207 = 207, + AlreadyReported_208 = 208, + IMUsed_226 = 226, + + // Redirection messages + MultipleChoices_300 = 300, + MovedPermanently_301 = 301, + Found_302 = 302, + SeeOther_303 = 303, + NotModified_304 = 304, + UseProxy_305 = 305, + unused_306 = 306, + TemporaryRedirect_307 = 307, + PermanentRedirect_308 = 308, + + // Client error responses + BadRequest_400 = 400, + Unauthorized_401 = 401, + PaymentRequired_402 = 402, + Forbidden_403 = 403, + NotFound_404 = 404, + MethodNotAllowed_405 = 405, + NotAcceptable_406 = 406, + ProxyAuthenticationRequired_407 = 407, + RequestTimeout_408 = 408, + Conflict_409 = 409, + Gone_410 = 410, + LengthRequired_411 = 411, + PreconditionFailed_412 = 412, + PayloadTooLarge_413 = 413, + UriTooLong_414 = 414, + UnsupportedMediaType_415 = 415, + RangeNotSatisfiable_416 = 416, + ExpectationFailed_417 = 417, + ImATeapot_418 = 418, + MisdirectedRequest_421 = 421, + UnprocessableContent_422 = 422, + Locked_423 = 423, + FailedDependency_424 = 424, + TooEarly_425 = 425, + UpgradeRequired_426 = 426, + PreconditionRequired_428 = 428, + TooManyRequests_429 = 429, + RequestHeaderFieldsTooLarge_431 = 431, + UnavailableForLegalReasons_451 = 451, + + // Server error responses + InternalServerError_500 = 500, + NotImplemented_501 = 501, + BadGateway_502 = 502, + ServiceUnavailable_503 = 503, + GatewayTimeout_504 = 504, + HttpVersionNotSupported_505 = 505, + VariantAlsoNegotiates_506 = 506, + InsufficientStorage_507 = 507, + LoopDetected_508 = 508, + NotExtended_510 = 510, + NetworkAuthenticationRequired_511 = 511, +}; + +using Headers = std::multimap; + +using Params = std::multimap; +using Match = std::smatch; + +using Progress = std::function; + +struct Response; +using ResponseHandler = std::function; + +struct MultipartFormData { + std::string name; + std::string content; + std::string filename; + std::string content_type; +}; +using MultipartFormDataItems = std::vector; +using MultipartFormDataMap = std::multimap; + +class DataSink { +public: + DataSink() : os(&sb_), sb_(*this) {} + + DataSink(const DataSink &) = delete; + DataSink &operator=(const DataSink &) = delete; + DataSink(DataSink &&) = delete; + DataSink &operator=(DataSink &&) = delete; + + std::function write; + std::function is_writable; + std::function done; + std::function done_with_trailer; + std::ostream os; + +private: + class data_sink_streambuf : public std::streambuf { + public: + explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {} + + protected: + std::streamsize xsputn(const char *s, std::streamsize n) override { + sink_.write(s, static_cast(n)); + return n; + } + + private: + DataSink &sink_; + }; + + data_sink_streambuf sb_; +}; + +using ContentProvider = + std::function; + +using ContentProviderWithoutLength = + std::function; + +using ContentProviderResourceReleaser = std::function; + +struct MultipartFormDataProvider { + std::string name; + ContentProviderWithoutLength provider; + std::string filename; + std::string content_type; +}; +using MultipartFormDataProviderItems = std::vector; + +using ContentReceiverWithProgress = + std::function; + +using ContentReceiver = + std::function; + +using MultipartContentHeader = + std::function; + +class ContentReader { +public: + using Reader = std::function; + using MultipartReader = std::function; + + ContentReader(Reader reader, MultipartReader multipart_reader) + : reader_(std::move(reader)), + multipart_reader_(std::move(multipart_reader)) {} + + bool operator()(MultipartContentHeader header, + ContentReceiver receiver) const { + return multipart_reader_(std::move(header), std::move(receiver)); + } + + bool operator()(ContentReceiver receiver) const { + return reader_(std::move(receiver)); + } + + Reader reader_; + MultipartReader multipart_reader_; +}; + +using Range = std::pair; +using Ranges = std::vector; + +struct Request { + std::string method; + std::string path; + Headers headers; + std::string body; + + std::string remote_addr; + int remote_port = -1; + std::string local_addr; + int local_port = -1; + + // for server + std::string version; + std::string target; + Params params; + MultipartFormDataMap files; + Ranges ranges; + Match matches; + std::unordered_map path_params; + + // for client + ResponseHandler response_handler; + ContentReceiverWithProgress content_receiver; + Progress progress; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + const SSL *ssl = nullptr; +#endif + + bool has_header(const std::string &key) const; + std::string get_header_value(const std::string &key, size_t id = 0) const; + uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; + size_t get_header_value_count(const std::string &key) const; + void set_header(const std::string &key, const std::string &val); + + bool has_param(const std::string &key) const; + std::string get_param_value(const std::string &key, size_t id = 0) const; + size_t get_param_value_count(const std::string &key) const; + + bool is_multipart_form_data() const; + + bool has_file(const std::string &key) const; + MultipartFormData get_file_value(const std::string &key) const; + std::vector get_file_values(const std::string &key) const; + + // private members... + size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT; + size_t content_length_ = 0; + ContentProvider content_provider_; + bool is_chunked_content_provider_ = false; + size_t authorization_count_ = 0; +}; + +struct Response { + std::string version; + int status = -1; + std::string reason; + Headers headers; + std::string body; + std::string location; // Redirect location + + bool has_header(const std::string &key) const; + std::string get_header_value(const std::string &key, size_t id = 0) const; + uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; + size_t get_header_value_count(const std::string &key) const; + void set_header(const std::string &key, const std::string &val); + + void set_redirect(const std::string &url, int status = StatusCode::Found_302); + void set_content(const char *s, size_t n, const std::string &content_type); + void set_content(const std::string &s, const std::string &content_type); + void set_content(std::string &&s, const std::string &content_type); + + void set_content_provider( + size_t length, const std::string &content_type, ContentProvider provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_chunked_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + Response() = default; + Response(const Response &) = default; + Response &operator=(const Response &) = default; + Response(Response &&) = default; + Response &operator=(Response &&) = default; + ~Response() { + if (content_provider_resource_releaser_) { + content_provider_resource_releaser_(content_provider_success_); + } + } + + // private members... + size_t content_length_ = 0; + ContentProvider content_provider_; + ContentProviderResourceReleaser content_provider_resource_releaser_; + bool is_chunked_content_provider_ = false; + bool content_provider_success_ = false; +}; + +class Stream { +public: + virtual ~Stream() = default; + + virtual bool is_readable() const = 0; + virtual bool is_writable() const = 0; + + virtual ssize_t read(char *ptr, size_t size) = 0; + virtual ssize_t write(const char *ptr, size_t size) = 0; + virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; + virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0; + virtual socket_t socket() const = 0; + + template + ssize_t write_format(const char *fmt, const Args &...args); + ssize_t write(const char *ptr); + ssize_t write(const std::string &s); +}; + +class TaskQueue { +public: + TaskQueue() = default; + virtual ~TaskQueue() = default; + + virtual bool enqueue(std::function fn) = 0; + virtual void shutdown() = 0; + + virtual void on_idle() {} +}; + +class ThreadPool : public TaskQueue { +public: + explicit ThreadPool(size_t n, size_t mqr = 0) + : shutdown_(false), max_queued_requests_(mqr) { + while (n) { + threads_.emplace_back(worker(*this)); + n--; + } + } + + ThreadPool(const ThreadPool &) = delete; + ~ThreadPool() override = default; + + bool enqueue(std::function fn) override { + { + std::unique_lock lock(mutex_); + if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) { + return false; + } + jobs_.push_back(std::move(fn)); + } + + cond_.notify_one(); + return true; + } + + void shutdown() override { + // Stop all worker threads... + { + std::unique_lock lock(mutex_); + shutdown_ = true; + } + + cond_.notify_all(); + + // Join... + for (auto &t : threads_) { + t.join(); + } + } + +private: + struct worker { + explicit worker(ThreadPool &pool) : pool_(pool) {} + + void operator()() { + for (;;) { + std::function fn; + { + std::unique_lock lock(pool_.mutex_); + + pool_.cond_.wait( + lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); + + if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } + + fn = std::move(pool_.jobs_.front()); + pool_.jobs_.pop_front(); + } + + assert(true == static_cast(fn)); + fn(); + } + } + + ThreadPool &pool_; + }; + friend struct worker; + + std::vector threads_; + std::list> jobs_; + + bool shutdown_; + size_t max_queued_requests_ = 0; + + std::condition_variable cond_; + std::mutex mutex_; +}; + +using Logger = std::function; + +using SocketOptions = std::function; + +void default_socket_options(socket_t sock); + +const char *status_message(int status); + +std::string get_bearer_token_auth(const Request &req); + +namespace detail { + +class MatcherBase { +public: + virtual ~MatcherBase() = default; + + // Match request path and populate its matches and + virtual bool match(Request &request) const = 0; +}; + +/** + * Captures parameters in request path and stores them in Request::path_params + * + * Capture name is a substring of a pattern from : to /. + * The rest of the pattern is matched agains the request path directly + * Parameters are captured starting from the next character after + * the end of the last matched static pattern fragment until the next /. + * + * Example pattern: + * "/path/fragments/:capture/more/fragments/:second_capture" + * Static fragments: + * "/path/fragments/", "more/fragments/" + * + * Given the following request path: + * "/path/fragments/:1/more/fragments/:2" + * the resulting capture will be + * {{"capture", "1"}, {"second_capture", "2"}} + */ +class PathParamsMatcher : public MatcherBase { +public: + PathParamsMatcher(const std::string &pattern); + + bool match(Request &request) const override; + +private: + static constexpr char marker = ':'; + // Treat segment separators as the end of path parameter capture + // Does not need to handle query parameters as they are parsed before path + // matching + static constexpr char separator = '/'; + + // Contains static path fragments to match against, excluding the '/' after + // path params + // Fragments are separated by path params + std::vector static_fragments_; + // Stores the names of the path parameters to be used as keys in the + // Request::path_params map + std::vector param_names_; +}; + +/** + * Performs std::regex_match on request path + * and stores the result in Request::matches + * + * Note that regex match is performed directly on the whole request. + * This means that wildcard patterns may match multiple path segments with /: + * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end". + */ +class RegexMatcher : public MatcherBase { +public: + RegexMatcher(const std::string &pattern) : regex_(pattern) {} + + bool match(Request &request) const override; + +private: + std::regex regex_; +}; + +ssize_t write_headers(Stream &strm, const Headers &headers); + +} // namespace detail + +class Server { +public: + using Handler = std::function; + + using ExceptionHandler = + std::function; + + enum class HandlerResponse { + Handled, + Unhandled, + }; + using HandlerWithResponse = + std::function; + + using HandlerWithContentReader = std::function; + + using Expect100ContinueHandler = + std::function; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server &Get(const std::string &pattern, Handler handler); + Server &Post(const std::string &pattern, Handler handler); + Server &Post(const std::string &pattern, HandlerWithContentReader handler); + Server &Put(const std::string &pattern, Handler handler); + Server &Put(const std::string &pattern, HandlerWithContentReader handler); + Server &Patch(const std::string &pattern, Handler handler); + Server &Patch(const std::string &pattern, HandlerWithContentReader handler); + Server &Delete(const std::string &pattern, Handler handler); + Server &Delete(const std::string &pattern, HandlerWithContentReader handler); + Server &Options(const std::string &pattern, Handler handler); + + bool set_base_dir(const std::string &dir, + const std::string &mount_point = std::string()); + bool set_mount_point(const std::string &mount_point, const std::string &dir, + Headers headers = Headers()); + bool remove_mount_point(const std::string &mount_point); + Server &set_file_extension_and_mimetype_mapping(const std::string &ext, + const std::string &mime); + Server &set_default_file_mimetype(const std::string &mime); + Server &set_file_request_handler(Handler handler); + + Server &set_error_handler(HandlerWithResponse handler); + Server &set_error_handler(Handler handler); + Server &set_exception_handler(ExceptionHandler handler); + Server &set_pre_routing_handler(HandlerWithResponse handler); + Server &set_post_routing_handler(Handler handler); + + Server &set_expect_100_continue_handler(Expect100ContinueHandler handler); + Server &set_logger(Logger logger); + + Server &set_address_family(int family); + Server &set_tcp_nodelay(bool on); + Server &set_socket_options(SocketOptions socket_options); + + Server &set_default_headers(Headers headers); + Server & + set_header_writer(std::function const &writer); + + Server &set_keep_alive_max_count(size_t count); + Server &set_keep_alive_timeout(time_t sec); + + Server &set_read_timeout(time_t sec, time_t usec = 0); + template + Server &set_read_timeout(const std::chrono::duration &duration); + + Server &set_write_timeout(time_t sec, time_t usec = 0); + template + Server &set_write_timeout(const std::chrono::duration &duration); + + Server &set_idle_interval(time_t sec, time_t usec = 0); + template + Server &set_idle_interval(const std::chrono::duration &duration); + + Server &set_payload_max_length(size_t length); + + bool bind_to_port(const std::string &host, int port, int socket_flags = 0); + int bind_to_any_port(const std::string &host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const std::string &host, int port, int socket_flags = 0); + + bool is_running() const; + void wait_until_ready() const; + void stop(); + + std::function new_task_queue; + +protected: + bool process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request); + + std::atomic svr_sock_{INVALID_SOCKET}; + size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; + time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; + size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; + +private: + using Handlers = + std::vector, Handler>>; + using HandlersForContentReader = + std::vector, + HandlerWithContentReader>>; + + static std::unique_ptr + make_matcher(const std::string &pattern); + + socket_t create_server_socket(const std::string &host, int port, + int socket_flags, + SocketOptions socket_options) const; + int bind_internal(const std::string &host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request &req, Response &res, Stream &strm); + bool handle_file_request(const Request &req, Response &res, + bool head = false); + bool dispatch_request(Request &req, Response &res, + const Handlers &handlers) const; + bool dispatch_request_for_content_reader( + Request &req, Response &res, ContentReader content_reader, + const HandlersForContentReader &handlers) const; + + bool parse_request_line(const char *s, Request &req) const; + void apply_ranges(const Request &req, Response &res, + std::string &content_type, std::string &boundary) const; + bool write_response(Stream &strm, bool close_connection, Request &req, + Response &res); + bool write_response_with_content(Stream &strm, bool close_connection, + const Request &req, Response &res); + bool write_response_core(Stream &strm, bool close_connection, + const Request &req, Response &res, + bool need_apply_ranges); + bool write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type); + bool read_content(Stream &strm, Request &req, Response &res); + bool + read_content_with_content_receiver(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver); + bool read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) const; + + virtual bool process_and_close_socket(socket_t sock); + + std::atomic is_running_{false}; + std::atomic done_{false}; + + struct MountPointEntry { + std::string mount_point; + std::string base_dir; + Headers headers; + }; + std::vector base_dirs_; + std::map file_extension_and_mimetype_map_; + std::string default_file_mimetype_ = "application/octet-stream"; + Handler file_request_handler_; + + Handlers get_handlers_; + Handlers post_handlers_; + HandlersForContentReader post_handlers_for_content_reader_; + Handlers put_handlers_; + HandlersForContentReader put_handlers_for_content_reader_; + Handlers patch_handlers_; + HandlersForContentReader patch_handlers_for_content_reader_; + Handlers delete_handlers_; + HandlersForContentReader delete_handlers_for_content_reader_; + Handlers options_handlers_; + + HandlerWithResponse error_handler_; + ExceptionHandler exception_handler_; + HandlerWithResponse pre_routing_handler_; + Handler post_routing_handler_; + Expect100ContinueHandler expect_100_continue_handler_; + + Logger logger_; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = default_socket_options; + + Headers default_headers_; + std::function header_writer_ = + detail::write_headers; +}; + +enum class Error { + Success = 0, + Unknown, + Connection, + BindIPAddress, + Read, + Write, + ExceedRedirectCount, + Canceled, + SSLConnection, + SSLLoadingCerts, + SSLServerVerification, + UnsupportedMultipartBoundaryChars, + Compression, + ConnectionTimeout, + ProxyConnection, + + // For internal use only + SSLPeerCouldBeClosed_, +}; + +std::string to_string(Error error); + +std::ostream &operator<<(std::ostream &os, const Error &obj); + +class Result { +public: + Result() = default; + Result(std::unique_ptr &&res, Error err, + Headers &&request_headers = Headers{}) + : res_(std::move(res)), err_(err), + request_headers_(std::move(request_headers)) {} + // Response + operator bool() const { return res_ != nullptr; } + bool operator==(std::nullptr_t) const { return res_ == nullptr; } + bool operator!=(std::nullptr_t) const { return res_ != nullptr; } + const Response &value() const { return *res_; } + Response &value() { return *res_; } + const Response &operator*() const { return *res_; } + Response &operator*() { return *res_; } + const Response *operator->() const { return res_.get(); } + Response *operator->() { return res_.get(); } + + // Error + Error error() const { return err_; } + + // Request Headers + bool has_request_header(const std::string &key) const; + std::string get_request_header_value(const std::string &key, + size_t id = 0) const; + uint64_t get_request_header_value_u64(const std::string &key, + size_t id = 0) const; + size_t get_request_header_value_count(const std::string &key) const; + +private: + std::unique_ptr res_; + Error err_ = Error::Unknown; + Headers request_headers_; +}; + +class ClientImpl { +public: + explicit ClientImpl(const std::string &host); + + explicit ClientImpl(const std::string &host, int port); + + explicit ClientImpl(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + virtual ~ClientImpl(); + + virtual bool is_valid() const; + + Result Get(const std::string &path); + Result Get(const std::string &path, const Headers &headers); + Result Get(const std::string &path, Progress progress); + Result Get(const std::string &path, const Headers &headers, + Progress progress); + Result Get(const std::string &path, ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const std::string &path, ContentReceiver content_receiver, + Progress progress); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ContentReceiver content_receiver, + Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress = nullptr); + + Result Head(const std::string &path); + Result Head(const std::string &path, const Headers &headers); + + Result Post(const std::string &path); + Result Post(const std::string &path, const Headers &headers); + Result Post(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Post(const std::string &path, const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Put(const std::string &path); + Result Put(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Put(const std::string &path, size_t content_length, + ContentProvider content_provider, const std::string &content_type); + Result Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Put(const std::string &path, const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Patch(const std::string &path); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + + Result Delete(const std::string &path); + Result Delete(const std::string &path, const Headers &headers); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + + Result Options(const std::string &path); + Result Options(const std::string &path, const Headers &headers); + + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); + + void stop(); + + std::string host() const; + int port() const; + + size_t is_socket_open() const; + socket_t socket() const; + + void set_hostname_addr_map(std::map addr_map); + + void set_default_headers(Headers headers); + + void + set_header_writer(std::function const &writer); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const std::string &username, const std::string &password); + void set_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const std::string &username, + const std::string &password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const std::string &intf); + + void set_proxy(const std::string &host, int port); + void set_proxy_basic_auth(const std::string &username, + const std::string &password); + void set_proxy_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const std::string &username, + const std::string &password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path = std::string()); + void set_ca_cert_store(X509_STORE *ca_cert_store); + X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + +protected: + struct Socket { + socket_t sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSL *ssl = nullptr; +#endif + + bool is_open() const { return sock != INVALID_SOCKET; } + }; + + virtual bool create_and_connect_socket(Socket &socket, Error &error); + + // All of: + // shutdown_ssl + // shutdown_socket + // close_socket + // should ONLY be called when socket_mutex_ is locked. + // Also, shutdown_ssl and close_socket should also NOT be called concurrently + // with a DIFFERENT thread sending requests using that socket. + virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully); + void shutdown_socket(Socket &socket) const; + void close_socket(Socket &socket); + + bool process_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + + bool write_content_with_provider(Stream &strm, const Request &req, + Error &error) const; + + void copy_settings(const ClientImpl &rhs); + + // Socket endpoint information + const std::string host_; + const int port_; + const std::string host_and_port_; + + // Current open socket + Socket socket_; + mutable std::mutex socket_mutex_; + std::recursive_mutex request_mutex_; + + // These are all protected under socket_mutex + size_t socket_requests_in_flight_ = 0; + std::thread::id socket_requests_are_from_thread_ = std::thread::id(); + bool socket_should_be_closed_when_request_is_done_ = false; + + // Hostname-IP map + std::map addr_map_; + + // Default headers + Headers default_headers_; + + // Header writer + std::function header_writer_ = + detail::write_headers; + + // Settings + std::string client_cert_path_; + std::string client_key_path_; + + time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; + time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + + std::string basic_auth_username_; + std::string basic_auth_password_; + std::string bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string digest_auth_username_; + std::string digest_auth_password_; +#endif + + bool keep_alive_ = false; + bool follow_location_ = false; + + bool url_encode_ = true; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = nullptr; + + bool compress_ = false; + bool decompress_ = true; + + std::string interface_; + + std::string proxy_host_; + int proxy_port_ = -1; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; + std::string proxy_bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string proxy_digest_auth_username_; + std::string proxy_digest_auth_password_; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + + X509_STORE *ca_cert_store_ = nullptr; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool server_certificate_verification_ = true; +#endif + + Logger logger_; + +private: + bool send_(Request &req, Response &res, Error &error); + Result send_(Request &&req); + + socket_t create_client_socket(Error &error) const; + bool read_response_line(Stream &strm, const Request &req, + Response &res) const; + bool write_request(Stream &strm, Request &req, bool close_connection, + Error &error); + bool redirect(Request &req, Response &res, Error &error); + bool handle_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + std::unique_ptr send_with_content_provider( + Request &req, const char *body, size_t content_length, + ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type, Error &error); + Result send_with_content_provider( + const std::string &method, const std::string &path, + const Headers &headers, const char *body, size_t content_length, + ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type); + ContentProviderWithoutLength get_multipart_content_provider( + const std::string &boundary, const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) const; + + std::string adjust_host_string(const std::string &host) const; + + virtual bool process_socket(const Socket &socket, + std::function callback); + virtual bool is_ssl() const; +}; + +class Client { +public: + // Universal interface + explicit Client(const std::string &scheme_host_port); + + explicit Client(const std::string &scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path); + + // HTTP only interface + explicit Client(const std::string &host, int port); + + explicit Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + Client(Client &&) = default; + + ~Client(); + + bool is_valid() const; + + Result Get(const std::string &path); + Result Get(const std::string &path, const Headers &headers); + Result Get(const std::string &path, Progress progress); + Result Get(const std::string &path, const Headers &headers, + Progress progress); + Result Get(const std::string &path, ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const std::string &path, ContentReceiver content_receiver, + Progress progress); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ContentReceiver content_receiver, + Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress = nullptr); + + Result Head(const std::string &path); + Result Head(const std::string &path, const Headers &headers); + + Result Post(const std::string &path); + Result Post(const std::string &path, const Headers &headers); + Result Post(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Post(const std::string &path, const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Put(const std::string &path); + Result Put(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Put(const std::string &path, size_t content_length, + ContentProvider content_provider, const std::string &content_type); + Result Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Put(const std::string &path, const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Patch(const std::string &path); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + + Result Delete(const std::string &path); + Result Delete(const std::string &path, const Headers &headers); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + + Result Options(const std::string &path); + Result Options(const std::string &path, const Headers &headers); + + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); + + void stop(); + + std::string host() const; + int port() const; + + size_t is_socket_open() const; + socket_t socket() const; + + void set_hostname_addr_map(std::map addr_map); + + void set_default_headers(Headers headers); + + void + set_header_writer(std::function const &writer); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const std::string &username, const std::string &password); + void set_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const std::string &username, + const std::string &password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const std::string &intf); + + void set_proxy(const std::string &host, int port); + void set_proxy_basic_auth(const std::string &username, + const std::string &password); + void set_proxy_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const std::string &username, + const std::string &password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + + // SSL +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path = std::string()); + + void set_ca_cert_store(X509_STORE *ca_cert_store); + void load_ca_cert_store(const char *ca_cert, std::size_t size); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; +#endif + +private: + std::unique_ptr cli_; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool is_ssl_ = false; +#endif +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLServer : public Server { +public: + SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path = nullptr, + const char *client_ca_cert_dir_path = nullptr, + const char *private_key_password = nullptr); + + SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store = nullptr); + + SSLServer( + const std::function &setup_ssl_ctx_callback); + + ~SSLServer() override; + + bool is_valid() const override; + + SSL_CTX *ssl_context() const; + +private: + bool process_and_close_socket(socket_t sock) override; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; +}; + +class SSLClient : public ClientImpl { +public: + explicit SSLClient(const std::string &host); + + explicit SSLClient(const std::string &host, int port); + + explicit SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + explicit SSLClient(const std::string &host, int port, X509 *client_cert, + EVP_PKEY *client_key); + + ~SSLClient() override; + + bool is_valid() const override; + + void set_ca_cert_store(X509_STORE *ca_cert_store); + void load_ca_cert_store(const char *ca_cert, std::size_t size); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; + +private: + bool create_and_connect_socket(Socket &socket, Error &error) override; + void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override; + void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully); + + bool process_socket(const Socket &socket, + std::function callback) override; + bool is_ssl() const override; + + bool connect_with_proxy(Socket &sock, Response &res, bool &success, + Error &error); + bool initialize_ssl(Socket &socket, Error &error); + + bool load_certs(); + + bool verify_host(X509 *server_cert) const; + bool verify_host_with_subject_alt_name(X509 *server_cert) const; + bool verify_host_with_common_name(X509 *server_cert) const; + bool check_host_name(const char *pattern, size_t pattern_len) const; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; + std::once_flag initialize_cert_; + + std::vector host_components_; + + long verify_result_ = 0; + + friend class ClientImpl; +}; +#endif + +/* + * Implementation of template methods. + */ + +namespace detail { + +template +inline void duration_to_sec_and_usec(const T &duration, U callback) { + auto sec = std::chrono::duration_cast(duration).count(); + auto usec = std::chrono::duration_cast( + duration - std::chrono::seconds(sec)) + .count(); + callback(static_cast(sec), static_cast(usec)); +} + +inline uint64_t get_header_value_u64(const Headers &headers, + const std::string &key, size_t id, + uint64_t def) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; +} + +} // namespace detail + +inline uint64_t Request::get_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(headers, key, id, 0); +} + +inline uint64_t Response::get_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(headers, key, id, 0); +} + +template +inline ssize_t Stream::write_format(const char *fmt, const Args &...args) { + const auto bufsiz = 2048; + std::array buf{}; + + auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); + if (sn <= 0) { return sn; } + + auto n = static_cast(sn); + + if (n >= buf.size() - 1) { + std::vector glowable_buf(buf.size()); + + while (n >= glowable_buf.size() - 1) { + glowable_buf.resize(glowable_buf.size() * 2); + n = static_cast( + snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); + } + return write(&glowable_buf[0], n); + } else { + return write(buf.data(), n); + } +} + +inline void default_socket_options(socket_t sock) { + int yes = 1; +#ifdef _WIN32 + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&yes), sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + reinterpret_cast(&yes), sizeof(yes)); +#else +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + reinterpret_cast(&yes), sizeof(yes)); +#else + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&yes), sizeof(yes)); +#endif +#endif +} + +inline const char *status_message(int status) { + switch (status) { + case StatusCode::Continue_100: return "Continue"; + case StatusCode::SwitchingProtocol_101: return "Switching Protocol"; + case StatusCode::Processing_102: return "Processing"; + case StatusCode::EarlyHints_103: return "Early Hints"; + case StatusCode::OK_200: return "OK"; + case StatusCode::Created_201: return "Created"; + case StatusCode::Accepted_202: return "Accepted"; + case StatusCode::NonAuthoritativeInformation_203: + return "Non-Authoritative Information"; + case StatusCode::NoContent_204: return "No Content"; + case StatusCode::ResetContent_205: return "Reset Content"; + case StatusCode::PartialContent_206: return "Partial Content"; + case StatusCode::MultiStatus_207: return "Multi-Status"; + case StatusCode::AlreadyReported_208: return "Already Reported"; + case StatusCode::IMUsed_226: return "IM Used"; + case StatusCode::MultipleChoices_300: return "Multiple Choices"; + case StatusCode::MovedPermanently_301: return "Moved Permanently"; + case StatusCode::Found_302: return "Found"; + case StatusCode::SeeOther_303: return "See Other"; + case StatusCode::NotModified_304: return "Not Modified"; + case StatusCode::UseProxy_305: return "Use Proxy"; + case StatusCode::unused_306: return "unused"; + case StatusCode::TemporaryRedirect_307: return "Temporary Redirect"; + case StatusCode::PermanentRedirect_308: return "Permanent Redirect"; + case StatusCode::BadRequest_400: return "Bad Request"; + case StatusCode::Unauthorized_401: return "Unauthorized"; + case StatusCode::PaymentRequired_402: return "Payment Required"; + case StatusCode::Forbidden_403: return "Forbidden"; + case StatusCode::NotFound_404: return "Not Found"; + case StatusCode::MethodNotAllowed_405: return "Method Not Allowed"; + case StatusCode::NotAcceptable_406: return "Not Acceptable"; + case StatusCode::ProxyAuthenticationRequired_407: + return "Proxy Authentication Required"; + case StatusCode::RequestTimeout_408: return "Request Timeout"; + case StatusCode::Conflict_409: return "Conflict"; + case StatusCode::Gone_410: return "Gone"; + case StatusCode::LengthRequired_411: return "Length Required"; + case StatusCode::PreconditionFailed_412: return "Precondition Failed"; + case StatusCode::PayloadTooLarge_413: return "Payload Too Large"; + case StatusCode::UriTooLong_414: return "URI Too Long"; + case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type"; + case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable"; + case StatusCode::ExpectationFailed_417: return "Expectation Failed"; + case StatusCode::ImATeapot_418: return "I'm a teapot"; + case StatusCode::MisdirectedRequest_421: return "Misdirected Request"; + case StatusCode::UnprocessableContent_422: return "Unprocessable Content"; + case StatusCode::Locked_423: return "Locked"; + case StatusCode::FailedDependency_424: return "Failed Dependency"; + case StatusCode::TooEarly_425: return "Too Early"; + case StatusCode::UpgradeRequired_426: return "Upgrade Required"; + case StatusCode::PreconditionRequired_428: return "Precondition Required"; + case StatusCode::TooManyRequests_429: return "Too Many Requests"; + case StatusCode::RequestHeaderFieldsTooLarge_431: + return "Request Header Fields Too Large"; + case StatusCode::UnavailableForLegalReasons_451: + return "Unavailable For Legal Reasons"; + case StatusCode::NotImplemented_501: return "Not Implemented"; + case StatusCode::BadGateway_502: return "Bad Gateway"; + case StatusCode::ServiceUnavailable_503: return "Service Unavailable"; + case StatusCode::GatewayTimeout_504: return "Gateway Timeout"; + case StatusCode::HttpVersionNotSupported_505: + return "HTTP Version Not Supported"; + case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates"; + case StatusCode::InsufficientStorage_507: return "Insufficient Storage"; + case StatusCode::LoopDetected_508: return "Loop Detected"; + case StatusCode::NotExtended_510: return "Not Extended"; + case StatusCode::NetworkAuthenticationRequired_511: + return "Network Authentication Required"; + + default: + case StatusCode::InternalServerError_500: return "Internal Server Error"; + } +} + +inline std::string get_bearer_token_auth(const Request &req) { + if (req.has_header("Authorization")) { + static std::string BearerHeaderPrefix = "Bearer "; + return req.get_header_value("Authorization") + .substr(BearerHeaderPrefix.length()); + } + return ""; +} + +template +inline Server & +Server::set_read_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); + return *this; +} + +template +inline Server & +Server::set_write_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); + return *this; +} + +template +inline Server & +Server::set_idle_interval(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); }); + return *this; +} + +inline std::string to_string(const Error error) { + switch (error) { + case Error::Success: return "Success (no error)"; + case Error::Connection: return "Could not establish connection"; + case Error::BindIPAddress: return "Failed to bind IP address"; + case Error::Read: return "Failed to read connection"; + case Error::Write: return "Failed to write connection"; + case Error::ExceedRedirectCount: return "Maximum redirect count exceeded"; + case Error::Canceled: return "Connection handling canceled"; + case Error::SSLConnection: return "SSL connection failed"; + case Error::SSLLoadingCerts: return "SSL certificate loading failed"; + case Error::SSLServerVerification: return "SSL server verification failed"; + case Error::UnsupportedMultipartBoundaryChars: + return "Unsupported HTTP multipart boundary characters"; + case Error::Compression: return "Compression failed"; + case Error::ConnectionTimeout: return "Connection timed out"; + case Error::ProxyConnection: return "Proxy connection failed"; + case Error::Unknown: return "Unknown"; + default: break; + } + + return "Invalid"; +} + +inline std::ostream &operator<<(std::ostream &os, const Error &obj) { + os << to_string(obj); + os << " (" << static_cast::type>(obj) << ')'; + return os; +} + +inline uint64_t Result::get_request_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(request_headers_, key, id, 0); +} + +template +inline void ClientImpl::set_connection_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { + set_connection_timeout(sec, usec); + }); +} + +template +inline void ClientImpl::set_read_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); +} + +template +inline void ClientImpl::set_write_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); +} + +template +inline void Client::set_connection_timeout( + const std::chrono::duration &duration) { + cli_->set_connection_timeout(duration); +} + +template +inline void +Client::set_read_timeout(const std::chrono::duration &duration) { + cli_->set_read_timeout(duration); +} + +template +inline void +Client::set_write_timeout(const std::chrono::duration &duration) { + cli_->set_write_timeout(duration); +} + +/* + * Forward declarations and types that will be part of the .h file if split into + * .h + .cc. + */ + +std::string hosted_at(const std::string &hostname); + +void hosted_at(const std::string &hostname, std::vector &addrs); + +std::string append_query_params(const std::string &path, const Params ¶ms); + +std::pair make_range_header(const Ranges &ranges); + +std::pair +make_basic_authentication_header(const std::string &username, + const std::string &password, + bool is_proxy = false); + +namespace detail { + +std::string encode_query_param(const std::string &value); + +std::string decode_url(const std::string &s, bool convert_plus_to_space); + +void read_file(const std::string &path, std::string &out); + +std::string trim_copy(const std::string &s); + +void split(const char *b, const char *e, char d, + std::function fn); + +void split(const char *b, const char *e, char d, size_t m, + std::function fn); + +bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, + std::function callback); + +socket_t create_client_socket( + const std::string &host, const std::string &ip, int port, + int address_family, bool tcp_nodelay, SocketOptions socket_options, + time_t connection_timeout_sec, time_t connection_timeout_usec, + time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, const std::string &intf, Error &error); + +const char *get_header_value(const Headers &headers, const std::string &key, + size_t id = 0, const char *def = nullptr); + +std::string params_to_query_str(const Params ¶ms); + +void parse_query_text(const std::string &s, Params ¶ms); + +bool parse_multipart_boundary(const std::string &content_type, + std::string &boundary); + +bool parse_range_header(const std::string &s, Ranges &ranges); + +int close_socket(socket_t sock); + +ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags); + +ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags); + +enum class EncodingType { None = 0, Gzip, Brotli }; + +EncodingType encoding_type(const Request &req, const Response &res); + +class BufferStream : public Stream { +public: + BufferStream() = default; + ~BufferStream() override = default; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + void get_local_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + + const std::string &get_buffer() const; + +private: + std::string buffer; + size_t position = 0; +}; + +class compressor { +public: + virtual ~compressor() = default; + + typedef std::function Callback; + virtual bool compress(const char *data, size_t data_length, bool last, + Callback callback) = 0; +}; + +class decompressor { +public: + virtual ~decompressor() = default; + + virtual bool is_valid() const = 0; + + typedef std::function Callback; + virtual bool decompress(const char *data, size_t data_length, + Callback callback) = 0; +}; + +class nocompressor : public compressor { +public: + ~nocompressor() override = default; + + bool compress(const char *data, size_t data_length, bool /*last*/, + Callback callback) override; +}; + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +class gzip_compressor : public compressor { +public: + gzip_compressor(); + ~gzip_compressor() override; + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override; + +private: + bool is_valid_ = false; + z_stream strm_; +}; + +class gzip_decompressor : public decompressor { +public: + gzip_decompressor(); + ~gzip_decompressor() override; + + bool is_valid() const override; + + bool decompress(const char *data, size_t data_length, + Callback callback) override; + +private: + bool is_valid_ = false; + z_stream strm_; +}; +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +class brotli_compressor : public compressor { +public: + brotli_compressor(); + ~brotli_compressor(); + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override; + +private: + BrotliEncoderState *state_ = nullptr; +}; + +class brotli_decompressor : public decompressor { +public: + brotli_decompressor(); + ~brotli_decompressor(); + + bool is_valid() const override; + + bool decompress(const char *data, size_t data_length, + Callback callback) override; + +private: + BrotliDecoderResult decoder_r; + BrotliDecoderState *decoder_s = nullptr; +}; +#endif + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { +public: + stream_line_reader(Stream &strm, char *fixed_buffer, + size_t fixed_buffer_size); + const char *ptr() const; + size_t size() const; + bool end_with_crlf() const; + bool getline(); + +private: + void append(char c); + + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_ = 0; + std::string glowable_buffer_; +}; + +class mmap { +public: + mmap(const char *path); + ~mmap(); + + bool open(const char *path); + void close(); + + bool is_open() const; + size_t size() const; + const char *data() const; + +private: +#if defined(_WIN32) + HANDLE hFile_; + HANDLE hMapping_; +#else + int fd_; +#endif + size_t size_; + void *addr_; +}; + +} // namespace detail + +// ---------------------------------------------------------------------------- + /* * Implementation that will be part of the .cc file if split into .h + .cc. */ namespace detail { -bool is_hex(char c, int &v) { +inline bool is_hex(char c, int &v) { if (0x20 <= c && isdigit(c)) { v = c - '0'; return true; @@ -21,7 +2301,7 @@ bool is_hex(char c, int &v) { return false; } -bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, +inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, int &val) { if (i >= s.size()) { return false; } @@ -38,7 +2318,7 @@ bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, return true; } -std::string from_i_to_hex(size_t n) { +inline std::string from_i_to_hex(size_t n) { static const auto charset = "0123456789abcdef"; std::string ret; do { @@ -48,7 +2328,7 @@ std::string from_i_to_hex(size_t n) { return ret; } -size_t to_utf8(int code, char *buff) { +inline size_t to_utf8(int code, char *buff) { if (code < 0x0080) { buff[0] = static_cast(code & 0x7F); return 1; @@ -82,7 +2362,7 @@ size_t to_utf8(int code, char *buff) { // NOTE: This code came up with the following stackoverflow post: // https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c -std::string base64_encode(const std::string &in) { +inline std::string base64_encode(const std::string &in) { static const auto lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -110,7 +2390,7 @@ std::string base64_encode(const std::string &in) { return out; } -bool is_file(const std::string &path) { +inline bool is_file(const std::string &path) { #ifdef _WIN32 return _access_s(path.c_str(), 0) == 0; #else @@ -119,12 +2399,12 @@ bool is_file(const std::string &path) { #endif } -bool is_dir(const std::string &path) { +inline bool is_dir(const std::string &path) { struct stat st; return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); } -bool is_valid_path(const std::string &path) { +inline bool is_valid_path(const std::string &path) { size_t level = 0; size_t i = 0; @@ -166,7 +2446,7 @@ bool is_valid_path(const std::string &path) { return true; } -std::string encode_query_param(const std::string &value) { +inline std::string encode_query_param(const std::string &value) { std::ostringstream escaped; escaped.fill('0'); escaped << std::hex; @@ -187,7 +2467,7 @@ std::string encode_query_param(const std::string &value) { return escaped.str(); } -std::string encode_url(const std::string &s) { +inline std::string encode_url(const std::string &s) { std::string result; result.reserve(s.size()); @@ -219,7 +2499,7 @@ std::string encode_url(const std::string &s) { return result; } -std::string decode_url(const std::string &s, +inline std::string decode_url(const std::string &s, bool convert_plus_to_space) { std::string result; @@ -256,7 +2536,7 @@ std::string decode_url(const std::string &s, return result; } -void read_file(const std::string &path, std::string &out) { +inline void read_file(const std::string &path, std::string &out) { std::ifstream fs(path, std::ios_base::binary); fs.seekg(0, std::ios_base::end); auto size = fs.tellg(); @@ -265,16 +2545,16 @@ void read_file(const std::string &path, std::string &out) { fs.read(&out[0], static_cast(size)); } -std::string file_extension(const std::string &path) { +inline std::string file_extension(const std::string &path) { std::smatch m; static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); if (std::regex_search(path, m, re)) { return m[1].str(); } return std::string(); } -bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } +inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } -std::pair trim(const char *b, const char *e, size_t left, +inline std::pair trim(const char *b, const char *e, size_t left, size_t right) { while (b + left < e && is_space_or_tab(b[left])) { left++; @@ -285,24 +2565,24 @@ std::pair trim(const char *b, const char *e, size_t left, return std::make_pair(left, right); } -std::string trim_copy(const std::string &s) { +inline std::string trim_copy(const std::string &s) { auto r = trim(s.data(), s.data() + s.size(), 0, s.size()); return s.substr(r.first, r.second - r.first); } -std::string trim_double_quotes_copy(const std::string &s) { +inline std::string trim_double_quotes_copy(const std::string &s) { if (s.length() >= 2 && s.front() == '"' && s.back() == '"') { return s.substr(1, s.size() - 2); } return s; } -void split(const char *b, const char *e, char d, +inline void split(const char *b, const char *e, char d, std::function fn) { return split(b, e, d, (std::numeric_limits::max)(), std::move(fn)); } -void split(const char *b, const char *e, char d, size_t m, +inline void split(const char *b, const char *e, char d, size_t m, std::function fn) { size_t i = 0; size_t beg = 0; @@ -324,12 +2604,12 @@ void split(const char *b, const char *e, char d, size_t m, } } -stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer, +inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size) {} -const char *stream_line_reader::ptr() const { +inline const char *stream_line_reader::ptr() const { if (glowable_buffer_.empty()) { return fixed_buffer_; } else { @@ -337,7 +2617,7 @@ const char *stream_line_reader::ptr() const { } } -size_t stream_line_reader::size() const { +inline size_t stream_line_reader::size() const { if (glowable_buffer_.empty()) { return fixed_buffer_used_size_; } else { @@ -345,12 +2625,12 @@ size_t stream_line_reader::size() const { } } -bool stream_line_reader::end_with_crlf() const { +inline bool stream_line_reader::end_with_crlf() const { auto end = ptr() + size(); return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; } -bool stream_line_reader::getline() { +inline bool stream_line_reader::getline() { fixed_buffer_used_size_ = 0; glowable_buffer_.clear(); @@ -376,7 +2656,7 @@ bool stream_line_reader::getline() { return true; } -void stream_line_reader::append(char c) { +inline void stream_line_reader::append(char c) { if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { fixed_buffer_[fixed_buffer_used_size_++] = c; fixed_buffer_[fixed_buffer_used_size_] = '\0'; @@ -389,7 +2669,7 @@ void stream_line_reader::append(char c) { } } -mmap::mmap(const char *path) +inline mmap::mmap(const char *path) #if defined(_WIN32) : hFile_(NULL), hMapping_(NULL) #else @@ -400,9 +2680,9 @@ mmap::mmap(const char *path) open(path); } -mmap::~mmap() { close(); } +inline mmap::~mmap() { close(); } -bool mmap::open(const char *path) { +inline bool mmap::open(const char *path) { close(); #if defined(_WIN32) @@ -443,15 +2723,15 @@ bool mmap::open(const char *path) { return true; } -bool mmap::is_open() const { return addr_ != nullptr; } +inline bool mmap::is_open() const { return addr_ != nullptr; } -size_t mmap::size() const { return size_; } +inline size_t mmap::size() const { return size_; } -const char *mmap::data() const { +inline const char *mmap::data() const { return static_cast(addr_); } -void mmap::close() { +inline void mmap::close() { #if defined(_WIN32) if (addr_) { ::UnmapViewOfFile(addr_); @@ -480,7 +2760,7 @@ void mmap::close() { #endif size_ = 0; } -int close_socket(socket_t sock) { +inline int close_socket(socket_t sock) { #ifdef _WIN32 return closesocket(sock); #else @@ -488,7 +2768,7 @@ int close_socket(socket_t sock) { #endif } -template ssize_t handle_EINTR(T fn) { +template inline ssize_t handle_EINTR(T fn) { ssize_t res = 0; while (true) { res = fn(); @@ -498,7 +2778,7 @@ template ssize_t handle_EINTR(T fn) { return res; } -ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) { +inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) { return handle_EINTR([&]() { return recv(sock, #ifdef _WIN32 @@ -510,7 +2790,7 @@ ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) { }); } -ssize_t send_socket(socket_t sock, const void *ptr, size_t size, +inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags) { return handle_EINTR([&]() { return send(sock, @@ -523,7 +2803,7 @@ ssize_t send_socket(socket_t sock, const void *ptr, size_t size, }); } -ssize_t select_read(socket_t sock, time_t sec, time_t usec) { +inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { #ifdef CPPHTTPLIB_USE_POLL struct pollfd pfd_read; pfd_read.fd = sock; @@ -551,7 +2831,7 @@ ssize_t select_read(socket_t sock, time_t sec, time_t usec) { #endif } -ssize_t select_write(socket_t sock, time_t sec, time_t usec) { +inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { #ifdef CPPHTTPLIB_USE_POLL struct pollfd pfd_read; pfd_read.fd = sock; @@ -579,7 +2859,7 @@ ssize_t select_write(socket_t sock, time_t sec, time_t usec) { #endif } -Error wait_until_socket_is_ready(socket_t sock, time_t sec, +inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { #ifdef CPPHTTPLIB_USE_POLL struct pollfd pfd_read; @@ -636,7 +2916,7 @@ Error wait_until_socket_is_ready(socket_t sock, time_t sec, #endif } -bool is_socket_alive(socket_t sock) { +inline bool is_socket_alive(socket_t sock) { const auto val = detail::select_read(sock, 0, 0); if (val == 0) { return true; @@ -701,7 +2981,7 @@ private: }; #endif -bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) { +inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) { using namespace std::chrono; auto start = steady_clock::now(); while (true) { @@ -721,7 +3001,7 @@ bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) { } template -bool +inline bool process_server_socket_core(const std::atomic &svr_sock, socket_t sock, size_t keep_alive_max_count, time_t keep_alive_timeout_sec, T callback) { @@ -740,7 +3020,7 @@ process_server_socket_core(const std::atomic &svr_sock, socket_t sock, } template -bool +inline bool process_server_socket(const std::atomic &svr_sock, socket_t sock, size_t keep_alive_max_count, time_t keep_alive_timeout_sec, time_t read_timeout_sec, @@ -755,7 +3035,7 @@ process_server_socket(const std::atomic &svr_sock, socket_t sock, }); } -bool process_client_socket(socket_t sock, time_t read_timeout_sec, +inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec, @@ -765,7 +3045,7 @@ bool process_client_socket(socket_t sock, time_t read_timeout_sec, return callback(strm); } -int shutdown_socket(socket_t sock) { +inline int shutdown_socket(socket_t sock) { #ifdef _WIN32 return shutdown(sock, SD_BOTH); #else @@ -906,7 +3186,7 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port, return INVALID_SOCKET; } -void set_nonblocking(socket_t sock, bool nonblocking) { +inline void set_nonblocking(socket_t sock, bool nonblocking) { #ifdef _WIN32 auto flags = nonblocking ? 1UL : 0UL; ioctlsocket(sock, FIONBIO, &flags); @@ -917,7 +3197,7 @@ void set_nonblocking(socket_t sock, bool nonblocking) { #endif } -bool is_connection_error() { +inline bool is_connection_error() { #ifdef _WIN32 return WSAGetLastError() != WSAEWOULDBLOCK; #else @@ -925,7 +3205,7 @@ bool is_connection_error() { #endif } -bool bind_ip_address(socket_t sock, const std::string &host) { +inline bool bind_ip_address(socket_t sock, const std::string &host) { struct addrinfo hints; struct addrinfo *result; @@ -954,7 +3234,7 @@ bool bind_ip_address(socket_t sock, const std::string &host) { #endif #ifdef USE_IF2IP -std::string if2ip(int address_family, const std::string &ifn) { +inline std::string if2ip(int address_family, const std::string &ifn) { struct ifaddrs *ifap; getifaddrs(&ifap); std::string addr_candidate; @@ -992,7 +3272,7 @@ std::string if2ip(int address_family, const std::string &ifn) { } #endif -socket_t create_client_socket( +inline socket_t create_client_socket( const std::string &host, const std::string &ip, int port, int address_family, bool tcp_nodelay, SocketOptions socket_options, time_t connection_timeout_sec, time_t connection_timeout_usec, @@ -1072,7 +3352,7 @@ socket_t create_client_socket( return sock; } -bool get_ip_and_port(const struct sockaddr_storage &addr, +inline bool get_ip_and_port(const struct sockaddr_storage &addr, socklen_t addr_len, std::string &ip, int &port) { if (addr.ss_family == AF_INET) { port = ntohs(reinterpret_cast(&addr)->sin_port); @@ -1094,7 +3374,7 @@ bool get_ip_and_port(const struct sockaddr_storage &addr, return true; } -void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) { +inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) { struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); if (!getsockname(sock, reinterpret_cast(&addr), @@ -1103,7 +3383,7 @@ void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) { } } -void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { +inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); @@ -1131,7 +3411,7 @@ void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { } } -constexpr unsigned int str2tag_core(const char *s, size_t l, +inline constexpr unsigned int str2tag_core(const char *s, size_t l, unsigned int h) { return (l == 0) ? h @@ -1143,19 +3423,19 @@ constexpr unsigned int str2tag_core(const char *s, size_t l, static_cast(*s)); } -unsigned int str2tag(const std::string &s) { +inline unsigned int str2tag(const std::string &s) { return str2tag_core(s.data(), s.size(), 0); } namespace udl { -constexpr unsigned int operator"" _t(const char *s, size_t l) { +inline constexpr unsigned int operator"" _t(const char *s, size_t l) { return str2tag_core(s, l, 0); } } // namespace udl -std::string +inline std::string find_content_type(const std::string &path, const std::map &user_data, const std::string &default_content_type) { @@ -1221,7 +3501,7 @@ find_content_type(const std::string &path, } } -bool can_compress_content_type(const std::string &content_type) { +inline bool can_compress_content_type(const std::string &content_type) { using udl::operator""_t; auto tag = str2tag(content_type); @@ -1239,7 +3519,7 @@ bool can_compress_content_type(const std::string &content_type) { } } -EncodingType encoding_type(const Request &req, const Response &res) { +inline EncodingType encoding_type(const Request &req, const Response &res) { auto ret = detail::can_compress_content_type(res.get_header_value("Content-Type")); if (!ret) { return EncodingType::None; } @@ -1262,14 +3542,14 @@ EncodingType encoding_type(const Request &req, const Response &res) { return EncodingType::None; } -bool nocompressor::compress(const char *data, size_t data_length, +inline bool nocompressor::compress(const char *data, size_t data_length, bool /*last*/, Callback callback) { if (!data_length) { return true; } return callback(data, data_length); } #ifdef CPPHTTPLIB_ZLIB_SUPPORT -gzip_compressor::gzip_compressor() { +inline gzip_compressor::gzip_compressor() { std::memset(&strm_, 0, sizeof(strm_)); strm_.zalloc = Z_NULL; strm_.zfree = Z_NULL; @@ -1279,9 +3559,9 @@ gzip_compressor::gzip_compressor() { Z_DEFAULT_STRATEGY) == Z_OK; } -gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); } +inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); } -bool gzip_compressor::compress(const char *data, size_t data_length, +inline bool gzip_compressor::compress(const char *data, size_t data_length, bool last, Callback callback) { assert(is_valid_); @@ -1320,7 +3600,7 @@ bool gzip_compressor::compress(const char *data, size_t data_length, return true; } -gzip_decompressor::gzip_decompressor() { +inline gzip_decompressor::gzip_decompressor() { std::memset(&strm_, 0, sizeof(strm_)); strm_.zalloc = Z_NULL; strm_.zfree = Z_NULL; @@ -1333,11 +3613,11 @@ gzip_decompressor::gzip_decompressor() { is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK; } -gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); } +inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); } -bool gzip_decompressor::is_valid() const { return is_valid_; } +inline bool gzip_decompressor::is_valid() const { return is_valid_; } -bool gzip_decompressor::decompress(const char *data, size_t data_length, +inline bool gzip_decompressor::decompress(const char *data, size_t data_length, Callback callback) { assert(is_valid_); @@ -1382,15 +3662,15 @@ bool gzip_decompressor::decompress(const char *data, size_t data_length, #endif #ifdef CPPHTTPLIB_BROTLI_SUPPORT -brotli_compressor::brotli_compressor() { +inline brotli_compressor::brotli_compressor() { state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); } -brotli_compressor::~brotli_compressor() { +inline brotli_compressor::~brotli_compressor() { BrotliEncoderDestroyInstance(state_); } -bool brotli_compressor::compress(const char *data, size_t data_length, +inline bool brotli_compressor::compress(const char *data, size_t data_length, bool last, Callback callback) { std::array buff{}; @@ -1422,19 +3702,19 @@ bool brotli_compressor::compress(const char *data, size_t data_length, return true; } -brotli_decompressor::brotli_decompressor() { +inline brotli_decompressor::brotli_decompressor() { decoder_s = BrotliDecoderCreateInstance(0, 0, 0); decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT : BROTLI_DECODER_RESULT_ERROR; } -brotli_decompressor::~brotli_decompressor() { +inline brotli_decompressor::~brotli_decompressor() { if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); } } -bool brotli_decompressor::is_valid() const { return decoder_s; } +inline bool brotli_decompressor::is_valid() const { return decoder_s; } -bool brotli_decompressor::decompress(const char *data, +inline bool brotli_decompressor::decompress(const char *data, size_t data_length, Callback callback) { if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || @@ -1467,11 +3747,11 @@ bool brotli_decompressor::decompress(const char *data, } #endif -bool has_header(const Headers &headers, const std::string &key) { +inline bool has_header(const Headers &headers, const std::string &key) { return headers.find(key) != headers.end(); } -const char *get_header_value(const Headers &headers, +inline const char *get_header_value(const Headers &headers, const std::string &key, size_t id, const char *def) { auto rng = headers.equal_range(key); @@ -1481,7 +3761,7 @@ const char *get_header_value(const Headers &headers, return def; } -bool compare_case_ignore(const std::string &a, const std::string &b) { +inline bool compare_case_ignore(const std::string &a, const std::string &b) { if (a.size() != b.size()) { return false; } for (size_t i = 0; i < b.size(); i++) { if (::tolower(a[i]) != ::tolower(b[i])) { return false; } @@ -1490,7 +3770,7 @@ bool compare_case_ignore(const std::string &a, const std::string &b) { } template -bool parse_header(const char *beg, const char *end, T fn) { +inline bool parse_header(const char *beg, const char *end, T fn) { // Skip trailing spaces and tabs. while (beg < end && is_space_or_tab(end[-1])) { end--; @@ -1526,7 +3806,7 @@ bool parse_header(const char *beg, const char *end, T fn) { return false; } -bool read_headers(Stream &strm, Headers &headers) { +inline bool read_headers(Stream &strm, Headers &headers) { const auto bufsiz = 2048; char buf[bufsiz]; stream_line_reader line_reader(strm, buf, bufsiz); @@ -1565,7 +3845,7 @@ bool read_headers(Stream &strm, Headers &headers) { return true; } -bool read_content_with_length(Stream &strm, uint64_t len, +inline bool read_content_with_length(Stream &strm, uint64_t len, Progress progress, ContentReceiverWithProgress out) { char buf[CPPHTTPLIB_RECV_BUFSIZ]; @@ -1587,7 +3867,7 @@ bool read_content_with_length(Stream &strm, uint64_t len, return true; } -void skip_content_with_length(Stream &strm, uint64_t len) { +inline void skip_content_with_length(Stream &strm, uint64_t len) { char buf[CPPHTTPLIB_RECV_BUFSIZ]; uint64_t r = 0; while (r < len) { @@ -1598,7 +3878,7 @@ void skip_content_with_length(Stream &strm, uint64_t len) { } } -bool read_content_without_length(Stream &strm, +inline bool read_content_without_length(Stream &strm, ContentReceiverWithProgress out) { char buf[CPPHTTPLIB_RECV_BUFSIZ]; uint64_t r = 0; @@ -1614,7 +3894,7 @@ bool read_content_without_length(Stream &strm, } template -bool read_content_chunked(Stream &strm, T &x, +inline bool read_content_chunked(Stream &strm, T &x, ContentReceiverWithProgress out) { const auto bufsiz = 16; char buf[bufsiz]; @@ -1668,7 +3948,7 @@ bool read_content_chunked(Stream &strm, T &x, return true; } -bool is_chunked_transfer_encoding(const Headers &headers) { +inline bool is_chunked_transfer_encoding(const Headers &headers) { return compare_case_ignore( get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked"); } @@ -1754,7 +4034,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, }); } // namespace detail -ssize_t write_headers(Stream &strm, const Headers &headers) { +inline ssize_t write_headers(Stream &strm, const Headers &headers) { ssize_t write_len = 0; for (const auto &x : headers) { auto len = @@ -1768,7 +4048,7 @@ ssize_t write_headers(Stream &strm, const Headers &headers) { return write_len; } -bool write_data(Stream &strm, const char *d, size_t l) { +inline bool write_data(Stream &strm, const char *d, size_t l) { size_t offset = 0; while (offset < l) { auto length = strm.write(d + offset, l - offset); @@ -1779,7 +4059,7 @@ bool write_data(Stream &strm, const char *d, size_t l) { } template -bool write_content(Stream &strm, const ContentProvider &content_provider, +inline bool write_content(Stream &strm, const ContentProvider &content_provider, size_t offset, size_t length, T is_shutting_down, Error &error) { size_t end_offset = offset + length; @@ -1817,7 +4097,7 @@ bool write_content(Stream &strm, const ContentProvider &content_provider, } template -bool write_content(Stream &strm, const ContentProvider &content_provider, +inline bool write_content(Stream &strm, const ContentProvider &content_provider, size_t offset, size_t length, const T &is_shutting_down) { auto error = Error::Success; @@ -1826,7 +4106,7 @@ bool write_content(Stream &strm, const ContentProvider &content_provider, } template -bool +inline bool write_content_without_length(Stream &strm, const ContentProvider &content_provider, const T &is_shutting_down) { @@ -1860,7 +4140,7 @@ write_content_without_length(Stream &strm, } template -bool +inline bool write_content_chunked(Stream &strm, const ContentProvider &content_provider, const T &is_shutting_down, U &compressor, Error &error) { size_t offset = 0; @@ -1965,7 +4245,7 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider, } template -bool write_content_chunked(Stream &strm, +inline bool write_content_chunked(Stream &strm, const ContentProvider &content_provider, const T &is_shutting_down, U &compressor) { auto error = Error::Success; @@ -1974,7 +4254,7 @@ bool write_content_chunked(Stream &strm, } template -bool redirect(T &cli, Request &req, Response &res, +inline bool redirect(T &cli, Request &req, Response &res, const std::string &path, const std::string &location, Error &error) { Request new_req = req; @@ -2000,7 +4280,7 @@ bool redirect(T &cli, Request &req, Response &res, return ret; } -std::string params_to_query_str(const Params ¶ms) { +inline std::string params_to_query_str(const Params ¶ms) { std::string query; for (auto it = params.begin(); it != params.end(); ++it) { @@ -2012,7 +4292,7 @@ std::string params_to_query_str(const Params ¶ms) { return query; } -void parse_query_text(const std::string &s, Params ¶ms) { +inline void parse_query_text(const std::string &s, Params ¶ms) { std::set cache; split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) { std::string kv(b, e); @@ -2035,7 +4315,7 @@ void parse_query_text(const std::string &s, Params ¶ms) { }); } -bool parse_multipart_boundary(const std::string &content_type, +inline bool parse_multipart_boundary(const std::string &content_type, std::string &boundary) { auto boundary_keyword = "boundary="; auto pos = content_type.find(boundary_keyword); @@ -2046,7 +4326,7 @@ bool parse_multipart_boundary(const std::string &content_type, return !boundary.empty(); } -void parse_disposition_params(const std::string &s, Params ¶ms) { +inline void parse_disposition_params(const std::string &s, Params ¶ms) { std::set cache; split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) { std::string kv(b, e); @@ -2071,9 +4351,9 @@ void parse_disposition_params(const std::string &s, Params ¶ms) { } #ifdef CPPHTTPLIB_NO_EXCEPTIONS -bool parse_range_header(const std::string &s, Ranges &ranges) { +inline bool parse_range_header(const std::string &s, Ranges &ranges) { #else -bool parse_range_header(const std::string &s, Ranges &ranges) try { +inline bool parse_range_header(const std::string &s, Ranges &ranges) try { #endif static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); std::smatch m; @@ -2355,7 +4635,7 @@ private: size_t buf_epos_ = 0; }; -std::string to_lower(const char *beg, const char *end) { +inline std::string to_lower(const char *beg, const char *end) { std::string out; auto it = beg; while (it != end) { @@ -2365,7 +4645,7 @@ std::string to_lower(const char *beg, const char *end) { return out; } -std::string random_string(size_t length) { +inline std::string random_string(size_t length) { static const char data[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -2387,11 +4667,11 @@ std::string random_string(size_t length) { return result; } -std::string make_multipart_data_boundary() { +inline std::string make_multipart_data_boundary() { return "--cpp-httplib-multipart-data-" + detail::random_string(16); } -bool is_multipart_boundary_chars_valid(const std::string &boundary) { +inline bool is_multipart_boundary_chars_valid(const std::string &boundary) { auto valid = true; for (size_t i = 0; i < boundary.size(); i++) { auto c = boundary[i]; @@ -2404,7 +4684,7 @@ bool is_multipart_boundary_chars_valid(const std::string &boundary) { } template -std::string +inline std::string serialize_multipart_formdata_item_begin(const T &item, const std::string &boundary) { std::string body = "--" + boundary + "\r\n"; @@ -2421,19 +4701,19 @@ serialize_multipart_formdata_item_begin(const T &item, return body; } -std::string serialize_multipart_formdata_item_end() { return "\r\n"; } +inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; } -std::string +inline std::string serialize_multipart_formdata_finish(const std::string &boundary) { return "--" + boundary + "--\r\n"; } -std::string +inline std::string serialize_multipart_formdata_get_content_type(const std::string &boundary) { return "multipart/form-data; boundary=" + boundary; } -std::string +inline std::string serialize_multipart_formdata(const MultipartFormDataItems &items, const std::string &boundary, bool finish = true) { std::string body; @@ -2448,21 +4728,21 @@ serialize_multipart_formdata(const MultipartFormDataItems &items, return body; } -bool normalize_ranges(Request &req, Response &res) { - ssize_t contant_len = static_cast( - res.content_length_ ? res.content_length_ : res.body.size()); +inline bool range_error(Request &req, Response &res) { + if (!req.ranges.empty() && 200 <= res.status && res.status < 300) { + ssize_t contant_len = static_cast( + res.content_length_ ? res.content_length_ : res.body.size()); - ssize_t prev_first_pos = -1; - ssize_t prev_last_pos = -1; - size_t overwrapping_count = 0; + ssize_t prev_first_pos = -1; + ssize_t prev_last_pos = -1; + size_t overwrapping_count = 0; - if (!req.ranges.empty()) { // NOTE: The following Range check is based on '14.2. Range' in RFC 9110 // 'HTTP Semantics' to avoid potential denial-of-service attacks. // https://www.rfc-editor.org/rfc/rfc9110#section-14.2 // Too many ranges - if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return false; } + if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; } for (auto &r : req.ranges) { auto &first_pos = r.first; @@ -2483,16 +4763,16 @@ bool normalize_ranges(Request &req, Response &res) { // Range must be within content length if (!(0 <= first_pos && first_pos <= last_pos && last_pos <= contant_len - 1)) { - return false; + return true; } // Ranges must be in ascending order - if (first_pos <= prev_first_pos) { return false; } + if (first_pos <= prev_first_pos) { return true; } // Request must not have more than two overlapping ranges if (first_pos <= prev_last_pos) { overwrapping_count++; - if (overwrapping_count > 2) { return false; } + if (overwrapping_count > 2) { return true; } } prev_first_pos = (std::max)(prev_first_pos, first_pos); @@ -2500,10 +4780,10 @@ bool normalize_ranges(Request &req, Response &res) { } } - return true; + return false; } -std::pair +inline std::pair get_range_offset_and_length(Range r, size_t content_length) { assert(r.first != -1 && r.second != -1); assert(0 <= r.first && r.first < static_cast(content_length)); @@ -2513,7 +4793,7 @@ get_range_offset_and_length(Range r, size_t content_length) { return std::make_pair(r.first, static_cast(r.second - r.first) + 1); } -std::string make_content_range_header_field( +inline std::string make_content_range_header_field( const std::pair &offset_and_length, size_t content_length) { auto st = offset_and_length.first; auto ed = st + offset_and_length.second - 1; @@ -2564,7 +4844,7 @@ bool process_multipart_ranges_data(const Request &req, return true; } -void make_multipart_ranges_data(const Request &req, Response &res, +inline void make_multipart_ranges_data(const Request &req, Response &res, const std::string &boundary, const std::string &content_type, size_t content_length, @@ -2580,7 +4860,7 @@ void make_multipart_ranges_data(const Request &req, Response &res, }); } -size_t get_multipart_ranges_data_length(const Request &req, +inline size_t get_multipart_ranges_data_length(const Request &req, const std::string &boundary, const std::string &content_type, size_t content_length) { @@ -2599,7 +4879,7 @@ size_t get_multipart_ranges_data_length(const Request &req, } template -bool +inline bool write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, const std::string &boundary, const std::string &content_type, @@ -2614,7 +4894,7 @@ write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, }); } -bool expect_content(const Request &req) { +inline bool expect_content(const Request &req) { if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI" || req.method == "DELETE") { return true; @@ -2623,7 +4903,7 @@ bool expect_content(const Request &req) { return false; } -bool has_crlf(const std::string &s) { +inline bool has_crlf(const std::string &s) { auto p = s.c_str(); while (*p) { if (*p == '\r' || *p == '\n') { return true; } @@ -2633,7 +4913,7 @@ bool has_crlf(const std::string &s) { } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -std::string message_digest(const std::string &s, const EVP_MD *algo) { +inline std::string message_digest(const std::string &s, const EVP_MD *algo) { auto context = std::unique_ptr( EVP_MD_CTX_new(), EVP_MD_CTX_free); @@ -2653,15 +4933,15 @@ std::string message_digest(const std::string &s, const EVP_MD *algo) { return ss.str(); } -std::string MD5(const std::string &s) { +inline std::string MD5(const std::string &s) { return message_digest(s, EVP_md5()); } -std::string SHA_256(const std::string &s) { +inline std::string SHA_256(const std::string &s) { return message_digest(s, EVP_sha256()); } -std::string SHA_512(const std::string &s) { +inline std::string SHA_512(const std::string &s) { return message_digest(s, EVP_sha512()); } #endif @@ -2670,7 +4950,7 @@ std::string SHA_512(const std::string &s) { #ifdef _WIN32 // NOTE: This code came up with the following stackoverflow post: // https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store -bool load_system_certs_on_windows(X509_STORE *store) { +inline bool load_system_certs_on_windows(X509_STORE *store) { auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); if (!hStore) { return false; } @@ -2700,11 +4980,11 @@ template using CFObjectPtr = std::unique_ptr::type, void (*)(CFTypeRef)>; -void cf_object_ptr_deleter(CFTypeRef obj) { +inline void cf_object_ptr_deleter(CFTypeRef obj) { if (obj) { CFRelease(obj); } } -bool retrieve_certs_from_keychain(CFObjectPtr &certs) { +inline bool retrieve_certs_from_keychain(CFObjectPtr &certs) { CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef}; CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll, kCFBooleanTrue}; @@ -2728,7 +5008,7 @@ bool retrieve_certs_from_keychain(CFObjectPtr &certs) { return true; } -bool retrieve_root_certs_from_keychain(CFObjectPtr &certs) { +inline bool retrieve_root_certs_from_keychain(CFObjectPtr &certs) { CFArrayRef root_security_items = nullptr; if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) { return false; @@ -2738,7 +5018,7 @@ bool retrieve_root_certs_from_keychain(CFObjectPtr &certs) { return true; } -bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) { +inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) { auto result = false; for (auto i = 0; i < CFArrayGetCount(certs); ++i) { const auto cert = reinterpret_cast( @@ -2770,7 +5050,7 @@ bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) { return result; } -bool load_system_certs_on_macos(X509_STORE *store) { +inline bool load_system_certs_on_macos(X509_STORE *store) { auto result = false; CFObjectPtr certs(nullptr, cf_object_ptr_deleter); if (retrieve_certs_from_keychain(certs) && certs) { @@ -2806,7 +5086,7 @@ static WSInit wsinit_; #endif #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -std::pair make_digest_authentication_header( +inline std::pair make_digest_authentication_header( const Request &req, const std::map &auth, size_t cnonce_count, const std::string &cnonce, const std::string &username, const std::string &password, bool is_proxy = false) { @@ -2867,7 +5147,7 @@ std::pair make_digest_authentication_header( } #endif -bool parse_www_authenticate(const Response &res, +inline bool parse_www_authenticate(const Response &res, std::map &auth, bool is_proxy) { auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; @@ -2916,14 +5196,14 @@ private: } // namespace detail -std::string hosted_at(const std::string &hostname) { +inline std::string hosted_at(const std::string &hostname) { std::vector addrs; hosted_at(hostname, addrs); if (addrs.empty()) { return std::string(); } return addrs[0]; } -void hosted_at(const std::string &hostname, +inline void hosted_at(const std::string &hostname, std::vector &addrs) { struct addrinfo hints; struct addrinfo *result; @@ -2954,7 +5234,7 @@ void hosted_at(const std::string &hostname, freeaddrinfo(result); } -std::string append_query_params(const std::string &path, +inline std::string append_query_params(const std::string &path, const Params ¶ms) { std::string path_with_query = path; const static std::regex re("[^?]+\\?.*"); @@ -2964,7 +5244,7 @@ std::string append_query_params(const std::string &path, } // Header utilities -std::pair +inline std::pair make_range_header(const Ranges &ranges) { std::string field = "bytes="; auto i = 0; @@ -2978,7 +5258,7 @@ make_range_header(const Ranges &ranges) { return std::make_pair("Range", std::move(field)); } -std::pair +inline std::pair make_basic_authentication_header(const std::string &username, const std::string &password, bool is_proxy) { auto field = "Basic " + detail::base64_encode(username + ":" + password); @@ -2986,7 +5266,7 @@ make_basic_authentication_header(const std::string &username, return std::make_pair(key, std::move(field)); } -std::pair +inline std::pair make_bearer_token_authentication_header(const std::string &token, bool is_proxy = false) { auto field = "Bearer " + token; @@ -2995,32 +5275,32 @@ make_bearer_token_authentication_header(const std::string &token, } // Request implementation -bool Request::has_header(const std::string &key) const { +inline bool Request::has_header(const std::string &key) const { return detail::has_header(headers, key); } -std::string Request::get_header_value(const std::string &key, +inline std::string Request::get_header_value(const std::string &key, size_t id) const { return detail::get_header_value(headers, key, id, ""); } -size_t Request::get_header_value_count(const std::string &key) const { +inline size_t Request::get_header_value_count(const std::string &key) const { auto r = headers.equal_range(key); return static_cast(std::distance(r.first, r.second)); } -void Request::set_header(const std::string &key, +inline void Request::set_header(const std::string &key, const std::string &val) { if (!detail::has_crlf(key) && !detail::has_crlf(val)) { headers.emplace(key, val); } } -bool Request::has_param(const std::string &key) const { +inline bool Request::has_param(const std::string &key) const { return params.find(key) != params.end(); } -std::string Request::get_param_value(const std::string &key, +inline std::string Request::get_param_value(const std::string &key, size_t id) const { auto rng = params.equal_range(key); auto it = rng.first; @@ -3029,27 +5309,27 @@ std::string Request::get_param_value(const std::string &key, return std::string(); } -size_t Request::get_param_value_count(const std::string &key) const { +inline size_t Request::get_param_value_count(const std::string &key) const { auto r = params.equal_range(key); return static_cast(std::distance(r.first, r.second)); } -bool Request::is_multipart_form_data() const { +inline bool Request::is_multipart_form_data() const { const auto &content_type = get_header_value("Content-Type"); return !content_type.rfind("multipart/form-data", 0); } -bool Request::has_file(const std::string &key) const { +inline bool Request::has_file(const std::string &key) const { return files.find(key) != files.end(); } -MultipartFormData Request::get_file_value(const std::string &key) const { +inline MultipartFormData Request::get_file_value(const std::string &key) const { auto it = files.find(key); if (it != files.end()) { return it->second; } return MultipartFormData(); } -std::vector +inline std::vector Request::get_file_values(const std::string &key) const { std::vector values; auto rng = files.equal_range(key); @@ -3060,28 +5340,28 @@ Request::get_file_values(const std::string &key) const { } // Response implementation -bool Response::has_header(const std::string &key) const { +inline bool Response::has_header(const std::string &key) const { return headers.find(key) != headers.end(); } -std::string Response::get_header_value(const std::string &key, +inline std::string Response::get_header_value(const std::string &key, size_t id) const { return detail::get_header_value(headers, key, id, ""); } -size_t Response::get_header_value_count(const std::string &key) const { +inline size_t Response::get_header_value_count(const std::string &key) const { auto r = headers.equal_range(key); return static_cast(std::distance(r.first, r.second)); } -void Response::set_header(const std::string &key, +inline void Response::set_header(const std::string &key, const std::string &val) { if (!detail::has_crlf(key) && !detail::has_crlf(val)) { headers.emplace(key, val); } } -void Response::set_redirect(const std::string &url, int stat) { +inline void Response::set_redirect(const std::string &url, int stat) { if (!detail::has_crlf(url)) { set_header("Location", url); if (300 <= stat && stat < 400) { @@ -3092,7 +5372,7 @@ void Response::set_redirect(const std::string &url, int stat) { } } -void Response::set_content(const char *s, size_t n, +inline void Response::set_content(const char *s, size_t n, const std::string &content_type) { body.assign(s, n); @@ -3101,12 +5381,12 @@ void Response::set_content(const char *s, size_t n, set_header("Content-Type", content_type); } -void Response::set_content(const std::string &s, +inline void Response::set_content(const std::string &s, const std::string &content_type) { set_content(s.data(), s.size(), content_type); } -void Response::set_content(std::string &&s, +inline void Response::set_content(std::string &&s, const std::string &content_type) { body = std::move(s); @@ -3115,7 +5395,7 @@ void Response::set_content(std::string &&s, set_header("Content-Type", content_type); } -void Response::set_content_provider( +inline void Response::set_content_provider( size_t in_length, const std::string &content_type, ContentProvider provider, ContentProviderResourceReleaser resource_releaser) { set_header("Content-Type", content_type); @@ -3125,7 +5405,7 @@ void Response::set_content_provider( is_chunked_content_provider_ = false; } -void Response::set_content_provider( +inline void Response::set_content_provider( const std::string &content_type, ContentProviderWithoutLength provider, ContentProviderResourceReleaser resource_releaser) { set_header("Content-Type", content_type); @@ -3135,7 +5415,7 @@ void Response::set_content_provider( is_chunked_content_provider_ = false; } -void Response::set_chunked_content_provider( +inline void Response::set_chunked_content_provider( const std::string &content_type, ContentProviderWithoutLength provider, ContentProviderResourceReleaser resource_releaser) { set_header("Content-Type", content_type); @@ -3146,34 +5426,34 @@ void Response::set_chunked_content_provider( } // Result implementation -bool Result::has_request_header(const std::string &key) const { +inline bool Result::has_request_header(const std::string &key) const { return request_headers_.find(key) != request_headers_.end(); } -std::string Result::get_request_header_value(const std::string &key, +inline std::string Result::get_request_header_value(const std::string &key, size_t id) const { return detail::get_header_value(request_headers_, key, id, ""); } -size_t +inline size_t Result::get_request_header_value_count(const std::string &key) const { auto r = request_headers_.equal_range(key); return static_cast(std::distance(r.first, r.second)); } // Stream implementation -ssize_t Stream::write(const char *ptr) { +inline ssize_t Stream::write(const char *ptr) { return write(ptr, strlen(ptr)); } -ssize_t Stream::write(const std::string &s) { +inline ssize_t Stream::write(const std::string &s) { return write(s.data(), s.size()); } namespace detail { // Socket stream implementation -SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, +inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec) @@ -3182,18 +5462,18 @@ SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, write_timeout_sec_(write_timeout_sec), write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {} -SocketStream::~SocketStream() = default; +inline SocketStream::~SocketStream() = default; -bool SocketStream::is_readable() const { +inline bool SocketStream::is_readable() const { return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; } -bool SocketStream::is_writable() const { +inline bool SocketStream::is_writable() const { return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && is_socket_alive(sock_); } -ssize_t SocketStream::read(char *ptr, size_t size) { +inline ssize_t SocketStream::read(char *ptr, size_t size) { #ifdef _WIN32 size = (std::min)(size, static_cast((std::numeric_limits::max)())); @@ -3239,7 +5519,7 @@ ssize_t SocketStream::read(char *ptr, size_t size) { } } -ssize_t SocketStream::write(const char *ptr, size_t size) { +inline ssize_t SocketStream::write(const char *ptr, size_t size) { if (!is_writable()) { return -1; } #if defined(_WIN32) && !defined(_WIN64) @@ -3250,24 +5530,24 @@ ssize_t SocketStream::write(const char *ptr, size_t size) { return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); } -void SocketStream::get_remote_ip_and_port(std::string &ip, +inline void SocketStream::get_remote_ip_and_port(std::string &ip, int &port) const { return detail::get_remote_ip_and_port(sock_, ip, port); } -void SocketStream::get_local_ip_and_port(std::string &ip, +inline void SocketStream::get_local_ip_and_port(std::string &ip, int &port) const { return detail::get_local_ip_and_port(sock_, ip, port); } -socket_t SocketStream::socket() const { return sock_; } +inline socket_t SocketStream::socket() const { return sock_; } // Buffer stream implementation -bool BufferStream::is_readable() const { return true; } +inline bool BufferStream::is_readable() const { return true; } -bool BufferStream::is_writable() const { return true; } +inline bool BufferStream::is_writable() const { return true; } -ssize_t BufferStream::read(char *ptr, size_t size) { +inline ssize_t BufferStream::read(char *ptr, size_t size) { #if defined(_MSC_VER) && _MSC_VER < 1910 auto len_read = buffer._Copy_s(ptr, size, size, position); #else @@ -3277,22 +5557,22 @@ ssize_t BufferStream::read(char *ptr, size_t size) { return static_cast(len_read); } -ssize_t BufferStream::write(const char *ptr, size_t size) { +inline ssize_t BufferStream::write(const char *ptr, size_t size) { buffer.append(ptr, size); return static_cast(size); } -void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, +inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, int & /*port*/) const {} -void BufferStream::get_local_ip_and_port(std::string & /*ip*/, +inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/, int & /*port*/) const {} -socket_t BufferStream::socket() const { return 0; } +inline socket_t BufferStream::socket() const { return 0; } -const std::string &BufferStream::get_buffer() const { return buffer; } +inline const std::string &BufferStream::get_buffer() const { return buffer; } -PathParamsMatcher::PathParamsMatcher(const std::string &pattern) { +inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) { // One past the last ending position of a path param substring std::size_t last_param_end = 0; @@ -3337,7 +5617,7 @@ PathParamsMatcher::PathParamsMatcher(const std::string &pattern) { } } -bool PathParamsMatcher::match(Request &request) const { +inline bool PathParamsMatcher::match(Request &request) const { request.matches = std::smatch(); request.path_params.clear(); request.path_params.reserve(param_names_.size()); @@ -3380,7 +5660,7 @@ bool PathParamsMatcher::match(Request &request) const { return starting_pos >= request.path.length(); } -bool RegexMatcher::match(Request &request) const { +inline bool RegexMatcher::match(Request &request) const { request.path_params.clear(); return std::regex_match(request.path, request.matches, regex_); } @@ -3388,7 +5668,7 @@ bool RegexMatcher::match(Request &request) const { } // namespace detail // HTTP server implementation -Server::Server() +inline Server::Server() : new_task_queue( [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) { #ifndef _WIN32 @@ -3396,9 +5676,9 @@ Server::Server() #endif } -Server::~Server() = default; +inline Server::~Server() = default; -std::unique_ptr +inline std::unique_ptr Server::make_matcher(const std::string &pattern) { if (pattern.find("/:") != std::string::npos) { return detail::make_unique(pattern); @@ -3407,70 +5687,70 @@ Server::make_matcher(const std::string &pattern) { } } -Server &Server::Get(const std::string &pattern, Handler handler) { +inline Server &Server::Get(const std::string &pattern, Handler handler) { get_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -Server &Server::Post(const std::string &pattern, Handler handler) { +inline Server &Server::Post(const std::string &pattern, Handler handler) { post_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -Server &Server::Post(const std::string &pattern, +inline Server &Server::Post(const std::string &pattern, HandlerWithContentReader handler) { post_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -Server &Server::Put(const std::string &pattern, Handler handler) { +inline Server &Server::Put(const std::string &pattern, Handler handler) { put_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -Server &Server::Put(const std::string &pattern, +inline Server &Server::Put(const std::string &pattern, HandlerWithContentReader handler) { put_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -Server &Server::Patch(const std::string &pattern, Handler handler) { +inline Server &Server::Patch(const std::string &pattern, Handler handler) { patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -Server &Server::Patch(const std::string &pattern, +inline Server &Server::Patch(const std::string &pattern, HandlerWithContentReader handler) { patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -Server &Server::Delete(const std::string &pattern, Handler handler) { +inline Server &Server::Delete(const std::string &pattern, Handler handler) { delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -Server &Server::Delete(const std::string &pattern, +inline Server &Server::Delete(const std::string &pattern, HandlerWithContentReader handler) { delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -Server &Server::Options(const std::string &pattern, Handler handler) { +inline Server &Server::Options(const std::string &pattern, Handler handler) { options_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); return *this; } -bool Server::set_base_dir(const std::string &dir, +inline bool Server::set_base_dir(const std::string &dir, const std::string &mount_point) { return set_mount_point(mount_point, dir); } -bool Server::set_mount_point(const std::string &mount_point, +inline bool Server::set_mount_point(const std::string &mount_point, const std::string &dir, Headers headers) { if (detail::is_dir(dir)) { std::string mnt = !mount_point.empty() ? mount_point : "/"; @@ -3482,7 +5762,7 @@ bool Server::set_mount_point(const std::string &mount_point, return false; } -bool Server::remove_mount_point(const std::string &mount_point) { +inline bool Server::remove_mount_point(const std::string &mount_point) { for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { if (it->mount_point == mount_point) { base_dirs_.erase(it); @@ -3492,29 +5772,29 @@ bool Server::remove_mount_point(const std::string &mount_point) { return false; } -Server & +inline Server & Server::set_file_extension_and_mimetype_mapping(const std::string &ext, const std::string &mime) { file_extension_and_mimetype_map_[ext] = mime; return *this; } -Server &Server::set_default_file_mimetype(const std::string &mime) { +inline Server &Server::set_default_file_mimetype(const std::string &mime) { default_file_mimetype_ = mime; return *this; } -Server &Server::set_file_request_handler(Handler handler) { +inline Server &Server::set_file_request_handler(Handler handler) { file_request_handler_ = std::move(handler); return *this; } -Server &Server::set_error_handler(HandlerWithResponse handler) { +inline Server &Server::set_error_handler(HandlerWithResponse handler) { error_handler_ = std::move(handler); return *this; } -Server &Server::set_error_handler(Handler handler) { +inline Server &Server::set_error_handler(Handler handler) { error_handler_ = [handler](const Request &req, Response &res) { handler(req, res); return HandlerResponse::Handled; @@ -3522,119 +5802,119 @@ Server &Server::set_error_handler(Handler handler) { return *this; } -Server &Server::set_exception_handler(ExceptionHandler handler) { +inline Server &Server::set_exception_handler(ExceptionHandler handler) { exception_handler_ = std::move(handler); return *this; } -Server &Server::set_pre_routing_handler(HandlerWithResponse handler) { +inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) { pre_routing_handler_ = std::move(handler); return *this; } -Server &Server::set_post_routing_handler(Handler handler) { +inline Server &Server::set_post_routing_handler(Handler handler) { post_routing_handler_ = std::move(handler); return *this; } -Server &Server::set_logger(Logger logger) { +inline Server &Server::set_logger(Logger logger) { logger_ = std::move(logger); return *this; } -Server & +inline Server & Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { expect_100_continue_handler_ = std::move(handler); return *this; } -Server &Server::set_address_family(int family) { +inline Server &Server::set_address_family(int family) { address_family_ = family; return *this; } -Server &Server::set_tcp_nodelay(bool on) { +inline Server &Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; return *this; } -Server &Server::set_socket_options(SocketOptions socket_options) { +inline Server &Server::set_socket_options(SocketOptions socket_options) { socket_options_ = std::move(socket_options); return *this; } -Server &Server::set_default_headers(Headers headers) { +inline Server &Server::set_default_headers(Headers headers) { default_headers_ = std::move(headers); return *this; } -Server &Server::set_header_writer( +inline Server &Server::set_header_writer( std::function const &writer) { header_writer_ = writer; return *this; } -Server &Server::set_keep_alive_max_count(size_t count) { +inline Server &Server::set_keep_alive_max_count(size_t count) { keep_alive_max_count_ = count; return *this; } -Server &Server::set_keep_alive_timeout(time_t sec) { +inline Server &Server::set_keep_alive_timeout(time_t sec) { keep_alive_timeout_sec_ = sec; return *this; } -Server &Server::set_read_timeout(time_t sec, time_t usec) { +inline Server &Server::set_read_timeout(time_t sec, time_t usec) { read_timeout_sec_ = sec; read_timeout_usec_ = usec; return *this; } -Server &Server::set_write_timeout(time_t sec, time_t usec) { +inline Server &Server::set_write_timeout(time_t sec, time_t usec) { write_timeout_sec_ = sec; write_timeout_usec_ = usec; return *this; } -Server &Server::set_idle_interval(time_t sec, time_t usec) { +inline Server &Server::set_idle_interval(time_t sec, time_t usec) { idle_interval_sec_ = sec; idle_interval_usec_ = usec; return *this; } -Server &Server::set_payload_max_length(size_t length) { +inline Server &Server::set_payload_max_length(size_t length) { payload_max_length_ = length; return *this; } -bool Server::bind_to_port(const std::string &host, int port, +inline bool Server::bind_to_port(const std::string &host, int port, int socket_flags) { return bind_internal(host, port, socket_flags) >= 0; } -int Server::bind_to_any_port(const std::string &host, int socket_flags) { +inline int Server::bind_to_any_port(const std::string &host, int socket_flags) { return bind_internal(host, 0, socket_flags); } -bool Server::listen_after_bind() { +inline bool Server::listen_after_bind() { auto se = detail::scope_exit([&]() { done_ = true; }); return listen_internal(); } -bool Server::listen(const std::string &host, int port, +inline bool Server::listen(const std::string &host, int port, int socket_flags) { auto se = detail::scope_exit([&]() { done_ = true; }); return bind_to_port(host, port, socket_flags) && listen_internal(); } -bool Server::is_running() const { return is_running_; } +inline bool Server::is_running() const { return is_running_; } -void Server::wait_until_ready() const { +inline void Server::wait_until_ready() const { while (!is_running() && !done_) { std::this_thread::sleep_for(std::chrono::milliseconds{1}); } } -void Server::stop() { +inline void Server::stop() { if (is_running_) { assert(svr_sock_ != INVALID_SOCKET); std::atomic sock(svr_sock_.exchange(INVALID_SOCKET)); @@ -3643,7 +5923,7 @@ void Server::stop() { } } -bool Server::parse_request_line(const char *s, Request &req) const { +inline bool Server::parse_request_line(const char *s, Request &req) const { auto len = strlen(s); if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; } len -= 2; @@ -3706,19 +5986,22 @@ bool Server::parse_request_line(const char *s, Request &req) const { return true; } -bool Server::write_response(Stream &strm, bool close_connection, - const Request &req, Response &res) { +inline bool Server::write_response(Stream &strm, bool close_connection, + Request &req, Response &res) { + // NOTE: `req.ranges` should be empty, otherwise it will be applied + // incorrectly to the error content. + req.ranges.clear(); return write_response_core(strm, close_connection, req, res, false); } -bool Server::write_response_with_content(Stream &strm, +inline bool Server::write_response_with_content(Stream &strm, bool close_connection, const Request &req, Response &res) { return write_response_core(strm, close_connection, req, res, true); } -bool Server::write_response_core(Stream &strm, bool close_connection, +inline bool Server::write_response_core(Stream &strm, bool close_connection, const Request &req, Response &res, bool need_apply_ranges) { assert(res.status != -1); @@ -3796,7 +6079,7 @@ bool Server::write_response_core(Stream &strm, bool close_connection, return ret; } -bool +inline bool Server::write_content_with_provider(Stream &strm, const Request &req, Response &res, const std::string &boundary, const std::string &content_type) { @@ -3847,7 +6130,7 @@ Server::write_content_with_provider(Stream &strm, const Request &req, } } -bool Server::read_content(Stream &strm, Request &req, Response &res) { +inline bool Server::read_content(Stream &strm, Request &req, Response &res) { MultipartFormDataMap::iterator cur; auto file_count = 0; if (read_content_core( @@ -3885,7 +6168,7 @@ bool Server::read_content(Stream &strm, Request &req, Response &res) { return false; } -bool Server::read_content_with_content_receiver( +inline bool Server::read_content_with_content_receiver( Stream &strm, Request &req, Response &res, ContentReceiver receiver, MultipartContentHeader multipart_header, ContentReceiver multipart_receiver) { @@ -3894,7 +6177,7 @@ bool Server::read_content_with_content_receiver( std::move(multipart_receiver)); } -bool +inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, ContentReceiver receiver, MultipartContentHeader multipart_header, @@ -3950,7 +6233,7 @@ Server::read_content_core(Stream &strm, Request &req, Response &res, return true; } -bool Server::handle_file_request(const Request &req, Response &res, +inline bool Server::handle_file_request(const Request &req, Response &res, bool head) { for (const auto &entry : base_dirs_) { // Prefix match @@ -3989,7 +6272,7 @@ bool Server::handle_file_request(const Request &req, Response &res, return false; } -socket_t +inline socket_t Server::create_server_socket(const std::string &host, int port, int socket_flags, SocketOptions socket_options) const { @@ -4005,7 +6288,7 @@ Server::create_server_socket(const std::string &host, int port, }); } -int Server::bind_internal(const std::string &host, int port, +inline int Server::bind_internal(const std::string &host, int port, int socket_flags) { if (!is_valid()) { return -1; } @@ -4031,7 +6314,7 @@ int Server::bind_internal(const std::string &host, int port, } } -bool Server::listen_internal() { +inline bool Server::listen_internal() { auto ret = true; is_running_ = true; auto se = detail::scope_exit([&]() { is_running_ = false; }); @@ -4115,7 +6398,7 @@ bool Server::listen_internal() { return ret; } -bool Server::routing(Request &req, Response &res, Stream &strm) { +inline bool Server::routing(Request &req, Response &res, Stream &strm) { if (pre_routing_handler_ && pre_routing_handler_(req, res) == HandlerResponse::Handled) { return true; @@ -4192,7 +6475,7 @@ bool Server::routing(Request &req, Response &res, Stream &strm) { return false; } -bool Server::dispatch_request(Request &req, Response &res, +inline bool Server::dispatch_request(Request &req, Response &res, const Handlers &handlers) const { for (const auto &x : handlers) { const auto &matcher = x.first; @@ -4206,7 +6489,7 @@ bool Server::dispatch_request(Request &req, Response &res, return false; } -void Server::apply_ranges(const Request &req, Response &res, +inline void Server::apply_ranges(const Request &req, Response &res, std::string &content_type, std::string &boundary) const { if (req.ranges.size() > 1) { @@ -4311,7 +6594,7 @@ void Server::apply_ranges(const Request &req, Response &res, } } -bool Server::dispatch_request_for_content_reader( +inline bool Server::dispatch_request_for_content_reader( Request &req, Response &res, ContentReader content_reader, const HandlersForContentReader &handlers) const { for (const auto &x : handlers) { @@ -4326,7 +6609,7 @@ bool Server::dispatch_request_for_content_reader( return false; } -bool +inline bool Server::process_request(Stream &strm, bool close_connection, bool &connection_closed, const std::function &setup_request) { @@ -4414,7 +6697,7 @@ Server::process_request(Stream &strm, bool close_connection, } } - // Rounting + // Routing auto routed = false; #ifdef CPPHTTPLIB_NO_EXCEPTIONS routed = routing(req, res, strm); @@ -4451,28 +6734,30 @@ Server::process_request(Stream &strm, bool close_connection, } #endif if (routed) { - if (detail::normalize_ranges(req, res)) { - if (res.status == -1) { - res.status = req.ranges.empty() ? StatusCode::OK_200 - : StatusCode::PartialContent_206; - } - return write_response_with_content(strm, close_connection, req, res); - } else { + if (res.status == -1) { + res.status = req.ranges.empty() ? StatusCode::OK_200 + : StatusCode::PartialContent_206; + } + + if (detail::range_error(req, res)) { res.body.clear(); res.content_length_ = 0; res.content_provider_ = nullptr; res.status = StatusCode::RangeNotSatisfiable_416; return write_response(strm, close_connection, req, res); } + + return write_response_with_content(strm, close_connection, req, res); } else { if (res.status == -1) { res.status = StatusCode::NotFound_404; } + return write_response(strm, close_connection, req, res); } } -bool Server::is_valid() const { return true; } +inline bool Server::is_valid() const { return true; } -bool Server::process_and_close_socket(socket_t sock) { +inline bool Server::process_and_close_socket(socket_t sock) { auto ret = detail::process_server_socket( svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, @@ -4488,28 +6773,28 @@ bool Server::process_and_close_socket(socket_t sock) { } // HTTP client implementation -ClientImpl::ClientImpl(const std::string &host) +inline ClientImpl::ClientImpl(const std::string &host) : ClientImpl(host, 80, std::string(), std::string()) {} -ClientImpl::ClientImpl(const std::string &host, int port) +inline ClientImpl::ClientImpl(const std::string &host, int port) : ClientImpl(host, port, std::string(), std::string()) {} -ClientImpl::ClientImpl(const std::string &host, int port, +inline ClientImpl::ClientImpl(const std::string &host, int port, const std::string &client_cert_path, const std::string &client_key_path) : host_(host), port_(port), host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)), client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} -ClientImpl::~ClientImpl() { +inline ClientImpl::~ClientImpl() { std::lock_guard guard(socket_mutex_); shutdown_socket(socket_); close_socket(socket_); } -bool ClientImpl::is_valid() const { return true; } +inline bool ClientImpl::is_valid() const { return true; } -void ClientImpl::copy_settings(const ClientImpl &rhs) { +inline void ClientImpl::copy_settings(const ClientImpl &rhs) { client_cert_path_ = rhs.client_cert_path_; client_key_path_ = rhs.client_key_path_; connection_timeout_sec_ = rhs.connection_timeout_sec_; @@ -4553,7 +6838,7 @@ void ClientImpl::copy_settings(const ClientImpl &rhs) { logger_ = rhs.logger_; } -socket_t ClientImpl::create_client_socket(Error &error) const { +inline socket_t ClientImpl::create_client_socket(Error &error) const { if (!proxy_host_.empty() && proxy_port_ != -1) { return detail::create_client_socket( proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_, @@ -4574,7 +6859,7 @@ socket_t ClientImpl::create_client_socket(Error &error) const { error); } -bool ClientImpl::create_and_connect_socket(Socket &socket, +inline bool ClientImpl::create_and_connect_socket(Socket &socket, Error &error) { auto sock = create_client_socket(error); if (sock == INVALID_SOCKET) { return false; } @@ -4582,7 +6867,7 @@ bool ClientImpl::create_and_connect_socket(Socket &socket, return true; } -void ClientImpl::shutdown_ssl(Socket & /*socket*/, +inline void ClientImpl::shutdown_ssl(Socket & /*socket*/, bool /*shutdown_gracefully*/) { // If there are any requests in flight from threads other than us, then it's // a thread-unsafe race because individual ssl* objects are not thread-safe. @@ -4590,12 +6875,12 @@ void ClientImpl::shutdown_ssl(Socket & /*socket*/, socket_requests_are_from_thread_ == std::this_thread::get_id()); } -void ClientImpl::shutdown_socket(Socket &socket) const { +inline void ClientImpl::shutdown_socket(Socket &socket) const { if (socket.sock == INVALID_SOCKET) { return; } detail::shutdown_socket(socket.sock); } -void ClientImpl::close_socket(Socket &socket) { +inline void ClientImpl::close_socket(Socket &socket) { // If there are requests in flight in another thread, usually closing // the socket will be fine and they will simply receive an error when // using the closed socket, but it is still a bug since rarely the OS @@ -4614,7 +6899,7 @@ void ClientImpl::close_socket(Socket &socket) { socket.sock = INVALID_SOCKET; } -bool ClientImpl::read_response_line(Stream &strm, const Request &req, +inline bool ClientImpl::read_response_line(Stream &strm, const Request &req, Response &res) const { std::array buf{}; @@ -4650,7 +6935,7 @@ bool ClientImpl::read_response_line(Stream &strm, const Request &req, return true; } -bool ClientImpl::send(Request &req, Response &res, Error &error) { +inline bool ClientImpl::send(Request &req, Response &res, Error &error) { std::lock_guard request_mutex_guard(request_mutex_); auto ret = send_(req, res, error); if (error == Error::SSLPeerCouldBeClosed_) { @@ -4660,7 +6945,7 @@ bool ClientImpl::send(Request &req, Response &res, Error &error) { return ret; } -bool ClientImpl::send_(Request &req, Response &res, Error &error) { +inline bool ClientImpl::send_(Request &req, Response &res, Error &error) { { std::lock_guard guard(socket_mutex_); @@ -4749,19 +7034,19 @@ bool ClientImpl::send_(Request &req, Response &res, Error &error) { return ret; } -Result ClientImpl::send(const Request &req) { +inline Result ClientImpl::send(const Request &req) { auto req2 = req; return send_(std::move(req2)); } -Result ClientImpl::send_(Request &&req) { +inline Result ClientImpl::send_(Request &&req) { auto res = detail::make_unique(); auto error = Error::Success; auto ret = send(req, *res, error); return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)}; } -bool ClientImpl::handle_request(Stream &strm, Request &req, +inline bool ClientImpl::handle_request(Stream &strm, Request &req, Response &res, bool close_connection, Error &error) { if (req.path.empty()) { @@ -4838,7 +7123,7 @@ bool ClientImpl::handle_request(Stream &strm, Request &req, return ret; } -bool ClientImpl::redirect(Request &req, Response &res, Error &error) { +inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { if (req.redirect_count_ == 0) { error = Error::ExceedRedirectCount; return false; @@ -4895,7 +7180,7 @@ bool ClientImpl::redirect(Request &req, Response &res, Error &error) { } } -bool ClientImpl::write_content_with_provider(Stream &strm, +inline bool ClientImpl::write_content_with_provider(Stream &strm, const Request &req, Error &error) const { auto is_shutting_down = []() { return false; }; @@ -4920,7 +7205,7 @@ bool ClientImpl::write_content_with_provider(Stream &strm, } } -bool ClientImpl::write_request(Stream &strm, Request &req, +inline bool ClientImpl::write_request(Stream &strm, Request &req, bool close_connection, Error &error) { // Prepare additional headers if (close_connection) { @@ -5038,7 +7323,7 @@ bool ClientImpl::write_request(Stream &strm, Request &req, return true; } -std::unique_ptr ClientImpl::send_with_content_provider( +inline std::unique_ptr ClientImpl::send_with_content_provider( Request &req, const char *body, size_t content_length, ContentProvider content_provider, ContentProviderWithoutLength content_provider_without_length, @@ -5117,7 +7402,7 @@ std::unique_ptr ClientImpl::send_with_content_provider( return send(req, *res, error) ? std::move(res) : nullptr; } -Result ClientImpl::send_with_content_provider( +inline Result ClientImpl::send_with_content_provider( const std::string &method, const std::string &path, const Headers &headers, const char *body, size_t content_length, ContentProvider content_provider, ContentProviderWithoutLength content_provider_without_length, @@ -5136,13 +7421,13 @@ Result ClientImpl::send_with_content_provider( return Result{std::move(res), error, std::move(req.headers)}; } -std::string +inline std::string ClientImpl::adjust_host_string(const std::string &host) const { if (host.find(':') != std::string::npos) { return "[" + host + "]"; } return host; } -bool ClientImpl::process_request(Stream &strm, Request &req, +inline bool ClientImpl::process_request(Stream &strm, Request &req, Response &res, bool close_connection, Error &error) { // Send request @@ -5222,7 +7507,7 @@ bool ClientImpl::process_request(Stream &strm, Request &req, return true; } -ContentProviderWithoutLength ClientImpl::get_multipart_content_provider( +inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider( const std::string &boundary, const MultipartFormDataItems &items, const MultipartFormDataProviderItems &provider_items) const { size_t cur_item = 0; @@ -5266,7 +7551,7 @@ ContentProviderWithoutLength ClientImpl::get_multipart_content_provider( }; } -bool +inline bool ClientImpl::process_socket(const Socket &socket, std::function callback) { return detail::process_client_socket( @@ -5274,21 +7559,21 @@ ClientImpl::process_socket(const Socket &socket, write_timeout_usec_, std::move(callback)); } -bool ClientImpl::is_ssl() const { return false; } +inline bool ClientImpl::is_ssl() const { return false; } -Result ClientImpl::Get(const std::string &path) { +inline Result ClientImpl::Get(const std::string &path) { return Get(path, Headers(), Progress()); } -Result ClientImpl::Get(const std::string &path, Progress progress) { +inline Result ClientImpl::Get(const std::string &path, Progress progress) { return Get(path, Headers(), std::move(progress)); } -Result ClientImpl::Get(const std::string &path, const Headers &headers) { +inline Result ClientImpl::Get(const std::string &path, const Headers &headers) { return Get(path, headers, Progress()); } -Result ClientImpl::Get(const std::string &path, const Headers &headers, +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, Progress progress) { Request req; req.method = "GET"; @@ -5299,45 +7584,45 @@ Result ClientImpl::Get(const std::string &path, const Headers &headers, return send_(std::move(req)); } -Result ClientImpl::Get(const std::string &path, +inline Result ClientImpl::Get(const std::string &path, ContentReceiver content_receiver) { return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr); } -Result ClientImpl::Get(const std::string &path, +inline Result ClientImpl::Get(const std::string &path, ContentReceiver content_receiver, Progress progress) { return Get(path, Headers(), nullptr, std::move(content_receiver), std::move(progress)); } -Result ClientImpl::Get(const std::string &path, const Headers &headers, +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver) { return Get(path, headers, nullptr, std::move(content_receiver), nullptr); } -Result ClientImpl::Get(const std::string &path, const Headers &headers, +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, Progress progress) { return Get(path, headers, nullptr, std::move(content_receiver), std::move(progress)); } -Result ClientImpl::Get(const std::string &path, +inline Result ClientImpl::Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver) { return Get(path, Headers(), std::move(response_handler), std::move(content_receiver), nullptr); } -Result ClientImpl::Get(const std::string &path, const Headers &headers, +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver) { return Get(path, headers, std::move(response_handler), std::move(content_receiver), nullptr); } -Result ClientImpl::Get(const std::string &path, +inline Result ClientImpl::Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) { @@ -5345,7 +7630,7 @@ Result ClientImpl::Get(const std::string &path, std::move(content_receiver), std::move(progress)); } -Result ClientImpl::Get(const std::string &path, const Headers &headers, +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) { @@ -5364,7 +7649,7 @@ Result ClientImpl::Get(const std::string &path, const Headers &headers, return send_(std::move(req)); } -Result ClientImpl::Get(const std::string &path, const Params ¶ms, +inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, const Headers &headers, Progress progress) { if (params.empty()) { return Get(path, headers); } @@ -5372,7 +7657,7 @@ Result ClientImpl::Get(const std::string &path, const Params ¶ms, return Get(path_with_query, headers, std::move(progress)); } -Result ClientImpl::Get(const std::string &path, const Params ¶ms, +inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, const Headers &headers, ContentReceiver content_receiver, Progress progress) { @@ -5380,7 +7665,7 @@ Result ClientImpl::Get(const std::string &path, const Params ¶ms, std::move(progress)); } -Result ClientImpl::Get(const std::string &path, const Params ¶ms, +inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, @@ -5395,11 +7680,11 @@ Result ClientImpl::Get(const std::string &path, const Params ¶ms, std::move(content_receiver), std::move(progress)); } -Result ClientImpl::Head(const std::string &path) { +inline Result ClientImpl::Head(const std::string &path) { return Head(path, Headers()); } -Result ClientImpl::Head(const std::string &path, +inline Result ClientImpl::Head(const std::string &path, const Headers &headers) { Request req; req.method = "HEAD"; @@ -5409,34 +7694,34 @@ Result ClientImpl::Head(const std::string &path, return send_(std::move(req)); } -Result ClientImpl::Post(const std::string &path) { +inline Result ClientImpl::Post(const std::string &path) { return Post(path, std::string(), std::string()); } -Result ClientImpl::Post(const std::string &path, +inline Result ClientImpl::Post(const std::string &path, const Headers &headers) { return Post(path, headers, nullptr, 0, std::string()); } -Result ClientImpl::Post(const std::string &path, const char *body, +inline Result ClientImpl::Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type) { return Post(path, Headers(), body, content_length, content_type); } -Result ClientImpl::Post(const std::string &path, const Headers &headers, +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return send_with_content_provider("POST", path, headers, body, content_length, nullptr, nullptr, content_type); } -Result ClientImpl::Post(const std::string &path, const std::string &body, +inline Result ClientImpl::Post(const std::string &path, const std::string &body, const std::string &content_type) { return Post(path, Headers(), body, content_type); } -Result ClientImpl::Post(const std::string &path, const Headers &headers, +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return send_with_content_provider("POST", path, headers, body.data(), @@ -5444,24 +7729,24 @@ Result ClientImpl::Post(const std::string &path, const Headers &headers, content_type); } -Result ClientImpl::Post(const std::string &path, const Params ¶ms) { +inline Result ClientImpl::Post(const std::string &path, const Params ¶ms) { return Post(path, Headers(), params); } -Result ClientImpl::Post(const std::string &path, size_t content_length, +inline Result ClientImpl::Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type) { return Post(path, Headers(), content_length, std::move(content_provider), content_type); } -Result ClientImpl::Post(const std::string &path, +inline Result ClientImpl::Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type) { return Post(path, Headers(), std::move(content_provider), content_type); } -Result ClientImpl::Post(const std::string &path, const Headers &headers, +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type) { @@ -5470,25 +7755,25 @@ Result ClientImpl::Post(const std::string &path, const Headers &headers, nullptr, content_type); } -Result ClientImpl::Post(const std::string &path, const Headers &headers, +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type) { return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr, std::move(content_provider), content_type); } -Result ClientImpl::Post(const std::string &path, const Headers &headers, +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const Params ¶ms) { auto query = detail::params_to_query_str(params); return Post(path, headers, query, "application/x-www-form-urlencoded"); } -Result ClientImpl::Post(const std::string &path, +inline Result ClientImpl::Post(const std::string &path, const MultipartFormDataItems &items) { return Post(path, Headers(), items); } -Result ClientImpl::Post(const std::string &path, const Headers &headers, +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items) { const auto &boundary = detail::make_multipart_data_boundary(); const auto &content_type = @@ -5497,7 +7782,7 @@ Result ClientImpl::Post(const std::string &path, const Headers &headers, return Post(path, headers, body, content_type); } -Result ClientImpl::Post(const std::string &path, const Headers &headers, +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items, const std::string &boundary) { if (!detail::is_multipart_boundary_chars_valid(boundary)) { @@ -5510,7 +7795,7 @@ Result ClientImpl::Post(const std::string &path, const Headers &headers, return Post(path, headers, body, content_type); } -Result +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items, const MultipartFormDataProviderItems &provider_items) { @@ -5523,29 +7808,29 @@ ClientImpl::Post(const std::string &path, const Headers &headers, content_type); } -Result ClientImpl::Put(const std::string &path) { +inline Result ClientImpl::Put(const std::string &path) { return Put(path, std::string(), std::string()); } -Result ClientImpl::Put(const std::string &path, const char *body, +inline Result ClientImpl::Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type) { return Put(path, Headers(), body, content_length, content_type); } -Result ClientImpl::Put(const std::string &path, const Headers &headers, +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return send_with_content_provider("PUT", path, headers, body, content_length, nullptr, nullptr, content_type); } -Result ClientImpl::Put(const std::string &path, const std::string &body, +inline Result ClientImpl::Put(const std::string &path, const std::string &body, const std::string &content_type) { return Put(path, Headers(), body, content_type); } -Result ClientImpl::Put(const std::string &path, const Headers &headers, +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return send_with_content_provider("PUT", path, headers, body.data(), @@ -5553,20 +7838,20 @@ Result ClientImpl::Put(const std::string &path, const Headers &headers, content_type); } -Result ClientImpl::Put(const std::string &path, size_t content_length, +inline Result ClientImpl::Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type) { return Put(path, Headers(), content_length, std::move(content_provider), content_type); } -Result ClientImpl::Put(const std::string &path, +inline Result ClientImpl::Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type) { return Put(path, Headers(), std::move(content_provider), content_type); } -Result ClientImpl::Put(const std::string &path, const Headers &headers, +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type) { @@ -5575,29 +7860,29 @@ Result ClientImpl::Put(const std::string &path, const Headers &headers, nullptr, content_type); } -Result ClientImpl::Put(const std::string &path, const Headers &headers, +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type) { return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider), content_type); } -Result ClientImpl::Put(const std::string &path, const Params ¶ms) { +inline Result ClientImpl::Put(const std::string &path, const Params ¶ms) { return Put(path, Headers(), params); } -Result ClientImpl::Put(const std::string &path, const Headers &headers, +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const Params ¶ms) { auto query = detail::params_to_query_str(params); return Put(path, headers, query, "application/x-www-form-urlencoded"); } -Result ClientImpl::Put(const std::string &path, +inline Result ClientImpl::Put(const std::string &path, const MultipartFormDataItems &items) { return Put(path, Headers(), items); } -Result ClientImpl::Put(const std::string &path, const Headers &headers, +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items) { const auto &boundary = detail::make_multipart_data_boundary(); const auto &content_type = @@ -5606,7 +7891,7 @@ Result ClientImpl::Put(const std::string &path, const Headers &headers, return Put(path, headers, body, content_type); } -Result ClientImpl::Put(const std::string &path, const Headers &headers, +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items, const std::string &boundary) { if (!detail::is_multipart_boundary_chars_valid(boundary)) { @@ -5619,7 +7904,7 @@ Result ClientImpl::Put(const std::string &path, const Headers &headers, return Put(path, headers, body, content_type); } -Result +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items, const MultipartFormDataProviderItems &provider_items) { @@ -5631,17 +7916,17 @@ ClientImpl::Put(const std::string &path, const Headers &headers, get_multipart_content_provider(boundary, items, provider_items), content_type); } -Result ClientImpl::Patch(const std::string &path) { +inline Result ClientImpl::Patch(const std::string &path) { return Patch(path, std::string(), std::string()); } -Result ClientImpl::Patch(const std::string &path, const char *body, +inline Result ClientImpl::Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type) { return Patch(path, Headers(), body, content_length, content_type); } -Result ClientImpl::Patch(const std::string &path, const Headers &headers, +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return send_with_content_provider("PATCH", path, headers, body, @@ -5649,13 +7934,13 @@ Result ClientImpl::Patch(const std::string &path, const Headers &headers, content_type); } -Result ClientImpl::Patch(const std::string &path, +inline Result ClientImpl::Patch(const std::string &path, const std::string &body, const std::string &content_type) { return Patch(path, Headers(), body, content_type); } -Result ClientImpl::Patch(const std::string &path, const Headers &headers, +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return send_with_content_provider("PATCH", path, headers, body.data(), @@ -5663,20 +7948,20 @@ Result ClientImpl::Patch(const std::string &path, const Headers &headers, content_type); } -Result ClientImpl::Patch(const std::string &path, size_t content_length, +inline Result ClientImpl::Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type) { return Patch(path, Headers(), content_length, std::move(content_provider), content_type); } -Result ClientImpl::Patch(const std::string &path, +inline Result ClientImpl::Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type) { return Patch(path, Headers(), std::move(content_provider), content_type); } -Result ClientImpl::Patch(const std::string &path, const Headers &headers, +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type) { @@ -5685,29 +7970,29 @@ Result ClientImpl::Patch(const std::string &path, const Headers &headers, nullptr, content_type); } -Result ClientImpl::Patch(const std::string &path, const Headers &headers, +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type) { return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider), content_type); } -Result ClientImpl::Delete(const std::string &path) { +inline Result ClientImpl::Delete(const std::string &path) { return Delete(path, Headers(), std::string(), std::string()); } -Result ClientImpl::Delete(const std::string &path, +inline Result ClientImpl::Delete(const std::string &path, const Headers &headers) { return Delete(path, headers, std::string(), std::string()); } -Result ClientImpl::Delete(const std::string &path, const char *body, +inline Result ClientImpl::Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type) { return Delete(path, Headers(), body, content_length, content_type); } -Result ClientImpl::Delete(const std::string &path, +inline Result ClientImpl::Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { @@ -5722,24 +8007,24 @@ Result ClientImpl::Delete(const std::string &path, return send_(std::move(req)); } -Result ClientImpl::Delete(const std::string &path, +inline Result ClientImpl::Delete(const std::string &path, const std::string &body, const std::string &content_type) { return Delete(path, Headers(), body.data(), body.size(), content_type); } -Result ClientImpl::Delete(const std::string &path, +inline Result ClientImpl::Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return Delete(path, headers, body.data(), body.size(), content_type); } -Result ClientImpl::Options(const std::string &path) { +inline Result ClientImpl::Options(const std::string &path) { return Options(path, Headers()); } -Result ClientImpl::Options(const std::string &path, +inline Result ClientImpl::Options(const std::string &path, const Headers &headers) { Request req; req.method = "OPTIONS"; @@ -5749,7 +8034,7 @@ Result ClientImpl::Options(const std::string &path, return send_(std::move(req)); } -void ClientImpl::stop() { +inline void ClientImpl::stop() { std::lock_guard guard(socket_mutex_); // If there is anything ongoing right now, the ONLY thread-safe thing we can @@ -5772,123 +8057,123 @@ void ClientImpl::stop() { close_socket(socket_); } -std::string ClientImpl::host() const { return host_; } +inline std::string ClientImpl::host() const { return host_; } -int ClientImpl::port() const { return port_; } +inline int ClientImpl::port() const { return port_; } -size_t ClientImpl::is_socket_open() const { +inline size_t ClientImpl::is_socket_open() const { std::lock_guard guard(socket_mutex_); return socket_.is_open(); } -socket_t ClientImpl::socket() const { return socket_.sock; } +inline socket_t ClientImpl::socket() const { return socket_.sock; } -void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { +inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { connection_timeout_sec_ = sec; connection_timeout_usec_ = usec; } -void ClientImpl::set_read_timeout(time_t sec, time_t usec) { +inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) { read_timeout_sec_ = sec; read_timeout_usec_ = usec; } -void ClientImpl::set_write_timeout(time_t sec, time_t usec) { +inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) { write_timeout_sec_ = sec; write_timeout_usec_ = usec; } -void ClientImpl::set_basic_auth(const std::string &username, +inline void ClientImpl::set_basic_auth(const std::string &username, const std::string &password) { basic_auth_username_ = username; basic_auth_password_ = password; } -void ClientImpl::set_bearer_token_auth(const std::string &token) { +inline void ClientImpl::set_bearer_token_auth(const std::string &token) { bearer_token_auth_token_ = token; } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -void ClientImpl::set_digest_auth(const std::string &username, +inline void ClientImpl::set_digest_auth(const std::string &username, const std::string &password) { digest_auth_username_ = username; digest_auth_password_ = password; } #endif -void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; } +inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; } -void ClientImpl::set_follow_location(bool on) { follow_location_ = on; } +inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; } -void ClientImpl::set_url_encode(bool on) { url_encode_ = on; } +inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; } -void +inline void ClientImpl::set_hostname_addr_map(std::map addr_map) { addr_map_ = std::move(addr_map); } -void ClientImpl::set_default_headers(Headers headers) { +inline void ClientImpl::set_default_headers(Headers headers) { default_headers_ = std::move(headers); } -void ClientImpl::set_header_writer( +inline void ClientImpl::set_header_writer( std::function const &writer) { header_writer_ = writer; } -void ClientImpl::set_address_family(int family) { +inline void ClientImpl::set_address_family(int family) { address_family_ = family; } -void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } +inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } -void ClientImpl::set_socket_options(SocketOptions socket_options) { +inline void ClientImpl::set_socket_options(SocketOptions socket_options) { socket_options_ = std::move(socket_options); } -void ClientImpl::set_compress(bool on) { compress_ = on; } +inline void ClientImpl::set_compress(bool on) { compress_ = on; } -void ClientImpl::set_decompress(bool on) { decompress_ = on; } +inline void ClientImpl::set_decompress(bool on) { decompress_ = on; } -void ClientImpl::set_interface(const std::string &intf) { +inline void ClientImpl::set_interface(const std::string &intf) { interface_ = intf; } -void ClientImpl::set_proxy(const std::string &host, int port) { +inline void ClientImpl::set_proxy(const std::string &host, int port) { proxy_host_ = host; proxy_port_ = port; } -void ClientImpl::set_proxy_basic_auth(const std::string &username, +inline void ClientImpl::set_proxy_basic_auth(const std::string &username, const std::string &password) { proxy_basic_auth_username_ = username; proxy_basic_auth_password_ = password; } -void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) { +inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) { proxy_bearer_token_auth_token_ = token; } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -void ClientImpl::set_proxy_digest_auth(const std::string &username, +inline void ClientImpl::set_proxy_digest_auth(const std::string &username, const std::string &password) { proxy_digest_auth_username_ = username; proxy_digest_auth_password_ = password; } -void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path, +inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path, const std::string &ca_cert_dir_path) { ca_cert_file_path_ = ca_cert_file_path; ca_cert_dir_path_ = ca_cert_dir_path; } -void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) { +inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) { if (ca_cert_store && ca_cert_store != ca_cert_store_) { ca_cert_store_ = ca_cert_store; } } -X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert, +inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert, std::size_t size) const { auto mem = BIO_new_mem_buf(ca_cert, static_cast(size)); if (!mem) { return nullptr; } @@ -5915,12 +8200,12 @@ X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert, return cts; } -void ClientImpl::enable_server_certificate_verification(bool enabled) { +inline void ClientImpl::enable_server_certificate_verification(bool enabled) { server_certificate_verification_ = enabled; } #endif -void ClientImpl::set_logger(Logger logger) { +inline void ClientImpl::set_logger(Logger logger) { logger_ = std::move(logger); } @@ -5931,7 +8216,7 @@ void ClientImpl::set_logger(Logger logger) { namespace detail { template -SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, +inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup) { SSL *ssl = nullptr; { @@ -5961,7 +8246,7 @@ SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, return ssl; } -void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, +inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, bool shutdown_gracefully) { // sometimes we may want to skip this to try to avoid SIGPIPE if we know // the remote has closed the network connection @@ -5996,7 +8281,7 @@ bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl, } template -bool process_server_socket_ssl( +inline bool process_server_socket_ssl( const std::atomic &svr_sock, SSL *ssl, socket_t sock, size_t keep_alive_max_count, time_t keep_alive_timeout_sec, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, @@ -6011,7 +8296,7 @@ bool process_server_socket_ssl( } template -bool +inline bool process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec, T callback) { @@ -6029,7 +8314,7 @@ public: }; // SSL socket stream implementation -SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, @@ -6041,18 +8326,18 @@ SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); } -SSLSocketStream::~SSLSocketStream() = default; +inline SSLSocketStream::~SSLSocketStream() = default; -bool SSLSocketStream::is_readable() const { +inline bool SSLSocketStream::is_readable() const { return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; } -bool SSLSocketStream::is_writable() const { +inline bool SSLSocketStream::is_writable() const { return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && is_socket_alive(sock_); } -ssize_t SSLSocketStream::read(char *ptr, size_t size) { +inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { if (SSL_pending(ssl_) > 0) { return SSL_read(ssl_, ptr, static_cast(size)); } else if (is_readable()) { @@ -6084,7 +8369,7 @@ ssize_t SSLSocketStream::read(char *ptr, size_t size) { return -1; } -ssize_t SSLSocketStream::write(const char *ptr, size_t size) { +inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { if (is_writable()) { auto handle_size = static_cast( std::min(size, (std::numeric_limits::max)())); @@ -6115,24 +8400,24 @@ ssize_t SSLSocketStream::write(const char *ptr, size_t size) { return -1; } -void SSLSocketStream::get_remote_ip_and_port(std::string &ip, +inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, int &port) const { detail::get_remote_ip_and_port(sock_, ip, port); } -void SSLSocketStream::get_local_ip_and_port(std::string &ip, +inline void SSLSocketStream::get_local_ip_and_port(std::string &ip, int &port) const { detail::get_local_ip_and_port(sock_, ip, port); } -socket_t SSLSocketStream::socket() const { return sock_; } +inline socket_t SSLSocketStream::socket() const { return sock_; } static SSLInit sslinit_; } // namespace detail // SSL HTTP server implementation -SSLServer::SSLServer(const char *cert_path, const char *private_key_path, +inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path, const char *client_ca_cert_dir_path, const char *private_key_password) { @@ -6167,7 +8452,7 @@ SSLServer::SSLServer(const char *cert_path, const char *private_key_path, } } -SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, +inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store) { ctx_ = SSL_CTX_new(TLS_server_method()); @@ -6191,7 +8476,7 @@ SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, } } -SSLServer::SSLServer( +inline SSLServer::SSLServer( const std::function &setup_ssl_ctx_callback) { ctx_ = SSL_CTX_new(TLS_method()); if (ctx_) { @@ -6202,15 +8487,15 @@ SSLServer::SSLServer( } } -SSLServer::~SSLServer() { +inline SSLServer::~SSLServer() { if (ctx_) { SSL_CTX_free(ctx_); } } -bool SSLServer::is_valid() const { return ctx_; } +inline bool SSLServer::is_valid() const { return ctx_; } -SSL_CTX *SSLServer::ssl_context() const { return ctx_; } +inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; } -bool SSLServer::process_and_close_socket(socket_t sock) { +inline bool SSLServer::process_and_close_socket(socket_t sock) { auto ssl = detail::ssl_new( sock, ctx_, ctx_mutex_, [&](SSL *ssl2) { @@ -6243,13 +8528,13 @@ bool SSLServer::process_and_close_socket(socket_t sock) { } // SSL HTTP client implementation -SSLClient::SSLClient(const std::string &host) +inline SSLClient::SSLClient(const std::string &host) : SSLClient(host, 443, std::string(), std::string()) {} -SSLClient::SSLClient(const std::string &host, int port) +inline SSLClient::SSLClient(const std::string &host, int port) : SSLClient(host, port, std::string(), std::string()) {} -SSLClient::SSLClient(const std::string &host, int port, +inline SSLClient::SSLClient(const std::string &host, int port, const std::string &client_cert_path, const std::string &client_key_path) : ClientImpl(host, port, client_cert_path, client_key_path) { @@ -6271,7 +8556,7 @@ SSLClient::SSLClient(const std::string &host, int port, } } -SSLClient::SSLClient(const std::string &host, int port, +inline SSLClient::SSLClient(const std::string &host, int port, X509 *client_cert, EVP_PKEY *client_key) : ClientImpl(host, port) { ctx_ = SSL_CTX_new(TLS_client_method()); @@ -6290,7 +8575,7 @@ SSLClient::SSLClient(const std::string &host, int port, } } -SSLClient::~SSLClient() { +inline SSLClient::~SSLClient() { if (ctx_) { SSL_CTX_free(ctx_); } // Make sure to shut down SSL since shutdown_ssl will resolve to the // base function rather than the derived function once we get to the @@ -6298,9 +8583,9 @@ SSLClient::~SSLClient() { shutdown_ssl_impl(socket_, true); } -bool SSLClient::is_valid() const { return ctx_; } +inline bool SSLClient::is_valid() const { return ctx_; } -void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { +inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { if (ca_cert_store) { if (ctx_) { if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) { @@ -6313,23 +8598,23 @@ void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { } } -void SSLClient::load_ca_cert_store(const char *ca_cert, +inline void SSLClient::load_ca_cert_store(const char *ca_cert, std::size_t size) { set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size)); } -long SSLClient::get_openssl_verify_result() const { +inline long SSLClient::get_openssl_verify_result() const { return verify_result_; } -SSL_CTX *SSLClient::ssl_context() const { return ctx_; } +inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; } -bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) { +inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) { return is_valid() && ClientImpl::create_and_connect_socket(socket, error); } // Assumes that socket_mutex_ is locked and that there are no requests in flight -bool SSLClient::connect_with_proxy(Socket &socket, Response &res, +inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, bool &success, Error &error) { success = true; Response proxy_res; @@ -6397,7 +8682,7 @@ bool SSLClient::connect_with_proxy(Socket &socket, Response &res, return true; } -bool SSLClient::load_certs() { +inline bool SSLClient::load_certs() { auto ret = true; std::call_once(initialize_cert_, [&]() { @@ -6429,7 +8714,7 @@ bool SSLClient::load_certs() { return ret; } -bool SSLClient::initialize_ssl(Socket &socket, Error &error) { +inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) { auto ssl = detail::ssl_new( socket.sock, ctx_, ctx_mutex_, [&](SSL *ssl2) { @@ -6492,11 +8777,11 @@ bool SSLClient::initialize_ssl(Socket &socket, Error &error) { return false; } -void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) { +inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) { shutdown_ssl_impl(socket, shutdown_gracefully); } -void SSLClient::shutdown_ssl_impl(Socket &socket, +inline void SSLClient::shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully) { if (socket.sock == INVALID_SOCKET) { assert(socket.ssl == nullptr); @@ -6509,7 +8794,7 @@ void SSLClient::shutdown_ssl_impl(Socket &socket, assert(socket.ssl == nullptr); } -bool +inline bool SSLClient::process_socket(const Socket &socket, std::function callback) { assert(socket.ssl); @@ -6518,9 +8803,9 @@ SSLClient::process_socket(const Socket &socket, write_timeout_sec_, write_timeout_usec_, std::move(callback)); } -bool SSLClient::is_ssl() const { return true; } +inline bool SSLClient::is_ssl() const { return true; } -bool SSLClient::verify_host(X509 *server_cert) const { +inline bool SSLClient::verify_host(X509 *server_cert) const { /* Quote from RFC2818 section 3.1 "Server Identity" If a subjectAltName extension of type dNSName is present, that MUST @@ -6546,7 +8831,7 @@ bool SSLClient::verify_host(X509 *server_cert) const { verify_host_with_common_name(server_cert); } -bool +inline bool SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { auto ret = false; @@ -6603,7 +8888,7 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { return ret; } -bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { +inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { const auto subject_name = X509_get_subject_name(server_cert); if (subject_name != nullptr) { @@ -6619,7 +8904,7 @@ bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { return false; } -bool SSLClient::check_host_name(const char *pattern, +inline bool SSLClient::check_host_name(const char *pattern, size_t pattern_len) const { if (host_.size() == pattern_len && host_ == pattern) { return true; } @@ -6649,10 +8934,10 @@ bool SSLClient::check_host_name(const char *pattern, #endif // Universal client implementation -Client::Client(const std::string &scheme_host_port) +inline Client::Client(const std::string &scheme_host_port) : Client(scheme_host_port, std::string(), std::string()) {} -Client::Client(const std::string &scheme_host_port, +inline Client::Client(const std::string &scheme_host_port, const std::string &client_cert_path, const std::string &client_key_path) { const static std::regex re( @@ -6698,84 +8983,84 @@ Client::Client(const std::string &scheme_host_port, } } -Client::Client(const std::string &host, int port) +inline Client::Client(const std::string &host, int port) : cli_(detail::make_unique(host, port)) {} -Client::Client(const std::string &host, int port, +inline Client::Client(const std::string &host, int port, const std::string &client_cert_path, const std::string &client_key_path) : cli_(detail::make_unique(host, port, client_cert_path, client_key_path)) {} -Client::~Client() = default; +inline Client::~Client() = default; -bool Client::is_valid() const { +inline bool Client::is_valid() const { return cli_ != nullptr && cli_->is_valid(); } -Result Client::Get(const std::string &path) { return cli_->Get(path); } -Result Client::Get(const std::string &path, const Headers &headers) { +inline Result Client::Get(const std::string &path) { return cli_->Get(path); } +inline Result Client::Get(const std::string &path, const Headers &headers) { return cli_->Get(path, headers); } -Result Client::Get(const std::string &path, Progress progress) { +inline Result Client::Get(const std::string &path, Progress progress) { return cli_->Get(path, std::move(progress)); } -Result Client::Get(const std::string &path, const Headers &headers, +inline Result Client::Get(const std::string &path, const Headers &headers, Progress progress) { return cli_->Get(path, headers, std::move(progress)); } -Result Client::Get(const std::string &path, +inline Result Client::Get(const std::string &path, ContentReceiver content_receiver) { return cli_->Get(path, std::move(content_receiver)); } -Result Client::Get(const std::string &path, const Headers &headers, +inline Result Client::Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver) { return cli_->Get(path, headers, std::move(content_receiver)); } -Result Client::Get(const std::string &path, +inline Result Client::Get(const std::string &path, ContentReceiver content_receiver, Progress progress) { return cli_->Get(path, std::move(content_receiver), std::move(progress)); } -Result Client::Get(const std::string &path, const Headers &headers, +inline Result Client::Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, Progress progress) { return cli_->Get(path, headers, std::move(content_receiver), std::move(progress)); } -Result Client::Get(const std::string &path, +inline Result Client::Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver) { return cli_->Get(path, std::move(response_handler), std::move(content_receiver)); } -Result Client::Get(const std::string &path, const Headers &headers, +inline Result Client::Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver) { return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver)); } -Result Client::Get(const std::string &path, +inline Result Client::Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) { return cli_->Get(path, std::move(response_handler), std::move(content_receiver), std::move(progress)); } -Result Client::Get(const std::string &path, const Headers &headers, +inline Result Client::Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) { return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver), std::move(progress)); } -Result Client::Get(const std::string &path, const Params ¶ms, +inline Result Client::Get(const std::string &path, const Params ¶ms, const Headers &headers, Progress progress) { return cli_->Get(path, params, headers, std::move(progress)); } -Result Client::Get(const std::string &path, const Params ¶ms, +inline Result Client::Get(const std::string &path, const Params ¶ms, const Headers &headers, ContentReceiver content_receiver, Progress progress) { return cli_->Get(path, params, headers, std::move(content_receiver), std::move(progress)); } -Result Client::Get(const std::string &path, const Params ¶ms, +inline Result Client::Get(const std::string &path, const Params ¶ms, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) { @@ -6783,344 +9068,344 @@ Result Client::Get(const std::string &path, const Params ¶ms, std::move(content_receiver), std::move(progress)); } -Result Client::Head(const std::string &path) { return cli_->Head(path); } -Result Client::Head(const std::string &path, const Headers &headers) { +inline Result Client::Head(const std::string &path) { return cli_->Head(path); } +inline Result Client::Head(const std::string &path, const Headers &headers) { return cli_->Head(path, headers); } -Result Client::Post(const std::string &path) { return cli_->Post(path); } -Result Client::Post(const std::string &path, const Headers &headers) { +inline Result Client::Post(const std::string &path) { return cli_->Post(path); } +inline Result Client::Post(const std::string &path, const Headers &headers) { return cli_->Post(path, headers); } -Result Client::Post(const std::string &path, const char *body, +inline Result Client::Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type) { return cli_->Post(path, body, content_length, content_type); } -Result Client::Post(const std::string &path, const Headers &headers, +inline Result Client::Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return cli_->Post(path, headers, body, content_length, content_type); } -Result Client::Post(const std::string &path, const std::string &body, +inline Result Client::Post(const std::string &path, const std::string &body, const std::string &content_type) { return cli_->Post(path, body, content_type); } -Result Client::Post(const std::string &path, const Headers &headers, +inline Result Client::Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return cli_->Post(path, headers, body, content_type); } -Result Client::Post(const std::string &path, size_t content_length, +inline Result Client::Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type) { return cli_->Post(path, content_length, std::move(content_provider), content_type); } -Result Client::Post(const std::string &path, +inline Result Client::Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type) { return cli_->Post(path, std::move(content_provider), content_type); } -Result Client::Post(const std::string &path, const Headers &headers, +inline Result Client::Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type) { return cli_->Post(path, headers, content_length, std::move(content_provider), content_type); } -Result Client::Post(const std::string &path, const Headers &headers, +inline Result Client::Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type) { return cli_->Post(path, headers, std::move(content_provider), content_type); } -Result Client::Post(const std::string &path, const Params ¶ms) { +inline Result Client::Post(const std::string &path, const Params ¶ms) { return cli_->Post(path, params); } -Result Client::Post(const std::string &path, const Headers &headers, +inline Result Client::Post(const std::string &path, const Headers &headers, const Params ¶ms) { return cli_->Post(path, headers, params); } -Result Client::Post(const std::string &path, +inline Result Client::Post(const std::string &path, const MultipartFormDataItems &items) { return cli_->Post(path, items); } -Result Client::Post(const std::string &path, const Headers &headers, +inline Result Client::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items) { return cli_->Post(path, headers, items); } -Result Client::Post(const std::string &path, const Headers &headers, +inline Result Client::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items, const std::string &boundary) { return cli_->Post(path, headers, items, boundary); } -Result +inline Result Client::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items, const MultipartFormDataProviderItems &provider_items) { return cli_->Post(path, headers, items, provider_items); } -Result Client::Put(const std::string &path) { return cli_->Put(path); } -Result Client::Put(const std::string &path, const char *body, +inline Result Client::Put(const std::string &path) { return cli_->Put(path); } +inline Result Client::Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type) { return cli_->Put(path, body, content_length, content_type); } -Result Client::Put(const std::string &path, const Headers &headers, +inline Result Client::Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return cli_->Put(path, headers, body, content_length, content_type); } -Result Client::Put(const std::string &path, const std::string &body, +inline Result Client::Put(const std::string &path, const std::string &body, const std::string &content_type) { return cli_->Put(path, body, content_type); } -Result Client::Put(const std::string &path, const Headers &headers, +inline Result Client::Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return cli_->Put(path, headers, body, content_type); } -Result Client::Put(const std::string &path, size_t content_length, +inline Result Client::Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type) { return cli_->Put(path, content_length, std::move(content_provider), content_type); } -Result Client::Put(const std::string &path, +inline Result Client::Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type) { return cli_->Put(path, std::move(content_provider), content_type); } -Result Client::Put(const std::string &path, const Headers &headers, +inline Result Client::Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type) { return cli_->Put(path, headers, content_length, std::move(content_provider), content_type); } -Result Client::Put(const std::string &path, const Headers &headers, +inline Result Client::Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type) { return cli_->Put(path, headers, std::move(content_provider), content_type); } -Result Client::Put(const std::string &path, const Params ¶ms) { +inline Result Client::Put(const std::string &path, const Params ¶ms) { return cli_->Put(path, params); } -Result Client::Put(const std::string &path, const Headers &headers, +inline Result Client::Put(const std::string &path, const Headers &headers, const Params ¶ms) { return cli_->Put(path, headers, params); } -Result Client::Put(const std::string &path, +inline Result Client::Put(const std::string &path, const MultipartFormDataItems &items) { return cli_->Put(path, items); } -Result Client::Put(const std::string &path, const Headers &headers, +inline Result Client::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items) { return cli_->Put(path, headers, items); } -Result Client::Put(const std::string &path, const Headers &headers, +inline Result Client::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items, const std::string &boundary) { return cli_->Put(path, headers, items, boundary); } -Result +inline Result Client::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items, const MultipartFormDataProviderItems &provider_items) { return cli_->Put(path, headers, items, provider_items); } -Result Client::Patch(const std::string &path) { +inline Result Client::Patch(const std::string &path) { return cli_->Patch(path); } -Result Client::Patch(const std::string &path, const char *body, +inline Result Client::Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type) { return cli_->Patch(path, body, content_length, content_type); } -Result Client::Patch(const std::string &path, const Headers &headers, +inline Result Client::Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return cli_->Patch(path, headers, body, content_length, content_type); } -Result Client::Patch(const std::string &path, const std::string &body, +inline Result Client::Patch(const std::string &path, const std::string &body, const std::string &content_type) { return cli_->Patch(path, body, content_type); } -Result Client::Patch(const std::string &path, const Headers &headers, +inline Result Client::Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return cli_->Patch(path, headers, body, content_type); } -Result Client::Patch(const std::string &path, size_t content_length, +inline Result Client::Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type) { return cli_->Patch(path, content_length, std::move(content_provider), content_type); } -Result Client::Patch(const std::string &path, +inline Result Client::Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type) { return cli_->Patch(path, std::move(content_provider), content_type); } -Result Client::Patch(const std::string &path, const Headers &headers, +inline Result Client::Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type) { return cli_->Patch(path, headers, content_length, std::move(content_provider), content_type); } -Result Client::Patch(const std::string &path, const Headers &headers, +inline Result Client::Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type) { return cli_->Patch(path, headers, std::move(content_provider), content_type); } -Result Client::Delete(const std::string &path) { +inline Result Client::Delete(const std::string &path) { return cli_->Delete(path); } -Result Client::Delete(const std::string &path, const Headers &headers) { +inline Result Client::Delete(const std::string &path, const Headers &headers) { return cli_->Delete(path, headers); } -Result Client::Delete(const std::string &path, const char *body, +inline Result Client::Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type) { return cli_->Delete(path, body, content_length, content_type); } -Result Client::Delete(const std::string &path, const Headers &headers, +inline Result Client::Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return cli_->Delete(path, headers, body, content_length, content_type); } -Result Client::Delete(const std::string &path, const std::string &body, +inline Result Client::Delete(const std::string &path, const std::string &body, const std::string &content_type) { return cli_->Delete(path, body, content_type); } -Result Client::Delete(const std::string &path, const Headers &headers, +inline Result Client::Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return cli_->Delete(path, headers, body, content_type); } -Result Client::Options(const std::string &path) { +inline Result Client::Options(const std::string &path) { return cli_->Options(path); } -Result Client::Options(const std::string &path, const Headers &headers) { +inline Result Client::Options(const std::string &path, const Headers &headers) { return cli_->Options(path, headers); } -bool Client::send(Request &req, Response &res, Error &error) { +inline bool Client::send(Request &req, Response &res, Error &error) { return cli_->send(req, res, error); } -Result Client::send(const Request &req) { return cli_->send(req); } +inline Result Client::send(const Request &req) { return cli_->send(req); } -void Client::stop() { cli_->stop(); } +inline void Client::stop() { cli_->stop(); } -std::string Client::host() const { return cli_->host(); } +inline std::string Client::host() const { return cli_->host(); } -int Client::port() const { return cli_->port(); } +inline int Client::port() const { return cli_->port(); } -size_t Client::is_socket_open() const { return cli_->is_socket_open(); } +inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); } -socket_t Client::socket() const { return cli_->socket(); } +inline socket_t Client::socket() const { return cli_->socket(); } -void +inline void Client::set_hostname_addr_map(std::map addr_map) { cli_->set_hostname_addr_map(std::move(addr_map)); } -void Client::set_default_headers(Headers headers) { +inline void Client::set_default_headers(Headers headers) { cli_->set_default_headers(std::move(headers)); } -void Client::set_header_writer( +inline void Client::set_header_writer( std::function const &writer) { cli_->set_header_writer(writer); } -void Client::set_address_family(int family) { +inline void Client::set_address_family(int family) { cli_->set_address_family(family); } -void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } +inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } -void Client::set_socket_options(SocketOptions socket_options) { +inline void Client::set_socket_options(SocketOptions socket_options) { cli_->set_socket_options(std::move(socket_options)); } -void Client::set_connection_timeout(time_t sec, time_t usec) { +inline void Client::set_connection_timeout(time_t sec, time_t usec) { cli_->set_connection_timeout(sec, usec); } -void Client::set_read_timeout(time_t sec, time_t usec) { +inline void Client::set_read_timeout(time_t sec, time_t usec) { cli_->set_read_timeout(sec, usec); } -void Client::set_write_timeout(time_t sec, time_t usec) { +inline void Client::set_write_timeout(time_t sec, time_t usec) { cli_->set_write_timeout(sec, usec); } -void Client::set_basic_auth(const std::string &username, +inline void Client::set_basic_auth(const std::string &username, const std::string &password) { cli_->set_basic_auth(username, password); } -void Client::set_bearer_token_auth(const std::string &token) { +inline void Client::set_bearer_token_auth(const std::string &token) { cli_->set_bearer_token_auth(token); } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -void Client::set_digest_auth(const std::string &username, +inline void Client::set_digest_auth(const std::string &username, const std::string &password) { cli_->set_digest_auth(username, password); } #endif -void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); } -void Client::set_follow_location(bool on) { +inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); } +inline void Client::set_follow_location(bool on) { cli_->set_follow_location(on); } -void Client::set_url_encode(bool on) { cli_->set_url_encode(on); } +inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); } -void Client::set_compress(bool on) { cli_->set_compress(on); } +inline void Client::set_compress(bool on) { cli_->set_compress(on); } -void Client::set_decompress(bool on) { cli_->set_decompress(on); } +inline void Client::set_decompress(bool on) { cli_->set_decompress(on); } -void Client::set_interface(const std::string &intf) { +inline void Client::set_interface(const std::string &intf) { cli_->set_interface(intf); } -void Client::set_proxy(const std::string &host, int port) { +inline void Client::set_proxy(const std::string &host, int port) { cli_->set_proxy(host, port); } -void Client::set_proxy_basic_auth(const std::string &username, +inline void Client::set_proxy_basic_auth(const std::string &username, const std::string &password) { cli_->set_proxy_basic_auth(username, password); } -void Client::set_proxy_bearer_token_auth(const std::string &token) { +inline void Client::set_proxy_bearer_token_auth(const std::string &token) { cli_->set_proxy_bearer_token_auth(token); } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -void Client::set_proxy_digest_auth(const std::string &username, +inline void Client::set_proxy_digest_auth(const std::string &username, const std::string &password) { cli_->set_proxy_digest_auth(username, password); } #endif #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -void Client::enable_server_certificate_verification(bool enabled) { +inline void Client::enable_server_certificate_verification(bool enabled) { cli_->enable_server_certificate_verification(enabled); } #endif -void Client::set_logger(Logger logger) { +inline void Client::set_logger(Logger logger) { cli_->set_logger(std::move(logger)); } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -void Client::set_ca_cert_path(const std::string &ca_cert_file_path, +inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path, const std::string &ca_cert_dir_path) { cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path); } -void Client::set_ca_cert_store(X509_STORE *ca_cert_store) { +inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) { if (is_ssl_) { static_cast(*cli_).set_ca_cert_store(ca_cert_store); } else { @@ -7128,21 +9413,29 @@ void Client::set_ca_cert_store(X509_STORE *ca_cert_store) { } } -void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) { +inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) { set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size)); } -long Client::get_openssl_verify_result() const { +inline long Client::get_openssl_verify_result() const { if (is_ssl_) { return static_cast(*cli_).get_openssl_verify_result(); } return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? } -SSL_CTX *Client::ssl_context() const { +inline SSL_CTX *Client::ssl_context() const { if (is_ssl_) { return static_cast(*cli_).ssl_context(); } return nullptr; } #endif +// ---------------------------------------------------------------------------- + } // namespace httplib + +#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL) +#undef poll +#endif + +#endif // CPPHTTPLIB_HTTPLIB_H diff --git a/src/third_party/httplib.h b/src/third_party/httplib.h deleted file mode 100644 index b10cb4963..000000000 --- a/src/third_party/httplib.h +++ /dev/null @@ -1,2289 +0,0 @@ -// -// httplib.h -// -// Copyright (c) 2024 Yuji Hirose. All rights reserved. -// MIT License -// - -#ifndef CPPHTTPLIB_HTTPLIB_H -#define CPPHTTPLIB_HTTPLIB_H - -#define CPPHTTPLIB_VERSION "0.15.2" - -/* - * Configuration - */ - -#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND -#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 -#endif - -#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT -#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 -#endif - -#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND -#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 -#endif - -#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND -#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 -#endif - -#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND -#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 -#endif - -#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND -#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 -#endif - -#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND -#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 -#endif - -#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND -#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 -#endif - -#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND -#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 -#endif - -#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND -#ifdef _WIN32 -#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 -#else -#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 -#endif -#endif - -#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH -#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 -#endif - -#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH -#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192 -#endif - -#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT -#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 -#endif - -#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT -#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024 -#endif - -#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH -#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) -#endif - -#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH -#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192 -#endif - -#ifndef CPPHTTPLIB_RANGE_MAX_COUNT -#define CPPHTTPLIB_RANGE_MAX_COUNT 1024 -#endif - -#ifndef CPPHTTPLIB_TCP_NODELAY -#define CPPHTTPLIB_TCP_NODELAY false -#endif - -#ifndef CPPHTTPLIB_RECV_BUFSIZ -#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) -#endif - -#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ -#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) -#endif - -#ifndef CPPHTTPLIB_THREAD_POOL_COUNT -#define CPPHTTPLIB_THREAD_POOL_COUNT \ - ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ - ? std::thread::hardware_concurrency() - 1 \ - : 0)) -#endif - -#ifndef CPPHTTPLIB_RECV_FLAGS -#define CPPHTTPLIB_RECV_FLAGS 0 -#endif - -#ifndef CPPHTTPLIB_SEND_FLAGS -#define CPPHTTPLIB_SEND_FLAGS 0 -#endif - -#ifndef CPPHTTPLIB_LISTEN_BACKLOG -#define CPPHTTPLIB_LISTEN_BACKLOG 5 -#endif - -/* - * Headers - */ - -#ifdef _WIN32 -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif //_CRT_SECURE_NO_WARNINGS - -#ifndef _CRT_NONSTDC_NO_DEPRECATE -#define _CRT_NONSTDC_NO_DEPRECATE -#endif //_CRT_NONSTDC_NO_DEPRECATE - -#if defined(_MSC_VER) -#if _MSC_VER < 1900 -#error Sorry, Visual Studio versions prior to 2015 are not supported -#endif - -#pragma comment(lib, "ws2_32.lib") - -#ifdef _WIN64 -using ssize_t = __int64; -#else -using ssize_t = long; -#endif -#endif // _MSC_VER - -#ifndef S_ISREG -#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG) -#endif // S_ISREG - -#ifndef S_ISDIR -#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) -#endif // S_ISDIR - -#ifndef NOMINMAX -#define NOMINMAX -#endif // NOMINMAX - -#include -#include -#include - -#ifndef WSA_FLAG_NO_HANDLE_INHERIT -#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 -#endif - -using socket_t = SOCKET; -#ifdef CPPHTTPLIB_USE_POLL -#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) -#endif - -#else // not _WIN32 - -#include -#if !defined(_AIX) && !defined(__MVS__) -#include -#endif -#ifdef __MVS__ -#include -#ifndef NI_MAXHOST -#define NI_MAXHOST 1025 -#endif -#endif -#include -#include -#include -#ifdef __linux__ -#include -#endif -#include -#ifdef CPPHTTPLIB_USE_POLL -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -using socket_t = int; -#ifndef INVALID_SOCKET -#define INVALID_SOCKET (-1) -#endif -#endif //_WIN32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -#ifdef _WIN32 -#include - -// these are defined in wincrypt.h and it breaks compilation if BoringSSL is -// used -#undef X509_NAME -#undef X509_CERT_PAIR -#undef X509_EXTENSIONS -#undef PKCS7_SIGNER_INFO - -#ifdef _MSC_VER -#pragma comment(lib, "crypt32.lib") -#endif -#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) -#include -#if TARGET_OS_OSX -#include -#include -#endif // TARGET_OS_OSX -#endif // _WIN32 - -#include -#include -#include -#include - -#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK) -#include -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x30000000L -#error Sorry, OpenSSL versions prior to 3.0.0 are not supported -#endif - -#endif - -#ifdef CPPHTTPLIB_ZLIB_SUPPORT -#include -#endif - -#ifdef CPPHTTPLIB_BROTLI_SUPPORT -#include -#include -#endif - -/* - * Declaration - */ -namespace httplib { - -namespace detail { - -/* - * Backport std::make_unique from C++14. - * - * NOTE: This code came up with the following stackoverflow post: - * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique - * - */ - -template -typename std::enable_if::value, std::unique_ptr>::type -make_unique(Args &&...args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - -template -typename std::enable_if::value, std::unique_ptr>::type -make_unique(std::size_t n) { - typedef typename std::remove_extent::type RT; - return std::unique_ptr(new RT[n]); -} - -struct ci { - bool operator()(const std::string &s1, const std::string &s2) const { - return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), - s2.end(), - [](unsigned char c1, unsigned char c2) { - return ::tolower(c1) < ::tolower(c2); - }); - } -}; - -// This is based on -// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". - -struct scope_exit { - explicit scope_exit(std::function &&f) - : exit_function(std::move(f)), execute_on_destruction{true} {} - - scope_exit(scope_exit &&rhs) noexcept - : exit_function(std::move(rhs.exit_function)), - execute_on_destruction{rhs.execute_on_destruction} { - rhs.release(); - } - - ~scope_exit() { - if (execute_on_destruction) { this->exit_function(); } - } - - void release() { this->execute_on_destruction = false; } - -private: - scope_exit(const scope_exit &) = delete; - void operator=(const scope_exit &) = delete; - scope_exit &operator=(scope_exit &&) = delete; - - std::function exit_function; - bool execute_on_destruction; -}; - -} // namespace detail - -enum StatusCode { - // Information responses - Continue_100 = 100, - SwitchingProtocol_101 = 101, - Processing_102 = 102, - EarlyHints_103 = 103, - - // Successful responses - OK_200 = 200, - Created_201 = 201, - Accepted_202 = 202, - NonAuthoritativeInformation_203 = 203, - NoContent_204 = 204, - ResetContent_205 = 205, - PartialContent_206 = 206, - MultiStatus_207 = 207, - AlreadyReported_208 = 208, - IMUsed_226 = 226, - - // Redirection messages - MultipleChoices_300 = 300, - MovedPermanently_301 = 301, - Found_302 = 302, - SeeOther_303 = 303, - NotModified_304 = 304, - UseProxy_305 = 305, - unused_306 = 306, - TemporaryRedirect_307 = 307, - PermanentRedirect_308 = 308, - - // Client error responses - BadRequest_400 = 400, - Unauthorized_401 = 401, - PaymentRequired_402 = 402, - Forbidden_403 = 403, - NotFound_404 = 404, - MethodNotAllowed_405 = 405, - NotAcceptable_406 = 406, - ProxyAuthenticationRequired_407 = 407, - RequestTimeout_408 = 408, - Conflict_409 = 409, - Gone_410 = 410, - LengthRequired_411 = 411, - PreconditionFailed_412 = 412, - PayloadTooLarge_413 = 413, - UriTooLong_414 = 414, - UnsupportedMediaType_415 = 415, - RangeNotSatisfiable_416 = 416, - ExpectationFailed_417 = 417, - ImATeapot_418 = 418, - MisdirectedRequest_421 = 421, - UnprocessableContent_422 = 422, - Locked_423 = 423, - FailedDependency_424 = 424, - TooEarly_425 = 425, - UpgradeRequired_426 = 426, - PreconditionRequired_428 = 428, - TooManyRequests_429 = 429, - RequestHeaderFieldsTooLarge_431 = 431, - UnavailableForLegalReasons_451 = 451, - - // Server error responses - InternalServerError_500 = 500, - NotImplemented_501 = 501, - BadGateway_502 = 502, - ServiceUnavailable_503 = 503, - GatewayTimeout_504 = 504, - HttpVersionNotSupported_505 = 505, - VariantAlsoNegotiates_506 = 506, - InsufficientStorage_507 = 507, - LoopDetected_508 = 508, - NotExtended_510 = 510, - NetworkAuthenticationRequired_511 = 511, -}; - -using Headers = std::multimap; - -using Params = std::multimap; -using Match = std::smatch; - -using Progress = std::function; - -struct Response; -using ResponseHandler = std::function; - -struct MultipartFormData { - std::string name; - std::string content; - std::string filename; - std::string content_type; -}; -using MultipartFormDataItems = std::vector; -using MultipartFormDataMap = std::multimap; - -class DataSink { -public: - DataSink() : os(&sb_), sb_(*this) {} - - DataSink(const DataSink &) = delete; - DataSink &operator=(const DataSink &) = delete; - DataSink(DataSink &&) = delete; - DataSink &operator=(DataSink &&) = delete; - - std::function write; - std::function is_writable; - std::function done; - std::function done_with_trailer; - std::ostream os; - -private: - class data_sink_streambuf : public std::streambuf { - public: - explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {} - - protected: - std::streamsize xsputn(const char *s, std::streamsize n) override { - sink_.write(s, static_cast(n)); - return n; - } - - private: - DataSink &sink_; - }; - - data_sink_streambuf sb_; -}; - -using ContentProvider = - std::function; - -using ContentProviderWithoutLength = - std::function; - -using ContentProviderResourceReleaser = std::function; - -struct MultipartFormDataProvider { - std::string name; - ContentProviderWithoutLength provider; - std::string filename; - std::string content_type; -}; -using MultipartFormDataProviderItems = std::vector; - -using ContentReceiverWithProgress = - std::function; - -using ContentReceiver = - std::function; - -using MultipartContentHeader = - std::function; - -class ContentReader { -public: - using Reader = std::function; - using MultipartReader = std::function; - - ContentReader(Reader reader, MultipartReader multipart_reader) - : reader_(std::move(reader)), - multipart_reader_(std::move(multipart_reader)) {} - - bool operator()(MultipartContentHeader header, - ContentReceiver receiver) const { - return multipart_reader_(std::move(header), std::move(receiver)); - } - - bool operator()(ContentReceiver receiver) const { - return reader_(std::move(receiver)); - } - - Reader reader_; - MultipartReader multipart_reader_; -}; - -using Range = std::pair; -using Ranges = std::vector; - -struct Request { - std::string method; - std::string path; - Headers headers; - std::string body; - - std::string remote_addr; - int remote_port = -1; - std::string local_addr; - int local_port = -1; - - // for server - std::string version; - std::string target; - Params params; - MultipartFormDataMap files; - Ranges ranges; - Match matches; - std::unordered_map path_params; - - // for client - ResponseHandler response_handler; - ContentReceiverWithProgress content_receiver; - Progress progress; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - const SSL *ssl = nullptr; -#endif - - bool has_header(const std::string &key) const; - std::string get_header_value(const std::string &key, size_t id = 0) const; - uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; - size_t get_header_value_count(const std::string &key) const; - void set_header(const std::string &key, const std::string &val); - - bool has_param(const std::string &key) const; - std::string get_param_value(const std::string &key, size_t id = 0) const; - size_t get_param_value_count(const std::string &key) const; - - bool is_multipart_form_data() const; - - bool has_file(const std::string &key) const; - MultipartFormData get_file_value(const std::string &key) const; - std::vector get_file_values(const std::string &key) const; - - // private members... - size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT; - size_t content_length_ = 0; - ContentProvider content_provider_; - bool is_chunked_content_provider_ = false; - size_t authorization_count_ = 0; -}; - -struct Response { - std::string version; - int status = -1; - std::string reason; - Headers headers; - std::string body; - std::string location; // Redirect location - - bool has_header(const std::string &key) const; - std::string get_header_value(const std::string &key, size_t id = 0) const; - uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; - size_t get_header_value_count(const std::string &key) const; - void set_header(const std::string &key, const std::string &val); - - void set_redirect(const std::string &url, int status = StatusCode::Found_302); - void set_content(const char *s, size_t n, const std::string &content_type); - void set_content(const std::string &s, const std::string &content_type); - void set_content(std::string &&s, const std::string &content_type); - - void set_content_provider( - size_t length, const std::string &content_type, ContentProvider provider, - ContentProviderResourceReleaser resource_releaser = nullptr); - - void set_content_provider( - const std::string &content_type, ContentProviderWithoutLength provider, - ContentProviderResourceReleaser resource_releaser = nullptr); - - void set_chunked_content_provider( - const std::string &content_type, ContentProviderWithoutLength provider, - ContentProviderResourceReleaser resource_releaser = nullptr); - - Response() = default; - Response(const Response &) = default; - Response &operator=(const Response &) = default; - Response(Response &&) = default; - Response &operator=(Response &&) = default; - ~Response() { - if (content_provider_resource_releaser_) { - content_provider_resource_releaser_(content_provider_success_); - } - } - - // private members... - size_t content_length_ = 0; - ContentProvider content_provider_; - ContentProviderResourceReleaser content_provider_resource_releaser_; - bool is_chunked_content_provider_ = false; - bool content_provider_success_ = false; -}; - -class Stream { -public: - virtual ~Stream() = default; - - virtual bool is_readable() const = 0; - virtual bool is_writable() const = 0; - - virtual ssize_t read(char *ptr, size_t size) = 0; - virtual ssize_t write(const char *ptr, size_t size) = 0; - virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; - virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0; - virtual socket_t socket() const = 0; - - template - ssize_t write_format(const char *fmt, const Args &...args); - ssize_t write(const char *ptr); - ssize_t write(const std::string &s); -}; - -class TaskQueue { -public: - TaskQueue() = default; - virtual ~TaskQueue() = default; - - virtual bool enqueue(std::function fn) = 0; - virtual void shutdown() = 0; - - virtual void on_idle() {} -}; - -class ThreadPool : public TaskQueue { -public: - explicit ThreadPool(size_t n, size_t mqr = 0) - : shutdown_(false), max_queued_requests_(mqr) { - while (n) { - threads_.emplace_back(worker(*this)); - n--; - } - } - - ThreadPool(const ThreadPool &) = delete; - ~ThreadPool() override = default; - - bool enqueue(std::function fn) override { - { - std::unique_lock lock(mutex_); - if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) { - return false; - } - jobs_.push_back(std::move(fn)); - } - - cond_.notify_one(); - return true; - } - - void shutdown() override { - // Stop all worker threads... - { - std::unique_lock lock(mutex_); - shutdown_ = true; - } - - cond_.notify_all(); - - // Join... - for (auto &t : threads_) { - t.join(); - } - } - -private: - struct worker { - explicit worker(ThreadPool &pool) : pool_(pool) {} - - void operator()() { - for (;;) { - std::function fn; - { - std::unique_lock lock(pool_.mutex_); - - pool_.cond_.wait( - lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); - - if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } - - fn = std::move(pool_.jobs_.front()); - pool_.jobs_.pop_front(); - } - - assert(true == static_cast(fn)); - fn(); - } - } - - ThreadPool &pool_; - }; - friend struct worker; - - std::vector threads_; - std::list> jobs_; - - bool shutdown_; - size_t max_queued_requests_ = 0; - - std::condition_variable cond_; - std::mutex mutex_; -}; - -using Logger = std::function; - -using SocketOptions = std::function; - -void default_socket_options(socket_t sock); - -const char *status_message(int status); - -std::string get_bearer_token_auth(const Request &req); - -namespace detail { - -class MatcherBase { -public: - virtual ~MatcherBase() = default; - - // Match request path and populate its matches and - virtual bool match(Request &request) const = 0; -}; - -/** - * Captures parameters in request path and stores them in Request::path_params - * - * Capture name is a substring of a pattern from : to /. - * The rest of the pattern is matched agains the request path directly - * Parameters are captured starting from the next character after - * the end of the last matched static pattern fragment until the next /. - * - * Example pattern: - * "/path/fragments/:capture/more/fragments/:second_capture" - * Static fragments: - * "/path/fragments/", "more/fragments/" - * - * Given the following request path: - * "/path/fragments/:1/more/fragments/:2" - * the resulting capture will be - * {{"capture", "1"}, {"second_capture", "2"}} - */ -class PathParamsMatcher : public MatcherBase { -public: - PathParamsMatcher(const std::string &pattern); - - bool match(Request &request) const override; - -private: - static constexpr char marker = ':'; - // Treat segment separators as the end of path parameter capture - // Does not need to handle query parameters as they are parsed before path - // matching - static constexpr char separator = '/'; - - // Contains static path fragments to match against, excluding the '/' after - // path params - // Fragments are separated by path params - std::vector static_fragments_; - // Stores the names of the path parameters to be used as keys in the - // Request::path_params map - std::vector param_names_; -}; - -/** - * Performs std::regex_match on request path - * and stores the result in Request::matches - * - * Note that regex match is performed directly on the whole request. - * This means that wildcard patterns may match multiple path segments with /: - * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end". - */ -class RegexMatcher : public MatcherBase { -public: - RegexMatcher(const std::string &pattern) : regex_(pattern) {} - - bool match(Request &request) const override; - -private: - std::regex regex_; -}; - -ssize_t write_headers(Stream &strm, const Headers &headers); - -} // namespace detail - -class Server { -public: - using Handler = std::function; - - using ExceptionHandler = - std::function; - - enum class HandlerResponse { - Handled, - Unhandled, - }; - using HandlerWithResponse = - std::function; - - using HandlerWithContentReader = std::function; - - using Expect100ContinueHandler = - std::function; - - Server(); - - virtual ~Server(); - - virtual bool is_valid() const; - - Server &Get(const std::string &pattern, Handler handler); - Server &Post(const std::string &pattern, Handler handler); - Server &Post(const std::string &pattern, HandlerWithContentReader handler); - Server &Put(const std::string &pattern, Handler handler); - Server &Put(const std::string &pattern, HandlerWithContentReader handler); - Server &Patch(const std::string &pattern, Handler handler); - Server &Patch(const std::string &pattern, HandlerWithContentReader handler); - Server &Delete(const std::string &pattern, Handler handler); - Server &Delete(const std::string &pattern, HandlerWithContentReader handler); - Server &Options(const std::string &pattern, Handler handler); - - bool set_base_dir(const std::string &dir, - const std::string &mount_point = std::string()); - bool set_mount_point(const std::string &mount_point, const std::string &dir, - Headers headers = Headers()); - bool remove_mount_point(const std::string &mount_point); - Server &set_file_extension_and_mimetype_mapping(const std::string &ext, - const std::string &mime); - Server &set_default_file_mimetype(const std::string &mime); - Server &set_file_request_handler(Handler handler); - - Server &set_error_handler(HandlerWithResponse handler); - Server &set_error_handler(Handler handler); - Server &set_exception_handler(ExceptionHandler handler); - Server &set_pre_routing_handler(HandlerWithResponse handler); - Server &set_post_routing_handler(Handler handler); - - Server &set_expect_100_continue_handler(Expect100ContinueHandler handler); - Server &set_logger(Logger logger); - - Server &set_address_family(int family); - Server &set_tcp_nodelay(bool on); - Server &set_socket_options(SocketOptions socket_options); - - Server &set_default_headers(Headers headers); - Server & - set_header_writer(std::function const &writer); - - Server &set_keep_alive_max_count(size_t count); - Server &set_keep_alive_timeout(time_t sec); - - Server &set_read_timeout(time_t sec, time_t usec = 0); - template - Server &set_read_timeout(const std::chrono::duration &duration); - - Server &set_write_timeout(time_t sec, time_t usec = 0); - template - Server &set_write_timeout(const std::chrono::duration &duration); - - Server &set_idle_interval(time_t sec, time_t usec = 0); - template - Server &set_idle_interval(const std::chrono::duration &duration); - - Server &set_payload_max_length(size_t length); - - bool bind_to_port(const std::string &host, int port, int socket_flags = 0); - int bind_to_any_port(const std::string &host, int socket_flags = 0); - bool listen_after_bind(); - - bool listen(const std::string &host, int port, int socket_flags = 0); - - bool is_running() const; - void wait_until_ready() const; - void stop(); - - std::function new_task_queue; - -protected: - bool process_request(Stream &strm, bool close_connection, - bool &connection_closed, - const std::function &setup_request); - - std::atomic svr_sock_{INVALID_SOCKET}; - size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; - time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; - time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; - time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; - time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; - time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; - time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; - time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; - size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; - -private: - using Handlers = - std::vector, Handler>>; - using HandlersForContentReader = - std::vector, - HandlerWithContentReader>>; - - static std::unique_ptr - make_matcher(const std::string &pattern); - - socket_t create_server_socket(const std::string &host, int port, - int socket_flags, - SocketOptions socket_options) const; - int bind_internal(const std::string &host, int port, int socket_flags); - bool listen_internal(); - - bool routing(Request &req, Response &res, Stream &strm); - bool handle_file_request(const Request &req, Response &res, - bool head = false); - bool dispatch_request(Request &req, Response &res, - const Handlers &handlers) const; - bool dispatch_request_for_content_reader( - Request &req, Response &res, ContentReader content_reader, - const HandlersForContentReader &handlers) const; - - bool parse_request_line(const char *s, Request &req) const; - void apply_ranges(const Request &req, Response &res, - std::string &content_type, std::string &boundary) const; - bool write_response(Stream &strm, bool close_connection, const Request &req, - Response &res); - bool write_response_with_content(Stream &strm, bool close_connection, - const Request &req, Response &res); - bool write_response_core(Stream &strm, bool close_connection, - const Request &req, Response &res, - bool need_apply_ranges); - bool write_content_with_provider(Stream &strm, const Request &req, - Response &res, const std::string &boundary, - const std::string &content_type); - bool read_content(Stream &strm, Request &req, Response &res); - bool - read_content_with_content_receiver(Stream &strm, Request &req, Response &res, - ContentReceiver receiver, - MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver); - bool read_content_core(Stream &strm, Request &req, Response &res, - ContentReceiver receiver, - MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver) const; - - virtual bool process_and_close_socket(socket_t sock); - - std::atomic is_running_{false}; - std::atomic done_{false}; - - struct MountPointEntry { - std::string mount_point; - std::string base_dir; - Headers headers; - }; - std::vector base_dirs_; - std::map file_extension_and_mimetype_map_; - std::string default_file_mimetype_ = "application/octet-stream"; - Handler file_request_handler_; - - Handlers get_handlers_; - Handlers post_handlers_; - HandlersForContentReader post_handlers_for_content_reader_; - Handlers put_handlers_; - HandlersForContentReader put_handlers_for_content_reader_; - Handlers patch_handlers_; - HandlersForContentReader patch_handlers_for_content_reader_; - Handlers delete_handlers_; - HandlersForContentReader delete_handlers_for_content_reader_; - Handlers options_handlers_; - - HandlerWithResponse error_handler_; - ExceptionHandler exception_handler_; - HandlerWithResponse pre_routing_handler_; - Handler post_routing_handler_; - Expect100ContinueHandler expect_100_continue_handler_; - - Logger logger_; - - int address_family_ = AF_UNSPEC; - bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; - SocketOptions socket_options_ = default_socket_options; - - Headers default_headers_; - std::function header_writer_ = - detail::write_headers; -}; - -enum class Error { - Success = 0, - Unknown, - Connection, - BindIPAddress, - Read, - Write, - ExceedRedirectCount, - Canceled, - SSLConnection, - SSLLoadingCerts, - SSLServerVerification, - UnsupportedMultipartBoundaryChars, - Compression, - ConnectionTimeout, - ProxyConnection, - - // For internal use only - SSLPeerCouldBeClosed_, -}; - -std::string to_string(Error error); - -std::ostream &operator<<(std::ostream &os, const Error &obj); - -class Result { -public: - Result() = default; - Result(std::unique_ptr &&res, Error err, - Headers &&request_headers = Headers{}) - : res_(std::move(res)), err_(err), - request_headers_(std::move(request_headers)) {} - // Response - operator bool() const { return res_ != nullptr; } - bool operator==(std::nullptr_t) const { return res_ == nullptr; } - bool operator!=(std::nullptr_t) const { return res_ != nullptr; } - const Response &value() const { return *res_; } - Response &value() { return *res_; } - const Response &operator*() const { return *res_; } - Response &operator*() { return *res_; } - const Response *operator->() const { return res_.get(); } - Response *operator->() { return res_.get(); } - - // Error - Error error() const { return err_; } - - // Request Headers - bool has_request_header(const std::string &key) const; - std::string get_request_header_value(const std::string &key, - size_t id = 0) const; - uint64_t get_request_header_value_u64(const std::string &key, - size_t id = 0) const; - size_t get_request_header_value_count(const std::string &key) const; - -private: - std::unique_ptr res_; - Error err_ = Error::Unknown; - Headers request_headers_; -}; - -class ClientImpl { -public: - explicit ClientImpl(const std::string &host); - - explicit ClientImpl(const std::string &host, int port); - - explicit ClientImpl(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path); - - virtual ~ClientImpl(); - - virtual bool is_valid() const; - - Result Get(const std::string &path); - Result Get(const std::string &path, const Headers &headers); - Result Get(const std::string &path, Progress progress); - Result Get(const std::string &path, const Headers &headers, - Progress progress); - Result Get(const std::string &path, ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver); - Result Get(const std::string &path, ContentReceiver content_receiver, - Progress progress); - Result Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver, Progress progress); - Result Get(const std::string &path, ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const std::string &path, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress); - Result Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, ContentReceiver content_receiver, - Progress progress); - - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, Progress progress = nullptr); - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, ContentReceiver content_receiver, - Progress progress = nullptr); - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress = nullptr); - - Result Head(const std::string &path); - Result Head(const std::string &path, const Headers &headers); - - Result Post(const std::string &path); - Result Post(const std::string &path, const Headers &headers); - Result Post(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type); - Result Post(const std::string &path, const std::string &body, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Post(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type); - Result Post(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Params ¶ms); - Result Post(const std::string &path, const Headers &headers, - const Params ¶ms); - Result Post(const std::string &path, const MultipartFormDataItems &items); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, const std::string &boundary); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items); - - Result Put(const std::string &path); - Result Put(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type); - Result Put(const std::string &path, const std::string &body, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Put(const std::string &path, size_t content_length, - ContentProvider content_provider, const std::string &content_type); - Result Put(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Params ¶ms); - Result Put(const std::string &path, const Headers &headers, - const Params ¶ms); - Result Put(const std::string &path, const MultipartFormDataItems &items); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, const std::string &boundary); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items); - - Result Patch(const std::string &path); - Result Patch(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type); - Result Patch(const std::string &path, const std::string &body, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Patch(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type); - Result Patch(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - - Result Delete(const std::string &path); - Result Delete(const std::string &path, const Headers &headers); - Result Delete(const std::string &path, const char *body, - size_t content_length, const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type); - Result Delete(const std::string &path, const std::string &body, - const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - - Result Options(const std::string &path); - Result Options(const std::string &path, const Headers &headers); - - bool send(Request &req, Response &res, Error &error); - Result send(const Request &req); - - void stop(); - - std::string host() const; - int port() const; - - size_t is_socket_open() const; - socket_t socket() const; - - void set_hostname_addr_map(std::map addr_map); - - void set_default_headers(Headers headers); - - void - set_header_writer(std::function const &writer); - - void set_address_family(int family); - void set_tcp_nodelay(bool on); - void set_socket_options(SocketOptions socket_options); - - void set_connection_timeout(time_t sec, time_t usec = 0); - template - void - set_connection_timeout(const std::chrono::duration &duration); - - void set_read_timeout(time_t sec, time_t usec = 0); - template - void set_read_timeout(const std::chrono::duration &duration); - - void set_write_timeout(time_t sec, time_t usec = 0); - template - void set_write_timeout(const std::chrono::duration &duration); - - void set_basic_auth(const std::string &username, const std::string &password); - void set_bearer_token_auth(const std::string &token); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_digest_auth(const std::string &username, - const std::string &password); -#endif - - void set_keep_alive(bool on); - void set_follow_location(bool on); - - void set_url_encode(bool on); - - void set_compress(bool on); - - void set_decompress(bool on); - - void set_interface(const std::string &intf); - - void set_proxy(const std::string &host, int port); - void set_proxy_basic_auth(const std::string &username, - const std::string &password); - void set_proxy_bearer_token_auth(const std::string &token); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_proxy_digest_auth(const std::string &username, - const std::string &password); -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_ca_cert_path(const std::string &ca_cert_file_path, - const std::string &ca_cert_dir_path = std::string()); - void set_ca_cert_store(X509_STORE *ca_cert_store); - X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const; -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void enable_server_certificate_verification(bool enabled); -#endif - - void set_logger(Logger logger); - -protected: - struct Socket { - socket_t sock = INVALID_SOCKET; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSL *ssl = nullptr; -#endif - - bool is_open() const { return sock != INVALID_SOCKET; } - }; - - virtual bool create_and_connect_socket(Socket &socket, Error &error); - - // All of: - // shutdown_ssl - // shutdown_socket - // close_socket - // should ONLY be called when socket_mutex_ is locked. - // Also, shutdown_ssl and close_socket should also NOT be called concurrently - // with a DIFFERENT thread sending requests using that socket. - virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully); - void shutdown_socket(Socket &socket) const; - void close_socket(Socket &socket); - - bool process_request(Stream &strm, Request &req, Response &res, - bool close_connection, Error &error); - - bool write_content_with_provider(Stream &strm, const Request &req, - Error &error) const; - - void copy_settings(const ClientImpl &rhs); - - // Socket endpoint information - const std::string host_; - const int port_; - const std::string host_and_port_; - - // Current open socket - Socket socket_; - mutable std::mutex socket_mutex_; - std::recursive_mutex request_mutex_; - - // These are all protected under socket_mutex - size_t socket_requests_in_flight_ = 0; - std::thread::id socket_requests_are_from_thread_ = std::thread::id(); - bool socket_should_be_closed_when_request_is_done_ = false; - - // Hostname-IP map - std::map addr_map_; - - // Default headers - Headers default_headers_; - - // Header writer - std::function header_writer_ = - detail::write_headers; - - // Settings - std::string client_cert_path_; - std::string client_key_path_; - - time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; - time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; - time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; - time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; - time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; - time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; - - std::string basic_auth_username_; - std::string basic_auth_password_; - std::string bearer_token_auth_token_; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - std::string digest_auth_username_; - std::string digest_auth_password_; -#endif - - bool keep_alive_ = false; - bool follow_location_ = false; - - bool url_encode_ = true; - - int address_family_ = AF_UNSPEC; - bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; - SocketOptions socket_options_ = nullptr; - - bool compress_ = false; - bool decompress_ = true; - - std::string interface_; - - std::string proxy_host_; - int proxy_port_ = -1; - - std::string proxy_basic_auth_username_; - std::string proxy_basic_auth_password_; - std::string proxy_bearer_token_auth_token_; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - std::string proxy_digest_auth_username_; - std::string proxy_digest_auth_password_; -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - std::string ca_cert_file_path_; - std::string ca_cert_dir_path_; - - X509_STORE *ca_cert_store_ = nullptr; -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - bool server_certificate_verification_ = true; -#endif - - Logger logger_; - -private: - bool send_(Request &req, Response &res, Error &error); - Result send_(Request &&req); - - socket_t create_client_socket(Error &error) const; - bool read_response_line(Stream &strm, const Request &req, - Response &res) const; - bool write_request(Stream &strm, Request &req, bool close_connection, - Error &error); - bool redirect(Request &req, Response &res, Error &error); - bool handle_request(Stream &strm, Request &req, Response &res, - bool close_connection, Error &error); - std::unique_ptr send_with_content_provider( - Request &req, const char *body, size_t content_length, - ContentProvider content_provider, - ContentProviderWithoutLength content_provider_without_length, - const std::string &content_type, Error &error); - Result send_with_content_provider( - const std::string &method, const std::string &path, - const Headers &headers, const char *body, size_t content_length, - ContentProvider content_provider, - ContentProviderWithoutLength content_provider_without_length, - const std::string &content_type); - ContentProviderWithoutLength get_multipart_content_provider( - const std::string &boundary, const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items) const; - - std::string adjust_host_string(const std::string &host) const; - - virtual bool process_socket(const Socket &socket, - std::function callback); - virtual bool is_ssl() const; -}; - -class Client { -public: - // Universal interface - explicit Client(const std::string &scheme_host_port); - - explicit Client(const std::string &scheme_host_port, - const std::string &client_cert_path, - const std::string &client_key_path); - - // HTTP only interface - explicit Client(const std::string &host, int port); - - explicit Client(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path); - - Client(Client &&) = default; - - ~Client(); - - bool is_valid() const; - - Result Get(const std::string &path); - Result Get(const std::string &path, const Headers &headers); - Result Get(const std::string &path, Progress progress); - Result Get(const std::string &path, const Headers &headers, - Progress progress); - Result Get(const std::string &path, ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver); - Result Get(const std::string &path, ContentReceiver content_receiver, - Progress progress); - Result Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver, Progress progress); - Result Get(const std::string &path, ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, ContentReceiver content_receiver, - Progress progress); - Result Get(const std::string &path, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress); - - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, Progress progress = nullptr); - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, ContentReceiver content_receiver, - Progress progress = nullptr); - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress = nullptr); - - Result Head(const std::string &path); - Result Head(const std::string &path, const Headers &headers); - - Result Post(const std::string &path); - Result Post(const std::string &path, const Headers &headers); - Result Post(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type); - Result Post(const std::string &path, const std::string &body, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Post(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type); - Result Post(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Params ¶ms); - Result Post(const std::string &path, const Headers &headers, - const Params ¶ms); - Result Post(const std::string &path, const MultipartFormDataItems &items); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, const std::string &boundary); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items); - - Result Put(const std::string &path); - Result Put(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type); - Result Put(const std::string &path, const std::string &body, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Put(const std::string &path, size_t content_length, - ContentProvider content_provider, const std::string &content_type); - Result Put(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Params ¶ms); - Result Put(const std::string &path, const Headers &headers, - const Params ¶ms); - Result Put(const std::string &path, const MultipartFormDataItems &items); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, const std::string &boundary); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items); - - Result Patch(const std::string &path); - Result Patch(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type); - Result Patch(const std::string &path, const std::string &body, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Patch(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type); - Result Patch(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - - Result Delete(const std::string &path); - Result Delete(const std::string &path, const Headers &headers); - Result Delete(const std::string &path, const char *body, - size_t content_length, const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type); - Result Delete(const std::string &path, const std::string &body, - const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - - Result Options(const std::string &path); - Result Options(const std::string &path, const Headers &headers); - - bool send(Request &req, Response &res, Error &error); - Result send(const Request &req); - - void stop(); - - std::string host() const; - int port() const; - - size_t is_socket_open() const; - socket_t socket() const; - - void set_hostname_addr_map(std::map addr_map); - - void set_default_headers(Headers headers); - - void - set_header_writer(std::function const &writer); - - void set_address_family(int family); - void set_tcp_nodelay(bool on); - void set_socket_options(SocketOptions socket_options); - - void set_connection_timeout(time_t sec, time_t usec = 0); - template - void - set_connection_timeout(const std::chrono::duration &duration); - - void set_read_timeout(time_t sec, time_t usec = 0); - template - void set_read_timeout(const std::chrono::duration &duration); - - void set_write_timeout(time_t sec, time_t usec = 0); - template - void set_write_timeout(const std::chrono::duration &duration); - - void set_basic_auth(const std::string &username, const std::string &password); - void set_bearer_token_auth(const std::string &token); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_digest_auth(const std::string &username, - const std::string &password); -#endif - - void set_keep_alive(bool on); - void set_follow_location(bool on); - - void set_url_encode(bool on); - - void set_compress(bool on); - - void set_decompress(bool on); - - void set_interface(const std::string &intf); - - void set_proxy(const std::string &host, int port); - void set_proxy_basic_auth(const std::string &username, - const std::string &password); - void set_proxy_bearer_token_auth(const std::string &token); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_proxy_digest_auth(const std::string &username, - const std::string &password); -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void enable_server_certificate_verification(bool enabled); -#endif - - void set_logger(Logger logger); - - // SSL -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_ca_cert_path(const std::string &ca_cert_file_path, - const std::string &ca_cert_dir_path = std::string()); - - void set_ca_cert_store(X509_STORE *ca_cert_store); - void load_ca_cert_store(const char *ca_cert, std::size_t size); - - long get_openssl_verify_result() const; - - SSL_CTX *ssl_context() const; -#endif - -private: - std::unique_ptr cli_; - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - bool is_ssl_ = false; -#endif -}; - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -class SSLServer : public Server { -public: - SSLServer(const char *cert_path, const char *private_key_path, - const char *client_ca_cert_file_path = nullptr, - const char *client_ca_cert_dir_path = nullptr, - const char *private_key_password = nullptr); - - SSLServer(X509 *cert, EVP_PKEY *private_key, - X509_STORE *client_ca_cert_store = nullptr); - - SSLServer( - const std::function &setup_ssl_ctx_callback); - - ~SSLServer() override; - - bool is_valid() const override; - - SSL_CTX *ssl_context() const; - -private: - bool process_and_close_socket(socket_t sock) override; - - SSL_CTX *ctx_; - std::mutex ctx_mutex_; -}; - -class SSLClient : public ClientImpl { -public: - explicit SSLClient(const std::string &host); - - explicit SSLClient(const std::string &host, int port); - - explicit SSLClient(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path); - - explicit SSLClient(const std::string &host, int port, X509 *client_cert, - EVP_PKEY *client_key); - - ~SSLClient() override; - - bool is_valid() const override; - - void set_ca_cert_store(X509_STORE *ca_cert_store); - void load_ca_cert_store(const char *ca_cert, std::size_t size); - - long get_openssl_verify_result() const; - - SSL_CTX *ssl_context() const; - -private: - bool create_and_connect_socket(Socket &socket, Error &error) override; - void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override; - void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully); - - bool process_socket(const Socket &socket, - std::function callback) override; - bool is_ssl() const override; - - bool connect_with_proxy(Socket &sock, Response &res, bool &success, - Error &error); - bool initialize_ssl(Socket &socket, Error &error); - - bool load_certs(); - - bool verify_host(X509 *server_cert) const; - bool verify_host_with_subject_alt_name(X509 *server_cert) const; - bool verify_host_with_common_name(X509 *server_cert) const; - bool check_host_name(const char *pattern, size_t pattern_len) const; - - SSL_CTX *ctx_; - std::mutex ctx_mutex_; - std::once_flag initialize_cert_; - - std::vector host_components_; - - long verify_result_ = 0; - - friend class ClientImpl; -}; -#endif - -/* - * Implementation of template methods. - */ - -namespace detail { - -template -inline void duration_to_sec_and_usec(const T &duration, U callback) { - auto sec = std::chrono::duration_cast(duration).count(); - auto usec = std::chrono::duration_cast( - duration - std::chrono::seconds(sec)) - .count(); - callback(static_cast(sec), static_cast(usec)); -} - -inline uint64_t get_header_value_u64(const Headers &headers, - const std::string &key, size_t id, - uint64_t def) { - auto rng = headers.equal_range(key); - auto it = rng.first; - std::advance(it, static_cast(id)); - if (it != rng.second) { - return std::strtoull(it->second.data(), nullptr, 10); - } - return def; -} - -} // namespace detail - -inline uint64_t Request::get_header_value_u64(const std::string &key, - size_t id) const { - return detail::get_header_value_u64(headers, key, id, 0); -} - -inline uint64_t Response::get_header_value_u64(const std::string &key, - size_t id) const { - return detail::get_header_value_u64(headers, key, id, 0); -} - -template -inline ssize_t Stream::write_format(const char *fmt, const Args &...args) { - const auto bufsiz = 2048; - std::array buf{}; - - auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); - if (sn <= 0) { return sn; } - - auto n = static_cast(sn); - - if (n >= buf.size() - 1) { - std::vector glowable_buf(buf.size()); - - while (n >= glowable_buf.size() - 1) { - glowable_buf.resize(glowable_buf.size() * 2); - n = static_cast( - snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); - } - return write(&glowable_buf[0], n); - } else { - return write(buf.data(), n); - } -} - -inline void default_socket_options(socket_t sock) { - int yes = 1; -#ifdef _WIN32 - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - reinterpret_cast(&yes), sizeof(yes)); - setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, - reinterpret_cast(&yes), sizeof(yes)); -#else -#ifdef SO_REUSEPORT - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, - reinterpret_cast(&yes), sizeof(yes)); -#else - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - reinterpret_cast(&yes), sizeof(yes)); -#endif -#endif -} - -inline const char *status_message(int status) { - switch (status) { - case StatusCode::Continue_100: return "Continue"; - case StatusCode::SwitchingProtocol_101: return "Switching Protocol"; - case StatusCode::Processing_102: return "Processing"; - case StatusCode::EarlyHints_103: return "Early Hints"; - case StatusCode::OK_200: return "OK"; - case StatusCode::Created_201: return "Created"; - case StatusCode::Accepted_202: return "Accepted"; - case StatusCode::NonAuthoritativeInformation_203: - return "Non-Authoritative Information"; - case StatusCode::NoContent_204: return "No Content"; - case StatusCode::ResetContent_205: return "Reset Content"; - case StatusCode::PartialContent_206: return "Partial Content"; - case StatusCode::MultiStatus_207: return "Multi-Status"; - case StatusCode::AlreadyReported_208: return "Already Reported"; - case StatusCode::IMUsed_226: return "IM Used"; - case StatusCode::MultipleChoices_300: return "Multiple Choices"; - case StatusCode::MovedPermanently_301: return "Moved Permanently"; - case StatusCode::Found_302: return "Found"; - case StatusCode::SeeOther_303: return "See Other"; - case StatusCode::NotModified_304: return "Not Modified"; - case StatusCode::UseProxy_305: return "Use Proxy"; - case StatusCode::unused_306: return "unused"; - case StatusCode::TemporaryRedirect_307: return "Temporary Redirect"; - case StatusCode::PermanentRedirect_308: return "Permanent Redirect"; - case StatusCode::BadRequest_400: return "Bad Request"; - case StatusCode::Unauthorized_401: return "Unauthorized"; - case StatusCode::PaymentRequired_402: return "Payment Required"; - case StatusCode::Forbidden_403: return "Forbidden"; - case StatusCode::NotFound_404: return "Not Found"; - case StatusCode::MethodNotAllowed_405: return "Method Not Allowed"; - case StatusCode::NotAcceptable_406: return "Not Acceptable"; - case StatusCode::ProxyAuthenticationRequired_407: - return "Proxy Authentication Required"; - case StatusCode::RequestTimeout_408: return "Request Timeout"; - case StatusCode::Conflict_409: return "Conflict"; - case StatusCode::Gone_410: return "Gone"; - case StatusCode::LengthRequired_411: return "Length Required"; - case StatusCode::PreconditionFailed_412: return "Precondition Failed"; - case StatusCode::PayloadTooLarge_413: return "Payload Too Large"; - case StatusCode::UriTooLong_414: return "URI Too Long"; - case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type"; - case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable"; - case StatusCode::ExpectationFailed_417: return "Expectation Failed"; - case StatusCode::ImATeapot_418: return "I'm a teapot"; - case StatusCode::MisdirectedRequest_421: return "Misdirected Request"; - case StatusCode::UnprocessableContent_422: return "Unprocessable Content"; - case StatusCode::Locked_423: return "Locked"; - case StatusCode::FailedDependency_424: return "Failed Dependency"; - case StatusCode::TooEarly_425: return "Too Early"; - case StatusCode::UpgradeRequired_426: return "Upgrade Required"; - case StatusCode::PreconditionRequired_428: return "Precondition Required"; - case StatusCode::TooManyRequests_429: return "Too Many Requests"; - case StatusCode::RequestHeaderFieldsTooLarge_431: - return "Request Header Fields Too Large"; - case StatusCode::UnavailableForLegalReasons_451: - return "Unavailable For Legal Reasons"; - case StatusCode::NotImplemented_501: return "Not Implemented"; - case StatusCode::BadGateway_502: return "Bad Gateway"; - case StatusCode::ServiceUnavailable_503: return "Service Unavailable"; - case StatusCode::GatewayTimeout_504: return "Gateway Timeout"; - case StatusCode::HttpVersionNotSupported_505: - return "HTTP Version Not Supported"; - case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates"; - case StatusCode::InsufficientStorage_507: return "Insufficient Storage"; - case StatusCode::LoopDetected_508: return "Loop Detected"; - case StatusCode::NotExtended_510: return "Not Extended"; - case StatusCode::NetworkAuthenticationRequired_511: - return "Network Authentication Required"; - - default: - case StatusCode::InternalServerError_500: return "Internal Server Error"; - } -} - -inline std::string get_bearer_token_auth(const Request &req) { - if (req.has_header("Authorization")) { - static std::string BearerHeaderPrefix = "Bearer "; - return req.get_header_value("Authorization") - .substr(BearerHeaderPrefix.length()); - } - return ""; -} - -template -inline Server & -Server::set_read_timeout(const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); - return *this; -} - -template -inline Server & -Server::set_write_timeout(const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); - return *this; -} - -template -inline Server & -Server::set_idle_interval(const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); }); - return *this; -} - -inline std::string to_string(const Error error) { - switch (error) { - case Error::Success: return "Success (no error)"; - case Error::Connection: return "Could not establish connection"; - case Error::BindIPAddress: return "Failed to bind IP address"; - case Error::Read: return "Failed to read connection"; - case Error::Write: return "Failed to write connection"; - case Error::ExceedRedirectCount: return "Maximum redirect count exceeded"; - case Error::Canceled: return "Connection handling canceled"; - case Error::SSLConnection: return "SSL connection failed"; - case Error::SSLLoadingCerts: return "SSL certificate loading failed"; - case Error::SSLServerVerification: return "SSL server verification failed"; - case Error::UnsupportedMultipartBoundaryChars: - return "Unsupported HTTP multipart boundary characters"; - case Error::Compression: return "Compression failed"; - case Error::ConnectionTimeout: return "Connection timed out"; - case Error::ProxyConnection: return "Proxy connection failed"; - case Error::Unknown: return "Unknown"; - default: break; - } - - return "Invalid"; -} - -inline std::ostream &operator<<(std::ostream &os, const Error &obj) { - os << to_string(obj); - os << " (" << static_cast::type>(obj) << ')'; - return os; -} - -inline uint64_t Result::get_request_header_value_u64(const std::string &key, - size_t id) const { - return detail::get_header_value_u64(request_headers_, key, id, 0); -} - -template -inline void ClientImpl::set_connection_timeout( - const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { - set_connection_timeout(sec, usec); - }); -} - -template -inline void ClientImpl::set_read_timeout( - const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); -} - -template -inline void ClientImpl::set_write_timeout( - const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); -} - -template -inline void Client::set_connection_timeout( - const std::chrono::duration &duration) { - cli_->set_connection_timeout(duration); -} - -template -inline void -Client::set_read_timeout(const std::chrono::duration &duration) { - cli_->set_read_timeout(duration); -} - -template -inline void -Client::set_write_timeout(const std::chrono::duration &duration) { - cli_->set_write_timeout(duration); -} - -/* - * Forward declarations and types that will be part of the .h file if split into - * .h + .cc. - */ - -std::string hosted_at(const std::string &hostname); - -void hosted_at(const std::string &hostname, std::vector &addrs); - -std::string append_query_params(const std::string &path, const Params ¶ms); - -std::pair make_range_header(const Ranges &ranges); - -std::pair -make_basic_authentication_header(const std::string &username, - const std::string &password, - bool is_proxy = false); - -namespace detail { - -std::string encode_query_param(const std::string &value); - -std::string decode_url(const std::string &s, bool convert_plus_to_space); - -void read_file(const std::string &path, std::string &out); - -std::string trim_copy(const std::string &s); - -void split(const char *b, const char *e, char d, - std::function fn); - -void split(const char *b, const char *e, char d, size_t m, - std::function fn); - -bool process_client_socket(socket_t sock, time_t read_timeout_sec, - time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, - std::function callback); - -socket_t create_client_socket( - const std::string &host, const std::string &ip, int port, - int address_family, bool tcp_nodelay, SocketOptions socket_options, - time_t connection_timeout_sec, time_t connection_timeout_usec, - time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, const std::string &intf, Error &error); - -const char *get_header_value(const Headers &headers, const std::string &key, - size_t id = 0, const char *def = nullptr); - -std::string params_to_query_str(const Params ¶ms); - -void parse_query_text(const std::string &s, Params ¶ms); - -bool parse_multipart_boundary(const std::string &content_type, - std::string &boundary); - -bool parse_range_header(const std::string &s, Ranges &ranges); - -int close_socket(socket_t sock); - -ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags); - -ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags); - -enum class EncodingType { None = 0, Gzip, Brotli }; - -EncodingType encoding_type(const Request &req, const Response &res); - -class BufferStream : public Stream { -public: - BufferStream() = default; - ~BufferStream() override = default; - - bool is_readable() const override; - bool is_writable() const override; - ssize_t read(char *ptr, size_t size) override; - ssize_t write(const char *ptr, size_t size) override; - void get_remote_ip_and_port(std::string &ip, int &port) const override; - void get_local_ip_and_port(std::string &ip, int &port) const override; - socket_t socket() const override; - - const std::string &get_buffer() const; - -private: - std::string buffer; - size_t position = 0; -}; - -class compressor { -public: - virtual ~compressor() = default; - - typedef std::function Callback; - virtual bool compress(const char *data, size_t data_length, bool last, - Callback callback) = 0; -}; - -class decompressor { -public: - virtual ~decompressor() = default; - - virtual bool is_valid() const = 0; - - typedef std::function Callback; - virtual bool decompress(const char *data, size_t data_length, - Callback callback) = 0; -}; - -class nocompressor : public compressor { -public: - ~nocompressor() override = default; - - bool compress(const char *data, size_t data_length, bool /*last*/, - Callback callback) override; -}; - -#ifdef CPPHTTPLIB_ZLIB_SUPPORT -class gzip_compressor : public compressor { -public: - gzip_compressor(); - ~gzip_compressor() override; - - bool compress(const char *data, size_t data_length, bool last, - Callback callback) override; - -private: - bool is_valid_ = false; - z_stream strm_; -}; - -class gzip_decompressor : public decompressor { -public: - gzip_decompressor(); - ~gzip_decompressor() override; - - bool is_valid() const override; - - bool decompress(const char *data, size_t data_length, - Callback callback) override; - -private: - bool is_valid_ = false; - z_stream strm_; -}; -#endif - -#ifdef CPPHTTPLIB_BROTLI_SUPPORT -class brotli_compressor : public compressor { -public: - brotli_compressor(); - ~brotli_compressor(); - - bool compress(const char *data, size_t data_length, bool last, - Callback callback) override; - -private: - BrotliEncoderState *state_ = nullptr; -}; - -class brotli_decompressor : public decompressor { -public: - brotli_decompressor(); - ~brotli_decompressor(); - - bool is_valid() const override; - - bool decompress(const char *data, size_t data_length, - Callback callback) override; - -private: - BrotliDecoderResult decoder_r; - BrotliDecoderState *decoder_s = nullptr; -}; -#endif - -// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` -// to store data. The call can set memory on stack for performance. -class stream_line_reader { -public: - stream_line_reader(Stream &strm, char *fixed_buffer, - size_t fixed_buffer_size); - const char *ptr() const; - size_t size() const; - bool end_with_crlf() const; - bool getline(); - -private: - void append(char c); - - Stream &strm_; - char *fixed_buffer_; - const size_t fixed_buffer_size_; - size_t fixed_buffer_used_size_ = 0; - std::string glowable_buffer_; -}; - -class mmap { -public: - mmap(const char *path); - ~mmap(); - - bool open(const char *path); - void close(); - - bool is_open() const; - size_t size() const; - const char *data() const; - -private: -#if defined(_WIN32) - HANDLE hFile_; - HANDLE hMapping_; -#else - int fd_; -#endif - size_t size_; - void *addr_; -}; - -} // namespace detail - - -} // namespace httplib - -#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL) -#undef poll -#endif - -#endif // CPPHTTPLIB_HTTPLIB_H