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