Update cpp-httplib to 3f44c80fd345 (0.25.0)
ee0bee390725 Fix HttpWatch tests (#2089)
f2928d71525d Switch redirect tests to httpbingo.org (#2090)
85b5cdd78d9b Move detail::read_file() to test/test.cc (#2092)
5a1ecc3958f4 Run 32-bit compiled unit tests on Ubuntu (#2095)
48084d55f229 Fix #2096
37399af9960e build(meson): copy MountTest.MultibytesPathName files
(#2098)
a9ba0a4dff62 Remove SSLInit (#2102)
2f39723d08ea Wrap poll()/WSAPoll() in a function and build compiled
library on Windows (#2107)
a8d6172250ef Avoid static std::string (#2103)
94a402882189 Update vendored gtest to 1.12.1 (#2100)
2eaa2ea64f9f Make random_string() thread-safe (#2110)
0bda3a7d1a79 Update benchmark
c765584e6b10 Add zstd support (#2088)
33acccb346ec Fix #2109
87a5ae64a416 Fix #2097
0be052608541 cmake: only validate component when the required library is
found (#2112)
7a212cfe40cc clang-format
787a34ad7f01 Release v0.20.0
65ce51aed7f1 Fix start_time shadow variable (#2114)
72b35befb242 Add AF_UNIX support on windows (#2115)
7dbf5471ce45 Fix the style error and comment
dbc4af819a2c Fix compilation error on windows (#2118)
0dbe8ba1446d Support zstd also via pkg-config (#2121)
b7e33b08f17a Add missing component comment (#2124)
3e3a8cc02f2b Made the max timeout threshold for SSL longer
65d6316d65ae Fix #2113
9e4aed482e70 Fix style error
caf7c55785ed Fix regression of #2121 (#2126)
9589519d5823 Fix #2130
7b752106ac42 Merge commit from fork
a0de42ebc41d clang-format
3af7f2c16147 Release v0.20.1
61893a00a42d Fix #2135
c216dc94d20e Code cleanup
366eda72dce5 Specify version in meson.build (#2139)
fd324e141289 Add KeepAliveTest.MaxCount
4a7aae54690c Detect if afunix.h exists (#2145)
365cbe37fac4 Fix #2101
fd8da4d8e4ec Feature/multipart headers (#2152)
3a1f379e751e Release v0.21.0
08a0452fb2fe Update README.md (#2156)
27879c4874aa Fix #2157 (#2158)
91e79e9a6345 Fix #1777 (#2160)
28dcf379e82a Merge commit from fork
b6c55c6030f5 Release v0.22.0
de5a255ac650 Fix bad request for multipart form data with boundary split
(#2159)
aabd0634aeac Fix warnings created by #2152
1729aa8c1f45 Issue 2162 (#2163)
b2bf17239363 Fix #1551
d37373a983f9 Performance investigation
a183dba9fc93 clang-format
696c02f2bcc4 Update README
e1ab5a604bef Proxy problems (#2165)
e6ff3d7ac28c Cleaner API (#2166)
ea850cbfa74e Fix #1601 (#2167)
7c303bb87128 Launch proxy server before proxy test
292f9a6c5563 Add CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
0b875e07471f Remove unnecessary parameters
d5409ab541ec Fix warnings
fd034832379e Fix warnings
9e36247665df clang-format
083fe43ad34d Remove httpwatch.com dependency
cfb56c0b787b Fix #1818
9a0571513e0f Fix Makefile
0c08c378d7d9 Simplify benchmark
ceff2c115405 Add non-blocking getaddrinfo option to Cmake (#2168)
120405beac2d clang-format
cb85e573dea2 Fix #1416 (#2169)
a636a094bf21 Fix #1656
a3f556919629 Fix #2082 (#2170)
af7337761181 Fix #1578 (#2171)
145fc8b0215f Proxy test (#2172)
af7a69bcf631 build(meson): add non_blocking_getaddrinfo option (#2174)
52163ed9823c Fix #2148 (#2173)
082acacd4581 Merge commit from fork
4ff7a1c858fa build(meson): simplify build options (#2176)
c551e972971b Add .pre-commit-config.yaml
9dbaed75efff Fix #2175 (#2177)
802743264cec Remove incorrect comment
17ba303889b8 Merge commit from fork
b5b2a1d1c862 Change uint64_t to size_t
ecfd84c1712b Release v0.23.0
53ea9e8bb475 Fix #2111 (#2179)
55b38907dcdf Resolve #1264
8b28577ec6c3 Resolve #366
7b6867bcdf72 Fix #2021 (#2180)
1f110b54d8b3 Chang #error to #warning for the 32-bit environment check
except 32-bit Windows
dd98d2a24d9d build(meson): warn/fail on 32-bit machines (#2181)
ca5fe354fb83 Release v0.23.1
890a2dd85d9f Fix #2189
8e8a23e3d248 Fix #2187
c0c36f021def Fix #2184, #2185 (#2190)
a5d4c143e524 Release v0.24.0
0b3758ec36be Fix problem with Windows version check
acf28a362df8 #2191 Check for minimum required Windows version (#2192)
b52d7d8411f9 ErrorLogger support (#870) (#2195)
cdaed14925a4 Update README
70cca55cafb4 Workaround for chocolatey issue with the latest OpenSSL
fbee136dca54 Fix #2193. Allow _WIN32
b1c1fa2dc635 Code cleanup
7012e765e131 CMake: Check pointer size at configure time (#2197)
a2bb6f6c1ea5 Update docker/main.cc
3f44c80fd345 Release v0.25.0
Bug: 438422635
Test: crashpad_util_test HTTPTransport.*
Change-Id: I37bc2b5bfa2dae5feee946eb46991a81fc324eea
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/6867730
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
GitOrigin-RevId: 64c8551549f3c7d77254e7e525ffe2b5f5c5285d
diff --git a/third_party/cpp-httplib/README.crashpad b/third_party/cpp-httplib/README.crashpad
index 9c413ce..b6e6597 100644
--- a/third_party/cpp-httplib/README.crashpad
+++ b/third_party/cpp-httplib/README.crashpad
@@ -1,8 +1,8 @@
Name: cpp-httplib
Short Name: cpp-httplib
URL: https://github.com/yhirose/cpp-httplib
-Version: 0.19.0 (+22 commits)
-Revision: 71ba7e7b1b328fe0de6cfbd3e94e5e0ddd4b4073
+Version: 0.25.0
+Revision: 3f44c80fd345e859d6f61b2dda566aa3329ce35e
License: MIT
License File: cpp-httplib/LICENSE
Security Critical: no (test only)
@@ -12,8 +12,14 @@
A C++11 single-file header-only cross-platform HTTP/HTTPS library.
Local Modifications:
- - Exclude benchmark/, docker/, example/, and test/ subdirectories, and
- build-related files.
+ - Exclude all subdirectories and build-related files.
- Patch httplib.h to use #include "third_party/zlib/zlib_crashpad.h" instead of
<zlib.h>.
- #define CPPHTTPLIB_NO_EXCEPTIONS 1.
+ - Revert upstream patches that drop support for 32-bit Windows:
+ - 52163ed9823c Fix #2148 (#2173)
+ - 9dbaed75efff Fix #2175 (#2177)
+ - 1f110b54d8b3 Chang #error to #warning for the 32-bit environment check
+ except 32-bit Windows
+ - 0b3758ec36be Fix problem with Windows version check
+ - fbee136dca54 Fix #2193. Allow _WIN32
diff --git a/third_party/cpp-httplib/cpp-httplib/README.md b/third_party/cpp-httplib/cpp-httplib/README.md
index aed19ca..ff07694 100644
--- a/third_party/cpp-httplib/cpp-httplib/README.md
+++ b/third_party/cpp-httplib/cpp-httplib/README.md
@@ -5,7 +5,7 @@
A C++11 single-file header-only cross platform HTTP/HTTPS library.
-It's extremely easy to setup. Just include the **httplib.h** file in your code!
+It's extremely easy to set up. Just include the **httplib.h** file in your code!
> [!IMPORTANT]
> This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want.
@@ -85,6 +85,49 @@
> [!NOTE]
> When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
+### SSL Error Handling
+
+When SSL operations fail, cpp-httplib provides detailed error information through two separate error fields:
+
+```c++
+#define CPPHTTPLIB_OPENSSL_SUPPORT
+#include "path/to/httplib.h"
+
+httplib::Client cli("https://example.com");
+
+auto res = cli.Get("/");
+if (!res) {
+ // Check the error type
+ auto err = res.error();
+
+ switch (err) {
+ case httplib::Error::SSLConnection:
+ std::cout << "SSL connection failed, SSL error: "
+ << res->ssl_error() << std::endl;
+ break;
+
+ case httplib::Error::SSLLoadingCerts:
+ std::cout << "SSL cert loading failed, OpenSSL error: "
+ << std::hex << res->ssl_openssl_error() << std::endl;
+ break;
+
+ case httplib::Error::SSLServerVerification:
+ std::cout << "SSL verification failed, X509 error: "
+ << res->ssl_openssl_error() << std::endl;
+ break;
+
+ case httplib::Error::SSLServerHostnameVerification:
+ std::cout << "SSL hostname verification failed, X509 error: "
+ << res->ssl_openssl_error() << std::endl;
+ break;
+
+ default:
+ std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
+ }
+ }
+}
+```
+
Server
------
@@ -187,7 +230,7 @@
svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h");
```
-The followings are built-in mappings:
+The following are built-in mappings:
| Extension | MIME Type | Extension | MIME Type |
| :--------- | :-------------------------- | :--------- | :-------------------------- |
@@ -201,7 +244,7 @@
| bmp | image/bmp | 7z | application/x-7z-compressed |
| gif | image/gif | atom | application/atom+xml |
| png | image/png | pdf | application/pdf |
-| svg | image/svg+xml | mjs, js | application/javascript |
+| svg | image/svg+xml | mjs, js | text/javascript |
| webp | image/webp | json | application/json |
| ico | image/x-icon | rss | application/rss+xml |
| tif | image/tiff | tar | application/x-tar |
@@ -226,9 +269,45 @@
### Logging
+cpp-httplib provides separate logging capabilities for access logs and error logs, similar to web servers like Nginx and Apache.
+
+#### Access Logging
+
+Access loggers capture successful HTTP requests and responses:
+
```cpp
-svr.set_logger([](const auto& req, const auto& res) {
- your_logger(req, res);
+svr.set_logger([](const httplib::Request& req, const httplib::Response& res) {
+ std::cout << req.method << " " << req.path << " -> " << res.status << std::endl;
+});
+```
+
+#### Pre-compression Logging
+
+You can also set a pre-compression logger to capture request/response data before compression is applied:
+
+```cpp
+svr.set_pre_compression_logger([](const httplib::Request& req, const httplib::Response& res) {
+ // Log before compression - res.body contains uncompressed content
+ // Content-Encoding header is not yet set
+ your_pre_compression_logger(req, res);
+});
+```
+
+The pre-compression logger is only called when compression would be applied. For responses without compression, only the access logger is called.
+
+#### Error Logging
+
+Error loggers capture failed requests and connection issues. Unlike access loggers, error loggers only receive the Error and Request information, as errors typically occur before a meaningful Response can be generated.
+
+```cpp
+svr.set_error_logger([](const httplib::Error& err, const httplib::Request* req) {
+ std::cerr << httplib::to_string(err) << " while processing request";
+ if (req) {
+ std::cerr << ", client: " << req->get_header_value("X-Forwarded-For")
+ << ", request: '" << req->method << " " << req->path << " " << req->version << "'"
+ << ", host: " << req->get_header_value("Host");
+ }
+ std::cerr << std::endl;
});
```
@@ -285,16 +364,96 @@
});
```
-### 'multipart/form-data' POST data
+### Pre request handler
```cpp
-svr.Post("/multipart", [&](const auto& req, auto& res) {
- auto size = req.files.size();
- auto ret = req.has_file("name1");
- const auto& file = req.get_file_value("name1");
- // file.filename;
- // file.content_type;
- // file.content;
+svr.set_pre_request_handler([](const auto& req, auto& res) {
+ if (req.matched_route == "/user/:user") {
+ auto user = req.path_params.at("user");
+ if (user != "john") {
+ res.status = StatusCode::Forbidden_403;
+ res.set_content("error", "text/html");
+ return Server::HandlerResponse::Handled;
+ }
+ }
+ return Server::HandlerResponse::Unhandled;
+});
+```
+
+### Form data handling
+
+#### URL-encoded form data ('application/x-www-form-urlencoded')
+
+```cpp
+svr.Post("/form", [&](const auto& req, auto& res) {
+ // URL query parameters and form-encoded data are accessible via req.params
+ std::string username = req.get_param_value("username");
+ std::string password = req.get_param_value("password");
+
+ // Handle multiple values with same name
+ auto interests = req.get_param_values("interests");
+
+ // Check existence
+ if (req.has_param("newsletter")) {
+ // Handle newsletter subscription
+ }
+});
+```
+
+#### 'multipart/form-data' POST data
+
+```cpp
+svr.Post("/multipart", [&](const Request& req, Response& res) {
+ // Access text fields (from form inputs without files)
+ std::string username = req.form.get_field("username");
+ std::string bio = req.form.get_field("bio");
+
+ // Access uploaded files
+ if (req.form.has_file("avatar")) {
+ const auto& file = req.form.get_file("avatar");
+ std::cout << "Uploaded file: " << file.filename
+ << " (" << file.content_type << ") - "
+ << file.content.size() << " bytes" << std::endl;
+
+ // Access additional headers if needed
+ for (const auto& header : file.headers) {
+ std::cout << "Header: " << header.first << " = " << header.second << std::endl;
+ }
+
+ // Save to disk
+ std::ofstream ofs(file.filename, std::ios::binary);
+ ofs << file.content;
+ }
+
+ // Handle multiple values with same name
+ auto tags = req.form.get_fields("tags"); // e.g., multiple checkboxes
+ for (const auto& tag : tags) {
+ std::cout << "Tag: " << tag << std::endl;
+ }
+
+ auto documents = req.form.get_files("documents"); // multiple file upload
+ for (const auto& doc : documents) {
+ std::cout << "Document: " << doc.filename
+ << " (" << doc.content.size() << " bytes)" << std::endl;
+ }
+
+ // Check existence before accessing
+ if (req.form.has_field("newsletter")) {
+ std::cout << "Newsletter subscription: " << req.form.get_field("newsletter") << std::endl;
+ }
+
+ // Get counts for validation
+ if (req.form.get_field_count("tags") > 5) {
+ res.status = StatusCode::BadRequest_400;
+ res.set_content("Too many tags", "text/plain");
+ return;
+ }
+
+ // Summary
+ std::cout << "Received " << req.form.fields.size() << " text fields and "
+ << req.form.files.size() << " files" << std::endl;
+
+ res.set_content("Upload successful", "text/plain");
});
```
@@ -305,16 +464,29 @@
[&](const Request &req, Response &res, const ContentReader &content_reader) {
if (req.is_multipart_form_data()) {
// NOTE: `content_reader` is blocking until every form data field is read
- MultipartFormDataItems files;
+ // This approach allows streaming processing of large files
+ std::vector<FormData> items;
content_reader(
- [&](const MultipartFormData &file) {
- files.push_back(file);
+ [&](const FormData &item) {
+ items.push_back(item);
return true;
},
[&](const char *data, size_t data_length) {
- files.back().content.append(data, data_length);
+ items.back().content.append(data, data_length);
return true;
});
+
+ // Process the received items
+ for (const auto& item : items) {
+ if (item.filename.empty()) {
+ // Text field
+ std::cout << "Field: " << item.name << " = " << item.content << std::endl;
+ } else {
+ // File
+ std::cout << "File: " << item.name << " (" << item.filename << ") - "
+ << item.content.size() << " bytes" << std::endl;
+ }
+ }
} else {
std::string body;
content_reader([&](const char *data, size_t data_length) {
@@ -435,7 +607,7 @@
### Keep-Alive connection
```cpp
-svr.set_keep_alive_max_count(2); // Default is 5
+svr.set_keep_alive_max_count(2); // Default is 100
svr.set_keep_alive_timeout(10); // Default is 5
```
@@ -562,12 +734,59 @@
SSLConnection,
SSLLoadingCerts,
SSLServerVerification,
+ SSLServerHostnameVerification,
UnsupportedMultipartBoundaryChars,
Compression,
ConnectionTimeout,
+ ProxyConnection,
};
```
+### Client Logging
+
+#### Access Logging
+
+```cpp
+cli.set_logger([](const httplib::Request& req, const httplib::Response& res) {
+ auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::steady_clock::now() - start_time).count();
+ std::cout << "✓ " << req.method << " " << req.path
+ << " -> " << res.status << " (" << res.body.size() << " bytes, "
+ << duration << "ms)" << std::endl;
+});
+```
+
+#### Error Logging
+
+```cpp
+cli.set_error_logger([](const httplib::Error& err, const httplib::Request* req) {
+ std::cerr << "✗ ";
+ if (req) {
+ std::cerr << req->method << " " << req->path << " ";
+ }
+ std::cerr << "failed: " << httplib::to_string(err);
+
+ // Add specific guidance based on error type
+ switch (err) {
+ case httplib::Error::Connection:
+ std::cerr << " (verify server is running and reachable)";
+ break;
+ case httplib::Error::SSLConnection:
+ std::cerr << " (check SSL certificate and TLS configuration)";
+ break;
+ case httplib::Error::ConnectionTimeout:
+ std::cerr << " (increase timeout or check network latency)";
+ break;
+ case httplib::Error::Read:
+ std::cerr << " (server may have closed connection prematurely)";
+ break;
+ default:
+ break;
+ }
+ std::cerr << std::endl;
+});
+```
+
### GET with HTTP headers
```c++
@@ -618,7 +837,7 @@
### POST with Multipart Form Data
```c++
-httplib::MultipartFormDataItems items = {
+httplib::UploadFormDataItems items = {
{ "text1", "text default", "", "" },
{ "text2", "aωb", "", "" },
{ "file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain" },
@@ -655,8 +874,8 @@
cli.set_read_timeout(5, 0); // 5 seconds
cli.set_write_timeout(5, 0); // 5 seconds
-// This method works the same as curl's `--max-timeout` option
-svr.set_max_timeout(5000); // 5 seconds
+// This method works the same as curl's `--max-time` option
+cli.set_max_timeout(5000); // 5 seconds
```
### Receive content with a content receiver
@@ -721,7 +940,7 @@
httplib::Client cli(url, port);
// prints: 0 / 000 bytes => 50% complete
-auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
+auto res = cli.Get("/", [](size_t len, size_t total) {
printf("%lld / %lld bytes => %d%% complete\n",
len, total,
(int)(len*100/total));
@@ -820,6 +1039,45 @@
cli.set_interface("eth0"); // Interface name, IP address or host name
```
+### Automatic Path Encoding
+
+The client automatically encodes special characters in URL paths by default:
+
+```cpp
+httplib::Client cli("https://example.com");
+
+// Automatic path encoding (default behavior)
+cli.set_path_encode(true);
+auto res = cli.Get("/path with spaces/file.txt"); // Automatically encodes spaces
+
+// Disable automatic path encoding
+cli.set_path_encode(false);
+auto res = cli.Get("/already%20encoded/path"); // Use pre-encoded paths
+```
+
+- `set_path_encode(bool on)` - Controls automatic encoding of special characters in URL paths
+ - `true` (default): Automatically encodes spaces, plus signs, newlines, and other special characters
+ - `false`: Sends paths as-is without encoding (useful for pre-encoded URLs)
+
+### Performance Note for Local Connections
+
+> [!WARNING]
+> On Windows systems with improperly configured IPv6 settings, using "localhost" as the hostname may cause significant connection delays (up to 2 seconds per request) due to DNS resolution issues. This affects both client and server operations. For better performance when connecting to local services, use "127.0.0.1" instead of "localhost".
+>
+> See: https://github.com/yhirose/cpp-httplib/issues/366#issuecomment-593004264
+
+```cpp
+// May be slower on Windows due to DNS resolution delays
+httplib::Client cli("localhost", 8080);
+httplib::Server svr;
+svr.listen("localhost", 8080);
+
+// Faster alternative for local connections
+httplib::Client cli("127.0.0.1", 8080);
+httplib::Server svr;
+svr.listen("127.0.0.1", 8080);
+```
+
Compression
-----------
@@ -830,6 +1088,7 @@
* application/javascript
* application/json
* application/xml
+ * application/protobuf
* application/xhtml+xml
### Zlib Support
@@ -841,13 +1100,18 @@
Brotli compression is available with `CPPHTTPLIB_BROTLI_SUPPORT`. Necessary libraries should be linked.
Please see https://github.com/google/brotli for more detail.
+### Zstd Support
+
+Zstd compression is available with `CPPHTTPLIB_ZSTD_SUPPORT`. Necessary libraries should be linked.
+Please see https://github.com/facebook/zstd for more detail.
+
### Default `Accept-Encoding` value
The default `Accept-Encoding` value contains all possible compression types. So, the following two examples are same.
```c++
res = cli.Get("/resource/foo");
-res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
+res = cli.Get("/resource/foo", {{"Accept-Encoding", "br, gzip, deflate, zstd"}});
```
If we don't want a response without compression, we have to set `Accept-Encoding` to an empty string. This behavior is similar to curl.
@@ -887,7 +1151,36 @@
cli.set_address_family(AF_UNIX);
```
-"my-socket.sock" can be a relative path or an absolute path. You application must have the appropriate permissions for the path. You can also use an abstract socket address on Linux. To use an abstract socket address, prepend a null byte ('\x00') to the path.
+"my-socket.sock" can be a relative path or an absolute path. Your application must have the appropriate permissions for the path. You can also use an abstract socket address on Linux. To use an abstract socket address, prepend a null byte ('\x00') to the path.
+
+This library automatically sets the Host header to "localhost" for Unix socket connections, similar to curl's behavior:
+
+
+URI Encoding/Decoding Utilities
+-------------------------------
+
+cpp-httplib provides utility functions for URI encoding and decoding:
+
+```cpp
+#include <httplib.h>
+
+std::string url = "https://example.com/search?q=hello world";
+std::string encoded = httplib::encode_uri(url);
+std::string decoded = httplib::decode_uri(encoded);
+
+std::string param = "hello world";
+std::string encoded_component = httplib::encode_uri_component(param);
+std::string decoded_component = httplib::decode_uri_component(encoded_component);
+```
+
+### Functions
+
+- `encode_uri(const std::string &value)` - Encodes a full URI, preserving reserved characters like `://`, `?`, `&`, `=`
+- `decode_uri(const std::string &value)` - Decodes a URI-encoded string
+- `encode_uri_component(const std::string &value)` - Encodes a URI component (query parameter, path segment), encoding all reserved characters
+- `decode_uri_component(const std::string &value)` - Decodes a URI component
+
+Use `encode_uri()` for full URLs and `encode_uri_component()` for individual query parameters or path segments.
Split httplib.h into .h and .cc
@@ -938,6 +1231,24 @@
NOTE
----
+### Regular Expression Stack Overflow
+
+> [!CAUTION]
+> When using complex regex patterns in route handlers, be aware that certain patterns may cause stack overflow during pattern matching. This is a known issue with `std::regex` implementations and affects the `dispatch_request()` method.
+>
+> ```cpp
+> // This pattern can cause stack overflow with large input
+> svr.Get(".*", handler);
+> ```
+>
+> Consider using simpler patterns or path parameters to avoid this issue:
+>
+> ```cpp
+> // Safer alternatives
+> svr.Get("/users/:id", handler); // Path parameters
+> svr.Get(R"(/api/v\d+/.*)", handler); // More specific patterns
+> ```
+
### g++
g++ 4.8 and below cannot build this library since `<regex>` in the versions are [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
diff --git a/third_party/cpp-httplib/cpp-httplib/httplib.h b/third_party/cpp-httplib/cpp-httplib/httplib.h
index b1a09d2..7cd665f 100644
--- a/third_party/cpp-httplib/cpp-httplib/httplib.h
+++ b/third_party/cpp-httplib/cpp-httplib/httplib.h
@@ -8,7 +8,8 @@
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
-#define CPPHTTPLIB_VERSION "0.19.0"
+#define CPPHTTPLIB_VERSION "0.25.0"
+#define CPPHTTPLIB_VERSION_NUM "0x001900"
/*
* Configuration
@@ -76,7 +77,7 @@
#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
#ifdef _WIN32
-#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
+#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
#else
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
#endif
@@ -90,6 +91,10 @@
#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
#endif
+#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
+#define CPPHTTPLIB_HEADER_MAX_COUNT 100
+#endif
+
#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
#endif
@@ -122,6 +127,10 @@
#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
#endif
+#ifndef CPPHTTPLIB_SEND_BUFSIZ
+#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
+#endif
+
#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
#endif
@@ -145,6 +154,10 @@
#define CPPHTTPLIB_LISTEN_BACKLOG 5
#endif
+#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
+#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
+#endif
+
#define CPPHTTPLIB_NO_EXCEPTIONS 1
/*
@@ -190,13 +203,21 @@
#include <winsock2.h>
#include <ws2tcpip.h>
+#if defined(__has_include)
+#if __has_include(<afunix.h>)
+// afunix.h uses types declared in winsock2.h, so has to be included after it.
+#include <afunix.h>
+#define CPPHTTPLIB_HAVE_AFUNIX_H 1
+#endif
+#endif
+
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
#endif
+using nfds_t = unsigned long;
using socket_t = SOCKET;
using socklen_t = int;
-#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
#else // not _WIN32
@@ -231,6 +252,10 @@
#endif
#endif //_WIN32
+#if defined(__APPLE__)
+#include <TargetConditionals.h>
+#endif
+
#include <algorithm>
#include <array>
#include <atomic>
@@ -242,7 +267,6 @@
#include <errno.h>
#include <exception>
#include <fcntl.h>
-#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
@@ -261,6 +285,15 @@
#include <unordered_set>
#include <utility>
+#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) || \
+ defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
+#if TARGET_OS_OSX
+#include <CFNetwork/CFHost.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO or
+ // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
+
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
#ifdef _WIN32
#include <wincrypt.h>
@@ -275,14 +308,14 @@
#ifdef _MSC_VER
#pragma comment(lib, "crypt32.lib")
#endif
-#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
-#include <TargetConditionals.h>
-#if TARGET_OS_OSX
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/Security.h>
-#endif // TARGET_OS_OSX
#endif // _WIN32
+#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
+#if TARGET_OS_OSX
+#include <Security/Security.h>
+#endif
+#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
+
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
@@ -304,7 +337,7 @@
#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
#endif
-#endif
+#endif // CPPHTTPLIB_OPENSSL_SUPPORT
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
#include "third_party/zlib/zlib_crashpad.h"
@@ -315,6 +348,10 @@
#include <brotli/encode.h>
#endif
+#ifdef CPPHTTPLIB_ZSTD_SUPPORT
+#include <zstd.h>
+#endif
+
/*
* Declaration
*/
@@ -398,6 +435,10 @@
}
};
+template <typename T>
+using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
+ detail::case_ignore::equal_to>;
+
} // namespace case_ignore
// This is based on
@@ -521,19 +562,53 @@
using Params = std::multimap<std::string, std::string>;
using Match = std::smatch;
-using Progress = std::function<bool(uint64_t current, uint64_t total)>;
+using DownloadProgress = std::function<bool(size_t current, size_t total)>;
+using UploadProgress = std::function<bool(size_t current, size_t total)>;
struct Response;
using ResponseHandler = std::function<bool(const Response &response)>;
+struct FormData {
+ std::string name;
+ std::string content;
+ std::string filename;
+ std::string content_type;
+ Headers headers;
+};
+
+struct FormField {
+ std::string name;
+ std::string content;
+ Headers headers;
+};
+using FormFields = std::multimap<std::string, FormField>;
+
+using FormFiles = std::multimap<std::string, FormData>;
+
struct MultipartFormData {
+ FormFields fields; // Text fields from multipart
+ FormFiles files; // Files from multipart
+
+ // Text field access
+ std::string get_field(const std::string &key, size_t id = 0) const;
+ std::vector<std::string> get_fields(const std::string &key) const;
+ bool has_field(const std::string &key) const;
+ size_t get_field_count(const std::string &key) const;
+
+ // File access
+ FormData get_file(const std::string &key, size_t id = 0) const;
+ std::vector<FormData> get_files(const std::string &key) const;
+ bool has_file(const std::string &key) const;
+ size_t get_file_count(const std::string &key) const;
+};
+
+struct UploadFormData {
std::string name;
std::string content;
std::string filename;
std::string content_type;
};
-using MultipartFormDataItems = std::vector<MultipartFormData>;
-using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
+using UploadFormDataItems = std::vector<UploadFormData>;
class DataSink {
public:
@@ -576,37 +651,34 @@
using ContentProviderResourceReleaser = std::function<void(bool success)>;
-struct MultipartFormDataProvider {
+struct FormDataProvider {
std::string name;
ContentProviderWithoutLength provider;
std::string filename;
std::string content_type;
};
-using MultipartFormDataProviderItems = std::vector<MultipartFormDataProvider>;
+using FormDataProviderItems = std::vector<FormDataProvider>;
-using ContentReceiverWithProgress =
- std::function<bool(const char *data, size_t data_length, uint64_t offset,
- uint64_t total_length)>;
+using ContentReceiverWithProgress = std::function<bool(
+ const char *data, size_t data_length, size_t offset, size_t total_length)>;
using ContentReceiver =
std::function<bool(const char *data, size_t data_length)>;
-using MultipartContentHeader =
- std::function<bool(const MultipartFormData &file)>;
+using FormDataHeader = std::function<bool(const FormData &file)>;
class ContentReader {
public:
using Reader = std::function<bool(ContentReceiver receiver)>;
- using MultipartReader = std::function<bool(MultipartContentHeader header,
- ContentReceiver receiver)>;
+ using FormDataReader =
+ std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
- ContentReader(Reader reader, MultipartReader multipart_reader)
+ ContentReader(Reader reader, FormDataReader multipart_reader)
: reader_(std::move(reader)),
- multipart_reader_(std::move(multipart_reader)) {}
+ formdata_reader_(std::move(multipart_reader)) {}
- bool operator()(MultipartContentHeader header,
- ContentReceiver receiver) const {
- return multipart_reader_(std::move(header), std::move(receiver));
+ bool operator()(FormDataHeader header, ContentReceiver receiver) const {
+ return formdata_reader_(std::move(header), std::move(receiver));
}
bool operator()(ContentReceiver receiver) const {
@@ -614,7 +686,7 @@
}
Reader reader_;
- MultipartReader multipart_reader_;
+ FormDataReader formdata_reader_;
};
using Range = std::pair<ssize_t, ssize_t>;
@@ -623,8 +695,10 @@
struct Request {
std::string method;
std::string path;
+ std::string matched_route;
Params params;
Headers headers;
+ Headers trailers;
std::string body;
std::string remote_addr;
@@ -635,16 +709,18 @@
// for server
std::string version;
std::string target;
- MultipartFormDataMap files;
+ MultipartFormData form;
Ranges ranges;
Match matches;
std::unordered_map<std::string, std::string> path_params;
std::function<bool()> is_connection_closed = []() { return true; };
// for client
+ std::vector<std::string> accept_content_types;
ResponseHandler response_handler;
ContentReceiverWithProgress content_receiver;
- Progress progress;
+ DownloadProgress download_progress;
+ UploadProgress upload_progress;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
const SSL *ssl = nullptr;
#endif
@@ -652,21 +728,21 @@
bool has_header(const std::string &key) const;
std::string get_header_value(const std::string &key, const char *def = "",
size_t id = 0) const;
- uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0,
- size_t id = 0) const;
+ size_t get_header_value_u64(const std::string &key, size_t def = 0,
+ 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_trailer(const std::string &key) const;
+ std::string get_trailer_value(const std::string &key, size_t id = 0) const;
+ size_t get_trailer_value_count(const std::string &key) const;
+
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<MultipartFormData> get_file_values(const std::string &key) const;
-
// private members...
size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
size_t content_length_ = 0;
@@ -682,17 +758,22 @@
int status = -1;
std::string reason;
Headers headers;
+ Headers trailers;
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, const char *def = "",
size_t id = 0) const;
- uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0,
- size_t id = 0) const;
+ size_t get_header_value_u64(const std::string &key, size_t def = 0,
+ 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_trailer(const std::string &key) const;
+ std::string get_trailer_value(const std::string &key, size_t id = 0) const;
+ size_t get_trailer_value_count(const std::string &key) const;
+
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);
@@ -852,6 +933,10 @@
using Logger = std::function<void(const Request &, const Response &)>;
+// Forward declaration for Error type
+enum class Error;
+using ErrorLogger = std::function<void(const Error &, const Request *)>;
+
using SocketOptions = std::function<void(socket_t sock)>;
namespace detail {
@@ -874,10 +959,16 @@
class MatcherBase {
public:
+ MatcherBase(std::string pattern) : pattern_(pattern) {}
virtual ~MatcherBase() = default;
+ const std::string &pattern() const { return pattern_; }
+
// Match request path and populate its matches and
virtual bool match(Request &request) const = 0;
+
+private:
+ std::string pattern_;
};
/**
@@ -929,7 +1020,8 @@
*/
class RegexMatcher final : public MatcherBase {
public:
- RegexMatcher(const std::string &pattern) : regex_(pattern) {}
+ RegexMatcher(const std::string &pattern)
+ : MatcherBase(pattern), regex_(pattern) {}
bool match(Request &request) const override;
@@ -996,11 +1088,16 @@
}
Server &set_exception_handler(ExceptionHandler handler);
+
Server &set_pre_routing_handler(HandlerWithResponse handler);
Server &set_post_routing_handler(Handler handler);
+ Server &set_pre_request_handler(HandlerWithResponse handler);
+
Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
Server &set_logger(Logger logger);
+ Server &set_pre_compression_logger(Logger logger);
+ Server &set_error_logger(ErrorLogger error_logger);
Server &set_address_family(int family);
Server &set_tcp_nodelay(bool on);
@@ -1079,8 +1176,7 @@
bool listen_internal();
bool routing(Request &req, Response &res, Stream &strm);
- bool handle_file_request(const Request &req, Response &res,
- bool head = false);
+ bool handle_file_request(const Request &req, Response &res);
bool dispatch_request(Request &req, Response &res,
const Handlers &handlers) const;
bool dispatch_request_for_content_reader(
@@ -1101,18 +1197,23 @@
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_with_content_receiver(Stream &strm, Request &req,
+ Response &res,
+ ContentReceiver receiver,
+ FormDataHeader multipart_header,
+ ContentReceiver multipart_receiver);
bool read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
- MultipartContentHeader multipart_header,
+ FormDataHeader multipart_header,
ContentReceiver multipart_receiver) const;
virtual bool process_and_close_socket(socket_t sock);
+ void output_log(const Request &req, const Response &res) const;
+ void output_pre_compression_log(const Request &req,
+ const Response &res) const;
+ void output_error_log(const Error &err, const Request *req) const;
+
std::atomic<bool> is_running_{false};
std::atomic<bool> is_decommissioned{false};
@@ -1141,9 +1242,13 @@
ExceptionHandler exception_handler_;
HandlerWithResponse pre_routing_handler_;
Handler post_routing_handler_;
+ HandlerWithResponse pre_request_handler_;
Expect100ContinueHandler expect_100_continue_handler_;
+ mutable std::mutex logger_mutex_;
Logger logger_;
+ Logger pre_compression_logger_;
+ ErrorLogger error_logger_;
int address_family_ = AF_UNSPEC;
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
@@ -1172,6 +1277,22 @@
Compression,
ConnectionTimeout,
ProxyConnection,
+ ResourceExhaustion,
+ TooManyFormDataFiles,
+ ExceedMaxPayloadSize,
+ ExceedUriMaxLength,
+ ExceedMaxSocketDescriptorCount,
+ InvalidRequestLine,
+ InvalidHTTPMethod,
+ InvalidHTTPVersion,
+ InvalidHeaders,
+ MultipartParsing,
+ OpenFile,
+ Listen,
+ GetSockName,
+ UnsupportedAddressFamily,
+ HTTPParsing,
+ InvalidRangeHeader,
// For internal use only
SSLPeerCouldBeClosed_,
@@ -1188,6 +1309,17 @@
Headers &&request_headers = Headers{})
: res_(std::move(res)), err_(err),
request_headers_(std::move(request_headers)) {}
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
+ int ssl_error)
+ : res_(std::move(res)), err_(err),
+ request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
+ Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
+ int ssl_error, unsigned long ssl_openssl_error)
+ : res_(std::move(res)), err_(err),
+ request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
+ ssl_openssl_error_(ssl_openssl_error) {}
+#endif
// Response
operator bool() const { return res_ != nullptr; }
bool operator==(std::nullptr_t) const { return res_ == nullptr; }
@@ -1202,19 +1334,30 @@
// Error
Error error() const { return err_; }
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ // SSL Error
+ int ssl_error() const { return ssl_error_; }
+ // OpenSSL Error
+ unsigned long ssl_openssl_error() const { return ssl_openssl_error_; }
+#endif
+
// Request Headers
bool has_request_header(const std::string &key) const;
std::string get_request_header_value(const std::string &key,
const char *def = "",
size_t id = 0) const;
- uint64_t get_request_header_value_u64(const std::string &key,
- uint64_t def = 0, size_t id = 0) const;
+ size_t get_request_header_value_u64(const std::string &key, size_t def = 0,
+ size_t id = 0) const;
size_t get_request_header_value_count(const std::string &key) const;
private:
std::unique_ptr<Response> res_;
Error err_ = Error::Unknown;
Headers request_headers_;
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ int ssl_error_ = 0;
+ unsigned long ssl_openssl_error_ = 0;
+#endif
};
class ClientImpl {
@@ -1231,185 +1374,86 @@
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);
+ // clang-format off
+ Result Get(const std::string &path, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Params ¶ms, const Headers &headers, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Params ¶ms, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Params ¶ms, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress 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 Headers &headers, const char *body,
- size_t content_length, const std::string &content_type,
- Progress progress);
- Result Post(const std::string &path, const std::string &body,
- const std::string &content_type);
- Result Post(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress);
- Result Post(const std::string &path, const Headers &headers,
- 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,
- Progress progress);
- 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 char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
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 Headers &headers,
- const Params ¶ms, Progress progress);
- 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 Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers);
+ Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const Params ¶ms);
+ Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
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 Headers &headers, const char *body,
- size_t content_length, const std::string &content_type,
- Progress progress);
- Result Put(const std::string &path, const std::string &body,
- const std::string &content_type);
- Result Put(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress);
- Result Put(const std::string &path, const Headers &headers,
- 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,
- Progress progress);
- 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 char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
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 Headers &headers,
- const Params ¶ms, Progress progress);
- 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 Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers);
+ Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const Params ¶ms);
+ Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
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 char *body, size_t content_length,
- const std::string &content_type, Progress progress);
- 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 Headers &headers,
- const char *body, size_t content_length,
- const std::string &content_type, Progress progress);
- Result Patch(const std::string &path, const std::string &body,
- const std::string &content_type);
- Result Patch(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress);
- Result Patch(const std::string &path, const Headers &headers,
- 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,
- Progress progress);
- 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 Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Params ¶ms);
+ Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const Params ¶ms);
+ Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
- 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 char *body,
- size_t content_length, const std::string &content_type,
- Progress progress);
- 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 Headers &headers,
- const char *body, size_t content_length,
- const std::string &content_type, Progress progress);
- Result Delete(const std::string &path, const std::string &body,
- const std::string &content_type);
- Result Delete(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress);
- Result Delete(const std::string &path, const Headers &headers,
- 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,
- Progress progress);
+ Result Delete(const std::string &path, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Params ¶ms, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Headers &headers, const Params ¶ms, DownloadProgress progress = nullptr);
Result Options(const std::string &path);
Result Options(const std::string &path, const Headers &headers);
+ // clang-format on
bool send(Request &req, Response &res, Error &error);
Result send(const Request &req);
@@ -1461,7 +1505,7 @@
void set_keep_alive(bool on);
void set_follow_location(bool on);
- void set_url_encode(bool on);
+ void set_path_encode(bool on);
void set_compress(bool on);
@@ -1493,6 +1537,7 @@
#endif
void set_logger(Logger logger);
+ void set_error_logger(ErrorLogger error_logger);
protected:
struct Socket {
@@ -1525,6 +1570,9 @@
void copy_settings(const ClientImpl &rhs);
+ void output_log(const Request &req, const Response &res) const;
+ void output_error_log(const Error &err, const Request *req) const;
+
// Socket endpoint information
const std::string host_;
const int port_;
@@ -1573,7 +1621,7 @@
bool keep_alive_ = false;
bool follow_location_ = false;
- bool url_encode_ = true;
+ bool path_encode_ = true;
int address_family_ = AF_UNSPEC;
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
@@ -1609,7 +1657,14 @@
std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
#endif
+ mutable std::mutex logger_mutex_;
Logger logger_;
+ ErrorLogger error_logger_;
+
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ int last_ssl_error_ = 0;
+ unsigned long last_openssl_error_ = 0;
+#endif
private:
bool send_(Request &req, Response &res, Error &error);
@@ -1621,6 +1676,11 @@
bool write_request(Stream &strm, Request &req, bool close_connection,
Error &error);
bool redirect(Request &req, Response &res, Error &error);
+ bool create_redirect_client(const std::string &scheme,
+ const std::string &host, int port, Request &req,
+ Response &res, const std::string &path,
+ const std::string &location, Error &error);
+ template <typename ClientType> void setup_redirect_client(ClientType &client);
bool handle_request(Stream &strm, Request &req, Response &res,
bool close_connection, Error &error);
std::unique_ptr<Response> send_with_content_provider(
@@ -1633,10 +1693,10 @@
const Headers &headers, const char *body, size_t content_length,
ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
- const std::string &content_type, Progress progress);
+ const std::string &content_type, UploadProgress progress);
ContentProviderWithoutLength get_multipart_content_provider(
- const std::string &boundary, const MultipartFormDataItems &items,
- const MultipartFormDataProviderItems &provider_items) const;
+ const std::string &boundary, const UploadFormDataItems &items,
+ const FormDataProviderItems &provider_items) const;
std::string adjust_host_string(const std::string &host) const;
@@ -1670,185 +1730,86 @@
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);
+ // clang-format off
+ Result Get(const std::string &path, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Params ¶ms, const Headers &headers, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Params ¶ms, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
+ Result Get(const std::string &path, const Params ¶ms, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress 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 Headers &headers, const char *body,
- size_t content_length, const std::string &content_type,
- Progress progress);
- Result Post(const std::string &path, const std::string &body,
- const std::string &content_type);
- Result Post(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress);
- Result Post(const std::string &path, const Headers &headers,
- 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,
- Progress progress);
- 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 char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
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 Headers &headers,
- const Params ¶ms, Progress progress);
- 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 Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers);
+ Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const Params ¶ms);
+ Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
+ Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
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 Headers &headers, const char *body,
- size_t content_length, const std::string &content_type,
- Progress progress);
- Result Put(const std::string &path, const std::string &body,
- const std::string &content_type);
- Result Put(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress);
- Result Put(const std::string &path, const Headers &headers,
- 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,
- Progress progress);
- 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 char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
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 Headers &headers,
- const Params ¶ms, Progress progress);
- 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 Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers);
+ Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const Params ¶ms);
+ Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
+ Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
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 char *body, size_t content_length,
- const std::string &content_type, Progress progress);
- 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 Headers &headers,
- const char *body, size_t content_length,
- const std::string &content_type, Progress progress);
- Result Patch(const std::string &path, const std::string &body,
- const std::string &content_type);
- Result Patch(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress);
- Result Patch(const std::string &path, const Headers &headers,
- 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,
- Progress progress);
- 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 Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Params ¶ms);
+ Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers);
+ Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const Params ¶ms);
+ Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
+ Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
- 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 char *body,
- size_t content_length, const std::string &content_type,
- Progress progress);
- 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 Headers &headers,
- const char *body, size_t content_length,
- const std::string &content_type, Progress progress);
- Result Delete(const std::string &path, const std::string &body,
- const std::string &content_type);
- Result Delete(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress);
- Result Delete(const std::string &path, const Headers &headers,
- 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,
- Progress progress);
+ Result Delete(const std::string &path, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Params ¶ms, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
+ Result Delete(const std::string &path, const Headers &headers, const Params ¶ms, DownloadProgress progress = nullptr);
Result Options(const std::string &path);
Result Options(const std::string &path, const Headers &headers);
+ // clang-format on
bool send(Request &req, Response &res, Error &error);
Result send(const Request &req);
@@ -1899,6 +1860,7 @@
void set_keep_alive(bool on);
void set_follow_location(bool on);
+ void set_path_encode(bool on);
void set_url_encode(bool on);
void set_compress(bool on);
@@ -1924,6 +1886,7 @@
#endif
void set_logger(Logger logger);
+ void set_error_logger(ErrorLogger error_logger);
// SSL
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -1974,6 +1937,9 @@
SSL_CTX *ctx_;
std::mutex ctx_mutex_;
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ int last_ssl_error_ = 0;
+#endif
};
class SSLClient final : public ClientImpl {
@@ -2053,13 +2019,19 @@
callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
}
-inline bool is_numeric(const std::string &str) {
- return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
+template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
+ return N - 1;
}
-inline uint64_t get_header_value_u64(const Headers &headers,
- const std::string &key, uint64_t def,
- size_t id, bool &is_invalid_value) {
+inline bool is_numeric(const std::string &str) {
+ return !str.empty() &&
+ std::all_of(str.cbegin(), str.cend(),
+ [](unsigned char c) { return std::isdigit(c); });
+}
+
+inline size_t get_header_value_u64(const Headers &headers,
+ const std::string &key, size_t def,
+ size_t id, bool &is_invalid_value) {
is_invalid_value = false;
auto rng = headers.equal_range(key);
auto it = rng.first;
@@ -2074,22 +2046,22 @@
return def;
}
-inline uint64_t get_header_value_u64(const Headers &headers,
- const std::string &key, uint64_t def,
- size_t id) {
- bool dummy = false;
+inline size_t get_header_value_u64(const Headers &headers,
+ const std::string &key, size_t def,
+ size_t id) {
+ auto dummy = false;
return get_header_value_u64(headers, key, def, id, dummy);
}
} // namespace detail
-inline uint64_t Request::get_header_value_u64(const std::string &key,
- uint64_t def, size_t id) const {
+inline size_t Request::get_header_value_u64(const std::string &key, size_t def,
+ size_t id) const {
return detail::get_header_value_u64(headers, key, def, id);
}
-inline uint64_t Response::get_header_value_u64(const std::string &key,
- uint64_t def, size_t id) const {
+inline size_t Response::get_header_value_u64(const std::string &key, size_t def,
+ size_t id) const {
return detail::get_header_value_u64(headers, key, def, id);
}
@@ -2212,9 +2184,9 @@
inline std::string get_bearer_token_auth(const Request &req) {
if (req.has_header("Authorization")) {
- static std::string BearerHeaderPrefix = "Bearer ";
+ constexpr auto bearer_header_prefix_len = detail::str_len("Bearer ");
return req.get_header_value("Authorization")
- .substr(BearerHeaderPrefix.length());
+ .substr(bearer_header_prefix_len);
}
return "";
}
@@ -2246,6 +2218,7 @@
inline std::string to_string(const Error error) {
switch (error) {
case Error::Success: return "Success (no error)";
+ case Error::Unknown: return "Unknown";
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";
@@ -2262,7 +2235,23 @@
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";
+ case Error::ResourceExhaustion: return "Resource exhaustion";
+ case Error::TooManyFormDataFiles: return "Too many form data files";
+ case Error::ExceedMaxPayloadSize: return "Exceeded maximum payload size";
+ case Error::ExceedUriMaxLength: return "Exceeded maximum URI length";
+ case Error::ExceedMaxSocketDescriptorCount:
+ return "Exceeded maximum socket descriptor count";
+ case Error::InvalidRequestLine: return "Invalid request line";
+ case Error::InvalidHTTPMethod: return "Invalid HTTP method";
+ case Error::InvalidHTTPVersion: return "Invalid HTTP version";
+ case Error::InvalidHeaders: return "Invalid headers";
+ case Error::MultipartParsing: return "Multipart parsing failed";
+ case Error::OpenFile: return "Failed to open file";
+ case Error::Listen: return "Failed to listen on socket";
+ case Error::GetSockName: return "Failed to get socket name";
+ case Error::UnsupportedAddressFamily: return "Unsupported address family";
+ case Error::HTTPParsing: return "HTTP parsing failed";
+ case Error::InvalidRangeHeader: return "Invalid Range header";
default: break;
}
@@ -2275,9 +2264,9 @@
return os;
}
-inline uint64_t Result::get_request_header_value_u64(const std::string &key,
- uint64_t def,
- size_t id) const {
+inline size_t Result::get_request_header_value_u64(const std::string &key,
+ size_t def,
+ size_t id) const {
return detail::get_header_value_u64(request_headers_, key, def, id);
}
@@ -2329,6 +2318,10 @@
cli_->set_write_timeout(duration);
}
+inline void Client::set_max_timeout(time_t msec) {
+ cli_->set_max_timeout(msec);
+}
+
template <class Rep, class Period>
inline void
Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
@@ -2344,6 +2337,20 @@
void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
+// JavaScript-style URL encoding/decoding functions
+std::string encode_uri_component(const std::string &value);
+std::string encode_uri(const std::string &value);
+std::string decode_uri_component(const std::string &value);
+std::string decode_uri(const std::string &value);
+
+// RFC 3986 compliant URL component encoding/decoding functions
+std::string encode_path_component(const std::string &component);
+std::string decode_path_component(const std::string &component);
+std::string encode_query_component(const std::string &component,
+ bool space_as_plus = true);
+std::string decode_query_component(const std::string &component,
+ bool plus_as_space = true);
+
std::string append_query_params(const std::string &path, const Params ¶ms);
std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
@@ -2385,12 +2392,6 @@
int ret_ = -1;
};
-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 divide(
@@ -2440,13 +2441,16 @@
bool parse_range_header(const std::string &s, Ranges &ranges);
+bool parse_accept_header(const std::string &s,
+ std::vector<std::string> &content_types);
+
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 };
+enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
EncodingType encoding_type(const Request &req, const Response &res);
@@ -2559,6 +2563,34 @@
};
#endif
+#ifdef CPPHTTPLIB_ZSTD_SUPPORT
+class zstd_compressor : public compressor {
+public:
+ zstd_compressor();
+ ~zstd_compressor();
+
+ bool compress(const char *data, size_t data_length, bool last,
+ Callback callback) override;
+
+private:
+ ZSTD_CCtx *ctx_ = nullptr;
+};
+
+class zstd_decompressor : public decompressor {
+public:
+ zstd_decompressor();
+ ~zstd_decompressor();
+
+ bool is_valid() const override;
+
+ bool decompress(const char *data, size_t data_length,
+ Callback callback) override;
+
+private:
+ ZSTD_DCtx *ctx_ = 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 {
@@ -2828,28 +2860,7 @@
return ret_ >= 0 && S_ISDIR(st_.st_mode);
}
-inline std::string encode_query_param(const std::string &value) {
- std::ostringstream escaped;
- escaped.fill('0');
- escaped << std::hex;
-
- for (auto c : value) {
- if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
- c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
- c == ')') {
- escaped << c;
- } else {
- escaped << std::uppercase;
- escaped << '%' << std::setw(2)
- << static_cast<int>(static_cast<unsigned char>(c));
- escaped << std::nouppercase;
- }
- }
-
- return escaped.str();
-}
-
-inline std::string encode_url(const std::string &s) {
+inline std::string encode_path(const std::string &s) {
std::string result;
result.reserve(s.size());
@@ -2881,55 +2892,9 @@
return result;
}
-inline std::string decode_url(const std::string &s,
- bool convert_plus_to_space) {
- std::string result;
-
- for (size_t i = 0; i < s.size(); i++) {
- if (s[i] == '%' && i + 1 < s.size()) {
- if (s[i + 1] == 'u') {
- auto val = 0;
- if (from_hex_to_i(s, i + 2, 4, val)) {
- // 4 digits Unicode codes
- char buff[4];
- size_t len = to_utf8(val, buff);
- if (len > 0) { result.append(buff, len); }
- i += 5; // 'u0000'
- } else {
- result += s[i];
- }
- } else {
- auto val = 0;
- if (from_hex_to_i(s, i + 1, 2, val)) {
- // 2 digits hex codes
- result += static_cast<char>(val);
- i += 2; // '00'
- } else {
- result += s[i];
- }
- }
- } else if (convert_plus_to_space && s[i] == '+') {
- result += ' ';
- } else {
- result += s[i];
- }
- }
-
- return result;
-}
-
-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();
- fs.seekg(0);
- out.resize(static_cast<size_t>(size));
- fs.read(&out[0], static_cast<std::streamsize>(size));
-}
-
inline std::string file_extension(const std::string &path) {
std::smatch m;
- static auto re = std::regex("\\.([a-zA-Z0-9]+)$");
+ thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
if (std::regex_search(path, m, re)) { return m[1].str(); }
return std::string();
}
@@ -3042,6 +3007,11 @@
#endif
for (size_t i = 0;; i++) {
+ if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH) {
+ // Treat exceptionally long lines as an error to
+ // prevent infinite loops/memory exhaustion
+ return false;
+ }
char byte;
auto n = strm_.read(&byte, 1);
@@ -3254,15 +3224,41 @@
});
}
+inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
+#ifdef _WIN32
+ return ::WSAPoll(fds, nfds, timeout);
+#else
+ return ::poll(fds, nfds, timeout);
+#endif
+}
+
template <bool Read>
inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
+#ifdef __APPLE__
+ if (sock >= FD_SETSIZE) { return -1; }
+
+ fd_set fds, *rfds, *wfds;
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ rfds = (Read ? &fds : nullptr);
+ wfds = (Read ? nullptr : &fds);
+
+ timeval tv;
+ tv.tv_sec = static_cast<long>(sec);
+ tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
+
+ return handle_EINTR([&]() {
+ return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);
+ });
+#else
struct pollfd pfd;
pfd.fd = sock;
pfd.events = (Read ? POLLIN : POLLOUT);
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
- return handle_EINTR([&]() { return poll(&pfd, 1, timeout); });
+ return handle_EINTR([&]() { return poll_wrapper(&pfd, 1, timeout); });
+#endif
}
inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
@@ -3275,13 +3271,44 @@
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
time_t usec) {
+#ifdef __APPLE__
+ if (sock >= FD_SETSIZE) { return Error::Connection; }
+
+ fd_set fdsr, fdsw;
+ FD_ZERO(&fdsr);
+ FD_ZERO(&fdsw);
+ FD_SET(sock, &fdsr);
+ FD_SET(sock, &fdsw);
+
+ timeval tv;
+ tv.tv_sec = static_cast<long>(sec);
+ tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
+
+ auto ret = handle_EINTR([&]() {
+ return select(static_cast<int>(sock + 1), &fdsr, &fdsw, nullptr, &tv);
+ });
+
+ if (ret == 0) { return Error::ConnectionTimeout; }
+
+ if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
+ auto error = 0;
+ socklen_t len = sizeof(error);
+ auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
+ reinterpret_cast<char *>(&error), &len);
+ auto successful = res >= 0 && !error;
+ return successful ? Error::Success : Error::Connection;
+ }
+
+ return Error::Connection;
+#else
struct pollfd pfd_read;
pfd_read.fd = sock;
pfd_read.events = POLLIN | POLLOUT;
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
- auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
+ auto poll_res =
+ handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });
if (poll_res == 0) { return Error::ConnectionTimeout; }
@@ -3295,6 +3322,7 @@
}
return Error::Connection;
+#endif
}
inline bool is_socket_alive(socket_t sock) {
@@ -3334,7 +3362,7 @@
time_t write_timeout_sec_;
time_t write_timeout_usec_;
time_t max_timeout_msec_;
- const std::chrono::time_point<std::chrono::steady_clock> start_time;
+ const std::chrono::time_point<std::chrono::steady_clock> start_time_;
std::vector<char> read_buff_;
size_t read_buff_off_ = 0;
@@ -3372,7 +3400,7 @@
time_t write_timeout_sec_;
time_t write_timeout_usec_;
time_t max_timeout_msec_;
- const std::chrono::time_point<std::chrono::steady_clock> start_time;
+ const std::chrono::time_point<std::chrono::steady_clock> start_time_;
};
#endif
@@ -3482,11 +3510,328 @@
return s;
}
+inline int getaddrinfo_with_timeout(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res, time_t timeout_sec) {
+#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
+ if (timeout_sec <= 0) {
+ // No timeout specified, use standard getaddrinfo
+ return getaddrinfo(node, service, hints, res);
+ }
+
+#ifdef _WIN32
+ // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
+ OVERLAPPED overlapped = {0};
+ HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ if (!event) { return EAI_FAIL; }
+
+ overlapped.hEvent = event;
+
+ PADDRINFOEXW result_addrinfo = nullptr;
+ HANDLE cancel_handle = nullptr;
+
+ ADDRINFOEXW hints_ex = {0};
+ if (hints) {
+ hints_ex.ai_flags = hints->ai_flags;
+ hints_ex.ai_family = hints->ai_family;
+ hints_ex.ai_socktype = hints->ai_socktype;
+ hints_ex.ai_protocol = hints->ai_protocol;
+ }
+
+ auto wnode = u8string_to_wstring(node);
+ auto wservice = u8string_to_wstring(service);
+
+ auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,
+ hints ? &hints_ex : nullptr, &result_addrinfo,
+ nullptr, &overlapped, nullptr, &cancel_handle);
+
+ if (ret == WSA_IO_PENDING) {
+ auto wait_result =
+ ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
+ if (wait_result == WAIT_TIMEOUT) {
+ if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
+ ::CloseHandle(event);
+ return EAI_AGAIN;
+ }
+
+ DWORD bytes_returned;
+ if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,
+ &bytes_returned, FALSE)) {
+ ::CloseHandle(event);
+ return ::WSAGetLastError();
+ }
+ }
+
+ ::CloseHandle(event);
+
+ if (ret == NO_ERROR || ret == WSA_IO_PENDING) {
+ *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);
+ return 0;
+ }
+
+ return ret;
+#elif defined(TARGET_OS_OSX)
+ // macOS implementation using CFHost API for asynchronous DNS resolution
+ CFStringRef hostname_ref = CFStringCreateWithCString(
+ kCFAllocatorDefault, node, kCFStringEncodingUTF8);
+ if (!hostname_ref) { return EAI_MEMORY; }
+
+ CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
+ CFRelease(hostname_ref);
+ if (!host_ref) { return EAI_MEMORY; }
+
+ // Set up context for callback
+ struct CFHostContext {
+ bool completed = false;
+ bool success = false;
+ CFArrayRef addresses = nullptr;
+ std::mutex mutex;
+ std::condition_variable cv;
+ } context;
+
+ CFHostClientContext client_context;
+ memset(&client_context, 0, sizeof(client_context));
+ client_context.info = &context;
+
+ // Set callback
+ auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,
+ const CFStreamError *error, void *info) {
+ auto ctx = static_cast<CFHostContext *>(info);
+ std::lock_guard<std::mutex> lock(ctx->mutex);
+
+ if (error && error->error != 0) {
+ ctx->success = false;
+ } else {
+ Boolean hasBeenResolved;
+ ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
+ if (ctx->addresses && hasBeenResolved) {
+ CFRetain(ctx->addresses);
+ ctx->success = true;
+ } else {
+ ctx->success = false;
+ }
+ }
+ ctx->completed = true;
+ ctx->cv.notify_one();
+ };
+
+ if (!CFHostSetClient(host_ref, callback, &client_context)) {
+ CFRelease(host_ref);
+ return EAI_SYSTEM;
+ }
+
+ // Schedule on run loop
+ CFRunLoopRef run_loop = CFRunLoopGetCurrent();
+ CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
+
+ // Start resolution
+ CFStreamError stream_error;
+ if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
+ CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
+ CFRelease(host_ref);
+ return EAI_FAIL;
+ }
+
+ // Wait for completion with timeout
+ auto timeout_time =
+ std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
+ bool timed_out = false;
+
+ {
+ std::unique_lock<std::mutex> lock(context.mutex);
+
+ while (!context.completed) {
+ auto now = std::chrono::steady_clock::now();
+ if (now >= timeout_time) {
+ timed_out = true;
+ break;
+ }
+
+ // Run the runloop for a short time
+ lock.unlock();
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
+ lock.lock();
+ }
+ }
+
+ // Clean up
+ CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
+ CFHostSetClient(host_ref, nullptr, nullptr);
+
+ if (timed_out || !context.completed) {
+ CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
+ CFRelease(host_ref);
+ return EAI_AGAIN;
+ }
+
+ if (!context.success || !context.addresses) {
+ CFRelease(host_ref);
+ return EAI_NODATA;
+ }
+
+ // Convert CFArray to addrinfo
+ CFIndex count = CFArrayGetCount(context.addresses);
+ if (count == 0) {
+ CFRelease(context.addresses);
+ CFRelease(host_ref);
+ return EAI_NODATA;
+ }
+
+ struct addrinfo *result_addrinfo = nullptr;
+ struct addrinfo **current = &result_addrinfo;
+
+ for (CFIndex i = 0; i < count; i++) {
+ CFDataRef addr_data =
+ static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));
+ if (!addr_data) continue;
+
+ const struct sockaddr *sockaddr_ptr =
+ reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));
+ socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));
+
+ // Allocate addrinfo structure
+ *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));
+ if (!*current) {
+ freeaddrinfo(result_addrinfo);
+ CFRelease(context.addresses);
+ CFRelease(host_ref);
+ return EAI_MEMORY;
+ }
+
+ memset(*current, 0, sizeof(struct addrinfo));
+
+ // Set up addrinfo fields
+ (*current)->ai_family = sockaddr_ptr->sa_family;
+ (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
+ (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
+ (*current)->ai_addrlen = sockaddr_len;
+
+ // Copy sockaddr
+ (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));
+ if (!(*current)->ai_addr) {
+ freeaddrinfo(result_addrinfo);
+ CFRelease(context.addresses);
+ CFRelease(host_ref);
+ return EAI_MEMORY;
+ }
+ memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
+
+ // Set port if service is specified
+ if (service && strlen(service) > 0) {
+ int port = atoi(service);
+ if (port > 0) {
+ if (sockaddr_ptr->sa_family == AF_INET) {
+ reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)
+ ->sin_port = htons(static_cast<uint16_t>(port));
+ } else if (sockaddr_ptr->sa_family == AF_INET6) {
+ reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)
+ ->sin6_port = htons(static_cast<uint16_t>(port));
+ }
+ }
+ }
+
+ current = &((*current)->ai_next);
+ }
+
+ CFRelease(context.addresses);
+ CFRelease(host_ref);
+
+ *res = result_addrinfo;
+ return 0;
+#elif defined(_GNU_SOURCE) && defined(__GLIBC__) && \
+ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
+ // Linux implementation using getaddrinfo_a for asynchronous DNS resolution
+ struct gaicb request;
+ struct gaicb *requests[1] = {&request};
+ struct sigevent sevp;
+ struct timespec timeout;
+
+ // Initialize the request structure
+ memset(&request, 0, sizeof(request));
+ request.ar_name = node;
+ request.ar_service = service;
+ request.ar_request = hints;
+
+ // Set up timeout
+ timeout.tv_sec = timeout_sec;
+ timeout.tv_nsec = 0;
+
+ // Initialize sigevent structure (not used, but required)
+ memset(&sevp, 0, sizeof(sevp));
+ sevp.sigev_notify = SIGEV_NONE;
+
+ // Start asynchronous resolution
+ int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
+ if (start_result != 0) { return start_result; }
+
+ // Wait for completion with timeout
+ int wait_result =
+ gai_suspend((const struct gaicb *const *)requests, 1, &timeout);
+
+ if (wait_result == 0) {
+ // Completed successfully, get the result
+ int gai_result = gai_error(&request);
+ if (gai_result == 0) {
+ *res = request.ar_result;
+ return 0;
+ } else {
+ // Clean up on error
+ if (request.ar_result) { freeaddrinfo(request.ar_result); }
+ return gai_result;
+ }
+ } else if (wait_result == EAI_AGAIN) {
+ // Timeout occurred, cancel the request
+ gai_cancel(&request);
+ return EAI_AGAIN;
+ } else {
+ // Other error occurred
+ gai_cancel(&request);
+ return wait_result;
+ }
+#else
+ // Fallback implementation using thread-based timeout for other Unix systems
+ std::mutex result_mutex;
+ std::condition_variable result_cv;
+ auto completed = false;
+ auto result = EAI_SYSTEM;
+ struct addrinfo *result_addrinfo = nullptr;
+
+ std::thread resolve_thread([&]() {
+ auto thread_result = getaddrinfo(node, service, hints, &result_addrinfo);
+
+ std::lock_guard<std::mutex> lock(result_mutex);
+ result = thread_result;
+ completed = true;
+ result_cv.notify_one();
+ });
+
+ // Wait for completion or timeout
+ std::unique_lock<std::mutex> lock(result_mutex);
+ auto finished = result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
+ [&] { return completed; });
+
+ if (finished) {
+ // Operation completed within timeout
+ resolve_thread.join();
+ *res = result_addrinfo;
+ return result;
+ } else {
+ // Timeout occurred
+ resolve_thread.detach(); // Let the thread finish in background
+ return EAI_AGAIN; // Return timeout error
+ }
+#endif
+#else
+ (void)(timeout_sec); // Unused parameter for non-blocking getaddrinfo
+ return getaddrinfo(node, service, hints, res);
+#endif
+}
+
template <typename BindOrConnect>
socket_t create_socket(const std::string &host, const std::string &ip, int port,
int address_family, int socket_flags, bool tcp_nodelay,
bool ipv6_v6only, SocketOptions socket_options,
- BindOrConnect bind_or_connect) {
+ BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
// Get address info
const char *node = nullptr;
struct addrinfo hints;
@@ -3507,7 +3852,7 @@
hints.ai_flags = socket_flags;
}
-#ifndef _WIN32
+#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
if (hints.ai_family == AF_UNIX) {
const auto addrlen = host.length();
if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
@@ -3531,11 +3876,19 @@
sizeof(addr) - sizeof(addr.sun_path) + addrlen);
#ifndef SOCK_CLOEXEC
+#ifndef _WIN32
fcntl(sock, F_SETFD, FD_CLOEXEC);
#endif
+#endif
if (socket_options) { socket_options(sock); }
+#ifdef _WIN32
+ // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so
+ // remove the option.
+ detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
+#endif
+
bool dummy;
if (!bind_or_connect(sock, hints, dummy)) {
close_socket(sock);
@@ -3548,7 +3901,8 @@
auto service = std::to_string(port);
- if (getaddrinfo(node, service.c_str(), &hints, &result)) {
+ if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
+ timeout_sec)) {
#if defined __linux__ && !defined __ANDROID__
res_init();
#endif
@@ -3646,7 +4000,10 @@
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
- if (getaddrinfo(host.c_str(), "0", &hints, &result)) { return false; }
+ if (getaddrinfo_with_timeout(host.c_str(), "0", &hints, &result, 0)) {
+ return false;
+ }
+
auto se = detail::scope_exit([&] { freeaddrinfo(result); });
auto ret = false;
@@ -3751,7 +4108,8 @@
error = Error::Success;
return true;
- });
+ },
+ connection_timeout_sec); // Pass DNS timeout
if (sock != INVALID_SOCKET) {
error = Error::Success;
@@ -3807,7 +4165,7 @@
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
port = ucred.pid;
}
-#elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__
+#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
pid_t pid;
socklen_t len = sizeof(pid);
if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
@@ -3950,6 +4308,12 @@
if (ret) { return EncodingType::Gzip; }
#endif
+#ifdef CPPHTTPLIB_ZSTD_SUPPORT
+ // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
+ ret = s.find("zstd") != std::string::npos;
+ if (ret) { return EncodingType::Zstd; }
+#endif
+
return EncodingType::None;
}
@@ -4158,6 +4522,61 @@
}
#endif
+#ifdef CPPHTTPLIB_ZSTD_SUPPORT
+inline zstd_compressor::zstd_compressor() {
+ ctx_ = ZSTD_createCCtx();
+ ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
+}
+
+inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
+
+inline bool zstd_compressor::compress(const char *data, size_t data_length,
+ bool last, Callback callback) {
+ std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
+
+ ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
+ ZSTD_inBuffer input = {data, data_length, 0};
+
+ bool finished;
+ do {
+ ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
+ size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
+
+ if (ZSTD_isError(remaining)) { return false; }
+
+ if (!callback(buff.data(), output.pos)) { return false; }
+
+ finished = last ? (remaining == 0) : (input.pos == input.size);
+
+ } while (!finished);
+
+ return true;
+}
+
+inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
+
+inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
+
+inline bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }
+
+inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
+ Callback callback) {
+ std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
+ ZSTD_inBuffer input = {data, data_length, 0};
+
+ while (input.pos < input.size) {
+ ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
+ size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
+
+ if (ZSTD_isError(remaining)) { return false; }
+
+ if (!callback(buff.data(), output.pos)) { return false; }
+ }
+
+ return true;
+}
+#endif
+
inline bool has_header(const Headers &headers, const std::string &key) {
return headers.find(key) != headers.end();
}
@@ -4184,6 +4603,9 @@
p++;
}
+ auto name = std::string(beg, p);
+ if (!detail::fields::is_field_name(name)) { return false; }
+
if (p == end) { return false; }
auto key_end = p;
@@ -4207,7 +4629,7 @@
case_ignore::equal(key, "Referer")) {
fn(key, val);
} else {
- fn(key, decode_url(val, false));
+ fn(key, decode_path_component(val));
}
return true;
@@ -4221,6 +4643,8 @@
char buf[bufsiz];
stream_line_reader line_reader(strm, buf, bufsiz);
+ size_t header_count = 0;
+
for (;;) {
if (!line_reader.getline()) { return false; }
@@ -4241,6 +4665,9 @@
if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
+ // Check header count limit
+ if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
+
// Exclude line terminator
auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
@@ -4250,24 +4677,26 @@
})) {
return false;
}
+
+ header_count++;
}
return true;
}
-inline bool read_content_with_length(Stream &strm, uint64_t len,
- Progress progress,
+inline bool read_content_with_length(Stream &strm, size_t len,
+ DownloadProgress progress,
ContentReceiverWithProgress out) {
char buf[CPPHTTPLIB_RECV_BUFSIZ];
- uint64_t r = 0;
+ size_t r = 0;
while (r < len) {
auto read_len = static_cast<size_t>(len - r);
auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if (n <= 0) { return false; }
if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
- r += static_cast<uint64_t>(n);
+ r += static_cast<size_t>(n);
if (progress) {
if (!progress(r, len)) { return false; }
@@ -4277,62 +4706,90 @@
return true;
}
-inline void skip_content_with_length(Stream &strm, uint64_t len) {
+inline void skip_content_with_length(Stream &strm, size_t len) {
char buf[CPPHTTPLIB_RECV_BUFSIZ];
- uint64_t r = 0;
+ size_t r = 0;
while (r < len) {
auto read_len = static_cast<size_t>(len - r);
auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if (n <= 0) { return; }
- r += static_cast<uint64_t>(n);
+ r += static_cast<size_t>(n);
}
}
-inline bool read_content_without_length(Stream &strm,
- ContentReceiverWithProgress out) {
+enum class ReadContentResult {
+ Success, // Successfully read the content
+ PayloadTooLarge, // The content exceeds the specified payload limit
+ Error // An error occurred while reading the content
+};
+
+inline ReadContentResult
+read_content_without_length(Stream &strm, size_t payload_max_length,
+ ContentReceiverWithProgress out) {
char buf[CPPHTTPLIB_RECV_BUFSIZ];
- uint64_t r = 0;
+ size_t r = 0;
for (;;) {
auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
- if (n <= 0) { return false; }
+ if (n == 0) { return ReadContentResult::Success; }
+ if (n < 0) { return ReadContentResult::Error; }
- if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
- r += static_cast<uint64_t>(n);
+ // Check if adding this data would exceed the payload limit
+ if (r > payload_max_length ||
+ payload_max_length - r < static_cast<size_t>(n)) {
+ return ReadContentResult::PayloadTooLarge;
+ }
+
+ if (!out(buf, static_cast<size_t>(n), r, 0)) {
+ return ReadContentResult::Error;
+ }
+ r += static_cast<size_t>(n);
}
- return true;
+ return ReadContentResult::Success;
}
template <typename T>
-inline bool read_content_chunked(Stream &strm, T &x,
- ContentReceiverWithProgress out) {
+inline ReadContentResult read_content_chunked(Stream &strm, T &x,
+ size_t payload_max_length,
+ ContentReceiverWithProgress out) {
const auto bufsiz = 16;
char buf[bufsiz];
stream_line_reader line_reader(strm, buf, bufsiz);
- if (!line_reader.getline()) { return false; }
+ if (!line_reader.getline()) { return ReadContentResult::Error; }
unsigned long chunk_len;
+ size_t total_len = 0;
while (true) {
char *end_ptr;
chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
- if (end_ptr == line_reader.ptr()) { return false; }
- if (chunk_len == ULONG_MAX) { return false; }
+ if (end_ptr == line_reader.ptr()) { return ReadContentResult::Error; }
+ if (chunk_len == ULONG_MAX) { return ReadContentResult::Error; }
if (chunk_len == 0) { break; }
- if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
- return false;
+ // Check if adding this chunk would exceed the payload limit
+ if (total_len > payload_max_length ||
+ payload_max_length - total_len < chunk_len) {
+ return ReadContentResult::PayloadTooLarge;
}
- if (!line_reader.getline()) { return false; }
+ total_len += chunk_len;
- if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; }
+ if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
+ return ReadContentResult::Error;
+ }
- if (!line_reader.getline()) { return false; }
+ if (!line_reader.getline()) { return ReadContentResult::Error; }
+
+ if (strcmp(line_reader.ptr(), "\r\n") != 0) {
+ return ReadContentResult::Error;
+ }
+
+ if (!line_reader.getline()) { return ReadContentResult::Error; }
}
assert(chunk_len == 0);
@@ -4349,10 +4806,54 @@
//
// According to the reference code in RFC 9112, cpp-httplib now allows
// chunked transfer coding data without the final CRLF.
- if (!line_reader.getline()) { return true; }
+ if (!line_reader.getline()) { return ReadContentResult::Success; }
+ // RFC 7230 Section 4.1.2 - Headers prohibited in trailers
+ thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {
+ // Message framing
+ "transfer-encoding", "content-length",
+
+ // Routing
+ "host",
+
+ // Authentication
+ "authorization", "www-authenticate", "proxy-authenticate",
+ "proxy-authorization", "cookie", "set-cookie",
+
+ // Request modifiers
+ "cache-control", "expect", "max-forwards", "pragma", "range", "te",
+
+ // Response control
+ "age", "expires", "date", "location", "retry-after", "vary", "warning",
+
+ // Payload processing
+ "content-encoding", "content-type", "content-range", "trailer"};
+
+ // Parse declared trailer headers once for performance
+ case_ignore::unordered_set<std::string> declared_trailers;
+ if (has_header(x.headers, "Trailer")) {
+ auto trailer_header = get_header_value(x.headers, "Trailer", "", 0);
+ auto len = std::strlen(trailer_header);
+
+ split(trailer_header, trailer_header + len, ',',
+ [&](const char *b, const char *e) {
+ std::string key(b, e);
+ if (prohibited_trailers.find(key) == prohibited_trailers.end()) {
+ declared_trailers.insert(key);
+ }
+ });
+ }
+
+ size_t trailer_header_count = 0;
while (strcmp(line_reader.ptr(), "\r\n") != 0) {
- if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
+ if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
+ return ReadContentResult::Error;
+ }
+
+ // Check trailer header count limit
+ if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) {
+ return ReadContentResult::Error;
+ }
// Exclude line terminator
constexpr auto line_terminator_len = 2;
@@ -4360,13 +4861,16 @@
parse_header(line_reader.ptr(), end,
[&](const std::string &key, const std::string &val) {
- x.headers.emplace(key, val);
+ if (declared_trailers.find(key) != declared_trailers.end()) {
+ x.trailers.emplace(key, val);
+ trailer_header_count++;
+ }
});
- if (!line_reader.getline()) { return false; }
+ if (!line_reader.getline()) { return ReadContentResult::Error; }
}
- return true;
+ return ReadContentResult::Success;
}
inline bool is_chunked_transfer_encoding(const Headers &headers) {
@@ -4396,12 +4900,19 @@
status = StatusCode::UnsupportedMediaType_415;
return false;
#endif
+ } else if (encoding == "zstd") {
+#ifdef CPPHTTPLIB_ZSTD_SUPPORT
+ decompressor = detail::make_unique<zstd_decompressor>();
+#else
+ status = StatusCode::UnsupportedMediaType_415;
+ return false;
+#endif
}
if (decompressor) {
if (decompressor->is_valid()) {
ContentReceiverWithProgress out = [&](const char *buf, size_t n,
- uint64_t off, uint64_t len) {
+ size_t off, size_t len) {
return decompressor->decompress(buf, n,
[&](const char *buf2, size_t n2) {
return receiver(buf2, n2, off, len);
@@ -4415,8 +4926,8 @@
}
}
- ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
- uint64_t len) {
+ ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,
+ size_t len) {
return receiver(buf, n, off, len);
};
return callback(std::move(out));
@@ -4424,8 +4935,8 @@
template <typename T>
bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
- Progress progress, ContentReceiverWithProgress receiver,
- bool decompress) {
+ DownloadProgress progress,
+ ContentReceiverWithProgress receiver, bool decompress) {
return prepare_content_receiver(
x, status, std::move(receiver), decompress,
[&](const ContentReceiverWithProgress &out) {
@@ -4433,14 +4944,31 @@
auto exceed_payload_max_length = false;
if (is_chunked_transfer_encoding(x.headers)) {
- ret = read_content_chunked(strm, x, out);
+ auto result = read_content_chunked(strm, x, payload_max_length, out);
+ if (result == ReadContentResult::Success) {
+ ret = true;
+ } else if (result == ReadContentResult::PayloadTooLarge) {
+ exceed_payload_max_length = true;
+ ret = false;
+ } else {
+ ret = false;
+ }
} else if (!has_header(x.headers, "Content-Length")) {
- ret = read_content_without_length(strm, out);
+ auto result =
+ read_content_without_length(strm, payload_max_length, out);
+ if (result == ReadContentResult::Success) {
+ ret = true;
+ } else if (result == ReadContentResult::PayloadTooLarge) {
+ exceed_payload_max_length = true;
+ ret = false;
+ } else {
+ ret = false;
+ }
} else {
auto is_invalid_value = false;
- auto len = get_header_value_u64(
- x.headers, "Content-Length",
- (std::numeric_limits<uint64_t>::max)(), 0, is_invalid_value);
+ auto len = get_header_value_u64(x.headers, "Content-Length",
+ (std::numeric_limits<size_t>::max)(),
+ 0, is_invalid_value);
if (is_invalid_value) {
ret = false;
@@ -4509,10 +5037,14 @@
}
template <typename T>
-inline bool write_content(Stream &strm, const ContentProvider &content_provider,
- size_t offset, size_t length, T is_shutting_down,
- Error &error) {
+inline bool write_content_with_progress(Stream &strm,
+ const ContentProvider &content_provider,
+ size_t offset, size_t length,
+ T is_shutting_down,
+ const UploadProgress &upload_progress,
+ Error &error) {
size_t end_offset = offset + length;
+ size_t start_offset = offset;
auto ok = true;
DataSink data_sink;
@@ -4520,6 +5052,14 @@
if (ok) {
if (write_data(strm, d, l)) {
offset += l;
+
+ if (upload_progress && length > 0) {
+ size_t current_written = offset - start_offset;
+ if (!upload_progress(current_written, length)) {
+ ok = false;
+ return false;
+ }
+ }
} else {
ok = false;
}
@@ -4548,6 +5088,14 @@
template <typename T>
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
+ size_t offset, size_t length, T is_shutting_down,
+ Error &error) {
+ return write_content_with_progress<T>(strm, content_provider, offset, length,
+ is_shutting_down, nullptr, error);
+}
+
+template <typename T>
+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;
@@ -4648,10 +5196,8 @@
}
}
- static const std::string done_marker("0\r\n");
- if (!write_data(strm, done_marker.data(), done_marker.size())) {
- ok = false;
- }
+ constexpr const char done_marker[] = "0\r\n";
+ if (!write_data(strm, done_marker, str_len(done_marker))) { ok = false; }
// Trailer
if (trailer) {
@@ -4663,8 +5209,8 @@
}
}
- static const std::string crlf("\r\n");
- if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; }
+ constexpr const char crlf[] = "\r\n";
+ if (!write_data(strm, crlf, str_len(crlf))) { ok = false; }
};
data_sink.done = [&](void) { done_with_trailer(nullptr); };
@@ -4731,9 +5277,9 @@
for (auto it = params.begin(); it != params.end(); ++it) {
if (it != params.begin()) { query += "&"; }
- query += it->first;
+ query += encode_query_component(it->first);
query += "=";
- query += encode_query_param(it->second);
+ query += encode_query_component(it->second);
}
return query;
}
@@ -4756,7 +5302,7 @@
});
if (!key.empty()) {
- params.emplace(decode_url(key, true), decode_url(val, true));
+ params.emplace(decode_query_component(key), decode_query_component(val));
}
});
}
@@ -4851,9 +5397,139 @@
} catch (...) { return false; }
#endif
-class MultipartFormDataParser {
+inline bool parse_accept_header(const std::string &s,
+ std::vector<std::string> &content_types) {
+ content_types.clear();
+
+ // Empty string is considered valid (no preference)
+ if (s.empty()) { return true; }
+
+ // Check for invalid patterns: leading/trailing commas or consecutive commas
+ if (s.front() == ',' || s.back() == ',' ||
+ s.find(",,") != std::string::npos) {
+ return false;
+ }
+
+ struct AcceptEntry {
+ std::string media_type;
+ double quality;
+ int order; // Original order in header
+ };
+
+ std::vector<AcceptEntry> entries;
+ int order = 0;
+ bool has_invalid_entry = false;
+
+ // Split by comma and parse each entry
+ split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e) {
+ std::string entry(b, e);
+ entry = trim_copy(entry);
+
+ if (entry.empty()) {
+ has_invalid_entry = true;
+ return;
+ }
+
+ AcceptEntry accept_entry;
+ accept_entry.quality = 1.0; // Default quality
+ accept_entry.order = order++;
+
+ // Find q= parameter
+ auto q_pos = entry.find(";q=");
+ if (q_pos == std::string::npos) { q_pos = entry.find("; q="); }
+
+ if (q_pos != std::string::npos) {
+ // Extract media type (before q parameter)
+ accept_entry.media_type = trim_copy(entry.substr(0, q_pos));
+
+ // Extract quality value
+ auto q_start = entry.find('=', q_pos) + 1;
+ auto q_end = entry.find(';', q_start);
+ if (q_end == std::string::npos) { q_end = entry.length(); }
+
+ std::string quality_str =
+ trim_copy(entry.substr(q_start, q_end - q_start));
+ if (quality_str.empty()) {
+ has_invalid_entry = true;
+ return;
+ }
+
+#ifdef CPPHTTPLIB_NO_EXCEPTIONS
+ {
+ std::istringstream iss(quality_str);
+ iss >> accept_entry.quality;
+
+ // Check if conversion was successful and entire string was consumed
+ if (iss.fail() || !iss.eof()) {
+ has_invalid_entry = true;
+ return;
+ }
+ }
+#else
+ try {
+ accept_entry.quality = std::stod(quality_str);
+ } catch (...) {
+ has_invalid_entry = true;
+ return;
+ }
+#endif
+ // Check if quality is in valid range [0.0, 1.0]
+ if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0) {
+ has_invalid_entry = true;
+ return;
+ }
+ } else {
+ // No quality parameter, use entire entry as media type
+ accept_entry.media_type = entry;
+ }
+
+ // Remove additional parameters from media type
+ auto param_pos = accept_entry.media_type.find(';');
+ if (param_pos != std::string::npos) {
+ accept_entry.media_type =
+ trim_copy(accept_entry.media_type.substr(0, param_pos));
+ }
+
+ // Basic validation of media type format
+ if (accept_entry.media_type.empty()) {
+ has_invalid_entry = true;
+ return;
+ }
+
+ // Check for basic media type format (should contain '/' or be '*')
+ if (accept_entry.media_type != "*" &&
+ accept_entry.media_type.find('/') == std::string::npos) {
+ has_invalid_entry = true;
+ return;
+ }
+
+ entries.push_back(accept_entry);
+ });
+
+ // Return false if any invalid entry was found
+ if (has_invalid_entry) { return false; }
+
+ // Sort by quality (descending), then by original order (ascending)
+ std::sort(entries.begin(), entries.end(),
+ [](const AcceptEntry &a, const AcceptEntry &b) {
+ if (a.quality != b.quality) {
+ return a.quality > b.quality; // Higher quality first
+ }
+ return a.order < b.order; // Earlier order first for same quality
+ });
+
+ // Extract sorted media types
+ content_types.reserve(entries.size());
+ for (const auto &entry : entries) {
+ content_types.push_back(entry.media_type);
+ }
+
+ return true;
+}
+
+class FormDataParser {
public:
- MultipartFormDataParser() = default;
+ FormDataParser() = default;
void set_boundary(std::string &&boundary) {
boundary_ = boundary;
@@ -4863,18 +5539,17 @@
bool is_valid() const { return is_valid_; }
- bool parse(const char *buf, size_t n, const ContentReceiver &content_callback,
- const MultipartContentHeader &header_callback) {
+ bool parse(const char *buf, size_t n, const FormDataHeader &header_callback,
+ const ContentReceiver &content_callback) {
buf_append(buf, n);
while (buf_size() > 0) {
switch (state_) {
case 0: { // Initial boundary
- buf_erase(buf_find(dash_boundary_crlf_));
- if (dash_boundary_crlf_.size() > buf_size()) { return true; }
- if (!buf_start_with(dash_boundary_crlf_)) { return false; }
- buf_erase(dash_boundary_crlf_.size());
+ auto pos = buf_find(dash_boundary_crlf_);
+ if (pos == buf_size()) { return true; }
+ buf_erase(pos + dash_boundary_crlf_.size());
state_ = 1;
break;
}
@@ -4906,13 +5581,23 @@
return false;
}
- static const std::string header_content_type = "Content-Type:";
+ // Parse and emplace space trimmed headers into a map
+ if (!parse_header(
+ header.data(), header.data() + header.size(),
+ [&](const std::string &key, const std::string &val) {
+ file_.headers.emplace(key, val);
+ })) {
+ is_valid_ = false;
+ return false;
+ }
+
+ constexpr const char header_content_type[] = "Content-Type:";
if (start_with_case_ignore(header, header_content_type)) {
file_.content_type =
- trim_copy(header.substr(header_content_type.size()));
+ trim_copy(header.substr(str_len(header_content_type)));
} else {
- static const std::regex re_content_disposition(
+ thread_local const std::regex re_content_disposition(
R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
std::regex_constants::icase);
@@ -4935,12 +5620,12 @@
it = params.find("filename*");
if (it != params.end()) {
// Only allow UTF-8 encoding...
- static const std::regex re_rfc5987_encoding(
+ thread_local const std::regex re_rfc5987_encoding(
R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
std::smatch m2;
if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
- file_.filename = decode_url(m2[1], false); // override...
+ file_.filename = decode_path_component(m2[1]); // override...
} else {
is_valid_ = false;
return false;
@@ -5005,12 +5690,13 @@
file_.name.clear();
file_.filename.clear();
file_.content_type.clear();
+ file_.headers.clear();
}
- bool start_with_case_ignore(const std::string &a,
- const std::string &b) const {
- if (a.size() < b.size()) { return false; }
- for (size_t i = 0; i < b.size(); i++) {
+ bool start_with_case_ignore(const std::string &a, const char *b) const {
+ const auto b_len = strlen(b);
+ if (a.size() < b_len) { return false; }
+ for (size_t i = 0; i < b_len; i++) {
if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
return false;
}
@@ -5026,7 +5712,7 @@
size_t state_ = 0;
bool is_valid_ = false;
- MultipartFormData file_;
+ FormData file_;
// Buffer
bool start_with(const std::string &a, size_t spos, size_t epos,
@@ -5097,19 +5783,18 @@
};
inline std::string random_string(size_t length) {
- static const char data[] =
+ constexpr const char data[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
- // std::random_device might actually be deterministic on some
- // platforms, but due to lack of support in the c++ standard library,
- // doing better requires either some ugly hacks or breaking portability.
- static std::random_device seed_gen;
-
- // Request 128 bits of entropy for initialization
- static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(),
- seed_gen()};
-
- static std::mt19937 engine(seed_sequence);
+ thread_local auto engine([]() {
+ // std::random_device might actually be deterministic on some
+ // platforms, but due to lack of support in the c++ standard library,
+ // doing better requires either some ugly hacks or breaking portability.
+ std::random_device seed_gen;
+ // Request 128 bits of entropy for initialization
+ std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
+ return std::mt19937(seed_sequence);
+ }());
std::string result;
for (size_t i = 0; i < length; i++) {
@@ -5165,7 +5850,7 @@
}
inline std::string
-serialize_multipart_formdata(const MultipartFormDataItems &items,
+serialize_multipart_formdata(const UploadFormDataItems &items,
const std::string &boundary, bool finish = true) {
std::string body;
@@ -5179,13 +5864,68 @@
return body;
}
+inline void coalesce_ranges(Ranges &ranges, size_t content_length) {
+ if (ranges.size() <= 1) return;
+
+ // Sort ranges by start position
+ std::sort(ranges.begin(), ranges.end(),
+ [](const Range &a, const Range &b) { return a.first < b.first; });
+
+ Ranges coalesced;
+ coalesced.reserve(ranges.size());
+
+ for (auto &r : ranges) {
+ auto first_pos = r.first;
+ auto last_pos = r.second;
+
+ // Handle special cases like in range_error
+ if (first_pos == -1 && last_pos == -1) {
+ first_pos = 0;
+ last_pos = static_cast<ssize_t>(content_length);
+ }
+
+ if (first_pos == -1) {
+ first_pos = static_cast<ssize_t>(content_length) - last_pos;
+ last_pos = static_cast<ssize_t>(content_length) - 1;
+ }
+
+ if (last_pos == -1 || last_pos >= static_cast<ssize_t>(content_length)) {
+ last_pos = static_cast<ssize_t>(content_length) - 1;
+ }
+
+ // Skip invalid ranges
+ if (!(0 <= first_pos && first_pos <= last_pos &&
+ last_pos < static_cast<ssize_t>(content_length))) {
+ continue;
+ }
+
+ // Coalesce with previous range if overlapping or adjacent (but not
+ // identical)
+ if (!coalesced.empty()) {
+ auto &prev = coalesced.back();
+ // Check if current range overlaps or is adjacent to previous range
+ // but don't coalesce identical ranges (allow duplicates)
+ if (first_pos <= prev.second + 1 &&
+ !(first_pos == prev.first && last_pos == prev.second)) {
+ // Extend the previous range
+ prev.second = (std::max)(prev.second, last_pos);
+ continue;
+ }
+ }
+
+ // Add new range
+ coalesced.emplace_back(first_pos, last_pos);
+ }
+
+ ranges = std::move(coalesced);
+}
+
inline bool range_error(Request &req, Response &res) {
if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
ssize_t content_len = static_cast<ssize_t>(
res.content_length_ ? res.content_length_ : res.body.size());
- ssize_t prev_first_pos = -1;
- ssize_t prev_last_pos = -1;
+ std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
size_t overwrapping_count = 0;
// NOTE: The following Range check is based on '14.2. Range' in RFC 9110
@@ -5228,18 +5968,21 @@
return true;
}
- // Ranges must be in ascending order
- 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 true; }
+ for (const auto &processed_range : processed_ranges) {
+ if (!(last_pos < processed_range.first ||
+ first_pos > processed_range.second)) {
+ overwrapping_count++;
+ if (overwrapping_count > 2) { return true; }
+ break; // Only count once per range
+ }
}
- prev_first_pos = (std::max)(prev_first_pos, first_pos);
- prev_last_pos = (std::max)(prev_last_pos, last_pos);
+ processed_ranges.emplace_back(first_pos, last_pos);
}
+
+ // After validation, coalesce overlapping ranges as per RFC 9110
+ coalesce_ranges(req.ranges, static_cast<size_t>(content_len));
}
return false;
@@ -5507,8 +6250,8 @@
return result;
}
-#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
-#if TARGET_OS_OSX
+#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && \
+ defined(TARGET_OS_OSX)
template <typename T>
using CFObjectPtr =
std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
@@ -5596,7 +6339,6 @@
return result;
}
-#endif // TARGET_OS_OSX
#endif // _WIN32
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
@@ -5623,7 +6365,8 @@
bool is_proxy) {
auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
if (res.has_header(auth_key)) {
- static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
+ thread_local auto re =
+ std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
auto s = res.get_header_value(auth_key);
auto pos = s.find(' ');
if (pos != std::string::npos) {
@@ -5684,7 +6427,8 @@
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
- if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) {
+ if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,
+ &result, 0)) {
#if defined __linux__ && !defined __ANDROID__
res_init();
#endif
@@ -5704,10 +6448,243 @@
}
}
+inline std::string encode_uri_component(const std::string &value) {
+ std::ostringstream escaped;
+ escaped.fill('0');
+ escaped << std::hex;
+
+ for (auto c : value) {
+ if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
+ c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
+ c == ')') {
+ escaped << c;
+ } else {
+ escaped << std::uppercase;
+ escaped << '%' << std::setw(2)
+ << static_cast<int>(static_cast<unsigned char>(c));
+ escaped << std::nouppercase;
+ }
+ }
+
+ return escaped.str();
+}
+
+inline std::string encode_uri(const std::string &value) {
+ std::ostringstream escaped;
+ escaped.fill('0');
+ escaped << std::hex;
+
+ for (auto c : value) {
+ if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
+ c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
+ c == ')' || c == ';' || c == '/' || c == '?' || c == ':' || c == '@' ||
+ c == '&' || c == '=' || c == '+' || c == '$' || c == ',' || c == '#') {
+ escaped << c;
+ } else {
+ escaped << std::uppercase;
+ escaped << '%' << std::setw(2)
+ << static_cast<int>(static_cast<unsigned char>(c));
+ escaped << std::nouppercase;
+ }
+ }
+
+ return escaped.str();
+}
+
+inline std::string decode_uri_component(const std::string &value) {
+ std::string result;
+
+ for (size_t i = 0; i < value.size(); i++) {
+ if (value[i] == '%' && i + 2 < value.size()) {
+ auto val = 0;
+ if (detail::from_hex_to_i(value, i + 1, 2, val)) {
+ result += static_cast<char>(val);
+ i += 2;
+ } else {
+ result += value[i];
+ }
+ } else {
+ result += value[i];
+ }
+ }
+
+ return result;
+}
+
+inline std::string decode_uri(const std::string &value) {
+ std::string result;
+
+ for (size_t i = 0; i < value.size(); i++) {
+ if (value[i] == '%' && i + 2 < value.size()) {
+ auto val = 0;
+ if (detail::from_hex_to_i(value, i + 1, 2, val)) {
+ result += static_cast<char>(val);
+ i += 2;
+ } else {
+ result += value[i];
+ }
+ } else {
+ result += value[i];
+ }
+ }
+
+ return result;
+}
+
+inline std::string encode_path_component(const std::string &component) {
+ std::string result;
+ result.reserve(component.size() * 3);
+
+ for (size_t i = 0; i < component.size(); i++) {
+ auto c = static_cast<unsigned char>(component[i]);
+
+ // Unreserved characters per RFC 3986: ALPHA / DIGIT / "-" / "." / "_" / "~"
+ if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
+ result += static_cast<char>(c);
+ }
+ // Path-safe sub-delimiters: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" /
+ // "," / ";" / "="
+ else if (c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' ||
+ c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||
+ c == '=') {
+ result += static_cast<char>(c);
+ }
+ // Colon is allowed in path segments except first segment
+ else if (c == ':') {
+ result += static_cast<char>(c);
+ }
+ // @ is allowed in path
+ else if (c == '@') {
+ result += static_cast<char>(c);
+ } else {
+ result += '%';
+ char hex[3];
+ snprintf(hex, sizeof(hex), "%02X", c);
+ result.append(hex, 2);
+ }
+ }
+ return result;
+}
+
+inline std::string decode_path_component(const std::string &component) {
+ std::string result;
+ result.reserve(component.size());
+
+ for (size_t i = 0; i < component.size(); i++) {
+ if (component[i] == '%' && i + 1 < component.size()) {
+ if (component[i + 1] == 'u') {
+ // Unicode %uXXXX encoding
+ auto val = 0;
+ if (detail::from_hex_to_i(component, i + 2, 4, val)) {
+ // 4 digits Unicode codes
+ char buff[4];
+ size_t len = detail::to_utf8(val, buff);
+ if (len > 0) { result.append(buff, len); }
+ i += 5; // 'u0000'
+ } else {
+ result += component[i];
+ }
+ } else {
+ // Standard %XX encoding
+ auto val = 0;
+ if (detail::from_hex_to_i(component, i + 1, 2, val)) {
+ // 2 digits hex codes
+ result += static_cast<char>(val);
+ i += 2; // 'XX'
+ } else {
+ result += component[i];
+ }
+ }
+ } else {
+ result += component[i];
+ }
+ }
+ return result;
+}
+
+inline std::string encode_query_component(const std::string &component,
+ bool space_as_plus) {
+ std::string result;
+ result.reserve(component.size() * 3);
+
+ for (size_t i = 0; i < component.size(); i++) {
+ auto c = static_cast<unsigned char>(component[i]);
+
+ // Unreserved characters per RFC 3986
+ if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
+ result += static_cast<char>(c);
+ }
+ // Space handling
+ else if (c == ' ') {
+ if (space_as_plus) {
+ result += '+';
+ } else {
+ result += "%20";
+ }
+ }
+ // Plus sign handling
+ else if (c == '+') {
+ if (space_as_plus) {
+ result += "%2B";
+ } else {
+ result += static_cast<char>(c);
+ }
+ }
+ // Query-safe sub-delimiters (excluding & and = which are query delimiters)
+ else if (c == '!' || c == '$' || c == '\'' || c == '(' || c == ')' ||
+ c == '*' || c == ',' || c == ';') {
+ result += static_cast<char>(c);
+ }
+ // Colon and @ are allowed in query
+ else if (c == ':' || c == '@') {
+ result += static_cast<char>(c);
+ }
+ // Forward slash is allowed in query values
+ else if (c == '/') {
+ result += static_cast<char>(c);
+ }
+ // Question mark is allowed in query values (after first ?)
+ else if (c == '?') {
+ result += static_cast<char>(c);
+ } else {
+ result += '%';
+ char hex[3];
+ snprintf(hex, sizeof(hex), "%02X", c);
+ result.append(hex, 2);
+ }
+ }
+ return result;
+}
+
+inline std::string decode_query_component(const std::string &component,
+ bool plus_as_space) {
+ std::string result;
+ result.reserve(component.size());
+
+ for (size_t i = 0; i < component.size(); i++) {
+ if (component[i] == '%' && i + 2 < component.size()) {
+ std::string hex = component.substr(i + 1, 2);
+ char *end;
+ unsigned long value = std::strtoul(hex.c_str(), &end, 16);
+ if (end == hex.c_str() + 2) {
+ result += static_cast<char>(value);
+ i += 2;
+ } else {
+ result += component[i];
+ }
+ } else if (component[i] == '+' && plus_as_space) {
+ result += ' '; // + becomes space in form-urlencoded
+ } else {
+ result += component[i];
+ }
+ }
+ return result;
+}
+
inline std::string append_query_params(const std::string &path,
const Params ¶ms) {
std::string path_with_query = path;
- const static std::regex re("[^?]+\\?.*");
+ thread_local const std::regex re("[^?]+\\?.*");
auto delm = std::regex_match(path, re) ? '&' : '?';
path_with_query += delm + detail::params_to_query_str(params);
return path_with_query;
@@ -5767,6 +6744,24 @@
}
}
+inline bool Request::has_trailer(const std::string &key) const {
+ return trailers.find(key) != trailers.end();
+}
+
+inline std::string Request::get_trailer_value(const std::string &key,
+ size_t id) const {
+ auto rng = trailers.equal_range(key);
+ auto it = rng.first;
+ std::advance(it, static_cast<ssize_t>(id));
+ if (it != rng.second) { return it->second; }
+ return std::string();
+}
+
+inline size_t Request::get_trailer_value_count(const std::string &key) const {
+ auto r = trailers.equal_range(key);
+ return static_cast<size_t>(std::distance(r.first, r.second));
+}
+
inline bool Request::has_param(const std::string &key) const {
return params.find(key) != params.end();
}
@@ -5790,19 +6785,47 @@
return !content_type.rfind("multipart/form-data", 0);
}
-inline bool Request::has_file(const std::string &key) const {
- return files.find(key) != files.end();
+// Multipart FormData implementation
+inline std::string MultipartFormData::get_field(const std::string &key,
+ size_t id) const {
+ auto rng = fields.equal_range(key);
+ auto it = rng.first;
+ std::advance(it, static_cast<ssize_t>(id));
+ if (it != rng.second) { return it->second.content; }
+ return std::string();
}
-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();
+inline std::vector<std::string>
+MultipartFormData::get_fields(const std::string &key) const {
+ std::vector<std::string> values;
+ auto rng = fields.equal_range(key);
+ for (auto it = rng.first; it != rng.second; it++) {
+ values.push_back(it->second.content);
+ }
+ return values;
}
-inline std::vector<MultipartFormData>
-Request::get_file_values(const std::string &key) const {
- std::vector<MultipartFormData> values;
+inline bool MultipartFormData::has_field(const std::string &key) const {
+ return fields.find(key) != fields.end();
+}
+
+inline size_t MultipartFormData::get_field_count(const std::string &key) const {
+ auto r = fields.equal_range(key);
+ return static_cast<size_t>(std::distance(r.first, r.second));
+}
+
+inline FormData MultipartFormData::get_file(const std::string &key,
+ size_t id) const {
+ auto rng = files.equal_range(key);
+ auto it = rng.first;
+ std::advance(it, static_cast<ssize_t>(id));
+ if (it != rng.second) { return it->second; }
+ return FormData();
+}
+
+inline std::vector<FormData>
+MultipartFormData::get_files(const std::string &key) const {
+ std::vector<FormData> values;
auto rng = files.equal_range(key);
for (auto it = rng.first; it != rng.second; it++) {
values.push_back(it->second);
@@ -5810,6 +6833,15 @@
return values;
}
+inline bool MultipartFormData::has_file(const std::string &key) const {
+ return files.find(key) != files.end();
+}
+
+inline size_t MultipartFormData::get_file_count(const std::string &key) const {
+ auto r = files.equal_range(key);
+ return static_cast<size_t>(std::distance(r.first, r.second));
+}
+
// Response implementation
inline bool Response::has_header(const std::string &key) const {
return headers.find(key) != headers.end();
@@ -5833,6 +6865,23 @@
headers.emplace(key, val);
}
}
+inline bool Response::has_trailer(const std::string &key) const {
+ return trailers.find(key) != trailers.end();
+}
+
+inline std::string Response::get_trailer_value(const std::string &key,
+ size_t id) const {
+ auto rng = trailers.equal_range(key);
+ auto it = rng.first;
+ std::advance(it, static_cast<ssize_t>(id));
+ if (it != rng.second) { return it->second; }
+ return std::string();
+}
+
+inline size_t Response::get_trailer_value_count(const std::string &key) const {
+ auto r = trailers.equal_range(key);
+ return static_cast<size_t>(std::distance(r.first, r.second));
+}
inline void Response::set_redirect(const std::string &url, int stat) {
if (detail::fields::is_field_value(url)) {
@@ -5945,6 +6994,8 @@
auto actual_timeout_msec =
(std::min)(max_timeout_msec - duration_msec, timeout_msec);
+ if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }
+
actual_timeout_sec = actual_timeout_msec / 1000;
actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
}
@@ -5959,7 +7010,7 @@
read_timeout_usec_(read_timeout_usec),
write_timeout_sec_(write_timeout_sec),
write_timeout_usec_(write_timeout_usec),
- max_timeout_msec_(max_timeout_msec), start_time(start_time),
+ max_timeout_msec_(max_timeout_msec), start_time_(start_time),
read_buff_(read_buff_size_, 0) {}
inline SocketStream::~SocketStream() = default;
@@ -6057,7 +7108,7 @@
inline time_t SocketStream::duration() const {
return std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now() - start_time)
+ std::chrono::steady_clock::now() - start_time_)
.count();
}
@@ -6095,8 +7146,9 @@
inline const std::string &BufferStream::get_buffer() const { return buffer; }
-inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
- static constexpr char marker[] = "/:";
+inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern)
+ : MatcherBase(pattern) {
+ constexpr const char marker[] = "/:";
// One past the last ending position of a path param substring
std::size_t last_param_end = 0;
@@ -6117,7 +7169,7 @@
static_fragments_.push_back(
pattern.substr(last_param_end, marker_pos - last_param_end + 1));
- const auto param_name_start = marker_pos + 2;
+ const auto param_name_start = marker_pos + str_len(marker);
auto sep_pos = pattern.find(separator, param_name_start);
if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
@@ -6346,11 +7398,26 @@
return *this;
}
+inline Server &Server::set_pre_request_handler(HandlerWithResponse handler) {
+ pre_request_handler_ = std::move(handler);
+ return *this;
+}
+
inline Server &Server::set_logger(Logger logger) {
logger_ = std::move(logger);
return *this;
}
+inline Server &Server::set_error_logger(ErrorLogger error_logger) {
+ error_logger_ = std::move(error_logger);
+ return *this;
+}
+
+inline Server &Server::set_pre_compression_logger(Logger logger) {
+ pre_compression_logger_ = std::move(logger);
+ return *this;
+}
+
inline Server &
Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
expect_100_continue_handler_ = std::move(handler);
@@ -6481,13 +7548,19 @@
if (count != 3) { return false; }
}
- static const std::set<std::string> methods{
+ thread_local const std::set<std::string> methods{
"GET", "HEAD", "POST", "PUT", "DELETE",
"CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
- if (methods.find(req.method) == methods.end()) { return false; }
+ if (methods.find(req.method) == methods.end()) {
+ output_error_log(Error::InvalidHTTPMethod, &req);
+ return false;
+ }
- if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; }
+ if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") {
+ output_error_log(Error::InvalidHTTPVersion, &req);
+ return false;
+ }
{
// Skip URL fragment
@@ -6501,8 +7574,8 @@
detail::divide(req.target, '?',
[&](const char *lhs_data, std::size_t lhs_size,
const char *rhs_data, std::size_t rhs_size) {
- req.path = detail::decode_url(
- std::string(lhs_data, lhs_size), false);
+ req.path =
+ decode_path_component(std::string(lhs_data, lhs_size));
detail::parse_query_text(rhs_data, rhs_size, req.params);
});
}
@@ -6594,7 +7667,7 @@
}
// Log
- if (logger_) { logger_(req, res); }
+ output_log(req, res);
return ret;
}
@@ -6636,6 +7709,10 @@
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
compressor = detail::make_unique<detail::brotli_compressor>();
#endif
+ } else if (type == detail::EncodingType::Zstd) {
+#ifdef CPPHTTPLIB_ZSTD_SUPPORT
+ compressor = detail::make_unique<detail::zstd_compressor>();
+#endif
} else {
compressor = detail::make_unique<detail::nocompressor>();
}
@@ -6651,8 +7728,10 @@
}
inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
- MultipartFormDataMap::iterator cur;
- auto file_count = 0;
+ FormFields::iterator cur_field;
+ FormFiles::iterator cur_file;
+ auto is_text_field = false;
+ size_t count = 0;
if (read_content_core(
strm, req, res,
// Regular
@@ -6661,24 +7740,40 @@
req.body.append(buf, n);
return true;
},
- // Multipart
- [&](const MultipartFormData &file) {
- if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
+ // Multipart FormData
+ [&](const FormData &file) {
+ if (count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
+ output_error_log(Error::TooManyFormDataFiles, &req);
return false;
}
- cur = req.files.emplace(file.name, file);
+
+ if (file.filename.empty()) {
+ cur_field = req.form.fields.emplace(
+ file.name, FormField{file.name, file.content, file.headers});
+ is_text_field = true;
+ } else {
+ cur_file = req.form.files.emplace(file.name, file);
+ is_text_field = false;
+ }
return true;
},
[&](const char *buf, size_t n) {
- auto &content = cur->second.content;
- if (content.size() + n > content.max_size()) { return false; }
- content.append(buf, n);
+ if (is_text_field) {
+ auto &content = cur_field->second.content;
+ if (content.size() + n > content.max_size()) { return false; }
+ content.append(buf, n);
+ } else {
+ auto &content = cur_file->second.content;
+ if (content.size() + n > content.max_size()) { return false; }
+ content.append(buf, n);
+ }
return true;
})) {
const auto &content_type = req.get_header_value("Content-Type");
if (!content_type.find("application/x-www-form-urlencoded")) {
if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
+ output_error_log(Error::ExceedMaxPayloadSize, &req);
return false;
}
detail::parse_query_text(req.body, req.params);
@@ -6690,19 +7785,16 @@
inline bool Server::read_content_with_content_receiver(
Stream &strm, Request &req, Response &res, ContentReceiver receiver,
- MultipartContentHeader multipart_header,
- ContentReceiver multipart_receiver) {
+ FormDataHeader multipart_header, ContentReceiver multipart_receiver) {
return read_content_core(strm, req, res, std::move(receiver),
std::move(multipart_header),
std::move(multipart_receiver));
}
-inline bool
-Server::read_content_core(Stream &strm, Request &req, Response &res,
- ContentReceiver receiver,
- MultipartContentHeader multipart_header,
- ContentReceiver multipart_receiver) const {
- detail::MultipartFormDataParser multipart_form_data_parser;
+inline bool Server::read_content_core(
+ Stream &strm, Request &req, Response &res, ContentReceiver receiver,
+ FormDataHeader multipart_header, ContentReceiver multipart_receiver) const {
+ detail::FormDataParser multipart_form_data_parser;
ContentReceiverWithProgress out;
if (req.is_multipart_form_data()) {
@@ -6710,28 +7802,18 @@
std::string boundary;
if (!detail::parse_multipart_boundary(content_type, boundary)) {
res.status = StatusCode::BadRequest_400;
+ output_error_log(Error::MultipartParsing, &req);
return false;
}
multipart_form_data_parser.set_boundary(std::move(boundary));
- out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
- /* For debug
- size_t pos = 0;
- while (pos < n) {
- auto read_size = (std::min)<size_t>(1, n - pos);
- auto ret = multipart_form_data_parser.parse(
- buf + pos, read_size, multipart_receiver, multipart_header);
- if (!ret) { return false; }
- pos += read_size;
- }
- return true;
- */
- return multipart_form_data_parser.parse(buf, n, multipart_receiver,
- multipart_header);
+ out = [&](const char *buf, size_t n, size_t /*off*/, size_t /*len*/) {
+ return multipart_form_data_parser.parse(buf, n, multipart_header,
+ multipart_receiver);
};
} else {
- out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
- uint64_t /*len*/) { return receiver(buf, n); };
+ out = [receiver](const char *buf, size_t n, size_t /*off*/,
+ size_t /*len*/) { return receiver(buf, n); };
}
if (req.method == "DELETE" && !req.has_header("Content-Length")) {
@@ -6746,6 +7828,7 @@
if (req.is_multipart_form_data()) {
if (!multipart_form_data_parser.is_valid()) {
res.status = StatusCode::BadRequest_400;
+ output_error_log(Error::MultipartParsing, &req);
return false;
}
}
@@ -6753,8 +7836,7 @@
return true;
}
-inline bool Server::handle_file_request(const Request &req, Response &res,
- bool head) {
+inline bool Server::handle_file_request(const Request &req, Response &res) {
for (const auto &entry : base_dirs_) {
// Prefix match
if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
@@ -6776,7 +7858,10 @@
}
auto mm = std::make_shared<detail::mmap>(path.c_str());
- if (!mm->is_open()) { return false; }
+ if (!mm->is_open()) {
+ output_error_log(Error::OpenFile, &req);
+ return false;
+ }
res.set_content_provider(
mm->size(),
@@ -6787,11 +7872,13 @@
return true;
});
- if (!head && file_request_handler_) {
+ if (req.method != "HEAD" && file_request_handler_) {
file_request_handler_(req, res);
}
return true;
+ } else {
+ output_error_log(Error::OpenFile, &req);
}
}
}
@@ -6806,11 +7893,15 @@
return detail::create_socket(
host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
ipv6_v6only_, std::move(socket_options),
- [](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
+ [&](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
+ output_error_log(Error::BindIPAddress, nullptr);
return false;
}
- if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; }
+ if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
+ output_error_log(Error::Listen, nullptr);
+ return false;
+ }
return true;
});
}
@@ -6829,6 +7920,7 @@
socklen_t addr_len = sizeof(addr);
if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
&addr_len) == -1) {
+ output_error_log(Error::GetSockName, nullptr);
return -1;
}
if (addr.ss_family == AF_INET) {
@@ -6836,6 +7928,7 @@
} else if (addr.ss_family == AF_INET6) {
return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
} else {
+ output_error_log(Error::UnsupportedAddressFamily, nullptr);
return -1;
}
} else {
@@ -6889,6 +7982,7 @@
if (svr_sock_ != INVALID_SOCKET) {
detail::close_socket(svr_sock_);
ret = false;
+ output_error_log(Error::Connection, nullptr);
} else {
; // The server socket was closed by user.
}
@@ -6902,6 +7996,7 @@
if (!task_queue->enqueue(
[this, sock]() { process_and_close_socket(sock); })) {
+ output_error_log(Error::ResourceExhaustion, nullptr);
detail::shutdown_socket(sock);
detail::close_socket(sock);
}
@@ -6921,9 +8016,8 @@
}
// File handler
- auto is_head_request = req.method == "HEAD";
- if ((req.method == "GET" || is_head_request) &&
- handle_file_request(req, res, is_head_request)) {
+ if ((req.method == "GET" || req.method == "HEAD") &&
+ handle_file_request(req, res)) {
return true;
}
@@ -6932,13 +8026,17 @@
{
ContentReader reader(
[&](ContentReceiver receiver) {
- return read_content_with_content_receiver(
+ auto result = read_content_with_content_receiver(
strm, req, res, std::move(receiver), nullptr, nullptr);
+ if (!result) { output_error_log(Error::Read, &req); }
+ return result;
},
- [&](MultipartContentHeader header, ContentReceiver receiver) {
- return read_content_with_content_receiver(strm, req, res, nullptr,
- std::move(header),
- std::move(receiver));
+ [&](FormDataHeader header, ContentReceiver receiver) {
+ auto result = read_content_with_content_receiver(
+ strm, req, res, nullptr, std::move(header),
+ std::move(receiver));
+ if (!result) { output_error_log(Error::Read, &req); }
+ return result;
});
if (req.method == "POST") {
@@ -6969,7 +8067,10 @@
}
// Read content into `req.body`
- if (!read_content(strm, req, res)) { return false; }
+ if (!read_content(strm, req, res)) {
+ output_error_log(Error::Read, &req);
+ return false;
+ }
}
// Regular handler
@@ -6998,7 +8099,11 @@
const auto &handler = x.second;
if (matcher->match(req)) {
- handler(req, res);
+ req.matched_route = matcher->pattern();
+ if (!pre_request_handler_ ||
+ pre_request_handler_(req, res) != HandlerResponse::Handled) {
+ handler(req, res);
+ }
return true;
}
}
@@ -7050,6 +8155,8 @@
res.set_header("Content-Encoding", "gzip");
} else if (type == detail::EncodingType::Brotli) {
res.set_header("Content-Encoding", "br");
+ } else if (type == detail::EncodingType::Zstd) {
+ res.set_header("Content-Encoding", "zstd");
}
}
}
@@ -7077,6 +8184,8 @@
}
if (type != detail::EncodingType::None) {
+ output_pre_compression_log(req, res);
+
std::unique_ptr<detail::compressor> compressor;
std::string content_encoding;
@@ -7090,6 +8199,11 @@
compressor = detail::make_unique<detail::brotli_compressor>();
content_encoding = "br";
#endif
+ } else if (type == detail::EncodingType::Zstd) {
+#ifdef CPPHTTPLIB_ZSTD_SUPPORT
+ compressor = detail::make_unique<detail::zstd_compressor>();
+ content_encoding = "zstd";
+#endif
}
if (compressor) {
@@ -7118,7 +8232,11 @@
const auto &handler = x.second;
if (matcher->match(req)) {
- handler(req, res, content_reader);
+ req.matched_route = matcher->pattern();
+ if (!pre_request_handler_ ||
+ pre_request_handler_(req, res) != HandlerResponse::Handled) {
+ handler(req, res, content_reader);
+ }
return true;
}
}
@@ -7144,10 +8262,28 @@
res.version = "HTTP/1.1";
res.headers = default_headers_;
+#ifdef __APPLE__
+ // Socket file descriptor exceeded FD_SETSIZE...
+ if (strm.socket() >= FD_SETSIZE) {
+ Headers dummy;
+ detail::read_headers(strm, dummy);
+ res.status = StatusCode::InternalServerError_500;
+ output_error_log(Error::ExceedMaxSocketDescriptorCount, &req);
+ return write_response(strm, close_connection, req, res);
+ }
+#endif
+
// Request line and headers
- if (!parse_request_line(line_reader.ptr(), req) ||
- !detail::read_headers(strm, req.headers)) {
+ if (!parse_request_line(line_reader.ptr(), req)) {
res.status = StatusCode::BadRequest_400;
+ output_error_log(Error::InvalidRequestLine, &req);
+ return write_response(strm, close_connection, req, res);
+ }
+
+ // Request headers
+ if (!detail::read_headers(strm, req.headers)) {
+ res.status = StatusCode::BadRequest_400;
+ output_error_log(Error::InvalidHeaders, &req);
return write_response(strm, close_connection, req, res);
}
@@ -7156,6 +8292,7 @@
Headers dummy;
detail::read_headers(strm, dummy);
res.status = StatusCode::UriTooLong_414;
+ output_error_log(Error::ExceedUriMaxLength, &req);
return write_response(strm, close_connection, req, res);
}
@@ -7178,10 +8315,20 @@
req.set_header("LOCAL_ADDR", req.local_addr);
req.set_header("LOCAL_PORT", std::to_string(req.local_port));
+ if (req.has_header("Accept")) {
+ const auto &accept_header = req.get_header_value("Accept");
+ if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
+ res.status = StatusCode::BadRequest_400;
+ output_error_log(Error::HTTPParsing, &req);
+ return write_response(strm, close_connection, req, res);
+ }
+ }
+
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)) {
res.status = StatusCode::RangeNotSatisfiable_416;
+ output_error_log(Error::InvalidRangeHeader, &req);
return write_response(strm, close_connection, req, res);
}
}
@@ -7206,8 +8353,9 @@
}
// Setup `is_connection_closed` method
- req.is_connection_closed = [&]() {
- return !detail::is_socket_alive(strm.socket());
+ auto sock = strm.socket();
+ req.is_connection_closed = [sock]() {
+ return !detail::is_socket_alive(sock);
};
// Routing
@@ -7261,6 +8409,7 @@
res.content_length_ = 0;
res.content_provider_ = nullptr;
res.status = StatusCode::NotFound_404;
+ output_error_log(Error::OpenFile, &req);
return write_response(strm, close_connection, req, res);
}
@@ -7320,6 +8469,29 @@
return ret;
}
+inline void Server::output_log(const Request &req, const Response &res) const {
+ if (logger_) {
+ std::lock_guard<std::mutex> guard(logger_mutex_);
+ logger_(req, res);
+ }
+}
+
+inline void Server::output_pre_compression_log(const Request &req,
+ const Response &res) const {
+ if (pre_compression_logger_) {
+ std::lock_guard<std::mutex> guard(logger_mutex_);
+ pre_compression_logger_(req, res);
+ }
+}
+
+inline void Server::output_error_log(const Error &err,
+ const Request *req) const {
+ if (error_logger_) {
+ std::lock_guard<std::mutex> guard(logger_mutex_);
+ error_logger_(err, req);
+ }
+}
+
// HTTP client implementation
inline ClientImpl::ClientImpl(const std::string &host)
: ClientImpl(host, 80, std::string(), std::string()) {}
@@ -7370,7 +8542,7 @@
#endif
keep_alive_ = rhs.keep_alive_;
follow_location_ = rhs.follow_location_;
- url_encode_ = rhs.url_encode_;
+ path_encode_ = rhs.path_encode_;
address_family_ = rhs.address_family_;
tcp_nodelay_ = rhs.tcp_nodelay_;
ipv6_v6only_ = rhs.ipv6_v6only_;
@@ -7398,6 +8570,7 @@
server_certificate_verifier_ = rhs.server_certificate_verifier_;
#endif
logger_ = rhs.logger_;
+ error_logger_ = rhs.error_logger_;
}
inline socket_t ClientImpl::create_client_socket(Error &error) const {
@@ -7470,9 +8643,9 @@
if (!line_reader.getline()) { return false; }
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
- const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
+ thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
#else
- const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
+ thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
#endif
std::cmatch m;
@@ -7540,7 +8713,10 @@
}
if (!is_alive) {
- if (!create_and_connect_socket(socket_, error)) { return false; }
+ if (!create_and_connect_socket(socket_, error)) {
+ output_error_log(error, &req);
+ return false;
+ }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
// TODO: refactoring
@@ -7550,11 +8726,15 @@
auto success = false;
if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
error)) {
+ if (!success) { output_error_log(error, &req); }
return success;
}
}
- if (!scli.initialize_ssl(socket_, error)) { return false; }
+ if (!scli.initialize_ssl(socket_, error)) {
+ output_error_log(error, &req);
+ return false;
+ }
}
#endif
}
@@ -7600,7 +8780,10 @@
});
if (!ret) {
- if (error == Error::Success) { error = Error::Unknown; }
+ if (error == Error::Success) {
+ error = Error::Unknown;
+ output_error_log(error, &req);
+ }
}
return ret;
@@ -7615,7 +8798,12 @@
auto res = detail::make_unique<Response>();
auto error = Error::Success;
auto ret = send(req, *res, error);
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers),
+ last_ssl_error_, last_openssl_error_};
+#else
return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
+#endif
}
inline bool ClientImpl::handle_request(Stream &strm, Request &req,
@@ -7623,6 +8811,7 @@
Error &error) {
if (req.path.empty()) {
error = Error::Connection;
+ output_error_log(error, &req);
return false;
}
@@ -7698,13 +8887,14 @@
inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
if (req.redirect_count_ == 0) {
error = Error::ExceedRedirectCount;
+ output_error_log(error, &req);
return false;
}
auto location = res.get_header_value("location");
if (location.empty()) { return false; }
- const static std::regex re(
+ thread_local const std::regex re(
R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
std::smatch m;
@@ -7730,26 +8920,154 @@
if (next_host.empty()) { next_host = host_; }
if (next_path.empty()) { next_path = "/"; }
- auto path = detail::decode_url(next_path, true) + next_query;
+ auto path = decode_query_component(next_path, true) + next_query;
+ // Same host redirect - use current client
if (next_scheme == scheme && next_host == host_ && next_port == port_) {
return detail::redirect(*this, req, res, path, location, error);
- } else {
- if (next_scheme == "https") {
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
- SSLClient cli(next_host, next_port);
- cli.copy_settings(*this);
- if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
- return detail::redirect(cli, req, res, path, location, error);
-#else
- return false;
-#endif
- } else {
- ClientImpl cli(next_host, next_port);
- cli.copy_settings(*this);
- return detail::redirect(cli, req, res, path, location, error);
+ }
+
+ // Cross-host/scheme redirect - create new client with robust setup
+ return create_redirect_client(next_scheme, next_host, next_port, req, res,
+ path, location, error);
+}
+
+// New method for robust redirect client creation
+inline bool ClientImpl::create_redirect_client(
+ const std::string &scheme, const std::string &host, int port, Request &req,
+ Response &res, const std::string &path, const std::string &location,
+ Error &error) {
+ // Determine if we need SSL
+ auto need_ssl = (scheme == "https");
+
+ // Clean up request headers that are host/client specific
+ // Remove headers that should not be carried over to new host
+ auto headers_to_remove =
+ std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
+
+ for (const auto &header_name : headers_to_remove) {
+ auto it = req.headers.find(header_name);
+ while (it != req.headers.end()) {
+ it = req.headers.erase(it);
+ it = req.headers.find(header_name);
}
}
+
+ // Create appropriate client type and handle redirect
+ if (need_ssl) {
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ // Create SSL client for HTTPS redirect
+ SSLClient redirect_client(host, port);
+
+ // Setup basic client configuration first
+ setup_redirect_client(redirect_client);
+
+ // SSL-specific configuration for proxy environments
+ if (!proxy_host_.empty() && proxy_port_ != -1) {
+ // Critical: Disable SSL verification for proxy environments
+ redirect_client.enable_server_certificate_verification(false);
+ redirect_client.enable_server_hostname_verification(false);
+ } else {
+ // For direct SSL connections, copy SSL verification settings
+ redirect_client.enable_server_certificate_verification(
+ server_certificate_verification_);
+ redirect_client.enable_server_hostname_verification(
+ server_hostname_verification_);
+ }
+
+ // Handle CA certificate store and paths if available
+ if (ca_cert_store_) { redirect_client.set_ca_cert_store(ca_cert_store_); }
+ if (!ca_cert_file_path_.empty()) {
+ redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
+ }
+
+ // Client certificates are set through constructor for SSLClient
+ // NOTE: SSLClient constructor already takes client_cert_path and
+ // client_key_path so we need to create it properly if client certs are
+ // needed
+
+ // Execute the redirect
+ return detail::redirect(redirect_client, req, res, path, location, error);
+#else
+ // SSL not supported - set appropriate error
+ error = Error::SSLConnection;
+ output_error_log(error, &req);
+ return false;
+#endif
+ } else {
+ // HTTP redirect
+ ClientImpl redirect_client(host, port);
+
+ // Setup client with robust configuration
+ setup_redirect_client(redirect_client);
+
+ // Execute the redirect
+ return detail::redirect(redirect_client, req, res, path, location, error);
+ }
+}
+
+// New method for robust client setup (based on basic_manual_redirect.cpp logic)
+template <typename ClientType>
+inline void ClientImpl::setup_redirect_client(ClientType &client) {
+ // Copy basic settings first
+ client.set_connection_timeout(connection_timeout_sec_);
+ client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
+ client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
+ client.set_keep_alive(keep_alive_);
+ client.set_follow_location(
+ true); // Enable redirects to handle multi-step redirects
+ client.set_path_encode(path_encode_);
+ client.set_compress(compress_);
+ client.set_decompress(decompress_);
+
+ // Copy authentication settings BEFORE proxy setup
+ if (!basic_auth_username_.empty()) {
+ client.set_basic_auth(basic_auth_username_, basic_auth_password_);
+ }
+ if (!bearer_token_auth_token_.empty()) {
+ client.set_bearer_token_auth(bearer_token_auth_token_);
+ }
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ if (!digest_auth_username_.empty()) {
+ client.set_digest_auth(digest_auth_username_, digest_auth_password_);
+ }
+#endif
+
+ // Setup proxy configuration (CRITICAL ORDER - proxy must be set
+ // before proxy auth)
+ if (!proxy_host_.empty() && proxy_port_ != -1) {
+ // First set proxy host and port
+ client.set_proxy(proxy_host_, proxy_port_);
+
+ // Then set proxy authentication (order matters!)
+ if (!proxy_basic_auth_username_.empty()) {
+ client.set_proxy_basic_auth(proxy_basic_auth_username_,
+ proxy_basic_auth_password_);
+ }
+ if (!proxy_bearer_token_auth_token_.empty()) {
+ client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
+ }
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ if (!proxy_digest_auth_username_.empty()) {
+ client.set_proxy_digest_auth(proxy_digest_auth_username_,
+ proxy_digest_auth_password_);
+ }
+#endif
+ }
+
+ // Copy network and socket settings
+ client.set_address_family(address_family_);
+ client.set_tcp_nodelay(tcp_nodelay_);
+ client.set_ipv6_v6only(ipv6_v6only_);
+ if (socket_options_) { client.set_socket_options(socket_options_); }
+ if (!interface_.empty()) { client.set_interface(interface_); }
+
+ // Copy logging and headers
+ if (logger_) { client.set_logger(logger_); }
+ if (error_logger_) { client.set_error_logger(error_logger_); }
+
+ // NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
+ // Each new client should generate its own headers based on its target host
}
inline bool ClientImpl::write_content_with_provider(Stream &strm,
@@ -7772,8 +9090,9 @@
return detail::write_content_chunked(strm, req.content_provider_,
is_shutting_down, *compressor, error);
} else {
- return detail::write_content(strm, req.content_provider_, 0,
- req.content_length_, is_shutting_down, error);
+ return detail::write_content_with_progress(
+ strm, req.content_provider_, 0, req.content_length_, is_shutting_down,
+ req.upload_progress, error);
}
}
@@ -7787,7 +9106,11 @@
}
if (!req.has_header("Host")) {
- if (is_ssl()) {
+ // For Unix socket connections, use "localhost" as Host header (similar to
+ // curl behavior)
+ if (address_family_ == AF_UNIX) {
+ req.set_header("Host", "localhost");
+ } else if (is_ssl()) {
if (port_ == 443) {
req.set_header("Host", host_);
} else {
@@ -7814,6 +9137,10 @@
if (!accept_encoding.empty()) { accept_encoding += ", "; }
accept_encoding += "gzip, deflate";
#endif
+#ifdef CPPHTTPLIB_ZSTD_SUPPORT
+ if (!accept_encoding.empty()) { accept_encoding += ", "; }
+ accept_encoding += "zstd";
+#endif
req.set_header("Accept-Encoding", accept_encoding);
}
@@ -7883,21 +9210,35 @@
{
detail::BufferStream bstrm;
- const auto &path_with_query =
- req.params.empty() ? req.path
- : append_query_params(req.path, req.params);
+ // Extract path and query from req.path
+ std::string path_part, query_part;
+ auto query_pos = req.path.find('?');
+ if (query_pos != std::string::npos) {
+ path_part = req.path.substr(0, query_pos);
+ query_part = req.path.substr(query_pos + 1);
+ } else {
+ path_part = req.path;
+ query_part = "";
+ }
- const auto &path =
- url_encode_ ? detail::encode_url(path_with_query) : path_with_query;
+ // Encode path and query
+ auto path_with_query =
+ path_encode_ ? detail::encode_path(path_part) : path_part;
- detail::write_request_line(bstrm, req.method, path);
+ detail::parse_query_text(query_part, req.params);
+ if (!req.params.empty()) {
+ path_with_query = append_query_params(path_with_query, req.params);
+ }
+ // Write request line and headers
+ detail::write_request_line(bstrm, req.method, path_with_query);
header_writer_(bstrm, req.headers);
// Flush buffer
auto &data = bstrm.get_buffer();
if (!detail::write_data(strm, data.data(), data.size())) {
error = Error::Write;
+ output_error_log(error, &req);
return false;
}
}
@@ -7907,9 +9248,32 @@
return write_content_with_provider(strm, req, error);
}
- if (!detail::write_data(strm, req.body.data(), req.body.size())) {
- error = Error::Write;
- return false;
+ if (req.upload_progress) {
+ auto body_size = req.body.size();
+ size_t written = 0;
+ auto data = req.body.data();
+
+ while (written < body_size) {
+ size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
+ if (!detail::write_data(strm, data + written, to_write)) {
+ error = Error::Write;
+ output_error_log(error, &req);
+ return false;
+ }
+ written += to_write;
+
+ if (!req.upload_progress(written, body_size)) {
+ error = Error::Canceled;
+ output_error_log(error, &req);
+ return false;
+ }
+ }
+ } else {
+ if (!detail::write_data(strm, req.body.data(), req.body.size())) {
+ error = Error::Write;
+ output_error_log(error, &req);
+ return false;
+ }
}
return true;
@@ -7959,6 +9323,7 @@
while (ok && offset < content_length) {
if (!content_provider(offset, content_length - offset, data_sink)) {
error = Error::Canceled;
+ output_error_log(error, &req);
return nullptr;
}
}
@@ -7969,6 +9334,7 @@
return true;
})) {
error = Error::Compression;
+ output_error_log(error, &req);
return nullptr;
}
}
@@ -7998,12 +9364,12 @@
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, Progress progress) {
+ const std::string &content_type, UploadProgress progress) {
Request req;
req.method = method;
req.headers = headers;
req.path = path;
- req.progress = progress;
+ req.upload_progress = std::move(progress);
if (max_timeout_msec_ > 0) {
req.start_time_ = std::chrono::steady_clock::now();
}
@@ -8014,7 +9380,12 @@
req, body, content_length, std::move(content_provider),
std::move(content_provider_without_length), content_type, error);
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ return Result{std::move(res), error, std::move(req.headers), last_ssl_error_,
+ last_openssl_error_};
+#else
return Result{std::move(res), error, std::move(req.headers)};
+#endif
}
inline std::string
@@ -8023,6 +9394,22 @@
return host;
}
+inline void ClientImpl::output_log(const Request &req,
+ const Response &res) const {
+ if (logger_) {
+ std::lock_guard<std::mutex> guard(logger_mutex_);
+ logger_(req, res);
+ }
+}
+
+inline void ClientImpl::output_error_log(const Error &err,
+ const Request *req) const {
+ if (error_logger_) {
+ std::lock_guard<std::mutex> guard(logger_mutex_);
+ error_logger_(err, req);
+ }
+}
+
inline bool ClientImpl::process_request(Stream &strm, Request &req,
Response &res, bool close_connection,
Error &error) {
@@ -8035,6 +9422,7 @@
if (!is_proxy_enabled) {
if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
error = Error::SSLPeerCouldBeClosed_;
+ output_error_log(error, &req);
return false;
}
}
@@ -8045,6 +9433,7 @@
if (!read_response_line(strm, req, res) ||
!detail::read_headers(strm, res.headers)) {
error = Error::Read;
+ output_error_log(error, &req);
return false;
}
@@ -8058,6 +9447,7 @@
if (req.response_handler && !redirect) {
if (!req.response_handler(res)) {
error = Error::Canceled;
+ output_error_log(error, &req);
return false;
}
}
@@ -8065,24 +9455,30 @@
auto out =
req.content_receiver
? static_cast<ContentReceiverWithProgress>(
- [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
+ [&](const char *buf, size_t n, size_t off, size_t len) {
if (redirect) { return true; }
auto ret = req.content_receiver(buf, n, off, len);
- if (!ret) { error = Error::Canceled; }
+ if (!ret) {
+ error = Error::Canceled;
+ output_error_log(error, &req);
+ }
return ret;
})
: static_cast<ContentReceiverWithProgress>(
- [&](const char *buf, size_t n, uint64_t /*off*/,
- uint64_t /*len*/) {
+ [&](const char *buf, size_t n, size_t /*off*/,
+ size_t /*len*/) {
assert(res.body.size() + n <= res.body.max_size());
res.body.append(buf, n);
return true;
});
- auto progress = [&](uint64_t current, uint64_t total) {
- if (!req.progress || redirect) { return true; }
- auto ret = req.progress(current, total);
- if (!ret) { error = Error::Canceled; }
+ auto progress = [&](size_t current, size_t total) {
+ if (!req.download_progress || redirect) { return true; }
+ auto ret = req.download_progress(current, total);
+ if (!ret) {
+ error = Error::Canceled;
+ output_error_log(error, &req);
+ }
return ret;
};
@@ -8091,6 +9487,7 @@
auto len = res.get_header_value_u64("Content-Length");
if (len > res.body.max_size()) {
error = Error::Read;
+ output_error_log(error, &req);
return false;
}
res.body.reserve(static_cast<size_t>(len));
@@ -8103,20 +9500,21 @@
dummy_status, std::move(progress),
std::move(out), decompress_)) {
if (error != Error::Canceled) { error = Error::Read; }
+ output_error_log(error, &req);
return false;
}
}
}
// Log
- if (logger_) { logger_(req, res); }
+ output_log(req, res);
return true;
}
inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
- const std::string &boundary, const MultipartFormDataItems &items,
- const MultipartFormDataProviderItems &provider_items) const {
+ const std::string &boundary, const UploadFormDataItems &items,
+ const FormDataProviderItems &provider_items) const {
size_t cur_item = 0;
size_t cur_start = 0;
// cur_item and cur_start are copied to within the std::function and maintain
@@ -8169,25 +9567,27 @@
inline bool ClientImpl::is_ssl() const { return false; }
-inline Result ClientImpl::Get(const std::string &path) {
- return Get(path, Headers(), Progress());
-}
-
-inline Result ClientImpl::Get(const std::string &path, Progress progress) {
+inline Result ClientImpl::Get(const std::string &path,
+ DownloadProgress progress) {
return Get(path, Headers(), std::move(progress));
}
-inline Result ClientImpl::Get(const std::string &path, const Headers &headers) {
- return Get(path, headers, Progress());
+inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,
+ const Headers &headers,
+ DownloadProgress progress) {
+ if (params.empty()) { return Get(path, headers); }
+
+ std::string path_with_query = append_query_params(path, params);
+ return Get(path_with_query, headers, std::move(progress));
}
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
- Progress progress) {
+ DownloadProgress progress) {
Request req;
req.method = "GET";
req.path = path;
req.headers = headers;
- req.progress = std::move(progress);
+ req.download_progress = std::move(progress);
if (max_timeout_msec_ > 0) {
req.start_time_ = std::chrono::steady_clock::now();
}
@@ -8196,47 +9596,23 @@
}
inline Result ClientImpl::Get(const std::string &path,
- ContentReceiver content_receiver) {
- return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr);
-}
-
-inline Result ClientImpl::Get(const std::string &path,
ContentReceiver content_receiver,
- Progress progress) {
+ DownloadProgress progress) {
return Get(path, Headers(), nullptr, std::move(content_receiver),
std::move(progress));
}
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
- ContentReceiver content_receiver) {
- return Get(path, headers, nullptr, std::move(content_receiver), nullptr);
-}
-
-inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
ContentReceiver content_receiver,
- Progress progress) {
+ DownloadProgress progress) {
return Get(path, headers, nullptr, std::move(content_receiver),
std::move(progress));
}
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);
-}
-
-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);
-}
-
-inline Result ClientImpl::Get(const std::string &path,
- ResponseHandler response_handler,
ContentReceiver content_receiver,
- Progress progress) {
+ DownloadProgress progress) {
return Get(path, Headers(), std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
@@ -8244,7 +9620,7 @@
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver,
- Progress progress) {
+ DownloadProgress progress) {
Request req;
req.method = "GET";
req.path = path;
@@ -8252,10 +9628,10 @@
req.response_handler = std::move(response_handler);
req.content_receiver =
[content_receiver](const char *data, size_t data_length,
- uint64_t /*offset*/, uint64_t /*total_length*/) {
+ size_t /*offset*/, size_t /*total_length*/) {
return content_receiver(data, data_length);
};
- req.progress = std::move(progress);
+ req.download_progress = std::move(progress);
if (max_timeout_msec_ > 0) {
req.start_time_ = std::chrono::steady_clock::now();
}
@@ -8264,17 +9640,9 @@
}
inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,
- const Headers &headers, Progress progress) {
- if (params.empty()) { return Get(path, headers); }
-
- std::string path_with_query = append_query_params(path, params);
- return Get(path_with_query, headers, std::move(progress));
-}
-
-inline Result ClientImpl::Get(const std::string &path, const Params ¶ms,
const Headers &headers,
ContentReceiver content_receiver,
- Progress progress) {
+ DownloadProgress progress) {
return Get(path, params, headers, nullptr, std::move(content_receiver),
std::move(progress));
}
@@ -8283,7 +9651,7 @@
const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver,
- Progress progress) {
+ DownloadProgress progress) {
if (params.empty()) {
return Get(path, headers, std::move(response_handler),
std::move(content_receiver), std::move(progress));
@@ -8322,85 +9690,35 @@
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, nullptr);
-}
-
-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, nullptr);
-}
-
-inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
- const char *body, size_t content_length,
const std::string &content_type,
- Progress progress) {
- return send_with_content_provider("POST", path, headers, body, content_length,
- nullptr, nullptr, content_type, progress);
-}
-
-inline Result ClientImpl::Post(const std::string &path, const std::string &body,
- const std::string &content_type) {
- return Post(path, Headers(), body, content_type);
+ UploadProgress progress) {
+ return Post(path, Headers(), body, content_length, content_type, progress);
}
inline Result ClientImpl::Post(const std::string &path, const std::string &body,
const std::string &content_type,
- Progress progress) {
+ UploadProgress progress) {
return Post(path, Headers(), body, content_type, progress);
}
-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(),
- body.size(), nullptr, nullptr, content_type,
- nullptr);
-}
-
-inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
- const std::string &body,
- const std::string &content_type,
- Progress progress) {
- return send_with_content_provider("POST", path, headers, body.data(),
- body.size(), nullptr, nullptr, content_type,
- progress);
-}
-
inline Result ClientImpl::Post(const std::string &path, const Params ¶ms) {
return Post(path, Headers(), params);
}
inline Result ClientImpl::Post(const std::string &path, size_t content_length,
ContentProvider content_provider,
- const std::string &content_type) {
+ const std::string &content_type,
+ UploadProgress progress) {
return Post(path, Headers(), content_length, std::move(content_provider),
- content_type);
+ content_type, progress);
}
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);
-}
-
-inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
- size_t content_length,
- ContentProvider content_provider,
- const std::string &content_type) {
- return send_with_content_provider("POST", path, headers, nullptr,
- content_length, std::move(content_provider),
- nullptr, content_type, nullptr);
-}
-
-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,
- nullptr);
+ const std::string &content_type,
+ UploadProgress progress) {
+ return Post(path, Headers(), std::move(content_provider), content_type,
+ progress);
}
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
@@ -8409,30 +9727,26 @@
return Post(path, headers, query, "application/x-www-form-urlencoded");
}
-inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
- const Params ¶ms, Progress progress) {
- auto query = detail::params_to_query_str(params);
- return Post(path, headers, query, "application/x-www-form-urlencoded",
- progress);
-}
-
inline Result ClientImpl::Post(const std::string &path,
- const MultipartFormDataItems &items) {
- return Post(path, Headers(), items);
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ return Post(path, Headers(), items, progress);
}
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
- const MultipartFormDataItems &items) {
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
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);
+ return Post(path, headers, body, content_type, progress);
}
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
- const MultipartFormDataItems &items,
- const std::string &boundary) {
+ const UploadFormDataItems &items,
+ const std::string &boundary,
+ UploadProgress progress) {
if (!detail::is_multipart_boundary_chars_valid(boundary)) {
return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
}
@@ -8440,139 +9754,151 @@
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);
+ return Post(path, headers, body, content_type, progress);
}
-inline Result
-ClientImpl::Post(const std::string &path, const Headers &headers,
- const MultipartFormDataItems &items,
- const MultipartFormDataProviderItems &provider_items) {
+inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
+ const char *body, size_t content_length,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return send_with_content_provider("POST", path, headers, body, content_length,
+ nullptr, nullptr, content_type, progress);
+}
+
+inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
+ const std::string &body,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return send_with_content_provider("POST", path, headers, body.data(),
+ body.size(), nullptr, nullptr, content_type,
+ progress);
+}
+
+inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
+ size_t content_length,
+ ContentProvider content_provider,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return send_with_content_provider("POST", path, headers, nullptr,
+ content_length, std::move(content_provider),
+ nullptr, content_type, progress);
+}
+
+inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
+ ContentProviderWithoutLength content_provider,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr,
+ std::move(content_provider), content_type,
+ progress);
+}
+
+inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ const FormDataProviderItems &provider_items,
+ UploadProgress progress) {
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, nullptr);
+ content_type, progress);
+}
+
+inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
+ const std::string &body,
+ const std::string &content_type,
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
+ Request req;
+ req.method = "POST";
+ req.path = path;
+ req.headers = headers;
+ req.body = body;
+ req.content_receiver =
+ [content_receiver](const char *data, size_t data_length,
+ size_t /*offset*/, size_t /*total_length*/) {
+ return content_receiver(data, data_length);
+ };
+ req.download_progress = std::move(progress);
+
+ if (max_timeout_msec_ > 0) {
+ req.start_time_ = std::chrono::steady_clock::now();
+ }
+
+ if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
+
+ return send_(std::move(req));
}
inline Result ClientImpl::Put(const std::string &path) {
return Put(path, std::string(), std::string());
}
+inline Result ClientImpl::Put(const std::string &path, const Headers &headers) {
+ return Put(path, headers, nullptr, 0, std::string());
+}
+
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);
-}
-
-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, nullptr);
-}
-
-inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
- const char *body, size_t content_length,
const std::string &content_type,
- Progress progress) {
- return send_with_content_provider("PUT", path, headers, body, content_length,
- nullptr, nullptr, content_type, progress);
-}
-
-inline Result ClientImpl::Put(const std::string &path, const std::string &body,
- const std::string &content_type) {
- return Put(path, Headers(), body, content_type);
+ UploadProgress progress) {
+ return Put(path, Headers(), body, content_length, content_type, progress);
}
inline Result ClientImpl::Put(const std::string &path, const std::string &body,
const std::string &content_type,
- Progress progress) {
+ UploadProgress progress) {
return Put(path, Headers(), body, content_type, progress);
}
-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(),
- body.size(), nullptr, nullptr, content_type,
- nullptr);
-}
-
-inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
- const std::string &body,
- const std::string &content_type,
- Progress progress) {
- return send_with_content_provider("PUT", path, headers, body.data(),
- body.size(), nullptr, nullptr, content_type,
- progress);
-}
-
-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);
-}
-
-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);
-}
-
-inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
- size_t content_length,
- ContentProvider content_provider,
- const std::string &content_type) {
- return send_with_content_provider("PUT", path, headers, nullptr,
- content_length, std::move(content_provider),
- nullptr, content_type, nullptr);
-}
-
-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,
- nullptr);
-}
-
inline Result ClientImpl::Put(const std::string &path, const Params ¶ms) {
return Put(path, Headers(), params);
}
+inline Result ClientImpl::Put(const std::string &path, size_t content_length,
+ ContentProvider content_provider,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return Put(path, Headers(), content_length, std::move(content_provider),
+ content_type, progress);
+}
+
+inline Result ClientImpl::Put(const std::string &path,
+ ContentProviderWithoutLength content_provider,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return Put(path, Headers(), std::move(content_provider), content_type,
+ progress);
+}
+
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");
}
-inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
- const Params ¶ms, Progress progress) {
- auto query = detail::params_to_query_str(params);
- return Put(path, headers, query, "application/x-www-form-urlencoded",
- progress);
-}
-
inline Result ClientImpl::Put(const std::string &path,
- const MultipartFormDataItems &items) {
- return Put(path, Headers(), items);
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ return Put(path, Headers(), items, progress);
}
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
- const MultipartFormDataItems &items) {
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
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);
+ return Put(path, headers, body, content_type, progress);
}
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
- const MultipartFormDataItems &items,
- const std::string &boundary) {
+ const UploadFormDataItems &items,
+ const std::string &boundary,
+ UploadProgress progress) {
if (!detail::is_multipart_boundary_chars_valid(boundary)) {
return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
}
@@ -8580,150 +9906,297 @@
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);
+ return Put(path, headers, body, content_type, progress);
}
-inline Result
-ClientImpl::Put(const std::string &path, const Headers &headers,
- const MultipartFormDataItems &items,
- const MultipartFormDataProviderItems &provider_items) {
+inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
+ const char *body, size_t content_length,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return send_with_content_provider("PUT", path, headers, body, content_length,
+ nullptr, nullptr, content_type, progress);
+}
+
+inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
+ const std::string &body,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return send_with_content_provider("PUT", path, headers, body.data(),
+ body.size(), nullptr, nullptr, content_type,
+ progress);
+}
+
+inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
+ size_t content_length,
+ ContentProvider content_provider,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return send_with_content_provider("PUT", path, headers, nullptr,
+ content_length, std::move(content_provider),
+ nullptr, content_type, progress);
+}
+
+inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
+ ContentProviderWithoutLength content_provider,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr,
+ std::move(content_provider), content_type,
+ progress);
+}
+
+inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ const FormDataProviderItems &provider_items,
+ UploadProgress progress) {
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, nullptr);
+ content_type, progress);
}
+
+inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
+ const std::string &body,
+ const std::string &content_type,
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
+ Request req;
+ req.method = "PUT";
+ req.path = path;
+ req.headers = headers;
+ req.body = body;
+ req.content_receiver =
+ [content_receiver](const char *data, size_t data_length,
+ size_t /*offset*/, size_t /*total_length*/) {
+ return content_receiver(data, data_length);
+ };
+ req.download_progress = std::move(progress);
+
+ if (max_timeout_msec_ > 0) {
+ req.start_time_ = std::chrono::steady_clock::now();
+ }
+
+ if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
+
+ return send_(std::move(req));
+}
+
inline Result ClientImpl::Patch(const std::string &path) {
return Patch(path, std::string(), std::string());
}
-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);
+inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
+ UploadProgress progress) {
+ return Patch(path, headers, nullptr, 0, std::string(), progress);
}
inline Result ClientImpl::Patch(const std::string &path, const char *body,
size_t content_length,
const std::string &content_type,
- Progress progress) {
+ UploadProgress progress) {
return Patch(path, Headers(), body, content_length, content_type, progress);
}
+inline Result ClientImpl::Patch(const std::string &path,
+ const std::string &body,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return Patch(path, Headers(), body, content_type, progress);
+}
+
+inline Result ClientImpl::Patch(const std::string &path, const Params ¶ms) {
+ return Patch(path, Headers(), params);
+}
+
+inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
+ ContentProvider content_provider,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return Patch(path, Headers(), content_length, std::move(content_provider),
+ content_type, progress);
+}
+
+inline Result ClientImpl::Patch(const std::string &path,
+ ContentProviderWithoutLength content_provider,
+ const std::string &content_type,
+ UploadProgress progress) {
+ return Patch(path, Headers(), std::move(content_provider), content_type,
+ progress);
+}
+
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
- const char *body, size_t content_length,
- const std::string &content_type) {
- return Patch(path, headers, body, content_length, content_type, nullptr);
+ const Params ¶ms) {
+ auto query = detail::params_to_query_str(params);
+ return Patch(path, headers, query, "application/x-www-form-urlencoded");
+}
+
+inline Result ClientImpl::Patch(const std::string &path,
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ return Patch(path, Headers(), items, progress);
+}
+
+inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ 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 Patch(path, headers, body, content_type, progress);
+}
+
+inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ const std::string &boundary,
+ UploadProgress progress) {
+ 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 Patch(path, headers, body, content_type, progress);
}
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
const char *body, size_t content_length,
const std::string &content_type,
- Progress progress) {
+ UploadProgress progress) {
return send_with_content_provider("PATCH", path, headers, body,
content_length, nullptr, nullptr,
content_type, progress);
}
-inline Result ClientImpl::Patch(const std::string &path,
- const std::string &body,
- const std::string &content_type) {
- return Patch(path, Headers(), body, content_type);
-}
-
-inline Result ClientImpl::Patch(const std::string &path,
- const std::string &body,
- const std::string &content_type,
- Progress progress) {
- return Patch(path, Headers(), body, content_type, progress);
-}
-
-inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
- const std::string &body,
- const std::string &content_type) {
- return Patch(path, headers, body, content_type, nullptr);
-}
-
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
const std::string &body,
const std::string &content_type,
- Progress progress) {
+ UploadProgress progress) {
return send_with_content_provider("PATCH", path, headers, body.data(),
body.size(), nullptr, nullptr, content_type,
progress);
}
-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);
-}
-
-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);
-}
-
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
- const std::string &content_type) {
+ const std::string &content_type,
+ UploadProgress progress) {
return send_with_content_provider("PATCH", path, headers, nullptr,
content_length, std::move(content_provider),
- nullptr, content_type, nullptr);
+ nullptr, content_type, progress);
}
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
ContentProviderWithoutLength content_provider,
- const std::string &content_type) {
+ const std::string &content_type,
+ UploadProgress progress) {
return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr,
std::move(content_provider), content_type,
- nullptr);
+ progress);
}
-inline Result ClientImpl::Delete(const std::string &path) {
- return Delete(path, Headers(), std::string(), std::string());
+inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ const FormDataProviderItems &provider_items,
+ UploadProgress progress) {
+ 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(
+ "PATCH", path, headers, nullptr, 0, nullptr,
+ get_multipart_content_provider(boundary, items, provider_items),
+ content_type, progress);
+}
+
+inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
+ const std::string &body,
+ const std::string &content_type,
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
+ Request req;
+ req.method = "PATCH";
+ req.path = path;
+ req.headers = headers;
+ req.body = body;
+ req.content_receiver =
+ [content_receiver](const char *data, size_t data_length,
+ size_t /*offset*/, size_t /*total_length*/) {
+ return content_receiver(data, data_length);
+ };
+ req.download_progress = std::move(progress);
+
+ if (max_timeout_msec_ > 0) {
+ req.start_time_ = std::chrono::steady_clock::now();
+ }
+
+ if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
+
+ return send_(std::move(req));
}
inline Result ClientImpl::Delete(const std::string &path,
- const Headers &headers) {
- return Delete(path, headers, std::string(), std::string());
+ DownloadProgress progress) {
+ return Delete(path, Headers(), std::string(), std::string(), progress);
}
-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);
+inline Result ClientImpl::Delete(const std::string &path,
+ const Headers &headers,
+ DownloadProgress progress) {
+ return Delete(path, headers, std::string(), std::string(), progress);
}
inline Result ClientImpl::Delete(const std::string &path, const char *body,
size_t content_length,
const std::string &content_type,
- Progress progress) {
+ DownloadProgress progress) {
return Delete(path, Headers(), body, content_length, content_type, progress);
}
inline Result ClientImpl::Delete(const std::string &path,
- const Headers &headers, const char *body,
- size_t content_length,
- const std::string &content_type) {
- return Delete(path, headers, body, content_length, content_type, nullptr);
+ const std::string &body,
+ const std::string &content_type,
+ DownloadProgress progress) {
+ return Delete(path, Headers(), body.data(), body.size(), content_type,
+ progress);
+}
+
+inline Result ClientImpl::Delete(const std::string &path,
+ const Headers &headers,
+ const std::string &body,
+ const std::string &content_type,
+ DownloadProgress progress) {
+ return Delete(path, headers, body.data(), body.size(), content_type,
+ progress);
+}
+
+inline Result ClientImpl::Delete(const std::string &path, const Params ¶ms,
+ DownloadProgress progress) {
+ return Delete(path, Headers(), params, progress);
+}
+
+inline Result ClientImpl::Delete(const std::string &path,
+ const Headers &headers, const Params ¶ms,
+ DownloadProgress progress) {
+ auto query = detail::params_to_query_str(params);
+ return Delete(path, headers, query, "application/x-www-form-urlencoded",
+ progress);
}
inline Result ClientImpl::Delete(const std::string &path,
const Headers &headers, const char *body,
size_t content_length,
const std::string &content_type,
- Progress progress) {
+ DownloadProgress progress) {
Request req;
req.method = "DELETE";
req.headers = headers;
req.path = path;
- req.progress = progress;
+ req.download_progress = std::move(progress);
if (max_timeout_msec_ > 0) {
req.start_time_ = std::chrono::steady_clock::now();
}
@@ -8734,36 +10207,6 @@
return send_(std::move(req));
}
-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);
-}
-
-inline Result ClientImpl::Delete(const std::string &path,
- const std::string &body,
- const std::string &content_type,
- Progress progress) {
- return Delete(path, Headers(), body.data(), body.size(), content_type,
- progress);
-}
-
-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);
-}
-
-inline Result ClientImpl::Delete(const std::string &path,
- const Headers &headers,
- const std::string &body,
- const std::string &content_type,
- Progress progress) {
- return Delete(path, headers, body.data(), body.size(), content_type,
- progress);
-}
-
inline Result ClientImpl::Options(const std::string &path) {
return Options(path, Headers());
}
@@ -8856,7 +10299,7 @@
inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
-inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }
+inline void ClientImpl::set_path_encode(bool on) { path_encode_ = on; }
inline void
ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
@@ -8968,6 +10411,10 @@
logger_ = std::move(logger);
}
+inline void ClientImpl::set_error_logger(ErrorLogger error_logger) {
+ error_logger_ = std::move(error_logger);
+}
+
/*
* SSL Implementation
*/
@@ -9028,8 +10475,8 @@
template <typename U>
bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
U ssl_connect_or_accept,
- time_t timeout_sec,
- time_t timeout_usec) {
+ time_t timeout_sec, time_t timeout_usec,
+ int *ssl_error) {
auto res = 0;
while ((res = ssl_connect_or_accept(ssl)) != 1) {
auto err = SSL_get_error(ssl, res);
@@ -9042,6 +10489,7 @@
break;
default: break;
}
+ if (ssl_error) { *ssl_error = err; }
return false;
}
return true;
@@ -9074,14 +10522,6 @@
return callback(strm);
}
-class SSLInit {
-public:
- SSLInit() {
- OPENSSL_init_ssl(
- OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
- }
-};
-
// SSL socket stream implementation
inline SSLSocketStream::SSLSocketStream(
socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
@@ -9092,7 +10532,7 @@
read_timeout_usec_(read_timeout_usec),
write_timeout_sec_(write_timeout_sec),
write_timeout_usec_(write_timeout_usec),
- max_timeout_msec_(max_timeout_msec), start_time(start_time) {
+ max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
}
@@ -9143,9 +10583,10 @@
if (ret >= 0) { return ret; }
err = SSL_get_error(ssl_, ret);
} else {
- return -1;
+ break;
}
}
+ assert(ret < 0);
}
return ret;
} else {
@@ -9175,9 +10616,10 @@
if (ret >= 0) { return ret; }
err = SSL_get_error(ssl_, ret);
} else {
- return -1;
+ break;
}
}
+ assert(ret < 0);
}
return ret;
}
@@ -9198,12 +10640,10 @@
inline time_t SSLSocketStream::duration() const {
return std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now() - start_time)
+ std::chrono::steady_clock::now() - start_time_)
.count();
}
-static SSLInit sslinit_;
-
} // namespace detail
// SSL HTTP server implementation
@@ -9230,6 +10670,7 @@
SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
1 ||
SSL_CTX_check_private_key(ctx_) != 1) {
+ last_ssl_error_ = static_cast<int>(ERR_get_error());
SSL_CTX_free(ctx_);
ctx_ = nullptr;
} else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
@@ -9303,7 +10744,8 @@
sock, ctx_, ctx_mutex_,
[&](SSL *ssl2) {
return detail::ssl_connect_or_accept_nonblocking(
- sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
+ sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_,
+ &last_ssl_error_);
},
[](SSL * /*ssl2*/) { return true; });
@@ -9371,6 +10813,7 @@
SSL_FILETYPE_PEM) != 1 ||
SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
SSL_FILETYPE_PEM) != 1) {
+ last_openssl_error_ = ERR_get_error();
SSL_CTX_free(ctx_);
ctx_ = nullptr;
}
@@ -9397,6 +10840,7 @@
if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
+ last_openssl_error_ = ERR_get_error();
SSL_CTX_free(ctx_);
ctx_ = nullptr;
}
@@ -9474,6 +10918,19 @@
!proxy_digest_auth_password_.empty()) {
std::map<std::string, std::string> auth;
if (detail::parse_www_authenticate(proxy_res, auth, true)) {
+ // Close the current socket and create a new one for the authenticated
+ // request
+ shutdown_ssl(socket, true);
+ shutdown_socket(socket);
+ close_socket(socket);
+
+ // Create a new socket for the authenticated CONNECT request
+ if (!create_and_connect_socket(socket, error)) {
+ success = false;
+ output_error_log(error, nullptr);
+ return false;
+ }
+
proxy_res = Response();
if (!detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_,
@@ -9508,6 +10965,7 @@
// as the response of the request
if (proxy_res.status != StatusCode::OK_200) {
error = Error::ProxyConnection;
+ output_error_log(error, nullptr);
res = std::move(proxy_res);
// Thread-safe to close everything because we are assuming there are
// no requests in flight
@@ -9528,11 +10986,13 @@
if (!ca_cert_file_path_.empty()) {
if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
nullptr)) {
+ last_openssl_error_ = ERR_get_error();
ret = false;
}
} else if (!ca_cert_dir_path_.empty()) {
if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
ca_cert_dir_path_.c_str())) {
+ last_openssl_error_ = ERR_get_error();
ret = false;
}
} else {
@@ -9540,10 +11000,9 @@
#ifdef _WIN32
loaded =
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
-#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
-#if TARGET_OS_OSX
+#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && \
+ defined(TARGET_OS_OSX)
loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
-#endif // TARGET_OS_OSX
#endif // _WIN32
if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
}
@@ -9559,6 +11018,7 @@
if (server_certificate_verification_) {
if (!load_certs()) {
error = Error::SSLLoadingCerts;
+ output_error_log(error, nullptr);
return false;
}
SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
@@ -9566,8 +11026,9 @@
if (!detail::ssl_connect_or_accept_nonblocking(
socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
- connection_timeout_usec_)) {
+ connection_timeout_usec_, &last_ssl_error_)) {
error = Error::SSLConnection;
+ output_error_log(error, nullptr);
return false;
}
@@ -9579,7 +11040,9 @@
}
if (verification_status == SSLVerifierResponse::CertificateRejected) {
+ last_openssl_error_ = ERR_get_error();
error = Error::SSLServerVerification;
+ output_error_log(error, nullptr);
return false;
}
@@ -9587,7 +11050,9 @@
verify_result_ = SSL_get_verify_result(ssl2);
if (verify_result_ != X509_V_OK) {
+ last_openssl_error_ = static_cast<unsigned long>(verify_result_);
error = Error::SSLServerVerification;
+ output_error_log(error, nullptr);
return false;
}
@@ -9595,13 +11060,17 @@
auto se = detail::scope_exit([&] { X509_free(server_cert); });
if (server_cert == nullptr) {
+ last_openssl_error_ = ERR_get_error();
error = Error::SSLServerVerification;
+ output_error_log(error, nullptr);
return false;
}
if (server_hostname_verification_) {
if (!verify_host(server_cert)) {
+ last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
error = Error::SSLServerHostnameVerification;
+ output_error_log(error, nullptr);
return false;
}
}
@@ -9858,72 +11327,54 @@
return cli_ != nullptr && cli_->is_valid();
}
-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);
-}
-inline Result Client::Get(const std::string &path, Progress progress) {
+inline Result Client::Get(const std::string &path, DownloadProgress progress) {
return cli_->Get(path, std::move(progress));
}
inline Result Client::Get(const std::string &path, const Headers &headers,
- Progress progress) {
+ DownloadProgress progress) {
return cli_->Get(path, headers, std::move(progress));
}
inline Result Client::Get(const std::string &path,
- ContentReceiver content_receiver) {
- return cli_->Get(path, std::move(content_receiver));
-}
-inline Result Client::Get(const std::string &path, const Headers &headers,
- ContentReceiver content_receiver) {
- return cli_->Get(path, headers, std::move(content_receiver));
-}
-inline Result Client::Get(const std::string &path,
- ContentReceiver content_receiver, Progress progress) {
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
return cli_->Get(path, std::move(content_receiver), std::move(progress));
}
inline Result Client::Get(const std::string &path, const Headers &headers,
- ContentReceiver content_receiver, Progress progress) {
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
return cli_->Get(path, headers, std::move(content_receiver),
std::move(progress));
}
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));
-}
-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));
-}
-inline Result Client::Get(const std::string &path,
- ResponseHandler response_handler,
- ContentReceiver content_receiver, Progress progress) {
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
return cli_->Get(path, std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
inline Result Client::Get(const std::string &path, const Headers &headers,
ResponseHandler response_handler,
- ContentReceiver content_receiver, Progress progress) {
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
return cli_->Get(path, headers, std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
inline Result Client::Get(const std::string &path, const Params ¶ms,
- const Headers &headers, Progress progress) {
+ const Headers &headers, DownloadProgress progress) {
return cli_->Get(path, params, headers, std::move(progress));
}
inline Result Client::Get(const std::string &path, const Params ¶ms,
const Headers &headers,
- ContentReceiver content_receiver, Progress progress) {
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
return cli_->Get(path, params, headers, std::move(content_receiver),
std::move(progress));
}
inline Result Client::Get(const std::string &path, const Params ¶ms,
const Headers &headers,
ResponseHandler response_handler,
- ContentReceiver content_receiver, Progress progress) {
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
return cli_->Get(path, params, headers, std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
@@ -9939,60 +11390,55 @@
}
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);
+ const std::string &content_type,
+ UploadProgress progress) {
+ return cli_->Post(path, body, content_length, content_type, progress);
}
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);
-}
-inline Result Client::Post(const std::string &path, const Headers &headers,
- const char *body, size_t content_length,
- const std::string &content_type, Progress progress) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Post(path, headers, body, content_length, content_type,
progress);
}
inline Result Client::Post(const std::string &path, const std::string &body,
- const std::string &content_type) {
- return cli_->Post(path, body, content_type);
-}
-inline Result Client::Post(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Post(path, body, content_type, progress);
}
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);
-}
-inline Result Client::Post(const std::string &path, const Headers &headers,
- const std::string &body,
- const std::string &content_type, Progress progress) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Post(path, headers, body, content_type, progress);
}
inline Result Client::Post(const std::string &path, size_t content_length,
ContentProvider content_provider,
- const std::string &content_type) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Post(path, content_length, std::move(content_provider),
- content_type);
+ content_type, progress);
}
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);
+ const std::string &content_type,
+ UploadProgress progress) {
+ return cli_->Post(path, std::move(content_provider), content_type, progress);
}
inline Result Client::Post(const std::string &path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
- const std::string &content_type) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Post(path, headers, content_length, std::move(content_provider),
- content_type);
+ content_type, progress);
}
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);
+ const std::string &content_type,
+ UploadProgress progress) {
+ return cli_->Post(path, headers, std::move(content_provider), content_type,
+ progress);
}
inline Result Client::Post(const std::string &path, const Params ¶ms) {
return cli_->Post(path, params);
@@ -10001,85 +11447,91 @@
const Params ¶ms) {
return cli_->Post(path, headers, params);
}
-inline Result Client::Post(const std::string &path, const Headers &headers,
- const Params ¶ms, Progress progress) {
- return cli_->Post(path, headers, params, progress);
-}
inline Result Client::Post(const std::string &path,
- const MultipartFormDataItems &items) {
- return cli_->Post(path, items);
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ return cli_->Post(path, items, progress);
}
inline Result Client::Post(const std::string &path, const Headers &headers,
- const MultipartFormDataItems &items) {
- return cli_->Post(path, headers, items);
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ return cli_->Post(path, headers, items, progress);
}
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);
+ const UploadFormDataItems &items,
+ const std::string &boundary,
+ UploadProgress progress) {
+ return cli_->Post(path, headers, items, boundary, progress);
}
-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);
+inline Result Client::Post(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ const FormDataProviderItems &provider_items,
+ UploadProgress progress) {
+ return cli_->Post(path, headers, items, provider_items, progress);
}
+inline Result Client::Post(const std::string &path, const Headers &headers,
+ const std::string &body,
+ const std::string &content_type,
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
+ return cli_->Post(path, headers, body, content_type, content_receiver,
+ progress);
+}
+
inline Result Client::Put(const std::string &path) { return cli_->Put(path); }
+inline Result Client::Put(const std::string &path, const Headers &headers) {
+ return cli_->Put(path, headers);
+}
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);
+ const std::string &content_type,
+ UploadProgress progress) {
+ return cli_->Put(path, body, content_length, content_type, progress);
}
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);
-}
-inline Result Client::Put(const std::string &path, const Headers &headers,
- const char *body, size_t content_length,
- const std::string &content_type, Progress progress) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Put(path, headers, body, content_length, content_type, progress);
}
inline Result Client::Put(const std::string &path, const std::string &body,
- const std::string &content_type) {
- return cli_->Put(path, body, content_type);
-}
-inline Result Client::Put(const std::string &path, const std::string &body,
- const std::string &content_type, Progress progress) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Put(path, body, content_type, progress);
}
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);
-}
-inline Result Client::Put(const std::string &path, const Headers &headers,
- const std::string &body,
- const std::string &content_type, Progress progress) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Put(path, headers, body, content_type, progress);
}
inline Result Client::Put(const std::string &path, size_t content_length,
ContentProvider content_provider,
- const std::string &content_type) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Put(path, content_length, std::move(content_provider),
- content_type);
+ content_type, progress);
}
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);
+ const std::string &content_type,
+ UploadProgress progress) {
+ return cli_->Put(path, std::move(content_provider), content_type, progress);
}
inline Result Client::Put(const std::string &path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
- const std::string &content_type) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Put(path, headers, content_length, std::move(content_provider),
- content_type);
+ content_type, progress);
}
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);
+ const std::string &content_type,
+ UploadProgress progress) {
+ return cli_->Put(path, headers, std::move(content_provider), content_type,
+ progress);
}
inline Result Client::Put(const std::string &path, const Params ¶ms) {
return cli_->Put(path, params);
@@ -10088,147 +11540,174 @@
const Params ¶ms) {
return cli_->Put(path, headers, params);
}
-inline Result Client::Put(const std::string &path, const Headers &headers,
- const Params ¶ms, Progress progress) {
- return cli_->Put(path, headers, params, progress);
-}
inline Result Client::Put(const std::string &path,
- const MultipartFormDataItems &items) {
- return cli_->Put(path, items);
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ return cli_->Put(path, items, progress);
}
inline Result Client::Put(const std::string &path, const Headers &headers,
- const MultipartFormDataItems &items) {
- return cli_->Put(path, headers, items);
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ return cli_->Put(path, headers, items, progress);
}
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);
+ const UploadFormDataItems &items,
+ const std::string &boundary,
+ UploadProgress progress) {
+ return cli_->Put(path, headers, items, boundary, progress);
}
-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);
+inline Result Client::Put(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ const FormDataProviderItems &provider_items,
+ UploadProgress progress) {
+ return cli_->Put(path, headers, items, provider_items, progress);
}
+inline Result Client::Put(const std::string &path, const Headers &headers,
+ const std::string &body,
+ const std::string &content_type,
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
+ return cli_->Put(path, headers, body, content_type, content_receiver,
+ progress);
+}
+
inline Result Client::Patch(const std::string &path) {
return cli_->Patch(path);
}
-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);
+inline Result Client::Patch(const std::string &path, const Headers &headers) {
+ return cli_->Patch(path, headers);
}
inline Result Client::Patch(const std::string &path, const char *body,
size_t content_length,
const std::string &content_type,
- Progress progress) {
+ UploadProgress progress) {
return cli_->Patch(path, body, content_length, content_type, progress);
}
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);
-}
-inline Result Client::Patch(const std::string &path, const Headers &headers,
- const char *body, size_t content_length,
const std::string &content_type,
- Progress progress) {
+ UploadProgress progress) {
return cli_->Patch(path, headers, body, content_length, content_type,
progress);
}
inline Result Client::Patch(const std::string &path, const std::string &body,
- const std::string &content_type) {
- return cli_->Patch(path, body, content_type);
-}
-inline Result Client::Patch(const std::string &path, const std::string &body,
const std::string &content_type,
- Progress progress) {
+ UploadProgress progress) {
return cli_->Patch(path, body, content_type, progress);
}
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);
-}
-inline Result Client::Patch(const std::string &path, const Headers &headers,
- const std::string &body,
const std::string &content_type,
- Progress progress) {
+ UploadProgress progress) {
return cli_->Patch(path, headers, body, content_type, progress);
}
inline Result Client::Patch(const std::string &path, size_t content_length,
ContentProvider content_provider,
- const std::string &content_type) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Patch(path, content_length, std::move(content_provider),
- content_type);
+ content_type, progress);
}
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);
+ const std::string &content_type,
+ UploadProgress progress) {
+ return cli_->Patch(path, std::move(content_provider), content_type, progress);
}
inline Result Client::Patch(const std::string &path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
- const std::string &content_type) {
+ const std::string &content_type,
+ UploadProgress progress) {
return cli_->Patch(path, headers, content_length, std::move(content_provider),
- content_type);
+ content_type, progress);
}
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);
+ const std::string &content_type,
+ UploadProgress progress) {
+ return cli_->Patch(path, headers, std::move(content_provider), content_type,
+ progress);
}
-inline Result Client::Delete(const std::string &path) {
- return cli_->Delete(path);
+inline Result Client::Patch(const std::string &path, const Params ¶ms) {
+ return cli_->Patch(path, params);
}
-inline Result Client::Delete(const std::string &path, const Headers &headers) {
- return cli_->Delete(path, headers);
+inline Result Client::Patch(const std::string &path, const Headers &headers,
+ const Params ¶ms) {
+ return cli_->Patch(path, headers, params);
}
-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);
+inline Result Client::Patch(const std::string &path,
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ return cli_->Patch(path, items, progress);
+}
+inline Result Client::Patch(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ UploadProgress progress) {
+ return cli_->Patch(path, headers, items, progress);
+}
+inline Result Client::Patch(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ const std::string &boundary,
+ UploadProgress progress) {
+ return cli_->Patch(path, headers, items, boundary, progress);
+}
+inline Result Client::Patch(const std::string &path, const Headers &headers,
+ const UploadFormDataItems &items,
+ const FormDataProviderItems &provider_items,
+ UploadProgress progress) {
+ return cli_->Patch(path, headers, items, provider_items, progress);
+}
+inline Result Client::Patch(const std::string &path, const Headers &headers,
+ const std::string &body,
+ const std::string &content_type,
+ ContentReceiver content_receiver,
+ DownloadProgress progress) {
+ return cli_->Patch(path, headers, body, content_type, content_receiver,
+ progress);
+}
+
+inline Result Client::Delete(const std::string &path,
+ DownloadProgress progress) {
+ return cli_->Delete(path, progress);
+}
+inline Result Client::Delete(const std::string &path, const Headers &headers,
+ DownloadProgress progress) {
+ return cli_->Delete(path, headers, progress);
}
inline Result Client::Delete(const std::string &path, const char *body,
size_t content_length,
const std::string &content_type,
- Progress progress) {
+ DownloadProgress progress) {
return cli_->Delete(path, body, content_length, content_type, progress);
}
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);
-}
-inline Result Client::Delete(const std::string &path, const Headers &headers,
- const char *body, size_t content_length,
const std::string &content_type,
- Progress progress) {
+ DownloadProgress progress) {
return cli_->Delete(path, headers, body, content_length, content_type,
progress);
}
inline Result Client::Delete(const std::string &path, const std::string &body,
- const std::string &content_type) {
- return cli_->Delete(path, body, content_type);
-}
-inline Result Client::Delete(const std::string &path, const std::string &body,
const std::string &content_type,
- Progress progress) {
+ DownloadProgress progress) {
return cli_->Delete(path, body, content_type, progress);
}
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);
-}
-inline Result Client::Delete(const std::string &path, const Headers &headers,
- const std::string &body,
const std::string &content_type,
- Progress progress) {
+ DownloadProgress progress) {
return cli_->Delete(path, headers, body, content_type, progress);
}
+inline Result Client::Delete(const std::string &path, const Params ¶ms,
+ DownloadProgress progress) {
+ return cli_->Delete(path, params, progress);
+}
+inline Result Client::Delete(const std::string &path, const Headers &headers,
+ const Params ¶ms, DownloadProgress progress) {
+ return cli_->Delete(path, headers, params, progress);
+}
+
inline Result Client::Options(const std::string &path) {
return cli_->Options(path);
}
@@ -10307,7 +11786,12 @@
cli_->set_follow_location(on);
}
-inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); }
+inline void Client::set_path_encode(bool on) { cli_->set_path_encode(on); }
+
+[[deprecated("Use set_path_encode instead")]]
+inline void Client::set_url_encode(bool on) {
+ cli_->set_path_encode(on);
+}
inline void Client::set_compress(bool on) { cli_->set_compress(on); }
@@ -10353,6 +11837,10 @@
cli_->set_logger(std::move(logger));
}
+inline void Client::set_error_logger(ErrorLogger error_logger) {
+ cli_->set_error_logger(std::move(error_logger));
+}
+
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path) {
@@ -10388,8 +11876,4 @@
} // namespace httplib
-#ifdef _WIN32
-#undef poll
-#endif
-
#endif // CPPHTTPLIB_HTTPLIB_H
diff --git a/util/net/http_transport_test_server.cc b/util/net/http_transport_test_server.cc
index b392d88..e4df221 100644
--- a/util/net/http_transport_test_server.cc
+++ b/util/net/http_transport_test_server.cc
@@ -96,7 +96,7 @@
std::string boundary;
httplib::detail::parse_multipart_boundary(
req.get_header_value("Content-Type"), boundary);
- for (const auto& part : req.files) {
+ for (const auto& part : req.form.fields) {
to_stdout += "--" + boundary + "\r\n";
to_stdout += "Content-Disposition: form-data; name=\"" +
part.first + "\"\r\n\r\n";