Skip to main content

ControlFrameWalker

Struct ControlFrameWalker 

Source
struct ControlFrameWalker {
    draft: DraftVersion,
    remaining: usize,
    header: [u8; 16],
    header_len: usize,
    lost: bool,
}
Expand description

Where the message boundaries are on a control stream being forwarded verbatim.

A control stream is one framed byte sequence — type, length, payload, repeated — and a byte injected into the middle of a payload is read by the peer as part of that payload, leaving its decoder wrong about every message after it. So an injection has to be placed between messages, and on the pass-through pipe nothing else knows where that is: that pipe forwards whatever recv.read returned, and read boundaries are not message boundaries.

This walks the framing without decoding anything. It reads a type varint’s length from its first byte, reads the payload length, and then counts payload bytes down to zero — one varint decode per message and no per-byte work beyond the header. It allocates nothing and never holds a message; the bytes go straight out as they always did.

§Why not the control parser

ControlStreamParser already knows this framing and is already built on the pipes that observe or mutate. It also buffers each message whole and decodes it into an AnyControlMessage, which is the cost the pass-through pipe exists not to pay — and on an Interest::NONE session with no observer it is not built at all, so a stream that has never been parsed has no idea where it stands.

§It is only as right as the draft it was given

The framing changed at draft 11: earlier drafts write the payload length as a QUIC varint, later ones as a fixed 16-bit big-endian field. This walker is built from the session’s current draft, which for the moq-00 cohort (drafts 07-14) is a configured guess until a SETUP is peeked. A wrong guess makes the lengths wrong and the boundaries wrong with them. It is the same exposure the object framer already documents for the same cohort, and it fails the same way: Self::at_boundary latches to false as soon as a header cannot be made sense of, so an injection on a stream whose framing has been lost is held rather than written into the middle of something.

Fields§

§draft: DraftVersion§remaining: usize

Payload bytes still owed on the message being forwarded.

§header: [u8; 16]

Header bytes of the next message collected so far.

§header_len: usize

How many of header are populated.

§lost: bool

Set once the framing stops making sense, and never cleared. A walker that has lost the stream reports no boundaries at all, which holds every later injection instead of placing it by guesswork.

Implementations§

Source§

impl ControlFrameWalker

Source

fn new(draft: DraftVersion) -> Self

A walker positioned at the start of a control stream, which is a message boundary.

Source

fn at_boundary(&self) -> bool

Whether everything written so far ends on a message boundary, so another message may be written now.

Source

fn is_mid_message(&self) -> bool

Whether a message has been started and not finished.

Distinct from !at_boundary(): a walker that has lost the framing is at no boundary but also cannot claim a message is half-written, and reporting a truncation it cannot see would be a fabrication.

Source

fn advance(&mut self, data: &[u8]) -> Option<usize>

Account for data being forwarded, and answer the offset within it of the first message boundary it reaches.

None when no message completes inside data — either because it is a middle slice of a long message, or because the framing has been lost. The first boundary rather than the last, so an injection held over from an earlier chunk goes out as early as this chunk allows.

Source

fn header_step(&self) -> HeaderStep

Read the header collected so far, if it is complete.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more