From 4014ba66a96f694dd429384f336de0428de7a2fc Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Wed, 9 Sep 2026 06:48:44 +0000 Subject: [PATCH] fix: remove the zombie DatagramCodecError::InvalidLength variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The coverage review found `InvalidLength {declared, have}` is a zombie: `DatagramReader::feed` never returns `Err` — it buffers partial frames indefinitely, and the truncated-stream contract is carried by `is_mid_datagram` (the codec diagnostic) plus `TunnelIoError::TruncatedDatagram` (the session surface). The variant's doc claimed it was the truncated-stream error; no spec (ADR-003, wire.md, bast.md) names it, and no external consumer exists (verified 2026-09-08 per ADR-005's N-3 amendment), so the API window is open — removed before the first consumer pins it. Also removes the `unreachable!` arm its removal strands in the `local` adapter's recv mapping (a no-panic path; single-variant destructure now), and records the planned-shape correction in the completed wire-codec task doc. Verified: cargo test (--all-features and default), clippy --all-targets -D warnings, fmt --check, wasm32 check + clippy — all clean. --- src/local/mod.rs | 29 ++++++++++++++--------------- src/wire.rs | 17 +++++++---------- tasks/tunnels/wire-codec.md | 13 ++++++++----- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/local/mod.rs b/src/local/mod.rs index a2f40de..5d5862c 100644 --- a/src/local/mod.rs +++ b/src/local/mod.rs @@ -317,21 +317,20 @@ impl AsyncRead for UdpHalf { .expect("scratch allocated above"); match this.sock.try_recv(scratch) { Ok(n) => { - let framed = frame_datagram(&scratch[..n]).map_err(|e| match e { - // A >u16 datagram cannot exist on a well-formed - // wire (the IP theoretical max is 65507); treat it - // as the fail-loud framing violation it is. - DatagramCodecError::Oversize(len, max) => std::io::Error::new( - std::io::ErrorKind::InvalidData, - format!( - "udp datagram {len} bytes exceeds the codec frame bound {max} \ - (truncation fails loud — OQ-TN-13)" - ), - ), - DatagramCodecError::InvalidLength { .. } => { - unreachable!("frame_datagram never yields InvalidLength") - } - })?; + let framed = frame_datagram(&scratch[..n]).map_err( + |DatagramCodecError::Oversize(len, max)| { + // A >u16 datagram cannot exist on a well-formed + // wire (the IP theoretical max is 65507); treat + // it as the fail-loud framing violation it is. + std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!( + "udp datagram {len} bytes exceeds the codec frame bound {max} \ + (truncation fails loud — OQ-TN-13)" + ), + ) + }, + )?; if framed.len() <= buf.remaining() { buf.put_slice(&framed); } else { diff --git a/src/wire.rs b/src/wire.rs index 16880c4..1236fc6 100644 --- a/src/wire.rs +++ b/src/wire.rs @@ -32,20 +32,17 @@ pub const MAX_DATAGRAM_LEN: usize = u16::MAX as usize; const DATAGRAM_LEN_FIELD: usize = 2; -/// Codec errors: oversize at frame time, and the truncated-stream -/// error a mid-datagram stream end maps to (the fail-loud posture, -/// OQ-TN-13 — never a silent partial datagram). +/// Codec errors: oversize at frame time. The truncated-stream case is +/// not a codec error — the decoder buffers partial frames indefinitely +/// (`is_mid_datagram` is the diagnostic) and the SESSION surfaces a +/// stream end mid-datagram as `TunnelIoError::TruncatedDatagram` (the +/// fail-loud posture, OQ-TN-13 — never a silent partial datagram). #[derive(Debug, Error)] pub enum DatagramCodecError { /// A datagram larger than [`MAX_DATAGRAM_LEN`] was framed — the /// u16 length field would wrap. Rejected at frame time. #[error("datagram too large for u16 length prefix: {0} bytes (max {1})")] Oversize(usize, usize), - /// The stream ended mid-datagram (declared length not satisfied). - /// A clean peer never ends the stream mid-datagram; this is a - /// truncated-stream error, not a partial datagram. - #[error("unexpected end of datagram buffer: declared {declared}, have {have}")] - InvalidLength { declared: usize, have: usize }, } /// Frame one datagram into the chunk stream: `[len: u16 BE][payload]`. @@ -131,8 +128,8 @@ impl DatagramReader { /// `true` while a partial datagram is in flight (for teardown /// diagnostics — a clean peer never ends the stream mid-datagram; - /// an end while this is set is [`DatagramCodecError::InvalidLength`]'s - /// condition). + /// an end while this is set is the session's + /// `TruncatedDatagram` condition). pub fn is_mid_datagram(&self) -> bool { self.current.is_some() } diff --git a/tasks/tunnels/wire-codec.md b/tasks/tunnels/wire-codec.md index 76a7b9c..b5759b6 100644 --- a/tasks/tunnels/wire-codec.md +++ b/tasks/tunnels/wire-codec.md @@ -23,7 +23,7 @@ pin. ```rust pub const MAX_DATAGRAM_LEN: usize = u16::MAX as usize; // 65535 -pub struct DatagramCodecError { .. } // thiserror: Oversize(usize), InvalidLength(...) +pub struct DatagramCodecError { .. } // thiserror: Oversize(usize, usize) (planned shape; InvalidLength removed post-review — see Notes) pub fn frame_datagram(payload: &[u8]) -> Result // [len: u16 BE][payload]; len=0 is a legal empty datagram; >65535 = Oversize at frame time @@ -90,9 +90,11 @@ the POC proved no collision). invariant is load-bearing, so the module doc states why raw pass-through is structurally broken for UDP). - Error naming per the task's API: `Oversize(usize, usize)` (was - POC's `TooLarge`) + `InvalidLength {declared, have}` (the - truncated-stream shape — the OQ-TN-13 fail-loud posture's codec-side - anchor). + POC's `TooLarge`). (2026-09-09: the `InvalidLength` variant was + REMOVED post-review — `feed` never returned it; the truncated-stream + contract is `is_mid_datagram` + the session's `TruncatedDatagram` + instead, per the coverage-review commit. A completed task doc + records its planned shape, not corrections made after.) - `feed` returns `Vec` per the task API (the POC's `Vec` wrapper dropped — `Bytes` is the payload; the mux handoff is zero-copy via `BytesMut::freeze`). @@ -113,7 +115,8 @@ the POC proved no collision). `src/wire.rs` complete: `frame_datagram` (Oversize at frame time, `len=0` legal), `DatagramReader` (incremental decoder, `is_mid_datagram` -teardown diagnostic), `DatagramCodecError {Oversize, InvalidLength}` — +teardown diagnostic), `DatagramCodecError {Oversize}` (originally +sketched `{Oversize, InvalidLength}`; see the 2026-09-09 note above) — the ADR-003/BAST contract in executable form. 8 codec tests + 6 params tests = 14 passing. Verified: cargo test, clippy --all-targets -D warnings (native + wasm32), fmt --check, wasm32 check — all clean. \ No newline at end of file