Skip to main content

Connection

Struct Connection 

Source
pub struct Connection {
    transport: Transport,
    endpoint: Endpoint,
    draft: DraftVersion,
    control_send: Option<FramedSendStream>,
    control_recv: Option<FramedRecvStream>,
    observer: Option<Box<dyn ConnectionObserver>>,
    pending_events: Vec<ClientEvent>,
    deferred_uni: Mutex<VecDeque<FramedRecvStream>>,
    pending_inbound: Mutex<VecDeque<(FramedSendStream, FramedRecvStream)>>,
}
Expand description

A live MoQT connection over QUIC or WebTransport, combining the endpoint state machine with actual network I/O.

Fields§

§transport: Transport§endpoint: Endpoint§draft: DraftVersion§control_send: Option<FramedSendStream>§control_recv: Option<FramedRecvStream>§observer: Option<Box<dyn ConnectionObserver>>§pending_events: Vec<ClientEvent>

Setup events buffered during connect() and replayed when an observer attaches via set_observer — without this, an observer attached after connect returns would never see the handshake.

§deferred_uni: Mutex<VecDeque<FramedRecvStream>>

Unidirectional streams accepted while connect was looking for the peer’s control stream, in arrival order.

Data streams are allowed to arrive before the control streams on this draft, so the search cannot assume the first unidirectional stream is the control one — and dropping the ones that are not would silently lose objects the peer already sent. accept_subgroup_stream empties this before it accepts anything new.

Behind a mutex because that method takes &self. The lock is only ever held for a pop_front, never across an await.

§pending_inbound: Mutex<VecDeque<(FramedSendStream, FramedRecvStream)>>

Bidirectional streams the peer opened that accept_request_stream took off the transport but did not finish reading a first message from, because its future was dropped. In arrival order.

Without this a caller could not put accept_request_stream in a select! at all: losing the race would lose a stream the peer had already opened and, with it, whatever of the request had arrived. accept_request_stream empties this before it accepts anything new.

Behind a mutex for the same reason deferred_uni is: the lock is only ever held for a push or a pop, never across an await.

Implementations§

Source§

impl Connection

Source

pub async fn connect( addr: &str, config: ClientConfig, ) -> Result<Self, ConnectionError>

Connect to a MoQT server as a client.

Establishes a QUIC or WebTransport connection (based on config.transport), brings up the control plane, performs the SETUP handshake, and returns a ready-to-use connection.

§The control plane is a pair of unidirectional streams

Draft-17 Section 3.3: “MOQT uses a pair of unidirectional streams for creating the session and exchanging control messages. Each peer opens one control stream beginning with a SETUP message.” So each direction is a separate stream opened by the peer that writes on it. This opens one with open_uni and writes SETUP on it, then finds the peer’s by accepting unidirectional streams until one leads with CONTROL_STREAM_TYPE.

Nothing is written ahead of the SETUP: 0x2F00 is both the SETUP message type and the unidirectional stream type for a control stream, so the message’s own first field is the stream header. See CONTROL_STREAM_TYPE.

A bidirectional stream is not the control stream here — the same section makes it a request stream, one that begins with TRACK_STATUS, SUBSCRIBE, PUBLISH, FETCH, PUBLISH_NAMESPACE or SUBSCRIBE_NAMESPACE: “Bidirectional streams MUST NOT begin with any other message type unless negotiated. If they do, the peer MUST close the Session with a PROTOCOL_VIOLATION.” A SETUP written on a bidirectional stream is exactly that case, so a peer that enforces the topology answers it by closing the session.

§Unidirectional streams that arrive before the peer’s control stream

They are kept, not dropped. Section 3.3 expects them: “Unidirectional streams containing Objects or bidirectional stream(s) beginning with a request message could arrive prior to the control streams, in which case the data SHOULD be buffered until both control streams arrive and setup is complete.” Each such stream is set aside and handed to accept_subgroup_stream in arrival order, ahead of any newly accepted stream. Only the leading type varint is read from them here; the rest stays on the transport, unread and still flow-controlled, so nothing is buffered in this process beyond those few bytes.

One limit worth knowing: the search waits for each stream’s type varint in turn, so a peer that opens a unidirectional stream and then writes nothing on it stalls the handshake behind that stream.

Source

pub async fn adopt( transport: Transport, config: ClientConfig, ) -> Result<Self, ConnectionError>

Run the MoQT setup handshake over a transport somebody else established.

For choosing the draft from what the server selected: dial once through crate::transport::dial_quic offering every ALPN, then bring the connection to the module its answer names. Self::connect cannot do this — it derives its single ALPN from the draft it was given.

config.draft must match this module. The transport is adopted as given; nothing here re-checks the ALPN it was negotiated with.

Source

async fn connect_quic( addr: &str, config: &ClientConfig, ) -> Result<Transport, ConnectionError>

Establish a raw QUIC connection.

Offers this draft’s ALPN alone; crate::transport::dial_quic holds the TLS and endpoint setup.

Source

async fn connect_webtransport( _url: &str, _config: &ClientConfig, ) -> Result<Transport, ConnectionError>

Stub for when the webtransport feature is not enabled.

Source

pub fn set_observer(&mut self, observer: Box<dyn ConnectionObserver>)

Attach an observer. Buffered handshake events from connect() are flushed in arrival order before this returns.

Source

pub fn clear_observer(&mut self)

Remove the observer.

Source

fn emit(&self, event: ClientEvent)

Emit an event to the observer, if one is attached.

Source

pub async fn send_control( &mut self, msg: &ControlMessage, ) -> Result<(), ConnectionError>

Send a control message on the control stream.

Wraps the draft-17 message in AnyControlMessage::Draft17 for framing. This is the route for the messages that belong to the session rather than to one request: GOAWAY, NAMESPACE, NAMESPACE_DONE, PUBLISH_BLOCKED and REQUEST_UPDATE, the last of which carries its own request id and is handled on the control stream by the peer’s endpoint. SETUP is written by connect and is the control stream’s own type varint.

§Requests are refused here

Draft-17 Section 3.3 moved requests off the control plane, in a sentence drafts 18 and 19 keep word for word: “In addition to the control streams, this specification uses bidirectional streams to carry requests.” The response comes back on that same bidirectional stream, and resetting it cancels the request (Section 3.3.1).

Which types may open one is not shared, so this method is written against draft-17’s six and no further: they are RequestKind, and draft-18 makes them seven by adding SUBSCRIBE_TRACKS.

Handing one of those six to this method returns ConnectionError::RequestOnControlStream and writes nothing — an enforcing peer sees no bytes at all, not a misplaced request. Use the typed helpers, which open a bidirectional stream each: subscribe, fetch, joining_fetch, publish, track_status, publish_namespace and subscribe_namespace.

Response types are still permitted. A response written here will be refused by a conforming peer, whose endpoint answers a response on the control stream with an error — but this is currently the only route for them at all, since nothing yet accepts a request stream the peer opened, and refusing them would remove capability rather than fix a misdirected write. publish_done, the one response with a helper, does not come through here: it takes the request stream its PUBLISH opened.

Source

pub async fn recv_control(&mut self) -> Result<ControlMessage, ConnectionError>

Read the next control message from the control stream.

Returns the AnyControlMessage and also extracts the draft-17 ControlMessage for internal endpoint dispatch.

Source

pub async fn recv_and_dispatch( &mut self, ) -> Result<ControlMessage, ConnectionError>

Read and dispatch the next incoming control message through the endpoint state machine. Returns the decoded message for inspection.

Responses never arrive here. Draft-17 responses carry no request id and belong on the request stream that asked for them, so the endpoint refuses a response that turns up on the control stream. Read them with recv_on_request_stream.

Source

async fn open_request_bi( &self, ) -> Result<(FramedSendStream, FramedRecvStream), ConnectionError>

Open the bidirectional stream a request will be carried on.

Opened before the endpoint allocates a request id, so a transport that refuses a new stream — the peer’s initial_max_streams_bidi is exhausted, the connection is gone — costs nothing. The endpoint has no way to abandon a request it has already allocated, so every failure that can be moved ahead of the allocation is.

Nothing is written here. A request stream carries no stream-type header: its first field is the leading message’s own type field, which is what begin_request writes.

Source

fn or_abandon<T>( halves: &mut (FramedSendStream, FramedRecvStream), built: Result<T, EndpointError>, ) -> Result<T, ConnectionError>

Reset a request stream that was opened but whose request could not be built, and pass the endpoint’s error through.

Without this, an endpoint refusal — the session is draining, the request-id range is exhausted — would leave a bidirectional stream open that never carries a first message, and dropping it would FIN it, telling the peer an empty stream ended cleanly.

Source

async fn begin_request( &mut self, halves: (FramedSendStream, FramedRecvStream), kind: RequestKind, request_id: VarInt, msg: &ControlMessage, ) -> Result<RequestStream, ConnectionError>

Write msg as the first message on an opened bidirectional stream and hand back the RequestStream that owns both halves.

This is the one place a request reaches the wire. Every request helper funnels through it, so the ordering — open, allocate, write, emit — is stated once.

A failed write resets both halves rather than leaving a half-written request stream behind. What it cannot undo is the endpoint’s allocation: the request id and its state machine already exist, and there is no way to retract them, so a write that fails here leaves one pending request the endpoint will never see answered.

Source

pub async fn recv_on_request_stream( &mut self, stream: &mut RequestStream, ) -> Result<ControlMessage, ConnectionError>

Read the next message off a request stream and dispatch it through the endpoint with that stream’s own request id.

On draft-17 a response carries no request id; the stream is the correlation, so the id comes from the handle and not from the wire.

This blocks until a whole message has arrived. Backpressure is per request: a stream nobody reads stays unread, and the peer stays flow controlled on it alone. A peer that reset the stream surfaces as ConnectionError::Transport carrying TransportError::StreamReset with the peer’s code; a caller that is deliberately not reading should watch RequestStream::peer_cancelled instead.

§Errors

ConnectionError::Endpoint if the message is not one of this draft’s response types, or if it does not fit the request’s state. The message has already been emitted to the observer by then — what arrived is reported whether or not the endpoint accepts it.

Source

pub async fn send_on_request_stream( &mut self, stream: &mut RequestStream, msg: &ControlMessage, ) -> Result<(), ConnectionError>

Write a follow-up message on an already-open request stream.

The request itself was written when the stream was opened; this is for what comes after it on the same stream, PUBLISH_DONE among them — see publish_done, which uses this.

It does not refuse any message type. Which messages may follow a request on its own stream is not something this implementation can settle, so the choice is left to the caller rather than guessed at.

Source

pub fn cancel_request_stream( &mut self, stream: &mut RequestStream, code: u64, ) -> Result<(), ConnectionError>

Cancel a request: record it at the endpoint, then terminate its stream.

Section 3.3.1 puts the cancel at the stream — “Implementations SHOULD cancel requests by abruptly terminating any directions of a stream that are still open” — while the request’s own state lives in the endpoint, so the two have to move together. This is the only place that moves both.

The endpoint goes first and the stream is terminated only if it agrees, which is the order every request path here uses: a caller acts on a stream after the endpoint has accepted the step, never before. A refused cancel therefore leaves the stream exactly as it was, and RequestStream::cancel is still there for a caller that wants the stream reset regardless.

Idempotent from both ends: a request that has already ended accepts the cancel and stays where it is, and a handle that has already been cancelled resets nothing a second time.

§Errors

ConnectionError::Endpoint if no request carries this stream’s id or the request has not been written, and ConnectionError::Transport if code is outside the QUIC varint range — see RequestStream::cancel, which is what sends it.

Source

pub async fn peer_cancelled_on_request_stream( &mut self, stream: &mut RequestStream, ) -> Result<Option<u64>, ConnectionError>

Wait for the peer to cancel this request, and record it if it does.

RequestStream::peer_cancelled with the endpoint’s record attached. A caller applying backpressure is deliberately not calling recv_on_request_stream, which is the other place a peer reset surfaces, so without this the request would end on the wire and stay open in the endpoint’s record for as long as the backpressure lasts.

Returns what the handle’s own method returns; see it for the Ok(None) case and for what WebTransport can and cannot observe. Cancel-safe, and it grants no flow-control credit.

Source

pub async fn accept_request_stream( &mut self, ) -> Result<(ControlMessage, RequestStream), ConnectionError>

Accept the next bidirectional stream the peer opened, read the request it begins with, and hand back that request and a handle to answer it on.

This is the mirror of the request helpers. Where subscribe and its siblings open a stream and write a request, this takes one the peer opened and reads one. Draft-17 Section 3.3 puts requests in both directions on bidirectional streams, so a client that only ever calls the helpers can never be published to or subscribed from.

The returned RequestStream carries RequestOrigin::Peer. Answer it with respond_subscribe_ok, respond_fetch_ok, respond_publish_ok, respond_ok or respond_error, and hold it for as long as the request lasts — a subscription’s PUBLISH_DONE is written on it, and dropping it resets the stream.

§Two refusals, two codes

Draft-17 Section 3.3, on a stream that begins with the wrong type: “Bidirectional streams MUST NOT begin with any other message type unless negotiated. If they do, the peer MUST close the Session with a PROTOCOL_VIOLATION.” Section 9.1, on the Request ID: “If an endpoint receives a Request ID where the least significant bit is incorrect for the sender, or a duplicate Request ID, it MUST close the session with INVALID_REQUEST_ID.” Both are closes of the session on the wire, with different codes, and both happen before this returns — the error handed back reports a session that is already gone, not one the caller must remember to close.

§Cancelling this future loses nothing

A stream taken off the transport but not yet read is put back on an internal queue, and the next call takes it before accepting anything new — including whatever bytes of the request had already arrived, which live in the stream’s own reader. So this is safe to select! against a shutdown signal or a timer. See pending_inbound_count.

What it is not safe to do is run concurrently with another method on the same connection: this takes &mut self because registering the peer’s request moves endpoint state, and no signature avoids that while the connection owns the endpoint. A caller blocked in recv_on_request_stream waiting for its own response is not accepting, and the peer’s request streams queue up in the transport behind it. One loop that never blocks indefinitely on a single read is the shape this supports.

§Ordering

The endpoint is told about the request last, after every step that can fail or be cancelled, and building the handle afterwards cannot fail. This is the inverse of the outbound path’s reasoning — it opens the stream before allocating a Request ID for the same reason — and rests on the same fact: the endpoint has no way to abandon a request it has already registered. Registering earlier would let a cancelled accept leave a state machine keyed to a stream nobody holds, and the peer’s next use of that Request ID would then be reported as a duplicate — a session close, over an id the peer used exactly once.

§Errors
Source

fn take_pending_inbound(&self) -> Option<(FramedSendStream, FramedRecvStream)>

Take the oldest stream pair a cancelled accept_request_stream put back, if any.

Synchronous on purpose, like take_deferred_uni: the guard is dropped before the caller awaits, so the lock is never held across a suspension point.

Source

pub fn pending_inbound_count(&self) -> usize

How many peer-opened request streams a cancelled accept_request_stream put back and a later call has not yet taken.

Zero unless an accept future was dropped mid-read.

Source

async fn respond( &mut self, stream: &mut RequestStream, msg: ControlMessage, fin: bool, ) -> Result<(), ConnectionError>

Write msg as the response to the request stream carries, driving the endpoint first and the wire second.

The response goes on the request’s own bidirectional stream and never on the control stream: draft-17 responses carry no Request ID, so the stream is the only thing that says what is being answered. Taking the id off the handle rather than from the caller makes that correlation unforgeable.

fin is true only for REQUEST_ERROR. See respond_error.

Source

pub async fn respond_ok( &mut self, stream: &mut RequestStream, response: RequestOk, ) -> Result<(), ConnectionError>

Answer a peer’s PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE or TRACK_STATUS with REQUEST_OK.

The send half is left open. A SUBSCRIBE_NAMESPACE responder still owes the peer the namespaces it accepted, so finishing here would end the request before it had been served; a TRACK_STATUS responder owes nothing further and may call RequestStream::finish straight after.

§Errors

ConnectionError::RespondedToOwnRequest if stream is one this endpoint opened, and ConnectionError::Endpoint if no request of a kind REQUEST_OK answers is pending on it. Nothing is written either way.

Source

pub async fn respond_subscribe_ok( &mut self, stream: &mut RequestStream, response: SubscribeOk, ) -> Result<(), ConnectionError>

Answer a peer’s SUBSCRIBE with SUBSCRIBE_OK.

The send half is left open, and it must be: this endpoint is now the publisher of an established subscription and owes it a PUBLISH_DONE, which travels on this same stream — publish_done_on.

Source

pub async fn respond_fetch_ok( &mut self, stream: &mut RequestStream, response: FetchOk, ) -> Result<(), ConnectionError>

Answer a peer’s FETCH with FETCH_OK.

The send half is left open. The fetched objects travel on separate unidirectional streams, so a fetch responder may call RequestStream::finish as soon as this returns; it is not done here because nothing about FETCH_OK says the responder has no more to write.

Source

pub async fn respond_publish_ok( &mut self, stream: &mut RequestStream, response: PublishOk, ) -> Result<(), ConnectionError>

Answer a peer’s PUBLISH with PUBLISH_OK.

Draft-17 has PUBLISH_OK as a message of its own, 0x1E; drafts 18 and 19 folded it into REQUEST_OK. The send half is left open: accepting a PUBLISH establishes a subscription whose PUBLISH_DONE arrives on this stream.

Source

pub async fn respond_error( &mut self, stream: &mut RequestStream, response: RequestError, ) -> Result<(), ConnectionError>

Reject a peer’s request with REQUEST_ERROR, and finish the send half.

The FIN is part of the act, not a convenience: draft-17 Section 3.3.1 says “When an endpoint rejects a request without performing any application processing, it SHOULD send a REQUEST_ERROR and FIN the stream.” It is also the one response that can be finished immediately, because a rejected request leaves nothing further to send — every success path owes the peer something more.

A finished handle does nothing further on Drop, so the rejected stream is not then reset.

Source

pub async fn publish_done_on( &mut self, stream: &mut RequestStream, status_code: VarInt, stream_count: VarInt, reason_phrase: Vec<u8>, ) -> Result<(), ConnectionError>

End a subscription this endpoint accepted, on the stream the peer’s SUBSCRIBE opened.

The mirror of publish_done, which ends a publication this endpoint offered with PUBLISH. Both write PUBLISH_DONE on a request stream and take the Request ID off the handle; they differ in which state machine moves, and therefore in which one refuses.

Source

pub async fn subscribe( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, parameters: Vec<KeyValuePair>, ) -> Result<RequestStream, ConnectionError>

Send a SUBSCRIBE on a bidirectional stream of its own.

The returned RequestStream is where SUBSCRIBE_OK, REQUEST_ERROR and later PUBLISH_DONE arrive — read them with recv_on_request_stream. Hold it for the subscription’s life: dropping it resets the stream, which cancels the subscription.

Source

pub async fn fetch( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, start_group: VarInt, start_object: VarInt, end_group: VarInt, end_object: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<RequestStream, ConnectionError>

Send a standalone FETCH on a bidirectional stream of its own.

FETCH_OK or REQUEST_ERROR comes back on the returned RequestStream; the fetched objects arrive on separate unidirectional data streams. Dropping the handle cancels the fetch.

Source

pub async fn joining_fetch( &mut self, joining_request_id: VarInt, joining_start: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<RequestStream, ConnectionError>

Send a Relative Joining Fetch (Fetch Type 0x2) on a bidirectional stream of its own.

A joining FETCH names an existing subscription’s request id but is still a FETCH, so it opens its own request stream rather than sharing the subscription’s.

joining_start counts groups back from the subscription’s largest group. To name the starting group outright, use absolute_joining_fetch.

Source

pub async fn absolute_joining_fetch( &mut self, joining_request_id: VarInt, joining_start: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<RequestStream, ConnectionError>

Send an Absolute Joining Fetch (Fetch Type 0x3) on a bidirectional stream of its own.

Here joining_start is the group to begin at rather than an offset: draft-17 Section 9.14.2.1 has the publisher set the Start Location to {Joining Start, 0}.

Source

pub async fn subscribe_namespace( &mut self, namespace_prefix: TrackNamespace, subscribe_options: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<RequestStream, ConnectionError>

Send a SUBSCRIBE_NAMESPACE on a bidirectional stream of its own.

subscribe_options is draft-17’s; draft-18 removed the field.

Source

pub async fn publish_namespace( &mut self, track_namespace: TrackNamespace, parameters: Vec<KeyValuePair>, ) -> Result<RequestStream, ConnectionError>

Send a PUBLISH_NAMESPACE on a bidirectional stream of its own.

Source

pub async fn track_status( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, parameters: Vec<KeyValuePair>, ) -> Result<RequestStream, ConnectionError>

Send a TRACK_STATUS on a bidirectional stream of its own.

Source

pub async fn publish( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, track_alias: VarInt, parameters: Vec<KeyValuePair>, track_properties: Vec<KeyValuePair>, ) -> Result<RequestStream, ConnectionError>

Send a PUBLISH on a bidirectional stream of its own.

PUBLISH_OK or REQUEST_ERROR comes back on the returned RequestStream, and publish_done is written back on it when the publication ends — so the handle must be held for as long as the publication lasts.

Source

pub async fn publish_done( &mut self, stream: &mut RequestStream, status_code: VarInt, stream_count: VarInt, reason_phrase: Vec<u8>, ) -> Result<(), ConnectionError>

Send a PUBLISH_DONE on the request stream the PUBLISH opened.

PUBLISH_DONE is a response and carries no request id on the wire, so the stream is the only thing that says which publication ended. The id the endpoint needs is taken off stream, which makes the correlation unforgeable — there is no way to name one request and write on another’s stream.

Source

pub async fn open_subgroup_stream( &self, header: &AnySubgroupHeader, ) -> Result<FramedSendStream, ConnectionError>

Open a new unidirectional stream for sending subgroup data.

Source

pub async fn open_fetch_stream( &self, header: &AnyFetchHeader, ) -> Result<FramedSendStream, ConnectionError>

Open a new unidirectional stream for sending a FETCH’s objects.

The objects answering a FETCH do not go on the request’s own stream: they go on a unidirectional stream of their own, which opens with a FETCH_HEADER naming the request they belong to. This writes that header and hands back the stream, the same way open_subgroup_stream does for a subgroup.

The caller owns the stream that comes back. Nothing here remembers which request it belongs to, so an endpoint serving several fetches at once keeps its own map from Request ID to stream.

Source

pub async fn accept_subgroup_stream( &self, ) -> Result<(AnySubgroupHeader, FramedRecvStream), ConnectionError>

Accept an incoming unidirectional data stream and read its subgroup header.

Streams the peer opened before its control stream are returned first, in arrival order, before any new one is accepted from the transport: connect had to look at them to find the control stream and set the rest aside rather than drop them. They are otherwise ordinary — the type varint connect read is still on the front of each one.

Source

pub async fn accept_fetch_stream( &self, ) -> Result<(AnyFetchHeader, FramedRecvStream), ConnectionError>

Accept the next unidirectional stream and read its fetch header.

accept_subgroup_stream’s twin. The two are separate because the header decides how every object after it is framed, so a caller has to know which it is expecting before the first byte is read.

Objects come off the returned stream with FramedRecvStream::read_fetch_object.

Source

fn take_deferred_uni(&self) -> Option<FramedRecvStream>

Take the oldest stream connect set aside, if any.

Synchronous on purpose: the guard is dropped before the caller awaits, so the lock is never held across a suspension point. A poisoned lock is recovered rather than propagated — nothing here can leave the queue in a state a later reader could be misled by, since the only mutation is a pop_front.

Source

pub fn deferred_stream_count(&self) -> usize

How many unidirectional streams connect set aside and accept_subgroup_stream has not yet handed back.

Zero for a peer that opened its control stream first, which is the ordinary case.

Source

pub fn send_datagram( &self, header: &AnyDatagramHeader, payload: &[u8], ) -> Result<(), ConnectionError>

Send an object via datagram.

The header goes through AnyDatagramHeader::encode, which refuses a header whose Object Status the framing it names cannot carry. Such a header errors here and nothing is sent, rather than going out as an ordinary payload datagram with the status quietly dropped.

Source

pub async fn recv_datagram( &self, ) -> Result<(AnyDatagramHeader, Bytes), ConnectionError>

Receive a datagram and decode its header.

Source

pub fn endpoint(&self) -> &Endpoint

Access the underlying endpoint state machine.

Source

pub fn endpoint_mut(&mut self) -> &mut Endpoint

Mutable access to the endpoint state machine.

Source

pub fn draft(&self) -> DraftVersion

Returns the draft version this connection is using.

Source

fn close_for(&self, err: &EndpointError)

Close the session on the wire when the endpoint says a violation is fatal to it.

EndpointError::session_error_code answers Some for exactly the errors draft-17 tells the receiver to close the session over, and the endpoint has already moved its own state machine to Closed by the time this runs. Without this step that move was purely internal: the local endpoint refused to start anything new while the peer, which is the one that broke the rule, saw a session that was still open and went on sending. “MUST close the session with a PROTOCOL_VIOLATION” is a statement about the wire, so it takes a CONNECTION_CLOSE to satisfy it.

The reason phrase is the error’s own Display text, which names the message and the rule rather than repeating the numeric code the close already carries.

Errors that answer None are recoverable and nothing is sent.

Source

fn close_if_session_fatal(&self, err: EndpointError) -> ConnectionError

close_for, then the error unchanged, for the common case where the endpoint’s error is also what the caller returns.

Source

fn codec_session_error_code(err: &CodecError) -> Option<SessionErrorCode>

The code to close the session with when a control message could not be decoded because the peer broke a rule draft-17 answers with a close.

Every variant listed here comes from a sentence in the draft that names the consequence: the reason phrase and GOAWAY URI maxima (Sections 1.4.4 and 9.5), the KVP value maximum and the delta-encoded type overflow (Section 1.4.3), the duplicate-parameter rule (Section 9.3), and the Track Namespace field, count and length rules (Section 2.4.1). Each of those reads “MUST close the session with a PROTOCOL_VIOLATION”. The Required Request ID Delta bound of Section 9.2 is the one that names a different code, INVALID_REQUIRED_REQUEST_ID.

One more rule reaches this table without naming a code: “An endpoint that receives an unknown message type MUST close the session”, stated in those words by all thirteen drafts. Protocol Violation is what carries it, as it does on every draft below this one.

CodecError::ObjectIdOverflow is absent on purpose, and this is the one draft where that is so. Section 10.4.2 gives the same Object ID delta arithmetic drafts 18 and 19 give, and the codec reports the wrap on all three, but only those two go on to say “the endpoint MUST close the session with a PROTOCOL_VIOLATION”. Draft-17 states no consequence, so the wrap stops here at a refused frame rather than a closed session.

None for everything else, including CodecError::InvalidField. That variant is shared by a dozen unrelated malformations, only some of which the draft answers with a close, so treating it as fatal would close sessions the draft does not ask to be closed. Splitting it is the way to bring the rest of those rules under this function; widening the match is not.

Source

fn close_for_codec(&self, err: ConnectionError) -> ConnectionError

Close the session on the wire when a decode failure is one draft-17 answers with a close, and hand the error back unchanged.

The codec’s counterpart to close_if_session_fatal. Without it every bound the decoder enforces would stop at this endpoint refused the frame while the peer, which is the one that broke the rule, saw a session that was still open and went on sending. “MUST close the session with a PROTOCOL_VIOLATION” is a statement about the wire.

Source

pub fn requests_to_cancel(&self, err: &ConnectionError) -> Vec<VarInt>

Name every request whose stream the caller must reset, for a track a data path has just found malformed.

Section 2.4.2 answers its whole list of conditions at once: “it MUST cancel any corresponding subscription or fetches for that Track from that publisher”. On this draft cancelling a request is a transport operation rather than a message — Section 3.3.1: “Implementations SHOULD cancel requests by abruptly terminating any directions of a stream that are still open using RESET_STREAM / RESET_STREAM_AT or STOP_SENDING.”

A SHOULD on this draft and on draft-18; draft-19 drops the keyword and states it outright.

§Why this returns ids instead of doing it

Because the streams are the caller’s. Every request on this draft lives at the front of a bidirectional stream of its own, and Connection::recv_on_request_stream hands that stream back as a RequestStream. There is no handle here to reset. So the connection does the half it can — note the track, and work out which requests receive it — and the caller passes each id to Connection::cancel_request_stream, which resets the stream and moves the endpoint’s record.

This is the one place in this crate where the two halves of an answer are split across the API boundary, and it is the draft that splits them: drafts 12 through 16 answer with a control message, which the connection owns, so withdraw_for_data_stream there does the whole thing.

§Both data paths come here

Unlike the drafts that answer with a message, where a datagram is read through the connection and answers itself. Here neither path can, for the same reason, so there is one entry point rather than two. Pass it whatever error a read returned; anything that is not this condition gives back an empty list.

Empty is not “the track was fine” — it is also what an alias no live binding names gives, and what a track this endpoint only publishes gives.

Source

pub fn close_for_data_stream(&self, err: &ConnectionError) -> bool

Close the session when a failure raised while reading a data stream is one draft-17 answers with a close. Reports whether it closed.

recv_control does this for itself, because it owns both the stream and the connection. A data stream does not: accept_subgroup_stream hands the caller a FramedRecvStream, which holds no connection and so cannot close one, and the reads that raise these failures happen there. The caller is the only party holding both halves, which is what this is for.

Splitting it this way rather than closing inside the reader keeps a caller that is deliberately permissive — a tool reproducing a capture, say — able to read a violating stream and report it without tearing the session down. The rule is stated at endpoints, and this is where an endpoint decides it is one.

On this draft only one rule reaches here: properties beside a status that is not Normal, Section 10.2.1.2. Drafts 18 and 19 also answer the Object ID delta wrap of their Section 11.4.2, which draft-17 describes without stating a consequence — see codec_session_error_code.

Source

pub fn close(&self, code: u32, reason: &[u8])

Close the connection.

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