Skip to main content

moqtap_client/draft12/
endpoint.rs

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