Skip to content

v0.64.0

Latest

Choose a tag to compare

@yhirose yhirose released this 29 Oct 21:11

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: Range is honored only on GET and HEAD. Other methods get the whole response.
  • Server: a request whose Transfer-Encoding lists a coding besides a single chunked is answered with 501 and the connection is closed. A list whose final coding is not chunked still 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 .pdf and .PDF files.
  • 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.headers before any header writer runs, so a writer set with set_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 with Error::Write before anything is written. This includes OPTIONS *.
  • 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() returns std::string instead of const std::string &. Code that copies the result or binds it to a const reference still compiles.
  • set_hostname_addr_map() is deprecated in favor of set_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() on SSLClient, Client and WebSocketClient sends 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, and set_direct_hostname_addr_map() to --resolve.
  • WebSocketClient::set_basic_auth() and set_bearer_token_auth() (#2652).

Security-relevant fixes

  • Request parsing and message framing:
    • Read no body from a request without Content-Length or chunked Transfer-Encoding in 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.
  • 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 in sanitize_filename() on Windows (#2651).
  • 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_notify in 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) in open_stream() and stream::Get() (#2640).
  • Finish short writes in write_websocket_frame and the request body write of open_stream_() (#2642). A short write dropped the rest of the frame or body.
  • SSEClient: guard last_event_id() against the async event loop (#2644). A read from another thread raced the writes.
  • Server: RequestWithoutContentLengthOrTransferEncoding now 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