pub struct Endpoint {Show 23 fields
role: Role,
session: SessionStateMachine,
request_ids: RequestIdAllocator,
advertised_max_id: u64,
subscriptions: HashMap<u64, Mutex<SubscriptionStateMachine>>,
fetches: HashMap<u64, Mutex<FetchStateMachine>>,
fetch_tracks: HashMap<u64, FetchTrack>,
subscribe_namespace_prefixes: HashMap<u64, TrackNamespace>,
subscribe_namespaces: HashMap<u64, SubscribeNamespaceStateMachine>,
inbound_subscribe_namespaces: HashMap<u64, SubscribeNamespace>,
overlapping_namespace_subscriptions: HashMap<u64, u64>,
publish_namespaces: HashMap<u64, PublishNamespaceStateMachine>,
inbound_publish_namespaces: HashMap<u64, InboundPublishNamespace>,
track_statuses: HashMap<u64, TrackStatusStateMachine>,
inbound_track_statuses: HashMap<u64, InboundTrackStatus>,
publishes: HashMap<u64, Mutex<PublishStateMachine>>,
inbound_publishes: HashMap<u64, Publish>,
inbound_subscribes: HashMap<u64, InboundSubscribe>,
inbound_fetches: HashMap<u64, InboundFetch>,
goaway_uri: Option<Vec<u8>>,
track_bindings: HashMap<u64, TrackBinding>,
malformed: Mutex<MalformedTracks>,
locations: Arc<Mutex<TrackLocations>>,
}Expand description
Unified MoQT endpoint wrapping session lifecycle, request ID allocation, and all per-request state machines (subscriptions, fetches, namespaces).
Fields§
§role: Role§session: SessionStateMachine§request_ids: RequestIdAllocator§advertised_max_id: u64Tracks the MAX_REQUEST_ID we have advertised to the peer (monotonic).
subscriptions: HashMap<u64, Mutex<SubscriptionStateMachine>>Each subscription this endpoint made, behind a lock apiece.
A lock rather than &mut self because Section 2.4.2’s answer to a
Malformed Track is a message per request, the conditions that make a
track malformed are detected where objects arrive, and objects arrive
through a shared reference to the connection carrying them.
fetches: HashMap<u64, Mutex<FetchStateMachine>>Each fetch this endpoint made, behind a lock apiece, for the reason the subscriptions above are: the same sentence ends a fetch for a malformed track and ends it from the same place.
fetch_tracks: HashMap<u64, FetchTrack>The track each fetch this endpoint made is for.
§Why this is not in track_bindings
That table exists to answer questions about Track Aliases, and a fetch has none: its objects arrive on a stream that opens by naming the Request ID, so no alias rule can ever be about a fetch. An entry that can never hold an alias would be one every reader of that table had to learn to skip.
§Why a Joining Fetch is resolved here rather than when it is read
A Joining Fetch names no track. It names the subscription it joins, and the draft takes the rest from there: a publisher receiving one uses properties of the associated Subscribe to determine the Track Namespace, Track Name and End Location. So its track is the joined subscription’s, and it is looked up once, as the fetch is made.
The other place to look it up is the withdrawal, through the request the fetch joined, and that fails in the case a Joining Fetch exists for: one fills a buffer behind the live edge, so it outlives the subscription it joined, and the lookup would come up empty exactly while there was still a fetch to cancel.
subscribe_namespace_prefixes: HashMap<u64, TrackNamespace>The namespace prefix each SUBSCRIBE_NAMESPACE this endpoint sent asked about, which the state machine beside it does not hold.
Read by Endpoint::own_prefix_overlap and by nothing else. The
withdrawal names the Request ID here, so until the rule about
overlapping prefixes was judged there was nothing to remember a prefix
for.
subscribe_namespaces: HashMap<u64, SubscribeNamespaceStateMachine>§inbound_subscribe_namespaces: HashMap<u64, SubscribeNamespace>The SUBSCRIBE_NAMESPACE each namespace subscription the peer opened arrived as, keyed by the Request ID it carries.
The state of each one stays in subscribe_namespaces beside this
endpoint’s own, because a Request ID belongs to one request whichever
end opened it. The prefix does not. It is named in the request and
nowhere else, and it is what both the answer and every later request
are read against – Section 9.25: “Within a session, if a publisher
receives a SUBSCRIBE_NAMESPACE with a Track Namespace Prefix that
shares a common prefix with an established namespace subscription, it
MUST respond with REQUEST_ERROR with error code PREFIX_OVERLAP.”
overlapping_namespace_subscriptions: HashMap<u64, u64>The namespace subscription each of the peer’s overlapped when it arrived, keyed by the Request ID of the one that arrived.
Section 9.25 judges the prefix on arrival – “if a publisher receives a SUBSCRIBE_NAMESPACE with a Track Namespace Prefix that shares a common prefix with an established namespace subscription” – so the verdict is taken there and read again when the answer is written. An entry means this endpoint owes that request a REQUEST_ERROR and may send it nothing else.
publish_namespaces: HashMap<u64, PublishNamespaceStateMachine>§inbound_publish_namespaces: HashMap<u64, InboundPublishNamespace>Announcements the peer made, keyed by the Request ID it opened each under.
track_statuses: HashMap<u64, TrackStatusStateMachine>§inbound_track_statuses: HashMap<u64, InboundTrackStatus>Track statuses the peer asked about, keyed by the Request ID it asked under.
Kept apart from track_statuses, which holds the ones this endpoint
asked about: the two are answered by opposite ends, and one Request ID
belongs to one request whichever end opened it.
publishes: HashMap<u64, Mutex<PublishStateMachine>>Both directions’ publish flows, behind a lock apiece.
A subscription the peer opened with PUBLISH is one of the two ways this endpoint receives a track, so the withdrawal a Malformed Track calls for has to reach these as well as the subscriptions above.
inbound_publishes: HashMap<u64, Publish>The PUBLISH each offer the peer made arrived as, keyed by the Request ID it carries.
The state of each one stays in publishes beside this endpoint’s own
offers, because every step after arrival names one Request ID and a
Request ID the peer allocated can never be one this endpoint
allocated. What a map of state machines cannot hold is the offer
itself, and the offer is what the answer is decided from. Section 5.1:
“A publisher initiates a subscription to a track by sending the
PUBLISH message. The subscriber either accepts or rejects the
subscription using PUBLISH_OK or REQUEST_ERROR.”
track_bindings already keeps the Full Track Name and the Track
Alias, because the rules about aliases read them. The parameters and
the track extensions are here and nowhere else.
inbound_subscribes: HashMap<u64, InboundSubscribe>Subscriptions the peer opened with SUBSCRIBE, by Request ID.
Section 9.11 puts the update in the subscriber’s hands, so one that
arrives names a subscription in here and never one in subscriptions.
The record holds the SUBSCRIBE itself and not only its state, because the answer is built out of the request: the track a SUBSCRIBE_OK is about is named nowhere else, and the alias it hands out has to be judged against that name. A PUBLISH needs no such record because it carries its track and its alias in the one message.
inbound_fetches: HashMap<u64, InboundFetch>Every FETCH the peer has sent, from arrival to the end of the fetch.
Separate from fetches, which holds the ones this endpoint made. The
record holds the FETCH itself and not only its state, because the
answer is built out of the request: a Joining Fetch has to be judged
against the subscription it names, and that name is nowhere else.
goaway_uri: Option<Vec<u8>>§track_bindings: HashMap<u64, TrackBinding>What Full Track Name the peer has attached each Track Alias to, per Request ID.
Sections 9.10 and 9.13 forbid one alias naming two tracks at once, and the “at once” is what makes this a table rather than a set: an alias the peer used for a track whose subscription has ended is free again. The table therefore records the binding and reads liveness back off the subscription’s own state machine, rather than keeping a second copy of it that every path ending a subscription would have to remember to prune.
malformed: Mutex<MalformedTracks>Which tracks this endpoint has given up on, and what for.
Behind a lock for the reason the flows above are: the note is taken on
the data plane, where this endpoint is reached through &self.
locations: Arc<Mutex<TrackLocations>>How far each track’s objects have reached, and where a track ended.
An Arc rather than a plain lock because a subgroup stream measures
its objects against one track for as long as it runs, and the handle it
holds outlives any single call into this endpoint.
Implementations§
Source§impl Endpoint
impl Endpoint
Sourcepub fn track_alias_for(&self, request_id: VarInt) -> Option<VarInt>
pub fn track_alias_for(&self, request_id: VarInt) -> Option<VarInt>
The Track Alias the peer attached to request_id, once it has named
one.
Answers for a subscription this endpoint asked for from the moment its
SUBSCRIBE_OK arrives, and for one the peer offered from the moment its
PUBLISH does. None before that, and for a Request ID this session has
no track for.
Sourcefn conflicting_track_alias(
&self,
request_id: u64,
alias: u64,
namespace: &TrackNamespace,
name: &[u8],
) -> Option<EndpointError>
fn conflicting_track_alias( &self, request_id: u64, alias: u64, namespace: &TrackNamespace, name: &[u8], ) -> Option<EndpointError>
The refusal Sections 9.10 and 9.13 require when alias already names a
different track whose subscription is still live, or None when it is
free.
§Why the set is read rather than kept
“Established” is a subscription state Section 5.1 defines, and this endpoint’s subscription state machine holds it: a subscription reaches Active on SUBSCRIBE_OK and leaves it on the message that ends the flow. Asking it is what makes an alias free again the moment its track’s subscription ends, with nothing to prune on the way out - and a path that ended a subscription without telling this table would otherwise leave the alias held forever and refuse the peer’s next, conforming, use of it.
§Why the request’s own binding is skipped
A SUBSCRIBE_OK is judged before its own alias is written down, so the skip is not what keeps it from finding itself. A PUBLISH is: it carries its alias and its track in the one message, and a second PUBLISH under a Request ID already bound is refused by the duplicate-Request-ID rule before it reaches here.
Sourcefn alias_held_elsewhere(
&self,
alias: u64,
namespace: &TrackNamespace,
name: &[u8],
) -> Option<EndpointError>
fn alias_held_elsewhere( &self, alias: u64, namespace: &TrackNamespace, name: &[u8], ) -> Option<EndpointError>
The request already using alias for a track other than (namespace,
name), or None when this endpoint may give the alias to that track.
Separate from Self::conflicting_track_alias because the two answer
different questions about the same table. That one judges a message
that has arrived and ends the session over it; this one judges one that
has not been built and declines to build it.
Sourcefn binding_is_in_use(&self, id: u64, kind: BindingKind) -> bool
fn binding_is_in_use(&self, id: u64, kind: BindingKind) -> bool
Whether a binding’s request has put its alias in play at all.
Broader than Self::binding_is_established, and the two sentences are
why. What a subscriber must close over is qualified - “the same Track
Alias as a different track with an Established subscription” - and the
prohibition on the publisher is not: “The same Track Alias MUST NOT be
used to refer to two different Tracks simultaneously.” Once a PUBLISH
carrying an alias has been sent, giving that alias to a second track is
what that sentence forbids, answered or not.
Sourcefn binding_is_established(&self, id: u64, kind: BindingKind) -> bool
fn binding_is_established(&self, id: u64, kind: BindingKind) -> bool
Whether the request that owns a binding still has a live subscription.
The two kinds are answered by two different state machines because the two sequences Section 5.1 names end in different places: a SUBSCRIBE this endpoint made is live once its SUBSCRIBE_OK arrives, a PUBLISH the peer made once this endpoint has answered PUBLISH_OK.
Sourcefn conflicting_alias_for_subscribe_ok(
&self,
id: u64,
alias: u64,
) -> Option<EndpointError>
fn conflicting_alias_for_subscribe_ok( &self, id: u64, alias: u64, ) -> Option<EndpointError>
The conflict a SUBSCRIBE_OK’s alias has with the tracks already bound.
Separate from Self::conflicting_track_alias because the track a
SUBSCRIBE_OK is about is not in the SUBSCRIBE_OK: it is the one this
endpoint’s own SUBSCRIBE asked for, which is why the request has to be
looked up before the alias can be judged.
Sourcefn track_for_alias(&self, alias: u64) -> Option<(&TrackNamespace, &[u8])>
fn track_for_alias(&self, alias: u64) -> Option<(&TrackNamespace, &[u8])>
The track a live binding has given alias to.
Read rather than kept, for the reason the alias table beside it gives: a binding whose request has ended holds nothing, and an alias that is free again may name a different track next. That is why the records this serves are keyed on the track it returns and never on the alias itself.
Sourcepub fn track_objects(&self, alias: u64) -> Option<TrackObjects>
pub fn track_objects(&self, alias: u64) -> Option<TrackObjects>
The record a stream carrying alias’s objects measures them against.
None for an alias no live binding names: an object for one breaks a
different rule, and measuring it against a track this endpoint never
asked for would answer that one with the wrong sentence.
Sourcepub fn note_received_object(
&self,
alias: u64,
at: ObjectLocation,
role: ObjectRole,
) -> Result<(), EndpointError>
pub fn note_received_object( &self, alias: u64, at: ObjectLocation, role: ObjectRole, ) -> Result<(), EndpointError>
Record or judge one object that arrived outside a subgroup stream, and report Section 2.4.2’s Malformed Track when it arrived after the place an end-of-track object put the end.
One rule and not two. The placement rule drafts 08 through 13 state about an end-of-track object is not in this draft, so an object that ends a track here is judged against nothing and only settles where the track stopped.
&self, because the call site is the data plane’s.
Sourcefn subscription(
&self,
id: u64,
) -> Option<MutexGuard<'_, SubscriptionStateMachine>>
fn subscription( &self, id: u64, ) -> Option<MutexGuard<'_, SubscriptionStateMachine>>
The subscription id opened, locked for a read or a transition.
&self, which is the whole point of the lock. Section 2.4.2 answers a
Malformed Track with a message per request, and the condition that makes
a track malformed here is detected on the data plane, where this
endpoint is reached through a shared reference. A flow that could only
be moved through &mut self would leave that sentence unanswerable from
the only place it is ever read.
Hold one guard at a time. Nothing here takes a second while a first is
live, and Self::withdraw_malformed_track collects the requests it is
going to move before it moves any of them for that reason.
Sourcefn publish_flow(&self, id: u64) -> Option<MutexGuard<'_, PublishStateMachine>>
fn publish_flow(&self, id: u64) -> Option<MutexGuard<'_, PublishStateMachine>>
The publish flow id opened, locked for a read or a transition.
One map for both directions, which is what makes this one accessor: the offer this endpoint made and the offer the peer made are the same flow read from opposite ends, and Request ID parity keeps them apart.
Sourcefn fetch_flow(&self, id: u64) -> Option<MutexGuard<'_, FetchStateMachine>>
fn fetch_flow(&self, id: u64) -> Option<MutexGuard<'_, FetchStateMachine>>
The fetch id opened, locked for a read or a transition.
Sourcepub fn malformed_track(
&self,
namespace: &TrackNamespace,
name: &[u8],
) -> Option<MalformedTrackCondition>
pub fn malformed_track( &self, namespace: &TrackNamespace, name: &[u8], ) -> Option<MalformedTrackCondition>
The condition a track was withdrawn for, or None for a track this
endpoint has found nothing wrong with.
The withdrawal happens without the application asking for it, so this is how an application that missed the error learns why a subscription it never ended has ended.
§Why this takes a track and not the alias the object carried
An alias only means anything through a live binding, and the withdrawal
ends the binding it would have been resolved through. An accessor taking
an alias would therefore answer None from the instant it had something
to say, which is worse than not existing. The record is keyed on the
track, and so is this.
Sourcepub fn withdraw_malformed_track(
&self,
alias: u64,
condition: MalformedTrackCondition,
) -> Vec<ControlMessage>
pub fn withdraw_malformed_track( &self, alias: u64, condition: MalformedTrackCondition, ) -> Vec<ControlMessage>
Withdraw from a track a condition has just shown to be malformed, and give back the messages Section 2.4.2 asks for.
“When a subscriber detects a Malformed Track, it MUST UNSUBSCRIBE any subscription and FETCH_CANCEL any fetch for that Track from that publisher, and SHOULD deliver an error to the application.” This is the first half. The second is the error the detecting path returns, which is why nothing here reports anything: a caller that gets an empty list back is not being told the track was fine.
&self, because the call site is the data plane’s.
§What comes back, and what does not
One message for each request through which this endpoint is receiving the track: an UNSUBSCRIBE for a SUBSCRIBE it sent and for a PUBLISH the peer sent that it accepted, and a FETCH_CANCEL for each fetch it made for that track. A request that names the track the other way round is not one of those — a peer subscribing to this endpoint makes it the publisher, and a publisher does not unsubscribe from what it is serving.
Empty for an alias no live binding names — there is no track to withdraw from — and for a track whose only requests are in a state that has no ending of that kind left in it.
§Two records, one scan
Subscriptions are found through the alias table and fetches through their own, because a fetch never holds an alias. Both are keyed by Request ID and a Request ID names one request, so the two lists cannot overlap and the withdrawal below can tell from the id alone which message a request takes.
§What makes the answer happen once
Not the record. The requests this withdraws end here, and a request that has ended has no second ending in it, so a publisher that goes on sending objects past the end of a track is answered once however many it sends. The record is read afterwards, by an application asking why a track it never gave up was given up.
Which leaves the case the two answers differ on: an application that subscribes to the same track again. That request has never been withdrawn from, and a publisher breaking the sentence again on it has broken it again, so it is withdrawn from too.
Sourcefn withdraw_one(&self, id: u64) -> Option<ControlMessage>
fn withdraw_one(&self, id: u64) -> Option<ControlMessage>
The message ending one request, or None when that request is not one
this endpoint receives a track through or is not in a state that can be
ended that way.
Sourcepub fn session_state(&self) -> SessionState
pub fn session_state(&self) -> SessionState
Returns the current session state.
Sourcepub fn goaway_uri(&self) -> Option<&[u8]>
pub fn goaway_uri(&self) -> Option<&[u8]>
Returns the URI from a received GOAWAY message, if any.
Sourcepub fn is_blocked(&self) -> bool
pub fn is_blocked(&self) -> bool
Returns whether this endpoint is blocked on request ID allocation.
Sourcepub fn active_subscription_count(&self) -> usize
pub fn active_subscription_count(&self) -> usize
Returns the number of active subscription state machines.
Sourcepub fn active_fetch_count(&self) -> usize
pub fn active_fetch_count(&self) -> usize
Returns the number of active fetch state machines.
Sourcepub fn active_subscribe_namespace_count(&self) -> usize
pub fn active_subscribe_namespace_count(&self) -> usize
Returns the number of active subscribe-namespace state machines.
Sourcepub fn active_publish_namespace_count(&self) -> usize
pub fn active_publish_namespace_count(&self) -> usize
Returns the number of active publish-namespace state machines.
Sourcepub fn active_track_status_count(&self) -> usize
pub fn active_track_status_count(&self) -> usize
Returns the number of active track status state machines.
Sourcepub fn active_publish_count(&self) -> usize
pub fn active_publish_count(&self) -> usize
Returns the number of active publish state machines.
Sourcepub fn connect(&mut self) -> Result<(), EndpointError>
pub fn connect(&mut self) -> Result<(), EndpointError>
Transition from Connecting to SetupExchange.
Sourcepub fn close(&mut self) -> Result<(), EndpointError>
pub fn close(&mut self) -> Result<(), EndpointError>
Close the session (SetupExchange, Active or Draining -> Closed).
Sourcepub fn send_client_setup(
&mut self,
parameters: Vec<KeyValuePair>,
) -> Result<ControlMessage, EndpointError>
pub fn send_client_setup( &mut self, parameters: Vec<KeyValuePair>, ) -> Result<ControlMessage, EndpointError>
Generate a CLIENT_SETUP message (client-side). Draft-16 uses ALPN for version negotiation – no versions field.
Sourcepub fn receive_server_setup(
&mut self,
msg: &ServerSetup,
) -> Result<(), EndpointError>
pub fn receive_server_setup( &mut self, msg: &ServerSetup, ) -> Result<(), EndpointError>
Process a SERVER_SETUP message (client-side). Transitions to Active. If the server includes a MAX_REQUEST_ID parameter (key 0x02), the request ID allocator is initialized with that value.
Sourcepub fn receive_client_setup_and_respond(
&mut self,
client_setup: &ClientSetup,
) -> Result<ControlMessage, EndpointError>
pub fn receive_client_setup_and_respond( &mut self, client_setup: &ClientSetup, ) -> Result<ControlMessage, EndpointError>
Process CLIENT_SETUP and generate SERVER_SETUP (server-side). Draft-16 uses ALPN for version negotiation – just transition and respond.
Sourcepub fn validate_peer_request_id(&mut self, id: u64) -> Result<(), EndpointError>
pub fn validate_peer_request_id(&mut self, id: u64) -> Result<(), EndpointError>
Take a Request ID from a new request the peer sent, holding it to all three halves of the rule.
Section 9.1 closes the session with INVALID_REQUEST_ID on a request ID
“that is not valid for the peer”, and Section 9.5 closes it with
TOO_MANY_REQUESTS on one at or above the ceiling this endpoint
advertised. The parity half belongs to the allocator; the ceiling half
needs advertised_max_id, which is this endpoint’s grant to the peer
and not the peer’s grant to it, so the two numbers are different and
only one of them answers this question.
The third is the sequence. Draft-15 rewrote “not expected” as “not the next in sequence”, which says the same thing in a way that cannot be read as advisory: each endpoint starts at 0 or 1 by role and steps by 2 per request, so the next value is a number. A repeat and a skip are both refused, and neither is caught by parity or by the ceiling - a repeat has the right parity and sits below the ceiling by construction, having been accepted once already.
Taking the ID is what advances the sequence, so this is for a new request only. A response, a cancellation or an update that names the request it modifies all carry an ID that has already been spent, and passing one here would refuse it.
§The ceiling is a session rule, not a request rule
Section 9.5: a Request ID “equal to or larger than this” received by the
endpoint that sent the MAX_REQUEST_ID in any request message - the list
Self::receive_request carries - closes the session, and
TOO_MANY_REQUESTS is the code. Which is also why the number measured
against is the one this endpoint sent. An id that reaches the ceiling
is not a request to refuse with an error message: the session is over, so
this moves the endpoint’s own state to Closed and leaves the code to
EndpointError::session_error_code.
§Two sections, two codes
This draft states the rule twice and does not agree with itself. Section 9.1 folds the ceiling into the sentence about the ID sequence - “a new request with a Request ID that is not the next in sequence or exceeds the received MAX_REQUEST_ID” - and answers it with INVALID_REQUEST_ID instead. It also measures against the received ceiling, which is the budget the peer granted this endpoint rather than the one this endpoint granted the peer, and measuring an incoming request against our own budget is not a rule that can be applied. Ten drafts state the ceiling in the MAX_REQUEST_ID section and all ten name the same code there; two of them restate it elsewhere. The MAX_REQUEST_ID section is the one followed here.
§Errors
RequestIdError::WrongParity if the ID belongs to this endpoint’s
half of the space, RequestIdError::ExceedsMax if it is not
below the advertised ceiling, or RequestIdError::OutOfSequence if it
is not the one the peer’s sequence called for. A ceiling that was never
raised is 0, which refuses every ID, matching a default that reads “the
peer MUST NOT send requests”.
All three end the session rather than the one request, so all three
move this endpoint’s own session to Closed on the way out and all three
answer Some from EndpointError::session_error_code. Returning one
of them without ending the session would leave a peer that broke the
rule free to keep sending, and never told.
Sourcefn record_advertised_max(&mut self, parameters: &[KeyValuePair])
fn record_advertised_max(&mut self, parameters: &[KeyValuePair])
Record a MAX_REQUEST_ID parameter this endpoint is about to send as the ceiling it has advertised to the peer.
Sourcepub fn receive_max_request_id(
&mut self,
msg: &MaxRequestId,
) -> Result<(), EndpointError>
pub fn receive_max_request_id( &mut self, msg: &MaxRequestId, ) -> Result<(), EndpointError>
Process an incoming MAX_REQUEST_ID message, ending the session if the ceiling it carries does not increase.
Section 9.5: “The Maximum Request ID MUST only increase within a session. If an endpoint receives MAX_REQUEST_ID message with an equal or smaller Request ID it MUST close the session with a PROTOCOL_VIOLATION.” Every draft below this one states the same rule as a condition rather than an action - naming the code without the verb - and this one supplies the verb.
§Errors
RequestIdError::Decreased if the value does not increase, with the
session already moved to Closed.
Sourcepub fn send_max_request_id(
&mut self,
max_id: VarInt,
) -> Result<ControlMessage, EndpointError>
pub fn send_max_request_id( &mut self, max_id: VarInt, ) -> Result<ControlMessage, EndpointError>
Generate a MAX_REQUEST_ID message (typically server-side).
Section 9.5: “The Maximum Request ID MUST only increase within a session”, and a peer that receives an equal or smaller value closes the session. The ceiling starts at 0 and 0 is not greater than 0, so the first value that may go on the wire is 1 and there is no opening case where a repeat is allowed.
§Errors
The decrease error if the value does not strictly increase.
Sourcepub fn send_requests_blocked(&self) -> Result<ControlMessage, EndpointError>
pub fn send_requests_blocked(&self) -> Result<ControlMessage, EndpointError>
Generate a REQUESTS_BLOCKED message indicating that this endpoint wants to create a new request but is blocked by the current MAX_REQUEST_ID.
Sourcepub fn receive_requests_blocked(
&self,
_msg: &RequestsBlocked,
) -> Result<(), EndpointError>
pub fn receive_requests_blocked( &self, _msg: &RequestsBlocked, ) -> Result<(), EndpointError>
Process an incoming REQUESTS_BLOCKED message from the peer.
Sourcepub fn receive_goaway(&mut self, msg: &GoAway) -> Result<(), EndpointError>
pub fn receive_goaway(&mut self, msg: &GoAway) -> Result<(), EndpointError>
Process an incoming GOAWAY message. Transitions to Draining.
§Errors
EndpointError::GoAwayUriAtServer if this endpoint is the server and
the GOAWAY carries a New Session URI. The session is over: this
endpoint’s own state has moved to Closed and the code the transport
should close with is in EndpointError::session_error_code.
EndpointError::RepeatedGoAway if a GOAWAY has already been
received. The session is over: this endpoint’s own state has moved to
Closed and the code the transport should close with is in
EndpointError::session_error_code.
fn require_active_or_err(&self) -> Result<(), EndpointError>
Sourcefn fail_session(&mut self, err: EndpointError) -> EndpointError
fn fail_session(&mut self, err: EndpointError) -> EndpointError
Record that the session is over because the peer broke a rule this draft answers with a session close, and hand the error back unchanged.
The state move is what makes the violation stick: every request entry
point goes through
require_active_or_err, so a caller
that ignores the returned error still cannot start anything new.
Closing on the wire is the connection layer’s job - see
EndpointError::session_error_code for the code it should use.
Sourcepub fn subscribe(
&mut self,
track_namespace: TrackNamespace,
track_name: Vec<u8>,
parameters: Vec<KeyValuePair>,
) -> Result<(VarInt, ControlMessage), EndpointError>
pub fn subscribe( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>
Send a SUBSCRIBE message. Allocates a request ID and creates a subscription state machine. Draft-16: simplified, no group_order/ filter_type/forward args.
Sourcepub fn receive_subscribe_ok(
&mut self,
msg: &SubscribeOk,
) -> Result<(), EndpointError>
pub fn receive_subscribe_ok( &mut self, msg: &SubscribeOk, ) -> Result<(), EndpointError>
Process an incoming SUBSCRIBE_OK. Draft-16: has request_id, track_alias, parameters.
Sourcepub fn unsubscribe(
&mut self,
request_id: VarInt,
) -> Result<ControlMessage, EndpointError>
pub fn unsubscribe( &mut self, request_id: VarInt, ) -> Result<ControlMessage, EndpointError>
Send an UNSUBSCRIBE message for an active subscription.
Section 5.1 gives the subscriber this for a subscription that came either way round, so a request the peer opened with PUBLISH ends here too. The two kinds share a map and cannot collide: a Request ID belongs to whichever endpoint allocated it, and the two halves of the space have opposite least significant bits.
Sourcepub fn request_update(
&mut self,
existing_request_id: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<(VarInt, ControlMessage), EndpointError>
pub fn request_update( &mut self, existing_request_id: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>
Send a REQUEST_UPDATE narrowing a subscription this endpoint opened.
Section 9.11 gives the message to the sender of the request, which is
what this endpoint is for every subscription in subscriptions. It
carries a Request ID of its own and is listed among the messages that
step the sequence, so one is allocated here and handed back with the
message.
§Errors
EndpointError::UnknownRequest when this endpoint opened no
subscription under that identifier, and the subscription flow’s own
InvalidTransition when the one it names has already ended.
Sourcepub fn receive_request_update(
&mut self,
msg: &RequestUpdate,
) -> Result<(), EndpointError>
pub fn receive_request_update( &mut self, msg: &RequestUpdate, ) -> Result<(), EndpointError>
Process an incoming REQUEST_UPDATE.
Section 9.11 puts the message in the subscriber’s hands, so one that arrives is about a subscription the peer opened, and is looked for among those and not among this endpoint’s own.
§Errors
EndpointError::UpdateForUnknownRequest when the Request ID names no
request this session has carried, with the session already moved to
Closed, and the subscription flow’s own InvalidTransition when it
names one that has already ended.
Sourcefn updated_request_kind(&self, id: u64) -> Option<UpdatedRequest>
fn updated_request_kind(&self, id: u64) -> Option<UpdatedRequest>
Which kind of request an identifier names, to the resolution Section 9.2.2’s qualifiers need.
Section 9.11 lists six kinds that can be updated - SUBSCRIBE, PUBLISH, FETCH, TRACK_STATUS, PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE - and adds that “a subscriber can also send REQUEST_UPDATE to modify parameters of a subscription established with PUBLISH”. The qualifiers distinguish only three of those: a subscription, a FETCH, and everything else. A subscription is one however it was established, so a PUBLISH this endpoint answered counts.
Sourcefn check_update_parameters(
&mut self,
id: u64,
kind: UpdatedRequest,
parameters: &[KeyValuePair],
) -> Result<(), EndpointError>
fn check_update_parameters( &mut self, id: u64, kind: UpdatedRequest, parameters: &[KeyValuePair], ) -> Result<(), EndpointError>
Hold a REQUEST_UPDATE’s parameters to the kind of request it names.
Section 9.2.2 qualifies four of them. SUBSCRIBER_PRIORITY “MAY appear in a SUBSCRIBE, FETCH, REQUEST_UPDATE (for a subscription or FETCH), PUBLISH_OK message”; SUBSCRIPTION_FILTER, FORWARD and NEW_GROUP_REQUEST each name REQUEST_UPDATE “for a subscription”. AUTHORIZATION TOKEN and DELIVERY TIMEOUT name REQUEST_UPDATE with no qualifier at all and are admitted whatever the kind.
Every other parameter passes untouched, which is not an oversight. GROUP_ORDER, EXPIRES and LARGEST_OBJECT do not name REQUEST_UPDATE among their message types, and Section 9.2.2 opens by saying a parameter that “appears in some other type of message … MUST be ignored”. Closing the session over one would answer with a violation what this draft answers by ignoring it.
Sourcepub fn receive_publish_done(
&mut self,
msg: &PublishDone,
) -> Result<(), EndpointError>
pub fn receive_publish_done( &mut self, msg: &PublishDone, ) -> Result<(), EndpointError>
Process an incoming PUBLISH_DONE (subscriber side – publisher finished). Draft-16: has request_id, status_code, stream_count, reason_phrase.
Section 5.1 gives the publisher this for a subscription that came either way round, so one the peer opened with PUBLISH ends here too.
Sourcepub fn receive_subscribe(
&mut self,
msg: &Subscribe,
) -> Result<(), EndpointError>
pub fn receive_subscribe( &mut self, msg: &Subscribe, ) -> Result<(), EndpointError>
Process an incoming SUBSCRIBE, recording the subscription it opens.
The Request ID has already been checked by Self::receive_request,
which every request message passes through before its own handler.
There is no Track Alias to judge here: Section 9.10 carries it in the
SUBSCRIBE_OK, so this endpoint chooses it, and it is judged when the
answer is built.
§Errors
The session error when the session is not established, and the
subscription flow’s own InvalidTransition for a second SUBSCRIBE
under a Request ID already carrying one.
Sourcepub fn pending_subscribe(&self, request_id: VarInt) -> Option<&Subscribe>
pub fn pending_subscribe(&self, request_id: VarInt) -> Option<&Subscribe>
The SUBSCRIBE the peer sent under request_id and this endpoint has
not answered yet.
None once it has been answered, and for an identifier this session
has no inbound subscription for. The record itself lives on for as long
as the session does, which is what lets an update say whether the
request it names has ever existed.
Sourcepub fn pending_subscribe_count(&self) -> usize
pub fn pending_subscribe_count(&self) -> usize
How many SUBSCRIBEs the peer has sent that are still waiting for an answer.
Sourcepub fn send_subscribe_ok(
&mut self,
request_id: VarInt,
track_alias: VarInt,
track_extensions: Vec<KeyValuePair>,
parameters: Vec<KeyValuePair>,
) -> Result<ControlMessage, EndpointError>
pub fn send_subscribe_ok( &mut self, request_id: VarInt, track_alias: VarInt, track_extensions: Vec<KeyValuePair>, parameters: Vec<KeyValuePair>, ) -> Result<ControlMessage, EndpointError>
Build the SUBSCRIBE_OK accepting a subscription the peer opened, giving its track a Track Alias.
§Errors
EndpointError::UnknownRequest if the peer opened no subscription
under that identifier, EndpointError::TrackAliasInUse if a live
track of this endpoint’s already holds the alias, and
EndpointError::Subscription if the request has already been
answered: Section 5.1 says “A publisher MUST send exactly one
SUBSCRIBE_OK or REQUEST_ERROR in response to a SUBSCRIBE.”
Sourcepub fn send_request_error(
&mut self,
request_id: VarInt,
error_code: VarInt,
retry_interval: VarInt,
reason_phrase: Vec<u8>,
) -> Result<ControlMessage, EndpointError>
pub fn send_request_error( &mut self, request_id: VarInt, error_code: VarInt, retry_interval: VarInt, reason_phrase: Vec<u8>, ) -> Result<ControlMessage, EndpointError>
Build the REQUEST_ERROR refusing a request the peer opened.
One message refuses more than one kind of request, so the record it ends is found by the identifier: the maps are keyed by Request ID and one id belongs to one request. A subscription, a fetch, an announcement the peer made and a track status it asked for all end here.
Section 9.19: “A publisher responds to a failed TRACK_STATUS with an appropriate REQUEST_ERROR message.”
Section 9.16.2 names the code a Joining Fetch naming an unjoinable subscription is refused with, so that refusal cannot go out under any other.
§Errors
EndpointError::UnknownRequest if the peer opened no subscription, no
fetch and no announcement under that identifier,
EndpointError::WrongJoiningRefusal if a Joining Fetch is refused
under the wrong code, and the flow’s own InvalidTransition if the
request has already been answered.
Sourcepub fn receive_unsubscribe(
&mut self,
msg: &Unsubscribe,
) -> Result<(), EndpointError>
pub fn receive_unsubscribe( &mut self, msg: &Unsubscribe, ) -> Result<(), EndpointError>
Process an incoming UNSUBSCRIBE, ending a subscription this endpoint publishes and freeing the Track Alias it held. Section 9.12: “A Subscriber issues an UNSUBSCRIBE message to a Publisher indicating it is no longer interested in receiving the specified Track, indicating that the Publisher stop sending Objects as soon as possible.” The message travels from subscriber to publisher, so what it can end is whatever this endpoint publishes - and Section 5.1 gives that two sources, not one: “A subscription can be initiated and moved to the Pending state by either a publisher or a subscriber.”
§Errors
EndpointError::UnknownRequest if this endpoint publishes no
subscription under that identifier, EndpointError::NotASubscription
for one that names a track status, EndpointError::Subscription if
it is one the peer opened that this endpoint never accepted or has
already ended, and EndpointError::PublishFlow for the same of one
this endpoint opened.
Sourcepub 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, ControlMessage), EndpointError>
pub 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, ControlMessage), EndpointError>
Send a standalone FETCH message. Allocates a request ID and creates a
fetch state machine. Draft-16: has end_object.
parameters are the request’s own, as they are on every other request
this endpoint makes. FETCH carried an empty list on all five of these
drafts while SUBSCRIBE, TRACK_STATUS, PUBLISH_NAMESPACE and PUBLISH
took the caller’s, which made it the only one an application could not
attach an authorization token to.
Sourcepub fn joining_fetch(
&mut self,
joining_request_id: VarInt,
joining_start: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<(VarInt, ControlMessage), EndpointError>
pub fn joining_fetch( &mut self, joining_request_id: VarInt, joining_start: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>
Send a Relative Joining Fetch (Fetch Type 0x2). Allocates a request ID.
joining_start is an offset rather than a group number: draft-16
Section 9.16.2.1 has the publisher set the Start Location to
“{Subscribe Largest Location.Group - Joining Start, 0}”. To name the starting group
outright, use absolute_joining_fetch.
parameters are the request’s own, as they are on every other request
this endpoint makes.
Sourcepub fn absolute_joining_fetch(
&mut self,
joining_request_id: VarInt,
joining_start: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<(VarInt, ControlMessage), EndpointError>
pub fn absolute_joining_fetch( &mut self, joining_request_id: VarInt, joining_start: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>
Send an Absolute Joining Fetch (Fetch Type 0x3). Allocates a request ID.
Draft-16 Section 9.16.2.1: “For an Absolute Joining Fetch, the
publisher sets the Start Location to {Joining Start, 0}.” So
joining_start is the group to begin at rather than a count back from
one, and Section 9.16.2 leaves the choice with the subscriber: “The
subscriber can set the Start Location to an absolute Location or a
Location relative to the current group.” Only the relative form could be sent
before, so an application that knew which group it wanted had to
express it as an offset from a largest group it may never have been
told.
Sourcefn joining_fetch_of_type(
&mut self,
fetch_type: FetchType,
joining_request_id: VarInt,
joining_start: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<(VarInt, ControlMessage), EndpointError>
fn joining_fetch_of_type( &mut self, fetch_type: FetchType, joining_request_id: VarInt, joining_start: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>
The two above, which differ in the Fetch Type and in what the publisher then reads Joining Start as. Nothing else about the message changes.
Sourcepub fn receive_fetch_ok(&mut self, msg: &FetchOk) -> Result<(), EndpointError>
pub fn receive_fetch_ok(&mut self, msg: &FetchOk) -> Result<(), EndpointError>
Process an incoming FETCH_OK. Draft-16: has request_id, end_of_track, end_group, end_object, parameters.
Sourcepub fn fetch_cancel(
&mut self,
request_id: VarInt,
) -> Result<ControlMessage, EndpointError>
pub fn fetch_cancel( &mut self, request_id: VarInt, ) -> Result<ControlMessage, EndpointError>
Send a FETCH_CANCEL message.
Sourcepub fn on_fetch_stream_fin(
&mut self,
request_id: VarInt,
) -> Result<(), EndpointError>
pub fn on_fetch_stream_fin( &mut self, request_id: VarInt, ) -> Result<(), EndpointError>
Notify that a fetch data stream received FIN.
It may arrive before the FETCH_OK or REQUEST_ERROR answering the
request, which leaves the fetch in FetchState::Unanswered until the
answer lands.
Sourcepub fn on_fetch_stream_reset(
&mut self,
request_id: VarInt,
) -> Result<(), EndpointError>
pub fn on_fetch_stream_reset( &mut self, request_id: VarInt, ) -> Result<(), EndpointError>
Notify that a fetch data stream was reset.
As with a FIN, it may arrive before the answer to the request.
Sourcepub fn receive_fetch(&mut self, msg: &Fetch) -> Result<(), EndpointError>
pub fn receive_fetch(&mut self, msg: &Fetch) -> Result<(), EndpointError>
Process an incoming FETCH, recording the fetch it opens.
The Request ID has already been checked by Self::receive_request,
which every request message passes through before its own handler.
A Joining Fetch is recorded like any other. Section 9.16.2 answers one naming a subscription this session cannot join with a refusal and not a session close, and a refusal is a message this endpoint has to build, so the request it refuses has to be on record first.
§Errors
The session error when the session is not established, and the fetch
flow’s own InvalidTransition for a second FETCH under an identifier
already carrying one.
Sourcepub fn pending_fetch(&self, request_id: VarInt) -> Option<&Fetch>
pub fn pending_fetch(&self, request_id: VarInt) -> Option<&Fetch>
The FETCH the peer sent under request_id and this endpoint has not
answered yet.
None once it has been answered, and for an identifier this session
has no inbound fetch for. The record itself lives on past the answer,
because the fetch is not over until its data stream is.
Sourcepub fn pending_fetch_count(&self) -> usize
pub fn pending_fetch_count(&self) -> usize
How many FETCHes the peer has sent that are still waiting for an answer.
Sourcefn joining_subscription_missing(&self, msg: &Fetch) -> Option<u64>
fn joining_subscription_missing(&self, msg: &Fetch) -> Option<u64>
The identifier an arriving Joining Fetch names, when this session has no subscription it may join.
Section 9.16.2: “If a publisher receives a Joining Fetch with a Request ID that does not correspond to a subscription in the same session in the Established or Pending (subscriber) states, it MUST return a REQUEST_ERROR with error code INVALID_JOINING_REQUEST_ID.”
The verdict is taken as the FETCH arrives, because that is the moment the sentence names, and it is kept. A subscription that ends between the FETCH and its answer does not turn a fetch that could be joined into one that could not.
A standalone fetch names none and answers None, and so does a joining
one whose subscription is live. The subscription is one the peer opened,
because the peer is the end that fetches and this endpoint is the one
answering.
A subscription established by a PUBLISH counts: Section 5.1 says the
Largest Location saved “in PUBLISH or SUBSCRIBE_OK when establishing a
subscription” is the one a Joining FETCH uses. Draft-15 Section 9.16.2
and the drafts before it say “an existing Subscribe” and reach only what
a SUBSCRIBE opened.
Sourcepub fn send_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<ControlMessage, EndpointError>
pub fn send_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<ControlMessage, EndpointError>
Build the FETCH_OK accepting a fetch the peer opened.
§Errors
EndpointError::UnknownRequest if the peer opened no fetch under
that identifier, EndpointError::UnjoinableSubscription for a
Joining Fetch naming a subscription this session cannot join, and the
fetch flow’s own InvalidTransition for a second answer: Section 5.2
says the publisher “MUST send exactly one FETCH_OK or REQUEST_ERROR in
response to a FETCH”.
Sourcepub fn receive_fetch_cancel(
&mut self,
msg: &FetchCancel,
) -> Result<(), EndpointError>
pub fn receive_fetch_cancel( &mut self, msg: &FetchCancel, ) -> Result<(), EndpointError>
Process an incoming FETCH_CANCEL, ending the fetch the peer opened.
Section 9.18: the subscriber sends it to stop a fetch it no longer wants, so the record this endpoint serves the fetch from is the one it ends.
§Errors
EndpointError::UnknownRequest if the peer opened no fetch under
that identifier, and the fetch flow’s own InvalidTransition for a fetch
that has already ended.
Sourcepub fn on_peer_fetch_stream_fin(
&mut self,
request_id: VarInt,
) -> Result<(), EndpointError>
pub fn on_peer_fetch_stream_fin( &mut self, request_id: VarInt, ) -> Result<(), EndpointError>
Note that this endpoint finished the data stream serving a fetch the peer opened.
A fetch is over when its answer and its data stream have both settled, and this is the second of those for the end that serves it.
§Errors
EndpointError::UnknownRequest if the peer opened no fetch under
that identifier, and the fetch flow’s own InvalidTransition from a state
the stream cannot close from.
Sourcepub fn subscribe_namespace(
&mut self,
namespace_prefix: TrackNamespace,
subscribe_options: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<(VarInt, ControlMessage), EndpointError>
pub fn subscribe_namespace( &mut self, namespace_prefix: TrackNamespace, subscribe_options: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>
Send a SUBSCRIBE_NAMESPACE message. Draft-16 adds subscribe_options
(0=PUBLISH, 1=NAMESPACE, 2=both).
Section 9.25 addresses the first half of the overlap rule to this end of the session: “A subscriber cannot make overlapping namespace subscriptions on a single session.” So a prefix overlapping one this endpoint has already asked about is refused here rather than built and sent for the peer to refuse.
§Errors
The session error when the session is not established,
EndpointError::OwnPrefixOverlap when the prefix overlaps one this
endpoint is already subscribed to, and the Request ID allocator’s own
error when the peer has granted no room for another request.
Sourcepub fn receive_subscribe_namespace_on_stream(
&mut self,
msg: &ControlMessage,
) -> Result<VarInt, EndpointError>
pub fn receive_subscribe_namespace_on_stream( &mut self, msg: &ControlMessage, ) -> Result<VarInt, EndpointError>
Register a SUBSCRIBE_NAMESPACE the peer opened a bidirectional stream with, and hand back the Request ID it carries.
The mirror of subscribe_namespace: that
one builds the request this endpoint writes on a stream it opened, this
one takes the request that arrived on a stream the peer opened.
§Two refusals, two codes
Section 3.3, on a bidirectional stream that begins with anything else:
“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 states a different rule for the
Request ID the stream’s first message carries: “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.”
validate_peer_request_id is what
holds it to that, and it answers with its own errors rather than this
one, so a peer reading close codes can tell the two rules apart.
§Why the state is shared and the request is not
A Request ID the peer allocated and one this endpoint allocated have
opposite least significant bits, so the two can never collide.
validate_peer_request_id is what
enforces that, and it runs before the insert, so one map of state
machines holds both directions safely. The request itself is another
matter: the prefix it names is not in a state machine, and it is what
an answer is about and what a later request is judged against, so it
is kept beside the state and read back through
pending_subscribe_namespace.
Sourcefn peer_prefix_overlap(&self, prefix: &TrackNamespace) -> Option<u64>
fn peer_prefix_overlap(&self, prefix: &TrackNamespace) -> Option<u64>
The earliest namespace subscription the peer has made whose prefix
overlaps prefix, and None when there is none.
Only ones that have not ended count: the sentence weighs the arriving prefix against “an established namespace subscription”, so one the peer has withdrawn and one this endpoint refused are both past. Drafts 07 through 11 say “an earlier” instead and count those too.
One that has arrived and has not been answered does count. It is not established yet, but this endpoint is the one about to establish it, and accepting both would leave the session holding exactly the pair the sentence exists to prevent.
Namespace subscriptions this endpoint made are a separate set and are not consulted. This endpoint is the subscriber for those, so a prefix it asked about says nothing about what the peer may ask about.
The lowest Request ID wins when more than one overlaps, so the answer does not depend on the order a map happens to iterate in. Identifiers are handed out in increasing order, so the lowest of them is the earliest request.
Sourcefn own_prefix_overlap(&self, prefix: &TrackNamespace) -> Option<u64>
fn own_prefix_overlap(&self, prefix: &TrackNamespace) -> Option<u64>
The earliest namespace subscription this endpoint has made whose
prefix overlaps prefix, and None when there is none.
The subscriber’s half of the same sentence reads over this endpoint’s
own requests, and takes the same view of which of them are past as
Self::peer_prefix_overlap takes of the peer’s.
Sourcepub fn pending_subscribe_namespace(
&self,
request_id: VarInt,
) -> Option<&SubscribeNamespace>
pub fn pending_subscribe_namespace( &self, request_id: VarInt, ) -> Option<&SubscribeNamespace>
The SUBSCRIBE_NAMESPACE the peer sent under request_id and this
endpoint has not answered yet.
None once it has been answered, and for an identifier the peer has
opened no namespace subscription under – one of this endpoint’s own
included, whose state is in the same map but whose message never
arrived here. The record itself lives on past the answer, because a
namespace subscription that was accepted lasts as long as its stream
does.
Sourcepub fn pending_subscribe_namespace_count(&self) -> usize
pub fn pending_subscribe_namespace_count(&self) -> usize
How many namespace subscriptions the peer has opened that are still waiting for an answer.
Sourcepub fn refuse_non_subscribe_namespace(
&mut self,
ty: MessageType,
) -> EndpointError
pub fn refuse_non_subscribe_namespace( &mut self, ty: MessageType, ) -> EndpointError
Close the session over a bidirectional stream that began with the wrong message type, and return the error that says so.
Separate from
receive_subscribe_namespace_on_stream
because the connection classifies the first message before it has a
request to register, and the close has to happen either way.
Sourcepub fn receive_on_namespace_stream(
&mut self,
request_id: VarInt,
msg: &ControlMessage,
) -> Result<(), EndpointError>
pub fn receive_on_namespace_stream( &mut self, request_id: VarInt, msg: &ControlMessage, ) -> Result<(), EndpointError>
Dispatch a message that arrived on the response half of the namespace
subscription request_id opened.
Four messages travel here and no others. REQUEST_OK and REQUEST_ERROR are the answer — Section 9.25: “The publisher will respond with REQUEST_OK or REQUEST_ERROR on the response half of the stream.” Then NAMESPACE reports a matching namespace and NAMESPACE_DONE withdraws one, both on the same stream and only “if they are requested”.
§Why the stream decides which request, and not the message
NAMESPACE and NAMESPACE_DONE carry no Request ID. What they carry is a Track Namespace Suffix, relative to a prefix that only the subscription knows, so the stream is the only thing that says which subscription they belong to.
REQUEST_OK and REQUEST_ERROR do carry one, which names the request
twice over. When the two agree the id is redundant; when they disagree
nothing here can tell which the sender meant, and the draft does not
say, so neither is acted on and
EndpointError::ResponseIdMismatch comes back. That is a refusal of
one message and not a session close, because no sentence asks for one.
Sourcefn require_same_request(
&self,
stream: u64,
message: VarInt,
) -> Result<(), EndpointError>
fn require_same_request( &self, stream: u64, message: VarInt, ) -> Result<(), EndpointError>
Refuse a response whose own Request ID is not the one its stream carries.
Sourcepub fn respond_on_namespace_stream(
&mut self,
request_id: VarInt,
refusal_code: Option<VarInt>,
) -> Result<(), EndpointError>
pub fn respond_on_namespace_stream( &mut self, request_id: VarInt, refusal_code: Option<VarInt>, ) -> Result<(), EndpointError>
Drive the state machine for the answer this endpoint is about to write on a namespace subscription stream the peer opened.
The mirror of
receive_on_namespace_stream, and
the reason the transitions it drives are named *_sent: the state
edges coincide with the received side, so a mis-dispatch would succeed
silently instead of naming the wrong event in an InvalidTransition.
The caller writes the message only after this returns Ok. What it
cannot undo is the opposite order: a write that fails afterwards leaves
the state machine one step ahead of the wire.
refusal_code is None for a REQUEST_OK and the code the REQUEST_ERROR
will carry otherwise, because Section 9.25 names the code a refusal for
an overlapping prefix must go out under and a boolean cannot carry it.
§Errors
EndpointError::UnknownRequest if no namespace subscription carries
that identifier, EndpointError::PeerPrefixOverlap if the request
overlaps another and this is an acceptance,
EndpointError::WrongOverlapRefusal if it overlaps another and the
refusal names a different code, and the namespace flow’s own
InvalidTransition for one already answered.
Sourcepub fn cancel_namespace_subscription(
&mut self,
request_id: VarInt,
) -> Result<(), EndpointError>
pub fn cancel_namespace_subscription( &mut self, request_id: VarInt, ) -> Result<(), EndpointError>
Record that the namespace subscription request_id was withdrawn by
closing its stream.
Section 6.1: “A SUBSCRIBE_NAMESPACE can be cancelled by closing the stream with either a FIN or RESET_STREAM.” Draft-16 removed UNSUBSCRIBE_NAMESPACE, so this is the whole of how a namespace subscription ends early, in either direction.
Idempotent: a subscription that has already ended accepts it and stays where it is.
§Errors
EndpointError::UnknownRequest if no namespace subscription carries
this id, and EndpointError::Namespace if one does but nothing has
been written on its stream.
Sourcepub fn publish_namespace(
&mut self,
track_namespace: TrackNamespace,
parameters: Vec<KeyValuePair>,
) -> Result<(VarInt, ControlMessage), EndpointError>
pub fn publish_namespace( &mut self, track_namespace: TrackNamespace, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>
Send a PUBLISH_NAMESPACE message.
Sourcepub fn receive_publish_namespace(
&mut self,
msg: &PublishNamespace,
) -> Result<(), EndpointError>
pub fn receive_publish_namespace( &mut self, msg: &PublishNamespace, ) -> Result<(), EndpointError>
Process an incoming PUBLISH_NAMESPACE, recording the announcement it makes.
Section 9.20: “The publisher sends the PUBLISH_NAMESPACE control message to advertise that it has tracks available within a Track Namespace. The receiver verifies the publisher is authorized to publish tracks under this namespace.”
Verifying is the application’s to do, and it needs both the message to
verify and somewhere to answer from. The Request ID has already been
checked by Self::receive_request, which every request message passes
through before its own handler, so a repeat of one the peer has already
spent never reaches here.
§Errors
The session error when the session is not established.
Sourcepub fn pending_publish_namespace(
&self,
request_id: VarInt,
) -> Option<&PublishNamespace>
pub fn pending_publish_namespace( &self, request_id: VarInt, ) -> Option<&PublishNamespace>
The PUBLISH_NAMESPACE the peer sent under request_id and this endpoint
has not answered yet.
None once it has been answered, and for an identifier the peer has
announced nothing under. The record itself lives on past the answer,
because an announcement that was accepted is not over until it is
withdrawn or cancelled.
Sourcepub fn pending_publish_namespace_count(&self) -> usize
pub fn pending_publish_namespace_count(&self) -> usize
How many announcements the peer has made that are still waiting for an answer.
Sourcepub fn send_request_ok(
&mut self,
request_id: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<ControlMessage, EndpointError>
pub fn send_request_ok( &mut self, request_id: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<ControlMessage, EndpointError>
Build the REQUEST_OK accepting an announcement the peer made or a track status it asked for.
One message accepts more than one kind of request, so the record it moves is found by the identifier: the maps are keyed by Request ID and one id belongs to one request.
Section 6.2: “A subscriber MUST send exactly one REQUEST_OK or REQUEST_ERROR in response to a PUBLISH_NAMESPACE. The publisher SHOULD close the session with a protocol error if it receives more than one.” Section 9.19 gives a track status the same answer - “If successful, the publisher responds with a REQUEST_OK message with the same parameters it would have set in a SUBSCRIBE_OK” - and adds that “Track Alias is not used”, which is why accepting one binds nothing.
One answer and no second one: the flow moves on the first, and a second call finds a record that has left Pending.
§Errors
EndpointError::UnknownRequest if the peer has neither announced nor
asked about anything under that identifier, and the answering flow’s own
InvalidTransition for a request already answered.
Sourcepub fn receive_publish_namespace_done(
&mut self,
msg: &PublishNamespaceDone,
) -> Result<(), EndpointError>
pub fn receive_publish_namespace_done( &mut self, msg: &PublishNamespaceDone, ) -> Result<(), EndpointError>
Process an incoming PUBLISH_NAMESPACE_DONE, ending the announcement the peer made.
Section 9.22: “The publisher sends the PUBLISH_NAMESPACE_DONE control message to indicate its intent to stop serving new subscriptions for tracks within the provided Track Namespace.”
The announcement it ends is the peer’s, so the record it reads is the
one this endpoint keeps of what the peer announced. An announcement this
endpoint made is withdrawn by Self::publish_namespace_done, which is
the same message travelling the other way.
§Errors
EndpointError::UnknownRequest if the peer announced nothing under
that identifier, and the namespace flow’s own InvalidTransition for
one this endpoint never accepted.
Sourcepub fn publish_namespace_cancel(
&mut self,
request_id: VarInt,
error_code: VarInt,
reason_phrase: Vec<u8>,
) -> Result<ControlMessage, EndpointError>
pub fn publish_namespace_cancel( &mut self, request_id: VarInt, error_code: VarInt, reason_phrase: Vec<u8>, ) -> Result<ControlMessage, EndpointError>
Build the PUBLISH_NAMESPACE_CANCEL revoking an acceptance.
Section 8.5 names what a cancellation revokes: a namespace “it previously responded REQUEST_OK to”. Section 9.24 says what it does: the subscriber “will stop sending new subscriptions for tracks within the provided Track Namespace”.
Previously responded REQUEST_OK to is a state, and it is Active: an announcement reaches it by being accepted and no other way. One still waiting for an answer, one refused and one already ended are all refused here rather than sent.
The announcement is the peer’s. An announcement this endpoint made is
not cancelled by its own publisher; the peer cancels it, and that
arrives at Self::receive_publish_namespace_cancel.
§Errors
EndpointError::UnknownRequest if the peer announced nothing under
that identifier, and the namespace flow’s own InvalidTransition for
one this endpoint never accepted.
Sourcepub fn publish_namespace_done(
&mut self,
request_id: VarInt,
) -> Result<ControlMessage, EndpointError>
pub fn publish_namespace_done( &mut self, request_id: VarInt, ) -> Result<ControlMessage, EndpointError>
Send the PUBLISH_NAMESPACE_DONE withdrawing an announcement this endpoint made.
Section 9.22: “The publisher sends the PUBLISH_NAMESPACE_DONE control
message to indicate its intent to stop serving new subscriptions for
tracks within the provided Track Namespace.” This endpoint is that
publisher, so the record it ends is one Self::publish_namespace
opened.
§Errors
EndpointError::UnknownRequest if this endpoint has announced nothing
under that identifier, and the namespace flow’s own InvalidTransition
for an announcement the peer has not accepted, or has already cancelled.
Sourcepub fn receive_publish_namespace_cancel(
&mut self,
msg: &PublishNamespaceCancel,
) -> Result<(), EndpointError>
pub fn receive_publish_namespace_cancel( &mut self, msg: &PublishNamespaceCancel, ) -> Result<(), EndpointError>
Process an incoming PUBLISH_NAMESPACE_CANCEL, ending an announcement this endpoint made.
Section 8.5 names what the peer is revoking: a namespace “it previously responded REQUEST_OK to”. What it responded to is an announcement this endpoint made, so the record this reads is the outbound one.
§Errors
EndpointError::UnknownRequest if this endpoint has announced nothing
under that identifier, and the namespace flow’s own InvalidTransition
for an announcement the peer never accepted.
Sourcepub fn track_status(
&mut self,
track_namespace: TrackNamespace,
track_name: Vec<u8>,
parameters: Vec<KeyValuePair>,
) -> Result<(VarInt, ControlMessage), EndpointError>
pub fn track_status( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>
Send a TRACK_STATUS message. Allocates a request ID. Draft-16: simplified with parameters.
Sourcepub fn receive_track_status(
&mut self,
msg: &TrackStatus,
) -> Result<(), EndpointError>
pub fn receive_track_status( &mut self, msg: &TrackStatus, ) -> Result<(), EndpointError>
Process an incoming TRACK_STATUS, recording what the peer asked about.
Section 9.19: the receiver of one “treats it identically as if it had received a SUBSCRIBE message, except it does not create downstream subscription state or send any Objects”. The exception is why this does not reach for the subscriptions the peer has opened: nothing that names a subscription is to find this request, and the surest way to hold to that is for it never to be one.
The same section says the answer carries no alias at all here - “Track Alias is not used” - so unlike a subscription the peer opens, accepting this one binds nothing.
§Errors
The session error when the session is not established.
Sourcepub fn pending_track_status(&self, request_id: VarInt) -> Option<&TrackStatus>
pub fn pending_track_status(&self, request_id: VarInt) -> Option<&TrackStatus>
The TRACK_STATUS the peer sent under request_id and this endpoint has
not answered yet.
None once it has been answered, and for an identifier this session has
carried no track status under.
Sourcepub fn pending_track_status_count(&self) -> usize
pub fn pending_track_status_count(&self) -> usize
How many track statuses the peer has asked about that are still waiting for an answer.
Sourcepub fn publish(
&mut self,
track_namespace: TrackNamespace,
track_name: Vec<u8>,
track_alias: VarInt,
track_extensions: Vec<KeyValuePair>,
parameters: Vec<KeyValuePair>,
) -> Result<(VarInt, ControlMessage), EndpointError>
pub fn publish( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, track_alias: VarInt, track_extensions: Vec<KeyValuePair>, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>
Send a PUBLISH message (publisher side). Allocates a request ID. Draft-16: takes track_alias, no forward.
Sourcepub fn receive_publish_ok(
&mut self,
msg: &PublishOk,
) -> Result<(), EndpointError>
pub fn receive_publish_ok( &mut self, msg: &PublishOk, ) -> Result<(), EndpointError>
Process an incoming PUBLISH_OK (publisher side). Draft-16: has request_id, parameters.
Sourcepub fn send_publish_done(
&mut self,
request_id: VarInt,
status_code: VarInt,
stream_count: VarInt,
reason_phrase: Vec<u8>,
) -> Result<ControlMessage, EndpointError>
pub fn send_publish_done( &mut self, request_id: VarInt, status_code: VarInt, stream_count: VarInt, reason_phrase: Vec<u8>, ) -> Result<ControlMessage, EndpointError>
Send a PUBLISH_DONE message (publisher finishing). Draft-16: has stream_count.
Sourcepub fn receive_request_ok(
&mut self,
msg: &RequestOk,
) -> Result<(), EndpointError>
pub fn receive_request_ok( &mut self, msg: &RequestOk, ) -> Result<(), EndpointError>
Process an incoming REQUEST_OK (consolidated ok for namespace/ track-status flows).
Sourcepub fn receive_request_error(
&mut self,
msg: &RequestError,
) -> Result<(), EndpointError>
pub fn receive_request_error( &mut self, msg: &RequestError, ) -> Result<(), EndpointError>
Process an incoming REQUEST_ERROR (consolidated error).
Sourcepub fn receive_publish(&mut self, msg: &Publish) -> Result<(), EndpointError>
pub fn receive_publish(&mut self, msg: &Publish) -> Result<(), EndpointError>
Process an incoming PUBLISH message, which opens a subscription this endpoint is the subscriber of.
Section 5.1: “A publisher initiates a subscription to a track by sending the PUBLISH message. The subscriber either accepts or rejects the subscription using PUBLISH_OK or REQUEST_ERROR.” The record made here outlives that answer, because everything the section gives the subscription afterwards names this same request: an UNSUBSCRIBE from this endpoint, a PUBLISH_DONE from the peer, and the Track Alias the offer spends for as long as it lasts.
§Errors
EndpointError::DuplicateTrackAlias when the Track Alias offered is
one a different live track already holds, which ends the session, and
the publish flow’s own InvalidTransition for a second PUBLISH under a
request id already carrying one.
Sourcepub fn pending_publish(&self, request_id: VarInt) -> Option<&Publish>
pub fn pending_publish(&self, request_id: VarInt) -> Option<&Publish>
The PUBLISH the peer sent under request_id and this endpoint has not
answered yet.
None once it has been answered, and for an identifier this session
has carried no offer from the peer under – an offer of this
endpoint’s own included, whose state is in the same map but whose
message was never received. The record itself lives on past the
answer, because the subscription the offer opened is not over until an
UNSUBSCRIBE or a PUBLISH_DONE ends it.
Sourcepub fn pending_publish_count(&self) -> usize
pub fn pending_publish_count(&self) -> usize
How many offers the peer has made that are still waiting for an answer.
Sourcepub fn send_publish_ok(
&mut self,
request_id: VarInt,
parameters: Vec<KeyValuePair>,
) -> Result<ControlMessage, EndpointError>
pub fn send_publish_ok( &mut self, request_id: VarInt, parameters: Vec<KeyValuePair>, ) -> Result<ControlMessage, EndpointError>
Generate a PUBLISH_OK accepting a PUBLISH the peer sent, which establishes the subscription it opened.
§Errors
EndpointError::UnknownRequest when no PUBLISH arrived under that
id, and the publish flow’s own InvalidTransition for a second answer:
Section 5.1 says “A subscriber MUST send exactly one PUBLISH_OK or
REQUEST_ERROR in response to a PUBLISH.”
Sourcepub fn send_publish_error(
&mut self,
request_id: VarInt,
error_code: VarInt,
retry_interval: VarInt,
reason_phrase: Vec<u8>,
) -> Result<ControlMessage, EndpointError>
pub fn send_publish_error( &mut self, request_id: VarInt, error_code: VarInt, retry_interval: VarInt, reason_phrase: Vec<u8>, ) -> Result<ControlMessage, EndpointError>
Generate the REQUEST_ERROR that rejects a PUBLISH the peer sent, which ends the subscription it opened before it was established.
This draft has no PUBLISH_ERROR: Section 5.1 answers a rejected PUBLISH with the one REQUEST_ERROR every request kind shares.
§Errors
EndpointError::UnknownRequest when no PUBLISH arrived under that
id, and the publish flow’s own InvalidTransition for a second answer:
Section 5.1 says “A subscriber MUST send exactly one PUBLISH_OK or
REQUEST_ERROR in response to a PUBLISH.”
Sourcepub fn receive_request(
&mut self,
msg: &ControlMessage,
) -> Result<(), EndpointError>
pub fn receive_request( &mut self, msg: &ControlMessage, ) -> Result<(), EndpointError>
Hold a Request ID a peer allocated to the rules Section 9.1 states about it.
“The client’s Request ID starts at 0 and are even and the server’s Request ID starts at 1 and are odd. The Request ID increments by 2 … If an endpoint receives a Request ID that is not valid for the peer, it MUST close the session with Invalid Request ID.” Parity says which end may have chosen it; the ceiling this endpoint advertised says how far the peer may go.
This is the call site Self::validate_peer_request_id did not have.
The rule was implemented and then applied to nothing, so a peer could
open requests with ids from this endpoint’s own half of the space, or
past the ceiling it had advertised, and neither was noticed.
A message that is a response rather than a request carries the id of a request this endpoint made, so it is not checked here - it is checked by finding the state machine it names.
§The list below is the rule, not a convenience
Every message named here spends one of the peer’s Request IDs, and nothing else does. That makes the list load-bearing in a way it was not before the sequence was tracked: a request left out of it spends an ID this endpoint never counts, so the peer’s next request looks like a skip and a conforming session is closed over it. Section 9.1 names the set, and REQUEST_UPDATE is in it - it carries a Request ID of its own, alongside the separate field naming the request it modifies.
§Errors
The request-id errors, for a wrong parity, an id at or above the advertised ceiling, or one that is not the next in the peer’s sequence.
Sourcepub fn receive_message(
&mut self,
msg: ControlMessage,
) -> Result<(), EndpointError>
pub fn receive_message( &mut self, msg: ControlMessage, ) -> Result<(), EndpointError>
Dispatch an incoming control message to the appropriate handler.