Debian Package Tracker
Register | Log in
Subscribe

cpp-httplib

Choose email to subscribe with

general
  • source: cpp-httplib (main)
  • version: 0.41.0+ds-3
  • maintainer: Andrea Pappacoda (DMD) (LowNMU)
  • arch: any
  • std-ver: 4.7.4
  • VCS: Git (Browse, QA)
versions [more versions can be listed by madison] [old versions available from snapshot.debian.org]
[pool directory]
  • oldstable: 0.11.4+ds-1+deb12u1
  • stable: 0.18.7-1+deb13u1
  • stable-sec: 0.18.7-1+deb13u1
  • stable-bpo: 0.41.0+ds-3~bpo13+1
  • testing: 0.41.0+ds-3
  • unstable: 0.41.0+ds-3
versioned links
  • 0.11.4+ds-1+deb12u1: [.dsc, use dget on this link to retrieve source package] [changelog] [copyright] [rules] [control]
  • 0.18.7-1+deb13u1: [.dsc, use dget on this link to retrieve source package] [changelog] [copyright] [rules] [control]
  • 0.41.0+ds-3~bpo13+1: [.dsc, use dget on this link to retrieve source package] [changelog] [copyright] [rules] [control]
  • 0.41.0+ds-3: [.dsc, use dget on this link to retrieve source package] [changelog] [copyright] [rules] [control]
binaries
  • libcpp-httplib-dev
  • libcpp-httplib0.41
action needed
A new upstream version is available: 0.64.0 high
A new upstream version 0.64.0 is available, you should consider packaging it.
Created: 2025-11-27 Last update: 2026-11-07 11:00
19 security issues in trixie high

There are 19 open security issues in trixie.

5 important issues:
  • CVE-2025-66570: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.27.0, a vulnerability allows attacker-controlled HTTP headers to influence server-visible metadata, logging, and authorization decisions. An attacker can inject headers named REMOTE_ADDR, REMOTE_PORT, LOCAL_ADDR, LOCAL_PORT that are parsed into the request header multimap via read_headers() in httplib.h (headers.emplace), then the server later appends its own internal metadata using the same header names in Server::process_request without erasing duplicates. Because Request::get_header_value returns the first entry for a header key (id == 0) and the client-supplied headers are parsed before server-inserted headers, downstream code that uses these header names may inadvertently use attacker-controlled values. Affected files/locations: cpp-httplib/httplib.h (read_headers, Server::process_request, Request::get_header_value, get_header_value_u64) and cpp-httplib/docker/main.cc (get_client_ip, nginx_access_logger, nginx_error_logger). Attack surface: attacker-controlled HTTP headers in incoming requests flow into the Request.headers multimap and into logging code that reads forwarded headers, enabling IP spoofing, log poisoning, and authorization bypass via header shadowing. This vulnerability is fixed in 0.27.0.
  • CVE-2025-66577: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.27.0, a vulnerability allows attacker-controlled HTTP headers to influence server-visible metadata, logging, and authorization decisions. An attacker can supply X-Forwarded-For or X-Real-IP headers which get accepted unconditionally by get_client_ip() in docker/main.cc, causing access and error logs (nginx_access_logger / nginx_error_logger) to record spoofed client IPs (log poisoning / audit evasion). This vulnerability is fixed in 0.27.0.
  • CVE-2026-21428: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to version 0.30.0, the ``write_headers`` function does not check for CR & LF characters in user supplied headers, allowing untrusted header value to escape header lines. This vulnerability allows attackers to add extra headers, modify request body unexpectedly & trigger an SSRF attack. When combined with a server that supports http1.1 pipelining (springboot, python twisted etc), this can be used for server side request forgery (SSRF). Version 0.30.0 fixes this issue.
  • CVE-2026-102944: cpp-httplib is a C++ header-only HTTP/HTTPS library. Prior to 0.51.0, create_redirect_client() removes Host, Proxy-Authorization, and Authorization headers when following a cross-origin 301, 302, 307, or 308 redirect but leaves Cookie headers attached. detail::redirect() copies the request for those redirect codes, so cookies supplied directly or through default_headers_ are sent to the redirect target. An attacker who controls a redirect response, or a network position for a non-HTTPS origin, can receive session cookies intended for the original origin. A 303 redirect is not affected because that path clears all request headers. This issue is fixed in version 0.51.0.
  • CVE-2026-103930: cpp-httplib is a C++ header-only HTTP/HTTPS library. From 0.29.0 until 0.51.0, Client::open_stream in httplib.h accepts a caller-controlled path that reaches detail::write_request_line() without the detail::encode_path() protection used by the ordinary buffered request path. Raw carriage-return and line-feed bytes in the path can therefore split the outbound HTTP/1.1 request line and inject attacker-shaped request metadata. Exploitation requires an embedding application to pass remote user input into the open_stream path and a backend or intermediary that interprets the injected bytes; the established impact is modification of backend-visible request data, not direct confidentiality or availability impact. This issue is fixed in version 0.51.0.
14 issues left for the package maintainer to handle:
  • CVE-2025-53628: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.20.1, cpp-httplib does not have a limit for a unique line, permitting an attacker to explore this to allocate memory arbitrarily. This vulnerability is fixed in 0.20.1. NOTE: This vulnerability is related to CVE-2025-53629.
  • CVE-2026-22776: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to version 0.30.1, a Denial of Service (DoS) vulnerability exists in cpp-httplib due to the unsafe handling of compressed HTTP request bodies (Content-Encoding: gzip, br, etc.). The library validates the payload_max_length against the compressed data size received from the network, but does not limit the size of the decompressed data stored in memory.
  • CVE-2026-28434: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.35.0, when a request handler throws a C++ exception and the application has not registered a custom exception handler via set_exception_handler(), the library catches the exception and writes its message directly into the HTTP response as a header named EXCEPTION_WHAT. This header is sent to whoever made the request, with no authentication check and no special configuration required to trigger it. The behavior is on by default. A developer who does not know to opt in to set_exception_handler() will ship a server that leaks internal exception messages to any client. This vulnerability is fixed in 0.35.0.
  • CVE-2026-28435: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.35.0, cpp-httplib (httplib.h) does not enforce Server::set_payload_max_length() on the decompressed request body when using HandlerWithContentReader (streaming ContentReader) with Content-Encoding: gzip (or other supported encodings). A small compressed payload can expand beyond the configured payload limit and be processed by the application, enabling a payload size limit bypass and potential denial of service (CPU/memory exhaustion). This vulnerability is fixed in 0.35.0.
  • CVE-2026-29076: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to version 0.37.0, cpp-httplib uses std::regex (libstdc++) to parse RFC 5987 encoded filename* values in multipart Content-Disposition headers. The regex engine in libstdc++ implements backtracking via deep recursion, consuming one stack frame per input character. An attacker can send a single HTTP POST request with a crafted filename* parameter that causes uncontrolled stack growth, resulting in a stack overflow (SIGSEGV) that crashes the server process. This issue has been patched in version 0.37.0.
  • CVE-2026-31870: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.37.1, when a cpp-httplib client uses the streaming API (httplib::stream::Get, httplib::stream::Post, etc.), the library calls std::stoull() directly on the Content-Length header value received from the server with no input validation and no exception handling. std::stoull throws std::invalid_argument for non-numeric strings and std::out_of_range for values exceeding ULLONG_MAX. Since nothing catches these exceptions, the C++ runtime calls std::terminate(), which kills the process with SIGABRT. Any server the client connects to — including servers reached via HTTP redirects, third-party APIs, or man-in-the-middle positions can crash the client application with a single HTTP response. No authentication is required. No interaction from the end user is required. The crash is deterministic and immediate. This vulnerability is fixed in 0.37.1.
  • CVE-2026-32627: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.37.2, when a cpp-httplib client is configured with a proxy and set_follow_location(true), any HTTPS redirect it follows will have TLS certificate and hostname verification silently disabled on the new connection. The client will accept any certificate presented by the redirect target — expired, self-signed, or forged — without raising an error or notifying the application. A network attacker in a position to return a redirect response can fully intercept the follow-up HTTPS connection, including any credentials or session tokens in flight. This vulnerability is fixed in 0.37.2.
  • CVE-2026-33745: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.39.0, the cpp-httplib HTTP client forwards stored Basic Auth, Bearer Token, and Digest Auth credentials to arbitrary hosts when following cross-origin HTTP redirects (301/302/307/308). A malicious or compromised server can redirect the client to an attacker-controlled host, which then receives the plaintext credentials in the `Authorization` header. Version 0.39.0 fixes the issue.
  • CVE-2026-34441: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to version 0.40.0, cpp-httplib is vulnerable to HTTP Request Smuggling. The server's static file handler serves GET responses without consuming the request body. On HTTP/1.1 keep-alive connections, the unread body bytes remain on the TCP stream and are interpreted as the start of a new HTTP request. An attacker can embed an arbitrary HTTP request inside the body of a GET request, which the server processes as a separate request. This issue has been patched in version 0.40.0.
  • CVE-2026-45352: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.43.4, negative chunk-size in chunked Transfer-Encoding causes unbounded memory allocation and process crash. The ChunkedDecoder::read_payload function in cpp-httplib (httplib.h) parses the chunk-size field of HTTP chunked transfer encoding using std::strtoul(). Per the C standard (§7.22.1.4), strtoul silently accepts a leading minus sign, performing unsigned wrap-around: strtoul("-2", …, 16) returns ULONG_MAX − 1 (0xFFFFFFFFFFFFFFFE). The library's only guard (line 12833) rejects ULONG_MAX (the result of "-1"), but any other negative value such as "-2" passes validation. The resulting near-maximum value is stored in chunk_remaining and controls how many bytes the server's read loop consumes from the network. This vulnerability is fixed in 0.43.4.
  • CVE-2026-45372: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.44.0, when cpp-httplib's server parses an incoming request, it applies percent-decoding to every header value except Location and Referer. The validity check (is_field_value) is run before decoding, so encoded %0D%0A passes the check and is then expanded to a literal \r\n byte pair inside the stored header value. This vulnerability is fixed in 0.44.0.
  • CVE-2026-46527: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.44.0, When the server has called Server::set_trusted_proxies() with a non-empty trusted-proxy list, an attacker can send an HTTP request that includes an X-Forwarded-For header whose value parses to no valid IP segments. The code path then executes get_client_ip(), which calls front() on an empty std::vector—undefined behavior in C++. On typical implementations this manifests as abnormal process termination (denial of service). With Sanitizers enabled, you get an explicit runtime diagnostic. This vulnerability is fixed in 0.44.0.
  • CVE-2026-77341: (needs triaging) cpp-httplib is a C++ header-only HTTP/HTTPS library. In version 0.49.0, the chunked-response trailer output path writes trailer header names and values directly to the socket without validating them, allowing CRLF sequences in a trailer field to inject additional headers or split the HTTP response. Unlike every other header-writing path in the library, the trailer-writing code applies none of the field-name and field-value checks that reject carriage return and line feed, so an application that places attacker-influenced data into a chunked response trailer emits attacker-controlled CRLF onto the wire. This enables HTTP response splitting, letting an attacker forge response headers or inject a second response. This issue is fixed in version 0.50.0.
  • CVE-2026-77358: (needs triaging) cpp-httplib is a C++ header-only HTTP/HTTPS library. In versions 0.33.0 through 0.50.0, the TLS-enabled WebSocket client frees the TLS session before closing the WebSocket that still uses it, producing a use-after-free. In WebSocketClient::shutdown_and_close the SSL object is freed and the pointer cleared, but the subsequent WebSocket close still sends a close frame through the SSL socket stream, which holds a raw copy of the now-dangling session pointer and reads from and writes to the freed memory. The same freed-then-used ordering is reachable through the client's destructor and its connect path, so ordinary teardown of a secure WebSocket connection triggers the defect. This issue is fixed in version 0.50.1.

You can find information about how to handle these issues in the security team's documentation.

Created: 2025-05-07 Last update: 2026-10-25 01:33
7 security issues in sid high

There are 7 open security issues in sid.

7 important issues:
  • CVE-2026-45352: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.43.4, negative chunk-size in chunked Transfer-Encoding causes unbounded memory allocation and process crash. The ChunkedDecoder::read_payload function in cpp-httplib (httplib.h) parses the chunk-size field of HTTP chunked transfer encoding using std::strtoul(). Per the C standard (§7.22.1.4), strtoul silently accepts a leading minus sign, performing unsigned wrap-around: strtoul("-2", …, 16) returns ULONG_MAX − 1 (0xFFFFFFFFFFFFFFFE). The library's only guard (line 12833) rejects ULONG_MAX (the result of "-1"), but any other negative value such as "-2" passes validation. The resulting near-maximum value is stored in chunk_remaining and controls how many bytes the server's read loop consumes from the network. This vulnerability is fixed in 0.43.4.
  • CVE-2026-45372: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.44.0, when cpp-httplib's server parses an incoming request, it applies percent-decoding to every header value except Location and Referer. The validity check (is_field_value) is run before decoding, so encoded %0D%0A passes the check and is then expanded to a literal \r\n byte pair inside the stored header value. This vulnerability is fixed in 0.44.0.
  • CVE-2026-46527: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.44.0, When the server has called Server::set_trusted_proxies() with a non-empty trusted-proxy list, an attacker can send an HTTP request that includes an X-Forwarded-For header whose value parses to no valid IP segments. The code path then executes get_client_ip(), which calls front() on an empty std::vector—undefined behavior in C++. On typical implementations this manifests as abnormal process termination (denial of service). With Sanitizers enabled, you get an explicit runtime diagnostic. This vulnerability is fixed in 0.44.0.
  • CVE-2026-77341: cpp-httplib is a C++ header-only HTTP/HTTPS library. In version 0.49.0, the chunked-response trailer output path writes trailer header names and values directly to the socket without validating them, allowing CRLF sequences in a trailer field to inject additional headers or split the HTTP response. Unlike every other header-writing path in the library, the trailer-writing code applies none of the field-name and field-value checks that reject carriage return and line feed, so an application that places attacker-influenced data into a chunked response trailer emits attacker-controlled CRLF onto the wire. This enables HTTP response splitting, letting an attacker forge response headers or inject a second response. This issue is fixed in version 0.50.0.
  • CVE-2026-77358: cpp-httplib is a C++ header-only HTTP/HTTPS library. In versions 0.33.0 through 0.50.0, the TLS-enabled WebSocket client frees the TLS session before closing the WebSocket that still uses it, producing a use-after-free. In WebSocketClient::shutdown_and_close the SSL object is freed and the pointer cleared, but the subsequent WebSocket close still sends a close frame through the SSL socket stream, which holds a raw copy of the now-dangling session pointer and reads from and writes to the freed memory. The same freed-then-used ordering is reachable through the client's destructor and its connect path, so ordinary teardown of a secure WebSocket connection triggers the defect. This issue is fixed in version 0.50.1.
  • CVE-2026-102944: cpp-httplib is a C++ header-only HTTP/HTTPS library. Prior to 0.51.0, create_redirect_client() removes Host, Proxy-Authorization, and Authorization headers when following a cross-origin 301, 302, 307, or 308 redirect but leaves Cookie headers attached. detail::redirect() copies the request for those redirect codes, so cookies supplied directly or through default_headers_ are sent to the redirect target. An attacker who controls a redirect response, or a network position for a non-HTTPS origin, can receive session cookies intended for the original origin. A 303 redirect is not affected because that path clears all request headers. This issue is fixed in version 0.51.0.
  • CVE-2026-103930: cpp-httplib is a C++ header-only HTTP/HTTPS library. From 0.29.0 until 0.51.0, Client::open_stream in httplib.h accepts a caller-controlled path that reaches detail::write_request_line() without the detail::encode_path() protection used by the ordinary buffered request path. Raw carriage-return and line-feed bytes in the path can therefore split the outbound HTTP/1.1 request line and inject attacker-shaped request metadata. Exploitation requires an embedding application to pass remote user input into the open_stream path and a backend or intermediary that interprets the injected bytes; the established impact is modification of backend-visible request data, not direct confidentiality or availability impact. This issue is fixed in version 0.51.0.
Created: 2026-05-30 Last update: 2026-10-25 01:33
7 security issues in forky high

There are 7 open security issues in forky.

7 important issues:
  • CVE-2026-45352: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.43.4, negative chunk-size in chunked Transfer-Encoding causes unbounded memory allocation and process crash. The ChunkedDecoder::read_payload function in cpp-httplib (httplib.h) parses the chunk-size field of HTTP chunked transfer encoding using std::strtoul(). Per the C standard (§7.22.1.4), strtoul silently accepts a leading minus sign, performing unsigned wrap-around: strtoul("-2", …, 16) returns ULONG_MAX − 1 (0xFFFFFFFFFFFFFFFE). The library's only guard (line 12833) rejects ULONG_MAX (the result of "-1"), but any other negative value such as "-2" passes validation. The resulting near-maximum value is stored in chunk_remaining and controls how many bytes the server's read loop consumes from the network. This vulnerability is fixed in 0.43.4.
  • CVE-2026-45372: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.44.0, when cpp-httplib's server parses an incoming request, it applies percent-decoding to every header value except Location and Referer. The validity check (is_field_value) is run before decoding, so encoded %0D%0A passes the check and is then expanded to a literal \r\n byte pair inside the stored header value. This vulnerability is fixed in 0.44.0.
  • CVE-2026-46527: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.44.0, When the server has called Server::set_trusted_proxies() with a non-empty trusted-proxy list, an attacker can send an HTTP request that includes an X-Forwarded-For header whose value parses to no valid IP segments. The code path then executes get_client_ip(), which calls front() on an empty std::vector—undefined behavior in C++. On typical implementations this manifests as abnormal process termination (denial of service). With Sanitizers enabled, you get an explicit runtime diagnostic. This vulnerability is fixed in 0.44.0.
  • CVE-2026-77341: cpp-httplib is a C++ header-only HTTP/HTTPS library. In version 0.49.0, the chunked-response trailer output path writes trailer header names and values directly to the socket without validating them, allowing CRLF sequences in a trailer field to inject additional headers or split the HTTP response. Unlike every other header-writing path in the library, the trailer-writing code applies none of the field-name and field-value checks that reject carriage return and line feed, so an application that places attacker-influenced data into a chunked response trailer emits attacker-controlled CRLF onto the wire. This enables HTTP response splitting, letting an attacker forge response headers or inject a second response. This issue is fixed in version 0.50.0.
  • CVE-2026-77358: cpp-httplib is a C++ header-only HTTP/HTTPS library. In versions 0.33.0 through 0.50.0, the TLS-enabled WebSocket client frees the TLS session before closing the WebSocket that still uses it, producing a use-after-free. In WebSocketClient::shutdown_and_close the SSL object is freed and the pointer cleared, but the subsequent WebSocket close still sends a close frame through the SSL socket stream, which holds a raw copy of the now-dangling session pointer and reads from and writes to the freed memory. The same freed-then-used ordering is reachable through the client's destructor and its connect path, so ordinary teardown of a secure WebSocket connection triggers the defect. This issue is fixed in version 0.50.1.
  • CVE-2026-102944: cpp-httplib is a C++ header-only HTTP/HTTPS library. Prior to 0.51.0, create_redirect_client() removes Host, Proxy-Authorization, and Authorization headers when following a cross-origin 301, 302, 307, or 308 redirect but leaves Cookie headers attached. detail::redirect() copies the request for those redirect codes, so cookies supplied directly or through default_headers_ are sent to the redirect target. An attacker who controls a redirect response, or a network position for a non-HTTPS origin, can receive session cookies intended for the original origin. A 303 redirect is not affected because that path clears all request headers. This issue is fixed in version 0.51.0.
  • CVE-2026-103930: cpp-httplib is a C++ header-only HTTP/HTTPS library. From 0.29.0 until 0.51.0, Client::open_stream in httplib.h accepts a caller-controlled path that reaches detail::write_request_line() without the detail::encode_path() protection used by the ordinary buffered request path. Raw carriage-return and line-feed bytes in the path can therefore split the outbound HTTP/1.1 request line and inject attacker-shaped request metadata. Exploitation requires an embedding application to pass remote user input into the open_stream path and a backend or intermediary that interprets the injected bytes; the established impact is modification of backend-visible request data, not direct confidentiality or availability impact. This issue is fixed in version 0.51.0.
Created: 2026-05-30 Last update: 2026-10-25 01:33
22 security issues in bookworm high

There are 22 open security issues in bookworm.

5 important issues:
  • CVE-2025-66570: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.27.0, a vulnerability allows attacker-controlled HTTP headers to influence server-visible metadata, logging, and authorization decisions. An attacker can inject headers named REMOTE_ADDR, REMOTE_PORT, LOCAL_ADDR, LOCAL_PORT that are parsed into the request header multimap via read_headers() in httplib.h (headers.emplace), then the server later appends its own internal metadata using the same header names in Server::process_request without erasing duplicates. Because Request::get_header_value returns the first entry for a header key (id == 0) and the client-supplied headers are parsed before server-inserted headers, downstream code that uses these header names may inadvertently use attacker-controlled values. Affected files/locations: cpp-httplib/httplib.h (read_headers, Server::process_request, Request::get_header_value, get_header_value_u64) and cpp-httplib/docker/main.cc (get_client_ip, nginx_access_logger, nginx_error_logger). Attack surface: attacker-controlled HTTP headers in incoming requests flow into the Request.headers multimap and into logging code that reads forwarded headers, enabling IP spoofing, log poisoning, and authorization bypass via header shadowing. This vulnerability is fixed in 0.27.0.
  • CVE-2025-66577: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.27.0, a vulnerability allows attacker-controlled HTTP headers to influence server-visible metadata, logging, and authorization decisions. An attacker can supply X-Forwarded-For or X-Real-IP headers which get accepted unconditionally by get_client_ip() in docker/main.cc, causing access and error logs (nginx_access_logger / nginx_error_logger) to record spoofed client IPs (log poisoning / audit evasion). This vulnerability is fixed in 0.27.0.
  • CVE-2026-21428: cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to version 0.30.0, the ``write_headers`` function does not check for CR & LF characters in user supplied headers, allowing untrusted header value to escape header lines. This vulnerability allows attackers to add extra headers, modify request body unexpectedly & trigger an SSRF attack. When combined with a server that supports http1.1 pipelining (springboot, python twisted etc), this can be used for server side request forgery (SSRF). Version 0.30.0 fixes this issue.
  • CVE-2026-102944: cpp-httplib is a C++ header-only HTTP/HTTPS library. Prior to 0.51.0, create_redirect_client() removes Host, Proxy-Authorization, and Authorization headers when following a cross-origin 301, 302, 307, or 308 redirect but leaves Cookie headers attached. detail::redirect() copies the request for those redirect codes, so cookies supplied directly or through default_headers_ are sent to the redirect target. An attacker who controls a redirect response, or a network position for a non-HTTPS origin, can receive session cookies intended for the original origin. A 303 redirect is not affected because that path clears all request headers. This issue is fixed in version 0.51.0.
  • CVE-2026-103930: cpp-httplib is a C++ header-only HTTP/HTTPS library. From 0.29.0 until 0.51.0, Client::open_stream in httplib.h accepts a caller-controlled path that reaches detail::write_request_line() without the detail::encode_path() protection used by the ordinary buffered request path. Raw carriage-return and line-feed bytes in the path can therefore split the outbound HTTP/1.1 request line and inject attacker-shaped request metadata. Exploitation requires an embedding application to pass remote user input into the open_stream path and a backend or intermediary that interprets the injected bytes; the established impact is modification of backend-visible request data, not direct confidentiality or availability impact. This issue is fixed in version 0.51.0.
17 issues postponed or untriaged:
  • CVE-2025-0825: (needs triaging) cpp-httplib version v0.17.3 through v0.18.3 fails to filter CRLF characters ("\r\n") when those are prefixed with a null byte. This enables attackers to exploit CRLF injection that could further lead to HTTP Response Splitting, XSS, and more.
  • CVE-2025-46728: (postponed; to be fixed through a stable update) cpp-httplib is a C++ header-only HTTP/HTTPS server and client library. Prior to version 0.20.1, the library fails to enforce configured size limits on incoming request bodies when `Transfer-Encoding: chunked` is used or when no `Content-Length` header is provided. A remote attacker can send a chunked request without the terminating zero-length chunk, causing uncontrolled memory allocation on the server. This leads to potential exhaustion of system memory and results in a server crash or unresponsiveness. Version 0.20.1 fixes the issue by enforcing limits during parsing. If the limit is exceeded at any point during reading, the connection is terminated immediately. A short-term workaround through a Reverse Proxy is available. If updating the library immediately is not feasible, deploy a reverse proxy (e.g., Nginx, HAProxy) in front of the `cpp-httplib` application. Configure the proxy to enforce maximum request body size limits, thereby stopping excessively large requests before they reach the vulnerable library code.
  • CVE-2025-53628: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.20.1, cpp-httplib does not have a limit for a unique line, permitting an attacker to explore this to allocate memory arbitrarily. This vulnerability is fixed in 0.20.1. NOTE: This vulnerability is related to CVE-2025-53629.
  • CVE-2025-53629: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.23.0, incoming requests using Transfer-Encoding: chunked in the header can allocate memory arbitrarily in the server, potentially leading to its exhaustion. This vulnerability is fixed in 0.23.0. NOTE: This vulnerability is related to CVE-2025-53628.
  • CVE-2026-22776: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to version 0.30.1, a Denial of Service (DoS) vulnerability exists in cpp-httplib due to the unsafe handling of compressed HTTP request bodies (Content-Encoding: gzip, br, etc.). The library validates the payload_max_length against the compressed data size received from the network, but does not limit the size of the decompressed data stored in memory.
  • CVE-2026-28434: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.35.0, when a request handler throws a C++ exception and the application has not registered a custom exception handler via set_exception_handler(), the library catches the exception and writes its message directly into the HTTP response as a header named EXCEPTION_WHAT. This header is sent to whoever made the request, with no authentication check and no special configuration required to trigger it. The behavior is on by default. A developer who does not know to opt in to set_exception_handler() will ship a server that leaks internal exception messages to any client. This vulnerability is fixed in 0.35.0.
  • CVE-2026-28435: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.35.0, cpp-httplib (httplib.h) does not enforce Server::set_payload_max_length() on the decompressed request body when using HandlerWithContentReader (streaming ContentReader) with Content-Encoding: gzip (or other supported encodings). A small compressed payload can expand beyond the configured payload limit and be processed by the application, enabling a payload size limit bypass and potential denial of service (CPU/memory exhaustion). This vulnerability is fixed in 0.35.0.
  • CVE-2026-29076: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to version 0.37.0, cpp-httplib uses std::regex (libstdc++) to parse RFC 5987 encoded filename* values in multipart Content-Disposition headers. The regex engine in libstdc++ implements backtracking via deep recursion, consuming one stack frame per input character. An attacker can send a single HTTP POST request with a crafted filename* parameter that causes uncontrolled stack growth, resulting in a stack overflow (SIGSEGV) that crashes the server process. This issue has been patched in version 0.37.0.
  • CVE-2026-31870: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.37.1, when a cpp-httplib client uses the streaming API (httplib::stream::Get, httplib::stream::Post, etc.), the library calls std::stoull() directly on the Content-Length header value received from the server with no input validation and no exception handling. std::stoull throws std::invalid_argument for non-numeric strings and std::out_of_range for values exceeding ULLONG_MAX. Since nothing catches these exceptions, the C++ runtime calls std::terminate(), which kills the process with SIGABRT. Any server the client connects to — including servers reached via HTTP redirects, third-party APIs, or man-in-the-middle positions can crash the client application with a single HTTP response. No authentication is required. No interaction from the end user is required. The crash is deterministic and immediate. This vulnerability is fixed in 0.37.1.
  • CVE-2026-32627: (needs triaging) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.37.2, when a cpp-httplib client is configured with a proxy and set_follow_location(true), any HTTPS redirect it follows will have TLS certificate and hostname verification silently disabled on the new connection. The client will accept any certificate presented by the redirect target — expired, self-signed, or forged — without raising an error or notifying the application. A network attacker in a position to return a redirect response can fully intercept the follow-up HTTPS connection, including any credentials or session tokens in flight. This vulnerability is fixed in 0.37.2.
  • CVE-2026-33745: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.39.0, the cpp-httplib HTTP client forwards stored Basic Auth, Bearer Token, and Digest Auth credentials to arbitrary hosts when following cross-origin HTTP redirects (301/302/307/308). A malicious or compromised server can redirect the client to an attacker-controlled host, which then receives the plaintext credentials in the `Authorization` header. Version 0.39.0 fixes the issue.
  • CVE-2026-34441: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to version 0.40.0, cpp-httplib is vulnerable to HTTP Request Smuggling. The server's static file handler serves GET responses without consuming the request body. On HTTP/1.1 keep-alive connections, the unread body bytes remain on the TCP stream and are interpreted as the start of a new HTTP request. An attacker can embed an arbitrary HTTP request inside the body of a GET request, which the server processes as a separate request. This issue has been patched in version 0.40.0.
  • CVE-2026-45352: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.43.4, negative chunk-size in chunked Transfer-Encoding causes unbounded memory allocation and process crash. The ChunkedDecoder::read_payload function in cpp-httplib (httplib.h) parses the chunk-size field of HTTP chunked transfer encoding using std::strtoul(). Per the C standard (§7.22.1.4), strtoul silently accepts a leading minus sign, performing unsigned wrap-around: strtoul("-2", …, 16) returns ULONG_MAX − 1 (0xFFFFFFFFFFFFFFFE). The library's only guard (line 12833) rejects ULONG_MAX (the result of "-1"), but any other negative value such as "-2" passes validation. The resulting near-maximum value is stored in chunk_remaining and controls how many bytes the server's read loop consumes from the network. This vulnerability is fixed in 0.43.4.
  • CVE-2026-45372: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.44.0, when cpp-httplib's server parses an incoming request, it applies percent-decoding to every header value except Location and Referer. The validity check (is_field_value) is run before decoding, so encoded %0D%0A passes the check and is then expanded to a literal \r\n byte pair inside the stored header value. This vulnerability is fixed in 0.44.0.
  • CVE-2026-46527: (postponed; to be fixed through a stable update) cpp-httplib is a C++11 single-file header-only cross platform HTTP/HTTPS library. Prior to 0.44.0, When the server has called Server::set_trusted_proxies() with a non-empty trusted-proxy list, an attacker can send an HTTP request that includes an X-Forwarded-For header whose value parses to no valid IP segments. The code path then executes get_client_ip(), which calls front() on an empty std::vector—undefined behavior in C++. On typical implementations this manifests as abnormal process termination (denial of service). With Sanitizers enabled, you get an explicit runtime diagnostic. This vulnerability is fixed in 0.44.0.
  • CVE-2026-77341: (postponed; to be fixed through a stable update) cpp-httplib is a C++ header-only HTTP/HTTPS library. In version 0.49.0, the chunked-response trailer output path writes trailer header names and values directly to the socket without validating them, allowing CRLF sequences in a trailer field to inject additional headers or split the HTTP response. Unlike every other header-writing path in the library, the trailer-writing code applies none of the field-name and field-value checks that reject carriage return and line feed, so an application that places attacker-influenced data into a chunked response trailer emits attacker-controlled CRLF onto the wire. This enables HTTP response splitting, letting an attacker forge response headers or inject a second response. This issue is fixed in version 0.50.0.
  • CVE-2026-77358: (postponed; to be fixed through a stable update) cpp-httplib is a C++ header-only HTTP/HTTPS library. In versions 0.33.0 through 0.50.0, the TLS-enabled WebSocket client frees the TLS session before closing the WebSocket that still uses it, producing a use-after-free. In WebSocketClient::shutdown_and_close the SSL object is freed and the pointer cleared, but the subsequent WebSocket close still sends a close frame through the SSL socket stream, which holds a raw copy of the now-dangling session pointer and reads from and writes to the freed memory. The same freed-then-used ordering is reachable through the client's destructor and its connect path, so ordinary teardown of a secure WebSocket connection triggers the defect. This issue is fixed in version 0.50.1.
Created: 2025-02-07 Last update: 2026-10-25 01:33
1 bug with a patch to review in the bug tracker normal
The bug tracker contains patches fixing 1 bug to review: include them, or remove the patch tag.
Created: 2026-09-02 Last update: 2026-11-07 13:17
version in VCS is newer than in repository, is it time to upload? normal
vcswatch reports that this package seems to have a new changelog entry (version 0.64.0+ds-0.1, distribution unstable) and new commits in its VCS. You should consider whether it's time to make an upload.

Here are the relevant commit messages:
commit 7c05b45f50531eb30f8165d9caaf77ebbbbab125
Author: Mario Limonciello <superm1@gmail.com>
Date:   Wed Nov 4 08:53:58 2026 -0600

    releasing package cpp-httplib version 0.64.0+ds-0.1

commit 4bf87ab72dbc1883f0eddad4e992bb4f3985dae7
Author: Mario Limonciello <superm1@gmail.com>
Date:   Wed Nov 4 08:53:39 2026 -0600

    d/tests/control: pkg-config->pkgconf

commit b3de07710b18f02018a3ef683640050f44644409
Author: Mario Limonciello <superm1@gmail.com>
Date:   Mon Nov 2 11:26:57 2026 -0600

    Update changelog

commit 85e15830807cdfc9622b79aad07135a7e307aa26
Author: Mario Limonciello <superm1@gmail.com>
Date:   Mon Nov 2 11:25:47 2026 -0600

    d/control pkg->config -> pkfconf

commit f8fc9eb0c3274bf06c0a5ec453abe11fd0fba11e
Merge: afa2aec fbd757a
Author: Mario Limonciello <superm1@gmail.com>
Date:   Mon Nov 2 10:35:08 2026 -0600

    Merge branch 'upstream/latest' into update-0.64.0

commit fbd757a540de26f95cac13d56fbc7cf9fda49297
Author: Mario Limonciello <superm1@gmail.com>
Date:   Mon Nov 2 10:34:50 2026 -0600

    Remove benchmark/ to match debian/copyright

commit 5d3d2470392934be4c60e94ba474e21e56e55353
Merge: 3caa443 24e58ce
Author: Mario Limonciello <superm1@gmail.com>
Date:   Mon Nov 2 10:18:38 2026 -0600

    Merge tag 'v0.64.0' into upstream/latest

commit 3caa44361c03b0e5ab5be54e8a6387d6788a964c
Merge: 9751cff d66d9a9
Author: Andrea Pappacoda <tachi@debian.org>
Date:   Fri Jul 24 17:20:13 2026 -0300

    Merge tag 'v0.51.0' into upstream/latest

commit d66d9a95997d51a8ba9822a611d1267757741535
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Thu Jul 23 22:52:55 2026 -0400

    Release v0.51.0

commit 2fa0417754347222065c423d7c4928ffa1577dfe
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Thu Jul 23 22:41:43 2026 -0400

    Fix mbedTLS is_peer_closed() destroying the first response byte
    
    Mbed TLS has no SSL_peek() equivalent, so is_peer_closed() (called after
    every SSL request write) probed liveness with a real 1-byte
    mbedtls_ssl_read() and discarded whatever it read. If the response had
    already arrived by the time the probe ran — plausible under CI load or
    plain OS scheduling — the probe silently ate the first byte of the
    status line, corrupting the response and surfacing as a fast
    "Failed to read connection" failure.
    
    This was the root cause of the long-standing MbedTLS-only CI flakiness
    (ServerTest cases failing intermittently on Ubuntu and macOS), previously
    worked around by reducing gtest shard parallelism. Fix: push the probed
    byte back into MbedTlsSession and have tls::read()/pending() account for
    it, so no data is lost.
    
    Also fix a second, unrelated flake: ProxyTunnelTest.
    OriginReturning407InsideTunnelDoesNotLeakProxyDigest used "localhost" for
    its client while the test's proxy harness only listens on 127.0.0.1;
    under dual-stack resolution this could race with another test's server
    on ::1 using the same ephemeral port. Pin the test to 127.0.0.1.
    
    With the root cause fixed, restore the mbedTLS CI jobs (ubuntu,
    ubuntu-26.04, macOS) to the default shard count instead of the
    previously reduced SHARDS=1/2 mitigation.

commit 82b1492c3dd380617cabf2c69592e6882c212798
Merge: cc1be5e f7c8455
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Thu Jul 23 20:04:02 2026 -0400

    Merge pull request #2507 from yhirose/flaky-ci-diagnostics
    
    Log res.error() on all ServerTest ASSERT_TRUE(res) assertions

commit f7c8455a624eb4bb1810f915443ce53a93f2cee6
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Thu Jul 23 18:06:22 2026 -0400

    Log res.error() on all ServerTest ASSERT_TRUE(res) assertions
    
    Only 8 of 423 call sites logged the actual error on failure. The
    MbedTLS-backend flaky CI failure (connection-level ASSERT_TRUE(res),
    ~20-30ms) keeps landing on assertions without this diagnostic, so the
    real error code has never been captured. Broadens the existing
    GetWithRange-only logging (a4d7066) to every plain ASSERT_TRUE(res);
    site, no behavior change.

commit cc1be5ebb5fb7f6ed1143854b77c5a42ab7ac04c
Merge: 613a41b cda2bb5
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Thu Jul 23 17:50:59 2026 -0400

    Merge pull request #2506 from yhirose/refactor/consolidate-field-validation
    
    Consolidate header field-pair validation into fields::is_field_valid

commit cda2bb5342650801e655abd03cf6b1c4c473b493
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Thu Jul 23 17:34:57 2026 -0400

    Consolidate header field-pair validation into fields::is_field_valid
    
    The is_field_name(name) && is_field_value(value) predicate was repeated
    across five output paths (set_header, write_headers, write_content_chunked
    trailer, perform_websocket_handshake, check_and_write_headers). Introduce
    fields::is_field_valid(name, value) and route all five through it so the
    CR/LF-injection guard has a single definition. No behavior change.

commit 613a41b49ddbcb14a4da04ce163ff1f590858858
Merge: c64bf21 695961f
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Thu Jul 23 17:32:22 2026 -0400

    Merge pull request #2505 from metsw24-max/write-headers-crlf-guard
    
    skip invalid fields in write_headers to prevent response splitting

commit 695961f8ae7eedec245e4a214eec61a841aebc14
Author: Sayed Kaif <metsw24@gmail.com>
Date:   Thu Jul 23 21:42:37 2026 +0530

    skip invalid fields in write_headers to prevent response splitting

commit c64bf21a5ea10c196187cda52ccd776725968e3c
Merge: bf8baf0 8bbfc90
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Wed Jul 22 12:01:20 2026 -0400

    Merge pull request #2504 from emreay-/skip-drain-on-closing-connection
    
    Skip request body drain when connection will close

commit 8bbfc90380ca402aa135576d8f8697c40a8193af
Author: Emre Ay <emre@emreay.com>
Date:   Wed Jul 22 10:53:34 2026 +0300

    Apply clang-format to test.cc and httplib.h
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit bf8baf07391cec982b99028b0bc2182fc5ad5083
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Tue Jul 21 19:47:15 2026 -0400

    clang-format

commit fd0c18b1b5bce8003c46130c5ce17ac44021038a
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Tue Jul 21 18:33:59 2026 -0400

    Validate connecting peer before honoring X-Forwarded-For
    
    Server::process_request only checked that trusted_proxies_ was
    non-empty before deriving req.remote_addr from the X-Forwarded-For
    header. It never verified that the actual TCP peer (remote_addr) was
    itself one of the trusted proxies, so any client connecting directly
    to the server could spoof remote_addr simply by sending an arbitrary
    X-Forwarded-For header.
    
    Now X-Forwarded-For is only honored when the connecting peer address
    matches an entry in trusted_proxies_.

commit bbd56a7e2cdc3306822bac1417f6a2c525aa5758
Merge: 54b3c1d 877a52f
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Tue Jul 21 19:15:50 2026 -0400

    Merge pull request #2497 from superm1/superm1/SWSPLAT-23636
    
    Limit the number of header lines per multipart form-data part

commit 54b3c1d072a5a6adeadcc79cfbd1be7afbbca71f
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Tue Jul 21 18:45:48 2026 -0400

    Add Mbed TLS 4.x support (PSA Crypto) (#2502)
    
    * Add Mbed TLS 4.x support (PSA Crypto) for macOS
    
    Auto-detect Mbed TLS 4.x via MBEDTLS_VERSION_MAJOR and adapt the backend:
    - Include psa/crypto.h and drop the headers removed in 4.x (ctr_drbg,
      entropy, md5, sha*), gated behind the version macro.
    - Compute MD5/SHA-256/SHA-512 via PSA (psa_hash_compute) and initialize
      PSA Crypto once with std::call_once.
    - Drop the explicit entropy/CTR-DRBG RNG (PSA provides the TLS RNG) and
      skip the RNG-callback overloads of pk_parse_key/pk_check_pair on 4.x.
    - Retry on a TLS 1.3 NewSessionTicket (the 4.x default) in connect, read,
      write and is_peer_closed via a single mbedtls_is_session_ticket() helper,
      so online HTTPS works, including large redirected downloads where the
      ticket arrives mid-write.
    
    Note V4 implies V3, so 3.x-only paths now check V3 && !V4.
    
    Build systems (macOS): the CMake config and pkg-config shipped by Homebrew
    resolve 4.x transitively, so CMakeLists.txt and meson.build need no change
    for linking; the Makefile links libtfpsacrypto when present, else
    libmbedcrypto.
    
    Tests: generate the encrypted client key as both PBES2-AES (3.6+/4.x,
    OpenSSL, wolfSSL) and PBES1-3DES (Mbed TLS 2.28) and pick by version, since
    4.x dropped DES and 2.28 lacks PBES2. Also generate the IP-host certs in
    test/meson.build to match gen-certs.sh and CMakeLists.txt.
    
    * CI: test Mbed TLS 4.x on macOS, 3.x on Ubuntu 26.04
    
    Homebrew's default mbedtls is now 4.x, so switch the macOS build and CI job
    to it (drop the mbedtls@3 pin). That leaves 3.x (Ubuntu 24.04 apt ships 2.28,
    macOS now 4.x) uncovered, so add an ubuntu-26.04 job whose apt provides Mbed
    TLS 3.6. Net coverage: 2.28 (ubuntu-latest), 3.6 (ubuntu-26.04), 4.2 (macOS).
    
    ubuntu-26.04 is a public-preview runner image; fold it into the main ubuntu
    matrix once ubuntu-latest moves to 26.04.
    
    * Document Mbed TLS 4.x support and libtfpsacrypto rename
    
    Update README.md and the tour's TLS setup pages (en/ja) to note that
    Mbed TLS 4.x is now auto-detected and that it renames libmbedcrypto
    to libtfpsacrypto.

commit 6c4cbd49a2e4ee6e76f2ca8c432fd5897de0996f
Author: metsw24-max <metsw24@gmail.com>
Date:   Wed Jul 22 03:42:40 2026 +0530

    scan X-Forwarded-For right-to-left in get_client_ip (#2503)

commit f3e9a4d88742a5c332918f1be5203942aab70c50
Author: Emre Ay <emre@emreay.com>
Date:   Mon Jul 20 20:36:06 2026 +0300

    Skip request body drain when connection will close
    
    The post-response drain exists to keep unread framed body bytes from being parsed as a subsequent request on a persistent connection. Once response generation has committed the connection to close, there can be no subsequent request, so draining no longer provides that protection.
    
    Continuing to drain is especially harmful when a ContentReader aborts an unterminated chunked upload: the server can wait indefinitely for the terminal chunk even after sending Connection: close. This delays the transport close that tells an in-flight uploader to stop and leaves a worker occupied consuming discarded data.
    
    Use the finalized response Connection header as the single source of truth for whether to skip the drain. write_response_core already sets this header for keep-alive exhaustion, request-directed closure, handler-directed closure, and error responses. Marking connection_closed then terminates the keep-alive loop and closes the socket.
    
    Add a raw-socket regression test whose ContentReader rejects the first chunk of an unterminated upload. The test verifies that the 409 response announces Connection: close and that the peer observes EOF rather than timing out while the server drains.

commit 877a52f6b34b1a9d426fc5344f7dbb119548fc2b
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Wed Jul 15 10:57:57 2026 -0500

    Limit the number of header lines per multipart form-data part
    
    The multipart/form-data parser bounded the length of each individual
    header line via CPPHTTPLIB_HEADER_MAX_LENGTH but did not cap how many
    header lines a single part could contain. Within the overall payload
    limit, a client could pack a large number of small header lines into
    one part, each of which is parsed twice, inflating CPU usage.
    
    Add a per-part header-line counter that resets when a new entry begins
    and abort parsing once it reaches CPPHTTPLIB_HEADER_MAX_COUNT, mirroring
    the limit already enforced by read_headers() for request headers.

commit ef2beaea34cca0c4e31b9f9a8463e36ca2938d02
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jul 18 20:08:33 2026 -0400

    Fix MSVC C4146 warning for unsigned types in from_chars
    
    The from_chars template is instantiated with unsigned types (e.g.
    uint64_t for Content-Length), where unary minus on `result` triggers
    MSVC warning C4146, failing the 32-bit build under warnings-as-errors.
    Use `T(0) - result`, which yields the identical two's-complement value
    without the warning.

commit 3adc525cec4e08d917a61a9fa54305c178d039b4
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jul 18 18:09:55 2026 -0400

    Use detail::from_chars for out-of-range Content-Length check
    
    Replace the strtoull + errno + cast-back dance in get_header_value_u64
    with the existing hand-written detail::from_chars, which reports
    result_out_of_range at size_t width. This detects the 32-bit truncation
    case directly (instead of via a separate cast-back comparison), drops
    the reliance on the global errno, and keeps the parsing locale-
    independent and consistent with the rest of the codebase.

commit a415362dfff5b140bcb9a7eb381de4afd888a4eb
Author: Sayed Kaif <metsw24@gmail.com>
Date:   Mon Jul 13 12:08:34 2026 +0530

    update OpenStreamMalformedContentLength.OutOfRange for stricter parsing

commit 982235c0a9975bef121a1d7866b546e165e243e4
Author: Sayed Kaif <metsw24@gmail.com>
Date:   Sun Jul 12 17:10:51 2026 +0530

    flag out-of-range Content-Length in get_header_value_u64

commit 255c075b82cf9f221e6e607e8e6789e6e2bf4c2a
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Sat Jul 18 18:07:44 2026 -0400

    Reject CR/LF in the request target and fail the request cleanly (#2501)
    
    * reject crlf in request target in write_request_line
    
    * declare write_request_line in test.cc for split builds
    
    split.py strips `inline` and moves the definition into httplib.cc, so
    detail::write_request_line is not visible from the split httplib.h and
    test_split failed to compile. Re-declare it in test.cc, matching what the
    base64_encode and getaddrinfo_with_timeout tests already do.
    
    * Fail the request when write_request_line rejects the target
    
    The CR/LF guard in write_request_line returns -1, but ClientImpl::write_request
    ignored that return value. A rejected target therefore produced a request-line-
    less request (headers only) that the client silently reported as a successful
    send. This is the primary path reachable via a decoded redirect Location under
    set_path_encode(false), and it also carries the CONNECT target.
    
    Check the return value like ClientImpl::open_stream already does and fail with
    Error::Write. Add an end-to-end test asserting the client refuses a CR/LF target
    instead of putting it on the wire.
    
    ---------
    
    Co-authored-by: Sayed Kaif <metsw24@gmail.com>

commit 75938f08c743eb985152c88c485b91d8fd42f948
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Sat Jul 18 18:07:29 2026 -0400

    Match chunked as the final transfer coding, order-independently (#2500)
    
    is_chunked_transfer_encoding compared the whole Transfer-Encoding field
    value against "chunked", so a message whose final coding is chunked but
    which names another coding first ("gzip, chunked", valid under RFC 9112
    6.1) was read as unframed. On a keep-alive server the body was then left
    in the socket and parsed as a smuggled request; open_stream repeated the
    check case-sensitively with a raw ==, desyncing the client stream the
    same way.
    
    Rework the helper to match the last coding token case-insensitively and
    route open_stream through it so both paths agree. The codings may also be
    split across multiple Transfer-Encoding lines (RFC 9110 5.3); since
    Headers is an unordered_multimap whose duplicate-key iteration order is
    not portable, the final coding of a multi-line field cannot be
    determined reliably, so treat any such message that names chunked as
    chunked (fail safe: a mis-parse only closes the connection, whereas the
    opposite error enables smuggling). A unit test covers the helper,
    including order-independent multi-line cases.
    
    Based on #2487 by @metsw24-max.

commit 0c1cc8c9866bb567ff11c1cd0d09779e8c5f8585
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sun Jul 12 10:45:36 2026 -0400

    Strip Cookie headers on cross-origin redirect
    
    Cookie and Cookie2 headers were forwarded to the new host when following
    a cross-origin redirect, even though Host, Proxy-Authorization, and
    Authorization were already stripped. Add them to the removal list so
    session cookies are not leaked to a different origin.

commit 62d899feac3cf9215a55f2b43da250fdd98d2156
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jul 11 21:37:36 2026 -0400

    Release v0.50.1

commit 2f986fd5e56e7c5f686d965174516360930f371d
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jul 11 21:20:36 2026 -0400

    Fix use-after-free of TLS session in WebSocketClient::shutdown_and_close()
    
    shutdown_and_close() freed the TLS session before ws_->close() sent the
    WebSocket close frame. The WebSocket's SSLSocketStream keeps a raw pointer
    to that session, so sending the close frame then read/wrote a freed SSL
    object. Reorder so ws_->close()/ws_.reset() run while the session is still
    alive, then free the session (GHSA-w7p7-f35j-mw7q).

commit ed97a6edbabb096fc0a7fb2c63599475b94b81a8
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jul 11 16:01:02 2026 -0400

    Release v0.50.0

commit 06b8b9158997338967600f2cf3bd210e8cc3333d
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jul 11 15:52:54 2026 -0400

    Fix Response::content_length_ not reflecting body size in Logger (Fix #2488)
    
    Server::apply_ranges computed the correct Content-Length header for
    body-based responses but never updated content_length_, so the
    Logger callback always saw 0. Set content_length_ to the final body
    size (post-range/post-compression) alongside the header.

commit 873d7019723cb91efdd8b89687222ec3029655c0
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jul 11 15:27:42 2026 -0400

    Fix use-after-free in SSLClient destructor with mbedTLS (Fix #2492)
    
    SSLClient::~SSLClient() freed the TLS context before shutting down
    the SSL session. mbedTLS sessions hold a raw pointer into the
    context's mbedtls_ssl_config, so a live keep-alive session's
    close_notify would read freed memory. Shut down the session first,
    then free the context.
    
    Add a regression test that destructs an SSLClient while a keep-alive
    mbedTLS session is still open.

commit b40937cea80d674fa67e50c3690b6b5056f05d47
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jul 11 14:12:25 2026 -0400

    Fix README WebSocket example to match actual API (Fix #2493)
    
    The quick preview used a nonexistent httplib::ws::Message type with
    .is_text()/.data. The actual API, as shown in README-websocket.md,
    uses a plain std::string with ws.read(msg).

commit 568d434e72fc51729d0ad33abffb181e5f7a453d
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jul 11 13:54:02 2026 -0400

    Fix CRLF injection in chunked response trailers
    
    Trailer field names and values written by write_content_chunked()'s
    done_with_trailer lambda were never validated, unlike every other
    header output path (set_header, WebSocket handshake, client request
    headers). An application reflecting untrusted input into a trailer
    via DataSink::done_with_trailer() could inject CR/LF sequences and
    achieve HTTP response splitting.
    
    Skip trailer fields with invalid names or values, matching the
    silent-skip behavior of set_header().

commit 0fa4912891f7229af34417ce859b02393f57ff5d
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 8 22:45:29 2026 -0400

    Fix broken relative links in cookbook docs (Fix #2490)
    
    Cookbook body links referenced sibling pages with a bare slug
    (e.g. `c14-keep-alive`). Under the pretty-URL layout each page lives
    in its own directory, so these resolve against the page's own
    directory and 404. Prefix them with `../` to match the convention
    already used in the tour and llm-app sections.
    
    Verified clean with `docs-gen check`.

commit 32abac3de5a1a5c57e0b06b4a8c261263c9e6499
Author: Copilot <198982749+Copilot@users.noreply.github.com>
Date:   Sun Jul 5 18:43:11 2026 -0400

    Fix ambiguous Get() examples in README after new Params overload (#2486)
    
    * Initial plan
    
    * Add Get(path, params) overload to ClientImpl and Client; fix ambiguous test calls
    
    * Fix clang-format style errors in test.cc
    
    * Fix remaining ambiguous Get() calls in test_proxy.cc and test.cc
    
    * Update README Get() examples to disambiguate Headers overload
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

commit 2132205e1a69c9fce8096f085b1b8d72efc759fa
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 1 23:56:17 2026 -0400

    Release v0.49.0

commit a4d7066c2ce21e0108d21fcede5e8ea8e4eb26a3
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 1 23:46:05 2026 -0400

    Log res.error() in GetWithRange test assertions to diagnose flaky CI failures

commit bd455f8b9168733cc9bfc204058abaced365f235
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 1 23:00:48 2026 -0400

    Add locale-independent ASCII classification helpers (Fix #2482, Fix #2483)
    
    std::isalnum and std::isdigit consult the global C locale, so a byte
    like 0xC5 can classify as alphanumeric once an embedder calls
    setlocale() (observed on macOS). HTTP grammars are defined over ASCII,
    so raw bytes must be classified without regard to the locale.
    
    Add detail::is_ascii_digit/is_ascii_alpha/is_ascii_alnum and use them
    at every classification site: multipart boundary validation, token
    checks, URI encoding, range header parsing, is_numeric, is_hex, and
    IPv4 host detection. Also unify the hand-written digit range checks in
    from_chars, URL parsing, and parse_ipv4 onto the same helpers.
    
    With these in place nothing uses <cctype> anymore, so the include is
    dropped.

commit 9a5321aadb71f043de72ba1da50f6be0df072c8d
Author: Yanjun Yang(Pluto) <yangyj.ee@gmail.com>
Date:   Thu Jul 2 10:21:44 2026 +0800

    meson: fix build failure on glibc >= 2.34 without standalone libanl (#2484)
    
    On glibc >= 2.34, getaddrinfo_a is no longer provided by a standalone
    libanl shared library — it is built directly into libc.  Newer
    architectures (e.g. loongarch64, riscv64) have never shipped a separate
    libanl, causing the meson build to fail with:
    
        C++ shared or static library 'anl' not found
    
    Signed-off-by: Pluto Yang <yangyj.ee@gmail.com>

commit 32ff75e3556d792b8ddf0982fb6775c8e80a1e0e
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 1 22:08:01 2026 -0400

    Make ThreadPool idle timeout configurable at runtime (Fix #2481)

commit 45da614dddcceacbdc491ee6c73a935b85702670
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 1 21:38:11 2026 -0400

    Omit default port from WebSocket handshake Host header (Fix #2480)
    
    The WebSocket upgrade request always appended ":port" to the Host
    header, violating RFC 6455 Section 4.1 which says the port should be
    included only when it is not the default (80 for ws, 443 for wss).
    Some CDNs alter routing when the Host header carries an explicit
    default port.
    
    Build the Host header with detail::make_host_and_port_string, which
    also brackets IPv6 literal hosts correctly.

commit f5c8c982df7989ae25f47129683962ea862d1ddd
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 1 21:22:15 2026 -0400

    Escape CR and LF in multipart part content types
    
    Same header-injection vector as the name/filename fix: item.content_type
    was concatenated into the part's Content-Type header unescaped, so
    embedded CR/LF could inject arbitrary part headers.
    
    Escape CR -> %0D and LF -> %0A via escape_multipart_field with a new
    escape_quote = false mode. '"' is left intact since it is legal in
    Content-Type values (e.g. quoted charset parameters) and appears outside
    a quoted-string context here.

commit 9e8b960e4457215354a98baa09173b1832839fb3
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 1 21:15:39 2026 -0400

    Add public MultipartFormDataWriter for multipart body serialization
    
    Building a multipart/form-data body outside Client::Post/Put/Patch
    (e.g. to feed a custom content provider or compose with other body
    sources) previously required calling several detail:: functions
    directly, which is not a stable interface.
    
    Introduce MultipartFormDataWriter, a small public class that owns the
    boundary and delegates to the existing detail:: serializers: whole-body
    serialization with known content length, and per-part framing
    (item_begin/item_end/finish) for streaming. Also expose
    is_valid_multipart_boundary() so callers using an explicit boundary can
    validate it without exceptions.

commit 8149bb38fc06d54537656a32ab0906879cbc431a
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 1 21:15:15 2026 -0400

    Escape quote, CR and LF in multipart field names and filenames
    
    detail::serialize_multipart_formdata_item_begin() concatenated item.name
    and item.filename into the Content-Disposition quoted-string without any
    escaping. A '"' in either value silently terminated the quoted-string
    early, and embedded CR/LF allowed injecting arbitrary part headers or
    forging part boundaries when a filename comes from external input.
    
    Escape both values following the WHATWG HTML standard ('"' -> %22,
    CR -> %0D, LF -> %0A), which matches what browsers send in
    multipart/form-data bodies. Behavior only changes for inputs that
    previously produced malformed HTTP.

commit 3fe32b63b42d7b273cd4d76d69df0097560375d6
Author: Saber Haj Rabiee <sabify@users.noreply.github.com>
Date:   Wed Jul 1 18:18:05 2026 -0700

    Send query string verbatim when path encoding is disabled (#2479)
    
    set_path_encode(false) only suppressed encoding of the path part. The
    query part was always run through normalize_query_string(), which
    decodes then re-encodes each key/value pair regardless of the flag.
    
    That round-trip is lossy for pre-encoded payloads: re-encoding emits
    sub-delimiters literally (%2C->",", %24->"$", %3B->";", ...) and turns
    %20 into "+", which a strict RFC 3986 server decodes back as "+"
    (0x2B) rather than a space (0x20), corrupting binary query data.
    
    Honor path_encode_ for the query as well: when disabled, append the
    caller-supplied query verbatim. Add a regression test asserting on the
    raw request target, since the server decodes "+" as space and would
    otherwise mask the difference.

commit 0ae93881b44bf94437843403bfcdc4f50445992e
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jul 1 20:12:44 2026 -0400

    Clarify comment on base64_encode accumulator signedness

commit a7b886b9cb982a415906490f8fdd0e7292dac878
Author: metsw24-max <metsw24@gmail.com>
Date:   Thu Jul 2 05:42:00 2026 +0530

    Use an unsigned accumulator in base64_encode (#2477)
    
    * use an unsigned accumulator in base64_encode
    
    * Forward-declare detail::base64_encode for split builds

commit 9d159bb412393eb7bc821fb193999cc44ef32636
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Thu Jun 18 20:13:57 2026 -0400

    Release v0.48.0

commit 9ac64e90db602cc4dad4501e762981ffa909e64f
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Thu Jun 18 19:50:46 2026 -0400

    Generate IP-host test certificates in CMake build
    
    The cert_ip_cn.pem and cert_ipv6.pem certificates added in ba390f2 were
    only generated by gen-certs.sh, which the Makefile-based Linux/macOS CI
    uses. The Windows CI builds with CMake, whose own certificate-generation
    block was not updated, so cert_ipv6.pem was missing there and
    SSLClientServerTest.TlsVerifyHostnameIpv6San failed on is_valid().
    
    Mirror the two openssl commands into test/CMakeLists.txt to keep both
    certificate-generation paths in sync.

commit ba390f23994cad46ebcd1497414d22a2ebf736de
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Thu Jun 18 12:37:16 2026 -0400

    Restrict IP-host hostname verification to iPAddress SANs on Mbed TLS and wolfSSL
    
    An IP-literal host must only be authenticated via a matching iPAddress SAN,
    never via the certificate's Common Name (RFC 9110), as the OpenSSL backend
    already does through X509_check_ip. The Mbed TLS and wolfSSL backends instead
    fell back to the CN when no IP SAN matched, and recognized IPv4 only.
    
    This is a more complete solution for #2476, which gated the CN fallback for
    IPv4 hosts only; here the same gap is closed for IPv6 as well, and IPv6
    iPAddress SANs are actually matched.
    
    - Add impl::parse_ip_address() to parse IPv4/IPv6 literals into raw bytes
    - Match IPv6 (16-byte) iPAddress SANs, not just IPv4
    - Skip the CN fallback for IP-literal hosts (both IPv4 and IPv6)
    - Remove the unused SSLClient::verify_host* dead code
    - Add regression tests and test certificates for the IP-host cases

commit 7307c41aa87adc194167b9badd2c2e6cfa12063b
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jun 17 23:21:36 2026 -0400

    Better `from_chars` implementation (Fix #2475)

commit df0b7d243b06860d8966688cda61dfbeeb55b755
Author: Arthur Chan <arthur.chan@adalogics.com>
Date:   Sun Jun 14 03:39:45 2026 +0100

    OSS-Fuzz: Add new fuzzer targets multipart parsing (#2473)
    
    * OSS-Fuzz: Add new fuzzer targets multipart parsing
    
    Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>
    
    * Fix formatting
    
    Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>
    
    ---------
    
    Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>

commit 28d95937b5a710436f367ab8e0e351fc2f3bb416
Author: metsw24-max <metsw24@gmail.com>
Date:   Fri Jun 12 23:24:04 2026 +0530

    use strict hex parsing in decode_query_component (#2472)

commit 75329322764a67cf27fc3b5ec0350b96287baaa6
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Fri Jun 12 13:20:15 2026 -0400

    Fix OpenSSL 4.0 deprecation warnings
    
    OpenSSL 4.0 deprecates X509_STORE_get0_objects() and
    X509_NAME_get_text_by_NID(), producing warnings on the OpenSSL
    backend (surfaced by Debian bug #1138434, which also reported a
    const-conversion build error already fixed in v0.43.1).
    
    - Fetch CA store objects via X509_STORE_get1_objects() (thread-safe,
      OpenSSL 3.3+) in get_ca_certs() and get_ca_names(), releasing the
      snapshot with a scope_exit guard; keep using get0 on older OpenSSL,
      BoringSSL, and LibreSSL
    - Extract the subject CN in get_cert_subject_cn() with
      X509_NAME_get_index_by_NID()/X509_NAME_get_entry() instead of the
      deprecated X509_NAME_get_text_by_NID()
    - Delegate SSLClient::verify_host_with_common_name() to
      tls::get_cert_subject_cn() to drop the remaining deprecated call
    
    Verified against OpenSSL 4.0.1, 3.6.2, and 3.0: builds warning-free
    and passes the full test suite.

commit 8829fac98e615f52a880bf758573806b39af9d9d
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jun 10 23:18:15 2026 -0400

    Use full git history in docs workflow for sitemap lastmod

commit fe332fa06bac76a1c6d402c08f414052999347da
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jun 10 00:24:05 2026 -0400

    Release v0.47.0

commit fa981cedae004ea9d946f1392b9dec22fac6fee6
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jun 10 00:07:27 2026 -0400

    Fix TLS chain verification bypass for IP hosts on Mbed TLS and wolfSSL
    
    For connections to IP-literal hosts with server certificate
    verification enabled, the Mbed TLS and wolfSSL backends downgraded the
    verification mode before the handshake because no hostname could be
    bound for in-handshake checks:
    
    - SSLClient skipped certificate chain validation entirely; only the
      post-handshake identity check (IP SAN match) remained, so any
      untrusted certificate carrying a matching IP SAN was accepted
    - The WebSocket client skipped verification altogether on Mbed TLS,
      accepting any certificate
    
    Keep the verification mode enabled for IP hosts and verify the
    certificate identity post-handshake via tls::verify_hostname(), which
    supports IP SANs on all backends. The WebSocket path now performs the
    same post-handshake identity check as SSLClient. On Mbed TLS, sessions
    explicitly opt out of in-handshake hostname verification (mandatory
    since Mbed TLS 3.6.4) and the post-handshake check covers identity
    instead; DNS hosts still bind the hostname during the handshake. Also
    stop sending SNI for IP hosts on Mbed TLS and wolfSSL (RFC 6066).

commit 39ec7d05082cd0c50f7a2fdb945352bf75de66ec
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Tue Jun 9 21:50:37 2026 -0400

    Add enable_system_ca() and unify WebSocketClient CA handling (#2471)
    
    Add an explicit opt-in for loading system CA certs alongside a custom
    CA, addressing the request in #2471. The default behavior is unchanged:
    a custom CA remains exclusive.
    
    - Add Client/SSLClient/WebSocketClient::enable_system_ca(bool); the
      policy carries over to redirect clients
    - Extract the CA loading policy into detail::load_client_ca_config()
      shared by SSLClient and WebSocketClient, making WebSocketClient
      exclusive by default (it previously always merged system certs)
    - Make the WebSocketClient TLS context live as long as the client,
      fixing a use-after-free when reconnecting after set_ca_cert_store()
    - Free the source store in the Mbed TLS and wolfSSL set_ca_store()
      backends, honoring the take-ownership contract (memory leak)
    - Verify IP hosts against IP SANs in the OpenSSL set_hostname() backend
      so WebSocket connections to IP hosts can use full verification

commit e7e7bf7b446252f5d9eaeff55c5a3da9da0ed3e2
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Tue Jun 9 20:46:26 2026 -0400

    Fix set_ca_cert_store() breaking CA exclusivity and redirect CA transfer
    
    Since the TLS abstraction layer was introduced, SSLClient::set_ca_cert_store()
    handed the store to the TLS context without leaving any trace on the client.
    As a result:
    
    - load_certs() merged system CA certs into the user-provided store,
      silently broadening the trust set (a custom store used to suppress
      system CA loading)
    - Client::load_ca_cert_store() went through the native store path,
      bypassing the PEM retention used for redirect transfer, so CA certs
      were not carried over to clients created for HTTPS redirects
    - The Windows Schannel verification skip for custom CA certs did not
      trigger
    
    Track custom store assignment with a flag checked by load_certs() and
    the Schannel path, and route Client::load_ca_cert_store() through the
    PEM-based SSLClient path so the CA data survives redirects.

commit 78ff94e6047938b5dbe4b4a321b9dd7c848b19ac
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Tue Jun 9 20:54:24 2026 -0400

    Wait for server startup before running benchmark

commit 018ce8e4ca194a42727f9c39cdc0fd8023673eab
Author: metsw24-max <metsw24@gmail.com>
Date:   Wed Jun 10 05:04:43 2026 +0530

    cast to unsigned char before ctype calls in is_hex and is_token_char (#2469)

commit 77bdf7921a21d18af7c4d82e6ab3e58e93e363ec
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Tue Jun 9 13:49:56 2026 -0400

    Read request body after route matching and pre-request handler
    
    Previously, for regular handlers the request body was read in routing()
    before the route was matched, so the pre-request handler always saw an
    already-read body. The ContentReader path, in contrast, ran the
    pre-request handler before the body was read. This inconsistency made
    it impossible to reject a request (e.g. failed per-route authentication
    via req.matched_route) without buffering a potentially large body.
    
    Move the read_content() call into dispatch_request(), after route
    matching and the pre-request handler, so both paths behave the same:
    route matching -> pre_request_handler -> body read -> handler. A
    request rejected by the pre-request handler no longer reads the body
    at all; the existing keep-alive drain logic still consumes any framed
    body afterwards.
    
    Note: code that referenced req.body or body-derived form fields inside
    the pre-request handler will now see an empty body. Inspect headers,
    path, query parameters, or matched_route instead.
    
    Also document the handler execution order in README and update the
    pre-request cookbook pages (en/ja).

commit 79d83feb1829dc6e44d636019e2883662c1b89ff
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sun Jun 7 17:08:11 2026 -0400

    Fix WebSocketClient dropping query string from URL during handshake (#2468)
    
    The constructor stored only uc.path in path_, discarding uc.query, so the
    WebSocket upgrade handshake sent the Request-URI without the query string.
    Append the query to path_ so query parameters (e.g. auth tokens) are sent.

commit fe56a07da5bc993cdf0e05704649fc6fd41f57c0
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat Jun 6 13:38:36 2026 -0400

    Wait for in-progress CI runs before releasing
    
    The release check treated runs with an empty conclusion as failures.
    Now it inspects each run's status and aborts with an error if any CI
    check is still running, so releases wait until CI completes.

commit 907257f51de5213b6f1febe983e7d39ad6e1118c
Author: Kim, Hyuk <hyukya@gmail.com>
Date:   Sat Jun 6 05:34:20 2026 +0900

    add set_hostname_addr_map to WebSocketClient (#2463)
    
    * add set_hostname_addr_map to WebSocketClient
    
    * add WebSocketTest unit test cases
    * SpecifyServerIPAddress_AnotherHostname
    * SpecifyServerIPAddress_RealHostname
    
    * Change wrong_ip from 0.0.0.0 to 192.0.2.1
    
    Use 192.0.2.1 (RFC 5737 documentation address) to ensure it acts
    as a non-routable address and does not alias to loopback.
    
    * Fix style check
    
    * set short timeout in WebSocketTest.SpecifyServerIPAddress_RealHostname
    
    cannot reach wrong_ip

commit 0c2f535b74f4aa32233a381805a15eb652a638b0
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Thu Jun 4 21:20:37 2026 -0400

    Fix #2467

commit c7ba963a17ee4139f3d8d8a31c727da494d2ac65
Author: Florian Fischer <60611027+florianfischerx@users.noreply.github.com>
Date:   Fri Jun 5 02:15:21 2026 +0200

    Ignore ranges for unknown-length streams (#2465)

commit 4465e81b9f7e08e2c88cc316db8dd6ddb6dafab6
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed Jun 3 22:24:52 2026 -0400

    Fix #2464

commit 44215e23e92c473a3553d24ae634aed6eefc7dd0
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Mon Jun 1 12:24:27 2026 -0400

    Release v0.46.1

commit 91219d450816b51ac1cdbcced6a7888b4a72ac8d
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Fri May 29 06:19:40 2026 -0400

    Fix #2458: send body when no 100 Continue arrives over TLS (#2460)
    
    The auto-added `Expect: 100-continue` (for bodies >= 1024 bytes) decided
    whether to withhold the request body based on raw socket readability via
    select_read(). Over TLS, post-handshake records such as TLS 1.3 session
    tickets make the socket readable without any HTTP response being
    available, so the client withheld the body and then blocked reading a
    response that never came, failing with `Failed to read connection`.
    
    Decide based on whether a status line can actually be read within the
    100-continue timeout instead: temporarily shorten the read timeout, try
    to read the status line, and if none arrives, send the body and proceed
    as usual (matching curl). This keeps the `100 Continue` and early
    final-response paths working while no longer being fooled by TLS records.
    
    Add a regression test using a raw OpenSSL server that never sends
    `100 Continue`.

commit c86c192f3e86ab2ccb5062c070274102a64780c3
Author: NsPro04 <64797367+NsPro04@users.noreply.github.com>
Date:   Fri May 29 04:19:33 2026 +0600

    Fix: (#2459)
    
    "httplib.h(5733,29): warning : missing field 'InternalHigh' initializer [-Wmissing-field-initializers]"
    "httplib.h(5742,28): warning : missing field 'ai_family' initializer [-Wmissing-field-initializers]"

commit 008e107d0fcddee7cb96dc5ad3c3189fd090e40a
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Mon May 25 00:30:27 2026 -0400

    Release v0.46.0

commit d278f965ccd05fe1ce908edb1a224a6730343e4e
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Mon May 25 00:21:57 2026 -0400

    Fix #2457

commit 4c4b62dd7e7ab59aec12c2c098f2084bdbc2d982
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Sun May 24 23:50:48 2026 -0400

    Feature 2446 no proxy env (#2448)
    
    * Route proxy-enabled checks through is_proxy_enabled_for_host helper
    
    In preparation for NO_PROXY support (#2446), centralize the proxy-enabled
    decision in a single helper so the upcoming bypass logic can be added in
    one place rather than to six divergent call sites. The helper's body for
    now is identical to the existing condition; the host parameter is unused
    until set_no_proxy() lands.
    
    Refactored sites:
      ClientImpl::create_client_socket
      ClientImpl::handle_request           (HTTP request rewrite)
      ClientImpl::setup_redirect_client
      ClientImpl::process_request          (SSL is_proxy_enabled flag)
      SSLClient::setup_proxy_connection
      SSLClient::ensure_socket_connection
    
    The two prepare_default_headers Proxy-Authorization injection blocks
    (currently gated only on proxy auth credentials being set) are
    intentionally not wrapped here. Doing so would change behavior in the
    rare misconfiguration case where credentials are set without set_proxy,
    so the gating is deferred to the NO_PROXY commit where it becomes
    meaningful.
    
    No behavior change. All 608 unit tests and the 22 squid-backed proxy
    tests pass.
    
    * Add detail::parse_proxy_url with control-char and scheme validation
    
    Building block for the upcoming set_proxy_from_env (#2446). Parses
    "http(s)://[user[:pass]@]host[:port][/...]" into a detail::ProxyUrl
    struct.
    
    Rejects:
      - empty input
      - any control character (< 0x20 or 0x7F), including CR/LF/NUL — these
        would otherwise let a malicious env value inject extra header lines
        into a CONNECT request or Proxy-Authorization header
      - schemes other than http and https
      - ports outside [1, 65535]
      - malformed IPv6 host literals (validated via inet_pton(AF_INET6))
      - non-numeric or trailing-garbage port strings
    
    Notes:
      - userinfo is split on the LAST '@' so passwords containing '@' are
        preserved in the password field
      - if no port is present, defaults to 80 (http) / 443 (https)
      - integer parse goes through detail::from_chars to stay compatible
        with -fno-exceptions builds
    
    The helper has no callers yet; it lands consumer-side when
    set_proxy_from_env arrives. All 608 unit tests pass.
    
    * Add NO_PROXY parsing and matching helpers in detail namespace
    
    Building blocks for the upcoming Client::set_no_proxy (#2446):
    
      - NoProxyEntry / NoProxyKind: parsed list entry (wildcard, hostname
        suffix, IPv4 CIDR, IPv6 CIDR)
      - NormalizedTarget: pre-normalized form of the connection's target
        host (lowercase, brackets stripped, trailing dot stripped, with
        inet_pton already attempted)
      - parse_no_proxy_entry / parse_no_proxy_list: token / list parsing.
        Port-specific entries are rejected by design — cpp-httplib's other
        host-keyed APIs (e.g. set_hostname_addr_map) are hostname-only, so
        supporting host:port for NO_PROXY alone would be inconsistent.
      - ipv4_in_cidr / ipv6_in_cidr: CIDR membership. IPv4 special-cases
        prefix=0 to avoid the (1u << 32) shift UB. IPv6 uses byte-wise
        memcmp plus a masked partial-byte compare.
      - normalize_target: prepares the target host for matching. Routes
        every IP literal through inet_pton so "127.0.0.1" vs
        "127.000.000.001" vs decimal-form integers cannot be used to bypass
        a NO_PROXY entry via alternate string forms.
      - host_matches_no_proxy: matches a normalized target against an
        entry list. Hostname suffix matching uses a dot-boundary rule so
        "evilexample.com" does NOT match the entry "example.com". IPv4 and
        IPv6 entries match only their own address family — IPv4-mapped IPv6
        ("::ffff:1.2.3.4") is not cross-matched against IPv4 entries.
    
    These helpers have no callers yet; they land consumer-side in the
    upcoming set_no_proxy / set_proxy_from_env commits. All 608 unit tests
    pass.
    
    * Add Client::set_no_proxy and wire NO_PROXY into proxy decision
    
    Implements the user-facing half of #2446 (set_proxy_from_env follows in
    the next commit). When a NO_PROXY pattern matches the target host, the
    client now bypasses the configured proxy and the corresponding
    Proxy-Authorization header is suppressed.
    
    Public API:
      - Client::set_no_proxy(const std::vector<std::string> &patterns)
        Patterns: "*", hostname suffix (e.g. "example.com" or
        ".example.com"), IPv4/IPv6 CIDR (e.g. "10.0.0.0/8", "fe80::/10"),
        or single IP literals. Replaces any previous list. Malformed
        entries are silently dropped.
    
    Internals:
      - is_proxy_enabled_for_host now consults no_proxy_entries_, normalizing
        the target through inet_pton so leading-zero or alternate-form IPs
        cannot be used to bypass an entry.
      - prepare_default_headers gates both Proxy-Authorization injection
        blocks (basic and bearer) on is_proxy_enabled_for_host(host_).
        Previously, Proxy-Authorization was sent whenever proxy auth
        credentials were configured, even when the request was going direct
        to the target. With NO_PROXY now in play, that path would leak
        proxy credentials to the destination server — analog of the
        redirect-leak class of bugs (cf. CVE-2023-32681 in Python requests,
        GHSA-6hrp-7fq9-3qv2 in cpp-httplib).
      - setup_redirect_client now takes the redirect target host as a
        parameter and re-evaluates is_proxy_enabled_for_host against it.
        no_proxy_entries_ is always copied to the redirect client so the
        bypass policy follows across redirects. This is the cross-origin
        leak surface that GHSA-c3h8-fqq4-xm4g lives in; centralizing the
        decision through is_proxy_enabled_for_host removes the chance of
        branch divergence.
      - copy_settings copies no_proxy_entries_.
    
    The slight behavior change for the rare misconfiguration "set
    proxy_basic_auth without set_proxy" — Proxy-Authorization is no longer
    sent in that case — is deliberate. The header has no addressee when
    the proxy is unset.
    
    All 608 unit tests and 22 squid-backed proxy integration tests pass.
    
    * Add Client::set_proxy_from_env with httpoxy mitigation
    
    Final user-facing piece for #2446. Reads proxy-related environment
    variables and configures the client.
    
      - HTTPS clients (SSLClient) read https_proxy / HTTPS_PROXY
      - HTTP clients read http_proxy (lowercase only — see below)
      - Both also read no_proxy / NO_PROXY
      - Returns true if at least one variable was found and applied
    
    The lowercase-only http_proxy rule mitigates httpoxy / CVE-2016-5385.
    In CGI / FastCGI environments the uppercase HTTP_PROXY collides with
    the HTTP_* namespace used to expose request headers, so a remote
    attacker controlling the "Proxy:" header can inject a proxy URL.
    cpp-httplib follows curl, Go, and Python requests in honoring only
    the lowercase form. https_proxy/HTTPS_PROXY and no_proxy/NO_PROXY do
    not have this problem because their names don't begin with HTTP_.
    
    Scheme dispatch uses virtual is_ssl(): an SSLClient picks
    https_proxy and a plain ClientImpl picks http_proxy. There is
    intentionally no cross-scheme fallback — the two variables describe
    different traffic.
    
    set_proxy_from_env() reads getenv() synchronously and is documented
    as "call once at startup" — concurrent setenv from other threads is
    undefined.
    
    All 608 unit tests pass.
    
    * Add NO_PROXY behavior tests
    
    27 black-box tests exercising the public Client API only (no detail::
    calls, BORDER-friendly; no EXPECT_NO_THROW, -fno-exceptions-friendly).
    
    In-process proxy mock + target server. Each test asserts which side
    of the routing decision each request landed on, and what headers (in
    particular Proxy-Authorization) the receiving side saw.
    
    Coverage:
    
      Suffix matching (dot-boundary rule)
        - exact-host match
        - subdomain match
        - "evilexample.com" does NOT match "example.com"  ← regression
          guard for the classic NO_PROXY suffix-match pitfall
        - "example.com.evil.com" does NOT match
        - leading-dot pattern still matches the bare domain (Go/curl
          convention)
        - case-insensitive
        - trailing-dot host normalization
    
      Wildcard
        - "*" bypasses everything
    
      IP normalization
        - exact IPv4 match
        - "::1" matches "0:0:0:0:0:0:0:1" via inet_pton
        - IPv4-mapped IPv6 ("::ffff:127.0.0.1") is NOT cross-matched
          against an IPv4 entry
    
      CIDR
        - basic v4 in-cidr / not-in-cidr
        - "0.0.0.0/0" (prefix=0; verifies no shift UB)
        - bare IP treated as /32
        - malformed prefix (/33) silently dropped → no NO_PROXY effect
    
      Proxy-Authorization handling
        - suppressed when NO_PROXY matches the target
        - sent when NO_PROXY does not match
    
      Backward compat
        - default behavior unchanged when set_no_proxy is never called
    
      Parsing edge cases
        - port-specific entries ("host:port") rejected
        - empty / whitespace tokens dropped
    
      Cross-origin redirect (analog of GHSA-6hrp-7fq9-3qv2)
        - redirect target in NO_PROXY → redirect leg goes direct, no
          Proxy-Authorization carried over
    
      set_proxy_from_env (Unix only — uses setenv/unsetenv)
        - lowercase http_proxy applied
        - uppercase HTTP_PROXY ignored (httpoxy / CVE-2016-5385)
        - NO_PROXY-only env returns true and applies the bypass list
        - CRLF in env value rejected (cf. CVE-2026-21428)
        - empty env value treated as unset
    
    635 tests (608 prior + 27 new) pass under both the regular and the
    split builds.
    
    * Document set_no_proxy and set_proxy_from_env in README
    
    Adds two subsections under "Proxy server support":
    
      - "Bypass the proxy for specific hosts (NO_PROXY)" — set_no_proxy,
        pattern syntax, dot-boundary rule, IP normalization, limitations
        (no port-specific entries, no v4-mapped v6 cross-match, replace
        semantics).
    
      - "Read proxy settings from the environment" — set_proxy_from_env,
        which variables are read, the lowercase-only http_proxy rule with
        an inline httpoxy / CVE-2016-5385 explanation, threading
        expectations.
    
    Documentation only. Closes the doc gap from #2446.
    
    * Document NO_PROXY and set_proxy_from_env in cookbook c16-proxy
    
    Replaces the now-incorrect Note at the bottom of c16-proxy ("cpp-httplib
    does not read HTTP_PROXY...") with the actual API.
    
    JA is the master per the project's translation workflow; the EN
    translation lands in the same PR. Both pages remain `status: "draft"`
    for normal review.
    
    Adds two sections:
    
      - Bypass the proxy for specific hosts (set_no_proxy):
        pattern syntax, dot-boundary rule, case-insensitivity, IP
        normalization via inet_pton, port-specific-entries unsupported,
        malformed entries dropped.
    
      - Read proxy settings from the environment (set_proxy_from_env):
        which variables are read, lowercase-only http_proxy with an
        inline httpoxy / CVE-2016-5385 explanation, threading caveat.
    
    * Simplify NO_PROXY implementation per review
    
    Apply seven post-implementation cleanups:
    
      - Move ProxyUrl, ProxyEnvSettings and most helper forward declarations
        below the BORDER. Only NoProxyKind/NoProxyEntry/NormalizedTarget stay
        above (they are used as ClientImpl members or by inline cache state).
        This shrinks the public header surface area considerably.
    
      - Drop ProxyUrl::scheme: the field was write-only after parsing. Track
        is_https as a local during parse_proxy_url and use it for the
        default-port branch directly.
    
      - Hoist the duplicate is_proxy_enabled_for_host(host_) gate in
        write_request: the previous form had two adjacent gates bracketing
        an unrelated end-server bearer-token block. Reordering puts the two
        proxy-auth blocks together under a single gate.
    
      - Drop the redundant trim_copy + empty-check inside parse_no_proxy_list:
        detail::split already trims each token and skips empties, so the inner
        work was dead code.
    
      - Cache normalize_target(host_) on the client. host_ is const, so the
        normalized form is invariant for the client's lifetime. The gate is
        called up to 7 times per request when NO_PROXY is configured;
        caching avoids repeating two heap allocations + two inet_pton calls
        per request. Cross-host calls (only setup_redirect_client passing
        next_host) still compute fresh.
    
      - Trim narrative comments in setup_redirect_client and
        set_proxy_from_env: replace WHAT-narration with single-line WHY
        statements.
    
      - Drop test comments that paraphrased their own test name.
    
    All 635 unit tests pass under both the regular and split builds.
    
    * Inline proxy URL parsing and env reading; drop intermediate structs
    
    The previous design had two intermediate structs that existed only to
    ferry parsed values between helper functions and the consuming method:
    
      - detail::ProxyUrl: filled by parse_proxy_url, drained back into
        proxy_host_ / proxy_port_ / proxy_basic_auth_* by set_proxy_from_env.
      - detail::ProxyEnvSettings: bundle of two ProxyUrl + a NoProxyEntry
        vector returned by read_proxy_env, drained by set_proxy_from_env.
    
    Both bundles had exactly one producer and exactly one consumer. Drop
    them and let the parsing flow directly into ClientImpl state:
    
      - New private member ClientImpl::apply_proxy_url(url) parses a proxy
        URL and, on success, assigns the result to proxy_host_, proxy_port_,
        and proxy_basic_auth_*. Same validation as before (CRLF rejection,
        scheme allowlist, port range, IPv6 bracket validation), same commit-
        on-success ordering — the local variables are kept until every check
        has passed so a malformed URL leaves no partial state.
    
      - set_proxy_from_env now reads getenv() directly, dispatches between
        https_proxy / http_proxy via virtual is_ssl(), and applies via
        apply_proxy_url. NO_PROXY is parsed in place via parse_no_proxy_list.
    
    Net effect:
    
      - Two structs and two free helper functions removed (~150 lines of
        declaration + body deleted).
      - set_proxy_from_env body grows ~20 lines (still well under 50).
      - Per-request hot path is unchanged (NoProxyEntry / NormalizedTarget
        cache stays). Setup path is marginally faster (no intermediate
        string copies through ProxyUrl / ProxyEnvSettings).
    
    635 unit tests pass under both the regular and split builds.
    
    * Trim doc comments to match the rest of httplib.h
    
    The new code carried inline doc comments (15-line set_no_proxy block,
    18-line set_proxy_from_env block, plus narrating comments inside parser
    bodies, plus section dividers in the test file) that were heavy
    compared to the rest of the codebase — neighboring setters like
    set_proxy / set_proxy_basic_auth carry no doc at all, the test file
    does not use sub-section dividers, and the README / cookbook already
    document the behavior in detail.
    
    Removed:
      - Public-API doc blocks on set_no_proxy and set_proxy_from_env.
      - Narrating comments inside parse_no_proxy_entry, normalize_target,
        apply_proxy_url, host_matches_no_proxy that were just describing
        the obvious code structure.
      - Multi-line BORDER-rationale meta comments.
      - In-test sub-section dividers ("// ---- Hostname suffix matching",
        etc.) and per-class doc comments on the test fixtures.
      - Test-side comments that paraphrased their own test name.
      - Redundant ordering comments inside setup_redirect_client.
    
    Kept:
      - Security WHY comments (CRLF rejection, dot-boundary suffix matching,
        httpoxy / CVE-2016-5385, GHSA-6hrp-7fq9-3qv2 analog, CVE-2026-21428).
      - Regression-target WHY comments (UB shift on prefix=0).
      - Non-obvious external knowledge (detail::split already trims).
    
    635 unit tests still pass under both the regular and split builds.
    
    * Add NO_PROXY tests covering edge cases found during PR review
    
    Three regression guards added during review of an alternate NO_PROXY
    implementation (PR #2449). All three pass on the current implementation
    and surface bugs in the alternate one:
    
      - BareIPv6LiteralMatchesIPv6Cidr: a host given as a bare IPv6 literal
        (no surrounding brackets) must still be recognized as IPv6 for CIDR
        matching. An implementation that only detects IPv6 when the host
        string starts with '[' would split the host at the first ':' and
        misclassify it as a hostname.
    
      - TrailingDotOnEntryIsNormalized: trailing dots must be canonicalized
        on BOTH sides — host and entry. An implementation that strips the
        host-side trailing dot only would fail to match host "example.com"
        against entry "example.com." because the substring lengths differ.
    
      - ValidEntryWithSurroundingWhitespaceStillMatches: an entry with
        leading/trailing whitespace must still match. An implementation
        that feeds raw tokens directly to inet_pton would reject valid
        CIDRs ("  10.0.0.0/8  ") because of the spaces.
    
    635 unit tests pass.
    
    * Unify IPv4/IPv6 CIDR matching into a single byte-buffer helper
    
    Adopts the unified 16-byte address representation suggested by the
    alternate NO_PROXY implementation in PR #2449. Both v4 and v6 entries
    now share one storage type and one matcher; the v4/v6 distinction is
    only the address-family flag and the max prefix length.
    
      - detail::NoProxyEntry: replaces in_addr v4_net + in6_addr v6_net
        with a single IPBytes net (std::array<uint8_t, 16>). v4 occupies
        the first 4 bytes, v6 fills all 16.
      - detail::NormalizedTarget: replaces in_addr v4 + in6_addr v6 with
        a single IPBytes ip.
      - Replaces detail::ipv4_in_cidr and detail::ipv6_in_cidr with one
        detail::ip_in_cidr that takes the address, the network, the prefix
        length and the family's max bits (32 for v4, 128 for v6). The mask
        is constructed by the byte-fill approach from the previous v6
        helper, which is straightforward to read and avoids the shift UB
        that the v4 helper had to special-case.
      - The NoProxyKind enum keeps IPv4Cidr / IPv6Cidr as separate values
        so the match dispatch stays explicit and IPv4 entries cannot
        accidentally cross-match an IPv6 target (the same address-family
        isolation the previous code had).
    
    Net change: -28 lines + -1 helper function. All 30 NoProxyTest cases
    plus 643 unit tests pass under both the regular and split builds.
    
    * Drop set_proxy_from_env per #2446 discussion
    
    Per @unterwegi's feedback in #2446, environment variable handling
    conflicts with cpp-httplib's long-standing policy of explicit
    configuration (e.g. set_ca_cert_path requires explicit paths instead
    of reading SSL_CERT_FILE / SSL_CERT_DIR). The NO_PROXY matching logic
    is the genuinely tricky part worth keeping in the library; getenv
    parsing is trivial and is left to the caller.
    
    - Remove Client::set_proxy_from_env, ClientImpl::set_proxy_from_env,
      and ClientImpl::apply_proxy_url
    - Remove ScopedEnv test helper and env-driven NoProxyTest cases
    - Replace the "Read proxy settings from the environment" docs with a
      short snippet showing how to parse no_proxy and feed set_no_proxy()
    - Keep set_no_proxy() and all NO_PROXY pattern matching intact
    
    * docs: blend NO_PROXY env-var note into c16-proxy cookbook style
    
    Match the granularity of the surrounding sections: imperative heading,
    inline paragraph instead of a heavyweight callout, and a simpler getenv
    snippet without the C++17 if-init.
    
    * Skip digest 407 retry when target is bypassed by NO_PROXY
    
    Before this fix, a NO_PROXY-bypassed origin that returns
    407 Proxy-Authentication-Required with a Digest challenge would
    trigger the same retry path the proxy uses, computing a
    Proxy-Authorization header from proxy_digest_auth_* and sending the
    user's proxy credentials directly to that (potentially hostile)
    origin.
    
    A 407 from a direct origin is semantically meaningless — RFC 9110
    defines it strictly as a proxy response. Skip the retry when the
    current target is not actually going through the proxy and let the
    407 propagate to the caller unchanged.
    
    Regression test BypassedTargetReturning407DoesNotLeakProxyDigest
    Credentials reproduces the leak without this gate.
    
    * Make set_no_proxy safe across redirects and keep-alive
    
    Two correctness bugs that the dynamic NO_PROXY API exposed:
    
    1. Multi-hop redirect through a bypassed host lost the proxy.
       setup_redirect_client only copied proxy_host_/port and the proxy auth
       credentials when is_proxy_enabled_for_host(next_host) was true. After
       a chain like A (proxied) -> B (NO_PROXY-matched, direct) -> C, the
       redirect client built for B had no proxy configured, so the further
       B -> C hop went direct even when C should have been proxied. Copy the
       proxy configuration unconditionally and let is_proxy_enabled_for_host
       gate at send time. The next_host parameter is no longer needed and
       removed from the signature.
    
    2. Keep-alive socket reuse with a stale bypass decision. set_proxy() /
       set_no_proxy() left the existing keep-alive socket open, so the next
       request reused a socket pointed at the previous endpoint (proxy vs
       origin) while write_request emitted the new request-line form
       (absolute vs relative URL). Add invalidate_keep_alive_socket() and
       call it from both setters; the helper handles the in-flight case by
       deferring the close.
    
    Regression tests MultiHopRedirectThroughBypassedHostKeepsProxy and
    KeepAliveSocketInvalidatedOnSetNoProxy reproduce each bug without the
    respective fix.
    
    * Tighten NO_PROXY entry parsing
    
    Three small parser fixes surfaced during code review:
    
    - Accept bracketed IPv6 entries like "[::1]" and "[fe80::]/10". Users
      coming from URL syntax naturally write the bracketed form; previously
      it was silently rejected because inet_pton does not accept brackets
      and the subsequent ':' check tripped.
    - Reject malformed trailing-slash CIDRs like "127.0.0.1/" instead of
      silently treating them as /32 (or /128). A typoed entry quietly
      turning into a single-host bypass changes semantics with no
      diagnostic.
    - Delete detail::parse_no_proxy_list — leftover from the removed
      set_proxy_from_env path, no longer called from anywhere.
    
    New regression tests: BracketedIPv6EntryAccepted,
    BracketedIPv6CidrEntryAccepted, TrailingSlashCidrIsRejected.
    
    * Refactor: introduce disconnect() and remove invalidate_keep_alive_socket
    
    Replace the repeated `shutdown_ssl + shutdown_socket + close_socket`
    pattern with a single `disconnect(bool gracefully)` helper. Used by
    `stop()`, the send_() peer-closed and epilogue branches, and the close
    in process_request after a non-keep-alive response.
    
    Drop `invalidate_keep_alive_socket()` — its body collapses to a
    `lock + disconnect()` pair which is now inlined in `set_proxy()` and
    `set_no_proxy()` directly.
    
    Also simplify `setup_redirect_client`: drop the now-unused next_host
    parameter and the verbose comment block; the per-target proxy decision
    is re-evaluated at send time anyway.
    
    Net -47 lines in httplib.h.
    
    * Fix MultiHopRedirect test on Windows; trim NoProxyTest comments
    
    The bypass leg redirected to "http://localhost:<port>/...", but on
    Windows `localhost` resolves to ::1 first while the mock server is
    bound to 127.0.0.1, causing the redirect leg to time out. Use the
    literal 127.0.0.1 in the Location and switch the NO_PROXY entry to
    match, so the test exercises the same multi-hop path on every
    platform.
    
    Also trim the heavier inline comments and EXPECT messages I added on
    recent NoProxyTest cases so they match the surrounding test style.
    
    * Consolidate NoProxyTest server boilerplate; drop hardcoded sentinel ports
    
    Add a small ScopedServer helper to no_proxy_test that wraps the
    bind/listen/thread/cleanup dance (~13 lines per server before). Use it
    to rewrite the four big tests (Redirect, BypassedTarget407, MultiHop,
    KeepAlive), shaving ~100 lines.
    
    Also drop the hardcoded port-1 / port-80 sentinels that violated the
    "new standalone tests MUST use bind_to_any_port" convention and risked
    collisions across gtest shards: re-use existing dynamic ports
    (target.port() / bypass_server.port()) instead.
    
    Verified pass under 4-shard parallel run.
    
    * Trim README NO_PROXY section to match surrounding granularity
    
    The block had ballooned to 62 lines while neighboring subsections
    (Authentication, Proxy server support, Range, Redirect) are 13-18 each.
    Collapse to a single code example + one-line behavior summary; point at
    the cookbook for the entry-form details, env-var parsing snippet, and
    httpoxy note that used to live inline.

commit b1792ef29ca66f8dcb44b1678e597676a33a3f1b
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sun May 24 20:58:48 2026 -0400

    Release v0.45.1

commit 0f3d063f0a6580307e9a9ac2a7bc8e8e167a86fe
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Sun May 24 02:48:46 2026 -0400

    ci: add best-effort BoringSSL job (#2456)
    
    Adds Ubuntu and macOS CI jobs that build BoringSSL from source and exercise cpp-httplib's existing OpenSSL backend path (continue-on-error: best-effort). Makes SSLClientServerTest.TlsVerifyHostname backend-aware (BoringSSL is SAN-only per RFC 6125 §6.4.4). README notes BoringSSL as a best-effort variant with the C++14 and SAN-only caveats.

commit 0d7d6374666956c9f5e2767573fb7be0d4477d16
Author: sakurai-ryuhei <50360667+sakurai-ryuhei@users.noreply.github.com>
Date:   Sun May 24 00:59:58 2026 +0900

    Fix zstd detection in installed httplibConfig.cmake (#2453)

commit 1ff0c8588d7a2c1baa251e8d615e86301526d8ec
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Sat May 23 08:39:45 2026 -0400

    Fix iOS build break and modernize macOS Keychain cert loading (#2455)
    
    * Replace deprecated SecTrustCopyAnchorCertificates on macOS
    
    SecTrustCopyAnchorCertificates was deprecated in macOS 13. Switch to
    SecTrustSettingsCopyCertificates, iterating over the System, Admin, and
    User trust domains to retain equivalent coverage of anchor certificates.
    
    * Restrict Keychain cert loading to macOS
    
    TARGET_OS_MAC is true on all Apple platforms including iOS, tvOS, and
    watchOS, which caused the keychain enumeration path to be compiled on
    iOS where SecTrustSettingsCopyCertificates is unavailable.
    
    Narrow the auto-enable and the Security.h include guards to
    TARGET_OS_OSX, and emit an explicit #error when the user defines
    CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN on a non-macOS Apple platform,
    directing them to use set_ca_cert_path() with a bundled CA file.
    
    Addresses the iOS build break reported in #2454.
    
    * Add iOS header parse check to CI
    
    Run a cross-compile syntax check against the iOS SDK to catch
    accidental use of macOS-only APIs or guards (e.g. TARGET_OS_MAC vs
    TARGET_OS_OSX) that would silently break iOS builds. Also verify that
    defining CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN on iOS fires the
    expected #error.
    
    iOS is not officially supported as a runtime target; this job only
    guarantees the header stays parse-clean on iOS toolchains.

commit b1cc8095a8feca1b99e296ef78eddd312e80d2c6
Author: NsPro04 <64797367+NsPro04@users.noreply.github.com>
Date:   Sat May 16 19:50:08 2026 +0600

    Specifying "Server::stop()" as noexcept (#2451)
    
    * The current implementation of "Server::stop()" doesn't throw an exception, so why not specify this explicitly?
    
    * Adding the missing "noexcept" to the declaration

commit 28f8264d134a576422cd0f99f19719c2749d9e47
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Fri May 15 09:22:11 2026 +0900

    Release v0.45.0

commit 91271c062d3a357d7e3ef5152db5495b7d6ba95a
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Fri May 15 06:57:51 2026 +0900

    Fix keep-alive corruption on requests without framed body (#2450)

commit d755c43d586b6ca49a62a3f4ad8ba57fbff90d0d
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Fri May 15 06:56:16 2026 +0900

    Extract has_framed_body and is_connection_persistent helpers

commit 5c9285776ee1803d8dc5f3ced5e3179454bd6bbd
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Thu May 14 23:19:36 2026 +0900

    Fix crash on empty X-Forwarded-For with trusted proxies configured

commit 811dd0b6f2382f50f801f3cfdb5ba349d08bb575
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sun May 10 21:46:24 2026 +0900

    Release v0.44.0

commit e8e652824b93a6f2e8a9dcd2f9967bea4d9302c3
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sun May 10 13:24:57 2026 +0900

    Add --minor flag to release.sh for forced minor bumps
    
    Allows forcing a minor version bump even when abidiff passes,
    for behavioral breaking changes that don't break ABI.

commit fbb031ed8504a0e48d9ca63caecc0614c296f755
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sun May 10 12:40:33 2026 +0900

    Stop percent-decoding HTTP request header values
    
    parse_header() applied decode_path_component() to every header value
    except Location and Referer, after is_field_value() validation. Wire
    sequences like %0D%0A passed the check and expanded into literal CR/LF
    inside stored values, enabling response splitting, log injection, and
    proxy smuggling. %3D/%2C/%3B also flipped Cookie and X-Forwarded-For
    boundaries against WAFs inspecting the wire form.
    
    RFC 9110 §5.5 specifies header values as opaque octets. Drop the
    decoding and the Location/Referer special case (originally workarounds
    for the same auto-decode misbehavior; redundant once decoding stops).
    Applications that need URI semantics should call decode_uri_component()
    or decode_path_component() on the result explicitly.
    
    Add regression tests covering CRLF injection, %3D/%2C/%3B boundary
    characters, UTF-8 and %uXXXX sequences, browser-style Referer URLs
    containing %0A (issue #2033), and the explicit-decode migration
    pattern.

commit 7d5082cc0e6e9b8e5e6c3d7daf18e7b7331569d6
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Sat May 9 21:13:40 2026 -0400

    Make ThreadPool ctor exception-safe on partial thread creation (#2445)
    
    * Make ThreadPool ctor exception-safe on partial thread creation
    
    If std::thread construction throws partway through the ThreadPool
    constructor (e.g., pthread_create returns EAGAIN under thread-resource
    pressure), the partially-built threads_ vector would destruct joinable
    std::thread objects, calling std::terminate(). Wrap the spawn loop and,
    on failure, signal shutdown to the workers already created, join them,
    and rethrow.
    
    Adds a reproducer test in test_thread_pool.cc that interposes
    pthread_create at link time to deterministically fail the second call,
    gated to POSIX + exceptions-enabled builds.
    
    Fix #2444
    
    * Strip ASAN from test_thread_pool to coexist with pthread_create override
    
    Linux libasan installs its own pthread_create interceptor; our in-binary
    symbol override sits on top of it and corrupts ASAN's thread bookkeeping,
    which surfaces as "Joining already joined thread" on the very first test.
    Disable ASAN for this small unit-test binary -- ThreadPool memory behavior
    is still exercised under ASAN by the main `test` binary.

commit 600d220c84853f52011ae4b95fc8518742bcdfac
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat May 9 21:29:23 2026 +0900

    Release v0.43.4

commit 87d62db46b37a660f64bc5663162e993752dd14a
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Sat May 9 16:52:32 2026 +0900

    Reject malformed chunk-size in chunked decoder
    
    strtoul silently accepts a leading "-" and wraps via unsigned
    arithmetic, so chunk-size "-2" produced ULONG_MAX-1, bypassing the
    ULONG_MAX guard and letting a client drive the server toward unbounded
    allocation.
    
    Replace strtoul with a manual hex parser that requires at least one hex
    digit, detects size_t overflow per digit, and accepts only chunk-ext or
    end-of-line after the digits (RFC 9112 §7.1).

commit a1fdc07f34ef4788b24c7be3c3fb41a402409662
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Wed May 6 08:36:38 2026 -0400

    Guard nullptr res in KeepAliveTest proxy template (#2443)
    
    When the upstream request to httpbingo.org transiently fails, cli.Get()
    returns nullptr and the next line dereferences it (res->status / res->body),
    producing a SEGV in std::string::begin() under ASan. Sibling templates in
    the same file already use ASSERT_TRUE(res != nullptr); apply the same
    guard to the four Get() call sites in KeepAliveTest so a flaky network
    turns into a clean test failure instead of a crash.

commit eb49a304b6ecbb200e94e720446ae540067ea9a4
Author: yhirose <yhirose@users.noreply.github.com>
Date:   Wed May 6 08:25:56 2026 -0400

    Use vswhere to locate VS install in 32-bit Windows CI (#2442)
    
    The hosted windows-latest runner is migrating from VS 2022 to VS 2026
    (NOTICE: windows-2025 -> windows-2025-vs2026 by 2026-05-12). The
    hardcoded path C:\Program Files\Microsoft Visual Studio\2022\Enterprise
    no longer exists on the new image, so vcvarsall.bat silently fails and
    'cl' is not on PATH.
    
    Resolve the install path via vswhere.exe (stable location, version
    agnostic) and exit if vcvarsall.bat fails so future breakage surfaces
    immediately instead of as a confusing 'cl not recognized' error.

commit a9bfe5914b2c00415a818d62a43b462d93e08a64
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Wed May 6 18:44:14 2026 +0900

    Fix #2441

commit ec5ce17929c56c8bb7f0307ce2af496bd017a3c9
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Mon May 4 16:19:49 2026 +0900

    Release v0.43.3

commit f6524c08020aa1e8732d153ca57c64931d382b62
Author: yhirose <yuji.hirose.bug@gmail.com>
Date:   Fri May 1 22:20:41 2026 +0900

    Drop Str2tagTest unit test that broke split / no-exceptions builds
    
    The test referenced detail::can_compress_content_type, which lives below
    the split BORDER in httplib.h and is therefore not visible to test.cc in
    test_split / Windows-CMake builds. EXPECT_NO_THROW also expanded to a
    try/catch that would not compile under -fno-exceptions. The OSS-Fuzz
    reproducer in test/fuzzing/corpus already serves as the regression test
    for #508087118 and is exercised by make fuzz_test.
Created: 2026-11-04 Last update: 2026-11-04 21:02
lintian reports 1 warning normal
Lintian reports 1 warning about this package. You should make the package lintian clean getting rid of them.
Created: 2026-02-28 Last update: 2026-02-28 15:00
Standards version of the package is outdated. wishlist
The package should be updated to follow the last version of Debian Policy (Standards-Version 4.7.4 instead of 4.7.2).
Created: 2026-05-16 Last update: 2026-05-16 18:19
testing migrations
  • This package will soon be part of the auto-openssl transition. You might want to ensure that your package is ready for it. You can probably find supplementary information in the debian-release archives or in the corresponding release.debian.org bug.
news
[rss feed]
  • [2026-06-13] Accepted cpp-httplib 0.41.0+ds-3~bpo13+1 (source amd64) into stable-backports (Debian FTP Masters) (signed by: Mario Limonciello)
  • [2026-05-03] Accepted cpp-httplib 0.18.7-1+deb13u1 (source) into proposed-updates (Debian FTP Masters) (signed by: Andrea Pappacoda)
  • [2026-04-22] Accepted cpp-httplib 0.18.7-1+deb13u1 (source) into stable-security (Debian FTP Masters) (signed by: Andrea Pappacoda)
  • [2026-04-21] cpp-httplib 0.41.0+ds-3 MIGRATED to testing (Debian testing watch)
  • [2026-04-17] Accepted cpp-httplib 0.41.0+ds-3 (source) into unstable (Andrea Pappacoda)
  • [2026-04-13] Accepted cpp-httplib 0.41.0+ds-2 (source) into experimental (Andrea Pappacoda)
  • [2026-04-11] Accepted cpp-httplib 0.41.0+ds-1 (amd64 source) into experimental (Debian FTP Masters) (signed by: Andrea Pappacoda)
  • [2025-09-21] Accepted cpp-httplib 0.26.0+ds-2 (amd64 source) into experimental (Debian FTP Masters) (signed by: Andrea Pappacoda)
  • [2025-08-17] Accepted cpp-httplib 0.25.0+ds-1 (amd64 source) into experimental (Debian FTP Masters) (signed by: Andrea Pappacoda)
  • [2025-06-20] Accepted cpp-httplib 0.20.1+ds-3 (amd64 source) into experimental (Debian FTP Masters) (signed by: Andrea Pappacoda)
  • [2025-03-14] cpp-httplib 0.18.7-1 MIGRATED to testing (Debian testing watch)
  • [2025-03-11] Accepted cpp-httplib 0.18.7-1 (source) into unstable (Andrea Pappacoda)
  • [2024-10-06] Accepted cpp-httplib 0.18.0+ds-1 (source arm64) into experimental (Debian FTP Masters) (signed by: Christian Hofstaedtler)
  • [2024-09-20] cpp-httplib 0.16.3+ds-2 MIGRATED to testing (Debian testing watch)
  • [2024-09-17] Accepted cpp-httplib 0.16.3+ds-2 (source) into unstable (Andrea Pappacoda)
  • [2024-08-21] cpp-httplib 0.16.3+ds-1 MIGRATED to testing (Debian testing watch)
  • [2024-08-18] Accepted cpp-httplib 0.16.3+ds-1 (source) into unstable (Andrea Pappacoda)
  • [2024-08-03] Accepted cpp-httplib 0.16.0+ds-1 (source amd64) into experimental (Debian FTP Masters) (signed by: bage@debian.org)
  • [2024-07-10] cpp-httplib 0.15.3+ds-3 MIGRATED to testing (Debian testing watch)
  • [2024-07-07] Accepted cpp-httplib 0.15.3+ds-3 (source) into unstable (Andrea Pappacoda)
  • [2024-06-26] cpp-httplib 0.15.3+ds-2 MIGRATED to testing (Debian testing watch)
  • [2024-06-23] Accepted cpp-httplib 0.15.3+ds-2 (source) into unstable (Andrea Pappacoda)
  • [2024-06-23] Accepted cpp-httplib 0.15.3+ds-1 (source amd64) into experimental (Debian FTP Masters) (signed by: bage@debian.org)
  • [2024-05-03] cpp-httplib 0.14.3+ds-1.1 MIGRATED to testing (Debian testing watch)
  • [2024-02-28] Accepted cpp-httplib 0.14.3+ds-1.1 (source) into unstable (Steve Langasek)
  • [2024-01-30] Accepted cpp-httplib 0.14.3+ds-1.1~exp1 (source amd64) into experimental (Michael Hudson-Doyle)
  • [2023-12-27] cpp-httplib 0.14.3+ds-1 MIGRATED to testing (Debian testing watch)
  • [2023-12-24] Accepted cpp-httplib 0.14.3+ds-1 (source) into unstable (Andrea Pappacoda)
  • [2023-12-14] cpp-httplib 0.14.2+ds-1 MIGRATED to testing (Debian testing watch)
  • [2023-12-11] Accepted cpp-httplib 0.14.2+ds-1 (source) into unstable (Andrea Pappacoda)
  • 1
  • 2
bugs [bug history graph]
  • all: 9
  • RC: 0
  • I&N: 7
  • M&W: 2
  • F&P: 0
  • patch: 1
links
  • homepage
  • lintian (0, 1)
  • buildd: logs, reproducibility, cross
  • popcon
  • browse source code
  • other distros
  • security tracker
  • debci
ubuntu Ubuntu logo [Information about Ubuntu for Debian Developers]
  • version: 0.26.0+ds-2ubuntu4
  • patches for 0.26.0+ds-2ubuntu4

Debian Package Tracker — Copyright 2013-2025 The Distro Tracker Developers
Report problems to the tracker.debian.org pseudo-package in the Debian BTS.
Documentation — Bugs — Git Repository — Contributing