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