Expand description
The ASSOCIATE datagram frame codec (ADR-003): the crate’s one
framed stage — [len: u16 BE][addr + data] per datagram, both
directions of the channel BiStream. CONNECT/BIND are raw
pass-through (no framing). Normative WHAT: wire.md §The Data
Plane; the binary contract: bast.md (AssociateFrame). The
length codec is first-party code shaped on alktunnels’
frame_datagram/DatagramReader (same F-2 behavior; no
dependency on it).
The inner bytes are the RFC 1928 address encoding — exactly
TargetAddr::to_be_bytes() (ATYP + addr + port; the RFC UDP
header minus RSV/FRAG, which ride only vanilla front doors,
ADR-003) — followed by the datagram payload.
Framing is applied exactly once, by this layer. The egress
seam speaks inner bytes (addr + data, no length prefix —
Egress::next_reply returns inner bytes); the producer’s frame
writer adds the single length prefix, and the consumer’s send path
frames via frame exactly once. The double-framing bug class
POC #2 caught (an egress framing its replies, the producer
prefixing again — the client parsed the length field as ATYP
0x00) is an API-design invariant: the wire layer owns the length
prefix; nothing else adds one.
The joint bound (the load-bearing constraint). The u16 length
field covers addr + data, not the payload alone: a frame is
rejected (WireError::Oversize, at frame time) whenever
len(addr) + len(payload) > 65535. The UDP payload bound alone
(65507) does NOT fit u16 unconditionally — a 65507-byte datagram
to a 255-character domain is 65507 + 259 = 65766 frame bytes. Max
payload per address form: 65276 (domain, 255 chars), 65516 (IPv6),
65528 (IPv4). addr_encoding_len exposes the per-address
encoding size so callers can compute the joint bound; no unchecked
u16 arithmetic is used on the payload bound.
The F-2 invariant, at payload level. A zero-length payload is
a legal empty datagram — a real datagram, never an EOF signal:
frame encodes it and DatagramReader decodes it as
(addr, empty). (len == 0 — zero inner bytes — is unreachable
from a compliant peer, since addr is always ≥ 7 bytes; a wire
len of 0 is protocol garbage and surfaces as
WireError::EmptyFrame.)
EOF layering. EOF is the channels-level sentinel on the
BiStream, outside this codec: the codec never emits zero-length
reads, so the sentinel and the datagram stream never collide (an
empty datagram and a zero-byte read are different shapes — the
reason boundary-preserving framing is mandatory for the datagram
stage at all). A stream end mid-frame is the session’s fail-loud
truncated-datagram condition (DatagramReader::is_mid_frame is
the teardown diagnostic); the codec itself only buffers — it never
errors on truncation and never yields a partial frame.
Structs§
- Datagram
Reader - Incremental datagram decoder over the framed stage’s read
direction. Feed it whatever
read()yields; it reassembles[len: u16 BE][addr + data]frames across chunk boundaries — a frame may be split across chunks, several frames may share one chunk, and partial headers wait for more bytes. Each completed frame is decoded into(TargetAddr, Bytes)— the addr sub-layout (ATYP determines the address length) is split off, the remainder is the datagram payload.
Enums§
- Wire
Error - Codec errors. Two families:
Constants§
- MAX_
INNER_ LEN - The u16 length-field bound on a frame’s inner bytes (
addr + data): frames whose address encoding plus payload exceed this are rejected at frame time (ADR-003 — never a wire overflow). The joint bound, not a payload bound: see the module docs.
Functions§
- addr_
encoding_ len - The RFC 1928 address encoding’s length for
addr(ATYP + addr + port — exactlyTargetAddr::to_be_bytes()’s size): 7 bytes for IPv4, 19 for IPv6,4 + domain.len()for domain form (259 max). Callers compute the joint bound with it: a payload is framable foraddriffaddr_encoding_len(addr) + payload.len() <= MAX_INNER_LEN. - frame
- Frame one datagram:
[len: u16 BE][addr + data]whereaddr + dataisTargetAddr::to_be_bytes()(ATYP + addr + port) followed bypayload. One frame per datagram; the single length prefix on this wire (framing-once — see the module docs). A zero-length payload is a legal empty datagram (the F-2 invariant, at payload level). RejectsWireError::Oversizeat frame time whenlen(addr) + len(payload) > 65535— the joint bound; the payload bound alone (65507) does not fit u16 unconditionally. - frame_
inner - Frame pre-encoded inner bytes (
addr + data— exactly whatcrate::egress::Egress::next_replyreturns) as one[len: u16 BE][inner]frame. The reply-direction half of the framing-once invariant: the egress speaks inner bytes (no length prefix), and this adds the single prefix at the wire layer. RejectsWireError::Oversizewhen the inner bytes exceedMAX_INNER_LEN(the u16 length field would wrap).