Expand description
Message framing / chunking. A message larger than the connection’s negotiated
max_message_size is split into MTU-sized Chunks on the sender and reassembled on the
receiver.
NOTE: this is whole-message buffering, not MSRP-style (RFC 4975) streaming. There is no mid-message interruption, interleaving, or incremental delivery — the receiver yields a payload only once every chunk has arrived (or drops it on TTL). The “split into ordered, id-tagged pieces and reassemble” idea is borrowed from MSRP chunking; the interruption semantics are not.
Two halves, deliberately separated:
- Send —
ChunkListturns aBytesinto orderedChunks, wherechunk_sizecomes from the connection’s negotiatedmax_message_size. The sender usesChunkList::stream, which yields chunks lazily as zero-copy slices so one chunk is held in flight at a time;ChunkList::split(eagerVec) remains for tests. - Receive —
MessageReassemblercollects incomingChunks keyed by message id and yields the original payload once every position has arrived.
The receiver is robust to the realities of a multi-hop / DHT overlay: out-of-order arrival,
duplicates / retransmits (first write per position wins), and partial messages (evicted
by TTL). It is also bounded against a hostile peer: per-chunk and per-message byte caps, a
global buffered-cost ceiling (charging a per-slot overhead so tiny-chunk floods are bounded by
count too), an id-count cap, and up-front rejection of already-expired chunks. No single id and
no peer-supplied total can drive memory without limit. See MessageReassembler.
send : Bytes ↦ [Chunk{ chunk=[i, n], data=dataᵢ, meta } | i ∈ 0..n] (Rust range, exclusive)
receive : a message id is complete ⟺ received positions = 0..total (all n of them);
then payload = concat(dataᵢ for i ∈ 0..total)Structs§
- Chunk
- One chunk of a chunked message, as it travels on the wire.
- Chunk
List - Sender side: an ordered list of
Chunks for one message. Build it from the payload withChunkList::split, passing the per-message data size to cut at (the connection’s negotiatedmax_message_sizeminus the envelope reserve), then iterate (or convert toVec<Chunk>) to put each chunk on the wire. The cut size is a runtime argument rather than a type parameter because it is decided per connection from the negotiated limit. Reassembly is the receiver’s job — seeMessageReassembler. - Chunk
Meta - Meta data of a chunk
- Message
Reassembler - Receiver side: whole-message reassembly for reliable data-channel
MessagePayloadfragments. Buffers a message’s chunks keyed by id and yields the completeBytesonce every position has arrived (then forgets it). - Reassembly
Limits - The limits a
MessageReassemblerenforces on incoming chunks, as an explicit value rather than module globals. This keeps the core admission rule independent of where the numbers come from: the shell supplies them (seeReassemblyLimits::production), the reassembler only enforces what it is given, and tests can use small limits instead of giant synthetic payloads. - Wire
Reserves - The bytes the transport adds around a payload on the wire, per framing path. Bundled as a named
value so the framing rule reads
reserves.plan(len, limit)instead of a row of positionalusizes, and so the production reserves live in exactly one place (WireReserves::PRODUCTION).
Enums§
- Framing
- How one payload should be framed for a size-limited connection: sent whole, or split.