Skip to main content

moqtap_client/draft16/
endpoint.rs

1use std::collections::HashMap;
2use std::sync::{Arc, Mutex, MutexGuard};
3
4use crate::draft16::fetch::{FetchError, FetchState, FetchStateMachine};
5use crate::draft16::namespace::{
6    NamespaceError, PublishNamespaceState, PublishNamespaceStateMachine, SubscribeNamespaceState,
7    SubscribeNamespaceStateMachine,
8};
9use crate::draft16::publish::{
10    PublishError as PublishFlowError, PublishState, PublishStateMachine,
11};
12use crate::draft16::session::request_id::{RequestIdAllocator, RequestIdError, Role};
13use crate::draft16::session::setup::{self, SetupError};
14use crate::draft16::session::state::{SessionError, SessionState, SessionStateMachine};
15use crate::draft16::subscription::{
16    SubscriptionError, SubscriptionState, SubscriptionStateMachine,
17};
18use crate::draft16::track_status::{TrackStatusError, TrackStatusState, TrackStatusStateMachine};
19use crate::malformed_tracks::{MalformedTrackCondition, MalformedTracks};
20use crate::track_locations::{ObjectLocation, ObjectRole, TrackLocations, TrackObjects};
21use moqtap_codec::draft16::error_codes::{RequestErrorCode, SessionErrorCode};
22use moqtap_codec::draft16::message::{
23    self, ClientSetup, ControlMessage, Fetch, FetchCancel, FetchPayload, FetchType, GoAway,
24    MaxRequestId, MessageType, PublishDone, PublishNamespace, PublishNamespaceCancel,
25    PublishNamespaceDone, RequestError, RequestOk, RequestUpdate, RequestsBlocked, ServerSetup,
26    Subscribe, SubscribeNamespace, SubscribeOk, Unsubscribe,
27};
28use moqtap_codec::kvp::{KeyValuePair, KvpValue};
29use moqtap_codec::types::*;
30use moqtap_codec::varint::VarInt;
31
32/// Errors that can occur during endpoint operations.
33#[derive(Debug, thiserror::Error)]
34pub enum EndpointError {
35    /// A GOAWAY carrying a New Session URI arrived at a server.
36    ///
37    /// Section 9.4: "If a server receives a GOAWAY with a non-zero New
38    /// Session URI Length it MUST close the session with a
39    /// PROTOCOL_VIOLATION." Migration is something a server offers a client, never the
40    /// other way round.
41    #[error("GOAWAY carrying a New Session URI received at a server")]
42    GoAwayUriAtServer,
43    /// A session-level state machine error.
44    #[error("session error: {0}")]
45    Session(#[from] SessionError),
46    /// A request ID allocation or validation error.
47    #[error("request ID error: {0}")]
48    RequestId(#[from] RequestIdError),
49    /// This endpoint was asked to advertise a Maximum Request ID that does
50    /// not increase, and refused. Nothing was written.
51    ///
52    /// The send-side mirror of the rule a peer breaks by sending one — Section 9.5:
53    /// "The Maximum Request ID MUST only increase within a session." Its own
54    /// variant, and not the received one, because the two are opposite
55    /// findings that would otherwise arrive as the same value: the received one
56    /// is a peer in violation and this one is a caller of this library asking
57    /// for a message that would put this endpoint in violation, and
58    /// [`EndpointError::session_error_code`] answers `Some` for it either way.
59    ///
60    /// Not fatal. The message is refused instead of built, the ceiling stays
61    /// where it was, and nothing reaches the peer to object to.
62    #[error(
63        "the Maximum Request ID already advertised is {advertised}, so {offered} would not increase it"
64    )]
65    MaxRequestIdWouldNotIncrease {
66        /// The ceiling this endpoint has already advertised.
67        advertised: u64,
68        /// The value it was asked to advertise instead.
69        offered: u64,
70    },
71    /// A subscription state machine error.
72    #[error("subscription error: {0}")]
73    Subscription(#[from] SubscriptionError),
74    /// A fetch state machine error.
75    #[error("fetch error: {0}")]
76    Fetch(#[from] FetchError),
77    /// A namespace state machine error.
78    #[error("namespace error: {0}")]
79    Namespace(#[from] NamespaceError),
80    /// A track status state machine error.
81    #[error("track status error: {0}")]
82    TrackStatus(#[from] TrackStatusError),
83    /// A publish flow state machine error.
84    #[error("publish flow error: {0}")]
85    PublishFlow(#[from] PublishFlowError),
86    /// A setup negotiation error.
87    #[error("setup error: {0}")]
88    Setup(#[from] SetupError),
89    /// The request ID does not match any known state machine.
90    #[error("unknown request ID: {0}")]
91    UnknownRequest(u64),
92    /// A message that only a subscription can carry named a track status.
93    ///
94    /// Section 9.19 treats a TRACK_STATUS as a SUBSCRIBE "except it does not
95    /// create downstream subscription state", and says what follows from that
96    /// in the same breath: "the subscriber cannot send REQUEST_UPDATE or
97    /// UNSUBSCRIBE". Both messages are about a subscription, and this request
98    /// opened none for them to name.
99    ///
100    /// Separate from [`EndpointError::UnknownRequest`], which says the
101    /// identifier names nothing at all. This one says it names something, and
102    /// that what it names is the one request kind neither message applies to.
103    #[error("request {0} is a track status, which cannot be updated or unsubscribed")]
104    NotASubscription(u64),
105    /// A REQUEST_UPDATE carried a parameter that the kind of request it names
106    /// does not admit.
107    ///
108    /// Separate from a parameter REQUEST_UPDATE cannot carry at all, which is
109    /// not an error: Section 9.2.2 says a parameter appearing in a message
110    /// type it is not defined for is ignored. This one is defined for
111    /// REQUEST_UPDATE and qualified by the request kind.
112    #[error("parameter {key:#x} is not for the kind of request {request_id} names")]
113    UpdateParameterNotForKind {
114        /// The Existing Request ID the update named.
115        request_id: u64,
116        /// The parameter type that the named request's kind does not admit.
117        key: u64,
118    },
119    /// The session is not in the Active state.
120    #[error("session not active")]
121    NotActive,
122    /// The session is draining and cannot accept new requests.
123    #[error("session is draining, no new requests allowed")]
124    Draining,
125    /// A second GOAWAY arrived on the control stream.
126    ///
127    /// The GOAWAY that says the peer is going away is one message, and the
128    /// draft answers a repeat of it with a session close rather than with an
129    /// error about the second message: there is no state a second one could
130    /// move that the first has not already moved.
131    #[error("a second GOAWAY arrived on the control stream")]
132    RepeatedGoAway,
133    /// A bidirectional stream the peer opened began with a message that is not
134    /// a SUBSCRIBE_NAMESPACE.
135    ///
136    /// Draft-16 Section 3.3: "This specification only specifies two uses of
137    /// bidirectional streams, the control stream, which begins with
138    /// CLIENT_SETUP, and SUBSCRIBE_NAMESPACE. Bidirectional streams MUST NOT
139    /// begin with any other message type unless negotiated. If they do, the
140    /// peer MUST close the Session with a Protocol Violation."
141    ///
142    /// The control stream is the client's own first bidirectional stream, so
143    /// of the two uses only one can ever arrive here.
144    #[error("{0:?} does not begin a namespace subscription stream")]
145    NotASubscribeNamespace(MessageType),
146    /// A NAMESPACE or NAMESPACE_DONE arrived on the control stream.
147    ///
148    /// Both are answers to a SUBSCRIBE_NAMESPACE and both are placed on that
149    /// request's own stream. Section 9.21: NAMESPACE "is sent on the response
150    /// stream of a SUBSCRIBE_NAMESPACE request". Section 9.23: "All
151    /// NAMESPACE_DONE messages are in response to a SUBSCRIBE_NAMESPACE, so
152    /// only the namespace tuples after the 'Track Namespace Prefix' are
153    /// included in the 'Track Namespace Suffix'."
154    ///
155    /// Neither carries a Request ID — the suffix is relative to a prefix only
156    /// the subscription knows — so on the control stream they name nothing at
157    /// all. That is why this is a refusal rather than a message that could be
158    /// handled anyway.
159    #[error(
160        "{0} received on the control stream; draft-16 sends it on a SUBSCRIBE_NAMESPACE response stream"
161    )]
162    NamespaceMessageOnControlStream(&'static str),
163    /// A response arrived on a namespace subscription's stream naming a
164    /// different request than the stream carries.
165    ///
166    /// Draft-16 puts the answer to a SUBSCRIBE_NAMESPACE "on the response half
167    /// of the stream" (Section 9.25) and also gives REQUEST_OK and
168    /// REQUEST_ERROR a Request ID field, so the request is named twice. When
169    /// the two disagree the endpoint cannot tell which one the sender meant,
170    /// and the draft says nothing about the case, so neither is acted on.
171    #[error("a response on the stream of request {stream} named request {message}")]
172    ResponseIdMismatch {
173        /// The Request ID of the subscription whose stream it arrived on.
174        stream: u64,
175        /// The Request ID the message itself carries.
176        message: u64,
177    },
178    /// The peer named a Track Alias it is already using for another track.
179    ///
180    /// Draft-16 states it twice, once per message. Section 9.10: "The same
181    /// Track Alias MUST NOT be used to refer to two different Tracks
182    /// simultaneously. If a subscriber receives a SUBSCRIBE_OK that uses the
183    /// same Track Alias as a different track with an Established subscription,
184    /// it MUST close the session with error DUPLICATE_TRACK_ALIAS." Section
185    /// 9.13 is the same sentence with PUBLISH in place of SUBSCRIBE_OK.
186    ///
187    /// The session is over: this endpoint's own state has moved to Closed and
188    /// the code the transport should close with is in
189    /// [`EndpointError::session_error_code`].
190    #[error("track alias {alias} already names request {established}'s track; request {offered} names a different one")]
191    DuplicateTrackAlias {
192        /// The alias both tracks are named by.
193        alias: u64,
194        /// The request whose live subscription holds the alias.
195        established: u64,
196        /// The request whose message arrived naming it for another track.
197        offered: u64,
198    },
199    /// This endpoint was asked to give a Track Alias to a second track.
200    ///
201    /// The same rule as [`EndpointError::DuplicateTrackAlias`] read at the end
202    /// that chooses the alias. Section 9.13 states it as a prohibition on the
203    /// publisher before it states what the subscriber does about one: "The
204    /// same Track Alias MUST NOT be used to refer to two different Tracks
205    /// simultaneously."
206    ///
207    /// The message is refused instead of built, and nothing else moves: no
208    /// Request ID is spent, no publish flow is created, and the session stays
209    /// as it was. The alias never reaches the peer, so there is nothing for
210    /// the peer to close over.
211    #[error("track alias {alias} already names request {held}'s track")]
212    TrackAliasInUse {
213        /// The alias that is already spoken for.
214        alias: u64,
215        /// The request whose live flow holds it.
216        held: u64,
217    },
218    /// An object arriving after the track's final object.
219    ///
220    /// Section 2.4.2 lists the condition: "An Object is received on a Track
221    /// whose Group and Object ID are larger than the final Object in the Track.
222    /// The final Object in a Track is the Object with Status END_OF_TRACK or
223    /// the last Object sent in a FETCH whose response indicated End of Track."
224    ///
225    /// **Larger is Section 1.4.1's comparison and not a reading of the
226    /// words.** That section puts one Location below another when "A.Group <
227    /// B.Group || (A.Group == B.Group && A.Object < B.Object)", so an Object in
228    /// a later group is past the end whatever its own Object ID is.
229    ///
230    /// **On this draft it is the only condition in that list this crate can
231    /// detect**, which is why the whole Malformed Track apparatus arrives with
232    /// it rather than before it. The condition the drafts on either side are
233    /// answered through — an object framed differently from the track's earlier
234    /// ones — is not a fault here: Section 10.2.1 says "Object Forwarding
235    /// Preference is a property of an individual Object and can vary among
236    /// Objects in the same Track", so this draft keeps no per-track framing for
237    /// an object to contradict. Draft-16 has stated the answer below since it
238    /// was written and had nothing to answer it with.
239    ///
240    /// **A Malformed Track and not a session error.** Section 2.4.2 answers its
241    /// whole list at once, and on this draft that answer is "it MUST UNSUBSCRIBE
242    /// any subscription and FETCH_CANCEL any fetch for that Track from that
243    /// publisher". The messages are the connection's; this is the error half.
244    #[error(
245        "the object at group {group}, object {object} on track alias {alias} arrived \
246         after the track's final object at group {final_group}, object {final_object}"
247    )]
248    ObjectPastFinalObject {
249        /// The Track Alias the offending object named.
250        alias: u64,
251        /// The Group ID it named.
252        group: u64,
253        /// The Object ID it named.
254        object: u64,
255        /// The Group ID of the object the track ended at.
256        final_group: u64,
257        /// The Object ID of the object the track ended at.
258        final_object: u64,
259    },
260    /// REQUEST_UPDATE named a Request ID this session has never carried a
261    /// request under.
262    ///
263    /// Section 9.11: "The receiver MUST close the session with
264    /// PROTOCOL_VIOLATION if the sender specifies an invalid Existing Request
265    /// ID, or if the parameters included in the REQUEST_UPDATE are invalid for
266    /// the type of request being modified."
267    ///
268    /// A request that has **ended** is not this: it existed. That is why the
269    /// record of an inbound SUBSCRIBE outlives the subscription, and why an
270    /// update naming an ended one is refused by the flow rather than by the
271    /// session.
272    #[error("REQUEST_UPDATE names request {0}, which this session has never carried")]
273    UpdateForUnknownRequest(u64),
274
275    /// A Joining Fetch named a subscription this session cannot join.
276    ///
277    /// Section 9.16.2: "If a publisher receives a Joining Fetch with a Request ID that
278    /// does not correspond to a subscription in the same session in the
279    /// Established or Pending (subscriber) states, it MUST return a
280    /// REQUEST_ERROR with error code INVALID_JOINING_REQUEST_ID."
281    ///
282    /// A refusal and not a session close, so the session runs on and the error
283    /// names both identifiers: the fetch to refuse, and the subscription it
284    /// asked to join.
285    #[error("FETCH {fetch} joins request {joining}, which is no live subscription of the peer's")]
286    UnjoinableSubscription {
287        /// The fetch that named it.
288        fetch: u64,
289        /// The identifier it named.
290        joining: u64,
291    },
292
293    /// A Joining Fetch was refused under a code other than the one the same
294    /// sentence names for it.
295    ///
296    /// The reason travels with the refusal, so a subscriber told the wrong one
297    /// retries the wrong thing: it can rebuild a fetch whose range was refused,
298    /// and cannot rebuild one whose subscription is gone.
299    #[error("refusing FETCH {fetch} for the subscription it joins takes error code {required}")]
300    WrongJoiningRefusal {
301        /// The fetch being refused.
302        fetch: u64,
303        /// The code the draft names for that refusal.
304        required: u64,
305    },
306    /// A message about an announcement named a namespace the peer has not
307    /// announced.
308    ///
309    /// Section 9.24 says what a cancellation is for: the subscriber "will stop
310    /// sending new subscriptions for tracks within the provided Track
311    /// Namespace". What a withdrawal ends and a cancellation revokes is an
312    /// announcement the **peer** made, so the record they reach for is the one
313    /// this endpoint keeps of the peer's announcements.
314    ///
315    /// Separate from [`EndpointError::UnknownRequest`], which is how the same
316    /// miss reads on the announcements this endpoint made: those are reached by
317    /// the Request ID this endpoint allocated, and PUBLISH_NAMESPACE_DONE and
318    /// PUBLISH_NAMESPACE_CANCEL both carry one.
319    #[error("the peer has made no live announcement for this namespace")]
320    UnknownPeerNamespace,
321    /// The peer subscribed to a namespace prefix overlapping one it is
322    /// already subscribed to.
323    ///
324    /// Section 9.25: "A subscriber cannot make overlapping namespace
325    /// subscriptions on a single session. Within a session, if a publisher
326    /// receives a SUBSCRIBE_NAMESPACE with a Track Namespace Prefix that
327    /// shares a common prefix with an established namespace subscription, it
328    /// MUST respond with REQUEST_ERROR with error code PREFIX_OVERLAP."
329    ///
330    /// Taken when the message arrives, which is the moment the sentence
331    /// names, and read again when an answer is built: a request this endpoint
332    /// may not accept is one no later call can accept.
333    ///
334    /// The refusal itself is not this error. It is a message the peer is
335    /// owed, so the request is recorded like any other and refused through
336    /// the same call that refuses any other, under the code the sentence
337    /// names.
338    #[error(
339        "request {request} subscribes to a namespace prefix overlapping request {established}"
340    )]
341    PeerPrefixOverlap {
342        /// The request that arrived.
343        request: u64,
344        /// The namespace subscription it overlaps.
345        established: u64,
346    },
347    /// This endpoint was asked to subscribe to a namespace prefix overlapping
348    /// one it is already subscribed to.
349    ///
350    /// The first half of the same sentence, which is addressed to the
351    /// subscriber: "A subscriber cannot make overlapping namespace
352    /// subscriptions on a single session."
353    ///
354    /// The message is refused instead of built, and nothing else moves: no
355    /// Request ID is spent, no state machine is created and the session stays
356    /// as it was. The request never reaches the peer, so there is nothing for
357    /// the peer to refuse.
358    #[error("the namespace prefix overlaps request {established}, which this endpoint made")]
359    OwnPrefixOverlap {
360        /// The namespace subscription this endpoint already has.
361        established: u64,
362    },
363    /// A namespace subscription that overlaps another was refused under a
364    /// code other than the one the sentence names.
365    ///
366    /// The same shape as [`EndpointError::WrongJoiningRefusal`]: a rule that
367    /// names the code its refusal carries is not satisfied by a refusal under
368    /// any other, because the peer reads the code to learn what went wrong.
369    #[error("request {request} overlaps a namespace subscription and must be refused with code {required:#x}")]
370    WrongOverlapRefusal {
371        /// The request being refused.
372        request: u64,
373        /// The code the sentence names for it.
374        required: u64,
375    },
376}
377
378/// Whether two namespace prefixes overlap.
379///
380/// Section 9.25: "A subscriber cannot make overlapping namespace
381/// subscriptions on a single session. Within a session, if a publisher
382/// receives a SUBSCRIBE_NAMESPACE with a Track Namespace Prefix that shares a
383/// common prefix with an established namespace subscription, it MUST respond
384/// with REQUEST_ERROR with error code PREFIX_OVERLAP."
385///
386/// A namespace matches a namespace subscription when the subscription's
387/// prefix is a prefix of it, so two prefixes select overlapping sets of
388/// namespaces exactly when one of them is a prefix of the other. Equal
389/// prefixes are that case as well: every prefix is a prefix of itself, and
390/// two equal ones select the same set.
391///
392/// "Shares a common prefix with" is read as the relation drafts 07 through 14
393/// spell out at greater length. Taken at its word it would forbid every
394/// second namespace subscription in a session, since any two prefixes share
395/// the empty one, and nothing else in this draft supports that.
396fn prefixes_overlap(a: &[Vec<u8>], b: &[Vec<u8>]) -> bool {
397    let shared = a.len().min(b.len());
398    a[..shared] == b[..shared]
399}
400
401impl EndpointError {
402    /// Whose doing this is — the peer's, or this endpoint's, or a variant that
403    /// cannot say.
404    ///
405    /// The companion of [`EndpointError::session_error_code`], which answers
406    /// *what the draft requires be done about it*. Neither answers the other's
407    /// question and the pair is what a caller needs: a code without a side
408    /// names nobody, and a side without a code is not grounds to publish
409    /// anything.
410    ///
411    /// Exhaustive, with no wildcard arm, so a variant added to this draft's
412    /// `EndpointError` is a compile error here rather than a silent arrival on
413    /// the wrong side of the answer. See
414    /// [`EndpointFault`](crate::above_codec_rules::EndpointFault) for the three
415    /// answers and for the collision that made the third one necessary.
416    pub fn fault(&self) -> crate::above_codec_rules::EndpointFault {
417        use crate::above_codec_rules::{AboveCodecRule as Rule, EndpointFault as Fault};
418
419        match self {
420            // Raised on both a receive path and a send path, so the
421            // variant cannot say which end is at fault. The state machines
422            // render as `invalid transition from X on event Y` whichever end
423            // asked for the transition, and the unknown-request errors name
424            // an id that may be one the peer sent or one a caller here made
425            // up.
426            EndpointError::Session(..)
427            | EndpointError::Subscription(..)
428            | EndpointError::Fetch(..)
429            | EndpointError::Namespace(..)
430            | EndpointError::TrackStatus(..)
431            | EndpointError::PublishFlow(..)
432            | EndpointError::Setup(..)
433            | EndpointError::UnknownRequest(..)
434            | EndpointError::UnknownPeerNamespace => Fault::EitherEnd,
435
436            // Raised on the way out. Nothing reached the wire, so none of
437            // these is evidence about a peer — including the ones a peer
438            // caused, where what failed is this side's attempt to accept
439            // something the draft says to refuse.
440            EndpointError::MaxRequestIdWouldNotIncrease { .. }
441            | EndpointError::NotActive
442            | EndpointError::Draining
443            | EndpointError::TrackAliasInUse { .. }
444            | EndpointError::UnjoinableSubscription { .. }
445            | EndpointError::WrongJoiningRefusal { .. }
446            | EndpointError::PeerPrefixOverlap { .. }
447            | EndpointError::OwnPrefixOverlap { .. }
448            | EndpointError::WrongOverlapRefusal { .. } => Fault::ThisEndpoint,
449
450            // Raised reading what the peer sent.
451            EndpointError::NotASubscribeNamespace(..) => Fault::Peer(Rule::BidiStreamOpener),
452            EndpointError::DuplicateTrackAlias { .. } => Fault::Peer(Rule::DuplicateTrackAlias),
453            EndpointError::GoAwayUriAtServer => Fault::Peer(Rule::GoAwayAtServer),
454            EndpointError::NamespaceMessageOnControlStream(..) => {
455                Fault::Peer(Rule::MessageOnTheWrongStream)
456            }
457            EndpointError::ObjectPastFinalObject { .. } => Fault::Peer(Rule::ObjectPastFinalObject),
458            EndpointError::RepeatedGoAway => Fault::Peer(Rule::RepeatedGoAway),
459            EndpointError::UpdateParameterNotForKind { .. }
460            | EndpointError::UpdateForUnknownRequest(..) => {
461                Fault::Peer(Rule::RequestUpdateForTheWrongRequest)
462            }
463            EndpointError::ResponseIdMismatch { .. } => {
464                Fault::Peer(Rule::ResponseNamesAnotherRequest)
465            }
466            EndpointError::NotASubscription(..) => Fault::Peer(Rule::TrackStatusIsNotASubscription),
467
468            // The Request ID rules, which are the peer's whenever they are
469            // read off the wire. The mirror — this endpoint asked to advertise
470            // a ceiling that does not increase — is
471            // `MaxRequestIdWouldNotIncrease` above, which is a variant of its
472            // own so that the two never arrive as one value.
473            EndpointError::RequestId(e) => match e {
474                RequestIdError::Decreased(..) => Fault::Peer(Rule::MaxRequestIdDecreased),
475                RequestIdError::ExceedsMax(..) => Fault::Peer(Rule::RequestIdCeiling),
476                RequestIdError::WrongParity(..) => Fault::Peer(Rule::RequestIdParity),
477                RequestIdError::Duplicate(..) => Fault::Peer(Rule::RequestIdOutOfSequence),
478                // This endpoint has spent the budget the peer granted it.
479                RequestIdError::Blocked => Fault::ThisEndpoint,
480            },
481        }
482    }
483
484    /// The code to close the session with, when draft-16 answers this error
485    /// with a close rather than leaving it to the one request it concerns.
486    ///
487    /// `None` means the error is recoverable: the caller may report it, give
488    /// up on the request it concerns, and keep the session running. `Some`
489    /// means the draft ends the session, and the endpoint has already moved
490    /// its own state to Closed - the code is what the transport should carry.
491    ///
492    /// The table grows one rule at a time, and a rule joins it with a gate
493    /// that drives the bytes at a real connection and reads the close code
494    /// back off the wire. An arm added without one asserts nothing: from
495    /// inside the process the session ends either way, and only the peer can
496    /// tell the difference.
497    pub fn session_error_code(&self) -> Option<SessionErrorCode> {
498        match self {
499            // Section 9.5 answers a ceiling that does not increase with a
500            // close, and names this code for it.
501            EndpointError::RequestId(RequestIdError::Decreased(..)) => {
502                Some(SessionErrorCode::ProtocolViolation)
503            }
504            // The same section answers a Request ID that reaches the ceiling
505            // this endpoint advertised, and names a different code for it.
506            EndpointError::RequestId(RequestIdError::ExceedsMax(..)) => {
507                Some(SessionErrorCode::TooManyRequests)
508            }
509            // Section 9.1 answers a Request ID that is not the peer's to
510            // spend with a close, and names INVALID_REQUEST_ID for it. Both
511            // halves of that sentence arrive here: "not valid for the peer"
512            // is an ID out of this endpoint's own half of the space, and "not
513            // the next in sequence" is enforced as reuse only (#1459).
514            //
515            // The sentence names a third condition too, an ID that "exceeds
516            // the received MAX_REQUEST_ID", and answers it with the same code.
517            // It is answered above with the code the MAX_REQUEST_ID section
518            // names instead, for the reason written at
519            // `validate_peer_request_id`.
520            EndpointError::RequestId(
521                RequestIdError::WrongParity(..) | RequestIdError::Duplicate(..),
522            ) => Some(SessionErrorCode::InvalidRequestId),
523            // Section 9.4 answers a GOAWAY that repeats one already
524            // received, and names this code in the same sentence.
525            EndpointError::RepeatedGoAway => Some(SessionErrorCode::ProtocolViolation),
526            // The same section answers a migration URI arriving at a server
527            // with a close, and names this code for it. Only a server may
528            // offer one, so a client that sends one is telling a server where
529            // to reconnect, which it has no standing to do.
530            EndpointError::GoAwayUriAtServer => Some(SessionErrorCode::ProtocolViolation),
531            // Section 3.3 answers a bidirectional stream that begins with the
532            // wrong message type with this code by name.
533            //
534            // A NAMESPACE or NAMESPACE_DONE on the control stream does not
535            // take the same answer, though it reads like *the same
536            // misplacement from the other end*. It is not, and the difference
537            // is the whole of what Section 3.3 says: its sentence is about
538            // what a bidirectional stream may *begin* with, and a message
539            // arriving on the control stream begins nothing. Read across all
540            // texts for a sentence that closes a session over a
541            // message being in the wrong place, draft-16 has none — Section
542            // 9.25 describes where NAMESPACE messages are sent ("the publisher
543            // will send matching NAMESPACE messages on the response stream"),
544            // which is prose about a conforming publisher with no consequence
545            // attached to a peer that does otherwise. A close there would be
546            // this build's reading of the protocol rather than the draft's.
547            // The message is refused; the session survives it, which it can,
548            // because a control message carries its own length and the next
549            // boundary is known.
550            EndpointError::NotASubscribeNamespace(_) => Some(SessionErrorCode::ProtocolViolation),
551            // Section 9.10 and Section 9.13 name this code in the sentence that
552            // states the rule, and name no other. A close carrying
553            // PROTOCOL_VIOLATION would tell the peer a different thing went
554            // wrong.
555            EndpointError::DuplicateTrackAlias { .. } => {
556                Some(SessionErrorCode::DuplicateTrackAlias)
557            }
558            // Section 9.11 answers an update naming a request the session has
559            // never carried with a close, and names this code for it.
560            EndpointError::UpdateForUnknownRequest(_) => Some(SessionErrorCode::ProtocolViolation),
561            EndpointError::UpdateParameterNotForKind { .. } => {
562                Some(SessionErrorCode::ProtocolViolation)
563            }
564            _ => None,
565        }
566    }
567}
568
569/// Unified MoQT endpoint wrapping session lifecycle, request ID allocation,
570/// and all per-request state machines (subscriptions, fetches, namespaces).
571pub struct Endpoint {
572    role: Role,
573    session: SessionStateMachine,
574    request_ids: RequestIdAllocator,
575    /// Tracks the MAX_REQUEST_ID we have advertised to the peer (monotonic).
576    advertised_max_id: u64,
577    /// Each subscription this endpoint made, behind a lock apiece.
578    ///
579    /// A lock rather than `&mut self` because Section 2.4.2's answer to a
580    /// Malformed Track is a message per request, the conditions that make a
581    /// track malformed are detected where objects arrive, and objects arrive
582    /// through a shared reference to the connection carrying them.
583    subscriptions: HashMap<u64, Mutex<SubscriptionStateMachine>>,
584    /// Each fetch this endpoint made, behind a lock apiece, for the reason the
585    /// subscriptions above are: the same sentence ends a fetch for a malformed
586    /// track and ends it from the same place.
587    fetches: HashMap<u64, Mutex<FetchStateMachine>>,
588    /// The track each fetch this endpoint made is for.
589    ///
590    /// # Why this is not in `track_bindings`
591    ///
592    /// That table exists to answer questions about Track Aliases, and a fetch
593    /// has none: its objects arrive on a stream that opens by naming the
594    /// Request ID, so no alias rule can ever be about a fetch. An entry that
595    /// can never hold an alias would be one every reader of that table had to
596    /// learn to skip.
597    ///
598    /// # Why a Joining Fetch is resolved here rather than when it is read
599    ///
600    /// A Joining Fetch names no track. It names the subscription it joins, and
601    /// the draft takes the rest from there: a publisher receiving one uses
602    /// properties of the associated Subscribe to determine the Track
603    /// Namespace, Track Name and End Location. So its track is the joined
604    /// subscription's, and it is looked up once, as the fetch is made.
605    ///
606    /// The other place to look it up is the withdrawal, through the request
607    /// the fetch joined, and that fails in the case a Joining Fetch exists
608    /// for: one fills a buffer behind the live edge, so it outlives the
609    /// subscription it joined, and the lookup would come up empty exactly
610    /// while there was still a fetch to cancel.
611    fetch_tracks: HashMap<u64, FetchTrack>,
612    /// The namespace prefix each SUBSCRIBE_NAMESPACE this endpoint sent asked
613    /// about, which the state machine beside it does not hold.
614    ///
615    /// Read by [`Endpoint::own_prefix_overlap`] and by nothing else. The
616    /// withdrawal names the Request ID here, so until the rule about
617    /// overlapping prefixes was judged there was nothing to remember a prefix
618    /// for.
619    subscribe_namespace_prefixes: HashMap<u64, TrackNamespace>,
620    subscribe_namespaces: HashMap<u64, SubscribeNamespaceStateMachine>,
621    /// The SUBSCRIBE_NAMESPACE each namespace subscription the **peer**
622    /// opened arrived as, keyed by the Request ID it carries.
623    ///
624    /// The state of each one stays in `subscribe_namespaces` beside this
625    /// endpoint's own, because a Request ID belongs to one request whichever
626    /// end opened it. The prefix does not. It is named in the request and
627    /// nowhere else, and it is what both the answer and every later request
628    /// are read against -- Section 9.25: "Within a session, if a publisher
629    /// receives a SUBSCRIBE_NAMESPACE with a Track Namespace Prefix that
630    /// shares a common prefix with an established namespace subscription, it
631    /// MUST respond with REQUEST_ERROR with error code PREFIX_OVERLAP."
632    inbound_subscribe_namespaces: HashMap<u64, SubscribeNamespace>,
633    /// The namespace subscription each of the peer's overlapped when it
634    /// arrived, keyed by the Request ID of the one that arrived.
635    ///
636    /// Section 9.25 judges the prefix on arrival -- "if a publisher receives a
637    /// SUBSCRIBE_NAMESPACE with a Track Namespace Prefix that shares a common
638    /// prefix with an established namespace subscription" -- so the verdict is
639    /// taken there and read again when the answer is written. An entry means
640    /// this endpoint owes that request a REQUEST_ERROR and may send it nothing
641    /// else.
642    overlapping_namespace_subscriptions: HashMap<u64, u64>,
643    publish_namespaces: HashMap<u64, PublishNamespaceStateMachine>,
644    /// Announcements the **peer** made, keyed by the Request ID it
645    /// opened each under.
646    inbound_publish_namespaces: HashMap<u64, InboundPublishNamespace>,
647    track_statuses: HashMap<u64, TrackStatusStateMachine>,
648    /// Track statuses the **peer** asked about, keyed by the Request ID it
649    /// asked under.
650    ///
651    /// Kept apart from `track_statuses`, which holds the ones this endpoint
652    /// asked about: the two are answered by opposite ends, and one Request ID
653    /// belongs to one request whichever end opened it.
654    inbound_track_statuses: HashMap<u64, InboundTrackStatus>,
655    /// Both directions' publish flows, behind a lock apiece.
656    ///
657    /// A subscription the peer opened with PUBLISH is one of the two ways this
658    /// endpoint receives a track, so the withdrawal a Malformed Track calls
659    /// for has to reach these as well as the subscriptions above.
660    publishes: HashMap<u64, Mutex<PublishStateMachine>>,
661    /// The PUBLISH each offer the **peer** made arrived as, keyed by the
662    /// Request ID it carries.
663    ///
664    ///
665    /// The state of each one stays in `publishes` beside this endpoint's own
666    /// offers, because every step after arrival names one Request ID and a
667    /// Request ID the peer allocated can never be one this endpoint
668    /// allocated. What a map of state machines cannot hold is the offer
669    /// itself, and the offer is what the answer is decided from. Section 5.1:
670    /// "A publisher initiates a subscription to a track by sending the
671    /// PUBLISH message. The subscriber either accepts or rejects the
672    /// subscription using PUBLISH_OK or REQUEST_ERROR."
673    ///
674    ///
675    /// `track_bindings` already keeps the Full Track Name and the Track
676    /// Alias, because the rules about aliases read them. The parameters and
677    /// the track extensions are here and nowhere else.
678    inbound_publishes: HashMap<u64, message::Publish>,
679    /// Subscriptions the **peer** opened with SUBSCRIBE, by Request ID.
680    ///
681    /// Section 9.11 puts the update in the subscriber's hands, so one that
682    /// arrives names a subscription in here and never one in `subscriptions`.
683    ///
684    /// The record holds the SUBSCRIBE itself and not only its state, because
685    /// the answer is built out of the request: the track a SUBSCRIBE_OK is
686    /// about is named nowhere else, and the alias it hands out has to be
687    /// judged against that name. A PUBLISH needs no such record because it
688    /// carries its track and its alias in the one message.
689    inbound_subscribes: HashMap<u64, InboundSubscribe>,
690    /// Every FETCH the peer has sent, from arrival to the end of the fetch.
691    ///
692    /// Separate from `fetches`, which holds the ones this endpoint made. The
693    /// record holds the FETCH itself and not only its state, because the
694    /// answer is built out of the request: a Joining Fetch has to be judged
695    /// against the subscription it names, and that name is nowhere else.
696    inbound_fetches: HashMap<u64, InboundFetch>,
697    goaway_uri: Option<Vec<u8>>,
698    /// What Full Track Name the peer has attached each Track Alias to, per
699    /// Request ID.
700    ///
701    /// Section 9.10 and Section 9.13 forbid one alias naming two tracks at
702    /// once, and the "at once" is what makes this a table rather than a set: an
703    /// alias the peer used for a track whose subscription has ended is free
704    /// again. The table therefore records the binding and reads liveness back
705    /// off the subscription's own state machine, rather than keeping a second
706    /// copy of it that every path ending a subscription would have to remember
707    /// to prune.
708    track_bindings: HashMap<u64, TrackBinding>,
709    /// Which tracks this endpoint has given up on, and what for.
710    ///
711    /// Behind a lock for the reason the flows above are: the note is taken on
712    /// the data plane, where this endpoint is reached through `&self`.
713    malformed: Mutex<MalformedTracks>,
714    /// How far each track's objects have reached, and where a track ended.
715    ///
716    /// An `Arc` rather than a plain lock because a subgroup stream measures
717    /// its objects against one track for as long as it runs, and the handle it
718    /// holds outlives any single call into this endpoint.
719    locations: Arc<Mutex<TrackLocations>>,
720}
721
722/// The track one fetch is for, which the fetch's own state machine does not
723/// hold. A fetch names no Track Alias, so this is the only place a fetch's
724/// track is written down; for a Joining Fetch, which names no track either,
725/// this is the joined subscription's track resolved when the fetch was made.
726#[derive(Debug, Clone)]
727struct FetchTrack {
728    namespace: TrackNamespace,
729    name: Vec<u8>,
730}
731
732/// A Track Alias the peer has attached to a Full Track Name, and the request
733/// whose lifetime the attachment follows.
734#[derive(Debug, Clone)]
735struct TrackBinding {
736    namespace: TrackNamespace,
737    name: Vec<u8>,
738    /// The alias, once the peer has named one.
739    ///
740    /// A SUBSCRIBE this endpoint sends names a track and waits for its alias,
741    /// so the binding exists with no alias in it from the moment the request
742    /// is made until its SUBSCRIBE_OK arrives. A PUBLISH carries both at once
743    /// and is never in that state.
744    alias: Option<u64>,
745    kind: BindingKind,
746}
747
748/// Which of the two sequences Section 5.1 names established the subscription
749/// that owns a binding, and so which state machine says whether it still has
750/// one.
751#[derive(Debug, Clone, Copy, PartialEq, Eq)]
752enum BindingKind {
753    /// This endpoint's SUBSCRIBE, established by the peer's SUBSCRIBE_OK.
754    Subscribe,
755    /// A PUBLISH, established by the PUBLISH_OK answering it - whichever end
756    /// sent which. Both are held in the same map, which Request ID parity
757    /// keeps from colliding.
758    Publish,
759    /// The peer's SUBSCRIBE, established by this endpoint's SUBSCRIBE_OK.
760    ///
761    /// The alias is this endpoint's to choose on that one, because Section
762    /// 9.10 carries it in the answer rather than in the request.
763    PeerSubscribe,
764}
765
766/// A subscription the peer opened with SUBSCRIBE.
767struct InboundSubscribe {
768    /// The message as it arrived, which is what the application answers from.
769    message: message::Subscribe,
770    /// How far the subscription it opened has got.
771    state: SubscriptionStateMachine,
772}
773/// A fetch the peer opened with FETCH.
774struct InboundFetch {
775    /// The message as it arrived, which is what the application answers from.
776    message: Fetch,
777    /// How far the fetch it opened has got.
778    state: FetchStateMachine,
779    /// The subscription a Joining Fetch named and this session had none live
780    /// for when the FETCH arrived, which is when the rule about it is read.
781    unjoinable: Option<u64>,
782}
783
784/// An announcement the peer made with PUBLISH_NAMESPACE.
785///
786/// Kept apart from the announcements this endpoint made because the two are
787/// answered by opposite ends: this one is waiting for an answer from here,
788/// and the other for one from the peer.
789struct InboundPublishNamespace {
790    /// The message as it arrived, which is what the application answers from.
791    message: PublishNamespace,
792    /// How far the announcement it makes has got.
793    state: PublishNamespaceStateMachine,
794}
795/// A track status the peer asked for with TRACK_STATUS.
796///
797/// Section 9.19 treats the request as a SUBSCRIBE "except it does not create
798/// downstream subscription state", so it is recorded here and not among the
799/// subscriptions the peer has opened. Nothing that names a subscription can
800/// find it, which is the whole of what that exception asks for.
801struct InboundTrackStatus {
802    /// The message as it arrived, which is what the answer is built from.
803    message: message::TrackStatus,
804    /// How far the request it opened has got.
805    state: TrackStatusStateMachine,
806}
807/// The kinds Section 9.2.2's qualifiers distinguish between.
808#[derive(Debug, Clone, Copy, PartialEq, Eq)]
809enum UpdatedRequest {
810    /// Established by SUBSCRIBE either way round, or by PUBLISH.
811    Subscription,
812    Fetch,
813    /// A track status or a namespace request: updatable, but admitting only
814    /// the parameters that carry no qualifier.
815    Other,
816}
817
818/// Section 9.2.2.3, "for a subscription or FETCH".
819const SUBSCRIBER_PRIORITY_PARAMETER: u64 = 0x20;
820/// Section 9.2.2.5, "for a subscription".
821const SUBSCRIPTION_FILTER_PARAMETER: u64 = 0x21;
822/// Section 9.2.2.8, "for a subscription".
823const FORWARD_PARAMETER: u64 = 0x10;
824/// Section 9.2.2.9, "for a subscription".
825const NEW_GROUP_REQUEST_PARAMETER: u64 = 0x32;
826
827impl Endpoint {
828    /// Create a new endpoint with the given role.
829    pub fn new(role: Role) -> Self {
830        Self {
831            role,
832            session: SessionStateMachine::new(),
833            request_ids: RequestIdAllocator::new(role),
834            advertised_max_id: 0,
835            subscriptions: HashMap::new(),
836            fetches: HashMap::new(),
837            fetch_tracks: HashMap::new(),
838            subscribe_namespaces: HashMap::new(),
839            subscribe_namespace_prefixes: HashMap::new(),
840            inbound_subscribe_namespaces: HashMap::new(),
841            overlapping_namespace_subscriptions: HashMap::new(),
842            publish_namespaces: HashMap::new(),
843            inbound_publish_namespaces: HashMap::new(),
844            track_statuses: HashMap::new(),
845            inbound_track_statuses: HashMap::new(),
846            publishes: HashMap::new(),
847            inbound_publishes: HashMap::new(),
848            inbound_subscribes: HashMap::new(),
849            inbound_fetches: HashMap::new(),
850            goaway_uri: None,
851            track_bindings: HashMap::new(),
852            malformed: Mutex::new(MalformedTracks::new()),
853            locations: Arc::new(Mutex::new(TrackLocations::new())),
854        }
855    }
856
857    /// The Track Alias the peer attached to `request_id`, once it has named
858    /// one.
859    ///
860    /// Answers for a subscription this endpoint asked for from the moment its
861    /// SUBSCRIBE_OK arrives, and for one the peer offered from the moment its
862    /// PUBLISH does. `None` before that, and for a Request ID this session has
863    /// no track for.
864    pub fn track_alias_for(&self, request_id: VarInt) -> Option<VarInt> {
865        let alias = self.track_bindings.get(&request_id.into_inner())?.alias?;
866        VarInt::from_u64(alias).ok()
867    }
868
869    /// The refusal Section 9.10 and Section 9.13 require when `alias` already
870    /// names a different track whose subscription is still live, or `None` when
871    /// it is free.
872    ///
873    /// # Why the set is read rather than kept
874    ///
875    /// "Established" is a subscription state Section 5.1 defines, and this
876    /// endpoint's subscription state machine holds it: a subscription reaches
877    /// 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
878    /// subscription ends, with nothing to prune on the way out - and a path
879    /// that ended a subscription without telling this table would otherwise
880    /// leave the alias held forever and refuse the peer's next, conforming,
881    /// use of it.
882    ///
883    /// # Why the request's own binding is skipped
884    ///
885    /// A SUBSCRIBE_OK is judged before its own alias is written down, so the
886    /// skip is not what keeps it from finding itself. A PUBLISH is: it carries
887    /// its alias and its track in the one message, and a second PUBLISH under
888    /// a Request ID already bound is refused by the duplicate-Request-ID rule
889    /// before it reaches here.
890    fn conflicting_track_alias(
891        &self,
892        request_id: u64,
893        alias: u64,
894        namespace: &TrackNamespace,
895        name: &[u8],
896    ) -> Option<EndpointError> {
897        for (&id, binding) in &self.track_bindings {
898            if id == request_id || binding.alias != Some(alias) {
899                continue;
900            }
901            if binding.namespace == *namespace && binding.name == name {
902                continue;
903            }
904            if self.binding_is_established(id, binding.kind) {
905                return Some(EndpointError::DuplicateTrackAlias {
906                    alias,
907                    established: id,
908                    offered: request_id,
909                });
910            }
911        }
912        None
913    }
914
915    /// The request already using `alias` for a track other than (`namespace`,
916    /// `name`), or `None` when this endpoint may give the alias to that track.
917    ///
918    /// Separate from [`Self::conflicting_track_alias`] because the two answer
919    /// different questions about the same table. That one judges a message
920    /// that has arrived and ends the session over it; this one judges one that
921    /// has not been built and declines to build it.
922    fn alias_held_elsewhere(
923        &self,
924        alias: u64,
925        namespace: &TrackNamespace,
926        name: &[u8],
927    ) -> Option<EndpointError> {
928        self.track_bindings.iter().find_map(|(&id, binding)| {
929            let other_track = binding.namespace != *namespace || binding.name != name;
930            (binding.alias == Some(alias)
931                && other_track
932                && self.binding_is_in_use(id, binding.kind))
933            .then_some(EndpointError::TrackAliasInUse { alias, held: id })
934        })
935    }
936
937    /// Whether a binding's request has put its alias in play at all.
938    /// Broader than [`Self::binding_is_established`], and the two sentences are
939    /// why. What a subscriber must close over is qualified - "the same Track
940    /// Alias as a different track with an Established subscription" - and the
941    /// prohibition on the publisher is not: "The same Track Alias MUST NOT be
942    /// used to refer to two different Tracks simultaneously." Once a PUBLISH
943    /// carrying an alias has been sent, giving that alias to a second track is
944    /// what that sentence forbids, answered or not.
945    fn binding_is_in_use(&self, id: u64, kind: BindingKind) -> bool {
946        match kind {
947            BindingKind::Subscribe => {
948                self.subscription(id).is_some_and(|sm| sm.state() != SubscriptionState::Done)
949            }
950            BindingKind::Publish => {
951                self.publish_flow(id).is_some_and(|sm| sm.state() != PublishState::Done)
952            }
953            BindingKind::PeerSubscribe => self
954                .inbound_subscribes
955                .get(&id)
956                .is_some_and(|s| s.state.state() != SubscriptionState::Done),
957        }
958    }
959
960    /// Whether the request that owns a binding still has a live subscription.
961    ///
962    /// The two kinds are answered by two different state machines because the
963    /// two sequences Section 5.1 names end in different places: a SUBSCRIBE
964    /// this endpoint made is live once its SUBSCRIBE_OK arrives, a PUBLISH the
965    /// peer made once this endpoint has answered PUBLISH_OK.
966    fn binding_is_established(&self, id: u64, kind: BindingKind) -> bool {
967        match kind {
968            BindingKind::Subscribe => {
969                self.subscription(id).is_some_and(|sm| sm.state() == SubscriptionState::Active)
970            }
971            BindingKind::Publish => {
972                self.publish_flow(id).is_some_and(|sm| sm.state() == PublishState::Active)
973            }
974            BindingKind::PeerSubscribe => self
975                .inbound_subscribes
976                .get(&id)
977                .is_some_and(|s| s.state.state() == SubscriptionState::Active),
978        }
979    }
980
981    /// The conflict a SUBSCRIBE_OK's alias has with the tracks already bound.
982    ///
983    /// Separate from [`Self::conflicting_track_alias`] because the track a
984    /// SUBSCRIBE_OK is about is not in the SUBSCRIBE_OK: it is the one this
985    /// endpoint's own SUBSCRIBE asked for, which is why the request has to be
986    /// looked up before the alias can be judged.
987    fn conflicting_alias_for_subscribe_ok(&self, id: u64, alias: u64) -> Option<EndpointError> {
988        let binding = self.track_bindings.get(&id)?;
989        self.conflicting_track_alias(id, alias, &binding.namespace, &binding.name)
990    }
991
992    /// The track a live binding has given `alias` to.
993    ///
994    /// Read rather than kept, for the reason the alias table beside it gives: a
995    /// binding whose request has ended holds nothing, and an alias that is free
996    /// again may name a different track next. That is why the records this
997    /// serves are keyed on the track it returns and never on the alias itself.
998    fn track_for_alias(&self, alias: u64) -> Option<(&TrackNamespace, &[u8])> {
999        self.track_bindings.iter().find_map(|(&id, binding)| {
1000            (binding.alias == Some(alias) && self.binding_is_in_use(id, binding.kind))
1001                .then_some((&binding.namespace, binding.name.as_slice()))
1002        })
1003    }
1004
1005    /// The record a stream carrying `alias`'s objects measures them against.
1006    ///
1007    /// `None` for an alias no live binding names: an object for one breaks a
1008    /// different rule, and measuring it against a track this endpoint never
1009    /// asked for would answer that one with the wrong sentence.
1010    pub fn track_objects(&self, alias: u64) -> Option<TrackObjects> {
1011        let (namespace, name) = self.track_for_alias(alias)?;
1012        Some(TrackObjects::new(
1013            Arc::clone(&self.locations),
1014            namespace.clone(),
1015            name.to_vec(),
1016            alias,
1017        ))
1018    }
1019
1020    /// Record or judge one object that arrived outside a subgroup stream, and
1021    /// report Section 2.4.2's Malformed Track when it arrived after the place
1022    /// an end-of-track object put the end.
1023    ///
1024    /// One rule and not two. The placement rule drafts 08 through 13 state
1025    /// about an end-of-track object is not in this draft, so an object that
1026    /// ends a track here is judged against nothing and only settles where the
1027    /// track stopped.
1028    ///
1029    /// `&self`, because the call site is the data plane's.
1030    pub fn note_received_object(
1031        &self,
1032        alias: u64,
1033        at: ObjectLocation,
1034        role: ObjectRole,
1035    ) -> Result<(), EndpointError> {
1036        let Some(objects) = self.track_objects(alias) else { return Ok(()) };
1037        objects.note_past_final(at, role).map_err(|end| EndpointError::ObjectPastFinalObject {
1038            alias,
1039            group: at.group,
1040            object: at.object,
1041            final_group: end.group,
1042            final_object: end.object,
1043        })
1044    }
1045
1046    /// The subscription `id` opened, locked for a read or a transition.
1047    ///
1048    /// `&self`, which is the whole point of the lock. Section 2.4.2 answers a
1049    /// Malformed Track with a message per request, and the condition that makes
1050    /// a track malformed here is detected on the data plane, where this
1051    /// endpoint is reached through a shared reference. A flow that could only
1052    /// be moved through `&mut self` would leave that sentence unanswerable from
1053    /// the only place it is ever read.
1054    ///
1055    /// Hold one guard at a time. Nothing here takes a second while a first is
1056    /// live, and [`Self::withdraw_malformed_track`] collects the requests it is
1057    /// going to move before it moves any of them for that reason.
1058    fn subscription(&self, id: u64) -> Option<MutexGuard<'_, SubscriptionStateMachine>> {
1059        self.subscriptions
1060            .get(&id)
1061            .map(|sm| sm.lock().unwrap_or_else(|poisoned| poisoned.into_inner()))
1062    }
1063
1064    /// The publish flow `id` opened, locked for a read or a transition.
1065    ///
1066    /// One map for both directions, which is what makes this one accessor: the
1067    /// offer this endpoint made and the offer the peer made are the same flow
1068    /// read from opposite ends, and Request ID parity keeps them apart.
1069    fn publish_flow(&self, id: u64) -> Option<MutexGuard<'_, PublishStateMachine>> {
1070        self.publishes.get(&id).map(|sm| sm.lock().unwrap_or_else(|p| p.into_inner()))
1071    }
1072
1073    /// The fetch `id` opened, locked for a read or a transition.
1074    fn fetch_flow(&self, id: u64) -> Option<MutexGuard<'_, FetchStateMachine>> {
1075        self.fetches.get(&id).map(|sm| sm.lock().unwrap_or_else(|p| p.into_inner()))
1076    }
1077
1078    /// The condition a track was withdrawn for, or `None` for a track this
1079    /// endpoint has found nothing wrong with.
1080    ///
1081    /// The withdrawal happens without the application asking for it, so this is
1082    /// how an application that missed the error learns why a subscription it
1083    /// never ended has ended.
1084    ///
1085    /// # Why this takes a track and not the alias the object carried
1086    ///
1087    /// An alias only means anything through a live binding, and the withdrawal
1088    /// ends the binding it would have been resolved through. An accessor taking
1089    /// an alias would therefore answer `None` from the instant it had something
1090    /// to say, which is worse than not existing. The record is keyed on the
1091    /// track, and so is this.
1092    pub fn malformed_track(
1093        &self,
1094        namespace: &TrackNamespace,
1095        name: &[u8],
1096    ) -> Option<MalformedTrackCondition> {
1097        self.malformed
1098            .lock()
1099            .unwrap_or_else(|poisoned| poisoned.into_inner())
1100            .condition(namespace, name)
1101    }
1102
1103    /// Withdraw from a track a condition has just shown to be malformed, and
1104    /// give back the messages Section 2.4.2 asks for.
1105    ///
1106    /// "When a subscriber detects a Malformed Track, it MUST UNSUBSCRIBE any
1107    /// subscription and FETCH_CANCEL any fetch for that Track from that
1108    /// publisher, and SHOULD deliver an error to the application." This is the
1109    /// first half. The second is the error the detecting path returns, which is
1110    /// why nothing here reports anything: a caller that gets an empty list back
1111    /// is not being told the track was fine.
1112    ///
1113    /// `&self`, because the call site is the data plane's.
1114    ///
1115    /// # What comes back, and what does not
1116    ///
1117    /// One message for each request through which this endpoint is *receiving*
1118    /// the track: an UNSUBSCRIBE for a SUBSCRIBE it sent and for a PUBLISH the
1119    /// peer sent that it accepted, and a FETCH_CANCEL for each fetch it made for
1120    /// that track. A request that names the track the other way round is not one
1121    /// of those — a peer subscribing to this endpoint makes it the publisher,
1122    /// and a publisher does not unsubscribe from what it is serving.
1123    ///
1124    /// Empty for an alias no live binding names — there is no track to withdraw
1125    /// from — and for a track whose only requests are in a state that has no
1126    /// ending of that kind left in it.
1127    ///
1128    /// # Two records, one scan
1129    ///
1130    /// Subscriptions are found through the alias table and fetches through their
1131    /// own, because a fetch never holds an alias. Both are keyed by Request ID
1132    /// and a Request ID names one request, so the two lists cannot overlap and
1133    /// the withdrawal below can tell from the id alone which message a request
1134    /// takes.
1135    ///
1136    /// # What makes the answer happen once
1137    ///
1138    /// Not the record. The requests this withdraws end here, and a request that
1139    /// has ended has no second ending in it, so a publisher that goes on sending
1140    /// objects past the end of a track is answered once however many it sends.
1141    /// The record is read afterwards, by an application asking why a track it
1142    /// never gave up was given up.
1143    ///
1144    /// Which leaves the case the two answers differ on: an application that
1145    /// subscribes to the same track again. That request has never been withdrawn
1146    /// from, and a publisher breaking the sentence again on it has broken it
1147    /// again, so it is withdrawn from too.
1148    pub fn withdraw_malformed_track(
1149        &self,
1150        alias: u64,
1151        condition: MalformedTrackCondition,
1152    ) -> Vec<ControlMessage> {
1153        let Some((namespace, name)) = self.track_for_alias(alias) else { return Vec::new() };
1154        let (namespace, name) = (namespace.clone(), name.to_vec());
1155        self.withdraw_malformed_track_named(namespace, name, condition)
1156    }
1157
1158    /// [`Self::withdraw_malformed_track`] for the track the FETCH `request_id`
1159    /// asked for, which a FETCH stream found malformed: the stream names no
1160    /// alias. Empty for a Request ID this endpoint sent no FETCH under.
1161    pub fn withdraw_malformed_fetch_track(
1162        &self,
1163        request_id: u64,
1164        condition: MalformedTrackCondition,
1165    ) -> Vec<ControlMessage> {
1166        let Some(track) = self.fetch_tracks.get(&request_id) else { return Vec::new() };
1167        let (namespace, name) = (track.namespace.clone(), track.name.clone());
1168        self.withdraw_malformed_track_named(namespace, name, condition)
1169    }
1170
1171    fn withdraw_malformed_track_named(
1172        &self,
1173        namespace: TrackNamespace,
1174        name: Vec<u8>,
1175        condition: MalformedTrackCondition,
1176    ) -> Vec<ControlMessage> {
1177        self.malformed
1178            .lock()
1179            .unwrap_or_else(|poisoned| poisoned.into_inner())
1180            .note(&namespace, &name, condition);
1181        // Collected before any of them is moved. The liveness checks below take
1182        // each request's own lock and the transitions take it again, so the scan
1183        // finishes first and holds nothing while it runs.
1184        let mut ids: Vec<u64> = self
1185            .track_bindings
1186            .iter()
1187            .filter(|(&id, binding)| {
1188                binding.namespace == namespace
1189                    && binding.name == name
1190                    && self.binding_is_in_use(id, binding.kind)
1191            })
1192            .map(|(&id, _)| id)
1193            .chain(self.fetch_tracks.iter().filter_map(|(&id, track)| {
1194                (track.namespace == namespace
1195                    && track.name == name
1196                    && self.fetch_flow(id).is_some_and(|sm| sm.state() != FetchState::Done))
1197                .then_some(id)
1198            }))
1199            .collect();
1200        // A HashMap iterates in no order, and two requests for one track is a
1201        // shape a peer can produce. Sorted so the wire is the same twice.
1202        ids.sort_unstable();
1203        ids.into_iter().filter_map(|id| self.withdraw_one(id)).collect()
1204    }
1205
1206    /// The message ending one request, or `None` when that request is not one
1207    /// this endpoint receives a track through or is not in a state that can be
1208    /// ended that way.
1209    fn withdraw_one(&self, id: u64) -> Option<ControlMessage> {
1210        let request_id = VarInt::from_u64(id).ok()?;
1211        if let Some(mut sm) = self.subscription(id) {
1212            sm.on_unsubscribe().ok()?;
1213            return Some(ControlMessage::Unsubscribe(Unsubscribe { request_id }));
1214        }
1215        if let Some(mut sm) = self.fetch_flow(id) {
1216            sm.on_fetch_cancel().ok()?;
1217            return Some(ControlMessage::FetchCancel(FetchCancel { request_id }));
1218        }
1219        // `publishes` holds both directions and the offer's own message is what
1220        // tells them apart: only one the peer made is a track this endpoint
1221        // receives.
1222        self.inbound_publishes.get(&id)?;
1223        self.publish_flow(id)?.on_unsubscribe_sent().ok()?;
1224        Some(ControlMessage::Unsubscribe(Unsubscribe { request_id }))
1225    }
1226
1227    // -- Accessors --------------------------------------------------
1228
1229    /// Returns the role (client or server) of this endpoint.
1230    pub fn role(&self) -> Role {
1231        self.role
1232    }
1233
1234    /// Returns the current session state.
1235    pub fn session_state(&self) -> SessionState {
1236        self.session.state()
1237    }
1238
1239    /// Returns the URI from a received GOAWAY message, if any.
1240    pub fn goaway_uri(&self) -> Option<&[u8]> {
1241        self.goaway_uri.as_deref()
1242    }
1243
1244    /// Returns whether this endpoint is blocked on request ID allocation.
1245    pub fn is_blocked(&self) -> bool {
1246        self.request_ids.is_blocked()
1247    }
1248
1249    /// Returns the number of active subscription state machines.
1250    pub fn active_subscription_count(&self) -> usize {
1251        self.subscriptions.len()
1252    }
1253
1254    /// Returns the number of active fetch state machines.
1255    pub fn active_fetch_count(&self) -> usize {
1256        self.fetches.len()
1257    }
1258
1259    /// Returns the number of active subscribe-namespace state machines.
1260    pub fn active_subscribe_namespace_count(&self) -> usize {
1261        self.subscribe_namespaces.len()
1262    }
1263
1264    /// Returns the number of active publish-namespace state machines.
1265    pub fn active_publish_namespace_count(&self) -> usize {
1266        self.publish_namespaces.len()
1267    }
1268
1269    /// Returns the number of active track status state machines.
1270    pub fn active_track_status_count(&self) -> usize {
1271        self.track_statuses.len()
1272    }
1273
1274    /// Returns the number of active publish state machines.
1275    pub fn active_publish_count(&self) -> usize {
1276        self.publishes.len()
1277    }
1278
1279    // -- Session lifecycle ------------------------------------------
1280
1281    /// Transition from Connecting to SetupExchange.
1282    pub fn connect(&mut self) -> Result<(), EndpointError> {
1283        self.session.on_connect()?;
1284        Ok(())
1285    }
1286
1287    /// Close the session (SetupExchange, Active or Draining -> Closed).
1288    pub fn close(&mut self) -> Result<(), EndpointError> {
1289        self.session.on_close()?;
1290        Ok(())
1291    }
1292
1293    // -- Client setup (draft-16: no versions) -----------------------
1294
1295    /// Generate a CLIENT_SETUP message (client-side).
1296    /// Draft-16 uses ALPN for version negotiation -- no versions field.
1297    pub fn send_client_setup(
1298        &mut self,
1299        parameters: Vec<KeyValuePair>,
1300    ) -> Result<ControlMessage, EndpointError> {
1301        let msg = ClientSetup { parameters };
1302        setup::validate_client_setup(&msg)?;
1303        // A MAX_REQUEST_ID parameter in our own CLIENT_SETUP is a grant to
1304        // the peer, so it is the first value of the ceiling we advertise.
1305        self.record_advertised_max(&msg.parameters);
1306        Ok(ControlMessage::ClientSetup(msg))
1307    }
1308
1309    /// Process a SERVER_SETUP message (client-side). Transitions to Active.
1310    /// If the server includes a MAX_REQUEST_ID parameter (key 0x02), the
1311    /// request ID allocator is initialized with that value.
1312    pub fn receive_server_setup(&mut self, msg: &ServerSetup) -> Result<(), EndpointError> {
1313        setup::validate_server_setup(msg)?;
1314        self.session.on_setup_complete()?;
1315        // Extract MAX_REQUEST_ID (key 0x02) from setup parameters if present
1316        for param in &msg.parameters {
1317            if param.key == VarInt::from_u64(0x02).unwrap() {
1318                if let KvpValue::Varint(v) = &param.value {
1319                    self.request_ids.update_max(v.into_inner())?;
1320                }
1321            }
1322        }
1323        Ok(())
1324    }
1325
1326    // -- Server setup -----------------------------------------------
1327
1328    /// Process CLIENT_SETUP and generate SERVER_SETUP (server-side).
1329    /// Draft-16 uses ALPN for version negotiation -- just transition and
1330    /// respond.
1331    pub fn receive_client_setup_and_respond(
1332        &mut self,
1333        client_setup: &ClientSetup,
1334    ) -> Result<ControlMessage, EndpointError> {
1335        setup::validate_client_setup(client_setup)?;
1336        // Section 9.3.1.3 puts no role restriction on MAX_REQUEST_ID, so a
1337        // CLIENT_SETUP may carry it and it grants this endpoint its budget.
1338        for param in &client_setup.parameters {
1339            if param.key == VarInt::from_u64(0x02).unwrap() {
1340                if let KvpValue::Varint(v) = &param.value {
1341                    self.request_ids.update_max(v.into_inner())?;
1342                }
1343            }
1344        }
1345        self.session.on_setup_complete()?;
1346        let msg = ServerSetup { parameters: vec![] };
1347        Ok(ControlMessage::ServerSetup(msg))
1348    }
1349
1350    // -- MAX_REQUEST_ID ---------------------------------------------
1351
1352    /// Take a Request ID from a new request the peer sent, holding it to all
1353    /// three halves of the rule.
1354    ///
1355    /// Section 9.1 closes the session with INVALID_REQUEST_ID on a request ID
1356    /// "that is not valid for the peer", and Section 9.5 closes it with
1357    /// TOO_MANY_REQUESTS on one at or above the ceiling this endpoint
1358    /// advertised. The parity half belongs to the allocator; the ceiling half
1359    /// needs `advertised_max_id`, which is this endpoint's grant to the peer
1360    /// and not the peer's grant to it, so the two numbers are different and
1361    /// only one of them answers this question.
1362    ///
1363    /// The third is reuse. Section 9.1 also asks for "the next in sequence",
1364    /// which is not enforced: SUBSCRIBE_NAMESPACE has a stream of its own, so
1365    /// requests can arrive out of order, and the editors' guidance for this
1366    /// draft (moq-wg/moq-transport#1459) is to check only parity, the ceiling
1367    /// and duplicates. A skipped ID is accepted when it arrives later; a repeat
1368    /// is refused.
1369    ///
1370    /// Taking the ID marks it used, so this is for a **new** request only. A
1371    /// response, a cancellation or an update that names the request it
1372    /// modifies all carry an ID that has already been spent, and passing one
1373    /// here would refuse it.
1374    ///
1375    /// # The ceiling is a session rule, not a request rule
1376    ///
1377    /// Section 9.5: a Request ID "equal to or larger than this" received by the
1378    /// endpoint that sent the MAX_REQUEST_ID in any request message - the list
1379    /// [`Self::receive_request`] carries - closes the session, and
1380    /// TOO_MANY_REQUESTS is the code. Which is also why the number measured
1381    /// against is the one **this** endpoint sent. An id that reaches the ceiling
1382    /// is not a request to refuse with an error message: the session is over, so
1383    /// this moves the endpoint's own state to Closed and leaves the code to
1384    /// [`EndpointError::session_error_code`].
1385    ///
1386    /// # Two sections, two codes
1387    ///
1388    /// This draft states the rule twice and does not agree with itself.
1389    /// Section 9.1 folds the ceiling into the sentence about the ID sequence -
1390    /// "a new request with a Request ID that is not the next in sequence or
1391    /// exceeds the received MAX_REQUEST_ID" - and answers it with
1392    /// INVALID_REQUEST_ID instead. It also measures against the *received*
1393    /// ceiling, which is the budget the peer granted this endpoint rather than
1394    /// the one this endpoint granted the peer, and measuring an incoming
1395    /// request against our own budget is not a rule that can be applied. Ten
1396    /// drafts state the ceiling in the MAX_REQUEST_ID section and all ten name
1397    /// the same code there; two of them restate it elsewhere. The
1398    /// MAX_REQUEST_ID section is the one followed here.
1399    ///
1400    /// # Errors
1401    ///
1402    /// [`RequestIdError::WrongParity`] if the ID belongs to this endpoint's
1403    /// half of the space, [`RequestIdError::ExceedsMax`] if it is not
1404    /// below the advertised ceiling, or [`RequestIdError::Duplicate`] if the
1405    /// peer has already used it. A ceiling that was never
1406    /// raised is 0, which refuses every ID, matching a default that reads "the
1407    /// peer MUST NOT send requests".
1408    ///
1409    /// All three end the session rather than the one request, so all three
1410    /// move this endpoint's own session to Closed on the way out and all three
1411    /// answer `Some` from [`EndpointError::session_error_code`]. Returning one
1412    /// of them without ending the session would leave a peer that broke the
1413    /// rule free to keep sending, and never told.
1414    pub fn validate_peer_request_id(&mut self, id: u64) -> Result<(), EndpointError> {
1415        if let Err(e) = self.request_ids.validate_peer_id(id) {
1416            return Err(self.fail_session(EndpointError::RequestId(e)));
1417        }
1418        if id >= self.advertised_max_id {
1419            return Err(self.fail_session(EndpointError::RequestId(RequestIdError::ExceedsMax(
1420                id,
1421                self.advertised_max_id,
1422            ))));
1423        }
1424        if let Err(e) = self.request_ids.record_peer_id(id) {
1425            return Err(self.fail_session(EndpointError::RequestId(e)));
1426        }
1427        Ok(())
1428    }
1429
1430    /// Record a MAX_REQUEST_ID parameter this endpoint is about to send as
1431    /// the ceiling it has advertised to the peer.
1432    fn record_advertised_max(&mut self, parameters: &[KeyValuePair]) {
1433        for param in parameters {
1434            if param.key == VarInt::from_u64(0x02).unwrap() {
1435                if let KvpValue::Varint(v) = &param.value {
1436                    self.advertised_max_id = v.into_inner();
1437                }
1438            }
1439        }
1440    }
1441
1442    /// Process an incoming MAX_REQUEST_ID message, ending the session if the
1443    /// ceiling it carries does not increase.
1444    ///
1445    /// Section 9.5: "The Maximum Request ID MUST only increase within a
1446    /// session. If an endpoint receives MAX_REQUEST_ID message with an equal
1447    /// or smaller Request ID it MUST close the session with a
1448    /// PROTOCOL_VIOLATION." Every draft below this one states the same rule as
1449    /// a condition rather than an action - naming the code without the verb -
1450    /// and this one supplies the verb.
1451    ///
1452    /// # Errors
1453    ///
1454    /// [`RequestIdError::Decreased`] if the value does not increase, with the
1455    /// session already moved to Closed.
1456    pub fn receive_max_request_id(&mut self, msg: &MaxRequestId) -> Result<(), EndpointError> {
1457        if let Err(err) = self.request_ids.update_max(msg.request_id.into_inner()) {
1458            return Err(self.fail_session(err.into()));
1459        }
1460        Ok(())
1461    }
1462
1463    /// Generate a MAX_REQUEST_ID message (typically server-side).
1464    ///
1465    /// Section 9.5: "The Maximum Request ID MUST only increase within a
1466    /// session", and a peer that receives an equal or smaller value closes
1467    /// the session. The ceiling starts at 0 and 0 is not greater than 0, so
1468    /// the first value that may go on the wire is 1 and there is no opening
1469    /// case where a repeat is allowed.
1470    ///
1471    /// # Errors
1472    ///
1473    /// [`EndpointError::MaxRequestIdWouldNotIncrease`] if the value does not
1474    /// strictly increase. Its own variant rather than the one a *received*
1475    /// ceiling that did not increase raises, so that a refusal to write is
1476    /// never read back as a peer in violation.
1477    pub fn send_max_request_id(&mut self, max_id: VarInt) -> Result<ControlMessage, EndpointError> {
1478        let new_val = max_id.into_inner();
1479        if new_val <= self.advertised_max_id {
1480            return Err(EndpointError::MaxRequestIdWouldNotIncrease {
1481                advertised: self.advertised_max_id,
1482                offered: new_val,
1483            });
1484        }
1485        self.advertised_max_id = new_val;
1486        Ok(ControlMessage::MaxRequestId(MaxRequestId { request_id: max_id }))
1487    }
1488
1489    /// Generate a REQUESTS_BLOCKED message indicating that this endpoint
1490    /// wants to create a new request but is blocked by the current
1491    /// MAX_REQUEST_ID.
1492    pub fn send_requests_blocked(&self) -> Result<ControlMessage, EndpointError> {
1493        let max_id = self.request_ids.max_id();
1494        Ok(ControlMessage::RequestsBlocked(RequestsBlocked {
1495            maximum_request_id: VarInt::from_u64(max_id).unwrap(),
1496        }))
1497    }
1498
1499    /// Process an incoming REQUESTS_BLOCKED message from the peer.
1500    pub fn receive_requests_blocked(&self, _msg: &RequestsBlocked) -> Result<(), EndpointError> {
1501        Ok(())
1502    }
1503
1504    // -- GoAway -----------------------------------------------------
1505
1506    /// Process an incoming GOAWAY message. Transitions to Draining.
1507    ///
1508    /// # Errors
1509    ///
1510    /// [`EndpointError::GoAwayUriAtServer`] if this endpoint is the server and
1511    /// the GOAWAY carries a New Session URI. The session is over: this
1512    /// endpoint's own state has moved to Closed and the code the transport
1513    /// should close with is in [`EndpointError::session_error_code`].
1514    ///
1515    /// [`EndpointError::RepeatedGoAway`] if a GOAWAY has already been
1516    /// received. The session is over: this endpoint's own state has moved to
1517    /// Closed and the code the transport should close with is in
1518    /// [`EndpointError::session_error_code`].
1519    pub fn receive_goaway(&mut self, msg: &GoAway) -> Result<(), EndpointError> {
1520        // Section 9.4: "If a server receives a GOAWAY with a non-zero New
1521        // Session URI Length it MUST close the session with a
1522        // PROTOCOL_VIOLATION." Refused before the URI is stored rather than
1523        // after, so an application reading `goaway_uri` back can never be
1524        // handed somewhere a client chose to send it. The session ends with
1525        // it: the sentence names a close and a code, and an endpoint that
1526        // raised the error and carried on would keep serving a peer it had
1527        // just found in violation.
1528        if self.role == Role::Server && !msg.new_session_uri.is_empty() {
1529            return Err(self.fail_session(EndpointError::GoAwayUriAtServer));
1530        }
1531        // Section 9.4: "The endpoint MUST close the session with a
1532        // PROTOCOL_VIOLATION (Section 3.4) if it receives multiple GOAWAY messages."
1533        // Draining is reached from nowhere else - `on_goaway` is its only
1534        // entry and this method is that method's only caller - so the session
1535        // state is the record of the first GOAWAY having arrived.
1536        if self.session.state() == SessionState::Draining {
1537            return Err(self.fail_session(EndpointError::RepeatedGoAway));
1538        }
1539        self.session.on_goaway()?;
1540        self.goaway_uri = Some(msg.new_session_uri.clone());
1541        Ok(())
1542    }
1543
1544    // -- Subscribe flow ---------------------------------------------
1545
1546    fn require_active_or_err(&self) -> Result<(), EndpointError> {
1547        match self.session.state() {
1548            SessionState::Active => Ok(()),
1549            SessionState::Draining => Err(EndpointError::Draining),
1550            _ => Err(EndpointError::NotActive),
1551        }
1552    }
1553
1554    /// Record that the session is over because the peer broke a rule this
1555    /// draft answers with a session close, and hand the error back unchanged.
1556    ///
1557    /// The state move is what makes the violation stick: every request entry
1558    /// point goes through
1559    /// [`require_active_or_err`](Self::require_active_or_err), so a caller
1560    /// that ignores the returned error still cannot start anything new.
1561    /// Closing on the wire is the connection layer's job - see
1562    /// [`EndpointError::session_error_code`] for the code it should use.
1563    fn fail_session(&mut self, err: EndpointError) -> EndpointError {
1564        // `on_close` accepts SetupExchange, Active and Draining. A violation
1565        // seen in Connecting or Closed leaves the state machine alone: there
1566        // is no session to close, and the error itself is still the answer.
1567        //
1568        // SetupExchange is in that set because the Termination section says
1569        // "The Transport Session can be terminated at any point", and the
1570        // Setup exchange is a point. So a violation caught while the setup is
1571        // still in flight does close the session, and the discarded result is
1572        // safe because that is one of the states `on_close` accepts.
1573        let _ = self.session.on_close();
1574        err
1575    }
1576
1577    /// Send a SUBSCRIBE message. Allocates a request ID and creates a
1578    /// subscription state machine. Draft-16: simplified, no group_order/
1579    /// filter_type/forward args.
1580    pub fn subscribe(
1581        &mut self,
1582        track_namespace: TrackNamespace,
1583        track_name: Vec<u8>,
1584        parameters: Vec<KeyValuePair>,
1585    ) -> Result<(VarInt, ControlMessage), EndpointError> {
1586        self.require_active_or_err()?;
1587        let req_id = self.request_ids.allocate()?;
1588
1589        let mut sm = SubscriptionStateMachine::new();
1590        sm.on_subscribe_sent()?;
1591        self.subscriptions.insert(req_id.into_inner(), Mutex::new(sm));
1592        // The track is recorded here because this is the only place it is
1593        // known: a SUBSCRIBE_OK names an alias and not the track it is for.
1594        self.track_bindings.insert(
1595            req_id.into_inner(),
1596            TrackBinding {
1597                namespace: track_namespace.clone(),
1598                name: track_name.clone(),
1599                alias: None,
1600                kind: BindingKind::Subscribe,
1601            },
1602        );
1603
1604        let msg = ControlMessage::Subscribe(Subscribe {
1605            request_id: req_id,
1606            track_namespace,
1607            track_name,
1608            parameters,
1609        });
1610        Ok((req_id, msg))
1611    }
1612
1613    /// Process an incoming SUBSCRIBE_OK. Draft-16: has request_id,
1614    /// track_alias, parameters.
1615    pub fn receive_subscribe_ok(&mut self, msg: &SubscribeOk) -> Result<(), EndpointError> {
1616        let id = msg.request_id.into_inner();
1617        if !self.subscriptions.contains_key(&id) {
1618            return Err(EndpointError::UnknownRequest(id));
1619        }
1620        let alias = msg.track_alias.into_inner();
1621        // Judged before the transition, so that the subscription this message
1622        // is about is not yet live and cannot be found as its own conflict,
1623        // and so that a refused SUBSCRIBE_OK leaves no alias behind.
1624        if let Some(conflict) = self.conflicting_alias_for_subscribe_ok(id, alias) {
1625            return Err(self.fail_session(conflict));
1626        }
1627        self.subscription(id).expect("checked above").on_subscribe_ok()?;
1628        if let Some(binding) = self.track_bindings.get_mut(&id) {
1629            binding.alias = Some(alias);
1630        }
1631        Ok(())
1632    }
1633
1634    /// Send an UNSUBSCRIBE message for an active subscription.
1635    ///
1636    /// Section 5.1 gives the subscriber this for a subscription that came
1637    /// either way round, so a request the peer opened with PUBLISH ends here
1638    /// too. The two kinds share a map and cannot collide: a Request ID belongs
1639    /// to whichever endpoint allocated it, and the two halves of the space
1640    /// have opposite least significant bits.
1641    pub fn unsubscribe(&mut self, request_id: VarInt) -> Result<ControlMessage, EndpointError> {
1642        let id = request_id.into_inner();
1643        if let Some(mut sm) = self.subscription(id) {
1644            sm.on_unsubscribe()?;
1645            return Ok(ControlMessage::Unsubscribe(Unsubscribe { request_id }));
1646        }
1647        let mut sm = self.publish_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
1648        sm.on_unsubscribe_sent()?;
1649        Ok(ControlMessage::Unsubscribe(Unsubscribe { request_id }))
1650    }
1651
1652    /// Send a REQUEST_UPDATE narrowing a subscription this endpoint opened.
1653    ///
1654    /// Section 9.11 gives the message to the sender of the request, which is
1655    /// what this endpoint is for every subscription in `subscriptions`. It
1656    /// carries a Request ID of its own and is listed among the messages that
1657    /// step the sequence, so one is allocated here and handed back with the
1658    /// message.
1659    ///
1660    /// # Errors
1661    ///
1662    /// [`EndpointError::UnknownRequest`] when this endpoint opened no
1663    /// subscription under that identifier, and the subscription flow's own
1664    /// `InvalidTransition` when the one it names has already ended.
1665    pub fn request_update(
1666        &mut self,
1667        existing_request_id: VarInt,
1668        parameters: Vec<KeyValuePair>,
1669    ) -> Result<(VarInt, ControlMessage), EndpointError> {
1670        self.require_active_or_err()?;
1671        let id = existing_request_id.into_inner();
1672        self.subscription(id).ok_or(EndpointError::UnknownRequest(id))?.on_subscribe_update()?;
1673        let request_id = self.request_ids.allocate()?;
1674        Ok((
1675            request_id,
1676            ControlMessage::RequestUpdate(RequestUpdate {
1677                request_id,
1678                existing_request_id,
1679                parameters,
1680            }),
1681        ))
1682    }
1683
1684    /// Process an incoming REQUEST_UPDATE.
1685    ///
1686    /// Section 9.11 puts the message in the subscriber's hands, so one that
1687    /// arrives is about a subscription the **peer** opened, and is looked for
1688    /// among those and not among this endpoint's own.
1689    ///
1690    /// # Errors
1691    ///
1692    /// [`EndpointError::UpdateForUnknownRequest`] when the Request ID names no
1693    /// request this session has carried, with the session already moved to
1694    /// Closed, and the subscription flow's own `InvalidTransition` when it
1695    /// names one that has already ended.
1696    pub fn receive_request_update(&mut self, msg: &RequestUpdate) -> Result<(), EndpointError> {
1697        let id = msg.existing_request_id.into_inner();
1698        // Section 9.11's second close, judged before the update is applied to
1699        // anything: "or if the parameters included in the REQUEST_UPDATE are
1700        // invalid for the type of request being modified". An identifier that
1701        // names nothing has no kind to judge against and falls through to the
1702        // first close below.
1703        if let Some(kind) = self.updated_request_kind(id) {
1704            self.check_update_parameters(id, kind, &msg.parameters)?;
1705        }
1706        if let Some(sub) = self.inbound_subscribes.get_mut(&id) {
1707            sub.state.on_request_update_received()?;
1708            return Ok(());
1709        }
1710        // Section 9.11 states the close: "This MUST match the Request ID of an
1711        // existing request. The receiver MUST close the session with
1712        // PROTOCOL_VIOLATION if the sender specifies an invalid Existing
1713        // Request ID, or if the parameters included in the REQUEST_UPDATE are
1714        // invalid for the type of request being modified." This draft names the
1715        // field Existing Request ID and defines it as "the Request ID of the
1716        // request this message is updating", so the width is explicit rather
1717        // than inferred: any existing request, and every request this session
1718        // has carried is one. Only a subscription the peer opened has a
1719        // transition for an update, so for the rest the message is accepted and
1720        // the state left alone. Section 9.19 takes the update away from this
1721        // request kind outright: "the subscriber cannot send REQUEST_UPDATE or
1722        // UNSUBSCRIBE". A track status the peer asked for and one this endpoint
1723        // asked for are the same kind of request and neither is a subscription,
1724        // so both are refused. The check comes before the set below because an
1725        // identifier naming a track status has existed, and being told so is
1726        // what would let it through.
1727        if self.track_statuses.contains_key(&id) || self.inbound_track_statuses.contains_key(&id) {
1728            return Err(EndpointError::NotASubscription(id));
1729        }
1730        let existed = self.subscriptions.contains_key(&id)
1731            || self.publishes.contains_key(&id)
1732            || self.fetches.contains_key(&id)
1733            || self.subscribe_namespaces.contains_key(&id)
1734            || self.publish_namespaces.contains_key(&id);
1735        if existed {
1736            return Ok(());
1737        }
1738        Err(self.fail_session(EndpointError::UpdateForUnknownRequest(id)))
1739    }
1740
1741    /// Which kind of request an identifier names, to the resolution Section
1742    /// 9.2.2's qualifiers need.
1743    ///
1744    /// Section 9.11 lists six kinds that can be updated - SUBSCRIBE, PUBLISH,
1745    /// FETCH, TRACK_STATUS, PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE - and adds
1746    /// that "a subscriber can also send REQUEST_UPDATE to modify parameters of
1747    /// a subscription established with PUBLISH". The qualifiers distinguish
1748    /// only three of those: a subscription, a FETCH, and everything else. A
1749    /// subscription is one however it was established, so a PUBLISH this
1750    /// endpoint answered counts.
1751    fn updated_request_kind(&self, id: u64) -> Option<UpdatedRequest> {
1752        if self.inbound_subscribes.contains_key(&id)
1753            || self.subscriptions.contains_key(&id)
1754            || self.publishes.contains_key(&id)
1755        {
1756            return Some(UpdatedRequest::Subscription);
1757        }
1758        if self.fetches.contains_key(&id) {
1759            return Some(UpdatedRequest::Fetch);
1760        }
1761        if self.track_statuses.contains_key(&id)
1762            || self.inbound_track_statuses.contains_key(&id)
1763            || self.subscribe_namespaces.contains_key(&id)
1764            || self.publish_namespaces.contains_key(&id)
1765        {
1766            return Some(UpdatedRequest::Other);
1767        }
1768        None
1769    }
1770
1771    /// Hold a REQUEST_UPDATE's parameters to the kind of request it names.
1772    ///
1773    /// Section 9.2.2 qualifies four of them. SUBSCRIBER_PRIORITY "MAY appear
1774    /// in a SUBSCRIBE, FETCH, REQUEST_UPDATE (for a subscription or FETCH),
1775    /// PUBLISH_OK message"; SUBSCRIPTION_FILTER, FORWARD and NEW_GROUP_REQUEST
1776    /// each name REQUEST_UPDATE "for a subscription". AUTHORIZATION TOKEN and
1777    /// DELIVERY TIMEOUT name REQUEST_UPDATE with no qualifier at all and are
1778    /// admitted whatever the kind.
1779    ///
1780    /// Every other parameter passes untouched, which is not an oversight.
1781    /// GROUP_ORDER, EXPIRES and LARGEST_OBJECT do not name REQUEST_UPDATE
1782    /// among their message types, and Section 9.2.2 opens by saying a
1783    /// parameter that "appears in some other type of message ... MUST be
1784    /// ignored". Closing the session over one would answer with a violation
1785    /// what this draft answers by ignoring it.
1786    fn check_update_parameters(
1787        &mut self,
1788        id: u64,
1789        kind: UpdatedRequest,
1790        parameters: &[KeyValuePair],
1791    ) -> Result<(), EndpointError> {
1792        for pair in parameters {
1793            let key = pair.key.into_inner();
1794            let admitted = match key {
1795                SUBSCRIBER_PRIORITY_PARAMETER => {
1796                    matches!(kind, UpdatedRequest::Subscription | UpdatedRequest::Fetch)
1797                }
1798                SUBSCRIPTION_FILTER_PARAMETER | FORWARD_PARAMETER | NEW_GROUP_REQUEST_PARAMETER => {
1799                    kind == UpdatedRequest::Subscription
1800                }
1801                _ => true,
1802            };
1803            if !admitted {
1804                return Err(self.fail_session(EndpointError::UpdateParameterNotForKind {
1805                    request_id: id,
1806                    key,
1807                }));
1808            }
1809        }
1810        Ok(())
1811    }
1812
1813    /// Process an incoming PUBLISH_DONE (subscriber side -- publisher
1814    /// finished). Draft-16: has request_id, status_code, stream_count,
1815    /// reason_phrase.
1816    ///
1817    /// Section 5.1 gives the publisher this for a subscription that came
1818    /// either way round, so one the peer opened with PUBLISH ends here too.
1819    pub fn receive_publish_done(&mut self, msg: &PublishDone) -> Result<(), EndpointError> {
1820        let id = msg.request_id.into_inner();
1821        if let Some(mut sm) = self.subscription(id) {
1822            sm.on_publish_done()?;
1823            return Ok(());
1824        }
1825        let mut sm = self.publish_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
1826        sm.on_publish_done_received()?;
1827        Ok(())
1828    }
1829
1830    // -- Answering a SUBSCRIBE the peer sent ------------------------
1831
1832    /// Process an incoming SUBSCRIBE, recording the subscription it opens.
1833    ///
1834    /// The Request ID has already been checked by [`Self::receive_request`],
1835    /// which every request message passes through before its own handler.
1836    /// There is no Track Alias to judge here: Section 9.10 carries it in the
1837    /// SUBSCRIBE_OK, so this endpoint chooses it, and it is judged when the
1838    /// answer is built.
1839    ///
1840    /// # Errors
1841    ///
1842    /// The session error when the session is not established, and the
1843    /// subscription flow's own `InvalidTransition` for a second SUBSCRIBE
1844    /// under a Request ID already carrying one.
1845    pub fn receive_subscribe(&mut self, msg: &message::Subscribe) -> Result<(), EndpointError> {
1846        self.require_active_or_err()?;
1847        let id = msg.request_id.into_inner();
1848        let mut state = SubscriptionStateMachine::new();
1849        state.on_subscribe_received()?;
1850        self.inbound_subscribes.insert(id, InboundSubscribe { message: msg.clone(), state });
1851        Ok(())
1852    }
1853
1854    /// The SUBSCRIBE the peer sent under `request_id` and this endpoint has
1855    /// not answered yet.
1856    ///
1857    /// `None` once it has been answered, and for an identifier this session
1858    /// has no inbound subscription for. The record itself lives on for as long
1859    /// as the session does, which is what lets an update say whether the
1860    /// request it names has ever existed.
1861    pub fn pending_subscribe(&self, request_id: VarInt) -> Option<&message::Subscribe> {
1862        self.inbound_subscribes
1863            .get(&request_id.into_inner())
1864            .filter(|s| s.state.state() == SubscriptionState::Subscribing)
1865            .map(|s| &s.message)
1866    }
1867
1868    /// How many SUBSCRIBEs the peer has sent that are still waiting for an
1869    /// answer.
1870    pub fn pending_subscribe_count(&self) -> usize {
1871        self.inbound_subscribes
1872            .values()
1873            .filter(|s| s.state.state() == SubscriptionState::Subscribing)
1874            .count()
1875    }
1876
1877    /// Build the SUBSCRIBE_OK accepting a subscription the peer opened, giving
1878    /// its track a Track Alias.
1879    ///
1880    /// # Errors
1881    ///
1882    /// [`EndpointError::UnknownRequest`] if the peer opened no subscription
1883    /// under that identifier, [`EndpointError::TrackAliasInUse`] if a live
1884    /// track of this endpoint's already holds the alias, and
1885    /// [`EndpointError::Subscription`] if the request has already been
1886    /// answered: Section 5.1 says "A publisher MUST send exactly one
1887    /// SUBSCRIBE_OK or REQUEST_ERROR in response to a SUBSCRIBE."
1888    pub fn send_subscribe_ok(
1889        &mut self,
1890        request_id: VarInt,
1891        track_alias: VarInt,
1892        track_extensions: Vec<KeyValuePair>,
1893        parameters: Vec<KeyValuePair>,
1894    ) -> Result<ControlMessage, EndpointError> {
1895        let id = request_id.into_inner();
1896        let alias = track_alias.into_inner();
1897        let sub = self.inbound_subscribes.get(&id).ok_or(EndpointError::UnknownRequest(id))?;
1898        let namespace = sub.message.track_namespace.clone();
1899        let name = sub.message.track_name.clone();
1900        if let Some(refusal) = self.alias_held_elsewhere(alias, &namespace, &name) {
1901            return Err(refusal);
1902        }
1903        let sub = self.inbound_subscribes.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
1904        sub.state.on_subscribe_ok_sent()?;
1905        self.track_bindings.insert(
1906            id,
1907            TrackBinding { namespace, name, alias: Some(alias), kind: BindingKind::PeerSubscribe },
1908        );
1909        Ok(ControlMessage::SubscribeOk(message::SubscribeOk {
1910            request_id,
1911            track_alias,
1912            track_extensions,
1913            parameters,
1914        }))
1915    }
1916
1917    /// Build the REQUEST_ERROR refusing a request the peer opened.
1918    ///
1919    /// One message refuses more than one kind of request, so the record it ends
1920    /// is found by the identifier: the maps are keyed by Request ID and one id
1921    /// belongs to one request. A subscription, a fetch, an announcement the peer
1922    /// made and a track status it asked for all end here.
1923    ///
1924    /// Section 9.19: "A publisher responds to a failed TRACK_STATUS with an
1925    /// appropriate REQUEST_ERROR message."
1926    ///
1927    /// Section 9.16.2 names the code a Joining Fetch naming an unjoinable
1928    /// subscription is refused with, so that refusal cannot go out under any
1929    /// other.
1930    ///
1931    /// # Errors
1932    ///
1933    /// [`EndpointError::UnknownRequest`] if the peer opened no subscription, no
1934    /// fetch and no announcement under that identifier,
1935    /// [`EndpointError::WrongJoiningRefusal`] if a Joining Fetch is refused
1936    /// under the wrong code, and the flow's own `InvalidTransition` if the
1937    /// request has already been answered.
1938    pub fn send_request_error(
1939        &mut self,
1940        request_id: VarInt,
1941        error_code: VarInt,
1942        retry_interval: VarInt,
1943        reason_phrase: Vec<u8>,
1944    ) -> Result<ControlMessage, EndpointError> {
1945        let id = request_id.into_inner();
1946        if self.inbound_subscribes.contains_key(&id) {
1947            let sub =
1948                self.inbound_subscribes.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
1949            sub.state.on_request_error_sent()?;
1950        } else if let Some(fetch) = self.inbound_fetches.get_mut(&id) {
1951            if fetch.unjoinable.is_some() {
1952                let required = RequestErrorCode::InvalidJoiningRequestId as u64;
1953                if error_code.into_inner() != required {
1954                    return Err(EndpointError::WrongJoiningRefusal { fetch: id, required });
1955                }
1956            }
1957            fetch.state.on_fetch_error_sent()?;
1958        } else if let Some(ann) = self.inbound_publish_namespaces.get_mut(&id) {
1959            ann.state.on_publish_namespace_error_sent()?;
1960        } else if let Some(req) = self.inbound_track_statuses.get_mut(&id) {
1961            req.state.on_track_status_error_sent()?;
1962        } else {
1963            return Err(EndpointError::UnknownRequest(id));
1964        }
1965        Ok(ControlMessage::RequestError(message::RequestError {
1966            request_id,
1967            error_code,
1968            retry_interval,
1969            reason_phrase,
1970        }))
1971    }
1972
1973    /// Process an incoming UNSUBSCRIBE, ending a subscription this endpoint
1974    /// publishes and freeing the Track Alias it held.
1975    /// Section 9.12: "A Subscriber issues an UNSUBSCRIBE message to a Publisher
1976    /// indicating it is no longer interested in receiving the specified Track,
1977    /// indicating that the Publisher stop sending Objects as soon as possible."
1978    /// The message travels from subscriber to publisher, so what it can end is
1979    /// whatever this endpoint publishes - and Section 5.1 gives that two
1980    /// sources, not one: "A subscription can be initiated and moved to the
1981    /// Pending state by either a publisher or a subscriber."
1982    ///
1983    /// # Errors
1984    ///
1985    /// [`EndpointError::UnknownRequest`] if this endpoint publishes no
1986    /// subscription under that identifier, [`EndpointError::NotASubscription`]
1987    /// for one that names a track status, [`EndpointError::Subscription`] if
1988    /// it is one the peer opened that this endpoint never accepted or has
1989    /// already ended, and [`EndpointError::PublishFlow`] for the same of one
1990    /// this endpoint opened.
1991    pub fn receive_unsubscribe(&mut self, msg: &message::Unsubscribe) -> Result<(), EndpointError> {
1992        let id = msg.request_id.into_inner();
1993        // The other half of the same sentence. Section 9.19: "the subscriber
1994        // cannot send REQUEST_UPDATE or UNSUBSCRIBE". Refused by name rather than left
1995        // to the miss below, which would say the identifier names nothing when
1996        // it names a request this session is carrying.
1997        if self.track_statuses.contains_key(&id) || self.inbound_track_statuses.contains_key(&id) {
1998            return Err(EndpointError::NotASubscription(id));
1999        }
2000        // The offers this endpoint made itself. `publishes` holds both
2001        // directions and the offer's own message is what tells them apart: one
2002        // the peer made was written down when it arrived and one of this
2003        // endpoint's never was. A peer sending UNSUBSCRIBE for its own PUBLISH
2004        // is the publisher ending a subscription the sentence above gives the
2005        // subscriber, so it falls to the miss below rather than being taken.
2006        if !self.inbound_publishes.contains_key(&id) {
2007            if let Some(mut sm) = self.publish_flow(id) {
2008                sm.on_unsubscribe_received()?;
2009                return Ok(());
2010            }
2011        }
2012        let sub = self.inbound_subscribes.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2013        sub.state.on_unsubscribe_received()?;
2014        Ok(())
2015    }
2016
2017    // -- Fetch flow -------------------------------------------------
2018
2019    /// Send a standalone FETCH message. Allocates a request ID and creates a
2020    /// fetch state machine. Draft-16: has end_object.
2021    /// `parameters` are the request's own, as they are on every other request
2022    /// this endpoint makes. FETCH carried an empty list on all five of these
2023    /// drafts while SUBSCRIBE, TRACK_STATUS, PUBLISH_NAMESPACE and PUBLISH
2024    /// took the caller's, which made it the only one an application could not
2025    /// attach an authorization token to.
2026    #[allow(clippy::too_many_arguments)]
2027    pub fn fetch(
2028        &mut self,
2029        track_namespace: TrackNamespace,
2030        track_name: Vec<u8>,
2031        start_group: VarInt,
2032        start_object: VarInt,
2033        end_group: VarInt,
2034        end_object: VarInt,
2035        parameters: Vec<KeyValuePair>,
2036    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2037        self.require_active_or_err()?;
2038        let req_id = self.request_ids.allocate()?;
2039
2040        let mut sm = FetchStateMachine::new();
2041        sm.on_fetch_sent()?;
2042        self.fetches.insert(req_id.into_inner(), Mutex::new(sm));
2043        // Before the two of them are moved into the message below, which is
2044        // where they would otherwise go and stay: a fetch that has left no
2045        // track behind is one no withdrawal can find.
2046        self.fetch_tracks.insert(
2047            req_id.into_inner(),
2048            FetchTrack { namespace: track_namespace.clone(), name: track_name.clone() },
2049        );
2050
2051        let msg = ControlMessage::Fetch(Fetch {
2052            request_id: req_id,
2053            fetch_type: FetchType::Standalone,
2054            fetch_payload: FetchPayload::Standalone {
2055                track_namespace,
2056                track_name,
2057                start_group,
2058                start_object,
2059                end_group,
2060                end_object,
2061            },
2062            parameters,
2063        });
2064        Ok((req_id, msg))
2065    }
2066
2067    /// Send a Relative Joining Fetch (Fetch Type 0x2). Allocates a request ID.
2068    ///
2069    /// `joining_start` is an offset rather than a group number: draft-16
2070    /// Section 9.16.2.1 has the publisher set the Start Location to
2071    /// "{Subscribe Largest Location.Group - Joining Start, 0}". To name the starting group
2072    /// outright, use [`absolute_joining_fetch`](Self::absolute_joining_fetch).
2073    ///
2074    /// `parameters` are the request's own, as they are on every other request
2075    /// this endpoint makes.
2076    pub fn joining_fetch(
2077        &mut self,
2078        joining_request_id: VarInt,
2079        joining_start: VarInt,
2080        parameters: Vec<KeyValuePair>,
2081    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2082        self.joining_fetch_of_type(
2083            FetchType::RelativeJoining,
2084            joining_request_id,
2085            joining_start,
2086            parameters,
2087        )
2088    }
2089
2090    /// Send an Absolute Joining Fetch (Fetch Type 0x3). Allocates a request ID.
2091    ///
2092    /// Draft-16 Section 9.16.2.1: "For an Absolute Joining Fetch, the
2093    /// publisher sets the Start Location to {Joining Start, 0}." So
2094    /// `joining_start` is the group to begin at rather than a count back from
2095    /// one, and Section 9.16.2 leaves the choice with the subscriber: "The
2096    /// subscriber can set the Start Location to an absolute Location or a
2097    /// Location relative to the current group." Only the relative form could be sent
2098    /// before, so an application that knew which group it wanted had to
2099    /// express it as an offset from a largest group it may never have been
2100    /// told.
2101    pub fn absolute_joining_fetch(
2102        &mut self,
2103        joining_request_id: VarInt,
2104        joining_start: VarInt,
2105        parameters: Vec<KeyValuePair>,
2106    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2107        self.joining_fetch_of_type(
2108            FetchType::AbsoluteJoining,
2109            joining_request_id,
2110            joining_start,
2111            parameters,
2112        )
2113    }
2114
2115    /// The two above, which differ in the Fetch Type and in what the publisher
2116    /// then reads Joining Start as. Nothing else about the message changes.
2117    fn joining_fetch_of_type(
2118        &mut self,
2119        fetch_type: FetchType,
2120        joining_request_id: VarInt,
2121        joining_start: VarInt,
2122        parameters: Vec<KeyValuePair>,
2123    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2124        self.require_active_or_err()?;
2125        let req_id = self.request_ids.allocate()?;
2126
2127        let mut sm = FetchStateMachine::new();
2128        sm.on_fetch_sent()?;
2129        self.fetches.insert(req_id.into_inner(), Mutex::new(sm));
2130        // Resolved through the join, once, here. Section 9.16.2 has the
2131        // publisher take the Track Namespace and Track Name from the
2132        // subscription this names, so that is the track, and a Request ID this
2133        // session holds no track for leaves the fetch out of the record
2134        // entirely rather than putting a guess in it.
2135        if let Some(binding) = self.track_bindings.get(&joining_request_id.into_inner()) {
2136            let track =
2137                FetchTrack { namespace: binding.namespace.clone(), name: binding.name.clone() };
2138            self.fetch_tracks.insert(req_id.into_inner(), track);
2139        }
2140
2141        let msg = ControlMessage::Fetch(Fetch {
2142            request_id: req_id,
2143            fetch_type,
2144            fetch_payload: FetchPayload::Joining { joining_request_id, joining_start },
2145            parameters,
2146        });
2147        Ok((req_id, msg))
2148    }
2149
2150    /// Process an incoming FETCH_OK. Draft-16: has request_id, end_of_track,
2151    /// end_group, end_object, parameters.
2152    pub fn receive_fetch_ok(&mut self, msg: &message::FetchOk) -> Result<(), EndpointError> {
2153        let id = msg.request_id.into_inner();
2154        let mut sm = self.fetch_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
2155        sm.on_fetch_ok()?;
2156        Ok(())
2157    }
2158
2159    /// Send a FETCH_CANCEL message.
2160    pub fn fetch_cancel(&mut self, request_id: VarInt) -> Result<ControlMessage, EndpointError> {
2161        let id = request_id.into_inner();
2162        let mut sm = self.fetch_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
2163        sm.on_fetch_cancel()?;
2164        Ok(ControlMessage::FetchCancel(FetchCancel { request_id }))
2165    }
2166
2167    /// Notify that a fetch data stream received FIN.
2168    ///
2169    /// It may arrive before the FETCH_OK or REQUEST_ERROR answering the
2170    /// request, which leaves the fetch in `FetchState::Unanswered` until the
2171    /// answer lands.
2172    pub fn on_fetch_stream_fin(&mut self, request_id: VarInt) -> Result<(), EndpointError> {
2173        let id = request_id.into_inner();
2174        let mut sm = self.fetch_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
2175        sm.on_stream_fin()?;
2176        Ok(())
2177    }
2178
2179    /// Notify that a fetch data stream was reset.
2180    ///
2181    /// As with a FIN, it may arrive before the answer to the request.
2182    pub fn on_fetch_stream_reset(&mut self, request_id: VarInt) -> Result<(), EndpointError> {
2183        let id = request_id.into_inner();
2184        let mut sm = self.fetch_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
2185        sm.on_stream_reset()?;
2186        Ok(())
2187    }
2188
2189    // -- Answering a FETCH the peer sent ----------------------------
2190
2191    /// Process an incoming FETCH, recording the fetch it opens.
2192    ///
2193    /// The Request ID has already been checked by [`Self::receive_request`],
2194    /// which every request message passes through before its own handler.
2195    ///
2196    /// A Joining Fetch is recorded like any other. Section 9.16.2 answers one
2197    /// naming a subscription this session cannot join with a refusal and not
2198    /// a session close, and a refusal is a message this endpoint has to
2199    /// build, so the request it refuses has to be on record first.
2200    ///
2201    /// # Errors
2202    ///
2203    /// The session error when the session is not established, and the fetch
2204    /// flow's own `InvalidTransition` for a second FETCH under an identifier
2205    /// already carrying one.
2206    pub fn receive_fetch(&mut self, msg: &Fetch) -> Result<(), EndpointError> {
2207        self.require_active_or_err()?;
2208        let id = msg.request_id.into_inner();
2209        let unjoinable = self.joining_subscription_missing(msg);
2210        let mut state = FetchStateMachine::new();
2211        state.on_fetch_received()?;
2212        self.inbound_fetches.insert(id, InboundFetch { message: msg.clone(), state, unjoinable });
2213        Ok(())
2214    }
2215
2216    /// The FETCH the peer sent under `request_id` and this endpoint has not
2217    /// answered yet.
2218    ///
2219    /// `None` once it has been answered, and for an identifier this session
2220    /// has no inbound fetch for. The record itself lives on past the answer,
2221    /// because the fetch is not over until its data stream is.
2222    pub fn pending_fetch(&self, request_id: VarInt) -> Option<&Fetch> {
2223        self.inbound_fetches
2224            .get(&request_id.into_inner())
2225            .filter(|f| matches!(f.state.state(), FetchState::Pending | FetchState::Unanswered))
2226            .map(|f| &f.message)
2227    }
2228
2229    /// How many FETCHes the peer has sent that are still waiting for an
2230    /// answer.
2231    pub fn pending_fetch_count(&self) -> usize {
2232        self.inbound_fetches
2233            .values()
2234            .filter(|f| matches!(f.state.state(), FetchState::Pending | FetchState::Unanswered))
2235            .count()
2236    }
2237
2238    /// The identifier an arriving Joining Fetch names, when this session has
2239    /// no subscription it may join.
2240    ///
2241    /// Section 9.16.2:
2242    /// "If a publisher receives a Joining Fetch with a Request ID that
2243    /// does not correspond to a subscription in the same session in the
2244    /// Established or Pending (subscriber) states, it MUST return a
2245    /// REQUEST_ERROR with error code INVALID_JOINING_REQUEST_ID."
2246    ///
2247    /// The verdict is taken as the FETCH arrives, because that is the moment
2248    /// the sentence names, and it is kept. A subscription that ends between
2249    /// the FETCH and its answer does not turn a fetch that could be joined
2250    /// into one that could not.
2251    ///
2252    /// A standalone fetch names none and answers `None`, and so does a joining
2253    /// one whose subscription is live. The subscription is one the peer opened,
2254    /// because the peer is the end that fetches and this endpoint is the one
2255    /// answering.
2256    /// A subscription established by a PUBLISH counts: Section 5.1 says the
2257    /// Largest Location saved "in PUBLISH or SUBSCRIBE_OK when establishing a
2258    /// subscription" is the one a Joining FETCH uses. Draft-15 Section 9.16.2
2259    /// and the drafts before it say "an existing Subscribe" and reach only what
2260    /// a SUBSCRIBE opened.
2261    fn joining_subscription_missing(&self, msg: &Fetch) -> Option<u64> {
2262        let message::FetchPayload::Joining { joining_request_id: joined, .. } = &msg.fetch_payload
2263        else {
2264            return None;
2265        };
2266        let joined = joined.into_inner();
2267        let live = self
2268            .inbound_subscribes
2269            .get(&joined)
2270            .is_some_and(|s| s.state.state() != SubscriptionState::Done)
2271            || self.publish_flow(joined).is_some_and(|p| p.state() != PublishState::Done);
2272        if live {
2273            None
2274        } else {
2275            Some(joined)
2276        }
2277    }
2278
2279    /// Build the FETCH_OK accepting a fetch the peer opened.
2280    ///
2281    /// # Errors
2282    ///
2283    /// [`EndpointError::UnknownRequest`] if the peer opened no fetch under
2284    /// that identifier, [`EndpointError::UnjoinableSubscription`] for a
2285    /// Joining Fetch naming a subscription this session cannot join, and the
2286    /// fetch flow's own `InvalidTransition` for a second answer: Section 5.2
2287    /// says the publisher "MUST send exactly one FETCH_OK or REQUEST_ERROR in
2288    /// response to a FETCH".
2289    pub fn send_fetch_ok(
2290        &mut self,
2291        request_id: VarInt,
2292        end_of_track: u8,
2293        end_group: VarInt,
2294        end_object: VarInt,
2295        parameters: Vec<KeyValuePair>,
2296        track_extensions: Vec<KeyValuePair>,
2297    ) -> Result<ControlMessage, EndpointError> {
2298        let id = request_id.into_inner();
2299        let unjoinable =
2300            self.inbound_fetches.get(&id).ok_or(EndpointError::UnknownRequest(id))?.unjoinable;
2301        if let Some(joining) = unjoinable {
2302            return Err(EndpointError::UnjoinableSubscription { fetch: id, joining });
2303        }
2304        let fetch = self.inbound_fetches.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2305        fetch.state.on_fetch_ok_sent()?;
2306        Ok(ControlMessage::FetchOk(message::FetchOk {
2307            request_id,
2308            end_of_track,
2309            end_group,
2310            end_object,
2311            parameters,
2312            track_extensions,
2313        }))
2314    }
2315
2316    /// Process an incoming FETCH_CANCEL, ending the fetch the peer opened.
2317    ///
2318    /// Section 9.18: the subscriber sends it to stop a fetch it no longer
2319    /// wants, so the record this endpoint serves the fetch from is the one it
2320    /// ends.
2321    ///
2322    /// # Errors
2323    ///
2324    /// [`EndpointError::UnknownRequest`] if the peer opened no fetch under
2325    /// that identifier, and the fetch flow's own `InvalidTransition` for a fetch
2326    /// that has already ended.
2327    pub fn receive_fetch_cancel(&mut self, msg: &FetchCancel) -> Result<(), EndpointError> {
2328        let id = msg.request_id.into_inner();
2329        let fetch = self.inbound_fetches.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2330        fetch.state.on_fetch_cancel_received()?;
2331        Ok(())
2332    }
2333
2334    /// Note that this endpoint finished the data stream serving a fetch the
2335    /// peer opened.
2336    ///
2337    /// A fetch is over when its answer and its data stream have both settled,
2338    /// and this is the second of those for the end that serves it.
2339    ///
2340    /// # Errors
2341    ///
2342    /// [`EndpointError::UnknownRequest`] if the peer opened no fetch under
2343    /// that identifier, and the fetch flow's own `InvalidTransition` from a state
2344    /// the stream cannot close from.
2345    pub fn on_peer_fetch_stream_fin(&mut self, request_id: VarInt) -> Result<(), EndpointError> {
2346        let id = request_id.into_inner();
2347        let fetch = self.inbound_fetches.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2348        fetch.state.on_stream_fin_sent()?;
2349        Ok(())
2350    }
2351
2352    // -- Subscribe Namespace flow -----------------------------------
2353
2354    /// Send a SUBSCRIBE_NAMESPACE message. Draft-16 adds `subscribe_options`
2355    /// (0=PUBLISH, 1=NAMESPACE, 2=both).
2356    ///
2357    /// Section 9.25 addresses the first half of the overlap rule to this end
2358    /// of the session: "A subscriber cannot make overlapping namespace
2359    /// subscriptions on a single session." So a prefix overlapping one this
2360    /// endpoint has already asked about is refused here rather than built and
2361    /// sent for the peer to refuse.
2362    ///
2363    /// # Errors
2364    ///
2365    /// The session error when the session is not established,
2366    /// [`EndpointError::OwnPrefixOverlap`] when the prefix overlaps one this
2367    /// endpoint is already subscribed to, and the Request ID allocator's own
2368    /// error when the peer has granted no room for another request.
2369    pub fn subscribe_namespace(
2370        &mut self,
2371        namespace_prefix: TrackNamespace,
2372        subscribe_options: VarInt,
2373        parameters: Vec<KeyValuePair>,
2374    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2375        self.require_active_or_err()?;
2376        // The subscriber's half of the rule, refused before the message
2377        // exists. A publisher that follows this draft answers it with a
2378        // refusal, so building it spends a Request ID on a namespace
2379        // subscription that is not going to open.
2380        if let Some(established) = self.own_prefix_overlap(&namespace_prefix) {
2381            return Err(EndpointError::OwnPrefixOverlap { established });
2382        }
2383        let req_id = self.request_ids.allocate()?;
2384
2385        let mut sm = SubscribeNamespaceStateMachine::new();
2386        sm.on_subscribe_namespace_sent()?;
2387        self.subscribe_namespaces.insert(req_id.into_inner(), sm);
2388        self.subscribe_namespace_prefixes.insert(req_id.into_inner(), namespace_prefix.clone());
2389
2390        let msg = ControlMessage::SubscribeNamespace(SubscribeNamespace {
2391            request_id: req_id,
2392            namespace_prefix,
2393            subscribe_options,
2394            parameters,
2395        });
2396        Ok((req_id, msg))
2397    }
2398
2399    /// Register a SUBSCRIBE_NAMESPACE the **peer** opened a bidirectional
2400    /// stream with, and hand back the Request ID it carries.
2401    ///
2402    /// The mirror of [`subscribe_namespace`](Self::subscribe_namespace): that
2403    /// one builds the request this endpoint writes on a stream it opened, this
2404    /// one takes the request that arrived on a stream the peer opened.
2405    ///
2406    /// # Two refusals, two codes
2407    ///
2408    /// Section 3.3, on a bidirectional stream that begins with anything else:
2409    /// "Bidirectional streams MUST NOT begin with any other message type
2410    /// unless negotiated. If they do, the peer MUST close the Session with a
2411    /// Protocol Violation." Section 9.1 states a different rule for the
2412    /// Request ID the stream's first message carries: "If an endpoint receives
2413    /// a Request ID that is not valid for the peer, or a new request with a
2414    /// Request ID that is not the next in sequence or exceeds the received
2415    /// MAX_REQUEST_ID, it MUST close the session with INVALID_REQUEST_ID."
2416    /// [`validate_peer_request_id`](Self::validate_peer_request_id) is what
2417    /// holds it to that, and it answers with its own errors rather than this
2418    /// one, so a peer reading close codes can tell the two rules apart.
2419    ///
2420    /// # Why the state is shared and the request is not
2421    ///
2422    /// A Request ID the peer allocated and one this endpoint allocated have
2423    /// opposite least significant bits, so the two can never collide.
2424    /// [`validate_peer_request_id`](Self::validate_peer_request_id) is what
2425    /// enforces that, and it runs before the insert, so one map of state
2426    /// machines holds both directions safely. The request itself is another
2427    /// matter: the prefix it names is not in a state machine, and it is what
2428    /// an answer is about and what a later request is judged against, so it
2429    /// is kept beside the state and read back through
2430    /// [`pending_subscribe_namespace`](Self::pending_subscribe_namespace).
2431    pub fn receive_subscribe_namespace_on_stream(
2432        &mut self,
2433        msg: &ControlMessage,
2434    ) -> Result<VarInt, EndpointError> {
2435        let ControlMessage::SubscribeNamespace(m) = msg else {
2436            return Err(self.refuse_non_subscribe_namespace(msg.message_type()));
2437        };
2438        self.require_active_or_err()?;
2439        self.validate_peer_request_id(m.request_id.into_inner())?;
2440
2441        if let Some(established) = self.peer_prefix_overlap(&m.namespace_prefix) {
2442            self.overlapping_namespace_subscriptions.insert(m.request_id.into_inner(), established);
2443        }
2444        let mut sm = SubscribeNamespaceStateMachine::new();
2445        sm.on_subscribe_namespace_received()?;
2446        self.subscribe_namespaces.insert(m.request_id.into_inner(), sm);
2447        // The request as it arrived. The state machine above says how far it
2448        // has got; what it cannot say is which namespace prefix was asked
2449        // for.
2450        self.inbound_subscribe_namespaces.insert(m.request_id.into_inner(), m.clone());
2451        Ok(m.request_id)
2452    }
2453
2454    /// The earliest namespace subscription the peer has made whose prefix
2455    /// overlaps `prefix`, and `None` when there is none.
2456    ///
2457    /// Only ones that have not ended count: the sentence weighs the arriving
2458    /// prefix against "an established namespace subscription", so one the
2459    /// peer has withdrawn and one this endpoint refused are both past. Drafts
2460    /// 07 through 11 say "an earlier" instead and count those too.
2461    ///
2462    /// One that has arrived and has not been answered does count. It is not
2463    /// established yet, but this endpoint is the one about to establish it,
2464    /// and accepting both would leave the session holding exactly the pair
2465    /// the sentence exists to prevent.
2466    ///
2467    /// Namespace subscriptions this endpoint made are a separate set and are
2468    /// not consulted. This endpoint is the subscriber for those, so a prefix
2469    /// it asked about says nothing about what the peer may ask about.
2470    ///
2471    /// The lowest Request ID wins when more than one overlaps, so the answer
2472    /// does not depend on the order a map happens to iterate in. Identifiers
2473    /// are handed out in increasing order, so the lowest of them is the
2474    /// earliest request.
2475    fn peer_prefix_overlap(&self, prefix: &TrackNamespace) -> Option<u64> {
2476        self.inbound_subscribe_namespaces
2477            .iter()
2478            .filter(|(id, _)| {
2479                self.subscribe_namespaces
2480                    .get(id)
2481                    .is_some_and(|sm| sm.state() != SubscribeNamespaceState::Done)
2482            })
2483            .filter_map(|(&id, m)| prefixes_overlap(&m.namespace_prefix.0, &prefix.0).then_some(id))
2484            .min()
2485    }
2486
2487    /// The earliest namespace subscription **this endpoint** has made whose
2488    /// prefix overlaps `prefix`, and `None` when there is none.
2489    ///
2490    /// The subscriber's half of the same sentence reads over this endpoint's
2491    /// own requests, and takes the same view of which of them are past as
2492    /// [`Self::peer_prefix_overlap`] takes of the peer's.
2493    fn own_prefix_overlap(&self, prefix: &TrackNamespace) -> Option<u64> {
2494        self.subscribe_namespace_prefixes
2495            .iter()
2496            .filter_map(|(&id, key)| prefixes_overlap(&key.0, &prefix.0).then_some(id))
2497            .filter(|id| {
2498                self.subscribe_namespaces
2499                    .get(id)
2500                    .is_some_and(|sm| sm.state() != SubscribeNamespaceState::Done)
2501            })
2502            .min()
2503    }
2504
2505    /// The SUBSCRIBE_NAMESPACE the peer sent under `request_id` and this
2506    /// endpoint has not answered yet.
2507    ///
2508    /// `None` once it has been answered, and for an identifier the peer has
2509    /// opened no namespace subscription under -- one of this endpoint's own
2510    /// included, whose state is in the same map but whose message never
2511    /// arrived here. The record itself lives on past the answer, because a
2512    /// namespace subscription that was accepted lasts as long as its stream
2513    /// does.
2514    pub fn pending_subscribe_namespace(&self, request_id: VarInt) -> Option<&SubscribeNamespace> {
2515        let id = request_id.into_inner();
2516        let unanswered = self
2517            .subscribe_namespaces
2518            .get(&id)
2519            .is_some_and(|sm| sm.state() == SubscribeNamespaceState::Pending);
2520        self.inbound_subscribe_namespaces.get(&id).filter(|_| unanswered)
2521    }
2522
2523    /// How many namespace subscriptions the peer has opened that are still
2524    /// waiting for an answer.
2525    pub fn pending_subscribe_namespace_count(&self) -> usize {
2526        self.inbound_subscribe_namespaces
2527            .keys()
2528            .filter(|id| {
2529                self.subscribe_namespaces
2530                    .get(id)
2531                    .is_some_and(|sm| sm.state() == SubscribeNamespaceState::Pending)
2532            })
2533            .count()
2534    }
2535    /// Close the session over a bidirectional stream that began with the wrong
2536    /// message type, and return the error that says so.
2537    ///
2538    /// Separate from
2539    /// [`receive_subscribe_namespace_on_stream`](Self::receive_subscribe_namespace_on_stream)
2540    /// because the connection classifies the first message before it has a
2541    /// request to register, and the close has to happen either way.
2542    pub fn refuse_non_subscribe_namespace(&mut self, ty: MessageType) -> EndpointError {
2543        self.fail_session(EndpointError::NotASubscribeNamespace(ty))
2544    }
2545
2546    /// Dispatch a message that arrived on the response half of the namespace
2547    /// subscription `request_id` opened.
2548    ///
2549    /// Four messages travel here and no others. REQUEST_OK and REQUEST_ERROR
2550    /// are the answer — Section 9.25: "The publisher will respond with
2551    /// REQUEST_OK or REQUEST_ERROR on the response half of the stream." Then
2552    /// NAMESPACE reports a matching namespace and NAMESPACE_DONE withdraws
2553    /// one, both on the same stream and only "if they are requested".
2554    ///
2555    /// # Why the stream decides which request, and not the message
2556    ///
2557    /// NAMESPACE and NAMESPACE_DONE carry no Request ID. What they carry is a
2558    /// Track Namespace **Suffix**, relative to a prefix that only the
2559    /// subscription knows, so the stream is the only thing that says which
2560    /// subscription they belong to.
2561    ///
2562    /// REQUEST_OK and REQUEST_ERROR do carry one, which names the request
2563    /// twice over. When the two agree the id is redundant; when they disagree
2564    /// nothing here can tell which the sender meant, and the draft does not
2565    /// say, so neither is acted on and
2566    /// [`EndpointError::ResponseIdMismatch`] comes back. That is a refusal of
2567    /// one message and not a session close, because no sentence asks for one.
2568    pub fn receive_on_namespace_stream(
2569        &mut self,
2570        request_id: VarInt,
2571        msg: &ControlMessage,
2572    ) -> Result<(), EndpointError> {
2573        let id = request_id.into_inner();
2574        match msg {
2575            ControlMessage::RequestOk(m) => {
2576                self.require_same_request(id, m.request_id)?;
2577                let sm = self
2578                    .subscribe_namespaces
2579                    .get_mut(&id)
2580                    .ok_or(EndpointError::UnknownRequest(id))?;
2581                sm.on_subscribe_namespace_ok()?;
2582                Ok(())
2583            }
2584            ControlMessage::RequestError(m) => {
2585                self.require_same_request(id, m.request_id)?;
2586                let sm = self
2587                    .subscribe_namespaces
2588                    .get_mut(&id)
2589                    .ok_or(EndpointError::UnknownRequest(id))?;
2590                sm.on_subscribe_namespace_error()?;
2591                Ok(())
2592            }
2593            // Reports about the namespaces the subscription asked for. They
2594            // move no state: a namespace appearing and being withdrawn is the
2595            // subscription doing its job rather than the subscription
2596            // changing. The arms are here so the dispatcher is total, and so
2597            // that the two messages have somewhere to arrive other than the
2598            // control stream, which refuses them.
2599            ControlMessage::Namespace(_) | ControlMessage::NamespaceDone(_) => {
2600                if self.subscribe_namespaces.contains_key(&id) {
2601                    Ok(())
2602                } else {
2603                    Err(EndpointError::UnknownRequest(id))
2604                }
2605            }
2606            other => Err(self.refuse_non_subscribe_namespace(other.message_type())),
2607        }
2608    }
2609
2610    /// Refuse a response whose own Request ID is not the one its stream
2611    /// carries.
2612    fn require_same_request(&self, stream: u64, message: VarInt) -> Result<(), EndpointError> {
2613        if message.into_inner() == stream {
2614            Ok(())
2615        } else {
2616            Err(EndpointError::ResponseIdMismatch { stream, message: message.into_inner() })
2617        }
2618    }
2619
2620    /// Drive the state machine for the answer this endpoint is about to write
2621    /// on a namespace subscription stream the peer opened.
2622    ///
2623    /// The mirror of
2624    /// [`receive_on_namespace_stream`](Self::receive_on_namespace_stream), and
2625    /// the reason the transitions it drives are named `*_sent`: the state
2626    /// edges coincide with the received side, so a mis-dispatch would succeed
2627    /// silently instead of naming the wrong event in an `InvalidTransition`.
2628    ///
2629    /// The caller writes the message only after this returns `Ok`. What it
2630    /// cannot undo is the opposite order: a write that fails afterwards leaves
2631    /// the state machine one step ahead of the wire.
2632    ///
2633    /// `refusal_code` is `None` for a REQUEST_OK and the code the REQUEST_ERROR
2634    /// will carry otherwise, because Section 9.25 names the code a refusal for
2635    /// an overlapping prefix must go out under and a boolean cannot carry it.
2636    ///
2637    /// # Errors
2638    ///
2639    /// [`EndpointError::UnknownRequest`] if no namespace subscription carries
2640    /// that identifier, [`EndpointError::PeerPrefixOverlap`] if the request
2641    /// overlaps another and this is an acceptance,
2642    /// [`EndpointError::WrongOverlapRefusal`] if it overlaps another and the
2643    /// refusal names a different code, and the namespace flow's own
2644    /// `InvalidTransition` for one already answered.
2645    pub fn respond_on_namespace_stream(
2646        &mut self,
2647        request_id: VarInt,
2648        refusal_code: Option<VarInt>,
2649    ) -> Result<(), EndpointError> {
2650        let id = request_id.into_inner();
2651        if let Some(&established) = self.overlapping_namespace_subscriptions.get(&id) {
2652            match refusal_code {
2653                // The MUST names one answer for this request, and it is not
2654                // an acceptance.
2655                None => {
2656                    return Err(EndpointError::PeerPrefixOverlap { request: id, established });
2657                }
2658                Some(code) => {
2659                    let required = RequestErrorCode::PrefixOverlap as u64;
2660                    if code.into_inner() != required {
2661                        return Err(EndpointError::WrongOverlapRefusal { request: id, required });
2662                    }
2663                }
2664            }
2665        }
2666        let sm = self.subscribe_namespaces.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2667        if refusal_code.is_none() {
2668            sm.on_subscribe_namespace_ok_sent()?;
2669        } else {
2670            sm.on_subscribe_namespace_error_sent()?;
2671        }
2672        Ok(())
2673    }
2674
2675    /// Record that the namespace subscription `request_id` was withdrawn by
2676    /// closing its stream.
2677    ///
2678    /// Section 6.1: "A SUBSCRIBE_NAMESPACE can be cancelled by closing the
2679    /// stream with either a FIN or RESET_STREAM." Draft-16 removed
2680    /// UNSUBSCRIBE_NAMESPACE, so this is the whole of how a namespace
2681    /// subscription ends early, in either direction.
2682    ///
2683    /// Idempotent: a subscription that has already ended accepts it and stays
2684    /// where it is.
2685    ///
2686    /// # Errors
2687    ///
2688    /// [`EndpointError::UnknownRequest`] if no namespace subscription carries
2689    /// this id, and [`EndpointError::Namespace`] if one does but nothing has
2690    /// been written on its stream.
2691    pub fn cancel_namespace_subscription(
2692        &mut self,
2693        request_id: VarInt,
2694    ) -> Result<(), EndpointError> {
2695        let id = request_id.into_inner();
2696        let sm = self.subscribe_namespaces.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2697        sm.on_request_cancelled()?;
2698        Ok(())
2699    }
2700
2701    // -- Publish Namespace flow -------------------------------------
2702
2703    /// Send a PUBLISH_NAMESPACE message.
2704    pub fn publish_namespace(
2705        &mut self,
2706        track_namespace: TrackNamespace,
2707        parameters: Vec<KeyValuePair>,
2708    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2709        self.require_active_or_err()?;
2710        let req_id = self.request_ids.allocate()?;
2711
2712        let mut sm = PublishNamespaceStateMachine::new();
2713        sm.on_publish_namespace_sent()?;
2714        self.publish_namespaces.insert(req_id.into_inner(), sm);
2715
2716        let msg = ControlMessage::PublishNamespace(PublishNamespace {
2717            request_id: req_id,
2718            track_namespace,
2719            parameters,
2720        });
2721        Ok((req_id, msg))
2722    }
2723
2724    // -- Answering a PUBLISH_NAMESPACE the peer sent ----------------
2725
2726    /// Process an incoming PUBLISH_NAMESPACE, recording the announcement it
2727    /// makes.
2728    ///
2729    /// Section 9.20: "The publisher sends the PUBLISH_NAMESPACE control message
2730    /// to advertise that it has tracks available within a Track Namespace. The
2731    /// receiver verifies the publisher is authorized to publish tracks under
2732    /// this namespace."
2733    ///
2734    /// Verifying is the application's to do, and it needs both the message to
2735    /// verify and somewhere to answer from. The Request ID has already been
2736    /// checked by [`Self::receive_request`], which every request message passes
2737    /// through before its own handler, so a repeat of one the peer has already
2738    /// spent never reaches here.
2739    ///
2740    /// # Errors
2741    ///
2742    /// The session error when the session is not established.
2743    pub fn receive_publish_namespace(
2744        &mut self,
2745        msg: &PublishNamespace,
2746    ) -> Result<(), EndpointError> {
2747        self.require_active_or_err()?;
2748        let id = msg.request_id.into_inner();
2749        let mut state = PublishNamespaceStateMachine::new();
2750        state.on_publish_namespace_received()?;
2751        self.inbound_publish_namespaces
2752            .insert(id, InboundPublishNamespace { message: msg.clone(), state });
2753        Ok(())
2754    }
2755
2756    /// The PUBLISH_NAMESPACE the peer sent under `request_id` and this endpoint
2757    /// has not answered yet.
2758    ///
2759    /// `None` once it has been answered, and for an identifier the peer has
2760    /// announced nothing under. The record itself lives on past the answer,
2761    /// because an announcement that was accepted is not over until it is
2762    /// withdrawn or cancelled.
2763    pub fn pending_publish_namespace(&self, request_id: VarInt) -> Option<&PublishNamespace> {
2764        self.inbound_publish_namespaces
2765            .get(&request_id.into_inner())
2766            .filter(|a| a.state.state() == PublishNamespaceState::Pending)
2767            .map(|a| &a.message)
2768    }
2769
2770    /// How many announcements the peer has made that are still waiting for an
2771    /// answer.
2772    pub fn pending_publish_namespace_count(&self) -> usize {
2773        self.inbound_publish_namespaces
2774            .values()
2775            .filter(|a| a.state.state() == PublishNamespaceState::Pending)
2776            .count()
2777    }
2778
2779    /// Build the REQUEST_OK accepting an announcement the peer made or a track
2780    /// status it asked for.
2781    ///
2782    /// One message accepts more than one kind of request, so the record it
2783    /// moves is found by the identifier: the maps are keyed by Request ID and
2784    /// one id belongs to one request.
2785    ///
2786    /// Section 6.2: "A subscriber MUST send exactly one REQUEST_OK or
2787    /// REQUEST_ERROR in response to a PUBLISH_NAMESPACE. The publisher SHOULD
2788    /// close the session with a protocol error if it receives more than one."
2789    /// Section 9.19 gives a track status the same answer - "If successful, the
2790    /// publisher responds with a REQUEST_OK message with the same parameters it
2791    /// would have set in a SUBSCRIBE_OK" - and adds that "Track Alias is not
2792    /// used", which is why accepting one binds nothing.
2793    ///
2794    /// One answer and no second one: the flow moves on the first, and a second
2795    /// call finds a record that has left Pending.
2796    ///
2797    /// # Errors
2798    ///
2799    /// [`EndpointError::UnknownRequest`] if the peer has neither announced nor
2800    /// asked about anything under that identifier, and the answering flow's own
2801    /// `InvalidTransition` for a request already answered.
2802    pub fn send_request_ok(
2803        &mut self,
2804        request_id: VarInt,
2805        parameters: Vec<KeyValuePair>,
2806    ) -> Result<ControlMessage, EndpointError> {
2807        let id = request_id.into_inner();
2808        if let Some(ann) = self.inbound_publish_namespaces.get_mut(&id) {
2809            ann.state.on_publish_namespace_ok_sent()?;
2810        } else if let Some(req) = self.inbound_track_statuses.get_mut(&id) {
2811            req.state.on_track_status_ok_sent()?;
2812        } else {
2813            return Err(EndpointError::UnknownRequest(id));
2814        }
2815        Ok(ControlMessage::RequestOk(message::RequestOk { request_id, parameters }))
2816    }
2817
2818    /// Process an incoming PUBLISH_NAMESPACE_DONE, ending the announcement the
2819    /// peer made.
2820    ///
2821    /// Section 9.22: "The publisher sends the PUBLISH_NAMESPACE_DONE control
2822    /// message to indicate its intent to stop serving new subscriptions for
2823    /// tracks within the provided Track Namespace."
2824    ///
2825    /// The announcement it ends is the peer's, so the record it reads is the
2826    /// one this endpoint keeps of what the peer announced. An announcement this
2827    /// endpoint made is withdrawn by [`Self::publish_namespace_done`], which is
2828    /// the same message travelling the other way.
2829    ///
2830    /// # Errors
2831    ///
2832    /// [`EndpointError::UnknownRequest`] if the peer announced nothing under
2833    /// that identifier, and the namespace flow's own `InvalidTransition` for
2834    /// one this endpoint never accepted.
2835    pub fn receive_publish_namespace_done(
2836        &mut self,
2837        msg: &PublishNamespaceDone,
2838    ) -> Result<(), EndpointError> {
2839        let id = msg.request_id.into_inner();
2840        let ann = self
2841            .inbound_publish_namespaces
2842            .get_mut(&id)
2843            .ok_or(EndpointError::UnknownRequest(id))?;
2844        ann.state.on_publish_namespace_done_received()?;
2845        Ok(())
2846    }
2847
2848    /// Build the PUBLISH_NAMESPACE_CANCEL revoking an acceptance.
2849    ///
2850    /// Section 8.5 names what a cancellation revokes: a namespace "it
2851    /// previously responded REQUEST_OK to". Section 9.24 says what it does: the
2852    /// subscriber "will stop sending new subscriptions for tracks within the
2853    /// provided Track Namespace".
2854    ///
2855    /// Previously responded REQUEST_OK to is a state, and it is Active: an
2856    /// announcement reaches it by being accepted and no other way. One still
2857    /// waiting for an answer, one refused and one already ended are all refused
2858    /// here rather than sent.
2859    ///
2860    /// The announcement is the peer's. An announcement this endpoint made is
2861    /// not cancelled by its own publisher; the peer cancels it, and that
2862    /// arrives at [`Self::receive_publish_namespace_cancel`].
2863    ///
2864    /// # Errors
2865    ///
2866    /// [`EndpointError::UnknownRequest`] if the peer announced nothing under
2867    /// that identifier, and the namespace flow's own `InvalidTransition` for
2868    /// one this endpoint never accepted.
2869    pub fn publish_namespace_cancel(
2870        &mut self,
2871        request_id: VarInt,
2872        error_code: VarInt,
2873        reason_phrase: Vec<u8>,
2874    ) -> Result<ControlMessage, EndpointError> {
2875        let id = request_id.into_inner();
2876        let ann = self
2877            .inbound_publish_namespaces
2878            .get_mut(&id)
2879            .ok_or(EndpointError::UnknownRequest(id))?;
2880        ann.state.on_publish_namespace_cancel_sent()?;
2881        Ok(ControlMessage::PublishNamespaceCancel(PublishNamespaceCancel {
2882            request_id,
2883            error_code,
2884            reason_phrase,
2885        }))
2886    }
2887
2888    /// Send the PUBLISH_NAMESPACE_DONE withdrawing an announcement this
2889    /// endpoint made.
2890    ///
2891    /// Section 9.22: "The publisher sends the PUBLISH_NAMESPACE_DONE control
2892    /// message to indicate its intent to stop serving new subscriptions for
2893    /// tracks within the provided Track Namespace." This endpoint is that
2894    /// publisher, so the record it ends is one [`Self::publish_namespace`]
2895    /// opened.
2896    ///
2897    /// # Errors
2898    ///
2899    /// [`EndpointError::UnknownRequest`] if this endpoint has announced nothing
2900    /// under that identifier, and the namespace flow's own `InvalidTransition`
2901    /// for an announcement the peer has not accepted, or has already cancelled.
2902    pub fn publish_namespace_done(
2903        &mut self,
2904        request_id: VarInt,
2905    ) -> Result<ControlMessage, EndpointError> {
2906        let id = request_id.into_inner();
2907        let sm = self.publish_namespaces.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2908        sm.on_publish_namespace_done()?;
2909        Ok(ControlMessage::PublishNamespaceDone(PublishNamespaceDone { request_id }))
2910    }
2911
2912    /// Process an incoming PUBLISH_NAMESPACE_CANCEL, ending an announcement
2913    /// this endpoint made.
2914    ///
2915    /// Section 8.5 names what the peer is revoking: a namespace "it previously
2916    /// responded REQUEST_OK to". What it responded to is an announcement this
2917    /// endpoint made, so the record this reads is the outbound one.
2918    ///
2919    /// # Errors
2920    ///
2921    /// [`EndpointError::UnknownRequest`] if this endpoint has announced nothing
2922    /// under that identifier, and the namespace flow's own `InvalidTransition`
2923    /// for an announcement the peer never accepted.
2924    pub fn receive_publish_namespace_cancel(
2925        &mut self,
2926        msg: &PublishNamespaceCancel,
2927    ) -> Result<(), EndpointError> {
2928        let id = msg.request_id.into_inner();
2929        let sm = self.publish_namespaces.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2930        // A cancellation that crossed this endpoint's own withdrawal: the
2931        // announcement is already over and the peer could not have known, so
2932        // there is nothing to end and nothing it did wrong.
2933        if sm.state() == PublishNamespaceState::Done {
2934            return Ok(());
2935        }
2936        sm.on_publish_namespace_cancel()?;
2937        Ok(())
2938    }
2939
2940    // -- Track Status flow ------------------------------------------
2941
2942    /// Send a TRACK_STATUS message. Allocates a request ID. Draft-16:
2943    /// simplified with parameters.
2944    pub fn track_status(
2945        &mut self,
2946        track_namespace: TrackNamespace,
2947        track_name: Vec<u8>,
2948        parameters: Vec<KeyValuePair>,
2949    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2950        self.require_active_or_err()?;
2951        let req_id = self.request_ids.allocate()?;
2952        let mut sm = TrackStatusStateMachine::new();
2953        sm.on_track_status_sent()?;
2954        self.track_statuses.insert(req_id.into_inner(), sm);
2955        let msg = ControlMessage::TrackStatus(message::TrackStatus {
2956            request_id: req_id,
2957            track_namespace,
2958            track_name,
2959            parameters,
2960        });
2961        Ok((req_id, msg))
2962    }
2963
2964    // -- Answering a TRACK_STATUS the peer sent ---------------------
2965
2966    /// Process an incoming TRACK_STATUS, recording what the peer asked about.
2967    ///
2968    /// Section 9.19: the receiver of one "treats it identically as if it had
2969    /// received a SUBSCRIBE message, except it does not create downstream
2970    /// subscription state or send any Objects". The exception is why this does
2971    /// not reach for the subscriptions the peer has opened: nothing that names
2972    /// a subscription is to find this request, and the surest way to hold to
2973    /// that is for it never to be one.
2974    ///
2975    /// The same section says the answer carries no alias at all here - "Track
2976    /// Alias is not used" - so unlike a subscription the peer opens, accepting
2977    /// this one binds nothing.
2978    ///
2979    /// # Errors
2980    ///
2981    /// The session error when the session is not established.
2982    pub fn receive_track_status(
2983        &mut self,
2984        msg: &message::TrackStatus,
2985    ) -> Result<(), EndpointError> {
2986        self.require_active_or_err()?;
2987        let id = msg.request_id.into_inner();
2988        let mut state = TrackStatusStateMachine::new();
2989        state.on_track_status_received()?;
2990        self.inbound_track_statuses.insert(id, InboundTrackStatus { message: msg.clone(), state });
2991        Ok(())
2992    }
2993
2994    /// The TRACK_STATUS the peer sent under `request_id` and this endpoint has
2995    /// not answered yet.
2996    ///
2997    /// `None` once it has been answered, and for an identifier this session has
2998    /// carried no track status under.
2999    pub fn pending_track_status(&self, request_id: VarInt) -> Option<&message::TrackStatus> {
3000        self.inbound_track_statuses
3001            .get(&request_id.into_inner())
3002            .filter(|t| t.state.state() == TrackStatusState::Pending)
3003            .map(|t| &t.message)
3004    }
3005
3006    /// How many track statuses the peer has asked about that are still waiting
3007    /// for an answer.
3008    pub fn pending_track_status_count(&self) -> usize {
3009        self.inbound_track_statuses
3010            .values()
3011            .filter(|t| t.state.state() == TrackStatusState::Pending)
3012            .count()
3013    }
3014
3015    // -- Publish flow (publisher side) ------------------------------
3016
3017    /// Send a PUBLISH message (publisher side). Allocates a request ID.
3018    /// Draft-16: takes track_alias, no forward.
3019    pub fn publish(
3020        &mut self,
3021        track_namespace: TrackNamespace,
3022        track_name: Vec<u8>,
3023        track_alias: VarInt,
3024        track_extensions: Vec<KeyValuePair>,
3025        parameters: Vec<KeyValuePair>,
3026    ) -> Result<(VarInt, ControlMessage), EndpointError> {
3027        self.require_active_or_err()?;
3028        // Section 9.13: "The same Track Alias MUST NOT be used to refer to
3029        // two different Tracks simultaneously." Refused before the
3030        // Request ID is allocated, so a refusal spends nothing.
3031        let alias = track_alias.into_inner();
3032        if let Some(refusal) = self.alias_held_elsewhere(alias, &track_namespace, &track_name) {
3033            return Err(refusal);
3034        }
3035        let req_id = self.request_ids.allocate()?;
3036        let mut sm = PublishStateMachine::new();
3037        sm.on_publish_sent()?;
3038        self.publishes.insert(req_id.into_inner(), Mutex::new(sm));
3039        // The alias and the track travel together in a PUBLISH, so the binding
3040        // is complete the moment the message is built.
3041        self.track_bindings.insert(
3042            req_id.into_inner(),
3043            TrackBinding {
3044                namespace: track_namespace.clone(),
3045                name: track_name.clone(),
3046                alias: Some(alias),
3047                kind: BindingKind::Publish,
3048            },
3049        );
3050        let msg = ControlMessage::Publish(message::Publish {
3051            request_id: req_id,
3052            track_namespace,
3053            track_name,
3054            track_alias,
3055            track_extensions,
3056            parameters,
3057        });
3058        Ok((req_id, msg))
3059    }
3060
3061    /// Process an incoming PUBLISH_OK (publisher side). Draft-16: has
3062    /// request_id, parameters.
3063    pub fn receive_publish_ok(&mut self, msg: &message::PublishOk) -> Result<(), EndpointError> {
3064        let id = msg.request_id.into_inner();
3065        let mut sm = self.publish_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
3066        sm.on_publish_ok()?;
3067        Ok(())
3068    }
3069
3070    /// Send a PUBLISH_DONE message (publisher finishing). Draft-16: has
3071    /// stream_count.
3072    pub fn send_publish_done(
3073        &mut self,
3074        request_id: VarInt,
3075        status_code: VarInt,
3076        stream_count: VarInt,
3077        reason_phrase: Vec<u8>,
3078    ) -> Result<ControlMessage, EndpointError> {
3079        let id = request_id.into_inner();
3080        // Section 9.15 gives the publisher this message for a subscription
3081        // that came either way round, so a subscription the peer opened with
3082        // SUBSCRIBE ends here too. Two records, one message.
3083        if let Some(sub) = self.inbound_subscribes.get_mut(&id) {
3084            sub.state.on_publish_done_sent()?;
3085            return Ok(ControlMessage::PublishDone(PublishDone {
3086                request_id,
3087                status_code,
3088                stream_count,
3089                reason_phrase,
3090            }));
3091        }
3092        let mut sm = self.publish_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
3093        sm.on_publish_done_sent()?;
3094        Ok(ControlMessage::PublishDone(PublishDone {
3095            request_id,
3096            status_code,
3097            stream_count,
3098            reason_phrase,
3099        }))
3100    }
3101
3102    // -- Consolidated responses (draft-16) --------------------------
3103
3104    /// Process an incoming REQUEST_OK (consolidated ok for namespace/
3105    /// track-status flows).
3106    pub fn receive_request_ok(&mut self, msg: &RequestOk) -> Result<(), EndpointError> {
3107        let id = msg.request_id.into_inner();
3108        // A namespace subscription is answered on its own stream and is
3109        // deliberately not reachable from here — see
3110        // [`receive_on_namespace_stream`](Self::receive_on_namespace_stream).
3111        // Its id falls through to `UnknownRequest`, which is what it is on
3112        // this stream.
3113        if let Some(sm) = self.publish_namespaces.get_mut(&id) {
3114            sm.on_publish_namespace_ok()?;
3115            return Ok(());
3116        }
3117        // Then track_statuses
3118        if let Some(sm) = self.track_statuses.get_mut(&id) {
3119            sm.on_track_status_ok()?;
3120            return Ok(());
3121        }
3122        Err(EndpointError::UnknownRequest(id))
3123    }
3124
3125    /// Process an incoming REQUEST_ERROR (consolidated error).
3126    pub fn receive_request_error(&mut self, msg: &RequestError) -> Result<(), EndpointError> {
3127        let id = msg.request_id.into_inner();
3128        // Check subscriptions
3129        if let Some(mut sm) = self.subscription(id) {
3130            sm.on_subscribe_error()?;
3131            return Ok(());
3132        }
3133        // Check fetches
3134        if let Some(mut sm) = self.fetch_flow(id) {
3135            sm.on_fetch_error()?;
3136            return Ok(());
3137        }
3138        // Check publishes
3139        if let Some(mut sm) = self.publish_flow(id) {
3140            sm.on_publish_error()?;
3141            return Ok(());
3142        }
3143        // A namespace subscription is answered on its own stream, so its id is
3144        // not looked for here either.
3145        // Check publish_namespaces
3146        if let Some(sm) = self.publish_namespaces.get_mut(&id) {
3147            sm.on_publish_namespace_error()?;
3148            return Ok(());
3149        }
3150        // Check track_statuses
3151        if let Some(sm) = self.track_statuses.get_mut(&id) {
3152            sm.on_track_status_error()?;
3153            return Ok(());
3154        }
3155        // Unknown ID -- silently ignore per spec
3156        Ok(())
3157    }
3158
3159    /// Process an incoming PUBLISH message, which opens a subscription this
3160    /// endpoint is the subscriber of.
3161    ///
3162    /// Section 5.1: "A publisher initiates a subscription to a track by
3163    /// sending the PUBLISH message. The subscriber either accepts or rejects
3164    /// the subscription using PUBLISH_OK or REQUEST_ERROR." The record made here
3165    /// outlives that answer, because everything the section gives the
3166    /// subscription afterwards names this same request: an UNSUBSCRIBE from
3167    /// this endpoint, a PUBLISH_DONE from the peer, and the Track Alias the
3168    /// offer spends for as long as it lasts.
3169    ///
3170    /// # Errors
3171    ///
3172    /// [`EndpointError::DuplicateTrackAlias`] when the Track Alias offered is
3173    /// one a different live track already holds, which ends the session, and
3174    /// the publish flow's own `InvalidTransition` for a second PUBLISH under a
3175    /// request id already carrying one.
3176    pub fn receive_publish(&mut self, msg: &message::Publish) -> Result<(), EndpointError> {
3177        self.require_active_or_err()?;
3178        // Section 9.13 answers a PUBLISH naming an alias another live track
3179        // already holds with a session close, in the same words Section 9.10
3180        // uses for a SUBSCRIBE_OK. Judged before anything is written down, so
3181        // that a refused offer leaves no binding behind.
3182        let id = msg.request_id.into_inner();
3183        let alias = msg.track_alias.into_inner();
3184        if let Some(conflict) =
3185            self.conflicting_track_alias(id, alias, &msg.track_namespace, &msg.track_name)
3186        {
3187            return Err(self.fail_session(conflict));
3188        }
3189        let mut sm = PublishStateMachine::new();
3190        sm.on_publish_received()?;
3191        self.publishes.insert(id, Mutex::new(sm));
3192        // The offer as it arrived. The state machine above says how far it
3193        // has got and the binding says which track it is about; neither can
3194        // say what was offered, and that is what an answer is built from.
3195        self.inbound_publishes.insert(id, msg.clone());
3196        // A PUBLISH names its track and its alias in the one message, so the
3197        // binding is complete on arrival. It counts against the next one only
3198        // once this endpoint has answered PUBLISH_OK, which is what moves the
3199        // subscription to Active.
3200        self.track_bindings.insert(
3201            id,
3202            TrackBinding {
3203                namespace: msg.track_namespace.clone(),
3204                name: msg.track_name.clone(),
3205                alias: Some(alias),
3206                kind: BindingKind::Publish,
3207            },
3208        );
3209        Ok(())
3210    }
3211
3212    /// The PUBLISH the peer sent under `request_id` and this endpoint has not
3213    /// answered yet.
3214    ///
3215    /// `None` once it has been answered, and for an identifier this session
3216    /// has carried no offer from the peer under -- an offer of this
3217    /// endpoint's own included, whose state is in the same map but whose
3218    /// message was never received. The record itself lives on past the
3219    /// answer, because the subscription the offer opened is not over until an
3220    /// UNSUBSCRIBE or a PUBLISH_DONE ends it.
3221    pub fn pending_publish(&self, request_id: VarInt) -> Option<&message::Publish> {
3222        let id = request_id.into_inner();
3223        let unanswered =
3224            self.publish_flow(id).is_some_and(|sm| sm.state() == PublishState::Publishing);
3225        self.inbound_publishes.get(&id).filter(|_| unanswered)
3226    }
3227
3228    /// How many offers the peer has made that are still waiting for an answer.
3229    pub fn pending_publish_count(&self) -> usize {
3230        self.inbound_publishes
3231            .keys()
3232            .filter(|id| {
3233                self.publish_flow(**id).is_some_and(|sm| sm.state() == PublishState::Publishing)
3234            })
3235            .count()
3236    }
3237    /// Generate a PUBLISH_OK accepting a PUBLISH the peer sent, which
3238    /// establishes the subscription it opened.
3239    ///
3240    /// # Errors
3241    ///
3242    /// [`EndpointError::UnknownRequest`] when no PUBLISH arrived under that
3243    /// id, and the publish flow's own `InvalidTransition` for a second answer:
3244    /// Section 5.1 says "A subscriber MUST send exactly one PUBLISH_OK or
3245    /// REQUEST_ERROR in response to a PUBLISH."
3246    pub fn send_publish_ok(
3247        &mut self,
3248        request_id: VarInt,
3249        parameters: Vec<KeyValuePair>,
3250    ) -> Result<ControlMessage, EndpointError> {
3251        let id = request_id.into_inner();
3252        let mut sm = self.publish_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
3253        sm.on_publish_ok_sent()?;
3254        Ok(ControlMessage::PublishOk(message::PublishOk { request_id, parameters }))
3255    }
3256
3257    /// Generate the REQUEST_ERROR that rejects a PUBLISH the peer sent, which
3258    /// ends the subscription it opened before it was established.
3259    ///
3260    /// This draft has no PUBLISH_ERROR: Section 5.1 answers a rejected PUBLISH
3261    /// with the one REQUEST_ERROR every request kind shares.
3262    ///
3263    /// # Errors
3264    ///
3265    /// [`EndpointError::UnknownRequest`] when no PUBLISH arrived under that
3266    /// id, and the publish flow's own `InvalidTransition` for a second answer:
3267    /// Section 5.1 says "A subscriber MUST send exactly one PUBLISH_OK or
3268    /// REQUEST_ERROR in response to a PUBLISH."
3269    pub fn send_publish_error(
3270        &mut self,
3271        request_id: VarInt,
3272        error_code: VarInt,
3273        retry_interval: VarInt,
3274        reason_phrase: Vec<u8>,
3275    ) -> Result<ControlMessage, EndpointError> {
3276        let id = request_id.into_inner();
3277        let mut sm = self.publish_flow(id).ok_or(EndpointError::UnknownRequest(id))?;
3278        sm.on_publish_error_sent()?;
3279        Ok(ControlMessage::RequestError(RequestError {
3280            request_id,
3281            error_code,
3282            retry_interval,
3283            reason_phrase,
3284        }))
3285    }
3286
3287    /// Hold a Request ID a peer allocated to the rules Section 9.1 states
3288    /// about it.
3289    ///
3290    /// "The client's Request ID starts at 0 and are even and the server's
3291    /// Request ID starts at 1 and are odd. The Request ID increments by 2 ...
3292    /// If an endpoint receives a Request ID that is not valid for the peer, it
3293    /// MUST close the session with Invalid Request ID." Parity says which end
3294    /// may have chosen it; the ceiling this endpoint advertised says how far
3295    /// the peer may go.
3296    ///
3297    /// This is the call site [`Self::validate_peer_request_id`] did not have.
3298    /// The rule was implemented and then applied to nothing, so a peer could
3299    /// open requests with ids from this endpoint's own half of the space, or
3300    /// past the ceiling it had advertised, and neither was noticed.
3301    ///
3302    /// A message that is a response rather than a request carries the id of a
3303    /// request this endpoint made, so it is not checked here - it is checked by
3304    /// finding the state machine it names.
3305    ///
3306    /// # The list below is the rule, not a convenience
3307    ///
3308    /// Every message named here spends one of the peer's Request IDs, and
3309    /// nothing else does. A request left out of it would go unchecked for
3310    /// parity, the ceiling and reuse. Section 9.1 names the set, and
3311    /// REQUEST_UPDATE is in it - it carries a Request ID of its own, alongside
3312    /// the separate field naming the request it modifies.
3313    ///
3314    /// # Errors
3315    ///
3316    /// The request-id errors, for a wrong parity, an id at or above the
3317    /// advertised ceiling, or one the peer has already used.
3318    pub fn receive_request(&mut self, msg: &ControlMessage) -> Result<(), EndpointError> {
3319        let request_id = match msg {
3320            ControlMessage::Subscribe(m) => m.request_id,
3321            ControlMessage::Fetch(m) => m.request_id,
3322            ControlMessage::Publish(m) => m.request_id,
3323            ControlMessage::PublishNamespace(m) => m.request_id,
3324            ControlMessage::SubscribeNamespace(m) => m.request_id,
3325            ControlMessage::TrackStatus(m) => m.request_id,
3326            ControlMessage::RequestUpdate(m) => m.request_id,
3327            _ => return Ok(()),
3328        };
3329        self.validate_peer_request_id(request_id.into_inner())
3330    }
3331
3332    // -- Unified message dispatch -----------------------------------
3333
3334    /// Dispatch an incoming control message to the appropriate handler.
3335    pub fn receive_message(&mut self, msg: ControlMessage) -> Result<(), EndpointError> {
3336        self.receive_request(&msg)?;
3337        match msg {
3338            ControlMessage::GoAway(ref m) => self.receive_goaway(m),
3339            ControlMessage::MaxRequestId(ref m) => self.receive_max_request_id(m),
3340            ControlMessage::RequestsBlocked(ref m) => self.receive_requests_blocked(m),
3341            ControlMessage::SubscribeOk(ref m) => self.receive_subscribe_ok(m),
3342            ControlMessage::RequestUpdate(ref m) => self.receive_request_update(m),
3343            ControlMessage::Subscribe(ref m) => self.receive_subscribe(m),
3344            ControlMessage::Unsubscribe(ref m) => self.receive_unsubscribe(m),
3345            ControlMessage::Fetch(ref m) => self.receive_fetch(m),
3346            ControlMessage::FetchCancel(ref m) => self.receive_fetch_cancel(m),
3347            ControlMessage::Publish(ref m) => self.receive_publish(m),
3348            ControlMessage::PublishDone(ref m) => self.receive_publish_done(m),
3349            ControlMessage::PublishOk(ref m) => self.receive_publish_ok(m),
3350            ControlMessage::FetchOk(ref m) => self.receive_fetch_ok(m),
3351            ControlMessage::RequestOk(ref m) => self.receive_request_ok(m),
3352            ControlMessage::RequestError(ref m) => self.receive_request_error(m),
3353            ControlMessage::TrackStatus(ref m) => self.receive_track_status(m),
3354            ControlMessage::PublishNamespaceDone(ref m) => self.receive_publish_namespace_done(m),
3355            // Both belong on a SUBSCRIBE_NAMESPACE response stream and carry
3356            // no Request ID, so here they name nothing. Refusing them is what
3357            // stops this endpoint writing on the control stream exactly what
3358            // its own receive half will not read there.
3359            //
3360            // Refused, and the session left running rather than going through
3361            // `fail_session`, which is the answer for a rule the draft closes
3362            // over — and draft-16 closes over no such rule; see
3363            // `session_error_code` below. The message is framed, so its length
3364            // is known and the next boundary on the control stream is too,
3365            // which is why refusing one costs nothing: this is the same answer
3366            // `ResponseOnControlStream` has always given on the drafts that
3367            // have it.
3368            ControlMessage::Namespace(_) => {
3369                Err(EndpointError::NamespaceMessageOnControlStream("NAMESPACE"))
3370            }
3371            ControlMessage::NamespaceDone(_) => {
3372                Err(EndpointError::NamespaceMessageOnControlStream("NAMESPACE_DONE"))
3373            }
3374            ControlMessage::PublishNamespace(ref m) => self.receive_publish_namespace(m),
3375            ControlMessage::PublishNamespaceCancel(ref m) => {
3376                self.receive_publish_namespace_cancel(m)
3377            }
3378            _ => Ok(()),
3379        }
3380    }
3381}
3382
3383impl PublishStateMachine {
3384    /// Idle -> Publishing (PUBLISH received from the peer).
3385    pub fn on_publish_received(&mut self) -> Result<(), PublishFlowError> {
3386        self.on_publish_sent().map_err(|_| PublishFlowError::InvalidTransition {
3387            from: self.state(),
3388            event: "on_publish_received".to_string(),
3389        })
3390    }
3391
3392    /// Publishing -> Active (PUBLISH_OK sent to the offering peer).
3393    pub fn on_publish_ok_sent(&mut self) -> Result<(), PublishFlowError> {
3394        self.on_publish_ok().map_err(|_| PublishFlowError::InvalidTransition {
3395            from: self.state(),
3396            event: "on_publish_ok_sent".to_string(),
3397        })
3398    }
3399
3400    /// Publishing -> Done (REQUEST_ERROR sent to the offering peer).
3401    pub fn on_publish_error_sent(&mut self) -> Result<(), PublishFlowError> {
3402        self.on_publish_error().map_err(|_| PublishFlowError::InvalidTransition {
3403            from: self.state(),
3404            event: "on_publish_error_sent".to_string(),
3405        })
3406    }
3407
3408    /// Active -> Done (PUBLISH_DONE received from the offering peer).
3409    pub fn on_publish_done_received(&mut self) -> Result<(), PublishFlowError> {
3410        self.on_publish_done_sent().map_err(|_| PublishFlowError::InvalidTransition {
3411            from: self.state(),
3412            event: "on_publish_done_received".to_string(),
3413        })
3414    }
3415
3416    /// Active -> Done (UNSUBSCRIBE sent to the offering peer).
3417    ///
3418    /// The same transition as the one above and a separate name, because a
3419    /// refusal has to say which of the two events was refused.
3420    pub fn on_unsubscribe_sent(&mut self) -> Result<(), PublishFlowError> {
3421        self.on_publish_done_sent().map_err(|_| PublishFlowError::InvalidTransition {
3422            from: self.state(),
3423            event: "on_unsubscribe_sent".to_string(),
3424        })
3425    }
3426
3427    /// Active -> Done (UNSUBSCRIBE received from the subscribing peer).
3428    ///
3429    /// The mirror of the one above, on an offer this endpoint made rather than
3430    /// one it took. Both directions are held in this one map, so the event
3431    /// name is the only thing that says which of them was refused.
3432    pub fn on_unsubscribe_received(&mut self) -> Result<(), PublishFlowError> {
3433        self.on_publish_done_sent().map_err(|_| PublishFlowError::InvalidTransition {
3434            from: self.state(),
3435            event: "on_unsubscribe_received".to_string(),
3436        })
3437    }
3438}