Skip to main content

moqtap_client/draft22/
endpoint.rs

1#![allow(missing_docs)]
2//! Draft-22 MoQT endpoint.
3//!
4//! Major changes from draft-19:
5//!
6//! * **FETCH lost its `Fetch Type`** and with it the Standalone and Joining
7//!   Fetch structures (Section 9.11). Track Namespace and Track Name are
8//!   inline fields, the range travels in the `LOCATION_FILTER` parameter, and
9//!   [`Endpoint::fetch`] therefore takes a parameter list where draft-19's took
10//!   four location varints. `joining_fetch` and `absolute_joining_fetch` are
11//!   gone, as is `INVALID_JOINING_REQUEST_ID` (REQUEST_ERROR 0x32) and every
12//!   rule this endpoint enforced with it.
13//! * **Fill fetch streams replace the joining fetch** (Section 3.4 and
14//!   Section 3.4.1). A SUBSCRIBE or REQUEST_UPDATE carrying `FILL_PARAMETERS`
15//!   (0x23) asks the publisher for a unidirectional stream that opens with a
16//!   FETCH_HEADER carrying the *subscription's* Request ID. This endpoint
17//!   records the request in [`Endpoint::fill_requested`] so an arriving
18//!   FETCH_HEADER can be told from a fetch's, and counts the streams so a
19//!   PUBLISH_DONE's Stream Count can include them.
20//! * **PUBLISH_STATE_NOTIFY (0x22) is new** (Section 9.10): unilateral, from
21//!   the publisher, on a subscription's stream, answered with nothing and
22//!   explicitly outside the `MAX_REQUEST_UPDATES` ceiling.
23//! * **Ranges are inclusive at both ends** (Section 3.2, Section 3.3.1,
24//!   Section 9.12), so no end location in this module carries draft-19's `+ 1`.
25//! * `INCLUDE_PROPERTIES` (0x35) is new; `SUBSCRIPTION_ENDED` (PUBLISH_DONE
26//!   0x3) and `VERSION_NEGOTIATION_FAILED` (session 0x15) are unassigned.
27//! * Six parameters dropped PUBLISH_OK from their scope and several gained
28//!   PUBLISH; only `EXPIRES` still names PUBLISH_OK. The table that decides it
29//!   lives in the codec.
30
31use std::collections::{HashMap, HashSet};
32use std::sync::{Arc, Mutex};
33
34use crate::draft22::fetch::{FetchError, FetchState, FetchStateMachine};
35use crate::draft22::fill::{self, FillError, LocationFilter};
36use crate::draft22::namespace::{
37    NamespaceError, PublishNamespaceState, PublishNamespaceStateMachine, SubscribeNamespaceState,
38    SubscribeNamespaceStateMachine,
39};
40use crate::draft22::publish::{
41    PublishError as PublishFlowError, PublishState, PublishStateMachine,
42};
43use crate::draft22::session::request_id::{RequestIdAllocator, RequestIdError, Role};
44use crate::draft22::session::setup::{self, SetupError};
45
46use moqtap_codec::draft22::data_stream::GroupOrder;
47use moqtap_codec::kvp::KvpValue;
48use moqtap_codec::range_filter::{self, RangeFilterError};
49use moqtap_codec::varint::Moqt18;
50
51/// The MAX_REQUEST_UPDATES Setup Option, Section 9.1.7.
52const MAX_REQUEST_UPDATES: u64 = 0x08;
53
54/// The MAX_FILTER_RANGES Setup Option, Section 9.1.6.
55const MAX_FILTER_RANGES: u64 = 0x06;
56
57/// The Message Parameters a request carries, or nothing for a message that is
58/// not a request.
59///
60/// Every one of the seven request kinds carries a parameter list, so the empty
61/// arm is only reached by a caller that is not looking at a request. Section
62/// 3.4 names five message types a Range Filter may appear in, and this does
63/// not narrow to them: a filter parameter arriving where the section does not
64/// list it is a scope question, and scope is answered by the parameter registry
65/// rather than here. What matters for the ceiling is that a filter this endpoint
66/// has no budget for is not accepted, wherever it turned up.
67fn request_parameters(msg: &ControlMessage) -> &[KeyValuePair] {
68    match msg {
69        ControlMessage::Subscribe(m) => &m.parameters,
70        ControlMessage::Publish(m) => &m.parameters,
71        ControlMessage::Fetch(m) => &m.parameters,
72        ControlMessage::PublishNamespace(m) => &m.parameters,
73        ControlMessage::SubscribeNamespace(m) => &m.parameters,
74        ControlMessage::SubscribeTracks(m) => &m.parameters,
75        ControlMessage::TrackStatus(m) => &m.parameters,
76        _ => &[],
77    }
78}
79
80/// The Range Filter parameters in `parameters`, dropping the removals.
81///
82/// A Range Filter whose value is empty is Section 3.3.2's removal form — "In
83/// REQUEST_UPDATE, Length of 0 removes the filter" — so it names a filter rather
84/// than being one, and carrying it forward would leave a filter of no ranges in
85/// force for the life of the request.
86fn range_filter_parameters(parameters: &[KeyValuePair]) -> Vec<KeyValuePair> {
87    parameters
88        .iter()
89        .filter(|p| range_filter::is_range_filter(p.key.into_inner()))
90        .filter(|p| !matches!(&p.value, KvpValue::Bytes(bytes) if bytes.is_empty()))
91        .cloned()
92        .collect()
93}
94
95/// Apply a REQUEST_UPDATE's filter parameters to the set already in force.
96///
97/// Replacement is by Parameter Type and takes the whole type with it: "non-zero
98/// replaces it entirely". A type the update does not mention is left alone,
99/// which is the sentence after it — "If a filter parameter is omitted from
100/// REQUEST_UPDATE, it is unchanged" — and is why this merges rather than
101/// measuring the update on its own.
102fn apply_filter_update(in_force: &mut Vec<KeyValuePair>, update: &[KeyValuePair]) {
103    let mentioned: Vec<u64> = update
104        .iter()
105        .map(|p| p.key.into_inner())
106        .filter(|key| range_filter::is_range_filter(*key))
107        .collect();
108    in_force.retain(|p| !mentioned.contains(&p.key.into_inner()));
109    in_force.extend(range_filter_parameters(update));
110}
111
112/// Read a Setup Option's value as a variable-length integer, or 0 if it is
113/// absent.
114///
115/// Zero is also the default every numeric option in Section 9.1 takes when
116/// it is not sent, so an absent option and an explicit zero mean the same thing
117/// and do not need to be told apart.
118fn setup_varint(options: &[KeyValuePair], key: u64) -> u64 {
119    let key = VarInt::from_u64(key).expect("option key fits a varint");
120    options
121        .iter()
122        .find(|o| o.key == key)
123        .and_then(|o| match &o.value {
124            KvpValue::Varint(v) => Some(v.into_inner()),
125            KvpValue::Bytes(bytes) => {
126                let mut cursor = &bytes[..];
127                let parsed = VarInt::decode_moqt::<Moqt18>(&mut cursor).ok()?;
128                cursor.is_empty().then(|| parsed.into_inner())
129            }
130        })
131        .unwrap_or(0)
132}
133use crate::draft22::session::state::{SessionError, SessionState, SessionStateMachine};
134use crate::draft22::subscription::{
135    SubscriptionError, SubscriptionState, SubscriptionStateMachine,
136};
137use crate::draft22::track_status::{TrackStatusError, TrackStatusState, TrackStatusStateMachine};
138use crate::malformed_tracks::{MalformedTrackCondition, MalformedTracks};
139use crate::track_locations::{ObjectLocation, ObjectRole, TrackLocations, TrackObjects};
140use moqtap_codec::draft22::error_codes::{
141    PublishDoneStatusCode, RequestErrorCode, SessionErrorCode,
142};
143use moqtap_codec::draft22::message::{
144    self, ControlMessage, Fetch, GoAway, MessageType, Publish, PublishDone, PublishNamespace,
145    PublishSkipped, PublishStateNotify, RequestError, RequestOk, RequestUpdate, Setup, Subscribe,
146    SubscribeNamespace, SubscribeOk, SubscribeTracks, FILL_PARAMETERS,
147};
148use moqtap_codec::kvp::KeyValuePair;
149use moqtap_codec::types::*;
150use moqtap_codec::varint::VarInt;
151
152/// Why a peer's request must be answered with REQUEST_ERROR and INVALID_FILTER.
153///
154/// Draft-22 gives the Range Filters four rules and answers every one of them
155/// the same way — "MUST reject this with REQUEST_ERROR with error code
156/// INVALID_FILTER" — so this says which rule was broken rather than which code
157/// to send. [`FilterRejection::request_error_code`] is the code, and it is the
158/// same for all four.
159///
160/// None of these is a session close, which is the whole reason the type exists.
161/// A rejection is a reply, and a reply names the Request ID of the request it
162/// answers, so the endpoint has to have taken the request in order to refuse it.
163/// The rejection is recorded against that id and spent by the REQUEST_ERROR that
164/// answers it.
165#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
166pub enum FilterRejection {
167    /// The peer sent a Range Filter and this endpoint advertised no budget.
168    ///
169    /// Section 9.1.6: "The default value is 0, so if not specified, the peer
170    /// MUST NOT send any such filter parameters." Zero is the default and it
171    /// means none allowed — the opposite of what zero means in the option
172    /// beside it, MAX_REQUEST_UPDATES, where it means no limit. The two sit in
173    /// consecutive subsections and read opposite ways.
174    #[error("this endpoint advertised no MAX_FILTER_RANGES, so no range filter is allowed")]
175    NoBudgetAdvertised,
176    /// More Ranges than MAX_FILTER_RANGES allows, counted across every filter.
177    #[error("{ranges} ranges across the request's filters, where {limit} were advertised")]
178    TooManyRanges {
179        /// The total the request carried.
180        ranges: usize,
181        /// The total this endpoint advertised.
182        limit: u64,
183    },
184    /// Two filters with the same Parameter Type, SetID and Property Type.
185    ///
186    /// Repeats of a type are otherwise expected, so the key is the whole triple:
187    /// "The Track Property filter parameter MAY appear multiple times in a
188    /// SUBSCRIBE_TRACKS message"
189    #[error("two filters share parameter type {0:#x}, set {1} and property type {2:?}")]
190    RepeatedFilter(u64, u8, Option<u64>),
191    /// A filter this codec could not read, or one that broke a rule of its own.
192    #[error("a range filter is not usable: {0}")]
193    Unreadable(#[from] RangeFilterError),
194}
195
196impl FilterRejection {
197    /// The REQUEST_ERROR code every one of these is answered with.
198    pub fn request_error_code(&self) -> RequestErrorCode {
199        RequestErrorCode::InvalidFilter
200    }
201}
202
203/// Errors that can occur during endpoint operations.
204#[derive(Debug, thiserror::Error)]
205pub enum EndpointError {
206    #[error("session error: {0}")]
207    Session(#[from] SessionError),
208    #[error("request ID error: {0}")]
209    RequestId(#[from] RequestIdError),
210    #[error("subscription error: {0}")]
211    Subscription(#[from] SubscriptionError),
212    #[error("fetch error: {0}")]
213    Fetch(#[from] FetchError),
214    /// A `LOCATION_FILTER` or `FILL_PARAMETERS` value this endpoint was asked
215    /// to build is one draft-22 does not define. Nothing was written.
216    #[error("fill parameter error: {0}")]
217    Fill(#[from] FillError),
218    #[error("namespace error: {0}")]
219    Namespace(#[from] NamespaceError),
220    #[error("track status error: {0}")]
221    TrackStatus(#[from] TrackStatusError),
222    #[error("publish flow error: {0}")]
223    PublishFlow(#[from] PublishFlowError),
224    #[error("setup error: {0}")]
225    Setup(#[from] SetupError),
226    #[error("unknown request ID: {0}")]
227    UnknownRequest(u64),
228    #[error(
229        "response message received on control stream; draft-22 responses belong on bidi request streams"
230    )]
231    ResponseOnControlStream,
232    /// A REQUEST_UPDATE arrived on the control stream.
233    ///
234    /// Draft-22 Table 5 gives REQUEST_UPDATE the Stream value "Request", and
235    /// Section 9.5 requires it on the same bidi stream as the request it
236    /// modifies. One on the control stream modifies nothing, which makes it a
237    /// case Section 9.5 says MUST close the session.
238    #[error(
239        "REQUEST_UPDATE received on the control stream; it belongs on its request's own stream"
240    )]
241    RequestUpdateOnControlStream,
242    /// A NAMESPACE, NAMESPACE_DONE or PUBLISH_SKIPPED arrived on the control
243    /// stream.
244    ///
245    /// Draft-22 Table 5 gives all three the Stream value "Request": NAMESPACE
246    /// (0x8, Section 9.16) and NAMESPACE_DONE (0xE, Section 9.17) belong on
247    /// the SUBSCRIBE_NAMESPACE request stream whose namespace they report, and
248    /// PUBLISH_SKIPPED (0xF, Section 9.19) on the SUBSCRIBE_TRACKS stream
249    /// whose namespace it names a skipped track in — "All PUBLISH_SKIPPED
250    /// messages are in response to a SUBSCRIBE_TRACKS". Only SETUP is
251    /// "Control" alone; GOAWAY is the one message the
252    /// table lists as "Control, Request". One of these three on the control
253    /// stream names no request, so nothing can be done with it.
254    #[error("{0} received on the control stream; draft-22 Table 5 places it on a request stream")]
255    RequestMessageOnControlStream(&'static str),
256    /// A REQUEST_UPDATE named a request that cannot be updated, or none.
257    ///
258    /// Draft-22 Section 9.5: "An endpoint that receives a REQUEST_UPDATE
259    /// other than in the two cases above MUST close the session with a
260    /// PROTOCOL_VIOLATION." TRACK_STATUS is called out in Section 9.13 as one
261    /// such case: "the subscriber cannot send REQUEST_UPDATE."
262    #[error("REQUEST_UPDATE for request {0}, which is not an updatable outstanding request")]
263    UnexpectedRequestUpdate(u64),
264    /// Track Properties on a REQUEST_OK answering something other than a
265    /// TRACK_STATUS.
266    ///
267    /// Draft-22 Section 9.3: they "are empty in PUBLISH_OK,
268    /// REQUEST_UPDATE_OK, SUBSCRIBE_NAMESPACE_OK and PUBLISH_NAMESPACE_OK. If
269    /// an endpoint receives Track Properties in one of these messages it MUST
270    /// close the session with a PROTOCOL_VIOLATION."
271    #[error(
272        "track properties on the REQUEST_OK answering request {0}, which is not a TRACK_STATUS"
273    )]
274    TrackPropertiesOnNonTrackStatus(u64),
275    /// A server received a Redirect naming a Connect URI.
276    ///
277    /// Draft-22 Section 9.4.1: "If a server receives a Redirect with a
278    /// non-zero Connect URI Length it MUST close the session with a
279    /// PROTOCOL_VIOLATION." As with GOAWAY, only a client is redirected.
280    #[error("Redirect carrying a Connect URI received at a server")]
281    RedirectUriAtServer,
282    /// A Redirect answering a namespace-scoped request carried a Track Name.
283    ///
284    /// Draft-22 Section 9.4.1: "Track Name is not meaningful for
285    /// namespace-scoped requests (SUBSCRIBE_NAMESPACE, PUBLISH_NAMESPACE,
286    /// SUBSCRIBE_TRACKS) and MUST be empty; an endpoint that receives a
287    /// non-empty Track Name in a Redirect for a namespace-scoped request MUST
288    /// close the session with a PROTOCOL_VIOLATION." Draft-18 names the same
289    /// rule with SUBSCRIBE_TRACKS left out of the list.
290    #[error("Redirect for namespace-scoped request {0} carries a track name")]
291    RedirectTrackNameOnNamespaceRequest(u64),
292    /// A server received a GOAWAY carrying a New Session URI.
293    ///
294    /// Draft-22 Section 9.2: "If a server receives a GOAWAY with a non-zero
295    /// New Session URI Length it MUST close the session with a
296    /// PROTOCOL_VIOLATION." Only a client can be redirected.
297    #[error("GOAWAY carrying a New Session URI received at a server")]
298    GoAwayUriAtServer,
299    /// The peer reused a Request ID it had already spent.
300    ///
301    /// Draft-22 Section 6.4.2.1: "If an endpoint receives a Request ID where the
302    /// least significant bit is incorrect for the sender, or a duplicate
303    /// Request ID, it MUST close the session with INVALID_REQUEST_ID."
304    #[error("request {0} was already used by the peer")]
305    DuplicateRequestId(u64),
306    /// A bidirectional stream the peer opened began with a message that does
307    /// not open a request stream.
308    ///
309    /// Draft-22 Section 6.3: "Bidirectional streams MUST NOT begin with any
310    /// other message type unless negotiated. If they do, the peer MUST close
311    /// the Session with a PROTOCOL_VIOLATION."
312    #[error("{0:?} does not begin a request stream")]
313    NotARequest(MessageType),
314    /// A message that this endpoint may not write on a request stream the peer
315    /// opened was handed to the responder path. Nothing was written and no
316    /// state moved.
317    #[error("{0:?} is not a message a responder writes on a peer's request stream")]
318    NotAResponse(MessageType),
319    /// A message arrived on a request stream the peer opened that may not
320    /// follow a request there.
321    ///
322    /// This endpoint is the responder on such a stream, so a response arriving
323    /// on it is the peer answering its own request.
324    #[error("{0:?} may not follow a request on a stream the peer opened")]
325    UnexpectedOnPeerRequestStream(MessageType),
326    /// A namespace-scoped request's response half opened with something other
327    /// than REQUEST_OK or REQUEST_ERROR.
328    ///
329    /// Draft-22 Section 4.2, of SUBSCRIBE_NAMESPACE: "The publisher MUST send
330    /// a single SUBSCRIBE_NAMESPACE_OK or SUBSCRIBE_NAMESPACE_ERROR as the first
331    /// message on the response half of the stream; if the subscriber receives
332    /// any other message first, it MUST close the session with a
333    /// PROTOCOL_VIOLATION." Section 3.6 says the same of SUBSCRIBE_TRACKS.
334    /// Both names are Section 1.5's shorthand for a REQUEST_OK and a
335    /// REQUEST_ERROR.
336    #[error("request {0} answered with {1:?} before its REQUEST_OK or REQUEST_ERROR")]
337    ResponseBeforeTheFirstResponse(u64, MessageType),
338    /// Track Properties were put on a REQUEST_OK answering something other
339    /// than a TRACK_STATUS, on the way out. Nothing was written.
340    ///
341    /// The send-side mirror of
342    /// [`TrackPropertiesOnNonTrackStatus`](Self::TrackPropertiesOnNonTrackStatus):
343    /// Section 9.3 answers receiving them with a session close, so writing
344    /// them would hand a conforming peer a reason to close this session. This
345    /// one is not fatal — nothing reached the wire, so there is nothing for the
346    /// peer to object to.
347    #[error("request {0} is not a TRACK_STATUS; only its response carries track properties")]
348    TrackPropertiesOnOutgoingRequestOk(u64),
349    /// A REQUEST_UPDATE arrived on a stream that had already used up the
350    /// concurrency this endpoint advertised.
351    ///
352    /// Draft-22 Section 9.1.7: "If an endpoint receives a REQUEST_UPDATE on
353    /// a stream that already has MAX_REQUEST_UPDATES outstanding
354    /// REQUEST_UPDATEs, it MUST close the session with
355    /// TOO_MANY_REQUEST_UPDATES."
356    #[error("request {0} already has the {1} outstanding REQUEST_UPDATEs it was allowed")]
357    TooManyRequestUpdates(u64, u64),
358    /// A REQUEST_OK was offered for a request whose Range Filters this endpoint
359    /// is required to reject.
360    ///
361    /// Not fatal, and deliberately not raised where the filter arrives. Every
362    /// Range Filter rule in Section 3.3.2 is answered with a REQUEST_ERROR, and
363    /// a REQUEST_ERROR names the Request ID of the request it answers — so the
364    /// request has to be taken before it can be refused. What this stops is the
365    /// other answer: accepting the request the draft says to reject leaves the
366    /// subscriber with a subscription whose filters this endpoint never agreed
367    /// to apply, and a publisher that then forwards by its own reading of them.
368    #[error("request {0} must be answered with REQUEST_ERROR: {1}")]
369    FilterMustBeRejected(u64, FilterRejection),
370    /// A REQUEST_OK or REQUEST_ERROR was offered as the answer to a
371    /// REQUEST_UPDATE on a stream with no update waiting for one.
372    ///
373    /// Section 9.5 requires "exactly one REQUEST_UPDATE_OK or
374    /// REQUEST_UPDATE_ERROR message indicating if the update was successful" —
375    /// a REQUEST_OK or a REQUEST_ERROR, under Section 1.5's names — so an answer with nothing to
376    /// answer is one the peer will read as belonging to an update it never
377    /// sent. Not fatal: nothing was written.
378    ///
379    /// A SUBSCRIBE is answered with SUBSCRIBE_OK and a FETCH with FETCH_OK, so
380    /// on those two streams a REQUEST_OK can be nothing but an update's answer
381    /// and this is what a mistimed one produces. On the five kinds REQUEST_OK
382    /// answers itself, the first one is the request's and only the ones after
383    /// it can reach here.
384    #[error("request {0} has no REQUEST_UPDATE waiting for an answer")]
385    NoUpdateToAnswer(u64),
386    /// An update was refused and the subscription it belongs to was then ended
387    /// under some status other than the one that names why.
388    ///
389    /// Section 9.5.1: "When a REQUEST_UPDATE is unsuccessful, the publisher MUST
390    /// also terminate the subscription by sending a PUBLISH_DONE with error
391    /// code UPDATE_FAILED." The REQUEST_ERROR is half of what that sentence
392    /// asks for and the termination is the other half, so this endpoint holds
393    /// the request to it: whatever else the caller writes first, the
394    /// termination it does write says so.
395    #[error("request {request}'s update was refused, so its PUBLISH_DONE must carry {required}")]
396    WrongUpdateFailureStatus {
397        /// The request whose update was refused.
398        request: u64,
399        /// The status code the termination must carry.
400        required: u64,
401    },
402    #[error("session not active")]
403    NotActive,
404    #[error("session is draining, no new requests allowed")]
405    Draining,
406    /// A second GOAWAY arrived on the control stream.
407    ///
408    /// The GOAWAY that says the peer is going away is one message, and the
409    /// draft answers a repeat of it with a session close rather than with an
410    /// error about the second message: there is no state a second one could
411    /// move that the first has not already moved.
412    #[error("a second GOAWAY arrived on the control stream")]
413    RepeatedGoAway,
414    /// A second GOAWAY arrived on one request's stream.
415    ///
416    /// The count is per stream rather than per session: this draft lets a
417    /// GOAWAY migrate a single request, so one on each of two request streams
418    /// is two first GOAWAYs and not a repeat.
419    #[error("a second GOAWAY arrived on request {0}'s stream")]
420    RepeatedGoAwayOnRequestStream(
421        /// The Request ID of the stream that carried both.
422        u64,
423    ),
424    /// The peer named a Track Alias it is already using for another track.
425    ///
426    /// Draft-22 Section 3.1.3: "The same Track Alias MUST NOT be used by a
427    /// publisher to refer to two different Tracks simultaneously in the same
428    /// session. If a subscriber receives a PUBLISH or SUBSCRIBE_OK that uses
429    /// the same Track Alias as a different Track with an Established
430    /// subscription, it MUST close the session with error
431    /// DUPLICATE_TRACK_ALIAS."
432    ///
433    /// The session is over: this endpoint's own state has moved to Closed and
434    /// the code the transport should close with is in
435    /// [`EndpointError::session_error_code`].
436    #[error("track alias {alias} already names request {established}'s track; request {offered} names a different one")]
437    DuplicateTrackAlias {
438        /// The alias both tracks are named by.
439        alias: u64,
440        /// The request whose Established subscription holds the alias.
441        established: u64,
442        /// The request whose message arrived naming it for another track.
443        offered: u64,
444    },
445    /// This endpoint was asked to give a Track Alias to a second track.
446    ///
447    /// The same rule as [`EndpointError::DuplicateTrackAlias`] read at the end
448    /// that chooses the alias. Section 3.1.3 states it as a prohibition on the
449    /// publisher before it states what the subscriber does about one: "The
450    /// same Track Alias MUST NOT be used by a publisher to refer to two different Tracks
451    /// simultaneously in the same session."
452    ///
453    /// The message is refused instead of built, and nothing else moves: no
454    /// Request ID is spent, no publish flow is created, and the session stays
455    /// as it was. The alias never reaches the peer, so there is nothing for
456    /// the peer to close over.
457    #[error("track alias {alias} already names request {held}'s track")]
458    TrackAliasInUse {
459        /// The alias that is already spoken for.
460        alias: u64,
461        /// The request whose live flow holds it.
462        held: u64,
463    },
464    /// An object arriving after the track's final object.
465    ///
466    /// Section 12.1 lists the condition: "An Object is received whose
467    /// Group and Object ID are larger
468    /// than the final Object in the Track.
469    /// The final Object in a Track is the Object with Status END_OF_TRACK or
470    /// the last Object sent in a FETCH whose response indicated End of Track."
471    ///
472    /// **Larger is Section 8.2's comparison and not a reading of the
473    /// words.** That section puts one Location below another when "A.Group <
474    /// B.Group || (A.Group == B.Group && A.Object < B.Object)", so an Object in
475    /// a later group is past the end whatever its own Object ID is.
476    ///
477    /// **A Malformed Track and not a session error**, and on this draft not a
478    /// message either. Section 12.1 answers its whole list at once with "it
479    /// MUST cancel any corresponding subscription or fetches for that Track
480    /// from that publisher", where cancelling a request is the transport
481    /// operation Section 6.4.2.3 describes. This is the error half; the
482    /// requests to cancel are named by
483    /// [`Endpoint::requests_for_malformed_track`].
484    #[error(
485        "the object at group {group}, object {object} on track alias {alias} arrived \
486         after the track's final object at group {final_group}, object {final_object}"
487    )]
488    ObjectPastFinalObject {
489        /// The Track Alias the offending object named.
490        alias: u64,
491        /// The Group ID it named.
492        group: u64,
493        /// The Object ID it named.
494        object: u64,
495        /// The Group ID of the object the track ended at.
496        final_group: u64,
497        /// The Object ID of the object the track ended at.
498        final_object: u64,
499    },
500
501    /// A PUBLISH_STATE_NOTIFY arrived for a request that is not a
502    /// subscription.
503    ///
504    /// Section 9.10: "PUBLISH_STATE_NOTIFY applies only to subscriptions, and
505    /// is sent only by the publisher. An endpoint that receives a
506    /// PUBLISH_STATE_NOTIFY for any other request type, or from the subscriber,
507    /// MUST close the session with a PROTOCOL_VIOLATION."
508    ///
509    /// This is the first half of that sentence. The session is over: this
510    /// endpoint's own state has moved to Closed and the code the transport
511    /// should close with is in [`EndpointError::session_error_code`].
512    #[error("PUBLISH_STATE_NOTIFY for request {0}, which is not a subscription")]
513    StateNotifyForNonSubscription(u64),
514
515    /// A PUBLISH_STATE_NOTIFY arrived from the subscriber.
516    ///
517    /// The second half of the same sentence. A subscription's publisher is the
518    /// end that did not send the SUBSCRIBE, so one arriving on a stream this
519    /// endpoint opened with a SUBSCRIBE is the publisher's and one arriving on
520    /// a stream the peer opened with a SUBSCRIBE is the subscriber's — which is
521    /// what this refuses, and what the section answers with a session close.
522    #[error(
523        "PUBLISH_STATE_NOTIFY on request {0} may only be sent by that subscription's publisher"
524    )]
525    StateNotifyFromSubscriber(u64),
526
527    /// A PUBLISH_STATE_NOTIFY was offered for a request this endpoint does not
528    /// publish a subscription for. Nothing was written and no state moved.
529    ///
530    /// The send-side mirror of the two above, and not fatal for the same reason
531    /// [`EndpointError::TrackPropertiesOnOutgoingRequestOk`] is not: nothing
532    /// reached the wire, so there is nothing for the peer to object to. What it
533    /// prevents is handing a conforming peer a message Section 9.10 tells it to
534    /// close the session over.
535    #[error("request {0} is not a subscription this endpoint publishes, so it sends no PUBLISH_STATE_NOTIFY")]
536    StateNotifyThisEndpointMayNotSend(u64),
537
538    /// A fill fetch stream opened against a request that asked for no fill.
539    ///
540    /// Section 9.20.15 makes `FILL_PARAMETERS` the whole of the request: "Its
541    /// presence is what requests a fill fetch stream; a subscription with no
542    /// FILL_PARAMETERS opens none." A FETCH_HEADER naming a subscription that
543    /// never asked is a stream this endpoint has nothing to attribute, and one
544    /// naming a request that is not a subscription at all is worse.
545    ///
546    /// Not a session close. Draft-22 states no rule for it — a fill fetch
547    /// stream has no FETCH_OK and no REQUEST_ERROR, so there is no answer to
548    /// send and no sentence naming a code — and the honest handling is
549    /// `STOP_SENDING` on that stream alone, which Section 3.4.1 gives the
550    /// subscriber outright and says "does not affect the subscription".
551    #[error("request {0} opened a fill fetch stream without asking for a fill")]
552    UnrequestedFillStream(u64),
553    /// The peer subscribed to a namespace prefix overlapping one it is
554    /// already subscribed to.
555    ///
556    /// Section 4.2: "Within a session, if a publisher receives a
557    /// SUBSCRIBE_NAMESPACE with a Track Namespace Prefix that shares a common
558    /// prefix with an established SUBSCRIBE_NAMESPACE, it MUST respond with
559    /// SUBSCRIBE_NAMESPACE_ERROR with error code PREFIX_OVERLAP."
560    ///
561    /// Section 3.6: "Within a session, if a publisher receives a
562    /// SUBSCRIBE_TRACKS with a Track Namespace Prefix that shares a common
563    /// prefix with an established SUBSCRIBE_TRACKS, it MUST respond with
564    /// SUBSCRIBE_TRACKS_ERROR with error code PREFIX_OVERLAP."
565    ///
566    /// Both answers are a REQUEST_ERROR under Section 1.5's names.
567    ///
568    /// Section 12.3: "SUBSCRIBE_NAMESPACE and SUBSCRIBE_TRACKS have
569    /// independent overlap spaces, so a SUBSCRIBE_NAMESPACE and a
570    /// SUBSCRIBE_TRACKS may share the same prefix."
571    ///
572    /// Taken when the message arrives, which is the moment the sentence
573    /// names, and read again when an answer is built: a request this endpoint
574    /// may not accept is one no later call can accept.
575    ///
576    /// The refusal itself is not this error. It is a message the peer is
577    /// owed, so the request is recorded like any other and refused through
578    /// the same call that refuses any other, under the code the sentence
579    /// names.
580    #[error(
581        "request {request} subscribes to a namespace prefix overlapping request {established}"
582    )]
583    PeerPrefixOverlap {
584        /// The request that arrived.
585        request: u64,
586        /// The namespace subscription it overlaps.
587        established: u64,
588    },
589    /// A namespace subscription that overlaps another was refused under a
590    /// code other than the one the sentence names.
591    ///
592    /// A rule that names the code its refusal carries is not satisfied by a
593    /// refusal under any other, because the peer reads the code to learn what
594    /// went wrong. Draft-19 had a second rule of this shape — a Joining Fetch
595    /// naming no live subscription took INVALID_JOINING_REQUEST_ID and nothing
596    /// else — and draft-20 deleted the mechanism and the code together, so this
597    /// is the only one left.
598    #[error("request {request} overlaps a namespace subscription and must be refused with code {required:#x}")]
599    WrongOverlapRefusal {
600        /// The request being refused.
601        request: u64,
602        /// The code the sentence names for it.
603        required: u64,
604    },
605}
606
607/// Whether two namespace prefixes overlap.
608///
609/// Section 4.2: "Within a session, if a publisher receives a
610/// SUBSCRIBE_NAMESPACE with a Track Namespace Prefix that shares a common
611/// prefix with an established SUBSCRIBE_NAMESPACE, it MUST respond with
612/// SUBSCRIBE_NAMESPACE_ERROR with error code PREFIX_OVERLAP."
613///
614/// Section 3.6: "Within a session, if a publisher receives a
615/// SUBSCRIBE_TRACKS with a Track Namespace Prefix that shares a common prefix
616/// with an established SUBSCRIBE_TRACKS, it MUST respond with
617/// SUBSCRIBE_TRACKS_ERROR with error code PREFIX_OVERLAP."
618///
619/// Section 12.3: "SUBSCRIBE_NAMESPACE and SUBSCRIBE_TRACKS have independent
620/// overlap spaces, so a SUBSCRIBE_NAMESPACE and a SUBSCRIBE_TRACKS may share
621/// the same prefix."
622///
623/// A namespace matches a namespace subscription when the subscription's
624/// prefix is a prefix of it, so two prefixes select overlapping sets of
625/// namespaces exactly when one of them is a prefix of the other. Equal
626/// prefixes are that case as well: every prefix is a prefix of itself, and
627/// two equal ones select the same set.
628///
629/// "Shares a common prefix with" is read as the relation drafts 07 through 14
630/// spell out at greater length. Taken at its word it would forbid every
631/// second namespace subscription in a session, since any two prefixes share
632/// the empty one, and nothing else in this draft supports that.
633fn prefixes_overlap(a: &[Vec<u8>], b: &[Vec<u8>]) -> bool {
634    let shared = a.len().min(b.len());
635    a[..shared] == b[..shared]
636}
637
638/// Parameter Type of TRACK_NAMESPACE_PREFIX.
639const TRACK_NAMESPACE_PREFIX: u64 = 0x34;
640
641/// The Track Namespace Prefix a REQUEST_UPDATE asks a namespace subscription
642/// to move to, and `None` when it asks for no such move.
643///
644/// Section 9.20.20: "The TRACK_NAMESPACE_PREFIX parameter (Parameter Type
645/// 0x34) uses the Track Namespace encoding described in Section 8.7. It MAY
646/// appear in REQUEST_UPDATE for a SUBSCRIBE_NAMESPACE or SUBSCRIBE_TRACKS
647/// request. It updates the Track Namespace Prefix for that subscription."
648///
649/// A prefix of no fields is a value rather than a removal. Section 2.4.1 puts
650/// a Track Namespace at "between 0 and 32 Track Namespace Fields", an empty
651/// prefix selects every namespace, and Section 9.5 leaves no other reading
652/// open: "There is no generic mechanism to remove a parameter from a
653/// request."
654///
655/// The last one wins when a message carries the parameter twice. That is the
656/// rule Section 9.5 gives for two messages -- "Parameter values from later
657/// REQUEST_UPDATE messages override values from earlier ones" -- applied
658/// inside one, which is the only reading under which a repeat means anything.
659///
660/// A value that is not a whole Track Namespace answers `None`, so an update
661/// carrying one leaves the prefix where it was. The decoder that built the
662/// message has already refused a malformed one; this is the arm that keeps a
663/// hand-built message from moving a subscription to half a prefix.
664fn updated_prefix(parameters: &[KeyValuePair]) -> Option<TrackNamespace> {
665    parameters.iter().rfind(|p| p.key.into_inner() == TRACK_NAMESPACE_PREFIX).and_then(|p| match &p
666        .value
667    {
668        KvpValue::Bytes(bytes) => {
669            let mut cursor = &bytes[..];
670            let prefix = TrackNamespace::decode_allow_empty_moqt::<Moqt18>(&mut cursor).ok()?;
671            cursor.is_empty().then_some(prefix)
672        }
673        KvpValue::Varint(_) => None,
674    })
675}
676
677impl EndpointError {
678    /// Whose doing this is — the peer's, or this endpoint's, or a variant that
679    /// cannot say.
680    ///
681    /// The companion of [`EndpointError::session_error_code`], which answers
682    /// *what the draft requires be done about it*. Neither answers the other's
683    /// question and the pair is what a caller needs: a code without a side
684    /// names nobody, and a side without a code is not grounds to publish
685    /// anything.
686    ///
687    /// Exhaustive, with no wildcard arm, so a variant added to this draft's
688    /// `EndpointError` is a compile error here rather than a silent arrival on
689    /// the wrong side of the answer. See
690    /// [`EndpointFault`](crate::above_codec_rules::EndpointFault) for the three
691    /// answers and for the collision that made the third one necessary.
692    pub fn fault(&self) -> crate::above_codec_rules::EndpointFault {
693        use crate::above_codec_rules::{AboveCodecRule as Rule, EndpointFault as Fault};
694
695        match self {
696            // Raised on both a receive path and a send path, so the
697            // variant cannot say which end is at fault. The state machines
698            // render as `invalid transition from X on event Y` whichever end
699            // asked for the transition, and the unknown-request errors name
700            // an id that may be one the peer sent or one a caller here made
701            // up.
702            EndpointError::Session(..)
703            | EndpointError::Subscription(..)
704            | EndpointError::Fetch(..)
705            | EndpointError::Fill(..)
706            | EndpointError::Namespace(..)
707            | EndpointError::TrackStatus(..)
708            | EndpointError::PublishFlow(..)
709            | EndpointError::Setup(..)
710            | EndpointError::UnknownRequest(..) => Fault::EitherEnd,
711
712            // Raised on the way out. Nothing reached the wire, so none of
713            // these is evidence about a peer — including the ones a peer
714            // caused, where what failed is this side's attempt to accept
715            // something the draft says to refuse.
716            EndpointError::NotAResponse(..)
717            | EndpointError::TrackPropertiesOnOutgoingRequestOk(..)
718            | EndpointError::FilterMustBeRejected(..)
719            | EndpointError::NoUpdateToAnswer(..)
720            | EndpointError::WrongUpdateFailureStatus { .. }
721            | EndpointError::NotActive
722            | EndpointError::Draining
723            | EndpointError::TrackAliasInUse { .. }
724            | EndpointError::StateNotifyThisEndpointMayNotSend(..)
725            | EndpointError::PeerPrefixOverlap { .. }
726            | EndpointError::WrongOverlapRefusal { .. } => Fault::ThisEndpoint,
727
728            // Raised reading what the peer sent.
729            EndpointError::NotARequest(..) => Fault::Peer(Rule::BidiStreamOpener),
730            EndpointError::DuplicateTrackAlias { .. } => Fault::Peer(Rule::DuplicateTrackAlias),
731            EndpointError::GoAwayUriAtServer => Fault::Peer(Rule::GoAwayAtServer),
732            EndpointError::ResponseOnControlStream
733            | EndpointError::RequestMessageOnControlStream(..)
734            | EndpointError::UnexpectedOnPeerRequestStream(..) => {
735                Fault::Peer(Rule::MessageOnTheWrongStream)
736            }
737            EndpointError::ObjectPastFinalObject { .. } => Fault::Peer(Rule::ObjectPastFinalObject),
738            EndpointError::RedirectTrackNameOnNamespaceRequest(..) => {
739                Fault::Peer(Rule::RedirectTrackNameOnNamespaceRequest)
740            }
741            EndpointError::RedirectUriAtServer => Fault::Peer(Rule::RedirectUriAtServer),
742            EndpointError::RepeatedGoAway | EndpointError::RepeatedGoAwayOnRequestStream(..) => {
743                Fault::Peer(Rule::RepeatedGoAway)
744            }
745            EndpointError::DuplicateRequestId(..) => Fault::Peer(Rule::RequestIdOutOfSequence),
746            // A REQUEST_UPDATE on the control stream is this rule and not
747            // `MessageOnTheWrongStream`. Section 9.5 states the rule as two
748            // permitted cases and closes over everything else — "The sender of
749            // a request (SUBSCRIBE, PUBLISH, FETCH, PUBLISH_NAMESPACE,
750            // SUBSCRIBE_NAMESPACE, SUBSCRIBE_TRACKS) can later send a
751            // REQUEST_UPDATE on the same bidi stream as the request to modify
752            // it. A subscriber can also send REQUEST_UPDATE to modify
753            // parameters of a subscription established with PUBLISH." — and
754            // the first case is *on the same bidi stream as the request*. One
755            // that arrives on the control stream is in neither case, so it is
756            // squarely inside "other than in the two cases above", which is the
757            // sentence this rule cites. Filed under the wrong-stream rule it
758            // would carry a close no draft states; filed here, it carries the
759            // one draft-19 and draft-22 do.
760            EndpointError::RequestUpdateOnControlStream
761            | EndpointError::UnexpectedRequestUpdate(..) => {
762                Fault::Peer(Rule::RequestUpdateForTheWrongRequest)
763            }
764            EndpointError::ResponseBeforeTheFirstResponse(..) => {
765                Fault::Peer(Rule::ResponseBeforeItsFirstResponse)
766            }
767            EndpointError::StateNotifyForNonSubscription(..)
768            | EndpointError::StateNotifyFromSubscriber(..) => {
769                Fault::Peer(Rule::StateNotifyOnTheWrongRequest)
770            }
771            EndpointError::TooManyRequestUpdates(..) => Fault::Peer(Rule::TooManyRequestUpdates),
772            EndpointError::TrackPropertiesOnNonTrackStatus(..) => {
773                Fault::Peer(Rule::TrackPropertiesOnNonTrackStatus)
774            }
775            EndpointError::UnrequestedFillStream(..) => Fault::Peer(Rule::UnrequestedFillStream),
776
777            // The Request ID rules, which are the peer's: every one of them is
778            // read off an id the peer put on the wire. This draft carries no
779            // MAX_REQUEST_ID, so neither ceiling arm can fire — the allocator
780            // opens at `u64::MAX` and nothing ever lowers it - and they are
781            // answered rather than left out so that a draft restoring the
782            // message does not restore a hole with it.
783            EndpointError::RequestId(e) => match e {
784                RequestIdError::Decreased(..) => Fault::Peer(Rule::MaxRequestIdDecreased),
785                RequestIdError::ExceedsMax(..) => Fault::Peer(Rule::RequestIdCeiling),
786                RequestIdError::WrongParity(..) => Fault::Peer(Rule::RequestIdParity),
787                // This endpoint has spent the budget the peer granted it.
788                RequestIdError::Blocked => Fault::ThisEndpoint,
789            },
790        }
791    }
792
793    /// The code to close the session with, when draft-22 says this error is
794    /// fatal to the session rather than to one request.
795    ///
796    /// `None` means the error is recoverable: the caller may report it and
797    /// keep the session running. `Some` means the draft requires a close, and
798    /// the endpoint has already moved its own session state to
799    /// [`SessionState::Closed`] — the code is what the transport should carry.
800    ///
801    /// The two codes are not interchangeable. Section 6.3 gives
802    /// PROTOCOL_VIOLATION for a bidirectional stream that begins with the wrong
803    /// message type; Section 6.4.2.1 gives INVALID_REQUEST_ID for a Request ID
804    /// with the wrong least significant bit or a duplicate one. A peer checking
805    /// close codes can tell the two apart, so this must too.
806    pub fn session_error_code(&self) -> Option<SessionErrorCode> {
807        match self {
808            EndpointError::RequestUpdateOnControlStream
809            | EndpointError::UnexpectedRequestUpdate(_)
810            | EndpointError::TrackPropertiesOnNonTrackStatus(_)
811            | EndpointError::RedirectUriAtServer
812            | EndpointError::RedirectTrackNameOnNamespaceRequest(_)
813            | EndpointError::ResponseBeforeTheFirstResponse(..)
814            | EndpointError::NotARequest(_)
815            | EndpointError::GoAwayUriAtServer
816            | EndpointError::RepeatedGoAway
817            | EndpointError::RepeatedGoAwayOnRequestStream(_)
818            // Section 9.10 names the code in the sentence that states the
819            // rule: a PUBLISH_STATE_NOTIFY "for any other request type, or from
820            // the subscriber, MUST close the session with a
821            // PROTOCOL_VIOLATION".
822            | EndpointError::StateNotifyForNonSubscription(_)
823            | EndpointError::StateNotifyFromSubscriber(_) => {
824                Some(SessionErrorCode::ProtocolViolation)
825            }
826            EndpointError::DuplicateRequestId(_)
827            | EndpointError::RequestId(RequestIdError::WrongParity(..)) => {
828                Some(SessionErrorCode::InvalidRequestId)
829            }
830            EndpointError::TooManyRequestUpdates(..) => {
831                Some(SessionErrorCode::TooManyRequestUpdates)
832            }
833            // Section 3.1.3 names this code in the sentence that states the
834            // rule, and names no other. A close carrying PROTOCOL_VIOLATION
835            // would tell the peer a different thing went wrong.
836            EndpointError::DuplicateTrackAlias { .. } => {
837                Some(SessionErrorCode::DuplicateTrackAlias)
838            }
839            // Stated rather than left to the arm below, because the neighbour
840            // above makes the opposite choice about a rule of the same shape.
841            // MAX_REQUEST_UPDATES is a ceiling the draft answers with a session
842            // close; MAX_FILTER_RANGES is a ceiling it answers with a
843            // REQUEST_ERROR. Two consecutive subsections, two different answers,
844            // and nothing about either sentence signals which.
845            EndpointError::FilterMustBeRejected(..) => None,
846            _ => None,
847        }
848    }
849}
850
851pub struct Endpoint {
852    role: Role,
853    session: SessionStateMachine,
854    request_ids: RequestIdAllocator,
855    subscriptions: HashMap<u64, SubscriptionStateMachine>,
856    fetches: HashMap<u64, FetchStateMachine>,
857    /// Which subscriptions have asked for a fill fetch stream, and how many of
858    /// their streams are open.
859    ///
860    /// Keyed by the subscription's Request ID, which is also the Request ID a
861    /// fill fetch stream's FETCH_HEADER carries — Section 3.4: "the SUBSCRIBE
862    /// Request ID for the initial fill, or the REQUEST_UPDATE Request ID for a
863    /// subsequent fill", and a REQUEST_UPDATE travels on its request's own
864    /// stream under that request's Request ID (Section 9.5). So the two ids
865    /// the section distinguishes are the same number, and the same section
866    /// nonetheless says a subscription "can have multiple fill fetch streams
867    /// open at once, each identified by its Request ID". **The draft is
868    /// inconsistent here and the identifier cannot separate them**, so this
869    /// counts the open streams rather than pretending to tell them apart, which
870    /// is what a PUBLISH_DONE's Stream Count needs anyway.
871    ///
872    /// An entry appears when a SUBSCRIBE or a REQUEST_UPDATE carrying
873    /// `FILL_PARAMETERS` is sent or received, and is never removed: Section
874    /// 9.20.15 keeps `FILL_PARAMETERS` off the sticky-parameter rules, but a
875    /// subscription that asked once is a subscription whose later FETCH_HEADERs
876    /// are attributable, and a Request ID is spent once so an entry can never
877    /// come to describe a different request.
878    fills: HashMap<u64, FillStreams>,
879    subscribe_namespaces: HashMap<u64, SubscribeNamespaceStateMachine>,
880    subscribe_tracks: HashMap<u64, SubscribeNamespaceStateMachine>,
881    publish_namespaces: HashMap<u64, PublishNamespaceStateMachine>,
882    track_statuses: HashMap<u64, TrackStatusStateMachine>,
883    publishes: HashMap<u64, PublishStateMachine>,
884    goaway_uri: Option<Vec<u8>>,
885    /// The peer's requests whose stream has already carried a GOAWAY.
886    ///
887    /// Section 9.2 makes the second GOAWAY on one request stream a session
888    /// close while leaving a first one on every other stream legal, so the
889    /// count cannot live on the session. Held apart from the per-kind request
890    /// maps because a GOAWAY says nothing about which kind of request it
891    /// migrates.
892    goaway_request_streams: HashSet<u64>,
893    /// Every Request ID the peer has spent, whether or not the request it
894    /// opened is still live.
895    ///
896    /// Draft-22 Section 6.4.2.1 makes a duplicate Request ID a session close, and
897    /// "duplicate" is about the id ever having been used, not about the
898    /// request still being open. Deriving it from the per-kind maps instead
899    /// would answer wrongly the moment those maps are ever pruned, so the rule
900    /// is stated once, here, and this set is never pruned.
901    peer_request_ids: HashSet<u64>,
902    /// Every request the **peer** opened a stream with, as it arrived, keyed
903    /// by the Request ID it carries.
904    ///
905    ///
906    /// One map for all seven kinds, because one entry point takes all seven:
907    /// [`receive_request_on_stream`](Self::receive_request_on_stream) is the
908    /// only thing that writes here, so what is in here is a request, and it
909    /// is the peer's, by construction. How far each one has got stays in the
910    /// per-kind map beside this endpoint's own requests, which is where every
911    /// later message on the stream reads it and where the parity of a Request
912    /// ID keeps the two ends apart.
913    ///
914    /// What a map of state machines cannot hold is the request. Section 3.1 has
915    /// the subscriber "either accepts or rejects the subscription", and what is
916    /// being accepted or rejected -- the track, the namespace prefix, the
917    /// parameters -- is named in the request and nowhere else once the message
918    /// has been dropped.
919    inbound_requests: HashMap<u64, ControlMessage>,
920    /// The request each namespace subscription of the peer's overlapped when
921    /// it arrived, keyed by the Request ID of the one that arrived.
922    ///
923    /// The prefix is judged where the sentence says it is judged, on receipt,
924    /// and the verdict is read again when the answer is written. An entry
925    /// means this endpoint owes that request a REQUEST_ERROR and may send it
926    /// nothing else.
927    overlapping_namespace_subscriptions: HashMap<u64, u64>,
928    /// The Track Namespace Prefix a REQUEST_UPDATE asks one of the peer's
929    /// namespace subscriptions to move to, held until that update is
930    /// answered.
931    ///
932    /// Section 9.5.2 ties the move to the acceptance: "If the update is
933    /// accepted, NAMESPACE and NAMESPACE_DONE messages following the
934    /// REQUEST_OK will contain Track Namespace suffixes relative to the
935    /// updated prefix." Until then the subscription still selects what the
936    /// peer opened it with, so a later request is weighed against the prefix
937    /// in `inbound_requests` and not against this one.
938    updated_namespace_prefixes: HashMap<u64, TrackNamespace>,
939    /// The subscription an unanswered prefix update would collide with, when
940    /// it would, keyed by the Request ID of the one being moved.
941    ///
942    /// Separate from `overlapping_namespace_subscriptions` because one
943    /// Request ID can be carrying both verdicts at once: the request that
944    /// opened the stream has one and an update on it has another, and they
945    /// are settled by different messages on that same stream.
946    overlapping_prefix_updates: HashMap<u64, u64>,
947    /// The MAX_REQUEST_UPDATES this endpoint put in its own SETUP.
948    ///
949    /// The peer's value is a different number and belongs to the sending side,
950    /// which this type has no path for: draft-22 gives the endpoint no
951    /// REQUEST_UPDATE builder, so there is nothing here to hold back.
952    ///
953    /// Zero means no limit rather than none allowed, which is the opposite of
954    /// how MAX_REQUEST_ID reads. Section 9.1.7 says so outright - "A value
955    /// of 0 means the endpoint does not limit REQUEST_UPDATE concurrency. If
956    /// not present, the default value is 0" - so an endpoint that never sends
957    /// the option is not limiting anything, and a check that read zero as a
958    /// ceiling would refuse the first update of every session.
959    advertised_max_request_updates: u64,
960    /// Per request stream, how many REQUEST_UPDATEs the peer has sent that this
961    /// endpoint has not yet answered.
962    ///
963    /// "Outstanding" is Section 9.1.7's word and it is per stream, not per
964    /// session: "Each REQUEST_OK or REQUEST_ERROR response restores one credit
965    /// on that stream."
966    outstanding_peer_updates: HashMap<u64, u64>,
967    /// Per request stream, how many REQUEST_UPDATEs are still waiting for the
968    /// answer Section 9.5 requires.
969    ///
970    /// A second count rather than a reading of the one above, because the two
971    /// sentences count different things and disagree on exactly one response.
972    /// Section 9.1.7 says "Each REQUEST_OK or REQUEST_ERROR response
973    /// restores one credit on that stream" — every response, including the
974    /// REQUEST_OK that answers a SUBSCRIBE_NAMESPACE rather than an update. That
975    /// is the sender's accounting rule as much as the receiver's, and an
976    /// endpoint that credited more carefully than the peer does would close a
977    /// conforming session, so the credit above stays literal.
978    ///
979    /// This one is about which response answers which message, and there the
980    /// request's own REQUEST_OK answers the request. Keeping them apart is what
981    /// lets a response be recognised as an update's answer without changing
982    /// what the peer is allowed to send.
983    unanswered_peer_updates: HashMap<u64, u64>,
984    /// The requests whose refused update has not been followed by the
985    /// PUBLISH_DONE that ends them.
986    ///
987    /// Emptied as each is written. A request is in here for exactly as long as
988    /// this endpoint owes the peer the second half of a refusal.
989    owed_update_failures: HashSet<u64>,
990    /// The peer's requests whose own response has already been written.
991    ///
992    /// Section 9.5 gives an update the same two answers a request has, and on
993    /// the five kinds REQUEST_OK answers, the message that answers the request
994    /// and the message that answers an update are the same message. Nothing on
995    /// the wire tells them apart, so both endpoints resolve it by order: the
996    /// first response on a stream answers the request that opened it and the
997    /// ones after it answer updates. This set is that order, recorded.
998    answered_peer_requests: HashSet<u64>,
999    /// The MAX_FILTER_RANGES this endpoint put in its own SETUP.
1000    ///
1001    /// Section 9.1.6 makes it a ceiling on the peer's total number of Ranges
1002    /// (Start/End pairs) allowed concurrently in all Range filter parameters for
1003    /// a given subscription or fetch, and fixes what its absence means: "The
1004    /// default value is 0, so if not specified, the peer MUST NOT send any such
1005    /// filter parameters."
1006    ///
1007    /// So zero is none allowed, and it is the default. The option in the
1008    /// subsection after this one, MAX_REQUEST_UPDATES, reads its zero the other
1009    /// way — no limit — and the two are otherwise the same shape. Reading either
1010    /// with the other's rule is a working implementation that is wrong in one
1011    /// direction or the other for every session.
1012    advertised_max_filter_ranges: u64,
1013    /// Requests whose Range Filters this endpoint owes the peer a REQUEST_ERROR
1014    /// about.
1015    ///
1016    /// Keyed by Request ID, because that is what the reply names. Entries are
1017    /// spent by the REQUEST_ERROR that answers them, and a REQUEST_OK offered
1018    /// for one is refused: taking the request in and then accepting it would
1019    /// leave the peer with a subscription whose filters were never agreed.
1020    peer_filter_rejections: HashMap<u64, FilterRejection>,
1021    /// The Range Filter parameters currently in force on each of the peer's
1022    /// requests.
1023    ///
1024    /// Section 3.3.2 makes the ceiling a property of the request rather than of
1025    /// the message that carried it — "the total number of Ranges allowed in all
1026    /// Range Filter parameters for a given subscription or fetch" — and lets a
1027    /// REQUEST_UPDATE rewrite the set: "In REQUEST_UPDATE, Length of 0 removes
1028    /// the filter; non-zero replaces it entirely. If a filter parameter is
1029    /// omitted from REQUEST_UPDATE, it is unchanged."
1030    ///
1031    /// So the count that matters is of what is in force after the update, and
1032    /// the parameters an update leaves alone are part of it. Held as the
1033    /// parameters rather than as decoded filters because replacement is by
1034    /// Parameter Type, which is the key of the pair.
1035    peer_request_filters: HashMap<u64, Vec<KeyValuePair>>,
1036    /// What Full Track Name the peer has attached each Track Alias to, per
1037    /// Request ID.
1038    ///
1039    /// Section 3.1.3 forbids one alias naming two tracks at once, and the "at
1040    /// once" is what makes this a table rather than a set: an alias the peer
1041    /// used for a track whose subscription has ended is free again. The table
1042    /// therefore records the binding and reads liveness back off the request's
1043    /// own state machine, rather than keeping a second copy of it that every
1044    /// path ending a subscription would have to remember to prune.
1045    track_bindings: HashMap<u64, TrackBinding>,
1046    /// The track each fetch this endpoint made is for.
1047    ///
1048    /// Not in `track_bindings`, because that table exists to answer questions
1049    /// about Track Aliases and a fetch has none: its objects arrive on a stream
1050    /// that opens by naming the Request ID.
1051    ///
1052    /// Every fetch names its own track on this draft, which is one thing the
1053    /// FETCH rewrite made simpler. Draft-19's Joining Fetch named none and took
1054    /// the joined subscription's, so the track had to be resolved through the
1055    /// join as the fetch was made rather than at the withdrawal — a fill
1056    /// outlives the subscription it was asked from, so a lookup through the
1057    /// join came up empty exactly while there was still a fetch to cancel.
1058    /// Draft-22 Section 9.11 has no joining fetch; a fill fetch stream
1059    /// belongs to its subscription and is tracked in `fills`.
1060    fetch_tracks: HashMap<u64, FetchTrack>,
1061    /// Which tracks this endpoint has given up on, and what for.
1062    ///
1063    /// Behind a lock because the note is taken on the data plane, where this
1064    /// endpoint is reached through `&self`.
1065    malformed: Mutex<MalformedTracks>,
1066    /// How far each track's objects have reached, and where a track ended.
1067    ///
1068    /// An `Arc` because a subgroup stream measures its objects against one
1069    /// track for as long as it runs, and the handle it holds outlives any
1070    /// single call into this endpoint.
1071    locations: Arc<Mutex<TrackLocations>>,
1072}
1073
1074/// What one subscription's fill fetch streams amount to, from the point of
1075/// view of an endpoint that has to attribute an arriving FETCH_HEADER and
1076/// count the streams a PUBLISH_DONE reports.
1077///
1078/// There is no state machine here on purpose. A fill fetch stream has no
1079/// FETCH_OK and no REQUEST_ERROR (Section 3.4.1), so it has none of the
1080/// states [`FetchStateMachine`] exists to keep apart: it is opened, it carries
1081/// objects, and it ends with a FIN meaning the fill is complete or a reset
1082/// meaning it failed. Neither ending touches the subscription — "Resetting or
1083/// cancelling a fill fetch stream, by either endpoint, does not affect the
1084/// subscription, which continues to deliver objects using subscribe subgroups
1085/// and datagrams."
1086#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1087struct FillStreams {
1088    /// How many of this subscription's fill fetch streams are open.
1089    open: usize,
1090    /// How many have been opened over the life of the subscription, ended or
1091    /// not.
1092    ///
1093    /// Section 9.9 makes the count a PUBLISH_DONE field: Stream Count is the
1094    /// total the publisher opened, "including streams that contained no Objects
1095    /// (e.g., an empty Subgroup) and including any fill fetch streams". So the
1096    /// number that matters is cumulative and the open count cannot answer it.
1097    opened: u64,
1098    /// The Group Order the fill's Objects arrive in, resolved from the request
1099    /// that asked for the fill by [`fill::group_order`].
1100    ///
1101    /// Held here because nothing on a fill fetch stream says which direction its
1102    /// Group ID Deltas count in (Section 11.4.1.1) and the answer is in a
1103    /// control message the stream never sees. Kept per subscription rather than
1104    /// per stream for the reason the field above is: a fill fetch stream carries
1105    /// its subscription's Request ID and nothing else, so two of them cannot be
1106    /// told apart here. A REQUEST_UPDATE that asks for a second fill with a
1107    /// different order **replaces** this, which is right for the stream it opens
1108    /// and wrong for one still running under the older order; a caller in that
1109    /// position restarts the reader itself with
1110    /// [`begin_fetch_objects`](crate::draft22::connection::FramedRecvStream::begin_fetch_objects).
1111    group_order: GroupOrder,
1112}
1113
1114/// The track one fetch is for, which the fetch's own state machine does not
1115/// hold.
1116#[derive(Debug, Clone)]
1117struct FetchTrack {
1118    namespace: TrackNamespace,
1119    name: Vec<u8>,
1120}
1121
1122/// A Track Alias the peer has attached to a Full Track Name, and the request
1123/// whose lifetime the attachment follows.
1124#[derive(Debug, Clone)]
1125struct TrackBinding {
1126    namespace: TrackNamespace,
1127    name: Vec<u8>,
1128    /// The alias, once the peer has named one.
1129    ///
1130    /// A SUBSCRIBE this endpoint sends names a track and waits for its alias,
1131    /// so the binding exists with no alias in it from the moment the request
1132    /// is made until its SUBSCRIBE_OK arrives. A PUBLISH carries both at once
1133    /// and is never in that state.
1134    alias: Option<u64>,
1135    kind: BindingKind,
1136}
1137
1138/// Which of the two sequences Section 3.1 names established the subscription
1139/// that owns a binding, and so which state machine says whether it still has
1140/// one.
1141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1142enum BindingKind {
1143    /// This endpoint's SUBSCRIBE, established by the peer's SUBSCRIBE_OK.
1144    Subscribe,
1145    /// A PUBLISH, established by the PUBLISH_OK answering it - whichever end
1146    /// sent which. Both are held in the same map, which Request ID parity
1147    /// keeps from colliding.
1148    Publish,
1149}
1150
1151impl Endpoint {
1152    pub fn new(role: Role) -> Self {
1153        Self {
1154            role,
1155            session: SessionStateMachine::new(),
1156            request_ids: RequestIdAllocator::new(role),
1157            subscriptions: HashMap::new(),
1158            fetches: HashMap::new(),
1159            fills: HashMap::new(),
1160            subscribe_namespaces: HashMap::new(),
1161            subscribe_tracks: HashMap::new(),
1162            publish_namespaces: HashMap::new(),
1163            track_statuses: HashMap::new(),
1164            publishes: HashMap::new(),
1165            goaway_uri: None,
1166            goaway_request_streams: HashSet::new(),
1167            peer_request_ids: HashSet::new(),
1168            inbound_requests: HashMap::new(),
1169            overlapping_namespace_subscriptions: HashMap::new(),
1170            updated_namespace_prefixes: HashMap::new(),
1171            overlapping_prefix_updates: HashMap::new(),
1172            advertised_max_request_updates: 0,
1173            outstanding_peer_updates: HashMap::new(),
1174            unanswered_peer_updates: HashMap::new(),
1175            owed_update_failures: HashSet::new(),
1176            answered_peer_requests: HashSet::new(),
1177            advertised_max_filter_ranges: 0,
1178            peer_filter_rejections: HashMap::new(),
1179            peer_request_filters: HashMap::new(),
1180            track_bindings: HashMap::new(),
1181            fetch_tracks: HashMap::new(),
1182            malformed: Mutex::new(MalformedTracks::new()),
1183            locations: Arc::new(Mutex::new(TrackLocations::new())),
1184        }
1185    }
1186
1187    /// The Track Alias the peer attached to `request_id`, once it has named
1188    /// one.
1189    ///
1190    /// Answers for a subscription this endpoint asked for from the moment its
1191    /// SUBSCRIBE_OK arrives, and for one the peer offered from the moment its
1192    /// PUBLISH does. `None` before that, and for a Request ID this session has
1193    /// no track for.
1194    pub fn track_alias_for(&self, request_id: VarInt) -> Option<VarInt> {
1195        let alias = self.track_bindings.get(&request_id.into_inner())?.alias?;
1196        VarInt::from_u64(alias).ok()
1197    }
1198
1199    /// The refusal Section 3.1.3 requires when `alias` already names a
1200    /// different track that still has an Established subscription, or `None`
1201    /// when it is free.
1202    ///
1203    /// # Why the set is read rather than kept
1204    ///
1205    /// "Established" is a subscription state Section 3.1 defines, and both
1206    /// state machines here already hold it: a subscription reaches it on
1207    /// SUBSCRIBE_OK and a publish on PUBLISH_OK, and each leaves it on the
1208    /// message that ends the flow. Asking them is what makes an alias free
1209    /// again the moment its track's subscription ends, with nothing to prune
1210    /// on the way out - and a path that ended a subscription without telling
1211    /// this table would otherwise leave the alias held forever and refuse the
1212    /// peer's next, conforming, use of it.
1213    ///
1214    /// # Why the request's own binding is skipped
1215    ///
1216    /// A SUBSCRIBE_OK is judged before its own alias is written down, so the
1217    /// skip is not what keeps it from finding itself. A PUBLISH is not: a
1218    /// second PUBLISH under a Request ID already bound is refused by the
1219    /// duplicate-Request-ID rule before it reaches here, and comparing a
1220    /// request against its own binding would answer the wrong rule if that one
1221    /// ever moved.
1222    fn conflicting_track_alias(
1223        &self,
1224        request_id: u64,
1225        alias: u64,
1226        namespace: &TrackNamespace,
1227        name: &[u8],
1228    ) -> Option<EndpointError> {
1229        for (&id, binding) in &self.track_bindings {
1230            if id == request_id || binding.alias != Some(alias) {
1231                continue;
1232            }
1233            if binding.namespace == *namespace && binding.name == name {
1234                continue;
1235            }
1236            if self.binding_is_established(id, binding.kind) {
1237                return Some(EndpointError::DuplicateTrackAlias {
1238                    alias,
1239                    established: id,
1240                    offered: request_id,
1241                });
1242            }
1243        }
1244        None
1245    }
1246
1247    /// The request already using `alias` for a track other than (`namespace`,
1248    /// `name`), or `None` when this endpoint may give the alias to that track.
1249    ///
1250    /// Separate from [`Self::conflicting_track_alias`] because the two answer
1251    /// different questions about the same table. That one judges a message
1252    /// that has arrived and ends the session over it; this one judges one that
1253    /// has not been built and declines to build it.
1254    fn alias_held_elsewhere(
1255        &self,
1256        alias: u64,
1257        namespace: &TrackNamespace,
1258        name: &[u8],
1259    ) -> Option<EndpointError> {
1260        self.track_bindings.iter().find_map(|(&id, binding)| {
1261            let other_track = binding.namespace != *namespace || binding.name != name;
1262            (binding.alias == Some(alias)
1263                && other_track
1264                && self.binding_is_in_use(id, binding.kind))
1265            .then_some(EndpointError::TrackAliasInUse { alias, held: id })
1266        })
1267    }
1268
1269    /// Whether a binding's request has put its alias in play at all. Broader
1270    /// than [`Self::binding_is_established`], and the two sentences are why.
1271    /// What a subscriber must close over is qualified - "the same Track Alias
1272    /// as a different Track with an Established subscription" - and the
1273    /// prohibition on the publisher is not: "The same Track Alias MUST NOT be
1274    /// used by a publisher to refer to two different Tracks simultaneously in
1275    /// the same session." Once a PUBLISH carrying an alias has been sent,
1276    /// giving that alias to a second track is what that sentence forbids,
1277    /// answered or not.
1278    fn binding_is_in_use(&self, id: u64, kind: BindingKind) -> bool {
1279        match kind {
1280            BindingKind::Subscribe => {
1281                self.subscriptions.get(&id).is_some_and(|sm| sm.state() != SubscriptionState::Done)
1282            }
1283            BindingKind::Publish => {
1284                self.publishes.get(&id).is_some_and(|sm| sm.state() != PublishState::Done)
1285            }
1286        }
1287    }
1288
1289    /// Whether the request that owns a binding still has an Established
1290    /// subscription.
1291    fn binding_is_established(&self, id: u64, kind: BindingKind) -> bool {
1292        match kind {
1293            BindingKind::Subscribe => self
1294                .subscriptions
1295                .get(&id)
1296                .is_some_and(|sm| sm.state() == SubscriptionState::Active),
1297            BindingKind::Publish => {
1298                self.publishes.get(&id).is_some_and(|sm| sm.state() == PublishState::Active)
1299            }
1300        }
1301    }
1302
1303    /// The track a live binding has given `alias` to.
1304    ///
1305    /// Read rather than kept: a binding whose request has ended holds nothing,
1306    /// and an alias that is free again may name a different track next.
1307    fn track_for_alias(&self, alias: u64) -> Option<(&TrackNamespace, &[u8])> {
1308        self.track_bindings.iter().find_map(|(&id, binding)| {
1309            (binding.alias == Some(alias) && self.binding_is_in_use(id, binding.kind))
1310                .then_some((&binding.namespace, binding.name.as_slice()))
1311        })
1312    }
1313
1314    /// Whether this endpoint *receives* the track a request names.
1315    ///
1316    /// The sentence to be answered is a subscriber's - "cancel any
1317    /// corresponding subscription or fetches for that Track from that
1318    /// publisher" - so a request that makes this endpoint the publisher is not
1319    /// one of them. A SUBSCRIBE in the table is always this endpoint's own,
1320    /// because a SUBSCRIBE the peer sends makes this endpoint the publisher and
1321    /// leaves no binding. A PUBLISH is in the table either way round, and
1322    /// Request ID parity is what separates them: the offer the peer made is the
1323    /// one this endpoint receives a track through.
1324    fn receives_through(&self, id: u64, kind: BindingKind) -> bool {
1325        match kind {
1326            BindingKind::Subscribe => true,
1327            BindingKind::Publish => self.request_ids.validate_peer_id(id).is_ok(),
1328        }
1329    }
1330
1331    /// The record a stream carrying `alias`'s objects measures them against.
1332    ///
1333    /// `None` for an alias no live binding names: an object for one breaks a
1334    /// different rule, and measuring it against a track this endpoint never
1335    /// asked for would answer that one with the wrong sentence.
1336    pub fn track_objects(&self, alias: u64) -> Option<TrackObjects> {
1337        let (namespace, name) = self.track_for_alias(alias)?;
1338        Some(TrackObjects::new(
1339            Arc::clone(&self.locations),
1340            namespace.clone(),
1341            name.to_vec(),
1342            alias,
1343        ))
1344    }
1345
1346    /// Record or judge one object that arrived outside a subgroup stream, and
1347    /// report Section 12.1's Malformed Track when it arrived after the place
1348    /// an end-of-track object put the end.
1349    ///
1350    /// One rule and not two. The placement rule drafts 08 through 13 state
1351    /// about an end-of-track object is not in this draft, so an object that
1352    /// ends a track here is judged against nothing and only settles where the
1353    /// track stopped.
1354    ///
1355    /// `&self`, because the call site is the data plane's.
1356    pub fn note_received_object(
1357        &self,
1358        alias: u64,
1359        at: ObjectLocation,
1360        role: ObjectRole,
1361    ) -> Result<(), EndpointError> {
1362        let Some(objects) = self.track_objects(alias) else { return Ok(()) };
1363        objects.note_past_final(at, role).map_err(|end| EndpointError::ObjectPastFinalObject {
1364            alias,
1365            group: at.group,
1366            object: at.object,
1367            final_group: end.group,
1368            final_object: end.object,
1369        })
1370    }
1371
1372    /// The condition a track was withdrawn for, or `None` for a track this
1373    /// endpoint has found nothing wrong with.
1374    ///
1375    /// # Why this takes a track and not the alias the object carried
1376    ///
1377    /// An alias only means anything through a live binding, and the withdrawal
1378    /// ends the binding it would have been resolved through. An accessor taking
1379    /// an alias would therefore answer `None` from the instant it had something
1380    /// to say. The record is keyed on the track, and so is this.
1381    pub fn malformed_track(
1382        &self,
1383        namespace: &TrackNamespace,
1384        name: &[u8],
1385    ) -> Option<MalformedTrackCondition> {
1386        self.malformed
1387            .lock()
1388            .unwrap_or_else(|poisoned| poisoned.into_inner())
1389            .condition(namespace, name)
1390    }
1391
1392    /// Note a track as malformed and name every request through which this
1393    /// endpoint is receiving it, so the caller can cancel them.
1394    ///
1395    /// **This is the whole of what this crate can do here, and the reason is
1396    /// structural rather than a shortfall.** Section 12.1 asks a subscriber
1397    /// that detects a Malformed Track to "cancel any corresponding subscription
1398    /// or fetches for that Track from that publisher", and on this draft
1399    /// cancelling a request means resetting the request's own bidirectional
1400    /// stream - Section 6.4.2.3. Every request lives at the front of a
1401    /// stream of its own, and `Connection::recv_on_request_stream` hands that
1402    /// stream to the caller as a `RequestStream`. So the stream this answer
1403    /// operates on is not the endpoint's to touch, and no amount of state here
1404    /// changes that. What the endpoint can do is say which requests they are.
1405    ///
1406    /// # Nothing here ends a request
1407    ///
1408    /// Deliberately, and it is what keeps the answer to one. The flows are read
1409    /// and not moved, so a caller that passes each id to
1410    /// `Connection::cancel_request_stream` ends the request there - and the
1411    /// binding stops being in use, so a second object past the end finds no
1412    /// track for the alias and names nothing. The request ending is still what
1413    /// closes the loop, exactly as it is on the drafts that answer with a
1414    /// message; what changed is which side ends it. A caller that ignores the
1415    /// list gets named the same requests again, which is honest: the condition
1416    /// really did fire again.
1417    ///
1418    /// # What is named, and what is not
1419    ///
1420    /// Requests through which this endpoint *receives* the track: a SUBSCRIBE
1421    /// this endpoint sent, a PUBLISH the peer sent, and a fetch this endpoint
1422    /// made. A request that makes this endpoint the publisher is not one of
1423    /// them. Empty for an alias no live binding names,
1424    /// and empty for a track whose only requests are ones this endpoint
1425    /// publishes.
1426    ///
1427    /// Sorted, because a `HashMap` iterates in no order and two requests for
1428    /// one track is a shape a peer can produce.
1429    pub fn requests_for_malformed_track(
1430        &self,
1431        alias: u64,
1432        condition: MalformedTrackCondition,
1433    ) -> Vec<VarInt> {
1434        let Some((namespace, name)) = self.track_for_alias(alias) else { return Vec::new() };
1435        let (namespace, name) = (namespace.clone(), name.to_vec());
1436        self.requests_for_malformed_track_named(namespace, name, condition)
1437    }
1438
1439    /// [`Self::requests_for_malformed_track`] for the track the FETCH
1440    /// `request_id` asked for, which a FETCH stream found malformed: the stream
1441    /// names no alias. Empty for a Request ID this endpoint sent no FETCH under.
1442    pub fn requests_for_malformed_fetch_track(
1443        &self,
1444        request_id: u64,
1445        condition: MalformedTrackCondition,
1446    ) -> Vec<VarInt> {
1447        let Some(track) = self.fetch_tracks.get(&request_id) else { return Vec::new() };
1448        let (namespace, name) = (track.namespace.clone(), track.name.clone());
1449        self.requests_for_malformed_track_named(namespace, name, condition)
1450    }
1451
1452    fn requests_for_malformed_track_named(
1453        &self,
1454        namespace: TrackNamespace,
1455        name: Vec<u8>,
1456        condition: MalformedTrackCondition,
1457    ) -> Vec<VarInt> {
1458        self.malformed
1459            .lock()
1460            .unwrap_or_else(|poisoned| poisoned.into_inner())
1461            .note(&namespace, &name, condition);
1462        let mut ids: Vec<u64> = self
1463            .track_bindings
1464            .iter()
1465            .filter(|(&id, binding)| {
1466                binding.namespace == namespace
1467                    && binding.name == name
1468                    && self.binding_is_in_use(id, binding.kind)
1469                    && self.receives_through(id, binding.kind)
1470            })
1471            .map(|(&id, _)| id)
1472            .chain(self.fetch_tracks.iter().filter_map(|(&id, track)| {
1473                (track.namespace == namespace
1474                    && track.name == name
1475                    && self.fetches.get(&id).is_some_and(|sm| sm.state() != FetchState::Done))
1476                .then_some(id)
1477            }))
1478            .collect();
1479        ids.sort_unstable();
1480        ids.into_iter().filter_map(|id| VarInt::from_u64(id).ok()).collect()
1481    }
1482
1483    /// The conflict a SUBSCRIBE_OK's alias has with the tracks already bound.
1484    ///
1485    /// Separate from [`Self::conflicting_track_alias`] because the track a
1486    /// SUBSCRIBE_OK is about is not in the SUBSCRIBE_OK: it is the one this
1487    /// endpoint's own SUBSCRIBE asked for, which is why the request has to be
1488    /// looked up before the alias can be judged.
1489    fn conflicting_alias_for_subscribe_ok(&self, id: u64, alias: u64) -> Option<EndpointError> {
1490        let binding = self.track_bindings.get(&id)?;
1491        self.conflicting_track_alias(id, alias, &binding.namespace, &binding.name)
1492    }
1493
1494    pub fn role(&self) -> Role {
1495        self.role
1496    }
1497
1498    /// Returns the role of the peer, which is the other one.
1499    pub fn peer_role(&self) -> Role {
1500        match self.role {
1501            Role::Client => Role::Server,
1502            Role::Server => Role::Client,
1503        }
1504    }
1505
1506    /// Hold a refused update's ending to the status the draft names, and
1507    /// retire the obligation once that ending is written.
1508    ///
1509    /// Both routes to a PUBLISH_DONE come through here. A subscription this
1510    /// endpoint accepted is ended by a response on the peer's stream; a PUBLISH
1511    /// this endpoint sent is ended on its own, which does not go through the
1512    /// response path at all. The rule is the same either way, so it is stated
1513    /// once and called twice rather than written where each route happened to
1514    /// need it.
1515    fn require_update_failure_status(
1516        &mut self,
1517        id: u64,
1518        status_code: VarInt,
1519    ) -> Result<(), EndpointError> {
1520        if !self.owed_update_failures.contains(&id) {
1521            return Ok(());
1522        }
1523        let required = PublishDoneStatusCode::UpdateFailed as u64;
1524        if status_code.into_inner() != required {
1525            return Err(EndpointError::WrongUpdateFailureStatus { request: id, required });
1526        }
1527        self.owed_update_failures.remove(&id);
1528        Ok(())
1529    }
1530
1531    /// Whether the peer has sent a REQUEST_UPDATE on this request that has not
1532    /// been answered yet.
1533    ///
1534    /// Draft-22 Section 9.5: "A subscriber can also send REQUEST_UPDATE to
1535    /// modify parameters of a subscription established with PUBLISH", and the
1536    /// receiver of one "MUST respond with exactly one REQUEST_UPDATE_OK or
1537    /// REQUEST_UPDATE_ERROR message indicating if the update was successful",
1538    /// which Section 1.5 names for a REQUEST_OK and a REQUEST_ERROR.
1539    ///
1540    /// Asked by the connection layer, which otherwise writes nothing on a
1541    /// stream this endpoint opened. An update the peer sent on a PUBLISH is
1542    /// the one thing on such a stream that this endpoint has to answer, and
1543    /// this is what tells it apart from a response to its own request.
1544    pub fn has_unanswered_update(&self, request_id: VarInt) -> bool {
1545        self.unanswered_peer_updates.get(&request_id.into_inner()).is_some_and(|&n| n > 0)
1546    }
1547
1548    /// Whether the request `id` names a subscription this endpoint publishes.
1549    ///
1550    /// Draft-22 Section 9.5.1 gives a refused REQUEST_UPDATE three different
1551    /// consequences and picks between them by what was being updated: "When a
1552    /// REQUEST_UPDATE is unsuccessful, the publisher MUST also terminate the
1553    /// subscription by sending a PUBLISH_DONE with error code UPDATE_FAILED.
1554    /// When a REQUEST_UPDATE fails for a FETCH, the publisher MUST reset the
1555    /// FETCH data stream. When a REQUEST_UPDATE fails for a SUBSCRIBE_NAMESPACE,
1556    /// SUBSCRIBE_TRACKS or PUBLISH_NAMESPACE, the responder MUST close the bidi
1557    /// stream (see Section 6.4.2.2)."
1558    ///
1559    /// Only the first of the three is a message, and only the first is owed
1560    /// here. Recording it for the other two is what would hold their streams
1561    /// open past the close the third sentence requires.
1562    ///
1563    /// Two requests leave this endpoint publishing: a SUBSCRIBE the peer sent,
1564    /// and a PUBLISH this endpoint sent. Either can carry an update from the
1565    /// other side, and either is ended by a PUBLISH_DONE written from here. A
1566    /// peer's PUBLISH is neither, because the peer is the publisher on it, and
1567    /// it is told apart by having arrived rather than been sent.
1568    fn publishes_a_subscription(&self, id: u64) -> bool {
1569        matches!(self.inbound_requests.get(&id), Some(ControlMessage::Subscribe(_)))
1570            || (self.publishes.contains_key(&id) && !self.inbound_requests.contains_key(&id))
1571    }
1572
1573    /// Whether this request's refused update still owes the peer the
1574    /// PUBLISH_DONE that ends it.
1575    ///
1576    /// Asked by the connection layer, which owns the stream that termination
1577    /// has to be written on and therefore has to know not to close it. A
1578    /// REQUEST_ERROR answering the request itself ends the exchange and takes
1579    /// the stream with it; one answering an update does not, and nothing in
1580    /// the message tells the two apart.
1581    pub fn owes_update_failure(&self, request_id: VarInt) -> bool {
1582        self.owed_update_failures.contains(&request_id.into_inner())
1583    }
1584
1585    pub fn session_state(&self) -> SessionState {
1586        self.session.state()
1587    }
1588
1589    pub fn goaway_uri(&self) -> Option<&[u8]> {
1590        self.goaway_uri.as_deref()
1591    }
1592
1593    pub fn active_subscription_count(&self) -> usize {
1594        self.subscriptions.len()
1595    }
1596
1597    pub fn active_fetch_count(&self) -> usize {
1598        self.fetches.len()
1599    }
1600
1601    pub fn active_subscribe_namespace_count(&self) -> usize {
1602        self.subscribe_namespaces.len()
1603    }
1604
1605    pub fn active_subscribe_tracks_count(&self) -> usize {
1606        self.subscribe_tracks.len()
1607    }
1608
1609    pub fn active_publish_namespace_count(&self) -> usize {
1610        self.publish_namespaces.len()
1611    }
1612
1613    pub fn active_track_status_count(&self) -> usize {
1614        self.track_statuses.len()
1615    }
1616
1617    pub fn active_publish_count(&self) -> usize {
1618        self.publishes.len()
1619    }
1620
1621    /// How many Request IDs the peer has spent on this session.
1622    ///
1623    /// Nothing here removes an entry, so this only grows. A responder that
1624    /// wants a ceiling on peer-created state has to impose one itself — see
1625    /// the note on [`receive_request_on_stream`](Self::receive_request_on_stream).
1626    pub fn peer_request_count(&self) -> usize {
1627        self.peer_request_ids.len()
1628    }
1629
1630    // -- Session lifecycle ------------------------------------------
1631
1632    pub fn connect(&mut self) -> Result<(), EndpointError> {
1633        self.session.on_connect()?;
1634        Ok(())
1635    }
1636
1637    pub fn close(&mut self) -> Result<(), EndpointError> {
1638        self.session.on_close()?;
1639        Ok(())
1640    }
1641
1642    // -- Unified SETUP ----------------------------------------------
1643
1644    /// Generate a SETUP message. Both client and server use the same message
1645    /// type; only the role (and the order of send/receive) distinguishes them.
1646    pub fn send_setup(
1647        &mut self,
1648        options: Vec<KeyValuePair>,
1649    ) -> Result<ControlMessage, EndpointError> {
1650        let msg = Setup { options };
1651        setup::validate_setup(&msg, self.role)?;
1652        self.advertised_max_request_updates = setup_varint(&msg.options, MAX_REQUEST_UPDATES);
1653        self.advertised_max_filter_ranges = setup_varint(&msg.options, MAX_FILTER_RANGES);
1654        Ok(ControlMessage::Setup(msg))
1655    }
1656
1657    /// Process an incoming SETUP message. Transitions the session to Active.
1658    pub fn receive_setup(&mut self, msg: &Setup) -> Result<(), EndpointError> {
1659        setup::validate_setup(msg, self.peer_role())?;
1660        self.session.on_setup_complete()?;
1661        Ok(())
1662    }
1663
1664    // -- GoAway -----------------------------------------------------
1665
1666    pub fn receive_goaway(&mut self, msg: &GoAway) -> Result<(), EndpointError> {
1667        // Draft-22 Section 9.2: "If a server receives a GOAWAY with a
1668        // non-zero New Session URI Length it MUST close the session with a
1669        // PROTOCOL_VIOLATION." Migration is something a server offers a
1670        // client, never the other way round, so the URI is refused here rather
1671        // than stored and later followed.
1672        if self.role == Role::Server && !msg.new_session_uri.is_empty() {
1673            return Err(self.fail_session(EndpointError::GoAwayUriAtServer));
1674        }
1675        // Draft-22 Section 9.2: "The endpoint MUST close the session with a
1676        // PROTOCOL_VIOLATION (Section 12.2) if it receives more than one GOAWAY on the
1677        // control stream or on a single request stream." Draining is reached
1678        // from nowhere else - `on_goaway` is its only entry and this method is
1679        // that method's only caller - so the session state is the record of
1680        // the first GOAWAY having arrived. This is the control-stream half;
1681        // the per-stream half is in `receive_goaway_on_request_stream`.
1682        if self.session.state() == SessionState::Draining {
1683            return Err(self.fail_session(EndpointError::RepeatedGoAway));
1684        }
1685        self.session.on_goaway()?;
1686        self.goaway_uri = Some(msg.new_session_uri.clone());
1687        Ok(())
1688    }
1689
1690    /// Record that the session is over because the peer broke a rule the draft
1691    /// answers with a session close, and hand the error back unchanged.
1692    ///
1693    /// The state move is what makes the violation stick: every request entry
1694    /// point goes through [`require_active_or_err`](Self::require_active_or_err),
1695    /// so a caller that ignores the returned error still cannot start anything
1696    /// new. The close on the wire is the connection layer's job — see
1697    /// [`EndpointError::session_error_code`] for the code it should use.
1698    fn fail_session(&mut self, err: EndpointError) -> EndpointError {
1699        // `on_close` accepts SetupExchange, Active and Draining. A violation
1700        // seen in Connecting or Closed leaves the state machine alone: there
1701        // is no session to close, and the error itself is still the answer.
1702        //
1703        // SetupExchange is in that set because the Termination section says
1704        // "The Transport Session can be terminated at any point", and the
1705        // Setup exchange is a point. So a violation caught while the setup is
1706        // still in flight does close the session, and the discarded result is
1707        // safe because that is one of the states `on_close` accepts.
1708        let _ = self.session.on_close();
1709        err
1710    }
1711
1712    fn require_active_or_err(&self) -> Result<(), EndpointError> {
1713        match self.session.state() {
1714            SessionState::Active => Ok(()),
1715            SessionState::Draining => Err(EndpointError::Draining),
1716            _ => Err(EndpointError::NotActive),
1717        }
1718    }
1719
1720    // -- Subscribe flow ---------------------------------------------
1721
1722    pub fn subscribe(
1723        &mut self,
1724        track_namespace: TrackNamespace,
1725        track_name: Vec<u8>,
1726        parameters: Vec<KeyValuePair>,
1727    ) -> Result<(VarInt, ControlMessage), EndpointError> {
1728        self.require_active_or_err()?;
1729        let req_id = self.request_ids.allocate()?;
1730
1731        let mut sm = SubscriptionStateMachine::new();
1732        sm.on_subscribe_sent()?;
1733        self.subscriptions.insert(req_id.into_inner(), sm);
1734        // Section 9.20.15: the presence of FILL_PARAMETERS "is what requests a
1735        // fill fetch stream". Noted so that the FETCH_HEADER the publisher
1736        // opens next — which carries this SUBSCRIBE's Request ID, not a
1737        // fetch's — can be attributed to this subscription rather than
1738        // answered with "unknown request".
1739        self.note_fill_requested(req_id.into_inner(), &parameters);
1740        // The track is recorded here because this is the only place it is
1741        // known: a SUBSCRIBE_OK names an alias and not the track it is for.
1742        self.track_bindings.insert(
1743            req_id.into_inner(),
1744            TrackBinding {
1745                namespace: track_namespace.clone(),
1746                name: track_name.clone(),
1747                alias: None,
1748                kind: BindingKind::Subscribe,
1749            },
1750        );
1751
1752        let msg = ControlMessage::Subscribe(Subscribe {
1753            request_id: req_id,
1754            track_namespace,
1755            track_name,
1756            parameters,
1757        });
1758        Ok((req_id, msg))
1759    }
1760
1761    /// Process an incoming SUBSCRIBE_OK. Draft-22: no request_id on wire; the
1762    /// caller supplies the `request_id` of the bidi stream on which the
1763    /// response arrived.
1764    pub fn receive_subscribe_ok(
1765        &mut self,
1766        request_id: VarInt,
1767        msg: &SubscribeOk,
1768    ) -> Result<(), EndpointError> {
1769        let id = request_id.into_inner();
1770        if !self.subscriptions.contains_key(&id) {
1771            return Err(EndpointError::UnknownRequest(id));
1772        }
1773        let alias = msg.track_alias.into_inner();
1774        // Judged before the transition, so that the subscription this message
1775        // is about is not yet Established and cannot be found as its own
1776        // conflict, and so that a refused SUBSCRIBE_OK leaves no alias behind.
1777        if let Some(conflict) = self.conflicting_alias_for_subscribe_ok(id, alias) {
1778            return Err(self.fail_session(conflict));
1779        }
1780        let sm = self.subscriptions.get_mut(&id).expect("checked above");
1781        sm.on_subscribe_ok()?;
1782        if let Some(binding) = self.track_bindings.get_mut(&id) {
1783            binding.alias = Some(alias);
1784        }
1785        Ok(())
1786    }
1787
1788    /// Take one of the REQUEST_UPDATE credits this endpoint advertised for
1789    /// `id`'s stream.
1790    ///
1791    /// Section 9.1.7 puts the limit on the number *outstanding*, so the
1792    /// count is a running balance rather than a total: it goes up on each
1793    /// REQUEST_UPDATE received and down on each REQUEST_OK or REQUEST_ERROR
1794    /// this endpoint writes back, and a peer that keeps pace never reaches the
1795    /// ceiling however many updates it sends.
1796    ///
1797    /// # Errors
1798    ///
1799    /// [`EndpointError::TooManyRequestUpdates`], which answers
1800    /// `Some(TooManyRequestUpdates)` - its own close code, not the general
1801    /// PROTOCOL_VIOLATION - and the session is failed before it returns.
1802    fn spend_update_credit(&mut self, id: u64) -> Result<(), EndpointError> {
1803        let limit = self.advertised_max_request_updates;
1804        if limit == 0 {
1805            return Ok(());
1806        }
1807        let outstanding = self.outstanding_peer_updates.entry(id).or_insert(0);
1808        if *outstanding >= limit {
1809            return Err(self.fail_session(EndpointError::TooManyRequestUpdates(id, limit)));
1810        }
1811        *outstanding += 1;
1812        Ok(())
1813    }
1814
1815    /// Give back the credit a REQUEST_OK or REQUEST_ERROR restores.
1816    ///
1817    /// Called for every response this endpoint writes, whether or not the
1818    /// stream ever carried an update: a stream with no outstanding updates has
1819    /// nothing to restore and the saturating subtraction says so, which is
1820    /// cheaper than deciding first whether the response is answering an update
1821    /// or the original request.
1822    fn restore_update_credit(&mut self, id: u64) {
1823        if let Some(outstanding) = self.outstanding_peer_updates.get_mut(&id) {
1824            *outstanding = outstanding.saturating_sub(1);
1825        }
1826    }
1827
1828    /// Process a REQUEST_UPDATE that arrived on the bidi request stream
1829    /// identified by `request_id`.
1830    ///
1831    /// Draft-22 Section 9.5: "The sender of a request (SUBSCRIBE, PUBLISH,
1832    /// FETCH, PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE, SUBSCRIBE_TRACKS) can
1833    /// later send a REQUEST_UPDATE on the same bidi stream as the request to
1834    /// modify it. A subscriber can also send REQUEST_UPDATE to modify
1835    /// parameters of a subscription established with PUBLISH." Anything else
1836    /// "MUST close the session with a PROTOCOL_VIOLATION", which is what
1837    /// [`EndpointError::UnexpectedRequestUpdate`] carries — TRACK_STATUS most
1838    /// of all, since Section 9.13 says outright that "the subscriber cannot
1839    /// send REQUEST_UPDATE".
1840    ///
1841    /// The message carries a Request ID of its own and the stream carries one
1842    /// too. They name the same request when the peer is conforming; a
1843    /// disagreement means the update was sent on a stream that is not its
1844    /// request's, which is the same violation, so it is refused rather than
1845    /// silently resolved to one of the two.
1846    ///
1847    /// Only SUBSCRIBE-established subscriptions have a state-machine event for
1848    /// this. That is not an omission: an update changes a request's parameters
1849    /// and not its lifecycle, so for the other five kinds the update is a
1850    /// self-transition with nothing to record.
1851    pub fn receive_request_update(
1852        &mut self,
1853        request_id: VarInt,
1854        msg: &RequestUpdate,
1855    ) -> Result<(), EndpointError> {
1856        let id = request_id.into_inner();
1857        if msg.request_id.into_inner() != id {
1858            return Err(self.fail_session(EndpointError::UnexpectedRequestUpdate(
1859                msg.request_id.into_inner(),
1860            )));
1861        }
1862        self.spend_update_credit(id)?;
1863        // The two cases are about who is sending, and only one of them is
1864        // about which request. Case one is the peer updating a request the
1865        // peer made, whatever its kind; case two is a subscriber updating a
1866        // subscription this endpoint established with PUBLISH. A SUBSCRIBE, a
1867        // FETCH or a namespace request this endpoint made is in neither, and
1868        // an update on one is a violation rather than a request to apply.
1869        //
1870        // They are not symmetrical about timing either. Case one allows an
1871        // update "later" and says nothing about the answer, so a request of
1872        // the peer's may be updated before this endpoint has answered it.
1873        // Case two rests on the subscription existing, and Section 3.1 says
1874        // when it does: "Once either of these sequences is successful, the
1875        // subscription moves to the Established state and can be updated by
1876        // the subscriber using REQUEST_UPDATE." A PUBLISH still waiting for
1877        // its answer is Pending, and an update on one is outside both cases.
1878        //
1879        // Checked before the state machine below moves, so a session that is
1880        // closing does not leave a subscription updated on the way out.
1881        let one_of_the_two_cases = self.inbound_requests.contains_key(&id)
1882            || self.binding_is_established(id, BindingKind::Publish);
1883        if !one_of_the_two_cases {
1884            return Err(self.fail_session(EndpointError::UnexpectedRequestUpdate(id)));
1885        }
1886        let updatable = if let Some(sm) = self.subscriptions.get_mut(&id) {
1887            sm.on_subscribe_update()?;
1888            true
1889        } else {
1890            self.publishes.contains_key(&id)
1891                || self.fetches.contains_key(&id)
1892                || self.subscribe_namespaces.contains_key(&id)
1893                || self.subscribe_tracks.contains_key(&id)
1894                || self.publish_namespaces.contains_key(&id)
1895        };
1896        if !updatable {
1897            return Err(self.fail_session(EndpointError::UnexpectedRequestUpdate(id)));
1898        }
1899        *self.unanswered_peer_updates.entry(id).or_insert(0) += 1;
1900
1901        // Section 3.4.1: "A publisher opens a fill fetch stream when it
1902        // processes a SUBSCRIBE or REQUEST_UPDATE that carries FILL_PARAMETERS
1903        // while the subscription is not paused", and "A REQUEST_UPDATE that
1904        // does not carry FILL_PARAMETERS does not open a new fill fetch
1905        // stream." So an update is the second place a fill can be asked for,
1906        // and the only place a second one can. The pause half of that sentence
1907        // is the publisher's decision about whether to open the stream, not
1908        // this endpoint's about whether a stream is attributable, so it is not
1909        // applied here: a fill fetch stream that arrives is one to account
1910        // for, whatever this endpoint thought the subscription's state was.
1911        self.note_fill_requested(id, &msg.parameters);
1912
1913        // The ceiling is on what the request carries after the update, so the
1914        // update is merged into the set in force and the whole set measured.
1915        // Recorded rather than refused for the reason the request itself is:
1916        // the answer is a REQUEST_ERROR, which names a Request ID.
1917        let mut in_force = self.peer_request_filters.remove(&id).unwrap_or_default();
1918        apply_filter_update(&mut in_force, &msg.parameters);
1919        if let Some(rejection) = self.filter_verdict(&in_force) {
1920            self.peer_filter_rejections.insert(id, rejection);
1921        }
1922        self.peer_request_filters.insert(id, in_force);
1923
1924        // Section 9.5.2 gives an update one thing to move that no draft before
1925        // draft-18 lets a request change: "A subscriber can update the Track
1926        // Namespace Prefix of an established SUBSCRIBE_NAMESPACE or
1927        // SUBSCRIBE_TRACKS by including the TRACK_NAMESPACE_PREFIX parameter
1928        // (Section 9.20.20) in a REQUEST_UPDATE."
1929        //
1930        // The two kinds keep their own ground -- "The overlap restriction
1931        // applies independently per type" -- so which set the new prefix is
1932        // weighed against is decided by what the peer opened the stream with.
1933        //
1934        // A request of any other kind has no prefix for the parameter to move,
1935        // and nothing in this draft says what to do with the parameter when it
1936        // turns up on one, so it is left alone rather than guessed at.
1937        if let Some(prefix) = updated_prefix(&msg.parameters) {
1938            let namespace = matches!(
1939                self.inbound_requests.get(&id),
1940                Some(ControlMessage::SubscribeNamespace(_))
1941            );
1942            let tracks =
1943                matches!(self.inbound_requests.get(&id), Some(ControlMessage::SubscribeTracks(_)));
1944            if namespace || tracks {
1945                let collides = if tracks {
1946                    self.peer_tracks_overlap(&prefix, Some(id))
1947                } else {
1948                    self.peer_namespace_overlap(&prefix, Some(id))
1949                };
1950                match collides {
1951                    Some(established) => {
1952                        self.overlapping_prefix_updates.insert(id, established);
1953                    }
1954                    // A later update clears an earlier one's verdict along with
1955                    // its prefix, which is what a receiver "applying only the
1956                    // cumulative result" is entitled to do.
1957                    None => {
1958                        self.overlapping_prefix_updates.remove(&id);
1959                    }
1960                }
1961                self.updated_namespace_prefixes.insert(id, prefix);
1962            }
1963        }
1964        Ok(())
1965    }
1966
1967    pub fn receive_publish_done(
1968        &mut self,
1969        request_id: VarInt,
1970        _msg: &PublishDone,
1971    ) -> Result<(), EndpointError> {
1972        let id = request_id.into_inner();
1973        let sm = self.subscriptions.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
1974        sm.on_publish_done()?;
1975        Ok(())
1976    }
1977
1978    /// Process a PUBLISH_STATE_NOTIFY that arrived on the subscription's bidi
1979    /// request stream, new in draft-20 (Section 9.10).
1980    ///
1981    /// # Nothing here moves, and that is the message
1982    ///
1983    /// "This message is informative and no action is required by the
1984    /// recipient." It reports that the subscription's state changed for a
1985    /// reason other than a REQUEST_UPDATE the subscriber sent, carries only the
1986    /// parameters whose values changed, and gets **no reply**: unlike a
1987    /// REQUEST_UPDATE it is "a unilateral notification: the receiver does not
1988    /// respond with REQUEST_OK or REQUEST_ERROR, and the message is not subject
1989    /// to the MAX_REQUEST_UPDATES limit". So this spends no update credit and
1990    /// leaves nothing owed — a notify routed through
1991    /// [`receive_request_update`](Self::receive_request_update) instead would
1992    /// consume a credit the peer never spent and leave this endpoint owing an
1993    /// answer that must not be sent.
1994    ///
1995    /// What the parameters mean is the caller's: the values changed at the
1996    /// publisher, and the publisher "MUST include the LARGEST_OBJECT parameter"
1997    /// — if known — "so the subscriber can determine the point in the Track at
1998    /// which the change took effect". A message that omits it is still a
1999    /// well-formed frame, so nothing here refuses one.
2000    ///
2001    /// # Errors
2002    ///
2003    /// [`EndpointError::StateNotifyForNonSubscription`] when `request_id` names
2004    /// something other than a subscription, and
2005    /// [`EndpointError::StateNotifyFromSubscriber`] when it names a
2006    /// subscription on which this endpoint is the publisher rather than the
2007    /// subscriber. Section 9.10 answers both with a session close, and both
2008    /// are returned after this endpoint's own session state has moved to
2009    /// Closed.
2010    pub fn receive_publish_state_notify(
2011        &mut self,
2012        request_id: VarInt,
2013        _msg: &PublishStateNotify,
2014    ) -> Result<(), EndpointError> {
2015        let id = request_id.into_inner();
2016        // The two ways a subscription exists, from Section 3.1: this endpoint
2017        // sent a SUBSCRIBE, or the peer sent a PUBLISH. Either way the peer is
2018        // the publisher, which is the sender this section permits. A
2019        // subscription the peer opened with a SUBSCRIBE makes this endpoint the
2020        // publisher, and a notify on it came from the subscriber.
2021        let ours = self.subscriptions.contains_key(&id) && !self.inbound_requests.contains_key(&id);
2022        let peers_publish =
2023            self.publishes.contains_key(&id) && self.inbound_requests.contains_key(&id);
2024        if ours || peers_publish {
2025            return Ok(());
2026        }
2027        let a_subscription =
2028            self.subscriptions.contains_key(&id) || self.publishes.contains_key(&id);
2029        let err = if a_subscription {
2030            EndpointError::StateNotifyFromSubscriber(id)
2031        } else {
2032            EndpointError::StateNotifyForNonSubscription(id)
2033        };
2034        Err(self.fail_session(err))
2035    }
2036
2037    // -- Fetch flow -------------------------------------------------
2038
2039    /// Send a FETCH.
2040    ///
2041    /// # This is not draft-19's FETCH
2042    ///
2043    /// Draft-22 Section 9.11 has no `Fetch Type` field, no Standalone Fetch or
2044    /// Joining Fetch structure and no Fetch Type registry (draft-20 removed
2045    /// them); the Track Namespace and the Track Name are inline
2046    /// fields, and **the range travels in the `LOCATION_FILTER` parameter**
2047    /// (Section 3.3.1) rather than in the message. So this takes the same three
2048    /// arguments [`subscribe`](Self::subscribe) does, and the four location
2049    /// varints draft-19 took are now something the caller puts in
2050    /// `parameters` — see [`fetch_range`](Self::fetch_range), which does it.
2051    ///
2052    /// A FETCH with no `LOCATION_FILTER` covers `{0,0}` through Largest Object,
2053    /// inclusive.
2054    ///
2055    /// **The code point did not change.** A draft-19 peer fed one of these
2056    /// reads the Number of Track Namespace Fields count as a Fetch Type and
2057    /// mis-parses in silence; there is no in-band version signal, only the
2058    /// ALPN.
2059    ///
2060    /// `parameters` are the request's own, as they are on every other request
2061    /// this endpoint makes.
2062    pub fn fetch(
2063        &mut self,
2064        track_namespace: TrackNamespace,
2065        track_name: Vec<u8>,
2066        parameters: Vec<KeyValuePair>,
2067    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2068        self.require_active_or_err()?;
2069        let req_id = self.request_ids.allocate()?;
2070
2071        let mut sm = FetchStateMachine::new();
2072        sm.on_fetch_sent()?;
2073        self.fetches.insert(req_id.into_inner(), sm);
2074        // Before the two of them are moved into the message below: a fetch
2075        // that has left no track behind is one no withdrawal can name.
2076        self.fetch_tracks.insert(
2077            req_id.into_inner(),
2078            FetchTrack { namespace: track_namespace.clone(), name: track_name.clone() },
2079        );
2080
2081        let msg = ControlMessage::Fetch(Fetch {
2082            request_id: req_id,
2083            track_namespace,
2084            track_name,
2085            parameters,
2086        });
2087        Ok((req_id, msg))
2088    }
2089
2090    /// Send a FETCH for one range, carried as a `LOCATION_FILTER` parameter.
2091    ///
2092    /// [`fetch`](Self::fetch) with the filter put into `parameters` at the
2093    /// position ascending Parameter Type order requires. The convenience is
2094    /// worth a method because Section 9.20 makes that order a wire rule and the
2095    /// codec's encoder refuses a descending pair rather than emitting one.
2096    ///
2097    /// **The range is inclusive at both ends** (Section 3.2 and Section 3.3.1),
2098    /// and nothing here adds or subtracts one. Draft-19's `End Location` was
2099    /// "the last Object, plus 1; or 0 to indicate the entire Group"; a caller
2100    /// that ports that arithmetic forward fetches one object too many, and one
2101    /// that ports the `0`-means-whole-group convention fetches a single object
2102    /// where it meant a group. [`LocationFilter`]
2103    /// carries the same warning at each constructor.
2104    pub fn fetch_range(
2105        &mut self,
2106        track_namespace: TrackNamespace,
2107        track_name: Vec<u8>,
2108        range: &LocationFilter,
2109        mut parameters: Vec<KeyValuePair>,
2110    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2111        let filter = range.parameter()?;
2112        fill::insert_parameter(&mut parameters, filter);
2113        self.fetch(track_namespace, track_name, parameters)
2114    }
2115
2116    // -- Fill fetch streams -----------------------------------------
2117    //
2118    // Draft-22 Section 3.4 and draft-22 Section 3.4.1, which take the place of
2119    // the joining fetch. There is no message here and no state machine: a fill
2120    // is asked for by a parameter on a SUBSCRIBE or a REQUEST_UPDATE, answered
2121    // by a unidirectional stream, and ended by that stream being finished or
2122    // reset.
2123
2124    /// Note that the request under `request_id` asked for a fill fetch stream.
2125    ///
2126    /// Called for this endpoint's own requests and for the peer's alike, from
2127    /// wherever a SUBSCRIBE or a REQUEST_UPDATE carrying `FILL_PARAMETERS`
2128    /// passes through, so both ends of a session can attribute the stream that
2129    /// follows.
2130    ///
2131    /// The Group Order the fill will be read in is resolved here rather than
2132    /// when the stream arrives, because this is the last place the request's
2133    /// parameters are in hand: a FETCH_HEADER carries a Request ID and nothing
2134    /// else. See [`fill::group_order`] for the three-step resolution and for
2135    /// why an unstated order is Ascending. A value the resolver refuses cannot
2136    /// come off the wire — the codec holds a received `GROUP_ORDER` to
2137    /// `{1, 2}` before this sees it — so the fallback here is the same
2138    /// Ascending default rather than an error this entry point has no way to
2139    /// report.
2140    fn note_fill_requested(&mut self, request_id: u64, parameters: &[KeyValuePair]) {
2141        if !parameters.iter().any(|p| p.key.into_inner() == FILL_PARAMETERS) {
2142            return;
2143        }
2144        let group_order = fill::group_order(parameters).unwrap_or(GroupOrder::Ascending);
2145        let entry =
2146            self.fills.entry(request_id).or_insert(FillStreams { open: 0, opened: 0, group_order });
2147        entry.group_order = group_order;
2148    }
2149
2150    /// Whether the request under `request_id` has asked for a fill fetch
2151    /// stream.
2152    ///
2153    /// This is what tells an arriving FETCH_HEADER apart from a fetch's.
2154    /// Section 3.4 puts the *subscription's* Request ID on a fill fetch
2155    /// stream's header, so a reader that knew only about fetches would answer a
2156    /// perfectly ordinary fill with "unknown request".
2157    pub fn fill_requested(&self, request_id: VarInt) -> bool {
2158        self.fills.contains_key(&request_id.into_inner())
2159    }
2160
2161    /// How many of this subscription's fill fetch streams are open.
2162    pub fn open_fill_streams(&self, request_id: VarInt) -> usize {
2163        self.fills.get(&request_id.into_inner()).map_or(0, |f| f.open)
2164    }
2165
2166    /// How many fill fetch streams this subscription has opened in total,
2167    /// ended or not.
2168    ///
2169    /// Section 9.9 makes PUBLISH_DONE's Stream Count the total the publisher
2170    /// opened for the subscription, "including streams that contained no
2171    /// Objects (e.g., an empty Subgroup) and including any fill fetch streams".
2172    /// Draft-19 counted no fill streams because it had none, so this number is
2173    /// new work for a publisher building that field.
2174    pub fn fill_streams_opened(&self, request_id: VarInt) -> u64 {
2175        self.fills.get(&request_id.into_inner()).map_or(0, |f| f.opened)
2176    }
2177
2178    /// The Group Order this subscription's fill fetch streams deliver in, or
2179    /// `None` if it never asked for a fill.
2180    ///
2181    /// What [`accept_fill_stream`](crate::draft22::connection::Connection::accept_fill_stream)
2182    /// starts the arriving stream's object reader with. Resolved once, from the
2183    /// request's own parameters, by [`fill::group_order`] — see there for the
2184    /// order of precedence and for why an unstated order is Ascending.
2185    pub fn fill_group_order(&self, request_id: VarInt) -> Option<GroupOrder> {
2186        self.fills.get(&request_id.into_inner()).map(|f| f.group_order)
2187    }
2188
2189    /// Record a fill fetch stream opening for `request_id`.
2190    ///
2191    /// Section 3.4: "a subscription can have multiple fill fetch streams open
2192    /// at once", and "opening a new fill fetch stream does not implicitly
2193    /// cancel any previously opened fill fetch streams", so this counts rather
2194    /// than replaces.
2195    ///
2196    /// # Errors
2197    ///
2198    /// [`EndpointError::UnrequestedFillStream`] when the request never carried
2199    /// `FILL_PARAMETERS`. Not fatal to the session — see the variant.
2200    pub fn on_fill_stream_opened(&mut self, request_id: VarInt) -> Result<(), EndpointError> {
2201        let id = request_id.into_inner();
2202        let Some(fill) = self.fills.get_mut(&id) else {
2203            return Err(EndpointError::UnrequestedFillStream(id));
2204        };
2205        fill.open += 1;
2206        fill.opened += 1;
2207        Ok(())
2208    }
2209
2210    /// Record a fill fetch stream ending, whether by FIN or by reset.
2211    ///
2212    /// One entry point for both endings on purpose. Section 3.4.1 gives them
2213    /// different meanings to the application — a FIN says the fill range was
2214    /// delivered, a reset says the fill failed — and the same meaning to the
2215    /// subscription, which is none: "Resetting or cancelling a fill fetch
2216    /// stream, by either endpoint, does not affect the subscription, which
2217    /// continues to deliver objects using subscribe subgroups and datagrams."
2218    /// Nothing here touches the subscription's state machine, and that is the
2219    /// point.
2220    ///
2221    /// A stream this endpoint never saw open is ignored rather than refused: a
2222    /// reset can arrive for a stream whose header was never read.
2223    pub fn on_fill_stream_ended(&mut self, request_id: VarInt) {
2224        if let Some(fill) = self.fills.get_mut(&request_id.into_inner()) {
2225            fill.open = fill.open.saturating_sub(1);
2226        }
2227    }
2228
2229    pub fn receive_fetch_ok(
2230        &mut self,
2231        request_id: VarInt,
2232        _msg: &message::FetchOk,
2233    ) -> Result<(), EndpointError> {
2234        let id = request_id.into_inner();
2235        let sm = self.fetches.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2236        sm.on_fetch_ok()?;
2237        Ok(())
2238    }
2239
2240    pub fn on_fetch_stream_fin(&mut self, request_id: VarInt) -> Result<(), EndpointError> {
2241        let id = request_id.into_inner();
2242        let sm = self.fetches.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2243        sm.on_stream_fin()?;
2244        Ok(())
2245    }
2246
2247    pub fn on_fetch_stream_reset(&mut self, request_id: VarInt) -> Result<(), EndpointError> {
2248        let id = request_id.into_inner();
2249        let sm = self.fetches.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2250        sm.on_stream_reset()?;
2251        Ok(())
2252    }
2253
2254    // -- Subscribe Namespace flow -----------------------------------
2255
2256    pub fn subscribe_namespace(
2257        &mut self,
2258        namespace_prefix: TrackNamespace,
2259        parameters: Vec<KeyValuePair>,
2260    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2261        self.require_active_or_err()?;
2262        let req_id = self.request_ids.allocate()?;
2263
2264        let mut sm = SubscribeNamespaceStateMachine::new();
2265        sm.on_subscribe_namespace_sent()?;
2266        self.subscribe_namespaces.insert(req_id.into_inner(), sm);
2267
2268        let msg = ControlMessage::SubscribeNamespace(SubscribeNamespace {
2269            request_id: req_id,
2270            namespace_prefix,
2271            parameters,
2272        });
2273        Ok((req_id, msg))
2274    }
2275
2276    // -- Subscribe Tracks flow (new in draft-18) --------------------
2277
2278    pub fn subscribe_tracks(
2279        &mut self,
2280        namespace_prefix: TrackNamespace,
2281        parameters: Vec<KeyValuePair>,
2282    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2283        self.require_active_or_err()?;
2284        let req_id = self.request_ids.allocate()?;
2285
2286        // Reuse the SubscribeNamespace state machine — the lifecycle is the
2287        // same (request → ok/error → done) and adding a parallel state
2288        // machine purely to disambiguate would be churn.
2289        let mut sm = SubscribeNamespaceStateMachine::new();
2290        sm.on_subscribe_namespace_sent()?;
2291        self.subscribe_tracks.insert(req_id.into_inner(), sm);
2292        self.note_fill_requested(req_id.into_inner(), &parameters);
2293
2294        let msg = ControlMessage::SubscribeTracks(SubscribeTracks {
2295            request_id: req_id,
2296            namespace_prefix,
2297            parameters,
2298        });
2299        Ok((req_id, msg))
2300    }
2301
2302    // -- Publish Namespace flow -------------------------------------
2303
2304    pub fn publish_namespace(
2305        &mut self,
2306        track_namespace: TrackNamespace,
2307        parameters: Vec<KeyValuePair>,
2308    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2309        self.require_active_or_err()?;
2310        let req_id = self.request_ids.allocate()?;
2311
2312        let mut sm = PublishNamespaceStateMachine::new();
2313        sm.on_publish_namespace_sent()?;
2314        self.publish_namespaces.insert(req_id.into_inner(), sm);
2315
2316        let msg = ControlMessage::PublishNamespace(PublishNamespace {
2317            request_id: req_id,
2318            track_namespace,
2319            parameters,
2320        });
2321        Ok((req_id, msg))
2322    }
2323
2324    // -- Track Status flow ------------------------------------------
2325
2326    pub fn track_status(
2327        &mut self,
2328        track_namespace: TrackNamespace,
2329        track_name: Vec<u8>,
2330        parameters: Vec<KeyValuePair>,
2331    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2332        self.require_active_or_err()?;
2333        let req_id = self.request_ids.allocate()?;
2334        let mut sm = TrackStatusStateMachine::new();
2335        sm.on_track_status_sent()?;
2336        self.track_statuses.insert(req_id.into_inner(), sm);
2337
2338        let msg = ControlMessage::TrackStatus(message::TrackStatus {
2339            request_id: req_id,
2340            track_namespace,
2341            track_name,
2342            parameters,
2343        });
2344        Ok((req_id, msg))
2345    }
2346
2347    // -- Publish flow (publisher side) ------------------------------
2348
2349    pub fn publish(
2350        &mut self,
2351        track_namespace: TrackNamespace,
2352        track_name: Vec<u8>,
2353        track_alias: VarInt,
2354        parameters: Vec<KeyValuePair>,
2355        track_properties: Vec<KeyValuePair>,
2356    ) -> Result<(VarInt, ControlMessage), EndpointError> {
2357        self.require_active_or_err()?;
2358        // Section 3.1.3: "The same Track Alias MUST NOT be used by a publisher to refer to
2359        // two different Tracks simultaneously in the same session." Refused before the
2360        // Request ID is allocated, so a refusal spends nothing.
2361        let alias = track_alias.into_inner();
2362        if let Some(refusal) = self.alias_held_elsewhere(alias, &track_namespace, &track_name) {
2363            return Err(refusal);
2364        }
2365        let req_id = self.request_ids.allocate()?;
2366        let mut sm = PublishStateMachine::new();
2367        sm.on_publish_sent()?;
2368        self.publishes.insert(req_id.into_inner(), sm);
2369        // The alias and the track travel together in a PUBLISH, so the binding
2370        // is complete the moment the message is built.
2371        self.track_bindings.insert(
2372            req_id.into_inner(),
2373            TrackBinding {
2374                namespace: track_namespace.clone(),
2375                name: track_name.clone(),
2376                alias: Some(alias),
2377                kind: BindingKind::Publish,
2378            },
2379        );
2380
2381        let msg = ControlMessage::Publish(Publish {
2382            request_id: req_id,
2383            track_namespace,
2384            track_name,
2385            track_alias,
2386            parameters,
2387            track_properties,
2388        });
2389        Ok((req_id, msg))
2390    }
2391
2392    pub fn send_publish_done(
2393        &mut self,
2394        request_id: VarInt,
2395        status_code: VarInt,
2396        stream_count: VarInt,
2397        reason_phrase: Vec<u8>,
2398    ) -> Result<ControlMessage, EndpointError> {
2399        let id = request_id.into_inner();
2400        // Before the state machine moves, so an ending under the wrong status
2401        // leaves the publication where it was. A PUBLISH this endpoint sent is
2402        // ended here rather than through the response path, and a refused
2403        // update on it owes the same ending as one on a peer's SUBSCRIBE.
2404        self.require_update_failure_status(id, status_code)?;
2405        let sm = self.publishes.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
2406        sm.on_publish_done_sent()?;
2407        Ok(ControlMessage::PublishDone(PublishDone { status_code, stream_count, reason_phrase }))
2408    }
2409
2410    // -- Consolidated responses (per-bidi-stream routing) -----------
2411
2412    /// Process an incoming REQUEST_OK on the bidi stream identified by
2413    /// `request_id`. Draft-22: PUBLISH_OK is a REQUEST_OK alias, so this
2414    /// handler also resolves outstanding PUBLISH requests.
2415    ///
2416    /// # Track Properties are refused except on TRACK_STATUS_OK
2417    ///
2418    /// Draft-22 Section 9.3: Track Properties "are populated in
2419    /// TRACK_STATUS_OK; they are empty in PUBLISH_OK, REQUEST_UPDATE_OK,
2420    /// SUBSCRIBE_NAMESPACE_OK and PUBLISH_NAMESPACE_OK. If an endpoint
2421    /// receives Track Properties in one of these messages it MUST close the
2422    /// session with a PROTOCOL_VIOLATION." The codec cannot make that check —
2423    /// REQUEST_OK is one wire form and only the request stream says which of
2424    /// the five shapes it is. This is the layer that knows, because finding
2425    /// the request id in one of the maps below is what names the shape.
2426    pub fn receive_request_ok(
2427        &mut self,
2428        request_id: VarInt,
2429        msg: &RequestOk,
2430    ) -> Result<(), EndpointError> {
2431        let id = request_id.into_inner();
2432        if !msg.track_properties.is_empty() && !self.track_statuses.contains_key(&id) {
2433            return Err(self.fail_session(EndpointError::TrackPropertiesOnNonTrackStatus(id)));
2434        }
2435        if let Some(sm) = self.publishes.get_mut(&id) {
2436            sm.on_publish_ok()?;
2437            return Ok(());
2438        }
2439        if let Some(sm) = self.subscribe_namespaces.get_mut(&id) {
2440            sm.on_subscribe_namespace_ok()?;
2441            return Ok(());
2442        }
2443        if let Some(sm) = self.subscribe_tracks.get_mut(&id) {
2444            sm.on_subscribe_namespace_ok()?;
2445            return Ok(());
2446        }
2447        if let Some(sm) = self.publish_namespaces.get_mut(&id) {
2448            sm.on_publish_namespace_ok()?;
2449            return Ok(());
2450        }
2451        if let Some(sm) = self.track_statuses.get_mut(&id) {
2452            sm.on_track_status_ok()?;
2453            return Ok(());
2454        }
2455        Err(EndpointError::UnknownRequest(id))
2456    }
2457
2458    /// Process an incoming REQUEST_ERROR on the bidi stream identified by
2459    /// `request_id`.
2460    pub fn receive_request_error(
2461        &mut self,
2462        request_id: VarInt,
2463        msg: &RequestError,
2464    ) -> Result<(), EndpointError> {
2465        let id = request_id.into_inner();
2466        if let Some(redirect) = &msg.redirect {
2467            // Draft-22 Section 9.4.1: "If a server receives a Redirect with a
2468            // non-zero Connect URI Length it MUST close the session with a
2469            // PROTOCOL_VIOLATION." Left unchecked, a Redirect sends a server
2470            // chasing a URI a client picked.
2471            if self.role == Role::Server && !redirect.connect_uri.is_empty() {
2472                return Err(self.fail_session(EndpointError::RedirectUriAtServer));
2473            }
2474            // Same section: "Track Name is not meaningful for namespace-scoped
2475            // requests (SUBSCRIBE_NAMESPACE, PUBLISH_NAMESPACE,
2476            // SUBSCRIBE_TRACKS) and MUST be empty; an endpoint that receives a
2477            // non-empty Track Name in a Redirect for a namespace-scoped request
2478            // MUST close the session with a PROTOCOL_VIOLATION." Which request
2479            // this answers is known only here, from the map the stream's id is
2480            // in.
2481            let namespace_scoped = self.subscribe_namespaces.contains_key(&id)
2482                || self.subscribe_tracks.contains_key(&id)
2483                || self.publish_namespaces.contains_key(&id);
2484            if namespace_scoped && !redirect.track_name.is_empty() {
2485                return Err(
2486                    self.fail_session(EndpointError::RedirectTrackNameOnNamespaceRequest(id))
2487                );
2488            }
2489        }
2490        if let Some(sm) = self.subscriptions.get_mut(&id) {
2491            sm.on_subscribe_error()?;
2492            return Ok(());
2493        }
2494        if let Some(sm) = self.fetches.get_mut(&id) {
2495            sm.on_fetch_error()?;
2496            return Ok(());
2497        }
2498        if let Some(sm) = self.publishes.get_mut(&id) {
2499            sm.on_publish_error()?;
2500            return Ok(());
2501        }
2502        if let Some(sm) = self.subscribe_namespaces.get_mut(&id) {
2503            sm.on_subscribe_namespace_error()?;
2504            return Ok(());
2505        }
2506        if let Some(sm) = self.subscribe_tracks.get_mut(&id) {
2507            sm.on_subscribe_namespace_error()?;
2508            return Ok(());
2509        }
2510        if let Some(sm) = self.publish_namespaces.get_mut(&id) {
2511            sm.on_publish_namespace_error()?;
2512            return Ok(());
2513        }
2514        if let Some(sm) = self.track_statuses.get_mut(&id) {
2515            sm.on_track_status_error()?;
2516            return Ok(());
2517        }
2518        Err(EndpointError::UnknownRequest(id))
2519    }
2520
2521    /// Record that a request was cancelled at its stream.
2522    ///
2523    /// This draft withdraws a request by terminating the bidirectional stream
2524    /// it was made on rather than by sending a message. Section 6.4.2.3: "Once
2525    /// a request stream has been opened, the request MAY be cancelled by either
2526    /// endpoint. Senders cancel requests if the response is no longer of
2527    /// interest; Receivers cancel requests if they are unable to or choose not
2528    /// to respond."
2529    ///
2530    /// Both of those reach here. A request the peer opened and one this
2531    /// endpoint opened share a map and cannot collide, because their Request
2532    /// IDs have opposite least significant bits, so one method records a cancel
2533    /// from whichever side performed it.
2534    ///
2535    /// The request moves to its end state, which is what makes the record worth
2536    /// keeping: a response arriving afterwards is refused rather than applied to
2537    /// a request that is over. The Request ID is not released — nothing here
2538    /// reuses one — and the per-request bookkeeping keyed by it is left alone,
2539    /// since a cancelled request's stream carries nothing more.
2540    ///
2541    /// Nothing is written on the wire. The reset that goes with this is
2542    /// [`Connection::cancel_request_stream`], which calls this first and
2543    /// terminates the stream only if it returns `Ok`.
2544    ///
2545    /// # Errors
2546    ///
2547    /// [`EndpointError::UnknownRequest`] when no request of any kind carries
2548    /// that id, and the kind's own `InvalidTransition` when the request has not
2549    /// been written yet.
2550    ///
2551    /// [`Connection::cancel_request_stream`]: crate::draft22::connection::Connection::cancel_request_stream
2552    pub fn cancel_request(&mut self, request_id: VarInt) -> Result<(), EndpointError> {
2553        let id = request_id.into_inner();
2554        if let Some(sm) = self.subscriptions.get_mut(&id) {
2555            sm.on_request_cancelled()?;
2556            return Ok(());
2557        }
2558        if let Some(sm) = self.fetches.get_mut(&id) {
2559            sm.on_request_cancelled()?;
2560            return Ok(());
2561        }
2562        if let Some(sm) = self.publishes.get_mut(&id) {
2563            sm.on_request_cancelled()?;
2564            return Ok(());
2565        }
2566        if let Some(sm) = self.subscribe_namespaces.get_mut(&id) {
2567            sm.on_request_cancelled()?;
2568            return Ok(());
2569        }
2570        if let Some(sm) = self.subscribe_tracks.get_mut(&id) {
2571            sm.on_request_cancelled()?;
2572            return Ok(());
2573        }
2574        if let Some(sm) = self.publish_namespaces.get_mut(&id) {
2575            sm.on_request_cancelled()?;
2576            return Ok(());
2577        }
2578        if let Some(sm) = self.track_statuses.get_mut(&id) {
2579            sm.on_request_cancelled()?;
2580            return Ok(());
2581        }
2582        Err(EndpointError::UnknownRequest(id))
2583    }
2584
2585    // -- PublishSkipped / Namespace announcements -------------------
2586
2587    pub fn receive_namespace(&mut self, _msg: &message::Namespace) -> Result<(), EndpointError> {
2588        Ok(())
2589    }
2590
2591    pub fn receive_namespace_done(
2592        &mut self,
2593        _msg: &message::NamespaceDone,
2594    ) -> Result<(), EndpointError> {
2595        Ok(())
2596    }
2597
2598    pub fn receive_publish_skipped(&mut self, _msg: &PublishSkipped) -> Result<(), EndpointError> {
2599        Ok(())
2600    }
2601
2602    // -- Unified message dispatch -----------------------------------
2603
2604    /// Dispatch a message that arrived on the control stream.
2605    ///
2606    /// Draft-22 Table 5 gives every message a Stream value, and only two of
2607    /// them name the control stream: SETUP is "Control", GOAWAY is "Control,
2608    /// Request". Everything else is "Request", so this method's job is to take
2609    /// those two and refuse the messages that identify a request they have no
2610    /// stream to name.
2611    ///
2612    /// Four are refused for that reason. REQUEST_UPDATE modifies the request
2613    /// its stream carries (Section 9.5). NAMESPACE and NAMESPACE_DONE report
2614    /// namespaces on the SUBSCRIBE_NAMESPACE request stream that asked for them
2615    /// (Section 9.16 and Section 9.17), and PUBLISH_SKIPPED names a track that
2616    /// will not be published on the SUBSCRIBE_TRACKS stream that asked for it
2617    /// (Section 9.19). All four route through
2618    /// [`receive_response_on_stream`](Self::receive_response_on_stream), which
2619    /// has the request ID they need.
2620    pub fn receive_message(&mut self, msg: ControlMessage) -> Result<(), EndpointError> {
2621        match msg {
2622            ControlMessage::Setup(ref m) => self.receive_setup(m),
2623            ControlMessage::GoAway(ref m) => self.receive_goaway(m),
2624            ControlMessage::RequestUpdate(_) => {
2625                Err(self.fail_session(EndpointError::RequestUpdateOnControlStream))
2626            }
2627            // Refused, and the session left running. No draft states a close
2628            // for a message arriving on a stream it does not belong on — see
2629            // `session_error_code`, which answers `None` for them. A
2630            // control message carries its own length, so the next boundary is
2631            // known and a refused message costs the session nothing; that is
2632            // the answer `ResponseOnControlStream` below has always given.
2633            //
2634            // REQUEST_UPDATE above is the one that is not like these: Section
2635            // 9.5 does close over one that arrives outside the two cases it
2636            // names, so it keeps both the close and `fail_session`.
2637            ControlMessage::Namespace(_) => {
2638                Err(EndpointError::RequestMessageOnControlStream("NAMESPACE"))
2639            }
2640            ControlMessage::NamespaceDone(_) => {
2641                Err(EndpointError::RequestMessageOnControlStream("NAMESPACE_DONE"))
2642            }
2643            ControlMessage::PublishSkipped(_) => {
2644                Err(EndpointError::RequestMessageOnControlStream("PUBLISH_SKIPPED"))
2645            }
2646            // Section 9.10 puts PUBLISH_STATE_NOTIFY on "a subscription's
2647            // bidirectional stream", and the subscription it reports on is the
2648            // one that stream carries. One on the control stream names no
2649            // subscription, so there is nothing it could be about.
2650            //
2651            // Not the close Section 9.10 names, and the difference is worth
2652            // being exact about. That sentence closes over a
2653            // PUBLISH_STATE_NOTIFY "for any other request type, or from the
2654            // subscriber" — both of which are readings of the request its
2655            // Request ID names, and one on the control stream names no request
2656            // at all. Stretching the sentence to cover it would be this build
2657            // reading a rule into a draft, so this takes the same answer as its
2658            // three neighbours above.
2659            ControlMessage::PublishStateNotify(_) => {
2660                Err(EndpointError::RequestMessageOnControlStream("PUBLISH_STATE_NOTIFY"))
2661            }
2662            ControlMessage::SubscribeOk(_)
2663            | ControlMessage::PublishDone(_)
2664            | ControlMessage::FetchOk(_)
2665            | ControlMessage::RequestOk(_)
2666            | ControlMessage::RequestError(_) => Err(EndpointError::ResponseOnControlStream),
2667            _ => Ok(()),
2668        }
2669    }
2670
2671    /// Hold the response half of a namespace-scoped request to the rule that
2672    /// its first message answers the request.
2673    ///
2674    /// Section 4.2 and Section 3.6 state it once each, for SUBSCRIBE_NAMESPACE
2675    /// and for SUBSCRIBE_TRACKS. Section 3.6: "The publisher will respond with
2676    /// SUBSCRIBE_TRACKS_OK or SUBSCRIBE_TRACKS_ERROR on the response half of
2677    /// the stream. If the subscriber receives any message other than a
2678    /// SUBSCRIBE_TRACKS_OK or a SUBSCRIBE_TRACKS_ERROR as the first message on
2679    /// the response half of the stream, then it MUST close the session with a
2680    /// PROTOCOL_VIOLATION." Section 1.5 makes both names a REQUEST_OK and a
2681    /// REQUEST_ERROR. Draft-18's own change log records it
2682    /// as new work rather than as a clarification, so drafts 17 and earlier are
2683    /// deliberately not held to it: they say nothing about which message comes
2684    /// first, and refusing one there would close a session over traffic those
2685    /// drafts permit.
2686    ///
2687    /// # Why only these two requests
2688    ///
2689    /// They are the two whose response half carries more than an answer.
2690    /// SUBSCRIBE_NAMESPACE goes on to carry NAMESPACE and NAMESPACE_DONE and
2691    /// SUBSCRIBE_TRACKS goes on to carry PUBLISH_SKIPPED, and those are exactly the
2692    /// messages that could arrive before the answer and be taken for it. A
2693    /// SUBSCRIBE has SUBSCRIBE_OK as its own first message and no such
2694    /// ambiguity, which is why the rule is written where it is.
2695    ///
2696    /// # What "first" is read from
2697    ///
2698    /// The request's own state machine. `Pending` means the request went out
2699    /// and nothing has come back, so it is the same question asked of the state
2700    /// rather than of a second counter that could disagree with it. A request
2701    /// this endpoint did not open, or one already answered, is not this rule's
2702    /// subject and passes through.
2703    ///
2704    /// # Errors
2705    ///
2706    /// [`EndpointError::ResponseBeforeTheFirstResponse`], which answers
2707    /// `Some(ProtocolViolation)`, and the session is failed before it returns.
2708    fn require_the_first_response_first(
2709        &mut self,
2710        id: u64,
2711        msg: &ControlMessage,
2712    ) -> Result<(), EndpointError> {
2713        let awaiting = [self.subscribe_namespaces.get(&id), self.subscribe_tracks.get(&id)]
2714            .into_iter()
2715            .flatten()
2716            .any(|sm| sm.state() == SubscribeNamespaceState::Pending);
2717        if !awaiting {
2718            return Ok(());
2719        }
2720        if matches!(msg, ControlMessage::RequestOk(_) | ControlMessage::RequestError(_)) {
2721            return Ok(());
2722        }
2723        let ty = msg.message_type();
2724        Err(self.fail_session(EndpointError::ResponseBeforeTheFirstResponse(id, ty)))
2725    }
2726
2727    /// Dispatch a message that arrived on the bidi request stream identified
2728    /// by `request_id`.
2729    ///
2730    /// Beyond the five responses this also takes the messages draft-22 Table 5
2731    /// places on a request stream without their being answers to it.
2732    ///
2733    /// REQUEST_UPDATE modifies the request the stream carries (Section 9.5).
2734    /// GOAWAY is listed "Control, Request" because "A GOAWAY MAY also be sent
2735    /// on a request stream to initiate migration of that individual request"
2736    /// (Section 9.2); draft-19 removed GOAWAY's Request ID and left one wire
2737    /// form for both places, and draft-22 keeps it that way.
2738    ///
2739    /// NAMESPACE (0x8) and NAMESPACE_DONE (0xE) arrive on the
2740    /// SUBSCRIBE_NAMESPACE request stream that asked for the namespaces they
2741    /// report, and PUBLISH_SKIPPED (0xF) on the SUBSCRIBE_TRACKS stream that
2742    /// asked for the track it says will not be published. Table 5 marks all
2743    /// three "Request", so this is where they land; the control stream refuses
2744    /// them.
2745    ///
2746    /// PUBLISH_STATE_NOTIFY (0x22) is draft-20's addition to that set, and the
2747    /// one message here that is neither a response nor a request: Section 9.10
2748    /// puts it on "a subscription's bidirectional stream" and answers it with
2749    /// nothing at all.
2750    pub fn receive_response_on_stream(
2751        &mut self,
2752        request_id: VarInt,
2753        msg: ControlMessage,
2754    ) -> Result<(), EndpointError> {
2755        self.require_the_first_response_first(request_id.into_inner(), &msg)?;
2756        match msg {
2757            ControlMessage::SubscribeOk(ref m) => self.receive_subscribe_ok(request_id, m),
2758            ControlMessage::PublishDone(ref m) => self.receive_publish_done(request_id, m),
2759            ControlMessage::FetchOk(ref m) => self.receive_fetch_ok(request_id, m),
2760            // New in draft-20 and the one message on a request stream that is
2761            // neither a response nor a request: Section 9.10 puts it on "a
2762            // subscription's bidirectional stream" and answers it with nothing.
2763            ControlMessage::PublishStateNotify(ref m) => {
2764                self.receive_publish_state_notify(request_id, m)
2765            }
2766            ControlMessage::RequestOk(ref m) => self.receive_request_ok(request_id, m),
2767            ControlMessage::RequestError(ref m) => self.receive_request_error(request_id, m),
2768            ControlMessage::RequestUpdate(ref m) => self.receive_request_update(request_id, m),
2769            ControlMessage::GoAway(ref m) => self.receive_goaway_on_request_stream(request_id, m),
2770            ControlMessage::Namespace(ref m) => self.receive_namespace(m),
2771            ControlMessage::NamespaceDone(ref m) => self.receive_namespace_done(m),
2772            ControlMessage::PublishSkipped(ref m) => self.receive_publish_skipped(m),
2773            _ => Err(EndpointError::ResponseOnControlStream),
2774        }
2775    }
2776
2777    /// Process a GOAWAY that arrived on one request stream rather than on the
2778    /// control stream.
2779    ///
2780    /// Draft-22 Section 9.2: "A GOAWAY MAY also be sent on a request stream
2781    /// to initiate migration of that individual request. Upon receiving a
2782    /// GOAWAY on a request stream, the endpoint SHOULD re-issue that specific
2783    /// request on a session at the specified URI". The session keeps running —
2784    /// only this request is being moved — so the session state machine is not
2785    /// touched and no draining event follows. The server-side URI rule of the
2786    /// same section still applies.
2787    ///
2788    /// # Errors
2789    ///
2790    /// [`EndpointError::RepeatedGoAwayOnRequestStream`] if this request's
2791    /// stream has already carried one. The session is over: this endpoint's own
2792    /// state has moved to Closed and the code the transport should close with
2793    /// is in [`EndpointError::session_error_code`].
2794    pub fn receive_goaway_on_request_stream(
2795        &mut self,
2796        request_id: VarInt,
2797        msg: &GoAway,
2798    ) -> Result<(), EndpointError> {
2799        if self.role == Role::Server && !msg.new_session_uri.is_empty() {
2800            return Err(self.fail_session(EndpointError::GoAwayUriAtServer));
2801        }
2802        let id = request_id.into_inner();
2803        if self.subscriptions.contains_key(&id)
2804            || self.publishes.contains_key(&id)
2805            || self.fetches.contains_key(&id)
2806            || self.subscribe_namespaces.contains_key(&id)
2807            || self.subscribe_tracks.contains_key(&id)
2808            || self.publish_namespaces.contains_key(&id)
2809            || self.track_statuses.contains_key(&id)
2810        {
2811            // Section 9.2 counts per stream, so this is a separate first
2812            // GOAWAY on every request and a repeat only on the one that has
2813            // already carried one. The set is fed only by GOAWAYs accepted
2814            // here, and never pruned: a Request ID is spent once, so an entry
2815            // can never come to describe a different request.
2816            if self.goaway_request_streams.insert(id) {
2817                Ok(())
2818            } else {
2819                Err(self.fail_session(EndpointError::RepeatedGoAwayOnRequestStream(id)))
2820            }
2821        } else {
2822            Err(EndpointError::UnknownRequest(id))
2823        }
2824    }
2825
2826    // -- Responder side: requests the peer opened a stream with -----
2827
2828    /// Refuse a bidirectional stream the peer opened with a message type that
2829    /// does not begin a request, and end the session.
2830    ///
2831    /// Draft-22 Section 6.3: "Bidirectional streams MUST NOT begin with any
2832    /// other message type unless negotiated. If they do, the peer MUST close
2833    /// the Session with a PROTOCOL_VIOLATION." The returned error answers
2834    /// `Some(ProtocolViolation)` from
2835    /// [`EndpointError::session_error_code`], which is what tells the
2836    /// connection layer to put the close on the wire.
2837    pub fn refuse_non_request(&mut self, ty: MessageType) -> EndpointError {
2838        self.fail_session(EndpointError::NotARequest(ty))
2839    }
2840
2841    /// Register a request the **peer** opened a bidirectional stream with, and
2842    /// hand back the Request ID it carries.
2843    ///
2844    /// The mirror of [`receive_response_on_stream`](Self::receive_response_on_stream):
2845    /// that one is fed what comes back on a stream this endpoint opened, this
2846    /// one is fed the first message on a stream the peer opened.
2847    ///
2848    /// # What it enforces, and with which code
2849    ///
2850    /// Draft-22 Section 6.4.2.1: "If an endpoint receives a Request ID where the
2851    /// least significant bit is incorrect for the sender, or a duplicate
2852    /// Request ID, it MUST close the session with INVALID_REQUEST_ID." Both
2853    /// halves are checked here and both are returned as errors that answer
2854    /// `Some(InvalidRequestId)` from
2855    /// [`EndpointError::session_error_code`]. A message that opens no request
2856    /// stream at all is a different rule with a different code — see
2857    /// [`refuse_non_request`](Self::refuse_non_request).
2858    ///
2859    /// # Why there are no separate inbound maps
2860    ///
2861    /// The peer's ids and this endpoint's ids have opposite least significant
2862    /// bits, so they cannot collide. A peer's request goes into the same
2863    /// `HashMap` its outbound twin would, keyed the same way, and only the
2864    /// transition names differ — `on_subscribe_received` where the outbound
2865    /// path calls `on_subscribe_sent`.
2866    ///
2867    /// # Peer-controlled growth
2868    ///
2869    /// Every accepted request adds an entry that nothing removes, and the peer
2870    /// chooses how many to open. [`peer_request_count`](Self::peer_request_count)
2871    /// is what a responder can watch to impose its own ceiling; this method
2872    /// imposes none.
2873    pub fn receive_request_on_stream(
2874        &mut self,
2875        msg: &ControlMessage,
2876    ) -> Result<VarInt, EndpointError> {
2877        let request_id = match msg {
2878            ControlMessage::Subscribe(m) => m.request_id,
2879            ControlMessage::Publish(m) => m.request_id,
2880            ControlMessage::Fetch(m) => m.request_id,
2881            ControlMessage::PublishNamespace(m) => m.request_id,
2882            ControlMessage::SubscribeNamespace(m) => m.request_id,
2883            ControlMessage::SubscribeTracks(m) => m.request_id,
2884            ControlMessage::TrackStatus(m) => m.request_id,
2885            other => return Err(self.refuse_non_request(other.message_type())),
2886        };
2887        self.require_active_or_err()?;
2888
2889        let id = request_id.into_inner();
2890        if let Err(e) = self.request_ids.validate_peer_id(id) {
2891            return Err(self.fail_session(EndpointError::RequestId(e)));
2892        }
2893        // `insert` answers false when the id was already present, which is
2894        // exactly the duplicate the section names. Doing the check and the
2895        // record in one step means no path can record an id it did not check.
2896        if !self.peer_request_ids.insert(id) {
2897            return Err(self.fail_session(EndpointError::DuplicateRequestId(id)));
2898        }
2899
2900        match msg {
2901            ControlMessage::Subscribe(m) => {
2902                // Section 9.20.15: the presence of FILL_PARAMETERS "is what
2903                // requests a fill fetch stream". Noted as the SUBSCRIBE
2904                // arrives, because this endpoint is the publisher on a stream
2905                // the peer opened and the fill is a stream it owes.
2906                self.note_fill_requested(id, &m.parameters);
2907                let mut sm = SubscriptionStateMachine::new();
2908                sm.on_subscribe_received()?;
2909                self.subscriptions.insert(id, sm);
2910            }
2911            ControlMessage::Publish(m) => {
2912                let alias = m.track_alias.into_inner();
2913                if let Some(conflict) =
2914                    self.conflicting_track_alias(id, alias, &m.track_namespace, &m.track_name)
2915                {
2916                    return Err(self.fail_session(conflict));
2917                }
2918                let mut sm = PublishStateMachine::new();
2919                sm.on_publish_received()?;
2920                self.publishes.insert(id, sm);
2921                // A PUBLISH names its track and its alias in the one message,
2922                // so the binding is complete on arrival. It counts against the
2923                // next one only once this endpoint has answered PUBLISH_OK,
2924                // which is what moves its state machine to Active.
2925                self.track_bindings.insert(
2926                    id,
2927                    TrackBinding {
2928                        namespace: m.track_namespace.clone(),
2929                        name: m.track_name.clone(),
2930                        alias: Some(alias),
2931                        kind: BindingKind::Publish,
2932                    },
2933                );
2934            }
2935            // Nothing is judged here that draft-19 judged. Its FETCH could
2936            // name a subscription to join, and a Joining Fetch naming none was
2937            // a REQUEST_ERROR this endpoint had to owe; draft-22 Section 9.11
2938            // has no such field and no such structures, and Section 16.11.2
2939            // does not assign `INVALID_JOINING_REQUEST_ID`, so a FETCH names a
2940            // track like a SUBSCRIBE and there is nothing about it to refuse
2941            // in advance.
2942            ControlMessage::Fetch(_) => {
2943                let mut sm = FetchStateMachine::new();
2944                sm.on_fetch_received()?;
2945                self.fetches.insert(id, sm);
2946            }
2947            ControlMessage::PublishNamespace(_) => {
2948                let mut sm = PublishNamespaceStateMachine::new();
2949                sm.on_publish_namespace_received()?;
2950                self.publish_namespaces.insert(id, sm);
2951            }
2952            ControlMessage::SubscribeNamespace(m) => {
2953                if let Some(established) = self.peer_namespace_overlap(&m.namespace_prefix, None) {
2954                    self.overlapping_namespace_subscriptions.insert(id, established);
2955                }
2956                let mut sm = SubscribeNamespaceStateMachine::new();
2957                sm.on_subscribe_namespace_received()?;
2958                self.subscribe_namespaces.insert(id, sm);
2959            }
2960            // The seventh request kind, and the one draft-17 does not have.
2961            // It gets its own map for the same reason the outbound path gives
2962            // it one: Section 12.3 makes SUBSCRIBE_TRACKS and
2963            // SUBSCRIBE_NAMESPACE independent overlap spaces, so a responder
2964            // that merged them would answer the wrong request.
2965            ControlMessage::SubscribeTracks(m) => {
2966                // Its own space: a SUBSCRIBE_TRACKS is weighed against the
2967                // SUBSCRIBE_TRACKS the peer has open and against nothing else,
2968                // so one prefix may carry one of each.
2969                if let Some(established) = self.peer_tracks_overlap(&m.namespace_prefix, None) {
2970                    self.overlapping_namespace_subscriptions.insert(id, established);
2971                }
2972                // Section 9.20.15 lists SUBSCRIBE and REQUEST_UPDATE and not
2973                // this message, and Section 3.6.2 names it anyway: "To join
2974                // Tracks initiated via the resulting PUBLISHes, the subscriber
2975                // can specify a Location Filter and optionally include
2976                // FILL_PARAMETERS". The codec's scope table follows that
2977                // sentence for this parameter, so this records what arrives
2978                // under it.
2979                self.note_fill_requested(id, &m.parameters);
2980                let mut sm = SubscribeNamespaceStateMachine::new();
2981                sm.on_subscribe_namespace_received()?;
2982                self.subscribe_tracks.insert(id, sm);
2983            }
2984            ControlMessage::TrackStatus(_) => {
2985                let mut sm = TrackStatusStateMachine::new();
2986                sm.on_track_status_received()?;
2987                self.track_statuses.insert(id, sm);
2988            }
2989            // Unreachable: the match above returned for every other variant.
2990            other => return Err(self.refuse_non_request(other.message_type())),
2991        }
2992
2993        // The request as it arrived, recorded last so that nothing above it
2994        // can leave one behind for a request it went on to refuse.
2995        self.inbound_requests.insert(id, msg.clone());
2996
2997        // After the request is recorded, never before. The answer to every
2998        // Range Filter rule is a REQUEST_ERROR, which names the Request ID of
2999        // the request it answers, so refusing here would leave nothing to
3000        // answer with.
3001        let parameters = request_parameters(msg);
3002        if let Some(rejection) = self.filter_verdict(parameters) {
3003            self.peer_filter_rejections.insert(id, rejection);
3004        }
3005        self.peer_request_filters.insert(id, range_filter_parameters(parameters));
3006        Ok(request_id)
3007    }
3008
3009    // -- What the peer asked for ------------------------------------
3010
3011    /// The SUBSCRIBE the peer sent under `request_id` and this endpoint has
3012    /// not answered yet.
3013    ///
3014    /// `None` once it has been answered, for an identifier this session has
3015    /// carried no SUBSCRIBE under, and for one this endpoint spent on a
3016    /// request of its own -- whose state is in the same map, but which never
3017    /// arrived here. The record itself lives on past the answer, because the
3018    /// subscription it opened runs on after it, for as long as the request
3019    /// stream does.
3020    pub fn pending_subscribe(&self, request_id: VarInt) -> Option<&Subscribe> {
3021        let id = request_id.into_inner();
3022        let unanswered = self
3023            .subscriptions
3024            .get(&id)
3025            .is_some_and(|sm| sm.state() == SubscriptionState::Subscribing);
3026        match self.inbound_requests.get(&id) {
3027            Some(ControlMessage::Subscribe(msg)) if unanswered => Some(msg),
3028            _ => None,
3029        }
3030    }
3031
3032    /// How many SUBSCRIBEs the peer has sent that are still waiting for an
3033    /// answer.
3034    pub fn pending_subscribe_count(&self) -> usize {
3035        self.inbound_requests
3036            .iter()
3037            .filter(|(id, msg)| {
3038                matches!(msg, ControlMessage::Subscribe(_))
3039                    && self
3040                        .subscriptions
3041                        .get(*id)
3042                        .is_some_and(|sm| sm.state() == SubscriptionState::Subscribing)
3043            })
3044            .count()
3045    }
3046
3047    /// The PUBLISH the peer sent under `request_id` and this endpoint has not
3048    /// answered yet.
3049    ///
3050    /// `None` once it has been answered, for an identifier this session has
3051    /// carried no PUBLISH under, and for one this endpoint spent on a request
3052    /// of its own -- whose state is in the same map, but which never arrived
3053    /// here. The record itself lives on past the answer, because the
3054    /// subscription the offer opened runs on after it, for as long as the
3055    /// request stream does.
3056    pub fn pending_publish(&self, request_id: VarInt) -> Option<&message::Publish> {
3057        let id = request_id.into_inner();
3058        let unanswered =
3059            self.publishes.get(&id).is_some_and(|sm| sm.state() == PublishState::Publishing);
3060        match self.inbound_requests.get(&id) {
3061            Some(ControlMessage::Publish(msg)) if unanswered => Some(msg),
3062            _ => None,
3063        }
3064    }
3065
3066    /// How many offers the peer has sent that are still waiting for an
3067    /// answer.
3068    pub fn pending_publish_count(&self) -> usize {
3069        self.inbound_requests
3070            .iter()
3071            .filter(|(id, msg)| {
3072                matches!(msg, ControlMessage::Publish(_))
3073                    && self
3074                        .publishes
3075                        .get(*id)
3076                        .is_some_and(|sm| sm.state() == PublishState::Publishing)
3077            })
3078            .count()
3079    }
3080
3081    /// The FETCH the peer sent under `request_id` and this endpoint has not
3082    /// answered yet.
3083    ///
3084    /// `None` once it has been answered, for an identifier this session has
3085    /// carried no FETCH under, and for one this endpoint spent on a request
3086    /// of its own -- whose state is in the same map, but which never arrived
3087    /// here. The record itself lives on past the answer, because the fetch is
3088    /// not over until its response stream is.
3089    pub fn pending_fetch(&self, request_id: VarInt) -> Option<&Fetch> {
3090        let id = request_id.into_inner();
3091        let unanswered = self
3092            .fetches
3093            .get(&id)
3094            .is_some_and(|sm| matches!(sm.state(), FetchState::Pending | FetchState::Unanswered));
3095        match self.inbound_requests.get(&id) {
3096            Some(ControlMessage::Fetch(msg)) if unanswered => Some(msg),
3097            _ => None,
3098        }
3099    }
3100
3101    /// How many FETCHes the peer has sent that are still waiting for an
3102    /// answer.
3103    pub fn pending_fetch_count(&self) -> usize {
3104        self.inbound_requests
3105            .iter()
3106            .filter(|(id, msg)| {
3107                matches!(msg, ControlMessage::Fetch(_))
3108                    && self.fetches.get(*id).is_some_and(|sm| {
3109                        matches!(sm.state(), FetchState::Pending | FetchState::Unanswered)
3110                    })
3111            })
3112            .count()
3113    }
3114
3115    /// The PUBLISH_NAMESPACE the peer sent under `request_id` and this
3116    /// endpoint has not answered yet.
3117    ///
3118    /// `None` once it has been answered, for an identifier this session has
3119    /// carried no PUBLISH_NAMESPACE under, and for one this endpoint spent on
3120    /// a request of its own -- whose state is in the same map, but which
3121    /// never arrived here. The record itself lives on past the answer,
3122    /// because an announcement that was accepted stands until it is
3123    /// withdrawn.
3124    pub fn pending_publish_namespace(&self, request_id: VarInt) -> Option<&PublishNamespace> {
3125        let id = request_id.into_inner();
3126        let unanswered = self
3127            .publish_namespaces
3128            .get(&id)
3129            .is_some_and(|sm| sm.state() == PublishNamespaceState::Pending);
3130        match self.inbound_requests.get(&id) {
3131            Some(ControlMessage::PublishNamespace(msg)) if unanswered => Some(msg),
3132            _ => None,
3133        }
3134    }
3135
3136    /// How many announcements the peer has sent that are still waiting for an
3137    /// answer.
3138    pub fn pending_publish_namespace_count(&self) -> usize {
3139        self.inbound_requests
3140            .iter()
3141            .filter(|(id, msg)| {
3142                matches!(msg, ControlMessage::PublishNamespace(_))
3143                    && self
3144                        .publish_namespaces
3145                        .get(*id)
3146                        .is_some_and(|sm| sm.state() == PublishNamespaceState::Pending)
3147            })
3148            .count()
3149    }
3150
3151    /// The earliest namespace subscription the peer has made whose prefix
3152    /// overlaps `prefix`, and `None` when there is none.
3153    ///
3154    /// Only ones that have not ended count: the sentence weighs the arriving
3155    /// prefix against an "established" one, so one the peer has withdrawn and
3156    /// one this endpoint refused are both past. Drafts 07 through 11 say "an
3157    /// earlier" instead and count those too.
3158    ///
3159    /// One that has arrived and has not been answered does count. It is not
3160    /// established yet, but this endpoint is the one about to establish it,
3161    /// and accepting both would leave the session holding exactly the pair
3162    /// the sentence exists to prevent.
3163    ///
3164    /// The record of what the peer sent is what tells the two directions
3165    /// apart. The state machines live in one map per kind whichever end
3166    /// opened the request, so a prefix this endpoint asked about would be
3167    /// indistinguishable there; only requests that arrived are written into
3168    /// `inbound_requests`.
3169    ///
3170    /// The lowest Request ID wins when more than one overlaps, so the answer
3171    /// does not depend on the order a map happens to iterate in.
3172    ///
3173    /// `except` is the subscription a REQUEST_UPDATE is moving, which Section
3174    /// 9.20.20 weighs against "another active subscription of the same type"
3175    /// and therefore not against the prefix it is leaving behind.
3176    fn peer_namespace_overlap(&self, prefix: &TrackNamespace, except: Option<u64>) -> Option<u64> {
3177        self.inbound_requests
3178            .iter()
3179            .filter(|(&id, _)| Some(id) != except)
3180            .filter_map(|(&id, msg)| match msg {
3181                ControlMessage::SubscribeNamespace(m) => Some((id, &m.namespace_prefix)),
3182                _ => None,
3183            })
3184            .filter(|(id, _)| {
3185                self.subscribe_namespaces
3186                    .get(id)
3187                    .is_some_and(|sm| sm.state() != SubscribeNamespaceState::Done)
3188            })
3189            .filter_map(|(id, p)| prefixes_overlap(&p.0, &prefix.0).then_some(id))
3190            .min()
3191    }
3192
3193    /// The earliest track subscription the peer has made whose prefix
3194    /// overlaps `prefix`, and `None` when there is none.
3195    ///
3196    /// Only ones that have not ended count: the sentence weighs the arriving
3197    /// prefix against an "established" one, so one the peer has withdrawn and
3198    /// one this endpoint refused are both past. Drafts 07 through 11 say "an
3199    /// earlier" instead and count those too.
3200    ///
3201    /// One that has arrived and has not been answered does count. It is not
3202    /// established yet, but this endpoint is the one about to establish it,
3203    /// and accepting both would leave the session holding exactly the pair
3204    /// the sentence exists to prevent.
3205    ///
3206    /// The record of what the peer sent is what tells the two directions
3207    /// apart. The state machines live in one map per kind whichever end
3208    /// opened the request, so a prefix this endpoint asked about would be
3209    /// indistinguishable there; only requests that arrived are written into
3210    /// `inbound_requests`.
3211    ///
3212    /// The lowest Request ID wins when more than one overlaps, so the answer
3213    /// does not depend on the order a map happens to iterate in.
3214    ///
3215    /// `except` is the subscription a REQUEST_UPDATE is moving, which Section
3216    /// 9.20.20 weighs against "another active subscription of the same type"
3217    /// and therefore not against the prefix it is leaving behind.
3218    fn peer_tracks_overlap(&self, prefix: &TrackNamespace, except: Option<u64>) -> Option<u64> {
3219        self.inbound_requests
3220            .iter()
3221            .filter(|(&id, _)| Some(id) != except)
3222            .filter_map(|(&id, msg)| match msg {
3223                ControlMessage::SubscribeTracks(m) => Some((id, &m.namespace_prefix)),
3224                _ => None,
3225            })
3226            .filter(|(id, _)| {
3227                self.subscribe_tracks
3228                    .get(id)
3229                    .is_some_and(|sm| sm.state() != SubscribeNamespaceState::Done)
3230            })
3231            .filter_map(|(id, p)| prefixes_overlap(&p.0, &prefix.0).then_some(id))
3232            .min()
3233    }
3234
3235    /// The SUBSCRIBE_NAMESPACE the peer sent under `request_id` and this
3236    /// endpoint has not answered yet.
3237    ///
3238    /// `None` once it has been answered, for an identifier this session has
3239    /// carried no SUBSCRIBE_NAMESPACE under, and for one this endpoint spent
3240    /// on a request of its own -- whose state is in the same map, but which
3241    /// never arrived here. The record itself lives on past the answer,
3242    /// because a namespace subscription lasts as long as its stream does.
3243    pub fn pending_subscribe_namespace(&self, request_id: VarInt) -> Option<&SubscribeNamespace> {
3244        let id = request_id.into_inner();
3245        let unanswered = self
3246            .subscribe_namespaces
3247            .get(&id)
3248            .is_some_and(|sm| sm.state() == SubscribeNamespaceState::Pending);
3249        match self.inbound_requests.get(&id) {
3250            Some(ControlMessage::SubscribeNamespace(msg)) if unanswered => Some(msg),
3251            _ => None,
3252        }
3253    }
3254
3255    /// How many namespace subscriptions the peer has sent that are still
3256    /// waiting for an answer.
3257    pub fn pending_subscribe_namespace_count(&self) -> usize {
3258        self.inbound_requests
3259            .iter()
3260            .filter(|(id, msg)| {
3261                matches!(msg, ControlMessage::SubscribeNamespace(_))
3262                    && self
3263                        .subscribe_namespaces
3264                        .get(*id)
3265                        .is_some_and(|sm| sm.state() == SubscribeNamespaceState::Pending)
3266            })
3267            .count()
3268    }
3269
3270    /// The SUBSCRIBE_TRACKS the peer sent under `request_id` and this
3271    /// endpoint has not answered yet.
3272    ///
3273    /// `None` once it has been answered, for an identifier this session has
3274    /// carried no SUBSCRIBE_TRACKS under, and for one this endpoint spent on
3275    /// a request of its own -- whose state is in the same map, but which
3276    /// never arrived here. The record itself lives on past the answer,
3277    /// because the subscription it opened lasts as long as its stream does.
3278    pub fn pending_subscribe_tracks(&self, request_id: VarInt) -> Option<&SubscribeTracks> {
3279        let id = request_id.into_inner();
3280        let unanswered = self
3281            .subscribe_tracks
3282            .get(&id)
3283            .is_some_and(|sm| sm.state() == SubscribeNamespaceState::Pending);
3284        match self.inbound_requests.get(&id) {
3285            Some(ControlMessage::SubscribeTracks(msg)) if unanswered => Some(msg),
3286            _ => None,
3287        }
3288    }
3289
3290    /// How many track subscriptions the peer has sent that are still waiting
3291    /// for an answer.
3292    pub fn pending_subscribe_tracks_count(&self) -> usize {
3293        self.inbound_requests
3294            .iter()
3295            .filter(|(id, msg)| {
3296                matches!(msg, ControlMessage::SubscribeTracks(_))
3297                    && self
3298                        .subscribe_tracks
3299                        .get(*id)
3300                        .is_some_and(|sm| sm.state() == SubscribeNamespaceState::Pending)
3301            })
3302            .count()
3303    }
3304
3305    /// The TRACK_STATUS the peer sent under `request_id` and this endpoint
3306    /// has not answered yet.
3307    ///
3308    /// `None` once it has been answered, for an identifier this session has
3309    /// carried no TRACK_STATUS under, and for one this endpoint spent on a
3310    /// request of its own -- whose state is in the same map, but which never
3311    /// arrived here. The record itself lives on past the answer, because an
3312    /// update may still name it once it has been answered.
3313    pub fn pending_track_status(&self, request_id: VarInt) -> Option<&message::TrackStatus> {
3314        let id = request_id.into_inner();
3315        let unanswered =
3316            self.track_statuses.get(&id).is_some_and(|sm| sm.state() == TrackStatusState::Pending);
3317        match self.inbound_requests.get(&id) {
3318            Some(ControlMessage::TrackStatus(msg)) if unanswered => Some(msg),
3319            _ => None,
3320        }
3321    }
3322
3323    /// How many track statuses the peer has sent that are still waiting for
3324    /// an answer.
3325    pub fn pending_track_status_count(&self) -> usize {
3326        self.inbound_requests
3327            .iter()
3328            .filter(|(id, msg)| {
3329                matches!(msg, ControlMessage::TrackStatus(_))
3330                    && self
3331                        .track_statuses
3332                        .get(*id)
3333                        .is_some_and(|sm| sm.state() == TrackStatusState::Pending)
3334            })
3335            .count()
3336    }
3337
3338    /// Whether the Range Filters in `parameters` are ones this endpoint may
3339    /// accept.
3340    ///
3341    /// The three rules that need more than one parameter to see, in the order
3342    /// that reports the most specific fault: whether the filters read at all,
3343    /// then whether any budget was advertised, then whether the request stays
3344    /// inside it, then whether two filters share a key. A request with no Range
3345    /// Filter at all is not measured against the ceiling, so an endpoint that
3346    /// advertised nothing still takes ordinary requests — which is the whole of
3347    /// the traffic today, since draft-19 is the first draft with these
3348    /// parameters.
3349    fn filter_verdict(&self, parameters: &[KeyValuePair]) -> Option<FilterRejection> {
3350        let filters = match range_filter::decode_all_moqt::<Moqt18>(parameters) {
3351            Ok(filters) => filters,
3352            Err(e) => return Some(FilterRejection::Unreadable(e)),
3353        };
3354        if filters.is_empty() {
3355            return None;
3356        }
3357        if self.advertised_max_filter_ranges == 0 {
3358            return Some(FilterRejection::NoBudgetAdvertised);
3359        }
3360        let ranges = range_filter::total_ranges(&filters);
3361        if ranges as u64 > self.advertised_max_filter_ranges {
3362            return Some(FilterRejection::TooManyRanges {
3363                ranges,
3364                limit: self.advertised_max_filter_ranges,
3365            });
3366        }
3367        if let Some((parameter_type, set_id, property_type)) =
3368            range_filter::first_repeated_key(&filters)
3369        {
3370            return Some(FilterRejection::RepeatedFilter(parameter_type, set_id, property_type));
3371        }
3372        None
3373    }
3374
3375    /// Why request `id` must be answered with a REQUEST_ERROR, if it must.
3376    ///
3377    /// The caller builds the message; [`FilterRejection::request_error_code`]
3378    /// gives the code and the variant gives the reason phrase. Answering it
3379    /// clears the record.
3380    pub fn filter_rejection(&self, id: VarInt) -> Option<&FilterRejection> {
3381        self.peer_filter_rejections.get(&id.into_inner())
3382    }
3383
3384    /// Whether this response answers a REQUEST_UPDATE rather than the request
3385    /// that opened the stream. Section 9.5 gives an update the same two
3386    /// answers a request has: "The receiver of a REQUEST_UPDATE MUST respond
3387    /// with exactly one REQUEST_UPDATE_OK or REQUEST_UPDATE_ERROR message
3388    /// indicating if the update was successful, unless it is coalescing failed
3389    /// updates to produce just one REQUEST_UPDATE_ERROR for multiple
3390    /// REQUEST_UPDATE messages." Section 1.5 makes those names a REQUEST_OK and
3391    /// a REQUEST_ERROR sent in answer to an update. Nothing in
3392    /// either message says which of the two it is answering, so the question is
3393    /// settled twice over.
3394    ///
3395    /// A SUBSCRIBE is answered with SUBSCRIBE_OK and a FETCH with FETCH_OK, so a
3396    /// REQUEST_OK on one of those streams has no other message it could be
3397    /// answering. That is the half that needs no ordering.
3398    ///
3399    /// Everywhere else it is ordering: the first REQUEST_OK or REQUEST_ERROR on
3400    /// a stream answers the request that opened it, and the ones after it answer
3401    /// updates. Both endpoints have to resolve it the same way and neither has
3402    /// anything else to resolve it with.
3403    fn answers_an_update(&self, id: u64, msg: &ControlMessage) -> bool {
3404        if !matches!(msg, ControlMessage::RequestOk(_) | ControlMessage::RequestError(_)) {
3405            return false;
3406        }
3407        // A request this endpoint made is answered by the peer, so a response
3408        // written here can only be answering an update the peer sent on it.
3409        // Section 9.5 names the one request that works that way: "A subscriber
3410        // can also send REQUEST_UPDATE to modify parameters of a subscription
3411        // established with PUBLISH."
3412        if self.publishes.contains_key(&id) && !self.inbound_requests.contains_key(&id) {
3413            return true;
3414        }
3415        if matches!(msg, ControlMessage::RequestOk(_))
3416            && (self.subscriptions.contains_key(&id) || self.fetches.contains_key(&id))
3417        {
3418            return true;
3419        }
3420        self.answered_peer_requests.contains(&id)
3421    }
3422
3423    /// Record a response as the answer to one or more outstanding updates.
3424    ///
3425    /// A REQUEST_OK answers exactly one: "The receiver MUST still send a
3426    /// REQUEST_UPDATE_OK for each successful update". A REQUEST_ERROR may
3427    /// answer every update still waiting, because Section 9.5.1 permits the
3428    /// receiver to coalesce them — "If the coalesced REQUEST_UPDATE results in
3429    /// an error, only a single REQUEST_UPDATE_ERROR will be sent and the sender
3430    /// of the REQUEST_UPDATEs will not always be able to determine which caused
3431    /// an error." Draft-17 has no such paragraph, and its endpoint answers one
3432    /// update per message in both directions.
3433    ///
3434    /// The credit MAX_REQUEST_UPDATES counts is restored once whatever the
3435    /// answer covered, which is Section 9.1.7 read literally: "Each
3436    /// REQUEST_OK or REQUEST_ERROR response restores one credit on that stream."
3437    /// The peer restores by the same sentence, so an endpoint that gave back one
3438    /// per coalesced update would be crediting a peer that is not.
3439    ///
3440    /// No state machine moves. An update changes a request's parameters and not
3441    /// its lifecycle, so a subscription that was Active before its update was
3442    /// answered is Active after it, whichever answer went out.
3443    ///
3444    /// # Errors
3445    ///
3446    /// [`EndpointError::NoUpdateToAnswer`], and nothing is written or spent.
3447    ///
3448    /// [`EndpointError::PeerPrefixOverlap`] and
3449    /// [`EndpointError::WrongOverlapRefusal`] when the update asked to move a
3450    /// namespace subscription onto a prefix that overlaps another of its kind
3451    /// and the answer is not the REQUEST_ERROR the sentence names. The same
3452    /// two the request itself is refused with, because it is the same rule
3453    /// under the same code; which of the two moments raised it is told apart
3454    /// by which call returned.
3455    fn answer_an_update(&mut self, id: u64, msg: &ControlMessage) -> Result<(), EndpointError> {
3456        let unanswered = self.unanswered_peer_updates.get(&id).copied().unwrap_or(0);
3457        if unanswered == 0 {
3458            return Err(EndpointError::NoUpdateToAnswer(id));
3459        }
3460        // The update's half of the overlap rule, and the reason it needs a
3461        // verdict of its own: an update is answered by the same two messages
3462        // on the same stream as the request, so the moment one of them is
3463        // about to be written is the one place both the answer and the code
3464        // the sentence names are known. Section 9.20.20: "If the new prefix
3465        // would share a common prefix with another active subscription of the
3466        // same type in the same session, the receiver MUST respond with
3467        // REQUEST_ERROR with error code PREFIX_OVERLAP."
3468        if let Some(&established) = self.overlapping_prefix_updates.get(&id) {
3469            let ControlMessage::RequestError(err) = msg else {
3470                return Err(EndpointError::PeerPrefixOverlap { request: id, established });
3471            };
3472            let required = RequestErrorCode::PrefixOverlap as u64;
3473            if err.error_code.into_inner() != required {
3474                return Err(EndpointError::WrongOverlapRefusal { request: id, required });
3475            }
3476        }
3477        let answered = if matches!(msg, ControlMessage::RequestError(_)) { unanswered } else { 1 };
3478        self.unanswered_peer_updates.insert(id, unanswered - answered);
3479        self.restore_update_credit(id);
3480        // The same rule the request's own REQUEST_ERROR spends: a filter this
3481        // endpoint owes an error about has been answered.
3482        if matches!(msg, ControlMessage::RequestError(_)) {
3483            self.peer_filter_rejections.remove(&id);
3484        }
3485        // The prefix moves on the acceptance and not before: "If the update is
3486        // accepted, NAMESPACE and NAMESPACE_DONE messages following the
3487        // REQUEST_OK will contain Track Namespace suffixes relative to the
3488        // updated prefix." A REQUEST_ERROR drops it and the subscription goes
3489        // on selecting what it selected before the update was sent.
3490        //
3491        // Nothing else is recomputed. A subscription this endpoint has already
3492        // refused keeps that verdict when ground it wanted comes free, because
3493        // the sentence that refused it names the moment it arrived and that
3494        // moment has passed.
3495        let accepted = matches!(msg, ControlMessage::RequestOk(_));
3496        if let Some(prefix) = self.updated_namespace_prefixes.remove(&id) {
3497            if accepted {
3498                match self.inbound_requests.get_mut(&id) {
3499                    Some(ControlMessage::SubscribeNamespace(m)) => m.namespace_prefix = prefix,
3500                    Some(ControlMessage::SubscribeTracks(m)) => m.namespace_prefix = prefix,
3501                    // Unreachable: nothing else is ever written above.
3502                    _ => {}
3503                }
3504            }
3505        }
3506        if !accepted {
3507            self.overlapping_prefix_updates.remove(&id);
3508        }
3509        Ok(())
3510    }
3511
3512    /// Drive the state machine for a message this endpoint is about to write
3513    /// on a request stream the peer opened.
3514    ///
3515    /// The mirror of [`receive_response_on_stream`](Self::receive_response_on_stream),
3516    /// and the reason the transitions are named `*_sent` rather than reusing
3517    /// the received-side ones: the state edges coincide, so a mis-dispatch
3518    /// would otherwise succeed silently instead of naming the wrong event in
3519    /// an `InvalidTransition`.
3520    ///
3521    /// Beyond the four responses this also takes the three messages draft-22
3522    /// Table 5 places on a request stream that a responder writes after its
3523    /// response: NAMESPACE and NAMESPACE_DONE on a SUBSCRIBE_NAMESPACE stream
3524    /// (Section 9.16 and Section 9.17) and PUBLISH_SKIPPED on a
3525    /// SUBSCRIBE_TRACKS stream (Section 9.19). All three require the request to
3526    /// have been accepted first, because each machine only leaves Pending on
3527    /// its REQUEST_OK.
3528    ///
3529    /// The caller writes `msg` only after this returns `Ok`. What it cannot
3530    /// undo is the opposite order: a write that fails afterwards leaves the
3531    /// state machine one step ahead of the wire, the same asymmetry the
3532    /// outbound request path already carries.
3533    ///
3534    /// # Errors
3535    ///
3536    /// [`EndpointError::UnknownRequest`] when no request of the answering kind
3537    /// carries that identifier, [`EndpointError::NotAResponse`] when the
3538    /// message is not one, [`EndpointError::PeerPrefixOverlap`] and
3539    /// [`EndpointError::WrongOverlapRefusal`] for a namespace subscription that
3540    /// overlapped one already open or that a REQUEST_UPDATE asked to move onto
3541    /// an overlapping prefix, [`EndpointError::FilterMustBeRejected`] for a request whose Range Filters this draft says to reject, and each flow's own
3542    /// `InvalidTransition` for a request already answered.
3543    pub fn send_response_on_stream(
3544        &mut self,
3545        request_id: VarInt,
3546        msg: &ControlMessage,
3547    ) -> Result<(), EndpointError> {
3548        let id = request_id.into_inner();
3549        // A request whose Range Filters the draft says to reject may be
3550        // answered with a REQUEST_ERROR and with nothing else. Checked across
3551        // every acceptance rather than inside the REQUEST_OK arm, because
3552        // SUBSCRIBE and FETCH — the two requests that most obviously carry these
3553        // filters — are accepted with SUBSCRIBE_OK and FETCH_OK instead.
3554        if matches!(
3555            msg,
3556            ControlMessage::SubscribeOk(_)
3557                | ControlMessage::FetchOk(_)
3558                | ControlMessage::RequestOk(_)
3559        ) {
3560            if let Some(rejection) = self.peer_filter_rejections.get(&id) {
3561                return Err(EndpointError::FilterMustBeRejected(id, rejection.clone()));
3562            }
3563        }
3564        // An answer to an update reaches none of the arms below: the request it
3565        // belongs to has its own lifecycle and an update does not move it.
3566        // A refused update leaves this endpoint owing the peer a termination,
3567        // and the draft names the status that termination must carry. So the
3568        // request's ending is what the debt governs: a PUBLISH_DONE under any
3569        // other status is refused, and everything else the caller may still
3570        // have to write for this request - the answer to a second update it
3571        // has not answered yet - is left alone, because the sentence orders
3572        // nothing.
3573        if let ControlMessage::PublishDone(done) = msg {
3574            self.require_update_failure_status(id, done.status_code)?;
3575        }
3576        if self.answers_an_update(id, msg) {
3577            self.answer_an_update(id, msg)?;
3578            // Refusing an update is half of what the draft asks for, and which
3579            // half it is depends on what was being updated. A subscription is
3580            // owed the PUBLISH_DONE that ends it, recorded here so that the
3581            // next ending written for this request has to be that one. A
3582            // namespace request, a fetch and a track status are owed no
3583            // message at all, and recording one for them would keep a stream
3584            // open that has nothing left to carry.
3585            if matches!(msg, ControlMessage::RequestError(_)) && self.publishes_a_subscription(id) {
3586                self.owed_update_failures.insert(id);
3587            }
3588            return Ok(());
3589        }
3590        // A namespace subscription that overlapped one already open when it
3591        // arrived has one answer available to it, and this is where both the
3592        // answer and its code are known. An update's answer returned above, so
3593        // nothing here judges one.
3594        if let Some(&established) = self.overlapping_namespace_subscriptions.get(&id) {
3595            let ControlMessage::RequestError(err) = msg else {
3596                return Err(EndpointError::PeerPrefixOverlap { request: id, established });
3597            };
3598            let required = RequestErrorCode::PrefixOverlap as u64;
3599            if err.error_code.into_inner() != required {
3600                return Err(EndpointError::WrongOverlapRefusal { request: id, required });
3601            }
3602        }
3603        match msg {
3604            ControlMessage::SubscribeOk(_) => {
3605                let sm =
3606                    self.subscriptions.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
3607                sm.on_subscribe_ok_sent()?;
3608            }
3609            ControlMessage::FetchOk(_) => {
3610                let sm = self.fetches.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
3611                sm.on_fetch_ok_sent()?;
3612            }
3613            ControlMessage::PublishDone(_) => {
3614                let sm =
3615                    self.subscriptions.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
3616                sm.on_publish_done_sent()?;
3617            }
3618            // Section 9.10, on the way out. Nothing moves: the message is
3619            // unilateral, so there is no answer to wait for and no lifecycle
3620            // edge to take. What is checked is the one thing writing it could
3621            // get wrong — a subscription this endpoint does not publish, which
3622            // is a message the peer must close the session over.
3623            ControlMessage::PublishStateNotify(_) => {
3624                if !self.subscriptions.contains_key(&id) || !self.inbound_requests.contains_key(&id)
3625                {
3626                    return Err(EndpointError::StateNotifyThisEndpointMayNotSend(id));
3627                }
3628            }
3629            // REQUEST_OK answers the kinds that have no response message of
3630            // their own, PUBLISH among them since draft-18 folded PUBLISH_OK
3631            // into it. The probe order does not matter: the maps are keyed by
3632            // Request ID and one id belongs to one request.
3633            ControlMessage::RequestOk(m) => {
3634                // Section 9.3 makes receiving Track Properties on anything but
3635                // a TRACK_STATUS response a session close, so putting them on
3636                // one of the others would be handing the peer a reason to close
3637                // this session. Refused before the write rather than after.
3638                if !m.track_properties.is_empty() && !self.track_statuses.contains_key(&id) {
3639                    return Err(EndpointError::TrackPropertiesOnOutgoingRequestOk(id));
3640                }
3641                if let Some(sm) = self.publishes.get_mut(&id) {
3642                    sm.on_publish_ok_sent()?;
3643                } else if let Some(sm) = self.subscribe_namespaces.get_mut(&id) {
3644                    sm.on_subscribe_namespace_ok_sent()?;
3645                } else if let Some(sm) = self.subscribe_tracks.get_mut(&id) {
3646                    sm.on_subscribe_namespace_ok_sent()?;
3647                } else if let Some(sm) = self.publish_namespaces.get_mut(&id) {
3648                    sm.on_publish_namespace_ok_sent()?;
3649                } else if let Some(sm) = self.track_statuses.get_mut(&id) {
3650                    sm.on_track_status_ok_sent()?;
3651                } else {
3652                    return Err(EndpointError::UnknownRequest(id));
3653                }
3654            }
3655            ControlMessage::RequestError(_) => {
3656                if let Some(sm) = self.subscriptions.get_mut(&id) {
3657                    sm.on_subscribe_error_sent()?;
3658                } else if let Some(sm) = self.fetches.get_mut(&id) {
3659                    // Draft-19 held one refusal of a FETCH to one code:
3660                    // INVALID_JOINING_REQUEST_ID, for a Joining Fetch naming no
3661                    // live subscription. Draft-22 Section 9.11 has no
3662                    // joining mechanism and Section 16.11.2 does not assign the
3663                    // code, so every FETCH refusal is the caller's choice.
3664                    sm.on_fetch_error_sent()?;
3665                } else if let Some(sm) = self.publishes.get_mut(&id) {
3666                    sm.on_publish_error_sent()?;
3667                } else if let Some(sm) = self.subscribe_namespaces.get_mut(&id) {
3668                    sm.on_subscribe_namespace_error_sent()?;
3669                } else if let Some(sm) = self.subscribe_tracks.get_mut(&id) {
3670                    sm.on_subscribe_namespace_error_sent()?;
3671                } else if let Some(sm) = self.publish_namespaces.get_mut(&id) {
3672                    sm.on_publish_namespace_error_sent()?;
3673                } else if let Some(sm) = self.track_statuses.get_mut(&id) {
3674                    sm.on_track_status_error_sent()?;
3675                } else {
3676                    return Err(EndpointError::UnknownRequest(id));
3677                }
3678            }
3679            ControlMessage::Namespace(_) => {
3680                let sm = self
3681                    .subscribe_namespaces
3682                    .get_mut(&id)
3683                    .ok_or(EndpointError::UnknownRequest(id))?;
3684                sm.on_namespace_sent()?;
3685            }
3686            ControlMessage::NamespaceDone(_) => {
3687                let sm = self
3688                    .subscribe_namespaces
3689                    .get_mut(&id)
3690                    .ok_or(EndpointError::UnknownRequest(id))?;
3691                sm.on_namespace_done_sent()?;
3692            }
3693            ControlMessage::PublishSkipped(_) => {
3694                let sm =
3695                    self.subscribe_tracks.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
3696                sm.on_publish_skipped_sent()?;
3697            }
3698            other => return Err(EndpointError::NotAResponse(other.message_type())),
3699        }
3700        // After the match, not before it: the caller writes `msg` only once
3701        // this returns `Ok`, so a response that was refused restores nothing.
3702        if matches!(msg, ControlMessage::RequestOk(_) | ControlMessage::RequestError(_)) {
3703            self.restore_update_credit(id);
3704        }
3705        // Reached only by a response that answered the request itself, since an
3706        // update's answer returned above. From here on, a REQUEST_OK or
3707        // REQUEST_ERROR on this stream can only be answering an update.
3708        if matches!(
3709            msg,
3710            ControlMessage::SubscribeOk(_)
3711                | ControlMessage::FetchOk(_)
3712                | ControlMessage::RequestOk(_)
3713                | ControlMessage::RequestError(_)
3714        ) {
3715            self.answered_peer_requests.insert(id);
3716        }
3717        // For the same reason, and it matters more here: a rejection cleared by
3718        // a REQUEST_ERROR that was itself refused would leave the request
3719        // acceptable on the next attempt.
3720        if matches!(msg, ControlMessage::RequestError(_)) {
3721            self.peer_filter_rejections.remove(&id);
3722        }
3723        Ok(())
3724    }
3725
3726    /// Dispatch a message that arrived on a request stream the **peer** opened,
3727    /// after the request that opened it.
3728    ///
3729    /// Nothing that arrives here is a response: this endpoint is the responder
3730    /// on such a stream, so a SUBSCRIBE_OK or REQUEST_ERROR turning up is the
3731    /// peer answering its own request, and it is refused with
3732    /// [`EndpointError::UnexpectedOnPeerRequestStream`].
3733    ///
3734    /// Four messages are expected instead.
3735    ///
3736    /// REQUEST_UPDATE, because draft-22 Section 9.5 puts it on the request's
3737    /// own stream: "The sender of a request (SUBSCRIBE, PUBLISH, FETCH,
3738    /// PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE, SUBSCRIBE_TRACKS) can later send
3739    /// a REQUEST_UPDATE on the same bidi stream as the request to modify it."
3740    /// It goes through the same [`receive_request_update`](Self::receive_request_update)
3741    /// the requester side uses, so the peer's update is held to the same rule
3742    /// its own would be: the message's Request ID must name the stream's
3743    /// request, and a kind the section does not allow to be updated —
3744    /// TRACK_STATUS, by Section 9.13 — closes the session.
3745    ///
3746    /// GOAWAY, because Section 9.2 lets one arrive on a request stream to
3747    /// migrate that request alone, in either direction.
3748    ///
3749    /// PUBLISH_DONE, because a peer that sent PUBLISH is the publisher and ends
3750    /// the publication it opened.
3751    ///
3752    /// PUBLISH_STATE_NOTIFY, new in draft-20 (Section 9.10), for the same
3753    /// reason: a peer that sent PUBLISH is the publisher, and the publisher is
3754    /// the one end that may send it. On a stream the peer opened with a
3755    /// SUBSCRIBE the roles are the other way round, and it is refused.
3756    pub fn receive_on_peer_request_stream(
3757        &mut self,
3758        request_id: VarInt,
3759        msg: ControlMessage,
3760    ) -> Result<(), EndpointError> {
3761        let id = request_id.into_inner();
3762        match msg {
3763            ControlMessage::RequestUpdate(ref m) => self.receive_request_update(request_id, m),
3764            // Section 9.2 puts no direction on it: "A GOAWAY MAY also be
3765            // sent on a request stream to initiate migration of that individual
3766            // request." A request the peer opened is a request stream, so a
3767            // GOAWAY on one migrates it the same way.
3768            ControlMessage::GoAway(ref m) => self.receive_goaway_on_request_stream(request_id, m),
3769            ControlMessage::PublishDone(_) => {
3770                let sm = self.publishes.get_mut(&id).ok_or(EndpointError::UnknownRequest(id))?;
3771                sm.on_publish_done_received()?;
3772                Ok(())
3773            }
3774            // A PUBLISH the peer opened the stream with makes the peer the
3775            // publisher and this endpoint the subscriber, so a notify on it is
3776            // from the end Section 9.10 permits. On a stream the peer opened
3777            // with a SUBSCRIBE the roles are the other way round, and
3778            // `receive_publish_state_notify` refuses it with the code the
3779            // section names.
3780            ControlMessage::PublishStateNotify(ref m) => {
3781                self.receive_publish_state_notify(request_id, m)
3782            }
3783            other => Err(EndpointError::UnexpectedOnPeerRequestStream(other.message_type())),
3784        }
3785    }
3786}
3787
3788// -- Responder-side state machine transitions -----------------------
3789//
3790// Each one is the same edge as an existing requester-side transition — the
3791// graph does not change with the direction — but under a name that says which
3792// way the message went, so a mis-dispatch names the responder event in the
3793// `InvalidTransition` it produces instead of quietly succeeding.
3794//
3795// They are written against the public surface of the machines rather than
3796// against the state field, so the rejected-state error has to be rebuilt to
3797// carry the responder event name.
3798
3799impl SubscriptionStateMachine {
3800    /// Idle -> Subscribing (SUBSCRIBE received from the peer).
3801    pub fn on_subscribe_received(&mut self) -> Result<(), SubscriptionError> {
3802        self.on_subscribe_sent().map_err(|_| SubscriptionError::InvalidTransition {
3803            from: self.state(),
3804            event: "on_subscribe_received".to_string(),
3805        })
3806    }
3807
3808    /// Subscribing -> Active (SUBSCRIBE_OK written on the peer's stream).
3809    pub fn on_subscribe_ok_sent(&mut self) -> Result<(), SubscriptionError> {
3810        self.on_subscribe_ok().map_err(|_| SubscriptionError::InvalidTransition {
3811            from: self.state(),
3812            event: "on_subscribe_ok_sent".to_string(),
3813        })
3814    }
3815
3816    /// Subscribing -> Done (REQUEST_ERROR written on the peer's stream).
3817    pub fn on_subscribe_error_sent(&mut self) -> Result<(), SubscriptionError> {
3818        self.on_subscribe_error().map_err(|_| SubscriptionError::InvalidTransition {
3819            from: self.state(),
3820            event: "on_subscribe_error_sent".to_string(),
3821        })
3822    }
3823
3824    /// Active -> Done (PUBLISH_DONE written on the peer's stream).
3825    pub fn on_publish_done_sent(&mut self) -> Result<(), SubscriptionError> {
3826        self.on_publish_done().map_err(|_| SubscriptionError::InvalidTransition {
3827            from: self.state(),
3828            event: "on_publish_done_sent".to_string(),
3829        })
3830    }
3831}
3832
3833impl FetchStateMachine {
3834    /// Idle -> Pending (FETCH received from the peer).
3835    pub fn on_fetch_received(&mut self) -> Result<(), FetchError> {
3836        self.on_fetch_sent().map_err(|_| FetchError::InvalidTransition {
3837            from: self.state(),
3838            event: "on_fetch_received".to_string(),
3839        })
3840    }
3841
3842    /// Pending -> Receiving, Unanswered -> Done (FETCH_OK written on the
3843    /// peer's stream).
3844    ///
3845    /// The state is named for the requester's view; for a responder the same
3846    /// node means the objects are being served rather than received. It is the
3847    /// same node in the graph, with the same edges, so it keeps its name.
3848    pub fn on_fetch_ok_sent(&mut self) -> Result<(), FetchError> {
3849        self.on_fetch_ok().map_err(|_| FetchError::InvalidTransition {
3850            from: self.state(),
3851            event: "on_fetch_ok_sent".to_string(),
3852        })
3853    }
3854
3855    /// Pending | Unanswered -> Done (REQUEST_ERROR written on the peer's
3856    /// stream).
3857    pub fn on_fetch_error_sent(&mut self) -> Result<(), FetchError> {
3858        self.on_fetch_error().map_err(|_| FetchError::InvalidTransition {
3859            from: self.state(),
3860            event: "on_fetch_error_sent".to_string(),
3861        })
3862    }
3863
3864    /// Receiving -> Done, Pending -> Unanswered (this endpoint finished the
3865    /// fetch data stream).
3866    pub fn on_stream_fin_sent(&mut self) -> Result<(), FetchError> {
3867        self.on_stream_fin().map_err(|_| FetchError::InvalidTransition {
3868            from: self.state(),
3869            event: "on_stream_fin_sent".to_string(),
3870        })
3871    }
3872}
3873
3874impl PublishStateMachine {
3875    /// Idle -> Publishing (PUBLISH received from the peer).
3876    pub fn on_publish_received(&mut self) -> Result<(), PublishFlowError> {
3877        self.on_publish_sent().map_err(|_| PublishFlowError::InvalidTransition {
3878            from: self.state(),
3879            event: "on_publish_received".to_string(),
3880        })
3881    }
3882
3883    /// Publishing -> Active (REQUEST_OK written on the peer's stream).
3884    ///
3885    /// Draft-18 folded PUBLISH_OK into REQUEST_OK and draft-22 keeps it that
3886    /// way, so the message that walks this edge is a REQUEST_OK here where on
3887    /// draft-17 it was a PUBLISH_OK of its own.
3888    pub fn on_publish_ok_sent(&mut self) -> Result<(), PublishFlowError> {
3889        self.on_publish_ok().map_err(|_| PublishFlowError::InvalidTransition {
3890            from: self.state(),
3891            event: "on_publish_ok_sent".to_string(),
3892        })
3893    }
3894
3895    /// Publishing -> Done (REQUEST_ERROR written on the peer's stream).
3896    pub fn on_publish_error_sent(&mut self) -> Result<(), PublishFlowError> {
3897        self.on_publish_error().map_err(|_| PublishFlowError::InvalidTransition {
3898            from: self.state(),
3899            event: "on_publish_error_sent".to_string(),
3900        })
3901    }
3902
3903    /// Active -> Done (PUBLISH_DONE received from the publishing peer).
3904    pub fn on_publish_done_received(&mut self) -> Result<(), PublishFlowError> {
3905        self.on_publish_done_sent().map_err(|_| PublishFlowError::InvalidTransition {
3906            from: self.state(),
3907            event: "on_publish_done_received".to_string(),
3908        })
3909    }
3910}
3911
3912impl PublishNamespaceStateMachine {
3913    /// Idle -> Pending (PUBLISH_NAMESPACE received from the peer).
3914    pub fn on_publish_namespace_received(&mut self) -> Result<(), NamespaceError> {
3915        self.on_publish_namespace_sent().map_err(|_| NamespaceError::InvalidTransition {
3916            from: format!("{:?}", self.state()),
3917            event: "on_publish_namespace_received".to_string(),
3918        })
3919    }
3920
3921    /// Pending -> Active (REQUEST_OK written on the peer's stream).
3922    pub fn on_publish_namespace_ok_sent(&mut self) -> Result<(), NamespaceError> {
3923        self.on_publish_namespace_ok().map_err(|_| NamespaceError::InvalidTransition {
3924            from: format!("{:?}", self.state()),
3925            event: "on_publish_namespace_ok_sent".to_string(),
3926        })
3927    }
3928
3929    /// Pending -> Done (REQUEST_ERROR written on the peer's stream).
3930    pub fn on_publish_namespace_error_sent(&mut self) -> Result<(), NamespaceError> {
3931        self.on_publish_namespace_error().map_err(|_| NamespaceError::InvalidTransition {
3932            from: format!("{:?}", self.state()),
3933            event: "on_publish_namespace_error_sent".to_string(),
3934        })
3935    }
3936}
3937
3938impl SubscribeNamespaceStateMachine {
3939    /// Idle -> Pending (SUBSCRIBE_NAMESPACE or SUBSCRIBE_TRACKS received from
3940    /// the peer).
3941    pub fn on_subscribe_namespace_received(&mut self) -> Result<(), NamespaceError> {
3942        self.on_subscribe_namespace_sent().map_err(|_| NamespaceError::InvalidTransition {
3943            from: format!("{:?}", self.state()),
3944            event: "on_subscribe_namespace_received".to_string(),
3945        })
3946    }
3947
3948    /// Pending -> Active (REQUEST_OK written on the peer's stream).
3949    pub fn on_subscribe_namespace_ok_sent(&mut self) -> Result<(), NamespaceError> {
3950        self.on_subscribe_namespace_ok().map_err(|_| NamespaceError::InvalidTransition {
3951            from: format!("{:?}", self.state()),
3952            event: "on_subscribe_namespace_ok_sent".to_string(),
3953        })
3954    }
3955
3956    /// Pending -> Done (REQUEST_ERROR written on the peer's stream).
3957    pub fn on_subscribe_namespace_error_sent(&mut self) -> Result<(), NamespaceError> {
3958        self.on_subscribe_namespace_error().map_err(|_| NamespaceError::InvalidTransition {
3959            from: format!("{:?}", self.state()),
3960            event: "on_subscribe_namespace_error_sent".to_string(),
3961        })
3962    }
3963
3964    /// Active -> Active (NAMESPACE written on the peer's SUBSCRIBE_NAMESPACE
3965    /// stream).
3966    ///
3967    /// Draft-22 Section 9.16 puts NAMESPACE "on the response stream of a
3968    /// SUBSCRIBE_NAMESPACE request", and Section 4.2 has the publisher send
3969    /// them only once the request has been accepted — "On success, the
3970    /// publisher MUST send a NAMESPACE message for each namespace it knows
3971    /// that matches the Track Namespace Prefix". Requiring Active is what
3972    /// makes one written ahead of the REQUEST_OK an error rather than a frame
3973    /// on the wire.
3974    ///
3975    /// Draft-17 has no such edge: its message table has no Stream column and
3976    /// nothing there moves NAMESPACE off the control stream.
3977    pub fn on_namespace_sent(&mut self) -> Result<(), NamespaceError> {
3978        self.require_active("on_namespace_sent")
3979    }
3980
3981    /// Active -> Active (NAMESPACE_DONE written on the peer's
3982    /// SUBSCRIBE_NAMESPACE stream).
3983    ///
3984    /// Section 9.17: "All NAMESPACE_DONE messages are in response to a
3985    /// SUBSCRIBE_NAMESPACE". The namespace subscription outlives it — Section
3986    /// 4.2 has the publisher go on sending "further NAMESPACE or
3987    /// NAMESPACE_DONE messages as that set changes" — so this ends one
3988    /// namespace, not the request, and the state does not move.
3989    pub fn on_namespace_done_sent(&mut self) -> Result<(), NamespaceError> {
3990        self.require_active("on_namespace_done_sent")
3991    }
3992
3993    /// Active -> Active (PUBLISH_SKIPPED written on the peer's
3994    /// SUBSCRIBE_TRACKS stream).
3995    ///
3996    /// Section 9.19: "All PUBLISH_SKIPPED messages are in response to a
3997    /// SUBSCRIBE_TRACKS". One skipped track says nothing about the rest, so
3998    /// like the two above this is a self-transition on an accepted request.
3999    pub fn on_publish_skipped_sent(&mut self) -> Result<(), NamespaceError> {
4000        self.require_active("on_publish_skipped_sent")
4001    }
4002
4003    /// The shared body of the three self-transitions above: accept the event
4004    /// when the request has been answered with REQUEST_OK, and name the event
4005    /// that was refused otherwise.
4006    fn require_active(&self, event: &str) -> Result<(), NamespaceError> {
4007        if self.state() == SubscribeNamespaceState::Active {
4008            Ok(())
4009        } else {
4010            Err(NamespaceError::InvalidTransition {
4011                from: format!("{:?}", self.state()),
4012                event: event.to_string(),
4013            })
4014        }
4015    }
4016}
4017
4018impl TrackStatusStateMachine {
4019    /// Idle -> Pending (TRACK_STATUS received from the peer).
4020    pub fn on_track_status_received(&mut self) -> Result<(), TrackStatusError> {
4021        self.on_track_status_sent().map_err(|_| TrackStatusError::InvalidTransition {
4022            from: self.state(),
4023            event: "on_track_status_received".to_string(),
4024        })
4025    }
4026
4027    /// Pending -> Done (REQUEST_OK written on the peer's stream).
4028    pub fn on_track_status_ok_sent(&mut self) -> Result<(), TrackStatusError> {
4029        self.on_track_status_ok().map_err(|_| TrackStatusError::InvalidTransition {
4030            from: self.state(),
4031            event: "on_track_status_ok_sent".to_string(),
4032        })
4033    }
4034
4035    /// Pending -> Done (REQUEST_ERROR written on the peer's stream).
4036    pub fn on_track_status_error_sent(&mut self) -> Result<(), TrackStatusError> {
4037        self.on_track_status_error().map_err(|_| TrackStatusError::InvalidTransition {
4038            from: self.state(),
4039            event: "on_track_status_error_sent".to_string(),
4040        })
4041    }
4042}
4043
4044#[cfg(test)]
4045mod responder_tests {
4046    use super::*;
4047    use crate::draft22::namespace::SubscribeNamespaceState;
4048    use crate::draft22::publish::PublishState;
4049    use crate::draft22::subscription::SubscriptionState;
4050    use moqtap_codec::kvp::KvpValue;
4051
4052    fn active_client() -> Endpoint {
4053        let mut ep = Endpoint::new(Role::Client);
4054        ep.connect().unwrap();
4055        let _ = ep.send_setup(vec![]).unwrap();
4056        ep.receive_setup(&Setup { options: vec![] }).unwrap();
4057        ep
4058    }
4059
4060    fn v(n: u64) -> VarInt {
4061        VarInt::from_u64(n).unwrap()
4062    }
4063
4064    fn ns() -> TrackNamespace {
4065        TrackNamespace(vec![b"live".to_vec()])
4066    }
4067
4068    fn peer_subscribe(id: u64) -> ControlMessage {
4069        ControlMessage::Subscribe(Subscribe {
4070            request_id: v(id),
4071            track_namespace: ns(),
4072            track_name: b"video".to_vec(),
4073            parameters: vec![],
4074        })
4075    }
4076
4077    fn peer_publish(id: u64) -> ControlMessage {
4078        ControlMessage::Publish(Publish {
4079            request_id: v(id),
4080            track_namespace: ns(),
4081            track_name: b"video".to_vec(),
4082            track_alias: v(7),
4083            parameters: vec![],
4084            track_properties: vec![],
4085        })
4086    }
4087
4088    fn request_ok() -> ControlMessage {
4089        ControlMessage::RequestOk(RequestOk { parameters: vec![], track_properties: vec![] })
4090    }
4091
4092    fn publish_done() -> ControlMessage {
4093        ControlMessage::PublishDone(PublishDone {
4094            status_code: v(0),
4095            stream_count: v(0),
4096            reason_phrase: Vec::new(),
4097        })
4098    }
4099
4100    /// The peer's requests and this endpoint's share one map per kind, and the
4101    /// opposite Request ID parity is what keeps them apart. Both directions
4102    /// are registered here and both are still there afterwards, which is the
4103    /// consequence a collision would destroy.
4104    #[test]
4105    fn a_peers_request_lives_beside_our_own_in_the_same_map() {
4106        let mut ep = active_client();
4107        let (ours, _) = ep.subscribe(ns(), b"video".to_vec(), vec![]).unwrap();
4108        assert_eq!(ours.into_inner(), 0, "a client allocates even Request IDs");
4109
4110        let theirs = ep.receive_request_on_stream(&peer_subscribe(1)).unwrap();
4111        assert_eq!(theirs.into_inner(), 1);
4112        assert_eq!(
4113            ep.active_subscription_count(),
4114            2,
4115            "the peer's subscription displaced ours in the map",
4116        );
4117        assert_eq!(ep.peer_request_count(), 1);
4118    }
4119
4120    /// Draft-22 Section 6.4.2.1: a Request ID whose least significant bit is
4121    /// wrong for the sender MUST close the session with INVALID_REQUEST_ID.
4122    /// The close is observable twice over — the endpoint stops accepting
4123    /// requests, and the code the connection layer will put on the wire is the
4124    /// one the section names.
4125    #[test]
4126    fn a_peer_id_with_our_own_parity_closes_the_session() {
4127        let mut ep = active_client();
4128        // 2 is even, so it is an id this client allocates, not one the server
4129        // may send.
4130        let err = ep.receive_request_on_stream(&peer_subscribe(2)).unwrap_err();
4131        assert_eq!(err.session_error_code(), Some(SessionErrorCode::InvalidRequestId));
4132        assert_eq!(err.to_string(), "request ID error: request ID 2 has wrong parity for Server");
4133        assert_eq!(ep.session_state(), SessionState::Closed);
4134        assert_eq!(ep.active_subscription_count(), 0, "a refused request was registered anyway");
4135        assert!(matches!(
4136            ep.receive_request_on_stream(&peer_subscribe(1)),
4137            Err(EndpointError::NotActive),
4138        ));
4139    }
4140
4141    /// The other half of the same sentence: a duplicate Request ID is also
4142    /// INVALID_REQUEST_ID. The id is remembered even though the first request
4143    /// is still open, which is why the second is caught.
4144    #[test]
4145    fn a_repeated_peer_request_id_closes_the_session() {
4146        let mut ep = active_client();
4147        ep.receive_request_on_stream(&peer_subscribe(1)).unwrap();
4148        let err = ep.receive_request_on_stream(&peer_publish(1)).unwrap_err();
4149        assert_eq!(err.session_error_code(), Some(SessionErrorCode::InvalidRequestId));
4150        assert_eq!(err.to_string(), "request 1 was already used by the peer");
4151        assert_eq!(ep.session_state(), SessionState::Closed);
4152        assert_eq!(ep.active_publish_count(), 0, "the duplicate was registered anyway");
4153    }
4154
4155    /// Draft-22 Section 6.3 gives a different code for a different rule: a
4156    /// bidirectional stream that begins with the wrong message type is a
4157    /// PROTOCOL_VIOLATION, not an INVALID_REQUEST_ID.
4158    #[test]
4159    fn a_stream_that_opens_no_request_closes_the_session_with_protocol_violation() {
4160        let mut ep = active_client();
4161        let not_a_request =
4162            ControlMessage::GoAway(GoAway { new_session_uri: Vec::new(), timeout: v(0) });
4163        let err = ep.receive_request_on_stream(&not_a_request).unwrap_err();
4164        assert_eq!(err.session_error_code(), Some(SessionErrorCode::ProtocolViolation));
4165        assert_eq!(err.to_string(), "GoAway does not begin a request stream");
4166        assert_eq!(ep.session_state(), SessionState::Closed);
4167    }
4168
4169    /// A peer's SUBSCRIBE runs the same graph our own does, in the other
4170    /// direction: received, then answered, then ended with PUBLISH_DONE by
4171    /// this endpoint rather than by the peer.
4172    #[test]
4173    fn answering_a_peers_subscribe_walks_the_subscription_to_done() {
4174        let mut ep = active_client();
4175        let id = ep.receive_request_on_stream(&peer_subscribe(1)).unwrap();
4176
4177        let ok = ControlMessage::SubscribeOk(SubscribeOk {
4178            track_alias: v(7),
4179            parameters: vec![],
4180            track_properties: vec![],
4181        });
4182        ep.send_response_on_stream(id, &ok).unwrap();
4183        assert_eq!(ep.subscriptions[&1].state(), SubscriptionState::Active);
4184
4185        ep.send_response_on_stream(id, &publish_done()).unwrap();
4186        assert_eq!(ep.subscriptions[&1].state(), SubscriptionState::Done);
4187    }
4188
4189    /// The responder transitions are separate from the requester ones so a
4190    /// mis-dispatch names the responder event rather than succeeding quietly.
4191    #[test]
4192    fn a_responder_transition_out_of_order_names_the_responder_event() {
4193        let mut ep = active_client();
4194        let id = ep.receive_request_on_stream(&peer_subscribe(1)).unwrap();
4195        // PUBLISH_DONE before SUBSCRIBE_OK: the subscription is not Active.
4196        let err = ep.send_response_on_stream(id, &publish_done()).unwrap_err();
4197        assert_eq!(
4198            err.to_string(),
4199            "subscription error: invalid transition from Subscribing on event on_publish_done_sent",
4200        );
4201    }
4202
4203    /// Draft-18 folded PUBLISH_OK into REQUEST_OK and draft-22 keeps the fold,
4204    /// so a peer's PUBLISH is accepted with REQUEST_OK here where draft-17
4205    /// answers with a PUBLISH_OK of its own. The peer is then the publisher,
4206    /// so PUBLISH_DONE comes back from it on the same stream — the one
4207    /// direction the requester path never has to handle, because there we are
4208    /// the publisher.
4209    #[test]
4210    fn a_peers_publish_is_accepted_with_request_ok_and_ended_by_the_peer() {
4211        let mut ep = active_client();
4212        let id = ep.receive_request_on_stream(&peer_publish(1)).unwrap();
4213        ep.send_response_on_stream(id, &request_ok()).unwrap();
4214        assert_eq!(ep.publishes[&1].state(), PublishState::Active);
4215
4216        ep.receive_on_peer_request_stream(id, publish_done()).unwrap();
4217        assert_eq!(ep.publishes[&1].state(), PublishState::Done);
4218        assert!(
4219            ep.receive_on_peer_request_stream(id, publish_done()).is_err(),
4220            "a second PUBLISH_DONE was accepted on a publication already Done",
4221        );
4222    }
4223
4224    /// This endpoint is the responder on a stream the peer opened, so a
4225    /// response arriving there is the peer answering itself. Routing it to the
4226    /// response dispatcher would look up a request we never made; refusing it
4227    /// is what the origin marker buys.
4228    #[test]
4229    fn a_response_on_a_peer_opened_stream_is_refused() {
4230        let mut ep = active_client();
4231        let id = ep.receive_request_on_stream(&peer_subscribe(1)).unwrap();
4232        let ok = ControlMessage::SubscribeOk(SubscribeOk {
4233            track_alias: v(7),
4234            parameters: vec![],
4235            track_properties: vec![],
4236        });
4237        let err = ep.receive_on_peer_request_stream(id, ok).unwrap_err();
4238        assert_eq!(
4239            err.to_string(),
4240            "SubscribeOk may not follow a request on a stream the peer opened",
4241        );
4242    }
4243
4244    /// A peer's REQUEST_UPDATE is held to the same rule the requester side is:
4245    /// draft-22 Section 9.5 puts it on its request's own stream, and the
4246    /// requester's receive path already refuses one whose Request ID names a
4247    /// different request. Routing the peer's updates through that same handler
4248    /// is what keeps the two directions from disagreeing.
4249    #[test]
4250    fn a_peers_request_update_is_held_to_its_own_stream() {
4251        let mut ep = active_client();
4252        let id = ep.receive_request_on_stream(&peer_subscribe(1)).unwrap();
4253        let ok = ControlMessage::SubscribeOk(SubscribeOk {
4254            track_alias: v(7),
4255            parameters: vec![],
4256            track_properties: vec![],
4257        });
4258        ep.send_response_on_stream(id, &ok).unwrap();
4259
4260        let update = |named: u64| {
4261            ControlMessage::RequestUpdate(RequestUpdate {
4262                request_id: v(named),
4263                parameters: vec![],
4264            })
4265        };
4266        ep.receive_on_peer_request_stream(id, update(1)).unwrap();
4267        assert_eq!(ep.subscriptions[&1].state(), SubscriptionState::Active);
4268
4269        // An update naming a different request than its stream is the
4270        // violation the section answers with a close.
4271        let err = ep.receive_on_peer_request_stream(id, update(3)).unwrap_err();
4272        assert!(matches!(err, EndpointError::UnexpectedRequestUpdate(3)), "{err}");
4273        assert_eq!(ep.session_state(), SessionState::Closed);
4274    }
4275
4276    /// SUBSCRIBE_TRACKS (0x51) is the seventh request kind, added in draft-18
4277    /// and absent from draft-17. A responder that carried draft-17's six over
4278    /// would refuse it as a non-request and close the session, so the
4279    /// consequence checked is that it is registered and answerable.
4280    ///
4281    /// Section 12.3 keeps its overlap space independent of
4282    /// SUBSCRIBE_NAMESPACE's, which is why it lands in its own map.
4283    #[test]
4284    fn a_peers_subscribe_tracks_is_a_request_of_its_own() {
4285        let mut ep = active_client();
4286        let tracks = ControlMessage::SubscribeTracks(SubscribeTracks {
4287            request_id: v(1),
4288            namespace_prefix: ns(),
4289            parameters: vec![],
4290        });
4291        let id = ep.receive_request_on_stream(&tracks).unwrap();
4292        assert_eq!(ep.active_subscribe_tracks_count(), 1);
4293        assert_eq!(
4294            ep.active_subscribe_namespace_count(),
4295            0,
4296            "SUBSCRIBE_TRACKS landed among the namespace subscriptions",
4297        );
4298        ep.send_response_on_stream(id, &request_ok()).unwrap();
4299        assert_eq!(ep.subscribe_tracks[&1].state(), SubscribeNamespaceState::Active);
4300
4301        // Section 9.19 puts PUBLISH_SKIPPED on this stream, and only on this
4302        // stream: a SUBSCRIBE_NAMESPACE has no such message.
4303        let skipped = ControlMessage::PublishSkipped(PublishSkipped {
4304            namespace_suffix: ns(),
4305            track_name: b"video".to_vec(),
4306        });
4307        ep.send_response_on_stream(id, &skipped).unwrap();
4308
4309        let mut other = active_client();
4310        let sub_ns = other
4311            .receive_request_on_stream(&ControlMessage::SubscribeNamespace(SubscribeNamespace {
4312                request_id: v(1),
4313                namespace_prefix: ns(),
4314                parameters: vec![],
4315            }))
4316            .unwrap();
4317        other.send_response_on_stream(sub_ns, &request_ok()).unwrap();
4318        assert!(
4319            other.send_response_on_stream(sub_ns, &skipped).is_err(),
4320            "PUBLISH_SKIPPED was accepted on a SUBSCRIBE_NAMESPACE stream",
4321        );
4322    }
4323
4324    /// Draft-22 Table 5 gives NAMESPACE and NAMESPACE_DONE the Stream value
4325    /// "Request", and Section 4.2 has the publisher send them only once the
4326    /// SUBSCRIBE_NAMESPACE has been accepted: "On success, the publisher MUST
4327    /// send a NAMESPACE message for each namespace it knows that matches the
4328    /// Track Namespace Prefix".
4329    ///
4330    /// Draft-17 has neither edge — its message table has no Stream column — so
4331    /// this is the half of the responder a port from draft-17 would leave out.
4332    #[test]
4333    fn namespaces_are_announced_on_the_peers_subscribe_namespace_stream() {
4334        let mut ep = active_client();
4335        let id = ep
4336            .receive_request_on_stream(&ControlMessage::SubscribeNamespace(SubscribeNamespace {
4337                request_id: v(1),
4338                namespace_prefix: ns(),
4339                parameters: vec![],
4340            }))
4341            .unwrap();
4342
4343        let namespace = ControlMessage::Namespace(message::Namespace { namespace_suffix: ns() });
4344        let err = ep.send_response_on_stream(id, &namespace).unwrap_err();
4345        assert_eq!(
4346            err.to_string(),
4347            "namespace error: invalid transition from Pending on event on_namespace_sent",
4348            "a NAMESPACE was allowed ahead of the REQUEST_OK that accepts the request",
4349        );
4350
4351        ep.send_response_on_stream(id, &request_ok()).unwrap();
4352        ep.send_response_on_stream(id, &namespace).unwrap();
4353        ep.send_response_on_stream(
4354            id,
4355            &ControlMessage::NamespaceDone(message::NamespaceDone { namespace_suffix: ns() }),
4356        )
4357        .unwrap();
4358        // One namespace ending does not end the subscription to the prefix.
4359        assert_eq!(ep.subscribe_namespaces[&1].state(), SubscribeNamespaceState::Active);
4360    }
4361
4362    /// Draft-22 Section 9.3 answers Track Properties on a REQUEST_OK that is
4363    /// not a TRACK_STATUS response with a session close. The receive path
4364    /// already refuses them; a responder that writes them would be handing a
4365    /// conforming peer that reason, so the send path refuses them too — and
4366    /// without closing this session, because nothing reached the wire.
4367    #[test]
4368    fn track_properties_are_refused_on_the_way_out_too() {
4369        let properties = vec![KeyValuePair { key: v(0x04), value: KvpValue::Varint(v(1000)) }];
4370        let with_properties = ControlMessage::RequestOk(RequestOk {
4371            parameters: vec![],
4372            track_properties: properties.clone(),
4373        });
4374
4375        let mut ep = active_client();
4376        let id = ep
4377            .receive_request_on_stream(&ControlMessage::SubscribeNamespace(SubscribeNamespace {
4378                request_id: v(1),
4379                namespace_prefix: ns(),
4380                parameters: vec![],
4381            }))
4382            .unwrap();
4383        let err = ep.send_response_on_stream(id, &with_properties).unwrap_err();
4384        assert!(matches!(err, EndpointError::TrackPropertiesOnOutgoingRequestOk(1)), "{err}");
4385        assert_eq!(err.session_error_code(), None, "refusing our own write closed the session");
4386        assert_eq!(
4387            ep.subscribe_namespaces[&1].state(),
4388            SubscribeNamespaceState::Pending,
4389            "the refused response moved the state machine anyway",
4390        );
4391        // The request is still answerable without them.
4392        ep.send_response_on_stream(id, &request_ok()).unwrap();
4393
4394        // TRACK_STATUS_OK is the shape that carries them.
4395        let mut ep = active_client();
4396        let id = ep
4397            .receive_request_on_stream(&ControlMessage::TrackStatus(message::TrackStatus {
4398                request_id: v(1),
4399                track_namespace: ns(),
4400                track_name: b"video".to_vec(),
4401                parameters: vec![],
4402            }))
4403            .unwrap();
4404        ep.send_response_on_stream(id, &with_properties).unwrap();
4405    }
4406}
4407
4408#[cfg(test)]
4409mod tests {
4410    use super::*;
4411    use moqtap_codec::kvp::KvpValue;
4412
4413    fn v(n: u64) -> VarInt {
4414        VarInt::from_u64_moqt(n)
4415    }
4416
4417    fn ns(label: &str) -> TrackNamespace {
4418        TrackNamespace(vec![label.as_bytes().to_vec()])
4419    }
4420
4421    fn peer_subscribe(id: u64, label: &str) -> ControlMessage {
4422        ControlMessage::Subscribe(Subscribe {
4423            request_id: v(id),
4424            track_namespace: ns(label),
4425            track_name: b"video".to_vec(),
4426            parameters: vec![],
4427        })
4428    }
4429
4430    fn peer_fetch(id: u64, label: &str) -> ControlMessage {
4431        ControlMessage::Fetch(Fetch {
4432            request_id: v(id),
4433            track_namespace: ns(label),
4434            track_name: b"video".to_vec(),
4435            parameters: vec![],
4436        })
4437    }
4438
4439    fn peer_subscribe_namespace(id: u64, label: &str) -> ControlMessage {
4440        ControlMessage::SubscribeNamespace(SubscribeNamespace {
4441            request_id: v(id),
4442            namespace_prefix: ns(label),
4443            parameters: vec![],
4444        })
4445    }
4446
4447    fn peer_subscribe_tracks(id: u64, label: &str) -> ControlMessage {
4448        ControlMessage::SubscribeTracks(SubscribeTracks {
4449            request_id: v(id),
4450            namespace_prefix: ns(label),
4451            parameters: vec![],
4452        })
4453    }
4454
4455    fn peer_publish_namespace(id: u64, label: &str) -> ControlMessage {
4456        ControlMessage::PublishNamespace(PublishNamespace {
4457            request_id: v(id),
4458            track_namespace: ns(label),
4459            parameters: vec![],
4460        })
4461    }
4462
4463    fn peer_publish(id: u64, label: &str) -> ControlMessage {
4464        ControlMessage::Publish(Publish {
4465            request_id: v(id),
4466            track_namespace: ns(label),
4467            track_name: b"video".to_vec(),
4468            track_alias: v(id + 100),
4469            parameters: vec![],
4470            track_properties: vec![],
4471        })
4472    }
4473
4474    fn active(role: Role) -> Endpoint {
4475        let mut ep = Endpoint::new(role);
4476        ep.connect().unwrap();
4477        ep.receive_setup(&Setup { options: vec![] }).unwrap();
4478        assert_eq!(ep.session_state(), SessionState::Active);
4479        ep
4480    }
4481
4482    fn update(id: u64) -> RequestUpdate {
4483        RequestUpdate { request_id: v(id), parameters: vec![] }
4484    }
4485
4486    /// A session-fatal error must leave the endpoint unable to carry on: the
4487    /// state machine is Closed and every new request is refused. Asserting the
4488    /// error alone would let a caller ignore it and keep the session running,
4489    /// which is the behaviour draft-22 Section 9.5 forbids.
4490    fn assert_session_failed(ep: &mut Endpoint, err: EndpointError) {
4491        assert_eq!(
4492            err.session_error_code(),
4493            Some(SessionErrorCode::ProtocolViolation),
4494            "{err} should be fatal to the session"
4495        );
4496        assert_eq!(ep.session_state(), SessionState::Closed);
4497        assert!(matches!(
4498            ep.subscribe(ns("a"), b"b".to_vec(), vec![]),
4499            Err(EndpointError::NotActive)
4500        ));
4501    }
4502
4503    /// The other answer, and the one this file gives to a rule draft-22
4504    /// states no consequence for: the message is refused and the session runs
4505    /// on.
4506    ///
4507    /// The three request-stream messages below take this helper rather than
4508    /// [`Self::assert_session_failed`]. Section 6.3's opener sentence does not
4509    /// reach a message arriving on the control stream — it is about what a
4510    /// bidirectional stream may *begin* with — and no sentence in draft-22
4511    /// closes a session over a message being in the wrong place. Table 5's
4512    /// Stream column says where each message is sent and attaches no
4513    /// consequence to a peer that sends one elsewhere.
4514    ///
4515    /// REQUEST_UPDATE is the one exception and takes the helper above: Section
4516    /// 9.5 names two cases and closes over everything else, so a
4517    /// REQUEST_UPDATE on the control stream really is a close the draft asks
4518    /// for.
4519    ///
4520    /// Recovery is not an aspiration here. A control message carries its own
4521    /// length, so the next boundary on the stream is known however this one
4522    /// was refused, and the endpoint that follows really can carry on — which
4523    /// is what the third assertion checks rather than assumes.
4524    fn assert_refused_without_closing(ep: &mut Endpoint, err: &EndpointError) {
4525        assert_eq!(
4526            err.session_error_code(),
4527            None,
4528            "no sentence in draft-22 answers {err} with a close"
4529        );
4530        assert_eq!(ep.session_state(), SessionState::Active);
4531        ep.subscribe(ns("a"), b"b".to_vec(), vec![])
4532            .expect("the session survives a message it could not place");
4533    }
4534
4535    /// Draft-22 Table 5 gives REQUEST_UPDATE the Stream value "Request", and
4536    /// Section 9.5 has it sent "on the same bidi stream as the request".
4537    #[test]
4538    fn a_request_update_belongs_on_its_request_stream_and_not_the_control_stream() {
4539        let mut ep = active(Role::Client);
4540        // A PUBLISH this endpoint made and the peer accepted. Section 9.5
4541        // allows an update on exactly that among the requests made here, so
4542        // this drives the routing question without also being a violation.
4543        let (id, _) = ep.publish(ns("live"), b"video".to_vec(), v(7), vec![], vec![]).unwrap();
4544        ep.receive_request_ok(id, &RequestOk { parameters: vec![], track_properties: vec![] })
4545            .unwrap();
4546
4547        // The request stream is where it belongs, and the request-stream
4548        // dispatcher is the route that has to accept it.
4549        ep.receive_response_on_stream(id, ControlMessage::RequestUpdate(update(id.into_inner())))
4550            .unwrap();
4551
4552        // The control stream is not, and the draft answers that with a close.
4553        let err =
4554            ep.receive_message(ControlMessage::RequestUpdate(update(id.into_inner()))).unwrap_err();
4555        assert!(matches!(err, EndpointError::RequestUpdateOnControlStream), "{err}");
4556        assert_session_failed(&mut ep, err);
4557    }
4558
4559    /// The three messages draft-22 Table 5 places on a request stream that are
4560    /// not responses: NAMESPACE (0x8), NAMESPACE_DONE (0xE) and PUBLISH_SKIPPED
4561    /// (0xF).
4562    ///
4563    /// Table 5's Stream column reads "Request" for all three, the same value it
4564    /// gives REQUEST_UPDATE. Only SETUP is "Control" on its own; GOAWAY is
4565    /// "Control, Request". Inverting the placement accepts them on the control
4566    /// stream and refuses them on the request stream, so a conforming peer
4567    /// sending NAMESPACE on the SUBSCRIBE_NAMESPACE stream that asked for it
4568    /// would have its announcement dropped.
4569    #[test]
4570    fn namespace_and_publish_skipped_belong_on_a_request_stream() {
4571        /// A message name paired with a way to build a fresh one, since each
4572        /// case needs two copies and `ControlMessage` is consumed by both
4573        /// dispatchers.
4574        type Case = (&'static str, fn() -> ControlMessage);
4575
4576        let cases: [Case; 3] = [
4577            ("NAMESPACE", || {
4578                ControlMessage::Namespace(message::Namespace { namespace_suffix: ns("live") })
4579            }),
4580            ("NAMESPACE_DONE", || {
4581                ControlMessage::NamespaceDone(message::NamespaceDone {
4582                    namespace_suffix: ns("live"),
4583                })
4584            }),
4585            ("PUBLISH_SKIPPED", || {
4586                ControlMessage::PublishSkipped(PublishSkipped {
4587                    namespace_suffix: ns("live"),
4588                    track_name: b"video".to_vec(),
4589                })
4590            }),
4591        ];
4592
4593        for (name, build) in cases {
4594            let mut ep = active(Role::Client);
4595            let id = ep.subscribe_namespace(ns("live"), vec![]).unwrap().0;
4596
4597            // Section 4.2 and Section 3.6 make REQUEST_OK or REQUEST_ERROR the
4598            // first message on this stream, so the answer comes before the
4599            // messages that follow it. Sending the NAMESPACE first is a
4600            // different rule's violation and would answer this test's question
4601            // with that rule's error.
4602            ep.receive_response_on_stream(
4603                id,
4604                ControlMessage::RequestOk(RequestOk {
4605                    parameters: vec![],
4606                    track_properties: vec![],
4607                }),
4608            )
4609            .expect("REQUEST_OK answers the namespace subscription");
4610
4611            // Where Table 5 puts it.
4612            ep.receive_response_on_stream(id, build())
4613                .unwrap_or_else(|e| panic!("{name} belongs on a request stream: {e:?}"));
4614
4615            // Where it does not. Refused, and the session left running.
4616            let err = match ep.receive_message(build()) {
4617                Err(e) => e,
4618                Ok(()) => panic!("{name} must be refused on the control stream: Ok(())"),
4619            };
4620            assert!(
4621                matches!(err, EndpointError::RequestMessageOnControlStream(m) if m == name),
4622                "{name} on the control stream gave {err}"
4623            );
4624            assert_refused_without_closing(&mut ep, &err);
4625        }
4626    }
4627
4628    /// Draft-22 Section 9.5: "An endpoint that receives a REQUEST_UPDATE
4629    /// other than in the two cases above MUST close the session with a
4630    /// PROTOCOL_VIOLATION." Section 9.13 names TRACK_STATUS as one such case:
4631    /// "the subscriber cannot send REQUEST_UPDATE."
4632    #[test]
4633    fn a_request_update_naming_a_non_updatable_request_closes_the_session() {
4634        // An id nothing was ever issued under.
4635        let mut ep = active(Role::Client);
4636        let err = ep.receive_request_update(v(40), &update(40)).unwrap_err();
4637        assert!(matches!(err, EndpointError::UnexpectedRequestUpdate(40)), "{err}");
4638        assert_session_failed(&mut ep, err);
4639
4640        // TRACK_STATUS, which the draft rules out by name.
4641        let mut ep = active(Role::Client);
4642        let (id, _) = ep.track_status(ns("live"), b"video".to_vec(), vec![]).unwrap();
4643        let err = ep.receive_request_update(id, &update(id.into_inner())).unwrap_err();
4644        assert!(matches!(err, EndpointError::UnexpectedRequestUpdate(_)), "{err}");
4645        assert_session_failed(&mut ep, err);
4646    }
4647
4648    /// Draft-22 Section 9.5's first case reaches every request kind: "The
4649    /// sender of a request (SUBSCRIBE, PUBLISH, FETCH, PUBLISH_NAMESPACE,
4650    /// SUBSCRIBE_NAMESPACE, SUBSCRIBE_TRACKS) can later send a REQUEST_UPDATE
4651    /// on the same bidi stream as the request to modify it."
4652    #[test]
4653    fn an_update_from_the_requester_resolves_against_every_request_kind() {
4654        let mut ep = active(Role::Client);
4655        let mut ids = Vec::new();
4656        for msg in [
4657            peer_subscribe(1, "a"),
4658            peer_fetch(3, "b"),
4659            peer_subscribe_namespace(5, "c"),
4660            peer_subscribe_tracks(7, "d"),
4661            peer_publish_namespace(9, "e"),
4662            peer_publish(11, "f"),
4663        ] {
4664            ids.push(ep.receive_request_on_stream(&msg).unwrap());
4665        }
4666        for id in ids {
4667            ep.receive_request_update(id, &update(id.into_inner()))
4668                .unwrap_or_else(|e| panic!("request {} should be updatable: {e}", id.into_inner()));
4669        }
4670        assert_eq!(ep.session_state(), SessionState::Active);
4671    }
4672
4673    /// Section 9.5's second case: "A subscriber can also send REQUEST_UPDATE
4674    /// to modify parameters of a subscription established with PUBLISH."
4675    ///
4676    /// The publication is this endpoint's, so this is the one request of its
4677    /// own that it may be sent an update on, and the one place the sender rule
4678    /// and the request-kind rule disagree.
4679    #[test]
4680    fn an_update_on_a_publish_this_endpoint_made_is_the_second_case() {
4681        let mut ep = active(Role::Client);
4682        let (id, _) = ep.publish(ns("live"), b"video".to_vec(), v(7), vec![], vec![]).unwrap();
4683        ep.receive_request_ok(id, &RequestOk { parameters: vec![], track_properties: vec![] })
4684            .unwrap();
4685        ep.receive_request_update(id, &update(id.into_inner())).unwrap();
4686        assert_eq!(ep.session_state(), SessionState::Active);
4687    }
4688
4689    /// The second case needs the subscription to exist before it can be
4690    /// updated, and the first case does not.
4691    ///
4692    /// Section 3.1: "Once either of these sequences is successful, the
4693    /// subscription moves to the Established state and can be updated by the
4694    /// subscriber using REQUEST_UPDATE." A PUBLISH this endpoint has sent and
4695    /// the peer has not answered is Pending, so a subscriber updating it is
4696    /// updating a subscription that does not exist yet.
4697    ///
4698    /// The first case rests on something else and keeps its own timing:
4699    /// Section 9.5 lets the sender of a request update it "later" with
4700    /// nothing said about the answer, and five of the six kinds it names are
4701    /// not subscriptions at all. Both halves are here so that the asymmetry is
4702    /// the thing asserted rather than a side effect of one of them.
4703    #[test]
4704    fn only_an_established_publication_may_be_updated_by_its_subscriber() {
4705        // Case two, before the answer: the subscription is not established.
4706        let mut ep = active(Role::Client);
4707        let (id, _) = ep.publish(ns("live"), b"video".to_vec(), v(7), vec![], vec![]).unwrap();
4708        let err = ep
4709            .receive_request_update(id, &update(id.into_inner()))
4710            .expect_err("a PUBLISH still waiting for its answer is not an established one");
4711        assert!(matches!(err, EndpointError::UnexpectedRequestUpdate(_)), "{err}");
4712        assert_session_failed(&mut ep, err);
4713
4714        // Case one, before the answer: allowed, and on the same beat.
4715        let mut ep = active(Role::Client);
4716        let peer = ep.receive_request_on_stream(&peer_subscribe(1, "live")).unwrap();
4717        ep.receive_request_update(peer, &update(peer.into_inner()))
4718            .expect("the sender of a request may update it before it is answered");
4719        assert_eq!(ep.session_state(), SessionState::Active);
4720    }
4721
4722    /// Section 9.5: "An endpoint that receives a REQUEST_UPDATE other than in
4723    /// the two cases above MUST close the session with a PROTOCOL_VIOLATION."
4724    ///
4725    /// Neither case reaches a SUBSCRIBE, FETCH or namespace request this
4726    /// endpoint made. Those are modified by the endpoint that made them, which
4727    /// is this one, and an update arriving on one came from the side that has
4728    /// no say over it.
4729    #[test]
4730    fn an_update_on_a_request_this_endpoint_made_closes_the_session() {
4731        for which in 0..5 {
4732            let mut ep = active(Role::Client);
4733            let (id, _) = match which {
4734                0 => ep.subscribe(ns("live"), b"video".to_vec(), vec![]).unwrap(),
4735                1 => ep.fetch(ns("live"), b"video".to_vec(), vec![]).unwrap(),
4736                2 => ep.subscribe_namespace(ns("live"), vec![]).unwrap(),
4737                3 => ep.subscribe_tracks(ns("live"), vec![]).unwrap(),
4738                _ => ep.publish_namespace(ns("live"), vec![]).unwrap(),
4739            };
4740            let err = ep
4741                .receive_request_update(id, &update(id.into_inner()))
4742                .expect_err("an update on a request this endpoint made is neither case");
4743            assert!(matches!(err, EndpointError::UnexpectedRequestUpdate(_)), "{which}: {err}");
4744            assert_session_failed(&mut ep, err);
4745        }
4746    }
4747
4748    /// A REQUEST_UPDATE whose own Request ID names a different request than
4749    /// the stream it arrived on was sent on a stream that is not its
4750    /// request's, which is the same violation.
4751    #[test]
4752    fn a_request_update_whose_id_disagrees_with_its_stream_closes_the_session() {
4753        let mut ep = active(Role::Client);
4754        let (a, _) = ep.subscribe(ns("live"), b"video".to_vec(), vec![]).unwrap();
4755        let (b, _) = ep.subscribe(ns("live"), b"audio".to_vec(), vec![]).unwrap();
4756        let err = ep.receive_request_update(a, &update(b.into_inner())).unwrap_err();
4757        assert!(matches!(err, EndpointError::UnexpectedRequestUpdate(_)), "{err}");
4758        assert_session_failed(&mut ep, err);
4759    }
4760
4761    /// Draft-22 Section 9.3: Track Properties "are populated in
4762    /// TRACK_STATUS_OK; they are empty in PUBLISH_OK, REQUEST_UPDATE_OK,
4763    /// SUBSCRIBE_NAMESPACE_OK and PUBLISH_NAMESPACE_OK. If an endpoint
4764    /// receives Track Properties in one of these messages it MUST close the
4765    /// session with a PROTOCOL_VIOLATION."
4766    #[test]
4767    fn track_properties_on_a_request_ok_that_is_not_a_track_status_close_the_session() {
4768        let properties = vec![KeyValuePair { key: v(0x04), value: KvpValue::Varint(v(1000)) }];
4769
4770        let mut ep = active(Role::Client);
4771        let (id, _) = ep.subscribe_namespace(ns("live"), vec![]).unwrap();
4772        let err = ep
4773            .receive_request_ok(
4774                id,
4775                &RequestOk { parameters: vec![], track_properties: properties.clone() },
4776            )
4777            .unwrap_err();
4778        assert!(matches!(err, EndpointError::TrackPropertiesOnNonTrackStatus(_)), "{err}");
4779        assert_session_failed(&mut ep, err);
4780
4781        // TRACK_STATUS_OK is the shape that carries them, and still does.
4782        let mut ep = active(Role::Client);
4783        let (id, _) = ep.track_status(ns("live"), b"video".to_vec(), vec![]).unwrap();
4784        ep.receive_request_ok(id, &RequestOk { parameters: vec![], track_properties: properties })
4785            .unwrap();
4786        assert_eq!(ep.session_state(), SessionState::Active);
4787    }
4788
4789    /// Draft-22 Section 9.2: "A GOAWAY MAY also be sent on a request stream
4790    /// to initiate migration of that individual request." Table 5 gives GOAWAY
4791    /// the Stream value "Control, Request".
4792    ///
4793    /// The session must survive it — only the one request is being moved.
4794    #[test]
4795    fn a_goaway_on_a_request_stream_migrates_that_request_and_not_the_session() {
4796        let mut ep = active(Role::Client);
4797        let (id, _) = ep.subscribe(ns("live"), b"video".to_vec(), vec![]).unwrap();
4798
4799        let goaway =
4800            GoAway { new_session_uri: b"https://elsewhere.example/moq".to_vec(), timeout: v(0) };
4801        ep.receive_response_on_stream(id, ControlMessage::GoAway(goaway)).unwrap();
4802
4803        assert_eq!(ep.session_state(), SessionState::Active);
4804        // Per-request migration leaves the session-wide URI unset; the control
4805        // stream is what sets that.
4806        assert_eq!(ep.goaway_uri(), None);
4807        // And a new request is still allowed.
4808        ep.subscribe(ns("live"), b"audio".to_vec(), vec![]).unwrap();
4809    }
4810
4811    /// Draft-22 Section 9.2: "If a server receives a GOAWAY with a non-zero
4812    /// New Session URI Length it MUST close the session with a
4813    /// PROTOCOL_VIOLATION."
4814    #[test]
4815    fn a_server_refuses_a_goaway_carrying_a_new_session_uri() {
4816        let mut ep = active(Role::Server);
4817        let goaway =
4818            GoAway { new_session_uri: b"https://elsewhere.example/moq".to_vec(), timeout: v(0) };
4819        let err = ep.receive_goaway(&goaway).unwrap_err();
4820        assert!(matches!(err, EndpointError::GoAwayUriAtServer), "{err}");
4821        assert_eq!(ep.goaway_uri(), None);
4822        assert_session_failed(&mut ep, err);
4823
4824        // An empty URI is the form a server may legitimately receive: it says
4825        // the peer is going away, not where to go.
4826        let mut ep = active(Role::Server);
4827        ep.receive_goaway(&GoAway { new_session_uri: vec![], timeout: v(0) }).unwrap();
4828        assert_eq!(ep.session_state(), SessionState::Draining);
4829
4830        // A client is the side that may be redirected.
4831        let mut ep = active(Role::Client);
4832        ep.receive_goaway(&goaway).unwrap();
4833        assert_eq!(ep.goaway_uri(), Some(&b"https://elsewhere.example/moq"[..]));
4834    }
4835
4836    /// Draft-22 Section 9.11 gives FETCH no `Fetch Type` field and no location
4837    /// fields, so a FETCH names a track the way a SUBSCRIBE does and the range
4838    /// travels in a parameter: Location Filter Type 0x04, then its four fields.
4839    #[test]
4840    fn a_fetch_carries_its_range_in_an_inclusive_location_filter() {
4841        let mut ep = active(Role::Client);
4842        let range = LocationFilter::range_to(10, 0, 0, 5).unwrap();
4843        let (_, msg) = ep.fetch_range(ns("live"), b"video".to_vec(), &range, vec![]).unwrap();
4844
4845        let ControlMessage::Fetch(fetch) = msg else { panic!("expected a FETCH") };
4846        assert_eq!(fetch.track_namespace, ns("live"));
4847        assert_eq!(fetch.track_name, b"video".to_vec());
4848        let filter = fetch
4849            .parameters
4850            .iter()
4851            .find(|p| p.key.into_inner() == 0x21)
4852            .expect("the range is a LOCATION_FILTER parameter");
4853        let KvpValue::Bytes(bytes) = &filter.value else { panic!("a Location Filter value") };
4854        // Type 0x04, Absolute Range. The last object is 5, not 6: Section 3.2
4855        // and Section 3.3.1 make the range inclusive.
4856        assert_eq!(bytes, &vec![0x04, 10, 0, 0, 5]);
4857    }
4858
4859    /// Draft-22 Section 3.4, and what replaced the joining fetch: a
4860    /// subscription that carries FILL_PARAMETERS is answered with a
4861    /// unidirectional stream whose FETCH_HEADER carries **the subscription's**
4862    /// Request ID.
4863    #[test]
4864    fn a_subscription_that_asks_for_a_fill_can_account_for_its_stream() {
4865        let mut ep = active(Role::Client);
4866        let fill_parameter = fill::FillParameters::inherited().parameter().unwrap();
4867        let (id, _) = ep.subscribe(ns("live"), b"video".to_vec(), vec![fill_parameter]).unwrap();
4868
4869        assert!(ep.fill_requested(id));
4870        ep.on_fill_stream_opened(id).unwrap();
4871        ep.on_fill_stream_opened(id).unwrap();
4872        // Section 3.4: "a subscription can have multiple fill fetch streams
4873        // open at once ... opening a new fill fetch stream does not implicitly
4874        // cancel any previously opened fill fetch streams."
4875        assert_eq!(ep.open_fill_streams(id), 2);
4876
4877        ep.on_fill_stream_ended(id);
4878        assert_eq!(ep.open_fill_streams(id), 1);
4879        // Section 9.9 counts every stream the publisher opened, ended or not.
4880        assert_eq!(ep.fill_streams_opened(id), 2);
4881
4882        // Section 3.4.1: neither ending touches the subscription.
4883        ep.on_fill_stream_ended(id);
4884        assert_eq!(ep.active_subscription_count(), 1);
4885    }
4886
4887    /// A subscription that asked for no fill has no fill fetch stream to
4888    /// attribute, and Section 9.20.15 is plain that the parameter is the whole
4889    /// of the request: "Its presence is what requests a fill fetch stream; a
4890    /// subscription with no FILL_PARAMETERS opens none."
4891    #[test]
4892    fn a_stream_for_a_subscription_that_asked_for_no_fill_is_refused() {
4893        let mut ep = active(Role::Client);
4894        let (id, _) = ep.subscribe(ns("live"), b"video".to_vec(), vec![]).unwrap();
4895        assert!(!ep.fill_requested(id));
4896        let err = ep.on_fill_stream_opened(id).unwrap_err();
4897        assert!(matches!(err, EndpointError::UnrequestedFillStream(_)), "{err}");
4898        // Not a session close: draft-22 names no code for it, and Section
4899        // 3.4.1 makes cancelling one stream something that "does not affect
4900        // the subscription".
4901        assert_eq!(err.session_error_code(), None);
4902        assert_eq!(ep.session_state(), SessionState::Active);
4903    }
4904
4905    /// Draft-22 Section 9.10: PUBLISH_STATE_NOTIFY "applies only to
4906    /// subscriptions, and is sent only by the publisher. An endpoint that
4907    /// receives a PUBLISH_STATE_NOTIFY for any other request type, or from the
4908    /// subscriber, MUST close the session with a PROTOCOL_VIOLATION."
4909    ///
4910    /// It is also unilateral, so it must not spend an update credit: routing it
4911    /// through `receive_request_update` instead would leave this endpoint owing
4912    /// an answer the section forbids it to send.
4913    #[test]
4914    fn a_state_notify_is_taken_on_a_subscription_and_refused_anywhere_else() {
4915        let notify = PublishStateNotify { parameters: vec![] };
4916
4917        // On this endpoint's own SUBSCRIBE the peer is the publisher.
4918        let mut ep = active(Role::Client);
4919        let (id, _) = ep.subscribe(ns("live"), b"video".to_vec(), vec![]).unwrap();
4920        ep.receive_publish_state_notify(id, &notify).unwrap();
4921        assert_eq!(ep.session_state(), SessionState::Active);
4922        // Nothing is owed in reply.
4923        assert!(!ep.has_unanswered_update(id));
4924
4925        // On a FETCH there is no subscription for it to be about.
4926        let mut ep = active(Role::Client);
4927        let (id, _) = ep.fetch(ns("live"), b"video".to_vec(), vec![]).unwrap();
4928        let err = ep.receive_publish_state_notify(id, &notify).unwrap_err();
4929        assert!(matches!(err, EndpointError::StateNotifyForNonSubscription(_)), "{err}");
4930        assert_eq!(err.session_error_code(), Some(SessionErrorCode::ProtocolViolation));
4931        assert_eq!(ep.session_state(), SessionState::Closed);
4932
4933        // On a SUBSCRIBE the peer sent, this endpoint is the publisher, so a
4934        // notify arriving on it came from the subscriber.
4935        let mut ep = active(Role::Server);
4936        let id = ep.receive_request_on_stream(&peer_subscribe(0, "live")).unwrap();
4937        let err = ep.receive_publish_state_notify(id, &notify).unwrap_err();
4938        assert!(matches!(err, EndpointError::StateNotifyFromSubscriber(_)), "{err}");
4939        assert_eq!(ep.session_state(), SessionState::Closed);
4940    }
4941}