From: Joel Rosdahl Date: Tue, 31 Jan 2023 19:56:21 +0000 (+0100) Subject: bump: Upgrade to cpp-httplib 0.11.4 X-Git-Tag: v4.8~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c81ca6011232f025a82ff4100ec714d88bb632e;p=thirdparty%2Fccache.git bump: Upgrade to cpp-httplib 0.11.4 --- diff --git a/src/third_party/httplib.cpp b/src/third_party/httplib.cpp index a7ebdd56b..e5b439a5d 100644 --- a/src/third_party/httplib.cpp +++ b/src/third_party/httplib.cpp @@ -548,6 +548,7 @@ public: 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; private: @@ -577,6 +578,7 @@ public: 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; private: @@ -697,10 +699,13 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port, addr.sun_family = AF_UNIX; std::copy(host.begin(), host.end(), addr.sun_path); - hints.ai_addr = reinterpret_cast(&addr); + hints.ai_addr = reinterpret_cast(&addr); hints.ai_addrlen = static_cast( sizeof(addr) - sizeof(addr.sun_path) + addrlen); + fcntl(sock, F_SETFD, FD_CLOEXEC); + if (socket_options) { socket_options(sock); } + if (!bind_or_connect(sock, hints)) { close_socket(sock); sock = INVALID_SOCKET; @@ -821,7 +826,7 @@ bool bind_ip_address(socket_t sock, const std::string &host) { return ret; } -#if !defined _WIN32 && !defined ANDROID +#if !defined _WIN32 && !defined ANDROID && !defined _AIX #define USE_IF2IP #endif @@ -942,9 +947,8 @@ socket_t create_client_socket( return sock; } -bool get_remote_ip_and_port(const struct sockaddr_storage &addr, - socklen_t addr_len, std::string &ip, - int &port) { +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); } else if (addr.ss_family == AF_INET6) { @@ -965,13 +969,40 @@ bool get_remote_ip_and_port(const struct sockaddr_storage &addr, return true; } +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), + &addr_len)) { + get_ip_and_port(addr, addr_len, ip, port); + } +} + void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); if (!getpeername(sock, reinterpret_cast(&addr), &addr_len)) { - get_remote_ip_and_port(addr, addr_len, ip, port); +#ifndef _WIN32 + if (addr.ss_family == AF_UNIX) { +#if defined(__linux__) + struct ucred ucred; + socklen_t len = sizeof(ucred); + if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) { + port = ucred.pid; + } +#elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__ + pid_t pid; + socklen_t len = sizeof(pid); + if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) { + port = pid; + } +#endif + return; + } +#endif + get_ip_and_port(addr, addr_len, ip, port); } } @@ -2179,6 +2210,63 @@ std::string make_multipart_data_boundary() { return result; } +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]; + if (!std::isalnum(c) && c != '-' && c != '_') { + valid = false; + break; + } + } + return valid; +} + +template +std::string +serialize_multipart_formdata_item_begin(const T &item, + const std::string &boundary) { + std::string body = "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + + return body; +} + +std::string serialize_multipart_formdata_item_end() { return "\r\n"; } + +std::string +serialize_multipart_formdata_finish(const std::string &boundary) { + return "--" + boundary + "--\r\n"; +} + +std::string +serialize_multipart_formdata_get_content_type(const std::string &boundary) { + return "multipart/form-data; boundary=" + boundary; +} + +std::string +serialize_multipart_formdata(const MultipartFormDataItems &items, + const std::string &boundary, bool finish = true) { + std::string body; + + for (const auto &item : items) { + body += serialize_multipart_formdata_item_begin(item, boundary); + body += item.content + serialize_multipart_formdata_item_end(); + } + + if (finish) body += serialize_multipart_formdata_finish(boundary); + + return body; +} + std::pair get_range_offset_and_length(const Request &req, size_t content_length, size_t index) { @@ -2560,8 +2648,8 @@ void hosted_at(const std::string &hostname, *reinterpret_cast(rp->ai_addr); std::string ip; int dummy = -1; - if (detail::get_remote_ip_and_port(addr, sizeof(struct sockaddr_storage), - ip, dummy)) { + if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip, + dummy)) { addrs.push_back(ip); } } @@ -2785,7 +2873,8 @@ bool SocketStream::is_readable() const { } bool SocketStream::is_writable() const { - return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && + is_socket_alive(sock_); } ssize_t SocketStream::read(char *ptr, size_t size) { @@ -2850,6 +2939,11 @@ void SocketStream::get_remote_ip_and_port(std::string &ip, return detail::get_remote_ip_and_port(sock_, ip, port); } +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_; } // Buffer stream implementation @@ -2875,6 +2969,9 @@ ssize_t BufferStream::write(const char *ptr, size_t size) { void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, int & /*port*/) const {} +void BufferStream::get_local_ip_and_port(std::string & /*ip*/, + int & /*port*/) const {} + socket_t BufferStream::socket() const { return 0; } const std::string &BufferStream::get_buffer() const { return buffer; } @@ -3322,6 +3419,7 @@ Server::write_content_with_provider(Stream &strm, const Request &req, bool Server::read_content(Stream &strm, Request &req, Response &res) { MultipartFormDataMap::iterator cur; + auto file_count = 0; if (read_content_core( strm, req, res, // Regular @@ -3332,6 +3430,9 @@ bool Server::read_content(Stream &strm, Request &req, Response &res) { }, // Multipart [&](const MultipartFormData &file) { + if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) { + return false; + } cur = req.files.emplace(file.name, file); return true; }, @@ -3518,6 +3619,8 @@ bool Server::listen_internal() { // Try to accept new connections after a short sleep. std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; + } else if (errno == EINTR || errno == EAGAIN) { + continue; } if (svr_sock_ != INVALID_SOCKET) { detail::close_socket(svr_sock_); @@ -3850,6 +3953,10 @@ Server::process_request(Stream &strm, bool close_connection, req.set_header("REMOTE_ADDR", req.remote_addr); req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); + strm.get_local_ip_and_port(req.local_addr, req.local_port); + req.set_header("LOCAL_ADDR", req.local_addr); + req.set_header("LOCAL_PORT", std::to_string(req.local_port)); + if (req.has_header("Range")) { const auto &range_header_value = req.get_header_value("Range"); if (!detail::parse_range_header(range_header_value, req.ranges)) { @@ -3889,7 +3996,16 @@ Server::process_request(Stream &strm, bool close_connection, routed = true; } else { res.status = 500; - res.set_header("EXCEPTION_WHAT", e.what()); + std::string val; + auto s = e.what(); + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case '\r': val += "\\r"; break; + case '\n': val += "\\n"; break; + default: val += s[i]; break; + } + } + res.set_header("EXCEPTION_WHAT", val); } } catch (...) { if (exception_handler_) { @@ -4643,6 +4759,49 @@ bool ClientImpl::process_request(Stream &strm, Request &req, return true; } +ContentProviderWithoutLength ClientImpl::get_multipart_content_provider( + const std::string &boundary, const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + size_t cur_item = 0, cur_start = 0; + // cur_item and cur_start are copied to within the std::function and maintain + // state between successive calls + return [&, cur_item, cur_start](size_t offset, + DataSink &sink) mutable -> bool { + if (!offset && items.size()) { + sink.os << detail::serialize_multipart_formdata(items, boundary, false); + return true; + } else if (cur_item < provider_items.size()) { + if (!cur_start) { + const auto &begin = detail::serialize_multipart_formdata_item_begin( + provider_items[cur_item], boundary); + offset += begin.size(); + cur_start = offset; + sink.os << begin; + } + + DataSink cur_sink; + bool has_data = true; + cur_sink.write = sink.write; + cur_sink.done = [&]() { has_data = false; }; + cur_sink.is_writable = sink.is_writable; + + if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) + return false; + + if (!has_data) { + sink.os << detail::serialize_multipart_formdata_item_end(); + cur_item++; + cur_start = 0; + } + return true; + } else { + sink.os << detail::serialize_multipart_formdata_finish(boundary); + sink.done(); + return true; + } + }; +} + bool ClientImpl::process_socket(const Socket &socket, std::function callback) { @@ -4788,6 +4947,11 @@ Result ClientImpl::Post(const std::string &path) { return Post(path, std::string(), std::string()); } +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, size_t content_length, const std::string &content_type) { @@ -4860,40 +5024,39 @@ Result ClientImpl::Post(const std::string &path, Result ClientImpl::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items) { - return Post(path, headers, items, detail::make_multipart_data_boundary()); + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Post(path, headers, body, content_type.c_str()); } + Result ClientImpl::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items, const std::string &boundary) { - for (size_t i = 0; i < boundary.size(); i++) { - char c = boundary[i]; - if (!std::isalnum(c) && c != '-' && c != '_') { - return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; - } - } - - std::string body; - - for (const auto &item : items) { - body += "--" + boundary + "\r\n"; - body += "Content-Disposition: form-data; name=\"" + item.name + "\""; - if (!item.filename.empty()) { - body += "; filename=\"" + item.filename + "\""; - } - body += "\r\n"; - if (!item.content_type.empty()) { - body += "Content-Type: " + item.content_type + "\r\n"; - } - body += "\r\n"; - body += item.content + "\r\n"; + if (!detail::is_multipart_boundary_chars_valid(boundary)) { + return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; } - body += "--" + boundary + "--\r\n"; - - std::string content_type = "multipart/form-data; boundary=" + boundary; + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); return Post(path, headers, body, content_type.c_str()); } +Result +ClientImpl::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + return send_with_content_provider( + "POST", path, headers, nullptr, 0, nullptr, + get_multipart_content_provider(boundary, items, provider_items), + content_type); +} + Result ClientImpl::Put(const std::string &path) { return Put(path, std::string(), std::string()); } @@ -4963,6 +5126,45 @@ Result ClientImpl::Put(const std::string &path, const Headers &headers, return Put(path, headers, query, "application/x-www-form-urlencoded"); } +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, + const MultipartFormDataItems &items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Put(path, headers, body, content_type); +} + +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)) { + return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; + } + + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Put(path, headers, body, content_type); +} + +Result +ClientImpl::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + return send_with_content_provider( + "PUT", path, headers, nullptr, 0, nullptr, + get_multipart_content_provider(boundary, items, provider_items), + content_type); +} Result ClientImpl::Patch(const std::string &path) { return Patch(path, std::string(), std::string()); } @@ -5088,9 +5290,7 @@ size_t ClientImpl::is_socket_open() const { return socket_.is_open(); } -socket_t ClientImpl::socket() const { - return socket_.sock; -} +socket_t ClientImpl::socket() const { return socket_.sock; } void ClientImpl::stop() { std::lock_guard guard(socket_mutex_); @@ -5324,55 +5524,12 @@ process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, return callback(strm); } -#if OPENSSL_VERSION_NUMBER < 0x10100000L -static std::shared_ptr> openSSL_locks_; - -class SSLThreadLocks { -public: - SSLThreadLocks() { - openSSL_locks_ = - std::make_shared>(CRYPTO_num_locks()); - CRYPTO_set_locking_callback(locking_callback); - } - - ~SSLThreadLocks() { CRYPTO_set_locking_callback(nullptr); } - -private: - static void locking_callback(int mode, int type, const char * /*file*/, - int /*line*/) { - auto &lk = (*openSSL_locks_)[static_cast(type)]; - if (mode & CRYPTO_LOCK) { - lk.lock(); - } else { - lk.unlock(); - } - } -}; - -#endif - class SSLInit { public: SSLInit() { -#if OPENSSL_VERSION_NUMBER < 0x1010001fL - SSL_load_error_strings(); - SSL_library_init(); -#else OPENSSL_init_ssl( OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); -#endif } - - ~SSLInit() { -#if OPENSSL_VERSION_NUMBER < 0x1010001fL - ERR_free_strings(); -#endif - } - -private: -#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSLThreadLocks thread_init_; -#endif }; // SSL socket stream implementation @@ -5395,8 +5552,8 @@ bool SSLSocketStream::is_readable() const { } bool SSLSocketStream::is_writable() const { - return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) > - 0; + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && + is_socket_alive(sock_); } ssize_t SSLSocketStream::read(char *ptr, size_t size) { @@ -5467,6 +5624,11 @@ void SSLSocketStream::get_remote_ip_and_port(std::string &ip, detail::get_remote_ip_and_port(sock_, ip, port); } +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_; } static SSLInit sslinit_; @@ -5776,7 +5938,7 @@ bool SSLClient::initialize_ssl(Socket &socket, Error &error) { return false; } - auto server_cert = SSL_get_peer_certificate(ssl2); + auto server_cert = SSL_get1_peer_certificate(ssl2); if (server_cert == nullptr) { error = Error::SSLServerVerification; @@ -5998,13 +6160,13 @@ Client::Client(const std::string &scheme_host_port, if (is_ssl) { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - cli_ = detail::make_unique(host, port, - client_cert_path, client_key_path); + cli_ = detail::make_unique(host, port, client_cert_path, + client_key_path); is_ssl_ = is_ssl; #endif } else { - cli_ = detail::make_unique(host, port, - client_cert_path, client_key_path); + cli_ = detail::make_unique(host, port, client_cert_path, + client_key_path); } } else { cli_ = detail::make_unique(scheme_host_port, 80, @@ -6102,6 +6264,9 @@ Result Client::Head(const std::string &path, const Headers &headers) { } Result Client::Post(const std::string &path) { return cli_->Post(path); } +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, size_t content_length, const std::string &content_type) { @@ -6164,6 +6329,12 @@ Result Client::Post(const std::string &path, const Headers &headers, const std::string &boundary) { return cli_->Post(path, headers, items, boundary); } +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, size_t content_length, @@ -6214,6 +6385,25 @@ 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, + const MultipartFormDataItems &items) { + return cli_->Put(path, items); +} +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, + const MultipartFormDataItems &items, + const std::string &boundary) { + return cli_->Put(path, headers, items, boundary); +} +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) { return cli_->Patch(path); } diff --git a/src/third_party/httplib.h b/src/third_party/httplib.h index ce146e733..f215228a3 100644 --- a/src/third_party/httplib.h +++ b/src/third_party/httplib.h @@ -8,7 +8,7 @@ #ifndef CPPHTTPLIB_HTTPLIB_H #define CPPHTTPLIB_HTTPLIB_H -#define CPPHTTPLIB_VERSION "0.11.1" +#define CPPHTTPLIB_VERSION "0.11.4" /* * Configuration @@ -70,6 +70,10 @@ #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 @@ -168,7 +172,9 @@ using socket_t = SOCKET; #else // not _WIN32 #include +#ifndef _AIX #include +#endif #include #include #include @@ -249,14 +255,10 @@ using socket_t = int; #if OPENSSL_VERSION_NUMBER < 0x1010100fL #error Sorry, OpenSSL versions prior to 1.1.1 are not supported +#elif OPENSSL_VERSION_NUMBER < 0x30000000L +#define SSL_get1_peer_certificate SSL_get_peer_certificate #endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include -inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { - return M_ASN1_STRING_data(asn1); -} -#endif #endif #ifdef CPPHTTPLIB_ZLIB_SUPPORT @@ -367,6 +369,14 @@ using ContentProviderWithoutLength = 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; @@ -411,6 +421,8 @@ struct Request { std::string remote_addr; int remote_port = -1; + std::string local_addr; + int local_port = -1; // for server std::string version; @@ -512,6 +524,7 @@ public: 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 @@ -544,8 +557,11 @@ public: ~ThreadPool() override = default; void enqueue(std::function fn) override { - std::unique_lock lock(mutex_); - jobs_.push_back(std::move(fn)); + { + std::unique_lock lock(mutex_); + jobs_.push_back(std::move(fn)); + } + cond_.notify_one(); } @@ -579,7 +595,7 @@ private: if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } - fn = pool_.jobs_.front(); + fn = std::move(pool_.jobs_.front()); pool_.jobs_.pop_front(); } @@ -897,6 +913,7 @@ public: 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, @@ -925,6 +942,9 @@ public: 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, @@ -949,6 +969,14 @@ public: 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, @@ -1187,6 +1215,9 @@ private: 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); std::string adjust_host_string(const std::string &host) const; @@ -1253,6 +1284,7 @@ public: 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, @@ -1281,6 +1313,10 @@ public: 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); @@ -1304,6 +1340,15 @@ public: 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); @@ -1519,7 +1564,7 @@ inline void duration_to_sec_and_usec(const T &duration, U callback) { auto usec = std::chrono::duration_cast( duration - std::chrono::seconds(sec)) .count(); - callback(sec, usec); + callback(static_cast(sec), static_cast(usec)); } template @@ -1765,6 +1810,7 @@ public: 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; @@ -1887,4 +1933,8 @@ private: } // namespace httplib +#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL) +#undef poll +#endif + #endif // CPPHTTPLIB_HTTPLIB_H