Skip to main content

Endpoint

Struct Endpoint 

Source
pub struct Endpoint {
Show 30 fields role: Role, session: SessionStateMachine, request_ids: RequestIdAllocator, subscriptions: HashMap<u64, SubscriptionStateMachine>, fetches: HashMap<u64, FetchStateMachine>, fills: HashMap<u64, FillStreams>, subscribe_namespaces: HashMap<u64, SubscribeNamespaceStateMachine>, subscribe_tracks: HashMap<u64, SubscribeNamespaceStateMachine>, publish_namespaces: HashMap<u64, PublishNamespaceStateMachine>, track_statuses: HashMap<u64, TrackStatusStateMachine>, publishes: HashMap<u64, PublishStateMachine>, goaway_uri: Option<Vec<u8>>, goaway_request_streams: HashSet<u64>, peer_request_ids: HashSet<u64>, inbound_requests: HashMap<u64, ControlMessage>, overlapping_namespace_subscriptions: HashMap<u64, u64>, updated_namespace_prefixes: HashMap<u64, TrackNamespace>, overlapping_prefix_updates: HashMap<u64, u64>, advertised_max_request_updates: u64, outstanding_peer_updates: HashMap<u64, u64>, unanswered_peer_updates: HashMap<u64, u64>, owed_update_failures: HashSet<u64>, answered_peer_requests: HashSet<u64>, advertised_max_filter_ranges: u64, peer_filter_rejections: HashMap<u64, FilterRejection>, peer_request_filters: HashMap<u64, Vec<KeyValuePair>>, track_bindings: HashMap<u64, TrackBinding>, fetch_tracks: HashMap<u64, FetchTrack>, malformed: Mutex<MalformedTracks>, locations: Arc<Mutex<TrackLocations>>,
}

Fields§

§role: Role§session: SessionStateMachine§request_ids: RequestIdAllocator§subscriptions: HashMap<u64, SubscriptionStateMachine>§fetches: HashMap<u64, FetchStateMachine>§fills: HashMap<u64, FillStreams>

Which subscriptions have asked for a fill fetch stream, and how many of their streams are open.

Keyed by the subscription’s Request ID, which is also the Request ID a fill fetch stream’s FETCH_HEADER carries — Section 5.1.3: “the SUBSCRIBE Request ID for the initial fill, or the REQUEST_UPDATE Request ID for a subsequent fill”, and a REQUEST_UPDATE travels on its request’s own stream under that request’s Request ID (Section 10.9). So the two ids the section distinguishes are the same number, and the same section nonetheless says a subscription “can have multiple fill fetch streams open at once, each identified by its Request ID”. The draft is inconsistent here and the identifier cannot separate them, so this counts the open streams rather than pretending to tell them apart, which is what a PUBLISH_DONE’s Stream Count needs anyway.

An entry appears when a SUBSCRIBE or a REQUEST_UPDATE carrying FILL_PARAMETERS is sent or received, and is never removed: Section 10.2.15 keeps FILL_PARAMETERS off the sticky-parameter rules, but a subscription that asked once is a subscription whose later FETCH_HEADERs are attributable, and a Request ID is spent once so an entry can never come to describe a different request.

§subscribe_namespaces: HashMap<u64, SubscribeNamespaceStateMachine>§subscribe_tracks: HashMap<u64, SubscribeNamespaceStateMachine>§publish_namespaces: HashMap<u64, PublishNamespaceStateMachine>§track_statuses: HashMap<u64, TrackStatusStateMachine>§publishes: HashMap<u64, PublishStateMachine>§goaway_uri: Option<Vec<u8>>§goaway_request_streams: HashSet<u64>

The peer’s requests whose stream has already carried a GOAWAY.

Section 10.4 makes the second GOAWAY on one request stream a session close while leaving a first one on every other stream legal, so the count cannot live on the session. Held apart from the per-kind request maps because a GOAWAY says nothing about which kind of request it migrates.

§peer_request_ids: HashSet<u64>

Every Request ID the peer has spent, whether or not the request it opened is still live.

Draft-20 Section 10.1 makes a duplicate Request ID a session close, and “duplicate” is about the id ever having been used, not about the request still being open. Deriving it from the per-kind maps instead would answer wrongly the moment those maps are ever pruned, so the rule is stated once, here, and this set is never pruned.

§inbound_requests: HashMap<u64, ControlMessage>

Every request the peer opened a stream with, as it arrived, keyed by the Request ID it carries.

One map for all seven kinds, because one entry point takes all seven: receive_request_on_stream is the only thing that writes here, so what is in here is a request, and it is the peer’s, by construction. How far each one has got stays in the per-kind map beside this endpoint’s own requests, which is where every later message on the stream reads it and where the parity of a Request ID keeps the two ends apart.

What a map of state machines cannot hold is the request. Section 5.1 has the subscriber “either accepts or rejects the subscription”, and what is being accepted or rejected – the track, the namespace prefix, the parameters – is named in the request and nowhere else once the message has been dropped.

§overlapping_namespace_subscriptions: HashMap<u64, u64>

The request each namespace subscription of the peer’s overlapped when it arrived, keyed by the Request ID of the one that arrived.

The prefix is judged where the sentence says it is judged, on receipt, and the verdict is read again when the answer is written. An entry means this endpoint owes that request a REQUEST_ERROR and may send it nothing else.

§updated_namespace_prefixes: HashMap<u64, TrackNamespace>

The Track Namespace Prefix a REQUEST_UPDATE asks one of the peer’s namespace subscriptions to move to, held until that update is answered.

Section 10.9.2 ties the move to the acceptance: “If the update is accepted, NAMESPACE and NAMESPACE_DONE messages following the REQUEST_OK will contain Track Namespace suffixes relative to the updated prefix.” Until then the subscription still selects what the peer opened it with, so a later request is weighed against the prefix in inbound_requests and not against this one.

§overlapping_prefix_updates: HashMap<u64, u64>

The subscription an unanswered prefix update would collide with, when it would, keyed by the Request ID of the one being moved.

Separate from overlapping_namespace_subscriptions because one Request ID can be carrying both verdicts at once: the request that opened the stream has one and an update on it has another, and they are settled by different messages on that same stream.

§advertised_max_request_updates: u64

The MAX_REQUEST_UPDATES this endpoint put in its own SETUP.

The peer’s value is a different number and belongs to the sending side, which this type has no path for: draft-20 gives the endpoint no REQUEST_UPDATE builder, so there is nothing here to hold back.

Zero means no limit rather than none allowed, which is the opposite of how MAX_REQUEST_ID reads. Section 10.3.1.7 says so outright - “A value of 0 means the endpoint does not limit REQUEST_UPDATE concurrency. If not present, the default value is 0” - so an endpoint that never sends the option is not limiting anything, and a check that read zero as a ceiling would refuse the first update of every session.

§outstanding_peer_updates: HashMap<u64, u64>

Per request stream, how many REQUEST_UPDATEs the peer has sent that this endpoint has not yet answered.

“Outstanding” is Section 10.3.1.7’s word and it is per stream, not per session: “Each REQUEST_OK or REQUEST_ERROR response restores one credit on that stream.”

§unanswered_peer_updates: HashMap<u64, u64>

Per request stream, how many REQUEST_UPDATEs are still waiting for the answer Section 10.9 requires.

A second count rather than a reading of the one above, because the two sentences count different things and disagree on exactly one response. Section 10.3.1.7 says “Each REQUEST_OK or REQUEST_ERROR response restores one credit on that stream” — every response, including the REQUEST_OK that answers a SUBSCRIBE_NAMESPACE rather than an update. That is the sender’s accounting rule as much as the receiver’s, and an endpoint that credited more carefully than the peer does would close a conforming session, so the credit above stays literal.

This one is about which response answers which message, and there the request’s own REQUEST_OK answers the request. Keeping them apart is what lets a response be recognised as an update’s answer without changing what the peer is allowed to send.

§owed_update_failures: HashSet<u64>

The requests whose refused update has not been followed by the PUBLISH_DONE that ends them.

Emptied as each is written. A request is in here for exactly as long as this endpoint owes the peer the second half of a refusal.

§answered_peer_requests: HashSet<u64>

The peer’s requests whose own response has already been written.

Section 10.9 gives an update the same two answers a request has, and on the five kinds REQUEST_OK answers, the message that answers the request and the message that answers an update are the same message. Nothing on the wire tells them apart, so both endpoints resolve it by order: the first response on a stream answers the request that opened it and the ones after it answer updates. This set is that order, recorded.

§advertised_max_filter_ranges: u64

The MAX_FILTER_RANGES this endpoint put in its own SETUP.

Section 10.3.1.6 makes it a ceiling on the peer’s total number of Ranges (Start/End pairs) allowed concurrently in all Range filter parameters for a given subscription or fetch, and fixes what its absence means: “The default value is 0, so if not specified, the peer MUST NOT send any such filter parameters.”

So zero is none allowed, and it is the default. The option in the subsection after this one, MAX_REQUEST_UPDATES, reads its zero the other way — no limit — and the two are otherwise the same shape. Reading either with the other’s rule is a working implementation that is wrong in one direction or the other for every session.

§peer_filter_rejections: HashMap<u64, FilterRejection>

Requests whose Range Filters this endpoint owes the peer a REQUEST_ERROR about.

Keyed by Request ID, because that is what the reply names. Entries are spent by the REQUEST_ERROR that answers them, and a REQUEST_OK offered for one is refused: taking the request in and then accepting it would leave the peer with a subscription whose filters were never agreed.

§peer_request_filters: HashMap<u64, Vec<KeyValuePair>>

The Range Filter parameters currently in force on each of the peer’s requests.

Section 5.1.4 makes the ceiling a property of the request rather than of the message that carried it — “the total number of Ranges allowed in all Range Filter parameters for a given subscription or fetch” — and lets a REQUEST_UPDATE rewrite the set: “In REQUEST_UPDATE, Length of 0 removes the filter; non-zero replaces it entirely. If a filter parameter is omitted from REQUEST_UPDATE, it is unchanged.”

So the count that matters is of what is in force after the update, and the parameters an update leaves alone are part of it. Held as the parameters rather than as decoded filters because replacement is by Parameter Type, which is the key of the pair.

§track_bindings: HashMap<u64, TrackBinding>

What Full Track Name the peer has attached each Track Alias to, per Request ID.

Section 11.1 forbids 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 request’s own state machine, rather than keeping a second copy of it that every path ending a subscription would have to remember to prune.

§fetch_tracks: HashMap<u64, FetchTrack>

The track each fetch this endpoint made is for.

Not in track_bindings, because 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.

Every fetch names its own track on this draft, which is one thing the FETCH rewrite made simpler. Draft-19’s Joining Fetch named none and took the joined subscription’s, so the track had to be resolved through the join as the fetch was made rather than at the withdrawal — a fill outlives the subscription it was asked from, so a lookup through the join came up empty exactly while there was still a fetch to cancel. Draft-20 Section 10.13 deleted the joining fetch; a fill fetch stream belongs to its subscription and is tracked in fills.

§malformed: Mutex<MalformedTracks>

Which tracks this endpoint has given up on, and what for.

Behind a lock because 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 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

Source

pub fn new(role: Role) -> Self

Source

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.

Source

fn conflicting_track_alias( &self, request_id: u64, alias: u64, namespace: &TrackNamespace, name: &[u8], ) -> Option<EndpointError>

The refusal Section 11.1 requires when alias already names a different track that still has an Established subscription, or None when it is free.

§Why the set is read rather than kept

“Established” is a subscription state Section 5.1 defines, and both state machines here already hold it: a subscription reaches it on SUBSCRIBE_OK and a publish on PUBLISH_OK, and each leaves it on the message that ends the flow. Asking them 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 not: a second PUBLISH under a Request ID already bound is refused by the duplicate-Request-ID rule before it reaches here, and comparing a request against its own binding would answer the wrong rule if that one ever moved.

Source

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.

Source

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 by a publisher to refer to two different Tracks simultaneously in the same session.” Once a PUBLISH carrying an alias has been sent, giving that alias to a second track is what that sentence forbids, answered or not.

Source

fn binding_is_established(&self, id: u64, kind: BindingKind) -> bool

Whether the request that owns a binding still has an Established subscription.

Source

fn track_for_alias(&self, alias: u64) -> Option<(&TrackNamespace, &[u8])>

The track a live binding has given alias to.

Read rather than kept: a binding whose request has ended holds nothing, and an alias that is free again may name a different track next.

Source

fn receives_through(&self, id: u64, kind: BindingKind) -> bool

Whether this endpoint receives the track a request names.

The sentence to be answered is a subscriber’s - “cancel any corresponding subscription or fetches for that Track from that publisher” - so a request that makes this endpoint the publisher is not one of them. A SUBSCRIBE in the table is always this endpoint’s own, because a SUBSCRIBE the peer sends makes this endpoint the publisher and leaves no binding. A PUBLISH is in the table either way round, and Request ID parity is what separates them: the offer the peer made is the one this endpoint receives a track through.

Source

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.

Source

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.

Source

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.

§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. The record is keyed on the track, and so is this.

Source

pub fn requests_for_malformed_track( &self, alias: u64, condition: MalformedTrackCondition, ) -> Vec<VarInt>

Note a track as malformed and name every request through which this endpoint is receiving it, so the caller can cancel them.

This is the whole of what this crate can do here, and the reason is structural rather than a shortfall. Section 2.4.2 asks a subscriber that detects a Malformed Track to “cancel any corresponding subscription or fetches for that Track from that publisher”, and on this draft cancelling a request means resetting the request’s own bidirectional stream - Section 3.3.3. Every request lives at the front of a stream of its own, and Connection::recv_on_request_stream hands that stream to the caller as a RequestStream. So the stream this answer operates on is not the endpoint’s to touch, and no amount of state here changes that. What the endpoint can do is say which requests they are.

§Nothing here ends a request

Deliberately, and it is what keeps the answer to one. The flows are read and not moved, so a caller that passes each id to Connection::cancel_request_stream ends the request there - and the binding stops being in use, so a second object past the end finds no track for the alias and names nothing. The request ending is still what closes the loop, exactly as it is on the drafts that answer with a message; what changed is which side ends it. A caller that ignores the list gets named the same requests again, which is honest: the condition really did fire again.

§What is named, and what is not

Requests through which this endpoint receives the track: a SUBSCRIBE this endpoint sent, a PUBLISH the peer sent, and a fetch this endpoint made. A request that makes this endpoint the publisher is not one of them. Empty for an alias no live binding names, and empty for a track whose only requests are ones this endpoint publishes.

Sorted, because a HashMap iterates in no order and two requests for one track is a shape a peer can produce.

Source

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.

Source

pub fn role(&self) -> Role

Source

pub fn peer_role(&self) -> Role

Returns the role of the peer, which is the other one.

Source

fn require_update_failure_status( &mut self, id: u64, status_code: VarInt, ) -> Result<(), EndpointError>

Hold a refused update’s ending to the status the draft names, and retire the obligation once that ending is written.

Both routes to a PUBLISH_DONE come through here. A subscription this endpoint accepted is ended by a response on the peer’s stream; a PUBLISH this endpoint sent is ended on its own, which does not go through the response path at all. The rule is the same either way, so it is stated once and called twice rather than written where each route happened to need it.

Source

pub fn has_unanswered_update(&self, request_id: VarInt) -> bool

Whether the peer has sent a REQUEST_UPDATE on this request that has not been answered yet.

Draft-20 Section 10.9: “A subscriber can also send REQUEST_UPDATE to modify parameters of a subscription established with PUBLISH”, and the receiver of one “MUST respond with exactly one REQUEST_OK or REQUEST_ERROR message indicating if the update was successful”.

Asked by the connection layer, which otherwise writes nothing on a stream this endpoint opened. An update the peer sent on a PUBLISH is the one thing on such a stream that this endpoint has to answer, and this is what tells it apart from a response to its own request.

Source

fn publishes_a_subscription(&self, id: u64) -> bool

Whether the request id names a subscription this endpoint publishes.

Draft-20 Section 10.9.1 gives a refused REQUEST_UPDATE three different consequences and picks between them by what was being updated: “When a REQUEST_UPDATE is unsuccessful, the publisher MUST also terminate the subscription by sending a PUBLISH_DONE with error code UPDATE_FAILED. When a REQUEST_UPDATE fails for a FETCH, the publisher MUST reset the FETCH data stream. When a REQUEST_UPDATE fails for a SUBSCRIBE_NAMESPACE, SUBSCRIBE_TRACKS or PUBLISH_NAMESPACE, the responder MUST close the bidi stream (see Section 3.3.2).”

Only the first of the three is a message, and only the first is owed here. Recording it for the other two is what would hold their streams open past the close the third sentence requires.

Two requests leave this endpoint publishing: a SUBSCRIBE the peer sent, and a PUBLISH this endpoint sent. Either can carry an update from the other side, and either is ended by a PUBLISH_DONE written from here. A peer’s PUBLISH is neither, because the peer is the publisher on it, and it is told apart by having arrived rather than been sent.

Source

pub fn owes_update_failure(&self, request_id: VarInt) -> bool

Whether this request’s refused update still owes the peer the PUBLISH_DONE that ends it.

Asked by the connection layer, which owns the stream that termination has to be written on and therefore has to know not to close it. A REQUEST_ERROR answering the request itself ends the exchange and takes the stream with it; one answering an update does not, and nothing in the message tells the two apart.

Source

pub fn session_state(&self) -> SessionState

Source

pub fn goaway_uri(&self) -> Option<&[u8]>

Source

pub fn active_subscription_count(&self) -> usize

Source

pub fn active_fetch_count(&self) -> usize

Source

pub fn active_subscribe_namespace_count(&self) -> usize

Source

pub fn active_subscribe_tracks_count(&self) -> usize

Source

pub fn active_publish_namespace_count(&self) -> usize

Source

pub fn active_track_status_count(&self) -> usize

Source

pub fn active_publish_count(&self) -> usize

Source

pub fn peer_request_count(&self) -> usize

How many Request IDs the peer has spent on this session.

Nothing here removes an entry, so this only grows. A responder that wants a ceiling on peer-created state has to impose one itself — see the note on receive_request_on_stream.

Source

pub fn connect(&mut self) -> Result<(), EndpointError>

Source

pub fn close(&mut self) -> Result<(), EndpointError>

Source

pub fn send_setup( &mut self, options: Vec<KeyValuePair>, ) -> Result<ControlMessage, EndpointError>

Generate a SETUP message. Both client and server use the same message type; only the role (and the order of send/receive) distinguishes them.

Source

pub fn receive_setup(&mut self, msg: &Setup) -> Result<(), EndpointError>

Process an incoming SETUP message. Transitions the session to Active.

Source

pub fn receive_goaway(&mut self, msg: &GoAway) -> Result<(), EndpointError>

Source

fn fail_session(&mut self, err: EndpointError) -> EndpointError

Record that the session is over because the peer broke a rule the 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. The close on the wire is the connection layer’s job — see EndpointError::session_error_code for the code it should use.

Source

fn require_active_or_err(&self) -> Result<(), EndpointError>

Source

pub fn subscribe( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>

Source

pub fn receive_subscribe_ok( &mut self, request_id: VarInt, msg: &SubscribeOk, ) -> Result<(), EndpointError>

Process an incoming SUBSCRIBE_OK. Draft-20: no request_id on wire; the caller supplies the request_id of the bidi stream on which the response arrived.

Source

fn spend_update_credit(&mut self, id: u64) -> Result<(), EndpointError>

Take one of the REQUEST_UPDATE credits this endpoint advertised for id’s stream.

Section 10.3.1.7 puts the limit on the number outstanding, so the count is a running balance rather than a total: it goes up on each REQUEST_UPDATE received and down on each REQUEST_OK or REQUEST_ERROR this endpoint writes back, and a peer that keeps pace never reaches the ceiling however many updates it sends.

§Errors

EndpointError::TooManyRequestUpdates, which answers Some(TooManyRequestUpdates) - its own close code, not the general PROTOCOL_VIOLATION - and the session is failed before it returns.

Source

fn restore_update_credit(&mut self, id: u64)

Give back the credit a REQUEST_OK or REQUEST_ERROR restores.

Called for every response this endpoint writes, whether or not the stream ever carried an update: a stream with no outstanding updates has nothing to restore and the saturating subtraction says so, which is cheaper than deciding first whether the response is answering an update or the original request.

Source

pub fn receive_request_update( &mut self, request_id: VarInt, msg: &RequestUpdate, ) -> Result<(), EndpointError>

Process a REQUEST_UPDATE that arrived on the bidi request stream identified by request_id.

Draft-20 Section 10.9: “The sender of a request (SUBSCRIBE, PUBLISH, FETCH, PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE, SUBSCRIBE_TRACKS) can later send a REQUEST_UPDATE on the same bidi stream as the request to modify it. A subscriber can also send REQUEST_UPDATE to modify parameters of a subscription established with PUBLISH.” Anything else “MUST close the session with a PROTOCOL_VIOLATION”, which is what EndpointError::UnexpectedRequestUpdate carries — TRACK_STATUS most of all, since Section 10.15 says outright that “the subscriber cannot send REQUEST_UPDATE”.

The message carries a Request ID of its own and the stream carries one too. They name the same request when the peer is conforming; a disagreement means the update was sent on a stream that is not its request’s, which is the same violation, so it is refused rather than silently resolved to one of the two.

Only SUBSCRIBE-established subscriptions have a state-machine event for this. That is not an omission: an update changes a request’s parameters and not its lifecycle, so for the other five kinds the update is a self-transition with nothing to record.

Source

pub fn receive_publish_done( &mut self, request_id: VarInt, _msg: &PublishDone, ) -> Result<(), EndpointError>

Source

pub fn receive_publish_state_notify( &mut self, request_id: VarInt, _msg: &PublishStateNotify, ) -> Result<(), EndpointError>

Process a PUBLISH_STATE_NOTIFY that arrived on the subscription’s bidi request stream, new in draft-20 (Section 10.10).

§Nothing here moves, and that is the message

“This message is informative and no action is required by the recipient.” It reports that the subscription’s state changed for a reason other than a REQUEST_UPDATE the subscriber sent, carries only the parameters whose values changed, and gets no reply: unlike a REQUEST_UPDATE it is “a unilateral notification: the receiver does not respond with REQUEST_OK or REQUEST_ERROR, and the message is not subject to the MAX_REQUEST_UPDATES limit”. So this spends no update credit and leaves nothing owed — a notify routed through receive_request_update instead would consume a credit the peer never spent and leave this endpoint owing an answer that must not be sent.

What the parameters mean is the caller’s: the values changed at the publisher, and the publisher “MUST include the LARGEST_OBJECT parameter” — if known — “so the subscriber can determine the point in the Track at which the change took effect”. A message that omits it is still a well-formed frame, so nothing here refuses one.

§Errors

EndpointError::StateNotifyForNonSubscription when request_id names something other than a subscription, and EndpointError::StateNotifyFromSubscriber when it names a subscription on which this endpoint is the publisher rather than the subscriber. Section 10.10 answers both with a session close, and both are returned after this endpoint’s own session state has moved to Closed.

Source

pub fn fetch( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>

Send a FETCH.

§This is not draft-19’s FETCH

Draft-20 Section 10.13 rebuilt the message. The Fetch Type field, the Standalone Fetch and Joining Fetch structures and the Fetch Type registry are gone; the Track Namespace and the Track Name are inline fields, and the range travels in the LOCATION_FILTER parameter (Section 5.1.2) rather than in the message. So this takes the same three arguments subscribe does, and the four location varints draft-19 took are now something the caller puts in parameters — see fetch_range, which does it.

A FETCH with no LOCATION_FILTER covers {0,0} through Largest Object, inclusive.

The code point did not change. A draft-19 peer fed one of these reads the Number of Track Namespace Fields count as a Fetch Type and mis-parses in silence; there is no in-band version signal, only the ALPN.

parameters are the request’s own, as they are on every other request this endpoint makes.

Source

pub fn fetch_range( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, range: &LocationFilter, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>

Send a FETCH for one range, carried as a LOCATION_FILTER parameter.

fetch with the filter put into parameters at the position ascending Parameter Type order requires. The convenience is worth a method because Section 10.2 makes that order a wire rule and the codec’s encoder refuses a descending pair rather than emitting one.

The range is inclusive at both ends (Sections 5.1.2 and 10.13), and nothing here adds or subtracts one. Draft-19’s End Location was “the last Object, plus 1; or 0 to indicate the entire Group”; a caller that ports that arithmetic forward fetches one object too many, and one that ports the 0-means-whole-group convention fetches a single object where it meant a group. LocationFilter carries the same warning at each constructor.

Source

fn note_fill_requested(&mut self, request_id: u64, parameters: &[KeyValuePair])

Note that the request under request_id asked for a fill fetch stream.

Called for this endpoint’s own requests and for the peer’s alike, from wherever a SUBSCRIBE or a REQUEST_UPDATE carrying FILL_PARAMETERS passes through, so both ends of a session can attribute the stream that follows.

The Group Order the fill will be read in is resolved here rather than when the stream arrives, because this is the last place the request’s parameters are in hand: a FETCH_HEADER carries a Request ID and nothing else. See fill::group_order for the three-step resolution and for why an unstated order is Ascending. A value the resolver refuses cannot come off the wire — the codec holds a received GROUP_ORDER to {1, 2} before this sees it — so the fallback here is the same Ascending default rather than an error this entry point has no way to report.

Source

pub fn fill_requested(&self, request_id: VarInt) -> bool

Whether the request under request_id has asked for a fill fetch stream.

This is what tells an arriving FETCH_HEADER apart from a fetch’s. Section 5.1.3 puts the subscription’s Request ID on a fill fetch stream’s header, so a reader that knew only about fetches would answer a perfectly ordinary fill with “unknown request”.

Source

pub fn open_fill_streams(&self, request_id: VarInt) -> usize

How many of this subscription’s fill fetch streams are open.

Source

pub fn fill_streams_opened(&self, request_id: VarInt) -> u64

How many fill fetch streams this subscription has opened in total, ended or not.

Section 10.12 makes PUBLISH_DONE’s Stream Count the total the publisher opened for the subscription, “including streams that contained no Objects (e.g., an empty Subgroup) and including any fill fetch streams”. Draft-19 counted no fill streams because it had none, so this number is new work for a publisher building that field.

Source

pub fn fill_group_order(&self, request_id: VarInt) -> Option<GroupOrder>

The Group Order this subscription’s fill fetch streams deliver in, or None if it never asked for a fill.

What accept_fill_stream starts the arriving stream’s object reader with. Resolved once, from the request’s own parameters, by fill::group_order — see there for the order of precedence and for why an unstated order is Ascending.

Source

pub fn on_fill_stream_opened( &mut self, request_id: VarInt, ) -> Result<(), EndpointError>

Record a fill fetch stream opening for request_id.

Section 5.1.3: “a subscription can have multiple fill fetch streams open at once”, and “opening a new fill fetch stream does not implicitly cancel any previously opened fill fetch streams”, so this counts rather than replaces.

§Errors

EndpointError::UnrequestedFillStream when the request never carried FILL_PARAMETERS. Not fatal to the session — see the variant.

Source

pub fn on_fill_stream_ended(&mut self, request_id: VarInt)

Record a fill fetch stream ending, whether by FIN or by reset.

One entry point for both endings on purpose. Section 5.1.3.1 gives them different meanings to the application — a FIN says the fill range was delivered, a reset says the fill failed — and the same meaning to the subscription, which is none: “Resetting or cancelling a fill fetch stream, by either endpoint, does not affect the subscription, which continues to deliver objects using subscribe subgroups and datagrams.” Nothing here touches the subscription’s state machine, and that is the point.

A stream this endpoint never saw open is ignored rather than refused: a reset can arrive for a stream whose header was never read.

Source

pub fn receive_fetch_ok( &mut self, request_id: VarInt, _msg: &FetchOk, ) -> Result<(), EndpointError>

Source

pub fn on_fetch_stream_fin( &mut self, request_id: VarInt, ) -> Result<(), EndpointError>

Source

pub fn on_fetch_stream_reset( &mut self, request_id: VarInt, ) -> Result<(), EndpointError>

Source

pub fn subscribe_namespace( &mut self, namespace_prefix: TrackNamespace, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>

Source

pub fn subscribe_tracks( &mut self, namespace_prefix: TrackNamespace, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>

Source

pub fn publish_namespace( &mut self, track_namespace: TrackNamespace, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>

Source

pub fn track_status( &mut self, track_namespace: TrackNamespace, track_name: Vec<u8>, parameters: Vec<KeyValuePair>, ) -> Result<(VarInt, ControlMessage), EndpointError>

Source

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

Source

pub fn send_publish_done( &mut self, request_id: VarInt, status_code: VarInt, stream_count: VarInt, reason_phrase: Vec<u8>, ) -> Result<ControlMessage, EndpointError>

Source

pub fn receive_request_ok( &mut self, request_id: VarInt, msg: &RequestOk, ) -> Result<(), EndpointError>

Process an incoming REQUEST_OK on the bidi stream identified by request_id. Draft-20: PUBLISH_OK is a REQUEST_OK alias, so this handler also resolves outstanding PUBLISH requests.

§Track Properties are refused except on TRACK_STATUS_OK

Draft-20 Section 10.5: Track Properties “are populated in TRACK_STATUS_OK; they are empty in PUBLISH_OK, REQUEST_UPDATE_OK, SUBSCRIBE_NAMESPACE_OK and PUBLISH_NAMESPACE_OK. If an endpoint receives Track Properties in one of these messages it MUST close the session with a PROTOCOL_VIOLATION.” The codec cannot make that check — REQUEST_OK is one wire form and only the request stream says which of the five shapes it is. This is the layer that knows, because finding the request id in one of the maps below is what names the shape.

Source

pub fn receive_request_error( &mut self, request_id: VarInt, msg: &RequestError, ) -> Result<(), EndpointError>

Process an incoming REQUEST_ERROR on the bidi stream identified by request_id.

Source

pub fn cancel_request( &mut self, request_id: VarInt, ) -> Result<(), EndpointError>

Record that a request was cancelled at its stream.

This draft withdraws a request by terminating the bidirectional stream it was made on rather than by sending a message. Section 3.3.3: “Once a request stream has been opened, the request MAY be cancelled by either endpoint. Senders cancel requests if the response is no longer of interest; Receivers cancel requests if they are unable to or choose not to respond.”

Both of those reach here. A request the peer opened and one this endpoint opened share a map and cannot collide, because their Request IDs have opposite least significant bits, so one method records a cancel from whichever side performed it.

The request moves to its end state, which is what makes the record worth keeping: a response arriving afterwards is refused rather than applied to a request that is over. The Request ID is not released — nothing here reuses one — and the per-request bookkeeping keyed by it is left alone, since a cancelled request’s stream carries nothing more.

Nothing is written on the wire. The reset that goes with this is Connection::cancel_request_stream, which calls this first and terminates the stream only if it returns Ok.

§Errors

EndpointError::UnknownRequest when no request of any kind carries that id, and the kind’s own InvalidTransition when the request has not been written yet.

Source

pub fn receive_namespace( &mut self, _msg: &Namespace, ) -> Result<(), EndpointError>

Source

pub fn receive_namespace_done( &mut self, _msg: &NamespaceDone, ) -> Result<(), EndpointError>

Source

pub fn receive_publish_skipped( &mut self, _msg: &PublishSkipped, ) -> Result<(), EndpointError>

Source

pub fn receive_message( &mut self, msg: ControlMessage, ) -> Result<(), EndpointError>

Dispatch a message that arrived on the control stream.

Draft-20 Table 5 gives every message a Stream value, and only two of them name the control stream: SETUP is “Control”, GOAWAY is “Control, Request”. Everything else is “Request”, so this method’s job is to take those two and refuse the messages that identify a request they have no stream to name.

Four are refused for that reason. REQUEST_UPDATE modifies the request its stream carries (Section 10.9). NAMESPACE and NAMESPACE_DONE report namespaces on the SUBSCRIBE_NAMESPACE request stream that asked for them (Sections 10.17 and 10.18), and PUBLISH_SKIPPED names a track that will not be published on the SUBSCRIBE_TRACKS stream that asked for it (Section 10.21). All four route through receive_response_on_stream, which has the request ID they need.

Source

fn require_the_first_response_first( &mut self, id: u64, msg: &ControlMessage, ) -> Result<(), EndpointError>

Hold the response half of a namespace-scoped request to the rule that its first message answers the request.

Sections 10.19 and 10.20 state it once each, for SUBSCRIBE_NAMESPACE and for SUBSCRIBE_TRACKS: “The publisher will respond with REQUEST_OK or REQUEST_ERROR on the response half of the stream. If the subscriber receives any message other than a REQUEST_OK or a REQUEST_ERROR as the first message on the response half of the stream, then it MUST close the session with a PROTOCOL_VIOLATION.” Draft-18’s own change log records it as new work rather than as a clarification, so drafts 17 and earlier are deliberately not held to it: they say nothing about which message comes first, and refusing one there would close a session over traffic those drafts permit.

§Why only these two requests

They are the two whose response half carries more than an answer. SUBSCRIBE_NAMESPACE goes on to carry NAMESPACE and NAMESPACE_DONE and SUBSCRIBE_TRACKS goes on to carry PUBLISH_SKIPPED, and those are exactly the messages that could arrive before the answer and be taken for it. A SUBSCRIBE has SUBSCRIBE_OK as its own first message and no such ambiguity, which is why the rule is written where it is.

§What “first” is read from

The request’s own state machine. Pending means the request went out and nothing has come back, so it is the same question asked of the state rather than of a second counter that could disagree with it. A request this endpoint did not open, or one already answered, is not this rule’s subject and passes through.

§Errors

EndpointError::ResponseBeforeTheFirstResponse, which answers Some(ProtocolViolation), and the session is failed before it returns.

Source

pub fn receive_response_on_stream( &mut self, request_id: VarInt, msg: ControlMessage, ) -> Result<(), EndpointError>

Dispatch a message that arrived on the bidi request stream identified by request_id.

Beyond the five responses this also takes the messages draft-20 Table 5 places on a request stream without their being answers to it.

REQUEST_UPDATE modifies the request the stream carries (Section 10.9). GOAWAY is listed “Control, Request” because “A GOAWAY MAY also be sent on a request stream to initiate migration of that individual request” (Section 10.4); draft-19 removed GOAWAY’s Request ID and left one wire form for both places, and draft-20 keeps it that way.

NAMESPACE (0x8) and NAMESPACE_DONE (0xE) arrive on the SUBSCRIBE_NAMESPACE request stream that asked for the namespaces they report, and PUBLISH_SKIPPED (0xF) on the SUBSCRIBE_TRACKS stream that asked for the track it says will not be published. Table 5 marks all three “Request”, so this is where they land; the control stream refuses them.

PUBLISH_STATE_NOTIFY (0x22) is draft-20’s addition to that set, and the one message here that is neither a response nor a request: Section 10.10 puts it on “a subscription’s bidirectional stream” and answers it with nothing at all.

Source

pub fn receive_goaway_on_request_stream( &mut self, request_id: VarInt, msg: &GoAway, ) -> Result<(), EndpointError>

Process a GOAWAY that arrived on one request stream rather than on the control stream.

Draft-20 Section 10.4: “A GOAWAY MAY also be sent on a request stream to initiate migration of that individual request. Upon receiving a GOAWAY on a request stream, the endpoint SHOULD re-issue that specific request on a session at the specified URI”. The session keeps running — only this request is being moved — so the session state machine is not touched and no draining event follows. The server-side URI rule of the same section still applies.

§Errors

EndpointError::RepeatedGoAwayOnRequestStream if this request’s stream has already carried one. 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.

Source

pub fn refuse_non_request(&mut self, ty: MessageType) -> EndpointError

Refuse a bidirectional stream the peer opened with a message type that does not begin a request, and end the session.

Draft-20 Section 3.3: “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.” The returned error answers Some(ProtocolViolation) from EndpointError::session_error_code, which is what tells the connection layer to put the close on the wire.

Source

pub fn receive_request_on_stream( &mut self, msg: &ControlMessage, ) -> Result<VarInt, EndpointError>

Register a request the peer opened a bidirectional stream with, and hand back the Request ID it carries.

The mirror of receive_response_on_stream: that one is fed what comes back on a stream this endpoint opened, this one is fed the first message on a stream the peer opened.

§What it enforces, and with which code

Draft-20 Section 10.1: “If an endpoint receives a Request ID where the least significant bit is incorrect for the sender, or a duplicate Request ID, it MUST close the session with INVALID_REQUEST_ID.” Both halves are checked here and both are returned as errors that answer Some(InvalidRequestId) from EndpointError::session_error_code. A message that opens no request stream at all is a different rule with a different code — see refuse_non_request.

§Why there are no separate inbound maps

The peer’s ids and this endpoint’s ids have opposite least significant bits, so they cannot collide. A peer’s request goes into the same HashMap its outbound twin would, keyed the same way, and only the transition names differ — on_subscribe_received where the outbound path calls on_subscribe_sent.

§Peer-controlled growth

Every accepted request adds an entry that nothing removes, and the peer chooses how many to open. peer_request_count is what a responder can watch to impose its own ceiling; this method imposes none.

Source

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, for an identifier this session has carried no SUBSCRIBE under, and for one this endpoint spent on a request of its own – whose state is in the same map, but which never arrived here. The record itself lives on past the answer, because the subscription it opened runs on after it, for as long as the request stream does.

Source

pub fn pending_subscribe_count(&self) -> usize

How many SUBSCRIBEs the peer has sent that are still waiting for an answer.

Source

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, for an identifier this session has carried no PUBLISH under, and for one this endpoint spent on a request of its own – whose state is in the same map, but which never arrived here. The record itself lives on past the answer, because the subscription the offer opened runs on after it, for as long as the request stream does.

Source

pub fn pending_publish_count(&self) -> usize

How many offers the peer has sent that are still waiting for an answer.

Source

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, for an identifier this session has carried no FETCH under, and for one this endpoint spent on a request of its own – whose state is in the same map, but which never arrived here. The record itself lives on past the answer, because the fetch is not over until its response stream is.

Source

pub fn pending_fetch_count(&self) -> usize

How many FETCHes the peer has sent that are still waiting for an answer.

Source

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, for an identifier this session has carried no PUBLISH_NAMESPACE under, and for one this endpoint spent on a request of its own – whose state is in the same map, but which never arrived here. The record itself lives on past the answer, because an announcement that was accepted stands until it is withdrawn.

Source

pub fn pending_publish_namespace_count(&self) -> usize

How many announcements the peer has sent that are still waiting for an answer.

Source

fn peer_namespace_overlap( &self, prefix: &TrackNamespace, except: Option<u64>, ) -> 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” one, 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.

The record of what the peer sent is what tells the two directions apart. The state machines live in one map per kind whichever end opened the request, so a prefix this endpoint asked about would be indistinguishable there; only requests that arrived are written into inbound_requests.

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.

except is the subscription a REQUEST_UPDATE is moving, which Section 10.2.20 weighs against “another active subscription of the same type” and therefore not against the prefix it is leaving behind.

Source

fn peer_tracks_overlap( &self, prefix: &TrackNamespace, except: Option<u64>, ) -> Option<u64>

The earliest track 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” one, 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.

The record of what the peer sent is what tells the two directions apart. The state machines live in one map per kind whichever end opened the request, so a prefix this endpoint asked about would be indistinguishable there; only requests that arrived are written into inbound_requests.

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.

except is the subscription a REQUEST_UPDATE is moving, which Section 10.2.20 weighs against “another active subscription of the same type” and therefore not against the prefix it is leaving behind.

Source

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, for an identifier this session has carried no SUBSCRIBE_NAMESPACE under, and for one this endpoint spent on a request of its own – whose state is in the same map, but which never arrived here. The record itself lives on past the answer, because a namespace subscription lasts as long as its stream does.

Source

pub fn pending_subscribe_namespace_count(&self) -> usize

How many namespace subscriptions the peer has sent that are still waiting for an answer.

Source

pub fn pending_subscribe_tracks( &self, request_id: VarInt, ) -> Option<&SubscribeTracks>

The SUBSCRIBE_TRACKS the peer sent under request_id and this endpoint has not answered yet.

None once it has been answered, for an identifier this session has carried no SUBSCRIBE_TRACKS under, and for one this endpoint spent on a request of its own – whose state is in the same map, but which never arrived here. The record itself lives on past the answer, because the subscription it opened lasts as long as its stream does.

Source

pub fn pending_subscribe_tracks_count(&self) -> usize

How many track subscriptions the peer has sent that are still waiting for an answer.

Source

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, for an identifier this session has carried no TRACK_STATUS under, and for one this endpoint spent on a request of its own – whose state is in the same map, but which never arrived here. The record itself lives on past the answer, because an update may still name it once it has been answered.

Source

pub fn pending_track_status_count(&self) -> usize

How many track statuses the peer has sent that are still waiting for an answer.

Source

fn filter_verdict(&self, parameters: &[KeyValuePair]) -> Option<FilterRejection>

Whether the Range Filters in parameters are ones this endpoint may accept.

The three rules that need more than one parameter to see, in the order that reports the most specific fault: whether the filters read at all, then whether any budget was advertised, then whether the request stays inside it, then whether two filters share a key. A request with no Range Filter at all is not measured against the ceiling, so an endpoint that advertised nothing still takes ordinary requests — which is the whole of the traffic today, since draft-20 is the first draft with these parameters.

Source

pub fn filter_rejection(&self, id: VarInt) -> Option<&FilterRejection>

Why request id must be answered with a REQUEST_ERROR, if it must.

The caller builds the message; FilterRejection::request_error_code gives the code and the variant gives the reason phrase. Answering it clears the record.

Source

fn answers_an_update(&self, id: u64, msg: &ControlMessage) -> bool

Whether this response answers a REQUEST_UPDATE rather than the request that opened the stream. Section 10.9 gives an update the same two answers a request has: “The receiver of a REQUEST_UPDATE MUST respond with exactly one REQUEST_OK or REQUEST_ERROR message indicating if the update was successful, unless it is coalescing failed updates to produce just one REQUEST_ERROR for multiple REQUEST_UPDATE messages.” Nothing in either message says which of the two it is answering, so the question is settled twice over.

A SUBSCRIBE is answered with SUBSCRIBE_OK and a FETCH with FETCH_OK, so a REQUEST_OK on one of those streams has no other message it could be answering. That is the half that needs no ordering.

Everywhere else it is ordering: the first REQUEST_OK or REQUEST_ERROR on a stream answers the request that opened it, and the ones after it answer updates. Both endpoints have to resolve it the same way and neither has anything else to resolve it with.

Source

fn answer_an_update( &mut self, id: u64, msg: &ControlMessage, ) -> Result<(), EndpointError>

Record a response as the answer to one or more outstanding updates.

A REQUEST_OK answers exactly one: “The receiver MUST still send a REQUEST_OK for each successful update”. A REQUEST_ERROR may answer every update still waiting, because Section 10.9.1 permits the receiver to coalesce them — “If the coalesced REQUEST_UPDATE results in REQUEST_ERROR, only a single REQUEST_ERROR will be sent and the sender of the REQUEST_UPDATEs will not always be able to determine which caused an error.” Draft-17 has no such paragraph, and its endpoint answers one update per message in both directions.

The credit MAX_REQUEST_UPDATES counts is restored once whatever the answer covered, which is Section 10.3.1.7 read literally: “Each REQUEST_OK or REQUEST_ERROR response restores one credit on that stream.” The peer restores by the same sentence, so an endpoint that gave back one per coalesced update would be crediting a peer that is not.

No state machine moves. An update changes a request’s parameters and not its lifecycle, so a subscription that was Active before its update was answered is Active after it, whichever answer went out.

§Errors

EndpointError::NoUpdateToAnswer, and nothing is written or spent.

EndpointError::PeerPrefixOverlap and EndpointError::WrongOverlapRefusal when the update asked to move a namespace subscription onto a prefix that overlaps another of its kind and the answer is not the REQUEST_ERROR the sentence names. The same two the request itself is refused with, because it is the same rule under the same code; which of the two moments raised it is told apart by which call returned.

Source

pub fn send_response_on_stream( &mut self, request_id: VarInt, msg: &ControlMessage, ) -> Result<(), EndpointError>

Drive the state machine for a message this endpoint is about to write on a request stream the peer opened.

The mirror of receive_response_on_stream, and the reason the transitions are named *_sent rather than reusing the received-side ones: the state edges coincide, so a mis-dispatch would otherwise succeed silently instead of naming the wrong event in an InvalidTransition.

Beyond the four responses this also takes the three messages draft-20 Table 5 places on a request stream that a responder writes after its response: NAMESPACE and NAMESPACE_DONE on a SUBSCRIBE_NAMESPACE stream (Sections 10.17 and 10.18) and PUBLISH_SKIPPED on a SUBSCRIBE_TRACKS stream (Section 10.21). All three require the request to have been accepted first, because each machine only leaves Pending on its REQUEST_OK.

The caller writes msg 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, the same asymmetry the outbound request path already carries. Drive the state machines for a response this endpoint is about to write on a request stream the peer opened.

The caller writes the message only after this returns Ok, so a response the endpoint refuses never reaches the wire.

§Errors

EndpointError::UnknownRequest when no request of the answering kind carries that identifier, EndpointError::NotAResponse when the message is not one, EndpointError::PeerPrefixOverlap and EndpointError::WrongOverlapRefusal for a namespace subscription that overlapped one already open or that a REQUEST_UPDATE asked to move onto an overlapping prefix, EndpointError::FilterMustBeRejected for a request whose Range Filters this draft says to reject, and each flow’s own InvalidTransition for a request already answered.

Source

pub fn receive_on_peer_request_stream( &mut self, request_id: VarInt, msg: ControlMessage, ) -> Result<(), EndpointError>

Dispatch a message that arrived on a request stream the peer opened, after the request that opened it.

Nothing that arrives here is a response: this endpoint is the responder on such a stream, so a SUBSCRIBE_OK or REQUEST_ERROR turning up is the peer answering its own request, and it is refused with EndpointError::UnexpectedOnPeerRequestStream.

Four messages are expected instead.

REQUEST_UPDATE, because draft-20 Section 10.9 puts it on the request’s own stream: “The sender of a request (SUBSCRIBE, PUBLISH, FETCH, PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE, SUBSCRIBE_TRACKS) can later send a REQUEST_UPDATE on the same bidi stream as the request to modify it.” It goes through the same receive_request_update the requester side uses, so the peer’s update is held to the same rule its own would be: the message’s Request ID must name the stream’s request, and a kind the section does not allow to be updated — TRACK_STATUS, by Section 10.15 — closes the session.

GOAWAY, because Section 10.4 lets one arrive on a request stream to migrate that request alone, in either direction.

PUBLISH_DONE, because a peer that sent PUBLISH is the publisher and ends the publication it opened.

PUBLISH_STATE_NOTIFY, new in draft-20 (Section 10.10), for the same reason: a peer that sent PUBLISH is the publisher, and the publisher is the one end that may send it. On a stream the peer opened with a SUBSCRIBE the roles are the other way round, and it is refused.

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

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

Source§

type Error = !

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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