From 4b32f84bd245e0dec58976dab3d4be0544f3e3f8 Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Tue, 29 Sep 2026 21:02:32 +0000 Subject: [PATCH] chore: release alkhttp 0.6.0 The fuzzing adoption waves (commits 5971c0a..a16dee7) plus the alkcall 0.8.1 patch pickup, released as a minor bump: - Added: fuzz/ infra (5 nightly targets, stable-toolchain corpus-replay gate), public SSE parse types (SseParser/SseEvent/SseParseError/ SSE_EVENT_BUFFER_CAP), the WriteChunkWalk/Walk chunk-walk API, and the fuzzing re-export hubs. - Fixed: FWD-20 (SSE dribble drain quadratic -> linear), FWD-21 (WriteChunkWalk failure arms carry the pre-failure pieces), FWD-22 (harness re-frame guard-CR; no crate change). - Changed: alkcall 0.8.1 (duplicate adopt/open collision fix). - Doc: fixed broken intra-doc link surfaced by SSE_EVENT_BUFFER_CAP going public. Verified: cargo test (457 passed), cargo test --all-features (590), cargo clippy --all-targets -- -D warnings, cargo fmt --check, cargo doc --no-deps (0 warnings), fuzz corpus replay (21 passed), cargo publish --dry-run --allow-dirty. --- CHANGELOG.md | 63 +++++++++++++++++++++++++++++++++++++++-- Cargo.lock | 2 +- Cargo.toml | 2 +- fuzz/Cargo.lock | 2 +- src/adapters/forward.rs | 2 +- 5 files changed, 65 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f60965a..7d68582 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,68 @@ All notable changes to this crate are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this crate adheres to [Semantic Versioning](https://semver.org/). -## [Unreleased] +## [0.6.0] — 2026-09-29 -## [0.5.0] — 2026-09-18 +### Added + +- **Fuzzing infrastructure adopted** (the sibling-crate pattern: + alkcall, alktty, alktunnels, alksocks). The `fuzz/` subtree holds + five nightly targets (`sse_parser`, `ws_chunk_framing`, + `spec_import`, `ws_session_opseq`, `retry_throttle`), a stable + toolchain shared crate replaying every committed seed (80 + 42 + 16 + committed) through the identical invariant functions as plain + `cargo test` — the standing fuzz gate, + `cargo test --manifest-path fuzz/shared/Cargo.toml` — plus a + detached campaign runner and a deterministic seed generator. `fuzz/` + is excluded from publish; nightly stays confined to the subtree + (MSRV 1.88 unchanged). +- **New public API for the tested parse surfaces.** The incremental + SSE parser widens `pub(crate)` → `pub` (`SseParser`, `SseEvent`, + `SseParseError`, `SSE_EVENT_BUFFER_CAP`, `sse_event_envelope`) and + the WS write pump's chunk walk is extracted as public API + (`WriteChunkWalk`, `Walk`) — both direct-drive test targets. Five + `fuzzing` re-export hubs (`adapters::forward::fuzzing`, + `adapters::openapi_spec::fuzzing`, `adapters::input_validation::fuzzing` + with the opaque `FuzzCompiledValidator`, `adapters::from_mcp::fuzzing` + (\`mcp\`), `client::fuzzing` + `websocket::byte_adapter::fuzzing` + with the mock-socket pump drive) gather the harness access surface + at one named path each; the underlying crate-internal items keep + their visibility. + +### Fixed + +- **SSE dribble drain was quadratic in the dribbled bytes (FWD-20).** + `SseParser::feed` re-walked the whole reassembly buffer per feed, so + a subscription drip-feeding one giant unterminated line paid O(n²) + (64 KiB ≈ 12.5 s CPU per connection; 1 MiB ran to minutes) with no + per-stream total-CPU bound. Line assembly now walks only the + arriving chunk — the reassembly buffer holds just the current line — + measured linear (1 MiB dribble: 21 ms). Regression test: + `sse_parser_dribble_feeds_do_not_rescan_the_buffer`. +- **`WriteChunkWalk` failure arms discarded the already-drained pieces + (FWD-21).** One feed that drained complete chunks and then hit an + oversize header (or the pending cap) returned + `Walk::Oversize`/`PendingOverCap` empty, dropping bytes the mux had + already counted as written (the walk's consumption-accounting + contract was violated; production could not reach the shape, but + `WriteChunkWalk` is public API and the silent drop broke it). The + failure variants now carry the pre-failure pieces (`Vec>`) + and the write pump emits them before failing the stream (1011 + close). Regression seeds cover the drain-then-fail shape in both + chunk harnesses. +- **FWD-22 (harness-assert fix):** the fuzz harness's SSE re-frame + round-trip probe is now total over anything JSON-encodable + (`frame_event` emits a guard CR as the line terminator; the + terminal-CR skip is dropped). No crate change. + +### Changed + +- **`alkcall` dependency consumed at 0.8.1** (patch pickup): the + duplicate adopt/open collision fix found by alkcall's + `manager_routing` fuzz campaign — a duplicate adopt/open no longer + replaces the live channel's state. This crate reaches the fixed + paths via the shared `ChannelManager`; no alkcall API surface used + here changed. ### Changed diff --git a/Cargo.lock b/Cargo.lock index 27bf633..8b6b290 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,7 +48,7 @@ dependencies = [ [[package]] name = "alkhttp" -version = "0.5.0" +version = "0.6.0" dependencies = [ "alkcall", "arc-swap", diff --git a/Cargo.toml b/Cargo.toml index dfff0fc..8e47a37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alkhttp" -version = "0.5.0" +version = "0.6.0" edition = "2021" rust-version = "1.88" license = "MIT OR Apache-2.0" diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 49f1525..1f64131 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -48,7 +48,7 @@ dependencies = [ [[package]] name = "alkhttp" -version = "0.5.0" +version = "0.6.0" dependencies = [ "alkcall", "arc-swap", diff --git a/src/adapters/forward.rs b/src/adapters/forward.rs index 5d7189e..778b2c5 100644 --- a/src/adapters/forward.rs +++ b/src/adapters/forward.rs @@ -1290,7 +1290,7 @@ pub enum SseParseError { /// takes a chunk's bytes, so the reassembly buffer can never exceed /// the cap (FWD-14). This bounds one *line*, not the /// stream; the per-subscription total is -/// [`HttpClientConfig::stream_total_byte_cap`], enforced by the +/// [`crate::client::HttpClientConfig::stream_total_byte_cap`], enforced by the /// `forward_stream` unfold across every `feed`. pub const SSE_EVENT_BUFFER_CAP: usize = 1024 * 1024;