Repository navigation
What's Changed
Behavior changes
Several changes below alter behavior that existing code may rely on:
- Server: an exception handler that sets no status now answers 500 (it used to send 200 or the status the throwing handler had set).
set_error_handler()therefore runs for it too. On a WebSocket upgrade, an exception from the pre-routing handler, the pre-request handler or the sub-protocol selector now goes through the exception handler and is answered over HTTP, where it used to drop the connection. - Server:
Rangeis honored only on GET and HEAD. Other methods get the whole response. - Server: a request whose
Transfer-Encodinglists a coding besides a singlechunkedis answered with 501 and the connection is closed. A list whose final coding is notchunkedstill gets 400. - Server: after answering a request whose body the handler left unread, the server skips at most
CPPHTTPLIB_UNREAD_PAYLOAD_MAX_LENGTH(256KB by default, 0 for no limit) within the read timeout, and closes the connection otherwise. - Server: a mount point serves no path with a segment ending in
.or a space, on every platform. On Windows it also refuses a path containing:. - Server:
set_file_extension_and_mimetype_mapping()lowercases the extension and drops a leading.when it is set, so".pdf"and"PDF"both match.pdfand.PDFfiles. - Server: chunked transfer coding counts the bytes around chunk data (size lines, extensions, CRLF) against a bound. Each chunk may spend 16 bytes plus twice its data, and the excess may build up to
CPPHTTPLIB_HEADER_MAX_LENGTH. This applies to request bodies on the server and response bodies on the client. - Server: invalid response header fields are dropped from
res.headersbefore any header writer runs, so a writer set withset_header_writer()no longer sees them. - Server:
set_trusted_proxies()matches addresses as IP addresses, so an IPv4-mapped IPv6 peer matches the IPv4 address listed. - Client: with
set_follow_location(true), only 301, 302, 303, 307 and 308 are followed. - Client: a path given to a plain HTTP proxy must start with
/. Otherwise the request fails withError::Writebefore anything is written. This includesOPTIONS *. - Client: after
set_*changes a TLS setting (certificate or host name verification, a CA store, a verifier), a connection kept alive under the old settings is closed instead of reused. - Client: a response stream honors
set_decompress(false). - All TLS backends: a host name is matched against subjectAltName only (RFC 9525). A certificate that names its host only in the Common Name is rejected.
SSEClient::last_event_id()returnsstd::stringinstead ofconst std::string &. Code that copies the result or binds it to a const reference still compiles.set_hostname_addr_map()is deprecated in favor ofset_direct_hostname_addr_map(), which behaves the same.
New features
Server::set_compress()turns off compression of handler responses, buffered or streamed. Compression stays on by default, and static file compression keeps its own setting. Turning it off over HTTPS avoids BREACH-style leaks.set_tunnel_hostname_addr_map()onSSLClient,ClientandWebSocketClientsends a CONNECT tunnel to another address than the origin hostname. The Host header, the SNI and the certificate check inside the tunnel keep the original hostname. It corresponds to curl's--connect-to, andset_direct_hostname_addr_map()to--resolve.WebSocketClient::set_basic_auth()andset_bearer_token_auth()(#2652).
Security-relevant fixes
- Request parsing and message framing:
- Read no body from a request without
Content-Lengthor chunkedTransfer-Encodingin builds without TLS too. They used to read what followed up to EOF on a connection about to close. - Bound the bytes around chunk data in chunked transfer coding, as described above.
- Bound how much of an unread request body the server skips, as described above. A slow sender could hold a worker for a long time.
- Answer 501 to a transfer coding besides
chunked, as described above.
- Read no body from a request without
- Static files:
- Refuse a path segment ending in
.or a space, and a:on Windows. Windows resolves such names to another file, so a file could be reached under a spelling that a check in the pre-routing handler did not match. - Treat
:as a path separator insanitize_filename()on Windows (#2651).
- Refuse a path segment ending in
- Proxy:
- Refuse a proxied path that does not start with a slash (#2638).
- Match trusted proxies as IP addresses, as described above.
- TLS:
- Match a host name against subjectAltName only, on every backend.
- Honor Keychain trust settings when loading macOS system CAs (#2641). A root set to "Never Trust", or trusted only for other purposes, used to verify TLS servers.
- Reject an EOF without
close_notifyin the Mbed TLS and wolfSSL read (#2649). A response delimited by the connection close could be cut short and still come back complete. - Reconnect after a TLS setting changes on a kept-alive client, as described above. A connection kept under the old settings stayed unverified if verification had just been turned on.
- Mbed TLS: parse a CA store PEM in
tls::create_ca_store()through a NUL-terminated copy. It used to read one byte past the buffer.
- Response headers:
- Drop invalid response header fields before any header writer, as described above.
Bug fixes
- Client: open a new connection for a redirect or an auth retry after sending
Connection: close(#2645). The next request was written to a connection the server was closing and failed with "Failed to read connection". - Client: remove
.and..segments from absolute-path and absolute-URI redirect locations (#2648), as RFC 3986 section 5.2.2 requires. - Client: honor
set_decompress(false)inopen_stream()andstream::Get()(#2640). - Finish short writes in
write_websocket_frameand the request body write ofopen_stream_()(#2642). A short write dropped the rest of the frame or body. SSEClient: guardlast_event_id()against the async event loop (#2644). A read from another thread raced the writes.- Server:
RequestWithoutContentLengthOrTransferEncodingnow runs on Windows too.
Documentation
- README: document
set_trusted_proxies(),set_compress()and the TLS settings of a plain HTTP redirect hop (they pass only from one HTTPS hop to the next). Mount point path rules are updated.
Full Changelog: v0.63.0...v0.64.0