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