Repository navigation
httpjet 0.0.5 — whole-codebase audit remediation
This release fixes the 15 findings of a whole-codebase bug sweep (2026-10-12: four parallel reviewers across the transport, page-cache, LSAPI/rewrite, and proxy/ACL/TLS surfaces, every finding hand-verified in source and the worst one reproduced dynamically). Every fix ships with a regression test. Dependencies are refreshed to their latest releases; the toolchain stays at Rust 1.99.
Availability (high)
- Rewrite
[S=n]infinite loop. A rule such asRewriteRule ^x$ - [S=18446744073709551615]wrapped the evaluator's rule index back onto itself in release builds, pinning a worker core forever per matching request. Skip advance now saturates past the end of the ruleset. - Unbounded HTTP/2 upstream handshake in the proxy. The
h2:///h2s://handshake could stall indefinitely at the IO layer, pinning a permit from the process-wide dial budget (64) plus an upstreammaxConnsslot. It now runs under the connect budget, mirroring the health-check probe. - HTTP/2 reset without teardown. After the server sent
RST_STREAMfor a body-pending stream (zeroWINDOW_UPDATEincrement, self-referentialPRIORITY), a laterDATA … END_STREAMstill completed and dispatched the already-reset request. The reset path now tears down the receiving stream and returns its buffered-body accounting.
Proxy correctness
- WebSocket targets enforce
maxConns. Default (non-load-balanced) WebSocket upstreams acquired no concurrency permit at all; the long-lived relay was bounded only by global client limits. A permit is now acquired and held for the relay's lifetime. - Failover survives config reloads. Retained upstreams kept writing dial failures to the previous generation's cooldown counter, so after a reload the new pool never saw the primary as bad and fast-failed instead of failing over. Cooldowns are carried into the retained generation and handles are rebound on selection.
Request handling and PHP trust boundary
- Forwarding-header synonyms. The blocklist for proxy-asserted identity headers was an exact-name list;
X-Client-IP,X-Forwarded-Protocol,Proxy-Client-IP, and similar synonyms from untrusted clients reached PHP as spoofable$_SERVERvalues. Matching is now class-based (the*-client-ip,x-forwarded-*, and*-real-ipfamilies, dash/underscore-folded) and fails closed. - CGI env overrides.
[E=…]could replace server-computedREQUEST_METHOD,REQUEST_URI,PATH_INFO,CONTENT_*,GATEWAY_INTERFACE, andREQUEST_TIME*; all are now on the override deny-list. - WAF fail-open no longer corrupts requests. A sidecar timeout mid-body-collection used to forward an emptied body downstream; the body is now collected and restored before the timed inspection exchange.
HTTP/3, page cache, and LSAPI lifecycle
- H3 request targets. A network-path
:path(//evil.example/x) minted a URI authority diverging from the routed Host; rejected. QPACK field sections with a nonzero Required Insert Count or Delta Base are rejected (the dynamic table is advertised at capacity 0). - Purge completeness.
/__hj_cache_purgehonors everyX-LiteSpeed-Purgeheader line instead of only the first, and the tag-purge version sweep can no longer unlink a live re-stored page's body file.enablePostCachenow warns (config lint + startup log) instead of being silently inert. - LSAPI lifecycle. Restart backoff is cancellable on shutdown, registry drain has a wall-clock bound so a mid-start pool cannot be orphaned, and a jail chroot configured without credentials is a hard spawn error rather than a silent skip.
Dependencies
Cargo.lock refreshed to the latest semver-compatible releases (aws-lc-rs 1.18.2, zstd 0.14.2, serde_json 1.0.154, tokio-util 0.7.20, OpenTelemetry 0.33.1, proptest 1.12, and more); all manifests already required the latest majors. cargo audit is clean and the full test suite (2,028 tests) passes.
Production deployments should follow the usual path: scripts/build-pgo.sh, then scripts/deploy-lscache.sh and the cache gate scripts.