feat: consumer-session — TunnelSession (open/adopt, data planes, teardown)
- TunnelSession (src/consumer.rs): open (ChannelClient::open_channel) + adopt (ChannelManager::adopt_channel) construction, substrate- shaped data planes (raw halves for stream; ADR-003 codec + pending frame queue for UDP), pump_against (session-owned pump_bidi handle), and teardown ownership per ADR-005: close (abort + ungraceful reap), join (await + reap + copy counts, pump-less (0,0,reaped)), Drop (abort + sync reap, never leaks). No Clone (compile_fail doc-test). - open_reverse_channel on the CallConnection (the reverse path's two-step: call then adopt — the POC's honest shape). - Error surfaces (src/error.rs): TunnelOpenError (typed ADR-049 §4 surface via open_ref/establishment_reason), TunnelIoError (wrong- substrate, TruncatedDatagram fail-loud, ChannelTaken), ReverseOpenError. - Tests: tests/consumer_session.rs (12) over two harness topologies — forward (wire_forward/ForwardTopology: consumer connect-side pure client, producer serving via adapter) and reverse (the POC shape); teardown matrix, W4 half-close, out-of-band close + self-reap (W3), F-2 empty datagram, AdoptFailed-on-collision (bogus-id adopt parks by design — documented), concurrent sessions. Harness: all_substrate_dial. - Adopt takes substrate (task-note deviation: the data plane shape rode the open call; cannot be inferred from the ID). Verified: cargo test green (38), clippy -D warnings (native + wasm32), fmt clean, cargo check --target wasm32-unknown-unknown passes.
This commit is contained in:
+76
-8
@@ -1,13 +1,13 @@
|
||||
//! Tunnel error surfaces: the crate's `TunnelError` plus the typed
|
||||
//! open-error helpers consumers branch on (ADR-049 §4 surface).
|
||||
//!
|
||||
//! Skeleton module — filled by `tunnels/params` (codec + establishment
|
||||
//! error mapping) and `tunnels/consumer-session` (session errors).
|
||||
//! Tunnel error surfaces: the crate-level [`TunnelError`], the typed
|
||||
//! session-open error ([`TunnelOpenError`], ADR-049 §4 surface), and
|
||||
//! the session data-plane error ([`TunnelIoError`]).
|
||||
|
||||
use alkcall::channels::client::ChannelOpenError;
|
||||
use alkcall::protocol::wire::CallError;
|
||||
|
||||
use crate::wire::DatagramCodecError;
|
||||
|
||||
/// The crate-level error type (thiserror; AGENTS.md convention 2).
|
||||
///
|
||||
/// Skeleton: the codec and session variants land with
|
||||
/// `tunnels/wire-codec` and `tunnels/consumer-session`.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TunnelError {
|
||||
/// The data-plane codec rejected an operation (UDP framing).
|
||||
@@ -21,3 +21,71 @@ pub enum TunnelError {
|
||||
#[error("io: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// A failed tunnel open or adopt. A failure never yields a session
|
||||
/// (no phantom session, mirroring the no-phantom-channel property —
|
||||
/// ADR-005). Branch on [`ChannelOpenError::establishment_reason`]
|
||||
/// through [`TunnelOpenError::open_ref`] for `channel:open_failed`'s
|
||||
/// reason (`dial_failed` / `unknown_resource` / `resource_shortage` /
|
||||
/// `handler_error` / `timeout`), or the pre-establishment codes
|
||||
/// (`FORBIDDEN`, `channel:too_many_channels`).
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TunnelOpenError {
|
||||
/// The open op failed, or the reply/adopt failed — the typed
|
||||
/// [`ChannelOpenError`] carries the wire [`CallError`] verbatim
|
||||
/// (adopt failures surface as `AdoptFailed`).
|
||||
#[error("tunnel open failed: {0}")]
|
||||
Open(#[from] ChannelOpenError),
|
||||
}
|
||||
|
||||
impl TunnelOpenError {
|
||||
/// The underlying [`ChannelOpenError`] (the ADR-049 typed surface).
|
||||
pub fn open_ref(&self) -> &ChannelOpenError {
|
||||
match self {
|
||||
TunnelOpenError::Open(e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An I/O failure on the session's data plane, or a substrate-shaped
|
||||
/// operation attempted on the wrong plane (the POC's shape).
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TunnelIoError {
|
||||
/// The underlying stream failed.
|
||||
#[error("io: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
/// The datagram codec rejected the operation (e.g. `Oversize` —
|
||||
/// a >65535-byte send, rejected at frame time per ADR-003).
|
||||
#[error("codec: {0}")]
|
||||
Codec(#[from] DatagramCodecError),
|
||||
/// The stream ended mid-datagram — declared length not satisfied
|
||||
/// (a clean peer never does this; the fail-loud posture, OQ-TN-13
|
||||
/// / ADR-003: never a silent partial datagram).
|
||||
#[error("stream ended mid-datagram (truncated)")]
|
||||
TruncatedDatagram,
|
||||
/// A substrate-shaped operation was attempted on the wrong data
|
||||
/// plane (e.g. `send_datagram` on a stream tunnel).
|
||||
#[error("wrong substrate for this operation")]
|
||||
WrongSubstrate,
|
||||
/// The session's channel halves were already taken — by
|
||||
/// `take_halves` (caller-driven) or `pump_against` (pump-owned);
|
||||
/// no data plane remains on the session.
|
||||
#[error("channel halves already taken from this session")]
|
||||
ChannelTaken,
|
||||
}
|
||||
|
||||
/// The reverse-path open call failed (`open_reverse_channel` — the
|
||||
/// assembly layer calls it before [`crate::consumer::TunnelSession::
|
||||
/// adopt`]; the hub's call surface is a `CallConnection`, so the
|
||||
/// two-step is the honest API).
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ReverseOpenError {
|
||||
/// The open op failed — the wire [`CallError`]; branch on
|
||||
/// `details.reason` of `channel:open_failed` (ADR-049 §4).
|
||||
#[error("open call failed: {0:?}")]
|
||||
Call(CallError),
|
||||
/// The open op succeeded but the reply carried no `channel_id`
|
||||
/// (malformed responder).
|
||||
#[error("open reply missing channel_id")]
|
||||
NoChannelId,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user