mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-09 12:53:18 +00:00
Fix README samples and statements that no longer match the code
Samples that did not compile or did not do what they showed:
- SSLServer has no default constructor, and SSLClient("host:port") does
not parse the port.
- res.user_data.get<T>() inside a generic lambda needs the `template`
keyword; use explicit parameter types instead.
- get_header_value() takes the default value before the index.
- Server has no socket(); show set_socket_opt inside set_socket_options.
- The content provider sample used a local without capturing it.
- "*.dev.local" is not a NO_PROXY pattern; ".dev.local" is.
- The reverse proxy example in README-stream.md called open_stream()
without a method and moved a StreamHandle into a std::function. Keep
the client and the handle in one shared object.
- open_stream() does not follow redirects, so drop set_follow_location()
from the Stream API samples.
Statements corrected:
- ssl_error() returns a tls::ErrorCode, not SSL_ERROR_*.
- An exception escaping the exception handler no longer crashes the
server; the connection is dropped and the error logger is told.
- post_routing_handler runs before every response, including those the
earlier hooks short-circuit; file_request_handler runs for GET only.
- Only wolfSSL cannot enumerate CAs loaded from a path or the system.
- Digest authentication and the WebSocket TLS setters need any TLS
backend, not OpenSSL specifically.
- A dynamic pool thread exits on the idle timeout only.
- The stream connection closes when the Result is destroyed.
- The pong timeout takes two to three ping intervals to fire.
- Regex routes skip paths over CPPHTTPLIB_REGEX_ROUTE_PATH_MAX_LENGTH.
Also list the Error values and compressible MIME types that were
missing, note that a content receiver lifts the default client payload
limit, and update the docker server's startup output.
This commit is contained in:
@@ -4,7 +4,7 @@ This document describes the streaming extensions for cpp-httplib, providing an i
|
||||
|
||||
> **Important Notes**:
|
||||
>
|
||||
> - **No Keep-Alive**: Each `stream::Get()` call uses a dedicated connection that is closed after the response is fully read. For connection reuse, use `Client::Get()`.
|
||||
> - **No Keep-Alive**: Each `stream::Get()` call uses a dedicated connection that is closed when the returned `stream::Result` is destroyed. For connection reuse, use `Client::Get()`.
|
||||
> - **Single iteration only**: The `next()` method can only iterate through the body once.
|
||||
> - **Result is not thread-safe**: While `stream::Get()` can be called from multiple threads simultaneously, the returned `stream::Result` must be used from a single thread only.
|
||||
|
||||
@@ -114,7 +114,6 @@ The `httplib.h` header provides a more ergonomic iterator-style API.
|
||||
#include "httplib.h"
|
||||
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
cli.set_follow_location(true);
|
||||
...
|
||||
|
||||
// Simple GET
|
||||
@@ -243,23 +242,29 @@ int main() {
|
||||
```cpp
|
||||
#include "httplib.h"
|
||||
|
||||
// Keeps the client and its stream alive until the response has been sent.
|
||||
struct Upstream {
|
||||
httplib::Client cli{"http://backend:8080"};
|
||||
httplib::ClientImpl::StreamHandle handle;
|
||||
};
|
||||
|
||||
httplib::Server svr;
|
||||
|
||||
svr.Get("/proxy/(.*)", [](const httplib::Request& req, httplib::Response& res) {
|
||||
httplib::Client upstream("http://backend:8080");
|
||||
auto handle = upstream.open_stream("/" + req.matches[1].str());
|
||||
|
||||
if (!handle.is_valid()) {
|
||||
auto upstream = std::make_shared<Upstream>();
|
||||
upstream->handle = upstream->cli.open_stream("GET", "/" + req.matches[1].str());
|
||||
|
||||
if (!upstream->handle.is_valid()) {
|
||||
res.status = 502;
|
||||
return;
|
||||
}
|
||||
|
||||
res.status = handle.response->status;
|
||||
|
||||
res.status = upstream->handle.response->status;
|
||||
res.set_chunked_content_provider(
|
||||
handle.response->get_header_value("Content-Type"),
|
||||
[handle = std::move(handle)](size_t, httplib::DataSink& sink) mutable {
|
||||
upstream->handle.response->get_header_value("Content-Type"),
|
||||
[upstream](size_t, httplib::DataSink& sink) {
|
||||
char buf[8192];
|
||||
auto n = handle.read(buf, sizeof(buf));
|
||||
auto n = upstream->handle.read(buf, sizeof(buf));
|
||||
if (n > 0) {
|
||||
sink.write(buf, static_cast<size_t>(n));
|
||||
return true;
|
||||
|
||||
@@ -151,9 +151,8 @@ bool is_open() const;
|
||||
explicit WebSocketClient(const std::string &scheme_host_port_path,
|
||||
const Headers &headers = {});
|
||||
|
||||
// Constructor with a client certificate for mutual TLS (wss:// only,
|
||||
// requires CPPHTTPLIB_OPENSSL_SUPPORT). The certificate is ignored for
|
||||
// ws:// URLs.
|
||||
// Constructor with a client certificate for mutual TLS (wss:// only, SSL
|
||||
// builds only). The certificate is ignored for ws:// URLs.
|
||||
struct PemMemory {
|
||||
const char *cert_pem;
|
||||
size_t cert_pem_len;
|
||||
@@ -199,7 +198,7 @@ void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
|
||||
template <class Rep, class Period>
|
||||
void set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
|
||||
|
||||
// SSL configuration (wss:// only, requires CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
// SSL configuration (wss:// only, SSL builds only)
|
||||
void set_ca_cert_path(const std::string &ca_cert_file_path,
|
||||
const std::string &ca_cert_dir_path = std::string());
|
||||
void set_ca_cert_store(tls::ca_store_t store);
|
||||
@@ -473,7 +472,7 @@ ws.set_websocket_max_missed_pongs(2); // close after 2 consecutive unacked pings
|
||||
|
||||
The server side has the same `set_websocket_max_missed_pongs()`.
|
||||
|
||||
With the default ping interval of 30 seconds, `max_missed_pongs = 2` detects a dead peer within ~60 seconds. The counter is reset every time a Pong frame is received, so the mechanism only works when your code is actively calling `read()` — exactly the pattern a normal WebSocket client already uses.
|
||||
With the default ping interval of 30 seconds, `max_missed_pongs = 2` detects a dead peer 60 to 90 seconds after it stops answering: the count is checked once per interval, just before the next ping goes out. A `read()` waiting on the peer at that moment returns `Fail`. The counter is reset every time a Pong frame is received, so the mechanism only works when your code is actively calling `read()`, which is exactly the pattern a normal WebSocket client already uses.
|
||||
|
||||
**The default is `0`**, which means "never close the connection because of missing pongs." Pings are still sent on the heartbeat interval, but their responses are not checked. On the server side a dead connection still does not linger: while a handler is inside `read()`, `CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND` (default **300 seconds = 5 minutes**) acts as a backstop. A client has no such backstop — it waits forever unless you set a read timeout — so there `max_missed_pongs` is what notices an unresponsive peer at all. On either side it is also the knob for noticing one *faster* than the 5-minute fallback.
|
||||
|
||||
|
||||
86
README.md
86
README.md
@@ -33,7 +33,7 @@ Learn more in the [official documentation](https://yhirose.github.io/cpp-httplib
|
||||
httplib::Server svr;
|
||||
|
||||
// HTTPS
|
||||
httplib::SSLServer svr;
|
||||
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||
|
||||
svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
@@ -71,7 +71,7 @@ cpp-httplib supports multiple TLS backends through an abstraction layer:
|
||||
| wolfSSL | `CPPHTTPLIB_WOLFSSL_SUPPORT` | `libwolfssl` | 5.x supported; must build with `--enable-opensslall` |
|
||||
|
||||
> [!NOTE]
|
||||
> **Mbed TLS / wolfSSL limitation:** `get_ca_certs()` and `get_ca_names()` only reflect CA certificates loaded via `load_ca_cert_store()`. Certificates loaded through `set_ca_cert_path()` or system certificates (`load_system_certs`) are not enumerable.
|
||||
> **wolfSSL limitation:** `get_ca_certs()` and `get_ca_names()` only reflect CA certificates loaded via `load_ca_cert_store()`. Certificates loaded through `set_ca_cert_path()` or system certificates (`load_system_certs`) are not enumerable.
|
||||
|
||||
> [!NOTE]
|
||||
> **BoringSSL (best-effort):** BoringSSL builds under `CPPHTTPLIB_OPENSSL_SUPPORT` and is exercised by CI against current upstream. Because BoringSSL does not guarantee API stability, support is best-effort — breakage may occasionally land. Two known behavioral differences vs OpenSSL: (1) BoringSSL's public headers require C++14 or later, so consumers must compile accordingly; (2) hostname verification is SAN-only per RFC 6125 §6.4.4 (no CN fallback).
|
||||
@@ -86,7 +86,7 @@ httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||
|
||||
// Client
|
||||
httplib::Client cli("https://localhost:1234"); // scheme + host
|
||||
httplib::SSLClient cli("localhost:1234"); // host
|
||||
httplib::SSLClient cli("localhost"); // host (port 443)
|
||||
httplib::SSLClient cli("localhost", 1234); // host, port
|
||||
|
||||
// Use your CA bundle
|
||||
@@ -103,8 +103,8 @@ cli.enable_server_hostname_verification(false);
|
||||
|
||||
When SSL operations fail, cpp-httplib provides detailed error information through `ssl_error()` and `ssl_backend_error()`:
|
||||
|
||||
- `ssl_error()` - Returns the TLS-level error code (e.g., `SSL_ERROR_SSL` for OpenSSL)
|
||||
- `ssl_backend_error()` - Returns the backend-specific error code (e.g., `ERR_get_error()` for OpenSSL/wolfSSL, return value for Mbed TLS)
|
||||
- `ssl_error()` - Returns the TLS-level error as a backend-independent `httplib::tls::ErrorCode` value (e.g., `Fatal`, `CertVerifyFailed`), cast to `int`
|
||||
- `ssl_backend_error()` - Returns the backend-specific error code (e.g., `ERR_get_error()` for OpenSSL/wolfSSL, return value for Mbed TLS). With OpenSSL, a certificate verification failure reports the verify result (`X509_V_ERR_*`) here instead
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT // or CPPHTTPLIB_MBEDTLS_SUPPORT or CPPHTTPLIB_WOLFSSL_SUPPORT
|
||||
@@ -337,7 +337,7 @@ Note the following:
|
||||
* The method name must be a valid HTTP method token (RFC 9110) and must be registered before `listen()` is called.
|
||||
* `GET`, `HEAD`, `POST`, `PUT`, `DELETE`, `CONNECT`, `OPTIONS`, `TRACE`, `PATCH` and `PRI` cannot be registered this way. Use the dedicated methods above instead.
|
||||
* A rejected registration makes `is_valid()` return `false`, and `listen()` then fails rather than starting a server with a route that would never fire.
|
||||
* Static file serving and WebSocket upgrades remain `GET`/`HEAD` only.
|
||||
* Static file serving remains `GET`/`HEAD` only, and WebSocket upgrades `GET` only.
|
||||
* `Allow` and the WebDAV `DAV:` header are not generated automatically. Register an `Options` handler if clients need them.
|
||||
|
||||
### Bind a socket to multiple interfaces and any available port
|
||||
@@ -518,7 +518,7 @@ svr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep)
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> if you don't provide the `catch (...)` block for a rethrown exception pointer, an uncaught exception will end up causing the server crash. Be careful!
|
||||
> If you don't provide the `catch (...)` block for a rethrown exception pointer, an exception that is not a `std::exception` escapes the handler. The server keeps running, but that connection is dropped without a response, and the error logger receives `Error::UserCallbackException`.
|
||||
|
||||
### Pre routing handler
|
||||
|
||||
@@ -569,21 +569,21 @@ svr.set_pre_request_handler([](const auto& req, auto& res) {
|
||||
Request received
|
||||
│
|
||||
├─ expect_100_continue_handler (when the request has "Expect: 100-continue")
|
||||
│ └─ returns a status other than 100 → stop here
|
||||
│ └─ returns a status other than 100 → go straight to post_routing_handler
|
||||
│
|
||||
├─ pre_routing_handler route not matched yet, body not read
|
||||
│ └─ returns Handled → stop here
|
||||
│ └─ returns Handled → go straight to post_routing_handler
|
||||
│
|
||||
├─ file_request_handler (GET/HEAD, static file serving)
|
||||
├─ file_request_handler when a static file is served (GET only)
|
||||
│
|
||||
├─ route matching → req.matched_route is set
|
||||
│
|
||||
├─ pre_request_handler route matched, body NOT read yet
|
||||
│ └─ returns Handled → stop here (route handler is skipped)
|
||||
│ └─ returns Handled → go straight to post_routing_handler
|
||||
│
|
||||
├─ route handler Get/Post/...; the request body is read first
|
||||
│
|
||||
└─ post_routing_handler after routing completes
|
||||
└─ post_routing_handler just before the response is written
|
||||
|
||||
On a thrown exception → exception_handler
|
||||
On an error status (4xx/5xx) → error_handler
|
||||
@@ -611,7 +611,7 @@ svr.set_pre_routing_handler([](const auto& req, auto& res) {
|
||||
return Server::HandlerResponse::Unhandled;
|
||||
});
|
||||
|
||||
svr.Get("/me", [](const auto& /*req*/, auto& res) {
|
||||
svr.Get("/me", [](const Request& /*req*/, Response& res) {
|
||||
auto* ctx = res.user_data.get<AuthContext>("auth");
|
||||
if (!ctx) {
|
||||
res.status = StatusCode::Unauthorized_401;
|
||||
@@ -908,7 +908,7 @@ Please see [Server example](https://github.com/yhirose/cpp-httplib/blob/master/e
|
||||
|
||||
`ThreadPool` is used as the **default** task queue, with dynamic scaling support. By default, it maintains a base thread count of 8 or `std::thread::hardware_concurrency() - 1` (whichever is greater), and can scale up to 4x that count under load. You can change these with `CPPHTTPLIB_THREAD_POOL_COUNT` and `CPPHTTPLIB_THREAD_POOL_MAX_COUNT`.
|
||||
|
||||
When all threads are busy and a new task arrives, a temporary thread is spawned (up to the maximum). When a dynamic thread finishes its task and the queue is empty, or after an idle timeout, it exits automatically. The idle timeout defaults to 3 seconds, configurable via `CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT`.
|
||||
When all threads are busy and a new task arrives, a temporary thread is spawned (up to the maximum). A dynamic thread exits automatically once it has waited for the idle timeout without receiving a task. The idle timeout defaults to 3 seconds, configurable via `CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT`.
|
||||
|
||||
If you want to set the thread counts at runtime:
|
||||
|
||||
@@ -1042,6 +1042,8 @@ enum class Error {
|
||||
HTTPParsing,
|
||||
InvalidRangeHeader,
|
||||
UnsupportedContentEncoding,
|
||||
WebSocketHandshake,
|
||||
UserCallbackException,
|
||||
};
|
||||
```
|
||||
|
||||
@@ -1257,7 +1259,7 @@ std::string body = ...;
|
||||
|
||||
auto res = cli.Post(
|
||||
"/stream", body.size(),
|
||||
[](size_t offset, size_t length, DataSink &sink) {
|
||||
[&](size_t offset, size_t length, DataSink &sink) {
|
||||
sink.write(body.data() + offset, length);
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
},
|
||||
@@ -1310,7 +1312,7 @@ cli.set_bearer_token_auth("token");
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> OpenSSL is required for Digest Authentication.
|
||||
> A TLS backend (OpenSSL, Mbed TLS, or wolfSSL) is required for Digest Authentication.
|
||||
|
||||
### Proxy server support
|
||||
|
||||
@@ -1328,12 +1330,12 @@ cli.set_proxy_bearer_token_auth("pass");
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> OpenSSL is required for Digest Authentication.
|
||||
> A TLS backend (OpenSSL, Mbed TLS, or wolfSSL) is required for Digest Authentication.
|
||||
|
||||
#### Bypass the proxy for specific hosts (`NO_PROXY`)
|
||||
|
||||
```cpp
|
||||
cli.set_no_proxy({"internal.corp", "10.0.0.0/8", "*.dev.local"});
|
||||
cli.set_no_proxy({"internal.corp", "10.0.0.0/8", ".dev.local"});
|
||||
```
|
||||
|
||||
Each pattern is `*`, a hostname suffix, an IP literal, or a CIDR block.
|
||||
@@ -1478,8 +1480,8 @@ for (auto it = req.headers.equal_range("Accept-Encoding").first;
|
||||
std::cout << it->second << std::endl;
|
||||
}
|
||||
|
||||
// get_header_value(key, id) reaches a specific one directly.
|
||||
auto second = req.get_header_value("Accept-Encoding", 1); // "br"
|
||||
// get_header_value(key, def, id) reaches a specific one directly.
|
||||
auto second = req.get_header_value("Accept-Encoding", "", 1); // "br"
|
||||
```
|
||||
|
||||
`Headers` matches field names case-insensitively, as before. `Params`, `FormFields`, and `FormFiles` are case-sensitive.
|
||||
@@ -1489,7 +1491,7 @@ auto second = req.get_header_value("Accept-Encoding", 1); // "br"
|
||||
|
||||
## Payload Limit
|
||||
|
||||
The maximum payload body size is limited to 100MB by default for both server and client. You can change it with `set_payload_max_length()` or by defining `CPPHTTPLIB_PAYLOAD_MAX_LENGTH` at compile time. Setting it to `0` disables the limit entirely.
|
||||
The maximum payload body size is limited to 100MB by default for both server and client. You can change it with `set_payload_max_length()` or by defining `CPPHTTPLIB_PAYLOAD_MAX_LENGTH` at compile time. Setting it to `0` disables the limit entirely. On the client, a request made with a content receiver is not limited unless you call `set_payload_max_length()` yourself, so that a large download can be streamed without raising the limit first.
|
||||
|
||||
## Compression
|
||||
|
||||
@@ -1498,10 +1500,15 @@ The server can apply compression to the following MIME type contents:
|
||||
- all text types except text/event-stream
|
||||
- image/svg+xml
|
||||
- application/javascript
|
||||
- application/x-javascript
|
||||
- application/json
|
||||
- application/ld+json
|
||||
- application/xml
|
||||
- application/protobuf
|
||||
- application/xhtml+xml
|
||||
- application/rss+xml
|
||||
- application/atom+xml
|
||||
- application/xslt+xml
|
||||
- application/protobuf
|
||||
|
||||
A response that already carries `Content-Encoding` is sent as it is. A handler serving content it encoded itself, an asset compressed at build time for instance, keeps its own coding and its own bytes:
|
||||
|
||||
@@ -1638,7 +1645,6 @@ Process large responses without loading everything into memory.
|
||||
|
||||
```c++
|
||||
httplib::Client cli("localhost", 8080);
|
||||
cli.set_follow_location(true);
|
||||
...
|
||||
|
||||
auto result = httplib::stream::Get(cli, "/large-file");
|
||||
@@ -1718,7 +1724,7 @@ SSL is also supported via `wss://` scheme (e.g. `WebSocketClient("wss://example.
|
||||
|
||||
> **WebSocket extensions are not supported.** `permessage-deflate` and other RFC 6455 extensions are not implemented. If a client proposes them via `Sec-WebSocket-Extensions`, the server silently declines them in its handshake response.
|
||||
|
||||
> **Unresponsive-peer detection.** Heartbeat pings also serve as a liveness probe when `set_websocket_max_missed_pongs(n)` is set: if the client sends `n` consecutive pings without receiving a pong, it will close the connection. Disabled by default (`0`).
|
||||
> **Unresponsive-peer detection.** Heartbeat pings also serve as a liveness probe when `set_websocket_max_missed_pongs(n)` is set: once `n` consecutive pings have gone unanswered, the connection is closed and a `read()` waiting on it fails. Both `WebSocketClient` and `Server` have the setter. Disabled by default (`0`).
|
||||
|
||||
See [README-websocket.md](README-websocket.md) for more details.
|
||||
|
||||
@@ -1727,12 +1733,14 @@ See [README-websocket.md](README-websocket.md) for more details.
|
||||
`set_socket_opt` is a convenience wrapper around `setsockopt` for setting integer socket options:
|
||||
|
||||
```cpp
|
||||
auto sock = svr.socket();
|
||||
httplib::set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
|
||||
svr.set_socket_options([](socket_t sock) {
|
||||
httplib::default_socket_options(sock);
|
||||
httplib::set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
|
||||
});
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> For most use cases, prefer `set_tcp_nodelay(true)` or `set_socket_options(callback)` on the Server/Client instead of calling `set_socket_opt` directly.
|
||||
> `set_socket_options` replaces the default socket options, so call `default_socket_options()` in the callback to keep them. For `TCP_NODELAY`, `set_tcp_nodelay(true)` is simpler.
|
||||
|
||||
## Split httplib.h into .h and .cc
|
||||
|
||||
@@ -1761,7 +1769,9 @@ Dockerfile for static HTTP server is available. Port number of this HTTP server
|
||||
...
|
||||
|
||||
> docker run --rm -it -p 8080:80 -v ./docker/html:/html cpp-httplib-server
|
||||
Serving HTTP on 0.0.0.0 port 80 ...
|
||||
Serving HTTP on 0.0.0.0:80
|
||||
Mount point: / -> ./html
|
||||
Press Ctrl+C to shutdown gracefully...
|
||||
192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] "GET / HTTP/1.1" 200 599 "-" "curl/8.7.1"
|
||||
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET / HTTP/1.1" 200 599 "-" "Mozilla/5.0 ..."
|
||||
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET /favicon.ico HTTP/1.1" 404 152 "-" "Mozilla/5.0 ..."
|
||||
@@ -1771,7 +1781,9 @@ From Docker Hub
|
||||
|
||||
```bash
|
||||
> docker run --rm -it -p 8080:80 -v ./docker/html:/html yhirose4dockerhub/cpp-httplib-server
|
||||
Serving HTTP on 0.0.0.0 port 80 ...
|
||||
Serving HTTP on 0.0.0.0:80
|
||||
Mount point: / -> ./html
|
||||
Press Ctrl+C to shutdown gracefully...
|
||||
192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] "GET / HTTP/1.1" 200 599 "-" "curl/8.7.1"
|
||||
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET / HTTP/1.1" 200 599 "-" "Mozilla/5.0 ..."
|
||||
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET /favicon.ico HTTP/1.1" 404 152 "-" "Mozilla/5.0 ..."
|
||||
@@ -1783,19 +1795,13 @@ 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.
|
||||
>
|
||||
> `std::regex` implementations can overflow the stack while matching a long input, even with a pattern as simple as `.*`. To keep a request from triggering this, a regex route is never applied to a path longer than `CPPHTTPLIB_REGEX_ROUTE_PATH_MAX_LENGTH` (256 by default); such a path does not match the route. Raising the limit brings the risk back with it.
|
||||
>
|
||||
> Path parameters are matched without `std::regex` and have no such limit, so prefer them where they fit:
|
||||
>
|
||||
> ```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
|
||||
> svr.Get(R"(/api/v\d+/.*)", handler); // Regex route, limited to short paths
|
||||
> ```
|
||||
|
||||
### g++
|
||||
|
||||
Reference in New Issue
Block a user