Security
- Strip credentialed headers on unsafe redirects.
Authorization,
Proxy-Authorization, andCookieare now removed whenever a redirect
crosses origins (scheme/host/port) or downgrades fromhttps://to
http://. Previously, a compromised or malicious upstream could capture
tokens by returning aLocation:pointing at an attacker domain. Auth
helpers re-apply their own headers on every attempt, so same-origin
chains still work transparently. (C1) - Reject CRLF / NUL / non-token characters in request construction.
Header names must now be valid RFC 7230 tokens; header values, request
methods, request targets, URL hostnames, multipart field names, and
multipart filenames are rejected if they contain\r,\n, or\0.
Previously these were written verbatim to the wire, enabling HTTP
request-smuggling, header injection, and multipart-part smuggling. (C2) - Cap decompressed response size. New
max_decompressed_size
Clientknob (default 64 MiB) applied inside every decoder
(gzip/deflate/brotli/zstd). Brotli input is also bounded pre-decode
because the C decoder cannot stream, which previously made a ~1 KB
brotli payload inflatable to unlimited memory. Raises
hyperhttp.DecompressionError. (H1) - Consumer-driven HTTP/2 flow control. Per-stream DATA queues are
now bounded (32 chunks) andacknowledge_received_datafires only
after the consumer drains each chunk. Previously the reader pre-acked
every DATA frame, letting a hostile server stream an unbounded body
regardless of the advertised window. (H2) - Cap response body size. New
max_response_sizeClientknob
enforces a maximum raw-body byte count across both the streaming
(aiter_bytes,aiter_raw) and materialising (aread) paths. An
oversizeContent-Lengthis rejected before any bytes are read.
Raiseshyperhttp.ResponseTooLarge. (H3) - Fix pool waiter cancellation race. A transport handed off to a
waiter whoseacquire()had just timed out is now reclaimed back to
the pool. Previously the connection could be stranded in the active
set, slowly exhaustingmax_connections. (M1) - Never leak
Proxy-Authorizationto origin servers. The H1
transport now strips any user-suppliedProxy-Authorizationheader
before writing the request head; on absolute-form (plain HTTP via
HTTP proxy) the value from the proxy URL always wins. H2 already
filtered this hop-by-hop header. (M2) - Warn on
verify=False. Disabling TLS verification now emits
hyperhttp.InsecureRequestWarningatClientconstruction and
on everycreate_ssl_contextcall, so misconfiguration surfaces
in tests/CI instead of shipping silently. (M3) - Scrub URLs in retry logs; validate redirect references. The
retry handler now logsURL.sanitized()(no query string, no
userinfo), so retry loops don't exfiltrate?api_key=or
user:pass@credentials.URL.join()rejects CR/LF/NUL/TAB in
the redirect reference. NewURL.sanitized()helper.
Added
hyperhttp.InsecureRequestWarning(exported from top-level).hyperhttp.ResponseTooLarge,hyperhttp.DecompressionError
exceptions (exported).hyperhttp.LocalProtocolErroris now exported from the top level.Client(max_response_size=..., max_decompressed_size=...)knobs.URL.sanitized()returns a log-safe string with query and userinfo
redacted.
Changed
-
HTTP/1 header insertion (
Headers.add,Headers.set, bulk update,
constructor) now validates names and values. This replaces silent
corruption with an explicitLocalProtocolErrorfor anything that
would corrupt the wire framing. -
Clientnow emitsInsecureRequestWarningat construction when
verify=Falseis passed (and nossl_contextoverride is provided). -
MockTransport/MockResponse/Router: in-memory transport for
tests. Accepts a callable handler (sync or async), a replay sequence,
a single response, or a route mapping. Pass as
Client(transport=MockTransport(...))— the entire production stack
(retries, auth, event hooks, cookies, redirects) runs, only the socket
is replaced. Recorded calls are exposed viamock.calls,mock.call_count,
mock.last_request, andmock.reset(). Handlers can raise any
hyperhttpexception to exercise failure paths. -
Event hooks:
Client(event_hooks={"request": [...], "response": [...]}).
Hooks are sync or async callables that fire once per network attempt,
unlocking OpenTelemetry / distributed tracing, structured logging,
request signing (AWS SigV4, OAuth1), and per-request metrics. The
event_hooksdict is also writable on a live client. -
Authentication helpers:
hyperhttp.BasicAuth,hyperhttp.BearerAuth,
hyperhttp.DigestAuth, and the extensiblehyperhttp.Authbase class.
Configure withClient(auth=...)or per-requestclient.get(..., auth=...).
auth=("user", "pass")is shorthand forBasicAuth;auth=Noneon a
request disables a client-level default.DigestAuthhandles the
401 → WWW-Authenticate → retryround-trip and supports MD5, SHA-256,
SHA-512-256, their-sessvariants, andqop=auth. -
HTTP and HTTPS proxy support via the new
proxies=andtrust_env=client
arguments. HTTPS targets are tunnelled viaCONNECT; environment variables
(HTTP_PROXY,HTTPS_PROXY,ALL_PROXY,NO_PROXY) are honoured by
default. Newhyperhttp.ProxyURLhelper andhyperhttp.ProxyErrorexception. -
multipart/form-datauploads viaClient.post(files=..., data=...). File
parts stream directly from disk in 1 MiB chunks with a pre-computed
Content-Length(no chunked framing overhead), so a 10 GiB upload uses
O(chunk) memory. Newhyperhttp.MultipartEncoderand
hyperhttp.MultipartFilefor advanced control. Local benchmark
(examples/benchmark_multipart.py) measures ~3.8 GiB/s on 100 MiB
loopback uploads — 2.7× httpx and 4.4× aiohttp on the same host. -
H1 transport now honours explicit
Content-Length(or acontent_length
attribute) on async-iterable bodies, skipping chunked framing whenever the
size is known up front. -
py.typedmarker so downstream type checkers pick up the public type hints. -
SECURITY.mdwith a disclosure policy.
Changed
- Minimum supported Python is now 3.9. Classifiers and tool configs
(black,ruff,mypy) updated accordingly.