pub struct Connection {
transport: Transport,
endpoint: Endpoint,
draft: DraftVersion,
control_send: Option<Mutex<FramedSendStream>>,
control_recv: Option<FramedRecvStream>,
observer: Option<Box<dyn ConnectionObserver>>,
pending_events: Vec<ClientEvent>,
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<Mutex<FramedSendStream>>The control stream’s write half, behind an async lock.
A lock rather than &mut self because Section 2.4.2’s answer to a
Malformed Track is a control message, and the condition is detected
where objects arrive - on a datagram read that takes &self, and on a
stream the caller holds, whose reader has no connection at all.
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.
pending_inbound: Mutex<VecDeque<(FramedSendStream, FramedRecvStream)>>Bidirectional streams the peer opened that
accept_namespace_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_namespace_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.
Behind a mutex because the lock is only ever held for a push or a pop, never across an await.
Implementations§
Source§impl Connection
impl Connection
Sourcepub async fn connect(
addr: &str,
config: ClientConfig,
) -> Result<Self, ConnectionError>
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), opens a bidirectional control stream,
performs the CLIENT_SETUP / SERVER_SETUP handshake, and returns
a ready-to-use connection.
Sourcepub async fn adopt(
transport: Transport,
config: ClientConfig,
) -> Result<Self, ConnectionError>
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.
Sourceasync fn connect_quic(
addr: &str,
config: &ClientConfig,
) -> Result<Transport, ConnectionError>
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.
Sourceasync fn connect_webtransport(
_url: &str,
_config: &ClientConfig,
) -> Result<Transport, ConnectionError>
async fn connect_webtransport( _url: &str, _config: &ClientConfig, ) -> Result<Transport, ConnectionError>
Stub for when the webtransport feature is not enabled.
Sourcepub fn set_observer(&mut self, observer: Box<dyn ConnectionObserver>)
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.
Sourcepub fn clear_observer(&mut self)
pub fn clear_observer(&mut self)
Remove the observer.
Sourcefn emit(&self, event: ClientEvent)
fn emit(&self, event: ClientEvent)
Emit an event to the observer, if one is attached.
Sourcepub async fn send_control(
&self,
msg: &ControlMessage,
) -> Result<(), ConnectionError>
pub async fn send_control( &self, msg: &ControlMessage, ) -> Result<(), ConnectionError>
Send a control message on the control stream.
Wraps the draft-16 message in AnyControlMessage::Draft16 for
framing.
Sourcepub async fn recv_control(&mut self) -> Result<ControlMessage, ConnectionError>
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-16
ControlMessage for internal endpoint dispatch.
Sourcepub async fn recv_and_dispatch(
&mut self,
) -> Result<ControlMessage, ConnectionError>
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.
Sourcepub async fn subscribe(
&mut self,
track_namespace: TrackNamespace,
track_name: Vec<u8>,
parameters: Vec<KeyValuePair>,
) -> Result<VarInt, ConnectionError>
pub async fn subscribe( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, parameters: Vec<KeyValuePair>, ) -> Result<VarInt, ConnectionError>
Send a SUBSCRIBE and return the allocated request ID.
Sourcepub async fn unsubscribe(
&mut self,
request_id: VarInt,
) -> Result<(), ConnectionError>
pub async fn unsubscribe( &mut self, request_id: VarInt, ) -> Result<(), ConnectionError>
Send an UNSUBSCRIBE for the given request ID.
Sourcepub async fn subscribe_ok(
&mut self,
request_id: VarInt,
track_alias: VarInt,
track_extensions: Vec<KeyValuePair>,
parameters: Vec<KeyValuePair>,
) -> Result<(), ConnectionError>
pub async fn subscribe_ok( &mut self, request_id: VarInt, track_alias: VarInt, track_extensions: Vec<KeyValuePair>, parameters: Vec<KeyValuePair>, ) -> Result<(), ConnectionError>
Accept a subscription the peer opened, sending SUBSCRIBE_OK and giving its track a Track Alias.
The endpoint refuses an alias a live track of its own already holds and refuses a second answer to one SUBSCRIBE, so nothing is written on the wire when it does either.
Sourcepub async fn request_error(
&mut self,
request_id: VarInt,
error_code: VarInt,
retry_interval: VarInt,
reason_phrase: Vec<u8>,
) -> Result<(), ConnectionError>
pub async fn request_error( &mut self, request_id: VarInt, error_code: VarInt, retry_interval: VarInt, reason_phrase: Vec<u8>, ) -> Result<(), ConnectionError>
Refuse a request the peer opened, sending REQUEST_ERROR.
One message refuses a SUBSCRIBE or a FETCH, and the endpoint finds which by the identifier. It refuses a second answer to either, and refuses a Joining Fetch’s refusal under any code but the one the draft names for it, so nothing is written on the wire when it does.
Sourcepub async fn request_update(
&mut self,
existing_request_id: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<VarInt, ConnectionError>
pub async fn request_update( &mut self, existing_request_id: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<VarInt, ConnectionError>
Narrow a subscription this endpoint opened, sending REQUEST_UPDATE, and return the Request ID the update itself spent.
Sourcepub async fn publish_ok(
&mut self,
request_id: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<(), ConnectionError>
pub async fn publish_ok( &mut self, request_id: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<(), ConnectionError>
Accept a PUBLISH the peer sent, which establishes the subscription it opened.
The endpoint refuses a second answer to one PUBLISH, so nothing is written on the wire when it does.
Sourcepub async fn publish_error(
&mut self,
request_id: VarInt,
error_code: VarInt,
retry_interval: VarInt,
reason_phrase: Vec<u8>,
) -> Result<(), ConnectionError>
pub async fn publish_error( &mut self, request_id: VarInt, error_code: VarInt, retry_interval: VarInt, reason_phrase: Vec<u8>, ) -> Result<(), ConnectionError>
Reject a PUBLISH the peer sent, which ends the subscription it opened before it was established.
The endpoint refuses a second answer to one PUBLISH, so nothing is written on the wire when it does.
Sourcepub 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<VarInt, ConnectionError>
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<VarInt, ConnectionError>
Send a standalone FETCH and return the allocated request ID.
Sourcepub async fn joining_fetch(
&mut self,
joining_request_id: VarInt,
joining_start: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<VarInt, ConnectionError>
pub async fn joining_fetch( &mut self, joining_request_id: VarInt, joining_start: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<VarInt, ConnectionError>
Send a Relative Joining Fetch and return the allocated request ID.
joining_start counts groups back from the subscription’s largest
group. To name the starting group outright, use
absolute_joining_fetch.
Sourcepub async fn absolute_joining_fetch(
&mut self,
joining_request_id: VarInt,
joining_start: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<VarInt, ConnectionError>
pub async fn absolute_joining_fetch( &mut self, joining_request_id: VarInt, joining_start: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<VarInt, ConnectionError>
Send an Absolute Joining Fetch and return the allocated request ID.
Here joining_start is the group to begin at rather than an offset,
which is what an application that knows the group it wants has: draft-16
Section 9.16.2.1 has the publisher set the Start Location to
{Joining Start, 0}.
Sourcepub async fn fetch_cancel(
&mut self,
request_id: VarInt,
) -> Result<(), ConnectionError>
pub async fn fetch_cancel( &mut self, request_id: VarInt, ) -> Result<(), ConnectionError>
Send a FETCH_CANCEL for the given request ID.
Sourcepub async fn fetch_ok(
&mut self,
request_id: VarInt,
end_of_track: u8,
end_group: VarInt,
end_object: VarInt,
parameters: Vec<KeyValuePair>,
track_extensions: Vec<KeyValuePair>,
) -> Result<(), ConnectionError>
pub async fn fetch_ok( &mut self, request_id: VarInt, end_of_track: u8, end_group: VarInt, end_object: VarInt, parameters: Vec<KeyValuePair>, track_extensions: Vec<KeyValuePair>, ) -> Result<(), ConnectionError>
Accept a fetch the peer opened, sending FETCH_OK.
The endpoint refuses a Joining Fetch naming a subscription this session cannot join and refuses a second answer to one FETCH, so nothing is written on the wire when it does either.
Sourcepub async fn subscribe_namespace(
&mut self,
namespace_prefix: TrackNamespace,
subscribe_options: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<NamespaceStream, ConnectionError>
pub async fn subscribe_namespace( &mut self, namespace_prefix: TrackNamespace, subscribe_options: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<NamespaceStream, ConnectionError>
Send a SUBSCRIBE_NAMESPACE on a bidirectional stream of its own.
Section 6.1: “The subscriber sends SUBSCRIBE_NAMESPACE on a new bidirectional stream and the publisher MUST send a single REQUEST_OK or REQUEST_ERROR as the first message on the bidirectional stream in response to a SUBSCRIBE_NAMESPACE.” Every other draft-16 request is still written on the control stream; this is the one that is not.
The returned NamespaceStream must be held while the subscription
is live. Section 6.1 makes closing the stream the cancellation, so
letting the handle fall out of scope withdraws the subscription — see
the type’s own note.
subscribe_options selects what the publisher reports back: PUBLISH
(0x00), NAMESPACE (0x01) or both (0x02), per Section 9.25.
§Ordering
The stream is opened before the Request ID is allocated, because a failed open would otherwise burn an id the endpoint cannot retract. If the endpoint refuses the request the stream is reset rather than dropped: dropping would FIN it, which on this draft says a subscription that was never made has been withdrawn.
Sourcepub async fn accept_namespace_stream(
&mut self,
) -> Result<(ControlMessage, NamespaceStream), ConnectionError>
pub async fn accept_namespace_stream( &mut self, ) -> Result<(ControlMessage, NamespaceStream), ConnectionError>
Accept the next bidirectional stream the peer opened, read the SUBSCRIBE_NAMESPACE it begins with, and hand back that request and a handle to answer it on.
The mirror of subscribe_namespace.
Section 3.3 does not say who may open the second kind of bidirectional
stream, and a relay subscribing to what a client publishes is the
ordinary case, so a client that never calls this can never be asked for
its namespaces.
The returned NamespaceStream carries RequestOrigin::Peer.
Answer it with
respond_ok_on_namespace_stream
or
respond_error_on_namespace_stream,
and hold it for as long as the subscription lasts — every NAMESPACE
and NAMESPACE_DONE is written on it, and dropping it ends the
subscription.
§Two refusals, two codes
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 that is not valid for the peer, or a new request with a Request ID that is not the next in sequence or exceeds the received MAX_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.
The refusal cannot be built without the acceptance. An endpoint that took a bidirectional stream only to refuse everything on it would close sessions over the SUBSCRIBE_NAMESPACE the same sentence permits.
§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.
§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. Registering earlier would let a cancelled accept leave a state machine keyed to a stream nobody holds, and the peer’s next Request ID would then look out of sequence — a session close, over an id the peer used exactly once.
§Errors
ConnectionError::NonSubscribeNamespaceOnBidiStream— the session has been closed with PROTOCOL_VIOLATION and the stream reset.ConnectionError::EndpointcarryingRequestId— the session has been closed with INVALID_REQUEST_ID and the stream reset.ConnectionError::EndpointcarryingNotActiveorDraining— the stream is reset, the session is left alone.ConnectionError::TransportorConnectionError::Codec— the stream is reset, the session is left alone.
Sourcefn take_pending_inbound(&self) -> Option<(FramedSendStream, FramedRecvStream)>
fn take_pending_inbound(&self) -> Option<(FramedSendStream, FramedRecvStream)>
Take the oldest stream pair a cancelled
accept_namespace_stream put back, if
any.
Synchronous on purpose: the guard is dropped before the caller awaits, so the lock is never held across a suspension point.
Sourcepub fn pending_inbound_count(&self) -> usize
pub fn pending_inbound_count(&self) -> usize
How many peer-opened namespace streams a cancelled
accept_namespace_stream put back and
a later call has not yet taken.
Zero unless an accept future was dropped mid-read.
Sourcepub async fn recv_on_namespace_stream(
&mut self,
stream: &mut NamespaceStream,
) -> Result<Option<ControlMessage>, ConnectionError>
pub async fn recv_on_namespace_stream( &mut self, stream: &mut NamespaceStream, ) -> Result<Option<ControlMessage>, ConnectionError>
Read the next message off a namespace subscription’s stream and dispatch it through the endpoint.
Three things can come back, and each is one of the shapes Section 6.1 and Section 9.25 describe:
Ok(Some(msg))— a REQUEST_OK or REQUEST_ERROR answering the subscription, or a NAMESPACE or NAMESPACE_DONE reporting on it.Ok(None)— the peer finished its half with a FIN. Section 6.1 makes that a cancellation, and it is recorded here.ErrcarryingTransportError::StreamReset— the peer reset the stream, the other form of the same cancellation, also recorded.
This blocks until a whole message has arrived. Backpressure is per subscription: a stream nobody reads stays unread, and the peer stays flow controlled on it alone.
§A stream the peer opened reports but does not dispatch
Draft-16 places nothing after the SUBSCRIBE_NAMESPACE on the
subscriber’s half, so on a RequestOrigin::Peer stream there is no
state for a second message to move and none is attempted; what a
responder reads for is the peer’s FIN. A message that arrives anyway is
handed back rather than refused, because no sentence in this draft
forbids it.
§Errors
ConnectionError::Endpoint if the message does not fit the
subscription’s state, or names a different request than this stream
carries. The message has already been emitted to the observer by
then — what arrived is reported whether or not the endpoint accepts it.
Sourcepub async fn send_on_namespace_stream(
&mut self,
stream: &mut NamespaceStream,
msg: &ControlMessage,
) -> Result<(), ConnectionError>
pub async fn send_on_namespace_stream( &mut self, stream: &mut NamespaceStream, msg: &ControlMessage, ) -> Result<(), ConnectionError>
Write a message on an open namespace subscription stream.
This is for what follows the answer: Section 9.25 says the publisher “will send matching NAMESPACE messages on the response stream if they are requested”, and NAMESPACE_DONE withdraws one of them on the same stream. The answer itself has its own helpers, which drive the endpoint as well as the wire.
It does not refuse any message type: which messages may follow the answer is not something this implementation can settle, so the choice is left to the caller rather than guessed at.
Sourcepub async fn respond_ok_on_namespace_stream(
&mut self,
stream: &mut NamespaceStream,
parameters: Vec<KeyValuePair>,
) -> Result<(), ConnectionError>
pub async fn respond_ok_on_namespace_stream( &mut self, stream: &mut NamespaceStream, parameters: Vec<KeyValuePair>, ) -> Result<(), ConnectionError>
Accept the peer’s SUBSCRIBE_NAMESPACE with a REQUEST_OK on its own stream.
Section 6.1: “the publisher MUST send a single REQUEST_OK or REQUEST_ERROR as the first message on the bidirectional stream in response to a SUBSCRIBE_NAMESPACE.” The Request ID is taken from the stream rather than from the caller, which is what makes the correlation unforgeable.
The endpoint goes first and the message is written only if it agrees.
§Errors
ConnectionError::NotOursToAnswer if stream was opened by this
endpoint, and ConnectionError::Endpoint if the subscription has
already been answered or has ended.
Sourcepub async fn respond_error_on_namespace_stream(
&mut self,
stream: &mut NamespaceStream,
error_code: VarInt,
retry_interval: VarInt,
reason_phrase: Vec<u8>,
) -> Result<(), ConnectionError>
pub async fn respond_error_on_namespace_stream( &mut self, stream: &mut NamespaceStream, error_code: VarInt, retry_interval: VarInt, reason_phrase: Vec<u8>, ) -> Result<(), ConnectionError>
Refuse the peer’s SUBSCRIBE_NAMESPACE with a REQUEST_ERROR on its own stream, and finish the stream.
Section 9.25 says what follows the refusal: “If it is an error, the stream will be immediately closed via FIN.” So this writes and then finishes, and the handle is closed when it returns.
§Errors
Sourceasync fn respond_on_namespace_stream(
&mut self,
stream: &mut NamespaceStream,
msg: ControlMessage,
) -> Result<(), ConnectionError>
async fn respond_on_namespace_stream( &mut self, stream: &mut NamespaceStream, msg: ControlMessage, ) -> Result<(), ConnectionError>
Drive the endpoint, then write the answer.
The order is the one every request path here uses: a caller acts on a stream after the endpoint has accepted the step, never before.
Sourcepub fn cancel_namespace_stream(
&mut self,
stream: &mut NamespaceStream,
code: u64,
) -> Result<(), ConnectionError>
pub fn cancel_namespace_stream( &mut self, stream: &mut NamespaceStream, code: u64, ) -> Result<(), ConnectionError>
Withdraw a namespace subscription by resetting its stream: record it at the endpoint, then reset.
Section 6.1 puts the withdrawal at the stream — “A SUBSCRIBE_NAMESPACE
can be cancelled by closing the stream with either a FIN or
RESET_STREAM” — while the subscription’s own state lives in the
endpoint, so the two have to move together. This and
finish_namespace_stream are the only
places that move both.
The endpoint goes first and the stream is reset only if it agrees. A
refused withdrawal therefore leaves the stream exactly as it was, and
NamespaceStream::cancel is still there for a caller that wants the
stream reset regardless.
Idempotent from both ends: a subscription that has already ended accepts it and stays where it is, and a handle that is already closed resets nothing a second time.
§Errors
ConnectionError::Endpoint if no namespace subscription carries this
stream’s id or nothing was ever written on it, and
ConnectionError::Transport if code is outside the QUIC varint
range — see NamespaceStream::cancel, which is what sends it.
Sourcepub async fn finish_namespace_stream(
&mut self,
stream: &mut NamespaceStream,
) -> Result<(), ConnectionError>
pub async fn finish_namespace_stream( &mut self, stream: &mut NamespaceStream, ) -> Result<(), ConnectionError>
Withdraw a namespace subscription by finishing its stream: record it at the endpoint, then FIN.
The other form Section 6.1 allows, and the one that needs no error
code. See cancel_namespace_stream
for the ordering and the idempotence, which are the same.
Sourcepub async fn peer_cancelled_on_namespace_stream(
&mut self,
stream: &mut NamespaceStream,
) -> Result<Option<u64>, ConnectionError>
pub async fn peer_cancelled_on_namespace_stream( &mut self, stream: &mut NamespaceStream, ) -> Result<Option<u64>, ConnectionError>
Wait for the peer to reset this subscription’s stream, and record it if it does.
NamespaceStream::peer_cancelled with the endpoint’s record
attached. A caller applying backpressure is deliberately not calling
recv_on_namespace_stream, which is
the other place a peer reset surfaces, so without this the subscription
would end on the wire and stay open in the endpoint’s record for as
long as the backpressure lasts.
It sees a reset and not a FIN — see
NamespaceStream::peer_cancelled. Cancel-safe, and it grants no
flow-control credit.
Sourcepub async fn publish_namespace(
&mut self,
track_namespace: TrackNamespace,
parameters: Vec<KeyValuePair>,
) -> Result<VarInt, ConnectionError>
pub async fn publish_namespace( &mut self, track_namespace: TrackNamespace, parameters: Vec<KeyValuePair>, ) -> Result<VarInt, ConnectionError>
Send a PUBLISH_NAMESPACE and return the request ID.
Sourcepub async fn request_ok(
&mut self,
request_id: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<(), ConnectionError>
pub async fn request_ok( &mut self, request_id: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<(), ConnectionError>
Accept a request the peer opened, sending REQUEST_OK.
The endpoint refuses a second answer to one request, so nothing is written on the wire when it does. On this draft an announcement and a track status are the requests REQUEST_OK accepts; a subscription, a publication and a fetch each have an acceptance of their own that carries more than this one can.
Sourcepub async fn publish_namespace_cancel(
&mut self,
request_id: VarInt,
error_code: VarInt,
reason_phrase: Vec<u8>,
) -> Result<(), ConnectionError>
pub async fn publish_namespace_cancel( &mut self, request_id: VarInt, error_code: VarInt, reason_phrase: Vec<u8>, ) -> Result<(), ConnectionError>
Revoke an acceptance, sending PUBLISH_NAMESPACE_CANCEL.
The endpoint refuses one for an announcement it never accepted, so nothing is written on the wire when it does.
Sourcepub async fn publish_namespace_done(
&mut self,
request_id: VarInt,
) -> Result<(), ConnectionError>
pub async fn publish_namespace_done( &mut self, request_id: VarInt, ) -> Result<(), ConnectionError>
Withdraw an announcement this endpoint made, sending PUBLISH_NAMESPACE_DONE.
The mirror of Self::publish_namespace, and the counterpart of
Self::publish_namespace_cancel: this one ends an announcement of
this endpoint’s, that one revokes the acceptance of one the peer made.
Sourcepub async fn track_status(
&mut self,
track_namespace: TrackNamespace,
track_name: Vec<u8>,
parameters: Vec<KeyValuePair>,
) -> Result<VarInt, ConnectionError>
pub async fn track_status( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, parameters: Vec<KeyValuePair>, ) -> Result<VarInt, ConnectionError>
Send a TRACK_STATUS and return the allocated request ID.
Sourcepub async fn publish(
&mut self,
track_namespace: TrackNamespace,
track_name: Vec<u8>,
track_alias: VarInt,
track_extensions: Vec<KeyValuePair>,
parameters: Vec<KeyValuePair>,
) -> Result<VarInt, ConnectionError>
pub async fn publish( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, track_alias: VarInt, track_extensions: Vec<KeyValuePair>, parameters: Vec<KeyValuePair>, ) -> Result<VarInt, ConnectionError>
Send a PUBLISH and return the allocated request ID.
Sourcepub async fn publish_done(
&mut self,
request_id: VarInt,
status_code: VarInt,
stream_count: VarInt,
reason_phrase: Vec<u8>,
) -> Result<(), ConnectionError>
pub async fn publish_done( &mut self, request_id: VarInt, status_code: VarInt, stream_count: VarInt, reason_phrase: Vec<u8>, ) -> Result<(), ConnectionError>
Send a PUBLISH_DONE for the given request ID.
Sourcepub async fn open_subgroup_stream(
&self,
header: &AnySubgroupHeader,
) -> Result<FramedSendStream, ConnectionError>
pub async fn open_subgroup_stream( &self, header: &AnySubgroupHeader, ) -> Result<FramedSendStream, ConnectionError>
Open a new unidirectional stream for sending subgroup data.
Sourcepub async fn open_fetch_stream(
&self,
header: &AnyFetchHeader,
) -> Result<FramedSendStream, ConnectionError>
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.
Sourcepub async fn accept_fetch_stream(
&self,
) -> Result<(AnyFetchHeader, FramedRecvStream), ConnectionError>
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.
Sourcepub async fn accept_subgroup_stream(
&self,
) -> Result<(AnySubgroupHeader, FramedRecvStream), ConnectionError>
pub async fn accept_subgroup_stream( &self, ) -> Result<(AnySubgroupHeader, FramedRecvStream), ConnectionError>
Accept an incoming unidirectional data stream and read its subgroup header.
Sourcepub fn send_datagram(
&self,
header: &AnyDatagramHeader,
payload: &[u8],
) -> Result<(), ConnectionError>
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.
Sourcepub async fn recv_datagram(
&self,
) -> Result<(AnyDatagramHeader, Bytes), ConnectionError>
pub async fn recv_datagram( &self, ) -> Result<(AnyDatagramHeader, Bytes), ConnectionError>
Receive a datagram and decode its header.
Sourcefn close_for(&self, err: &EndpointError)
fn close_for(&self, err: &EndpointError)
Close the session on the wire when the endpoint says a violation is fatal to it, and hand the error back unchanged.
EndpointError::session_error_code answers Some for exactly the
errors this draft ends 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 is purely internal: the local endpoint refuses to
start anything new while the peer, which is the one that broke the
rule, sees a session that is still open and goes on sending. A rule
that names a session termination code 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
rule rather than repeating the numeric code the close already carries.
Errors that answer None are recoverable and nothing is sent.
Sourcefn close_if_session_fatal(&self, err: EndpointError) -> ConnectionError
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.
Sourceasync fn withdraw_malformed_track(
&self,
alias: u64,
condition: MalformedTrackCondition,
)
async fn withdraw_malformed_track( &self, alias: u64, condition: MalformedTrackCondition, )
Send the messages Section 2.4.2 asks for when a track is found Send the messages Section 2.4.2 asks for when a track is found malformed, and stop at the first one the control stream refuses.
“When a subscriber detects a Malformed Track, it MUST UNSUBSCRIBE any subscription and FETCH_CANCEL any fetch for that Track from that publisher” - one message per request, in Request ID order, and the endpoint decides which message each request takes.
A write that fails is not reported. The caller is on its way to returning an error that says what went wrong with the track, and a control stream that will not take an UNSUBSCRIBE is a session on its way out for a reason of its own; replacing the condition’s report with a transport error would lose the only account of why the track was withdrawn. The rest of the withdrawal is abandoned, because a stream that refused one message will refuse the next.
Sourcepub async fn withdraw_for_data_stream(&self, err: &ConnectionError) -> bool
pub async fn withdraw_for_data_stream(&self, err: &ConnectionError) -> bool
Withdraw from a track a data stream found malformed, reporting whether it did.
The Malformed Track twin of Connection::close_for_data_stream, and
separate from it for the same reason and one more. The same one: a
FramedRecvStream holds no connection, so the reader that finds the
fault is not the object that can send an UNSUBSCRIBE. The one more: the
two answers are opposites - that call ends the session, this one gives
up a track and leaves it running - and a single entry point would have
to decide between them from the error alone, which is exactly the
decision a caller reproducing a capture wants to make itself.
The datagram path needs none of this. It is read through the connection,
so Connection::recv_datagram answers the condition where it finds
it, and this is only for the objects that arrive on a stream the caller
holds.
Sourcepub fn close_for_data_stream(&self, err: &ConnectionError) -> bool
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-16 answers with a close. Reports whether it closed.
accept_subgroup_stream hands the caller
a FramedRecvStream, which holds no connection and so cannot close
one, and the read that raises this failure happens 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.
Answers the extension-header rule of Section 10.2.1.2, and any decode
failure codec_session_error_code recognises, so a rule is answered
with one code whichever stream carried it.
Sourcepub fn endpoint_mut(&mut self) -> &mut Endpoint
pub fn endpoint_mut(&mut self) -> &mut Endpoint
Mutable access to the endpoint state machine.
Sourcepub fn draft(&self) -> DraftVersion
pub fn draft(&self) -> DraftVersion
Returns the draft version this connection is using.
Sourcefn codec_session_error_code(err: &CodecError) -> Option<SessionErrorCode>
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-16 answers with a close.
Every variant listed here comes from a sentence in this draft that names the consequence, and the list is deliberately shorter than draft-17’s: the bounds are per draft, and answering one this draft does not state would close a session over traffic a conforming peer may send.
-
Reason Phrase, maximum 1024 bytes: “If an endpoint receives a length exceeding the maximum, it MUST close the session with a PROTOCOL_VIOLATION.”
-
KVP value, maximum 2^16-1 bytes, with the same sentence.
-
Track Namespace field count: “If an endpoint receives a Track Namespace consisting of 0 or greater than 32 Track Namespace Fields, it MUST close the session with a PROTOCOL_VIOLATION.” Note the lower bound — an empty tuple is refused here, where drafts 17 and later permit it.
-
Full Track Name, maximum 4,096 bytes. This draft widened the rule from draft-15’s: “If an endpoint receives a Track Namespace or a Full Track Name exceeding 4,096 bytes”.
-
Duplicate parameters, a SHOULD rather than a MUST: “Receivers SHOULD check that there are no unexpected duplicate parameters and close the session as a PROTOCOL_VIOLATION”
-
A Track Namespace Field of length zero, which draft-15 does not state: “Each Track Namespace Field Value MUST contain at least one byte.”
-
The delta-encoded parameter type overflow, which arrives with this draft along with delta encoding itself.
-
GOAWAY New Session URI, maximum 8,192 bytes: “If an endpoint receives a length exceeding the maximum, it MUST close the session with a PROTOCOL_VIOLATION.” Every draft from 11 to 19 states it; 07 through 10 state no maximum for the field at all.
-
Unknown control message type: “An endpoint that receives an unknown message type MUST close the session.” All thirteen drafts state it, in the same words, and the sentence names no code, so Protocol Violation is what carries it.
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.
Sourcefn close_for_codec(&self, err: ConnectionError) -> ConnectionError
fn close_for_codec(&self, err: ConnectionError) -> ConnectionError
Close the session on the wire when a decode failure is one draft-16 answers with a close, and hand the error back unchanged. 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.