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:
yhirose
2026-10-08 00:40:16 -04:00
parent f6aee694e2
commit 05f7478523
3 changed files with 66 additions and 56 deletions

View File

@@ -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.