# http-net v0.9.2 - Beta
**Date:** 2026-11-02
**Compare:** `v0.9.1...v0.9.2`
**Fixes from the documentation audit.** Every factual claim in the
documentation was checked against the build again, on 0.9.1. The audit
found no smuggling, desync, panic, or memory defect. It found three places
where the code did not do what its documentation said, fixed here with
tests, and 18 documentation corrections. No API change: `cargo
semver-checks` against 0.9.1 reports none.
## What is http-net?
An HTTP/1.1 engine that does no I/O. Feed it the bytes a socket delivered;
it hands back requests or responses as borrowed views of those bytes,
decodes their bodies, and serializes what you send back. Parsing is strict:
a message two implementations could frame differently is refused with a
specific error, never repaired. Every buffer is bounded by configuration,
and each limit is checked before the bytes it governs are buffered.
## Fixed
- **`ClientConn::outstanding()` after a failed response.** It left out the
request whose response failed, so a driver retrying from the count, as
the documentation says to, dropped one idempotent request. With three
requests pipelined and the peer gone, it said 2; it says 3 now. A clean
close is unchanged.
- **The reuse queries at `ResponseEnd`.** Before the next `poll`,
`is_reusable()` could say yes while `send_request` refused anything but
a pipelined idempotent request, and `progress()` said `Idle` with a
pipelined request outstanding, which stops a driver's response deadline.
When the connection stays open and nothing else is outstanding,
`send_request` now accepts any request at once. With a request
outstanding, `is_reusable()` is false and `progress()` is
`AwaitingResponse`.
- **`HEAD` errors in the head.** 0.9.1 covered a failure in the body. Once
a `HEAD` request line has been read, every error answer now has no
content, including a failure later in the head (an invalid
`Content-Length`, no `Host`) and the blocking adapter's `408` to a head
that never finished (RFC 9110 section 9.3.2).
## Documentation
- **The semver promise.** `docs/STABILITY-1.0.md` covered items "marked
exists", which `docs/API.md` defines as items from 0.2.0, so read
literally it left out everything added since. It now covers every
public item in `docs/API.md` apart from those planned after 1.0.
- **Behaviour.** Framing refusals do not apply to a response with no body
(RFC 9112 section 6.3 ends it at its blank line). `UnexpectedUpgrade`
also covers a `101` naming a protocol the request did not offer.
`BareCr` is not raised in a chunk line. `blocking::Client` opens one
connection per request. `close` and `set_keep_alive(false)` do nothing
on a tunnel. The client deadline does not bound name resolution.
- **The record.** The migration notes list every noticeable change; the
wipe cost is the measured figure everywhere; the allocation guarantee
and "runnable example" say exactly what holds.
`dev/PLAN.md` D-75 has the list.
## Testing
386 tests plus 346 doctests, all passing (`cargo test --all-features`):
117 unit tests and 269 integration tests, 14 more than 0.9.1.
`tests/prop_split.rs` is new: the split-invariance properties the 0.9.x
review ran (the lenient client, tiny budgets with reporting on, the
pipelining client, the stateless parser against the connection) and every
budget at its limit and one over, whole, at every two-piece split, and
one byte at a time.
The four fuzz targets on the changed paths (`client_conn`, `server_conn`,
`reference_diff`, `httparse_diff`) ran 15 minutes each on this code (43
million executions) with no finding. `ls-studio`'s 42 tests and serve-net's
suite pass against it with no source change.
## Performance
The hot paths are unchanged: interleaved against 0.9.1, nine rounds, no
benchmark moved past the 5% gate (`request_head` -4.3%,
`response_parse` +2.9%, the response write -6.1%, pipelined +0.5%, on a
heavily loaded machine). `docs/PERFORMANCE.md` adds aarch64 numbers
from the manual benchmark workflow: Linux arm64 and macOS runners track
the x86_64 runner, so the SWAR scanners behave the same there.
## Migration from v0.9.1
No code change is needed. Three behaviours can be noticed; see
`docs/MIGRATION.md`.
## Stability commitment
The API is frozen as the 1.0 candidate: `0.9.x` releases contain fixes
only, and `docs/STABILITY-1.0.md` describes what 1.0 commits to. 1.0.0
ships at the earliest on 2026-11-12, two weeks after 0.9.0 with no API
change in between, on James's decision. The MSRV is Rust 1.85.
## Verification
Run on Windows x86_64 and in WSL Ubuntu (Linux x86_64), each on Rust
stable and the 1.85 MSRV toolchain:
```bash
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings
cargo clippy --all-targets --no-default-features -- -D warnings
cargo test
cargo test --all-features
cargo test --no-default-features
cargo build --examples
cargo build --lib --no-default-features --target thumbv7em-none-eabihf
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --no-default-features
cargo package
cargo deny check
cargo audit
cargo semver-checks --baseline-version 0.9.1 --all-features # no change
```
CI runs format, clippy, tests, examples, and docs on Linux, macOS, and
Windows, stable and 1.85, plus the `no_std` build and the 60 s fuzz smoke
run per target; `cargo deny` and `cargo audit` on Linux stable. `cargo
package` and `cargo semver-checks` run locally.
## What's next
- **0.9.x:** fixes only.
- **1.0.0:** after the items in `docs/STABILITY-1.0.md` "Open before
1.0", on James's decision.
## Installation
```toml
[dependencies]
http-net = "0.9"
```
MSRV: Rust 1.85.
**Full Changelog:** https://github.com/jamesgober/http-net/compare/v0.9.1...v0.9.2