Skip to main content

moqtap_client/draft15/
endpoint.rs

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