Skip to main content

moq_net/model/
origin.rs

1use crate::{broadcast, cache, group, stats, track};
2use kio::Pollable;
3use std::{
4	cmp::Reverse,
5	collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
6	fmt,
7	sync::Arc,
8	sync::atomic::{AtomicU64, Ordering},
9	task::{Poll, ready},
10	time::Duration,
11};
12
13use rand::RngExt;
14
15use super::{
16	Requests, WeakCache, WeakEntry,
17	front::{Action, Candidate, Event, Front, Pin, Refusal},
18};
19use crate::{
20	AsPath, Error, InvalidPattern, Path, PathOwned, Pattern, Patterns,
21	coding::{BoundsExceeded, Decode, DecodeError, Encode, EncodeError},
22	runtime::{Instant, Timers},
23	time::Clock,
24	util::{Keepalive, TaskSet, Tasks, TasksWeak},
25};
26
27/// One relay's identity in a broadcast's hop chain: a 62-bit varint on the wire.
28///
29/// Names a *hop*, not an [`origin::Producer`](Producer): a relay's routing table is the
30/// origin, and this is the id it stamps into a route's hop chain as an announcement
31/// passes through, so a receiver can spot its own id and reject a loop.
32///
33/// Local hops are built with [`Hop::new`] or [`Hop::random`], both of which guarantee a
34/// non-zero id so loop detection can work. Remote peers may still send `0`; it is legal
35/// on the wire, names nobody, and marks the chain anonymous for route selection.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub struct Hop {
38	/// 62-bit identifier. Encoded as a QUIC varint on the wire.
39	id: u64,
40}
41
42impl Hop {
43	/// The reserved id 0: no identity.
44	///
45	/// It stands in for an endpoint that never declared one, and for Lite03 hop-count
46	/// placeholders. Any number of endpoints can be 0, so it identifies nothing: it is
47	/// never a loop, never a publisher two chains have in common, and a chain that
48	/// holds one anywhere is anonymous for route selection.
49	pub const UNKNOWN: Self = Self { id: 0 };
50
51	/// Build a hop from a stable id.
52	///
53	/// The id must be non-zero and fit in the 62-bit QUIC varint range. Wire
54	/// decode accepts remote id 0 ([`Self::UNKNOWN`]), but a local hop should
55	/// not use it because it cannot be excluded for loop detection.
56	pub fn new(id: u64) -> Result<Self, InvalidHop> {
57		if id == 0 || id >= 1u64 << 62 {
58			return Err(InvalidHop::Range);
59		}
60		Ok(Self { id })
61	}
62
63	/// Generate a fresh hop with a random non-zero id. Use this for any relay that
64	/// does not need a stable identity across restarts.
65	///
66	/// Older `@moq/lite` clients decode the exclude hop as a JavaScript number
67	/// and reject values above 2^53-1. Keep generated IDs in that range while
68	/// [`Self::new`] accepts the full 62-bit wire range for explicit IDs.
69	pub fn random() -> Self {
70		let mut rng = rand::rng();
71		let id = rng.random_range(1..(1u64 << 53));
72		Self { id }
73	}
74
75	/// Return the origin's wire id.
76	pub fn id(self) -> u64 {
77		self.id
78	}
79
80	/// Build a hop from an id read off the wire, where 0 is legal.
81	pub(crate) fn from_wire(id: u64) -> Result<Self, DecodeError> {
82		if id >= 1u64 << 62 {
83			return Err(DecodeError::InvalidValue);
84		}
85		Ok(Self { id })
86	}
87}
88
89/// An origin's identity plus the cache pool its broadcasts inherit.
90///
91/// Construction config for an [origin `Producer`](Producer). The origin passes its
92/// [`cache::Pool`] to every broadcast it creates, so every track and group beneath it
93/// shares one budget. Defaults to no byte target and the cache's standard idle expiry.
94#[derive(Clone, Debug)]
95#[non_exhaustive]
96pub struct Config {
97	/// The origin's wire identity, appended to broadcast hop chains for loop
98	/// detection and shortest-path routing.
99	pub hop: Hop,
100
101	/// The cache pool broadcasts under this origin charge their groups into. It flows
102	/// down the ownership chain (origin -> broadcast -> track -> group): a track opens
103	/// an account against it, and its groups charge through that. It has no byte target
104	/// and uses [`cache::DEFAULT_EXPIRY`] by default; a relay sets a shared configured
105	/// pool (assign [`Self::pool`]) so cached groups across the whole process share
106	/// one policy.
107	pub pool: cache::Pool,
108
109	/// Ceiling on each track's media-timestamp retention window under this origin.
110	/// Each track's own [`max_age`](track::Info::max_age) is clamped down to this
111	/// when the track binds, so a subscriber is never promised more history than the
112	/// origin allows, regardless of what a publisher advertises. Wall-clock
113	/// reclamation of idle content is separate: [`Self::pool`]'s
114	/// [`expiry`](cache::Pool::expiry) window. [`Duration::MAX`] (the default)
115	/// imposes no ceiling, leaving each track's own window in force.
116	pub cache_duration: Duration,
117
118	/// The retention window given to a track whose publisher advertises none.
119	///
120	/// moq-lite 05+ carries [`max_age`](track::Info::max_age) in TRACK_INFO, so a
121	/// track relayed over it keeps the window its publisher chose. Every moq-transport
122	/// draft and moq-lite 01-04 have no such wire property, so a track arriving over one
123	/// of them lands here instead. Raise it on a relay fronting a segmented egress
124	/// (HLS/DASH), which needs a playlist window's worth of history rather than the live
125	/// edge. Defaults to [`track::DEFAULT_MAX_AGE`], and [`Self::cache_duration`]
126	/// still caps it.
127	pub default_max_age: Duration,
128}
129
130impl Default for Config {
131	/// A fresh random hop with no byte target and the default idle expiry.
132	fn default() -> Self {
133		let pool = cache::Pool::new(cache::Config::default().with_expiry(cache::DEFAULT_EXPIRY));
134		Self {
135			hop: Hop::random(),
136			pool,
137			cache_duration: Duration::MAX,
138			default_max_age: track::DEFAULT_MAX_AGE,
139		}
140	}
141}
142
143impl Config {
144	/// Config for the given origin id with no byte target and the default idle expiry.
145	pub fn new(hop: Hop) -> Self {
146		Self { hop, ..Self::default() }
147	}
148}
149
150impl From<Hop> for Config {
151	/// Config for the given origin id with the defaults of [`Config::new`].
152	fn from(hop: Hop) -> Self {
153		Self::new(hop)
154	}
155}
156
157impl TryFrom<u64> for Hop {
158	type Error = InvalidHop;
159
160	fn try_from(id: u64) -> Result<Self, Self::Error> {
161		Self::new(id)
162	}
163}
164
165impl fmt::Display for Hop {
166	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167		self.id.fmt(f)
168	}
169}
170
171impl<V: Copy> Encode<V> for Hop
172where
173	u64: Encode<V>,
174{
175	fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
176		self.id.encode(w, version)
177	}
178}
179
180impl<V: Copy> Decode<V> for Hop
181where
182	u64: Decode<V>,
183{
184	fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
185		Self::from_wire(u64::decode(r, version)?)
186	}
187}
188
189/// Maximum number of origins (hops) an [`Hops`] can hold.
190///
191/// Caps pathological or loop-induced announcements at a reasonable cluster
192/// diameter; appending past this limit returns [`InvalidHop::TooMany`] rather than
193/// silently truncating.
194pub(crate) const MAX_HOPS: usize = 32;
195
196/// Bounded, loop-free list of [`Hop`] entries: the hop chain of a broadcast.
197///
198/// Guarantees `len() <= MAX_HOPS` and that no non-zero [`Hop`] appears twice. Both
199/// are wire rules, and both hold wherever a list exists rather than only where one was
200/// parsed, so a chain that a conforming receiver would reject cannot be built and sent.
201/// Construct via [`Hops::new`] + [`Hops::push`], or fall back to the
202/// fallible [`TryFrom<Vec<Hop>>`].
203#[derive(Debug, Clone, Default, PartialEq, Eq)]
204pub struct Hops(Vec<Hop>);
205
206/// Why a [`Hop`] is not usable, on its own or as part of a [`Hops`] chain.
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208#[non_exhaustive]
209pub enum InvalidHop {
210	/// The id is zero or outside the 62-bit wire range, so it cannot identify a local
211	/// hop. Only [`Hop::new`] returns this; a chain never holds one.
212	Range,
213
214	/// The list is already at its hop-count cap, which a real path never reaches and a
215	/// loop does.
216	TooMany,
217
218	/// The id is already in the list. A chain that revisits a hop looped, which every
219	/// receiver of it must reject, so it must not be built in the first place. The
220	/// reserved id 0 identifies nothing and may repeat.
221	Duplicate,
222}
223
224impl fmt::Display for InvalidHop {
225	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226		match self {
227			Self::Range => write!(f, "local hop id must be non-zero and below 2^62"),
228			Self::TooMany => write!(f, "too many hops (max {MAX_HOPS})"),
229			Self::Duplicate => write!(f, "hop already in the chain"),
230		}
231	}
232}
233
234impl std::error::Error for InvalidHop {}
235
236impl From<InvalidHop> for DecodeError {
237	fn from(err: InvalidHop) -> Self {
238		match err {
239			InvalidHop::TooMany => DecodeError::BoundsExceeded,
240			InvalidHop::Range | InvalidHop::Duplicate => DecodeError::InvalidValue,
241		}
242	}
243}
244
245impl Hops {
246	/// Create an empty list.
247	pub fn new() -> Self {
248		Self(Vec::new())
249	}
250
251	/// Append an [`Hop`], rejecting anything a conforming receiver would.
252	///
253	/// Fails with [`InvalidHop::TooMany`] once the list is full, and with
254	/// [`InvalidHop::Duplicate`] for an id already in the chain, which is a loop. The
255	/// reserved id 0 identifies nothing, so it may repeat.
256	pub fn push(&mut self, hop: Hop) -> Result<(), InvalidHop> {
257		if self.0.len() >= MAX_HOPS {
258			return Err(InvalidHop::TooMany);
259		}
260		if hop != Hop::UNKNOWN && self.0.contains(&hop) {
261			return Err(InvalidHop::Duplicate);
262		}
263		self.0.push(hop);
264		Ok(())
265	}
266
267	/// Returns true if any entry matches `hop`.
268	pub fn contains(&self, hop: &Hop) -> bool {
269		self.0.contains(hop)
270	}
271
272	/// Number of entries currently in the list (always `<= MAX_HOPS`).
273	pub fn len(&self) -> usize {
274		self.0.len()
275	}
276
277	/// Whether the list contains no entries.
278	pub fn is_empty(&self) -> bool {
279		self.0.is_empty()
280	}
281
282	/// Iterate over the entries in hop order (oldest first).
283	pub fn iter(&self) -> std::slice::Iter<'_, Hop> {
284		self.0.iter()
285	}
286
287	/// Borrow the entries as a slice.
288	pub fn as_slice(&self) -> &[Hop] {
289		&self.0
290	}
291}
292
293impl TryFrom<Vec<Hop>> for Hops {
294	type Error = InvalidHop;
295
296	fn try_from(v: Vec<Hop>) -> Result<Self, Self::Error> {
297		if v.len() > MAX_HOPS {
298			return Err(InvalidHop::TooMany);
299		}
300		// MAX_HOPS is 32, so the quadratic scan is cheaper than allocating a set.
301		for (i, hop) in v.iter().enumerate() {
302			if *hop != Hop::UNKNOWN && v[i + 1..].contains(hop) {
303				return Err(InvalidHop::Duplicate);
304			}
305		}
306		Ok(Self(v))
307	}
308}
309
310impl<'a> IntoIterator for &'a Hops {
311	type Item = &'a Hop;
312	type IntoIter = std::slice::Iter<'a, Hop>;
313
314	fn into_iter(self) -> Self::IntoIter {
315		self.iter()
316	}
317}
318
319impl<V: Copy> Encode<V> for Hops
320where
321	u64: Encode<V>,
322	Hop: Encode<V>,
323{
324	fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
325		(self.0.len() as u64).encode(w, version)?;
326		for origin in &self.0 {
327			origin.encode(w, version)?;
328		}
329		Ok(())
330	}
331}
332
333impl<V: Copy> Decode<V> for Hops
334where
335	u64: Decode<V>,
336	Hop: Decode<V>,
337{
338	fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
339		let count = u64::decode(r, version)? as usize;
340		if count > MAX_HOPS {
341			return Err(DecodeError::BoundsExceeded);
342		}
343		// Through `push`, so a chain that revisits a hop is rejected here rather than
344		// entering the model and being forwarded on to a receiver that must close on it.
345		let mut list = Self(Vec::with_capacity(count));
346		for _ in 0..count {
347			list.push(Hop::decode(r, version)?)?;
348		}
349		Ok(list)
350	}
351}
352
353/// The highest value either half of a [`Cost`] can take, and where cost
354/// accumulation saturates.
355///
356/// The ceiling is the wire's, not the model's: lite-06 carries each cost as a QUIC
357/// varint, which tops out at 2^62-1, so a larger value could be selected on but
358/// never forwarded.
359const MAX_COST: u64 = (1 << 62) - 1;
360
361/// What pulling content via a route costs, in two magnitudes that accumulate
362/// together and are compared in that order: lower [`warm`](Self::warm) wins, and
363/// [`cold`](Self::cold) breaks the tie.
364///
365/// Both are the same path priced against different cache states. `warm` is what one
366/// more subscription would cost the mesh right now; `cold` prices the identical
367/// path as if nothing were cached, so it stays meaningful once discounts have
368/// flattened `warm`.
369#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
370pub struct Cost {
371	/// The cost of pulling content via this route as the mesh stands today,
372	/// accumulated per link. Lower wins.
373	///
374	/// The original publisher seeds it with its production cost (zero for a live
375	/// publish, something large for a standby that would have to start working, like
376	/// a cold transcoder), and each link adds its own configured price as the
377	/// announcement crosses it, so a route over a metered backbone ranks worse than
378	/// an equal-length one within a datacenter.
379	pub warm: u64,
380
381	/// The same path with every warm discount removed: what pulling the content
382	/// would cost if no relay along it were carrying anything.
383	///
384	/// Accumulates exactly like [`warm`](Self::warm) but never restarts. [`MAX`](Self::MAX)
385	/// when the peer's wire cannot express it (pre-lite-06, or the MoQ Cluster
386	/// extension), which ranks last rather than pretending the path is free.
387	pub cold: u64,
388}
389
390impl Cost {
391	/// Both magnitudes at `cost`: an undiscounted route, which is what a publisher
392	/// seeding its production cost means.
393	pub const fn new(cost: u64) -> Self {
394		Self { warm: cost, cold: cost }
395	}
396
397	/// The highest cost either half can take, and where accumulation saturates.
398	///
399	/// A draining session stamps this on its routes so every other candidate outranks
400	/// them while they stay selectable as the last path to the content. Draining is
401	/// not a distinct state: cost is the whole mechanism, and a route whose accumulated
402	/// cost saturates the wire ceiling ranks (and is treated) the same way.
403	pub const MAX: Self = Self::new(MAX_COST);
404
405	/// A draining route: [`MAX`](Self::MAX) in both magnitudes, so every other
406	/// candidate outranks it.
407	pub const DRAIN: Self = Self::MAX;
408
409	/// What a peer advertises when its wire has no room for a cost at all: free to
410	/// reach (leaving hop count as the effective metric, exactly as before route
411	/// cost existed) with an unknown cold path.
412	pub(crate) const UNKNOWN: Self = Self {
413		warm: 0,
414		cold: MAX_COST,
415	};
416
417	/// Add a link's price to both magnitudes, saturating at the largest cost the
418	/// wire can carry so a huge cost sorts last instead of wrapping around to best.
419	pub(crate) fn charged(self, link_cost: u64) -> Self {
420		Self {
421			warm: self.warm.saturating_add(link_cost).min(MAX_COST),
422			cold: self.cold.saturating_add(link_cost).min(MAX_COST),
423		}
424	}
425
426	/// Clamp both magnitudes to what a varint can carry, since a locally created
427	/// route can name an arbitrary `u64`.
428	pub(crate) fn clamped(self) -> Self {
429		Self {
430			warm: self.warm.min(MAX_COST),
431			cold: self.cold.min(MAX_COST),
432		}
433	}
434}
435
436impl From<u64> for Cost {
437	fn from(cost: u64) -> Self {
438		Self::new(cost)
439	}
440}
441
442/// The path a route took through the mesh and what using it costs.
443///
444/// The metadata half of an advertisement: [`Producer::dynamic`] pairs it with
445/// the prefix it covers, [`broadcast::Producer::announce`] with the
446/// broadcast's exact path, and [`Consumer::announced`] yields both. A route
447/// claims capability, not inventory: it says paths under its prefix are
448/// servable, never that any specific broadcast exists. The common convention is
449/// that a publisher announces each broadcast's exact path, so subscribers can
450/// enumerate broadcasts; a service instead announces one short prefix and
451/// answers whatever is requested beneath it.
452#[derive(Clone, Debug, PartialEq, Eq)]
453#[non_exhaustive]
454pub struct Route {
455	/// The chain of origins the route has traversed, oldest first. Each relay
456	/// appends its own [`crate::Hop`] when forwarding; used for loop detection
457	/// and as the selection tie-break. A 0 entry is the anonymous mark and
458	/// travels unchanged; see [`Self::is_anonymous`].
459	pub hops: Hops,
460
461	/// What pulling content via this route costs, accumulated per link: lower wins
462	/// among routes of the same anonymity, with ties broken by a broadcast published
463	/// on this origin, then hop length, then a deterministic hash, and finally the
464	/// most recently announced route. See [`Cost`].
465	pub cost: Cost,
466
467	/// The announcing session's declared or assigned identity.
468	///
469	/// Local selection state: split-horizon matches this as well as the chain, so a
470	/// route is never advertised back to the session it came from even when that
471	/// session withheld an identity (hop 0). Never forwarded.
472	pub(crate) via: Hop,
473
474	/// Where the route entered this origin; see [`Self::source`]. Never forwarded.
475	pub(crate) source: Source,
476}
477
478impl Default for Route {
479	fn default() -> Self {
480		Self {
481			hops: Hops::new(),
482			cost: Cost::default(),
483			via: Hop::UNKNOWN,
484			source: Source::Local,
485		}
486	}
487}
488
489/// Where a route entered an origin: here, or from a cluster peer.
490///
491/// Origin bookkeeping, not a chain fact: the hop chain cannot say it, since a
492/// client and a peer relay each append one hop. The origin records it from the
493/// handle that announced the route; see [`Producer::peer`].
494#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
495pub enum Source {
496	/// Announced on this origin: by an in-process producer or a client session.
497	#[default]
498	Local,
499	/// Learned from a cluster peer, named by the announcing session's declared or
500	/// assigned identity.
501	Peer(Hop),
502}
503
504impl Route {
505	/// Replace the hop chain.
506	pub fn with_hops(mut self, hops: Hops) -> Self {
507		self.hops = hops;
508		self
509	}
510
511	/// Set the cost: lower wins among routes covering the same prefix and anonymity.
512	///
513	/// A bare `u64` prices the route undiscounted (both halves of [`Cost`] alike),
514	/// which is what a publisher seeding its production cost means.
515	pub fn with_cost(mut self, cost: impl Into<Cost>) -> Self {
516		self.cost = cost.into();
517		self
518	}
519
520	/// The announcing session's declared or assigned identity, for split-horizon.
521	///
522	/// Not part of the advertised route: an assigned identity is private selection
523	/// state and must not be forwarded.
524	pub(crate) fn with_via(mut self, via: Hop) -> Self {
525		self.via = via;
526		self
527	}
528
529	/// Whether this route passed through an anonymous hop.
530	///
531	/// True when the chain holds a 0 anywhere, including Lite03 hop-count
532	/// placeholders. An anonymous route ranks below every fully identified one,
533	/// whatever the costs say. An empty chain is a local announcement, not the
534	/// anonymous mark; ingress fills a received empty list with 0 before it
535	/// enters the table.
536	pub fn is_anonymous(&self) -> bool {
537		self.hops.iter().any(|hop| *hop == Hop::UNKNOWN)
538	}
539
540	/// Where the route entered this origin, as delivered by [`Consumer::announced`].
541	///
542	/// Set by the origin, not the announcer: a route handed to
543	/// [`Producer::dynamic`] reports [`Source::Local`] until the origin delivers it.
544	pub fn source(&self) -> Source {
545		self.source
546	}
547}
548
549static NEXT_CONSUMER_ID: AtomicU64 = AtomicU64::new(0);
550
551#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
552struct ConsumerId(u64);
553
554impl ConsumerId {
555	fn new() -> Self {
556		Self(NEXT_CONSUMER_ID.fetch_add(1, Ordering::Relaxed))
557	}
558}
559
560/// FNV-1a over a path and a sequence of origin ids.
561///
562/// FNV-1a, not the std hasher: its output is fixed across Rust versions and
563/// builds, which matters when nodes run mismatched binaries during a rolling
564/// deploy and still need to agree on the same route. SEED is a custom basis
565/// (any nonzero u64 works, the textbook one is just as arbitrary); FNV_PRIME is
566/// the standard FNV-64 prime and should stay put. Mixing the path in spreads
567/// equal routes across different upstreams rather than funneling onto one.
568fn fnv_key(name: &str, origins: impl IntoIterator<Item = Hop>) -> u64 {
569	const SEED: u64 = 0x420C0DECB00B; // 420 C0DEC B00B
570	const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
571
572	let mut hash = SEED;
573	for &byte in name.as_bytes() {
574		hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
575	}
576	for origin in origins {
577		for &byte in &origin.id().to_le_bytes() {
578			hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
579		}
580	}
581
582	hash
583}
584
585/// Ordering key for a route entry covering one prefix. Lower wins: an identified
586/// chain (no 0) outranks an anonymous one regardless of cost, then the cheapest
587/// cost, then a broadcast published on this origin (it serves what is here, not a
588/// claim that has to ask), then the shortest hop chain, then a deterministic hash
589/// of the prefix and chain so every node converges on the same winner, and finally
590/// the newest announcement, so a reconnect under an otherwise identical route wins
591/// the moment it lands instead of after the transport retires the old session.
592fn route_order(prefix: &Path, entry: &RouteEntry) -> (bool, Cost, bool, usize, u64, Reverse<u64>) {
593	(
594		entry.is_anonymous(),
595		entry.cost,
596		!entry.local,
597		entry.hops.len(),
598		fnv_key(prefix.as_str(), entry.hops.iter().copied()),
599		Reverse(entry.id),
600	)
601}
602
603/// The `(hops, cost, source)` metadata an announce cursor delivers alongside a prefix.
604type RouteMeta = (Hops, Cost, Source);
605
606/// One coalesced update queued for an `AnnounceConsumer`.
607///
608/// At most one entry exists per prefix, so a slow consumer's pending set is
609/// bounded by the number of distinct prefixes. A metadata change on a live route
610/// overwrites the pending `Announce` (or is delivered as another active update),
611/// while `UnannounceAnnounce` preserves a real retract-then-announce sequence.
612type AnnounceMeta = (RouteMeta, Option<Vec<Pattern>>);
613
614enum PendingUpdate {
615	Announce(AnnounceMeta),
616	Unannounce(AnnounceMeta),
617	UnannounceAnnounce { old: AnnounceMeta, new: AnnounceMeta },
618}
619
620/// Pending updates keyed by prefix. `BTreeMap` keeps memory strictly bounded by
621/// the number of distinct prefixes with outstanding work (collapsed pairs are
622/// fully erased) and gives a deterministic lexicographic delivery order so
623/// tests can predict it.
624#[derive(Default)]
625struct OriginConsumerState {
626	pending: BTreeMap<PathOwned, PendingUpdate>,
627	/// Prefixes whose most recently delivered update was an announce. A pending
628	/// `Announce` is ambiguous on its own: it is an unseen initial announce (a
629	/// retraction cancels it entirely) or a metadata update on a route the
630	/// consumer already observed (a retraction must still be delivered).
631	delivered: BTreeSet<PathOwned>,
632	/// Set by the origin's teardown: the cursor drains `pending`, then reports
633	/// the end instead of parking forever on a table that can never fire again.
634	ended: bool,
635}
636
637impl OriginConsumerState {
638	fn apply_announce(&mut self, prefix: PathOwned, meta: RouteMeta, captures: Option<Vec<Pattern>>) {
639		let meta = (meta, captures);
640		let new = match self.pending.remove(&prefix) {
641			// First announce, a stale announce being replaced, or a metadata update.
642			None | Some(PendingUpdate::Announce(_)) => PendingUpdate::Announce(meta),
643			// Consumer needs to observe the retraction before this announce.
644			Some(PendingUpdate::Unannounce(old) | PendingUpdate::UnannounceAnnounce { old, .. }) => {
645				PendingUpdate::UnannounceAnnounce { old, new: meta }
646			}
647		};
648		self.pending.insert(prefix, new);
649	}
650
651	fn apply_unannounce(&mut self, prefix: PathOwned, last: RouteMeta, captures: Option<Vec<Pattern>>) {
652		let last = (last, captures);
653		match self.pending.remove(&prefix) {
654			// The pending announce was never delivered and neither was any earlier
655			// one, so the pair cancels entirely.
656			Some(PendingUpdate::Announce(_)) if !self.delivered.contains(&prefix) => {}
657			// Either nothing is pending or the pending announce was a metadata
658			// update on a delivered route; the consumer still owes a retraction.
659			None | Some(PendingUpdate::Announce(_) | PendingUpdate::Unannounce(_)) => {
660				self.pending.insert(prefix, PendingUpdate::Unannounce(last));
661			}
662			// The embedded announce cancels with this retraction; the consumer still
663			// needs the leading one.
664			Some(PendingUpdate::UnannounceAnnounce { old, .. }) => {
665				self.pending.insert(prefix, PendingUpdate::Unannounce(old));
666			}
667		}
668	}
669
670	/// Take one update to deliver to the consumer, if any.
671	fn take(&mut self) -> Option<AnnounceUpdate> {
672		let prefix = self.pending.keys().next()?.clone();
673		let ((meta, captures), kind) = match self.pending.remove(&prefix).unwrap() {
674			PendingUpdate::Announce(meta) => {
675				// The consumer has seen this prefix before, so it is a metadata update.
676				let kind = match self.delivered.insert(prefix.clone()) {
677					true => AnnounceKind::Announced,
678					false => AnnounceKind::Updated,
679				};
680				(meta, kind)
681			}
682			PendingUpdate::Unannounce(meta) => {
683				self.delivered.remove(&prefix);
684				(meta, AnnounceKind::Retracted)
685			}
686			PendingUpdate::UnannounceAnnounce { old, new } => {
687				// Deliver the retraction now; leave the trailing announce pending so
688				// the next take returns it for the same prefix.
689				self.delivered.remove(&prefix);
690				self.pending.insert(prefix.clone(), PendingUpdate::Announce(new));
691				(old, AnnounceKind::Retracted)
692			}
693		};
694		Some(AnnounceUpdate {
695			prefix,
696			captures,
697			route: Route {
698				hops: meta.0,
699				cost: meta.1,
700				via: Hop::UNKNOWN,
701				source: meta.2,
702			},
703			kind,
704		})
705	}
706}
707
708/// One announced route in the origin's table, absolute prefix.
709struct RouteEntry {
710	id: u64,
711	prefix: PathOwned,
712	/// The absolute patterns the announcing producer may serve. The prefix is
713	/// only the wire-visible covering claim; this scope remains authoritative.
714	scope: Patterns,
715	hops: Hops,
716	cost: Cost,
717	/// The announcing session's declared or assigned identity. Split-horizon
718	/// matches this as well as [`Self::hops`], so an anonymous hop 0 still
719	/// cannot echo back to the session it came from.
720	via: Hop,
721	/// Whether this is a broadcast published on this origin: a front that
722	/// starts from one only fails over to another local publisher.
723	local: bool,
724	/// Whether a handle marked [`Producer::peer`] inserted the entry, so it
725	/// entered from a cluster peer rather than here.
726	peer: bool,
727	/// The queue requests under this route are served from, when the announcer
728	/// serves content on demand (a [`Dynamic`]). `None` for an advertise-only
729	/// announcement ([`Producer::announce`]) and for a local broadcast.
730	server: Option<kio::Shared<ServeState>>,
731	/// The broadcast published on this origin at exactly `prefix`, when the
732	/// entry is one: requests resolve to it directly, and the newest one at a
733	/// path wins through [`route_order`].
734	source: Option<broadcast::Consumer>,
735	/// Whether the entry exists for anyone: cursors see it and requests resolve
736	/// through it. A broadcast is in the table from creation but serves nobody,
737	/// locally or remotely, until it announces.
738	advertised: bool,
739	/// [`prefix_claim`] of [`Self::prefix`], built once at announce time.
740	///
741	/// The announce sync evaluates a route's claim once per (cursor, route) pair,
742	/// and building one allocates a segment vector and a canonical string. Holding
743	/// it makes that visit a comparison.
744	claim: Pattern,
745}
746
747impl RouteEntry {
748	fn is_anonymous(&self) -> bool {
749		self.hops.iter().any(|hop| *hop == Hop::UNKNOWN)
750	}
751
752	/// Where the entry entered this origin.
753	fn entered(&self) -> Source {
754		match self.peer {
755			true => Source::Peer(self.via),
756			false => Source::Local,
757		}
758	}
759
760	/// Whether a request for `path` can be served through this entry. A served
761	/// route covers everything beneath its prefix; a broadcast published here
762	/// is only itself, so it serves its exact path and shadows what is beneath.
763	fn serves(&self, path: &Path) -> bool {
764		self.server.is_some() || (self.source.is_some() && self.prefix == *path)
765	}
766
767	/// Whether `pin` admits this entry for a front's selection.
768	fn qualifies(&self, pin: Pin) -> bool {
769		match pin {
770			Pin::Any => true,
771			Pin::Local => self.local,
772			Pin::Publisher(first) => self.hops.iter().next() == Some(&first),
773			Pin::Route(id) => self.id == id,
774		}
775	}
776
777	/// Whether this entry may be observed or served to a requester excluding `peer`.
778	///
779	/// A non-zero peer is hidden when it is the announcing session (`via`) or
780	/// appears in the chain. Hop 0 identifies nobody, so it is never excluded.
781	fn visible_to(&self, exclude: Option<Hop>) -> bool {
782		match exclude {
783			Some(peer) if peer != Hop::UNKNOWN => self.via != peer && !self.hops.contains(&peer),
784			_ => true,
785		}
786	}
787
788	/// Whether this route and `allowed` share any path beneath the advertised prefix.
789	fn overlaps(&self, allowed: &Patterns) -> bool {
790		self.scope.iter().any(|scope| {
791			scope
792				.intersect(&self.claim)
793				.is_ok_and(|scoped| scoped.iter().any(|restriction| allowed.overlaps(restriction)))
794		})
795	}
796}
797
798/// The paths a prefix can cover, using an exact pattern at the path depth limit.
799fn prefix_claim(prefix: &Path) -> Result<Pattern, InvalidPattern> {
800	if prefix.parts().count() == Path::MAX_PARTS {
801		Pattern::literal(prefix.as_str())
802	} else {
803		Pattern::subtree(prefix.as_str())
804	}
805}
806
807/// A served route's request queue: what materializes a requested path on demand.
808///
809/// Shared by every requester resolving through the owning route and the
810/// [`Dynamic`] draining it, so both sides work under one lock.
811#[derive(Default)]
812struct ServeState {
813	// Result channels for pending requests, keyed by absolute path so concurrent
814	// `request_broadcast` calls for the same path coalesce onto one channel.
815	requests: Requests<PathOwned, kio::Producer<PendingBroadcast>>,
816
817	// Broadcasts the handler has already served, kept weakly so a repeat request for the
818	// same path resolves to a shared clone instead of re-invoking the handler (which would
819	// open a duplicate upstream subscription). Weak so a served broadcast still closes once
820	// its real consumers drop. The cache reclaims closed entries incrementally on insert, so a
821	// long-lived origin serving many distinct one-shot paths stays bounded by the live count.
822	served: WeakCache<PathOwned, broadcast::WeakConsumer>,
823
824	// Set when the announcement is retracted or the origin tears down: new requests
825	// fail immediately and the handler observes the end instead of parking forever.
826	closed: bool,
827}
828
829/// Key of a remotely-served front: the absolute path and the requester's
830/// [`Horizon`]. Requesters excluding different peers get separate fronts, so a
831/// front's failover never adopts a route flowing back through one of its own
832/// readers, nor a local view a peer's route.
833type FrontKey = (PathOwned, Horizon);
834
835/// Which routes a reader sees: the split-horizon exclusion and the local-only view.
836#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
837struct Horizon {
838	/// Routes whose hop chain or announcing session (`via`) is this peer are
839	/// hidden. `Some(UNKNOWN)` marks an anonymous peer: no hop is excluded, but
840	/// local-only broadcasts are not advertised. `None` is a local reader.
841	exclude: Option<Hop>,
842	/// Hide the routes that entered from a cluster peer ([`Consumer::local`]).
843	local: bool,
844}
845
846impl Horizon {
847	/// Whether `entry` may be observed or served through this horizon.
848	fn admits(&self, entry: &RouteEntry) -> bool {
849		!(self.local && entry.peer) && entry.visible_to(self.exclude)
850	}
851}
852
853/// One remotely-served front in [`OriginState::fronts`]: the shared spliced
854/// broadcast at a path plus the channel requesters resolve through.
855#[derive(Clone)]
856struct RemoteFront {
857	/// Resolves requesters with the front's consumer (or the error that ended it
858	/// unresolved). The producer lives here so the teardown can reject requesters
859	/// still parked on a front whose watcher was cancelled.
860	request: kio::Producer<PendingBroadcast>,
861	/// The front's spliced broadcast, weak: dead once the front ends, so a
862	/// later request re-creates the front instead of joining a corpse.
863	broadcast: broadcast::WeakConsumer,
864	/// Which routes serve the front's content, fixed by its first source. A
865	/// request joins the front only while the best route is one of them.
866	pin: kio::Lock<Pin>,
867}
868
869/// The last route a cursor observed: entry id, metadata, servability, and captures.
870type CursorRoute = (u64, RouteMeta, bool, Option<Vec<Pattern>>);
871
872impl WeakEntry for RemoteFront {
873	fn is_closed(&self) -> bool {
874		self.broadcast.is_closed()
875	}
876
877	fn same_channel(&self, other: &Self) -> bool {
878		self.broadcast.same_channel(&other.broadcast)
879	}
880}
881
882/// One registered announce cursor: which patterns it may see, how prefixes are
883/// re-rooted, and the per-cursor delivery buffer.
884struct TableCursor {
885	/// The prefix stripped from every delivered path.
886	root: PathOwned,
887	/// The absolute patterns this cursor is scoped to (its token / scope).
888	allowed: Patterns,
889	/// Where the cursor hangs in the [`RouteTable`]: the literal heads of
890	/// `allowed`. A route the cursor can see sits at or under one of them, or on
891	/// the walk down to one.
892	heads: Vec<PathOwned>,
893	/// The routes this cursor may see (control-plane split horizon).
894	horizon: Horizon,
895	/// Which routes beneath a hidden segment are reported.
896	hidden: Hidden,
897	/// The delivery buffer, drained by the cursor's `poll_next`.
898	state: kio::Producer<OriginConsumerState>,
899	/// The last delivered best route per presented (relative) prefix, for change
900	/// detection: `(entry id, hops, cost)`.
901	// entry id, metadata, and whether the entry could serve requests: the last
902	// is part of the dedupe key (see `sync_cursor`) but never leaves the model.
903	current: HashMap<PathOwned, CursorRoute>,
904}
905
906impl TableCursor {
907	/// Where `prefix` presents on this cursor, named relative to the cursor root.
908	/// The prefix stays a prefix; the pattern scope only decides visibility.
909	/// `claim` is the prefix's [`prefix_claim`], which the caller already holds:
910	/// building one allocates, and the sweeps below ask this per route per
911	/// cursor.
912	fn presented(&self, prefix: &Path, claim: &Pattern) -> Option<PathOwned> {
913		if !self.allowed.overlaps(claim) {
914			return None;
915		}
916
917		if let Some(relative) = prefix.strip_prefix(&self.root) {
918			return Some(relative.to_owned());
919		}
920		self.root.has_prefix(prefix).then(PathOwned::default)
921	}
922
923	/// What the cursor's most specific matching scope member captures from an
924	/// exact announced prefix. An overlap-only route does not pin every wildcard.
925	fn captures(&self, prefix: &Path) -> Option<Vec<Pattern>> {
926		let literal = Pattern::literal(prefix.as_str()).ok()?;
927		self.allowed
928			.iter()
929			.filter_map(|allowed| {
930				allowed
931					.captures(&literal)
932					.map(|captures| (allowed.specificity(), captures))
933			})
934			.max_by_key(|(specificity, _)| *specificity)
935			.map(|(_, captures)| captures)
936	}
937
938	/// Whether this cursor may observe `entry` at all: advertised, not behind
939	/// the excluded peer (split horizon), and within the cursor's patterns.
940	fn visible(&self, entry: &RouteEntry) -> bool {
941		entry.advertised && self.horizon.admits(entry) && entry.overlaps(&self.allowed) && self.discovers(&entry.prefix)
942	}
943
944	/// Whether the hidden rule lets this cursor discover a route at `prefix`.
945	fn discovers(&self, prefix: &Path) -> bool {
946		(self.hidden.include || !hides(&self.heads, prefix))
947			&& self.hidden.beyond.as_ref().is_none_or(|outer| hides(outer, prefix))
948	}
949}
950
951/// A handle's view of an origin: the absolute patterns it may reach.
952#[derive(Clone)]
953struct OriginScope {
954	// The paths this handle may reach, absolute.
955	allowed: Patterns,
956}
957
958impl OriginScope {
959	/// A view that reaches nothing.
960	fn empty() -> Self {
961		Self {
962			allowed: Patterns::new(),
963		}
964	}
965
966	/// This view narrowed to the absolute `patterns`: the paths in both.
967	fn narrow(&self, patterns: &Patterns) -> Option<Self> {
968		let allowed = self.allowed.intersect(patterns).ok()?;
969		if allowed.is_empty() {
970			None
971		} else {
972			Some(Self { allowed })
973		}
974	}
975
976	/// Whether this view reaches the absolute `path`.
977	fn permits(&self, path: &Path) -> bool {
978		self.allowed.matches(path.as_str())
979	}
980
981	/// What this view reaches, named from `root`.
982	fn relative(&self, root: &Path) -> Patterns {
983		self.allowed.rebase(root.as_str())
984	}
985}
986
987impl Default for OriginScope {
988	fn default() -> Self {
989		Self {
990			allowed: Patterns::from(Pattern::all()),
991		}
992	}
993}
994
995/// The announce-interest prefixes that cover a pattern scope on a prefix-only
996/// wire: each member's literal head, minus heads another already covers.
997pub(crate) fn interest_prefixes(allowed: &Patterns) -> Vec<PathOwned> {
998	let mut heads: Vec<PathOwned> = allowed
999		.iter()
1000		.map(|pattern| Path::new(pattern.head()).to_owned())
1001		.collect();
1002	heads.sort();
1003	heads.dedup();
1004	let covered = heads.clone();
1005	heads.retain(|head| !covered.iter().any(|other| other != head && head.has_prefix(other)));
1006	heads
1007}
1008
1009/// Which routes beneath a hidden segment an announce cursor reports.
1010///
1011/// A route is hidden when a segment below the cursor's requested prefix (its
1012/// interest head) starts with `.`; a prefix that names the dot segment itself
1013/// lists what is under it. Only discovery is affected: a request by exact path
1014/// resolves either way.
1015#[derive(Clone, Debug, Default, PartialEq, Eq)]
1016pub(crate) struct Hidden {
1017	/// Report hidden routes too.
1018	include: bool,
1019	/// Report only what a feed scoped to these heads hides, for a stream that tops
1020	/// up a feed already carrying everything visible from them.
1021	beyond: Option<Vec<PathOwned>>,
1022}
1023
1024/// Whether a segment of `prefix` below the head it sits under starts with `.`.
1025/// A route at or above a head has nothing below it, so it never hides.
1026fn hides(heads: &[PathOwned], prefix: &Path) -> bool {
1027	heads
1028		.iter()
1029		.any(|head| prefix.strip_prefix(head).is_some_and(|below| below.is_hidden()))
1030}
1031
1032/// What an [`AnnounceUpdate`] reports about its path.
1033#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1034pub enum AnnounceKind {
1035	/// A route now covers the path; the cursor had none there.
1036	Announced,
1037	/// The route covering the path changed hops or cost; it is delivered in place.
1038	Updated,
1039	/// No route covers the path any more.
1040	Retracted,
1041}
1042
1043impl AnnounceKind {
1044	/// Whether a route covers the path after this update.
1045	pub fn is_active(self) -> bool {
1046		!matches!(self, Self::Retracted)
1047	}
1048}
1049
1050/// A route announcement, update, or retraction, delivered by [`AnnounceConsumer`].
1051///
1052/// An announcement is always a prefix, never a broadcast: it advertises that
1053/// [`prefix`](Self::prefix) and every path beneath it are servable. A broadcast
1054/// announces its own path, so the prefix usually names one, but resolve it with
1055/// [`Consumer::request_broadcast`]; the application decides which paths name
1056/// broadcasts, and filters with a [`Pattern`] locally when it wants a subset.
1057#[derive(Clone, Debug)]
1058pub struct AnnounceUpdate {
1059	/// The prefix the route covers, relative to the consuming cursor's root.
1060	pub prefix: PathOwned,
1061	/// What the scope's wildcards stood for when the announced prefix pins all of
1062	/// them. `None` for an overlap-only route or a scope without a complete match.
1063	pub captures: Option<Vec<Pattern>>,
1064	/// The route serving the prefix. On a retraction this carries its last
1065	/// advertised metadata.
1066	pub route: Route,
1067	/// Whether the prefix was announced, re-priced, or retracted.
1068	pub kind: AnnounceKind,
1069}
1070
1071/// Publishes broadcasts and announces routes into an origin.
1072#[derive(Clone)]
1073pub struct Producer {
1074	// Identity for this origin. Appended to route hops when re-announcing so
1075	// downstream relays can detect loops and prefer the shortest path.
1076	hop: Hop,
1077
1078	// The absolute patterns this handle may publish under.
1079	scope: OriginScope,
1080
1081	// The prefix that is automatically stripped from all paths.
1082	root: PathOwned,
1083
1084	// The origin's shared state: the route table, announce cursors, and the
1085	// remotely-served fronts. Shared with every derived consumer.
1086	shared: kio::Shared<OriginState>,
1087
1088	// The cache pool inherited by broadcasts created under this origin (sessions
1089	// mint their remote broadcasts with it). Unbounded by default.
1090	pool: cache::Pool,
1091
1092	// Retention ceiling inherited by broadcasts created under this origin (see
1093	// [`Config::cache_duration`]). `Duration::MAX` (no ceiling) by default.
1094	cache_duration: Duration,
1095
1096	// Retention window for a track whose publisher advertises none (see
1097	// [`Config::default_max_age`]).
1098	default_max_age: Duration,
1099
1100	// Ingress stats context. Broadcasts created through this producer are attributed
1101	// to it (writes counted on the subscriber/ingress side). Empty (no-op) unless a
1102	// session tagged this handle via [`Self::with_stats`].
1103	stats: stats::Session,
1104
1105	// Whether routes announced through this handle entered from a cluster peer
1106	// (see [`Self::peer`]).
1107	peer: bool,
1108
1109	// Submission handle to the origin's [`Driver`]: source watchers, fronts, and
1110	// serve tasks queued here run when the driver is polled. Closed once the
1111	// driver drops, which is what makes later mutations fail with `Closed`.
1112	tasks: Tasks,
1113
1114	// The clock advanced by the origin driver.
1115	timers: Clock,
1116}
1117
1118impl Producer {
1119	/// Build a producer from a [`Config`] (identity + cache pool) with no scoped
1120	/// prefix and no pre-existing broadcasts, paired with the [`Driver`] that runs
1121	/// the origin's lifecycle work.
1122	///
1123	/// Poll the driver with caller-supplied time for the origin to make progress.
1124	/// `moq_tokio::origin::spawn` wraps this for tokio callers.
1125	pub fn new(config: Config) -> (Self, Driver) {
1126		let (tasks, set) = TaskSet::new();
1127		let scope = OriginScope::default();
1128		let shared = kio::Shared::<OriginState>::default();
1129		let timers = Clock::default();
1130		let pool = config.pool.clone();
1131		let producer = Self {
1132			hop: config.hop,
1133			scope: scope.clone(),
1134			root: PathOwned::default(),
1135			shared: shared.clone(),
1136			pool: config.pool,
1137			cache_duration: config.cache_duration,
1138			default_max_age: config.default_max_age,
1139			stats: stats::Session::default(),
1140			peer: false,
1141			tasks,
1142			timers: timers.clone(),
1143		};
1144		let driver = Driver {
1145			state: DriverState {
1146				set,
1147				shared,
1148				done: false,
1149			},
1150			timers,
1151			pool,
1152		};
1153		(producer, driver)
1154	}
1155
1156	/// Attach an ingress stats context: broadcasts created through this handle (and
1157	/// any handle derived from it) are attributed to `session` on the subscriber
1158	/// (ingress) side. Pass [`stats::Session::default`] to opt out.
1159	pub fn with_stats(mut self, session: stats::Session) -> Self {
1160		self.stats = session;
1161		self
1162	}
1163
1164	/// Mark this handle (and any handle derived from it) as a cluster peer's:
1165	/// every route it announces reports [`Source::Peer`], and
1166	/// [`Consumer::local`] hides it.
1167	///
1168	/// Hand it to a session with another relay, so this origin can tell what
1169	/// entered here from what a peer forwarded. The hop chain cannot: a client
1170	/// and a peer each append one hop.
1171	pub fn peer(mut self) -> Self {
1172		self.peer = true;
1173		self
1174	}
1175
1176	/// This origin's construction config.
1177	pub fn config(&self) -> Config {
1178		Config {
1179			hop: self.hop,
1180			pool: self.pool.clone(),
1181			cache_duration: self.cache_duration,
1182			default_max_age: self.default_max_age,
1183		}
1184	}
1185
1186	/// This origin's hop identity.
1187	pub fn hop(&self) -> Hop {
1188		self.hop
1189	}
1190
1191	// The retention window for a track whose publisher advertises none (see
1192	// [`Config::default_max_age`]). Cheaper than `config()`, which clones the pool.
1193	pub(crate) fn default_max_age(&self) -> Duration {
1194		self.default_max_age
1195	}
1196
1197	/// A producer with *no* allowed prefixes: it can't publish anything and
1198	/// advertises no subscribe interest (its `allowed()` is empty, so the
1199	/// subscriber issues no ANNOUNCE_PLEASE). Used to fill an unset session half
1200	/// so both the publisher and subscriber loops still run.
1201	pub(crate) fn empty(hop: Hop) -> Self {
1202		// No allowed prefixes means no broadcast is ever created, so nothing will
1203		// ever be queued on the detached submission handle.
1204		let (tasks, _) = TaskSet::new();
1205		Self {
1206			hop,
1207			scope: OriginScope::empty(),
1208			root: PathOwned::default(),
1209			shared: kio::Shared::default(),
1210			pool: cache::Pool::default(),
1211			cache_duration: Duration::MAX,
1212			default_max_age: track::DEFAULT_MAX_AGE,
1213			stats: stats::Session::default(),
1214			peer: false,
1215			tasks,
1216			timers: Clock::default(),
1217		}
1218	}
1219
1220	/// Create a broadcast at `path`, fed through the returned producer.
1221	///
1222	/// This is how local content enters an origin. The returned
1223	/// [`broadcast::Producer`] is a source: the origin owns the broadcast
1224	/// consumers actually see, and splices its tracks across every source created
1225	/// at the same path, preferring the newest. When the serving source changes,
1226	/// tracks resume from the replacement at the first missing group; consumers
1227	/// never observe the swap.
1228	///
1229	/// The broadcast exists for nobody until [`broadcast::Producer::announce`]:
1230	/// until then no announce cursor lists it and a request for its path fails
1231	/// with [`Error::Unroutable`], for a consumer of this origin exactly as for a
1232	/// peer. Announce once the tracks a subscriber needs first exist. To serve
1233	/// paths on demand without publishing each one, use [`Self::dynamic`].
1234	///
1235	/// Announcing is visible to local consumers before it returns; only
1236	/// lifecycle work (track serving, teardown) waits for the [`Driver`] to be
1237	/// polled. Register a [`broadcast::Producer::dynamic`] handler before
1238	/// announcing, so the first consumer finds the tracks it serves.
1239	///
1240	/// End the broadcast with [`broadcast::Producer::close`] or by dropping it;
1241	/// either way the path closes once it was the last source.
1242	///
1243	/// Fails with [`Error::Unauthorized`] if `path` is outside the prefixes this
1244	/// producer may publish under (after [`scope`](Self::scope)),
1245	/// [`Error::BoundsExceeded`] if the full rooted path exceeds
1246	/// [`Path::MAX_PARTS`], [`Error::InvalidPath`] if it holds a segment no
1247	/// pattern can spell (`*` or `**`), or [`Error::Closed`] once the origin's
1248	/// [`Driver`] has been dropped.
1249	pub fn create_broadcast(&self, path: impl AsPath) -> Result<broadcast::Producer, Error> {
1250		let path = path.as_path();
1251
1252		let full = self.root.join(&path).to_owned();
1253		if !self.scope.permits(&full) {
1254			return Err(Error::Unauthorized);
1255		}
1256		// A decoded prefix and suffix are each within the wire limit, but their
1257		// join might not be. Enforcing here bounds the table depth and guarantees the
1258		// path can be re-encoded when forwarded.
1259		if full.parts().count() > Path::MAX_PARTS {
1260			return Err(BoundsExceeded.into());
1261		}
1262		// A path only a pattern could spell (a `*` segment) advertises nowhere, so
1263		// refuse it here rather than publish a broadcast no cursor can see.
1264		let claim = prefix_claim(&full)?;
1265
1266		// Resolve the ingress counters once, keyed by the absolute broadcast path.
1267		let ingress = self.stats.ingress(&full);
1268
1269		// The broadcast is a route table entry at its exact path from the start,
1270		// hidden from cursors and requests until it announces. The entry lives
1271		// as long as the broadcast: its announcer drops on close, abort, or the
1272		// last handle.
1273		let announcing = Announcing {
1274			hop: self.hop,
1275			shared: self.shared.clone(),
1276			requested: full.clone(),
1277			prefixes: vec![(full.clone(), claim)],
1278			scope: self.scope.allowed.clone(),
1279			local: true,
1280			peer: self.peer,
1281			stats: self.stats.clone(),
1282		};
1283		let info = broadcast::Info {
1284			pool: self.pool.clone(),
1285			cache_duration: self.cache_duration,
1286			path: full,
1287		};
1288		let source = info.produce().with_stats(ingress.clone());
1289		let entry = announcing.announce(
1290			Route::default(),
1291			Serving {
1292				server: None,
1293				source: Some(source.consume()),
1294				advertised: false,
1295			},
1296		)?;
1297		Ok(source.with_announcer(Announcer {
1298			entry,
1299			ingress,
1300			_keepalive: self.tasks.keepalive(),
1301		}))
1302	}
1303
1304	/// Create and advertise a broadcast in one call.
1305	pub fn publish(&self, path: impl AsPath, route: Route) -> Result<broadcast::Producer, Error> {
1306		let broadcast = self.create_broadcast(path)?;
1307		broadcast.announce(route)?;
1308		Ok(broadcast)
1309	}
1310
1311	/// Mint a standalone source broadcast for a served-route request: it carries
1312	/// this origin's cache policy and ingress attribution, but
1313	/// is *not* entered into the route table. Sessions answer
1314	/// [`Dynamic`] requests with one of these; the requester already holds
1315	/// the request's result channel, so the table never needs to resolve it.
1316	pub(crate) fn create_source(&self, path: impl AsPath) -> broadcast::Producer {
1317		let path = path.as_path();
1318		let full = self.root.join(&path).to_owned();
1319		let ingress = self.stats.ingress(&full);
1320		broadcast::Info {
1321			pool: self.pool.clone(),
1322			cache_duration: self.cache_duration,
1323			path: full,
1324		}
1325		.produce()
1326		.with_stats(ingress)
1327	}
1328
1329	/// Advertise a route without serving it: a claim that paths under `prefix`
1330	/// can be served, answered by nothing.
1331	///
1332	/// A request under an advertise-only route resolves [`Error::Unroutable`]
1333	/// unless an announced broadcast or a served route ([`Self::dynamic`]) covers
1334	/// the path too. Tests use it to shape the route table; everything else
1335	/// advertises through a broadcast ([`broadcast::Producer::announce`]) or a
1336	/// [`Dynamic`] handler, which serve what they claim.
1337	#[cfg(test)]
1338	pub(crate) fn announce(&self, prefix: impl AsPath, route: Route) -> Result<AnnounceProducer, Error> {
1339		Announcing::new(self, prefix)?.announce(
1340			route,
1341			Serving {
1342				server: None,
1343				source: None,
1344				advertised: true,
1345			},
1346		)
1347	}
1348
1349	/// Advertise a route over `prefix` and serve the requests beneath it.
1350	///
1351	/// A route is always a prefix: it claims `prefix` and every path beneath it
1352	/// (the empty prefix claims every path). A service that only serves some of
1353	/// them, say `pid/*.hang`, advertises the covering prefix and refuses the
1354	/// rest as they are requested; consumers narrow with a [`Pattern`] locally.
1355	/// This is the one shape every wire carries, so a route means the same
1356	/// thing on every hop.
1357	///
1358	/// The advertisement is visible to [`Consumer::announced`] and forwarded by
1359	/// sessions for as long as the returned [`Dynamic`] (and every clone) lives.
1360	/// A consumer resolving a path under it through this route is handed to the
1361	/// handler as a [`Request`] to materialize on demand. This is how a service
1362	/// answers a whole subtree without publishing each path, and how sessions
1363	/// land the routes a peer announces to them; a publisher that
1364	/// knows its broadcasts advertises each one's exact path with
1365	/// [`broadcast::Producer::announce`] instead, so subscribers can enumerate
1366	/// them.
1367	///
1368	/// The prefix must overlap this producer's pattern scope. Individual requests
1369	/// remain authoritative and are refused when they do not match the scope.
1370	pub fn dynamic(&self, prefix: impl AsPath, route: Route) -> Result<Dynamic, Error> {
1371		let announcing = Announcing::new(self, prefix)?;
1372		let serve = kio::Shared::<ServeState>::default();
1373		serve.lock().requests.add_handler();
1374		let announcement = announcing.announce(
1375			route,
1376			Serving {
1377				server: Some(serve.clone()),
1378				source: None,
1379				advertised: true,
1380			},
1381		)?;
1382		Ok(Dynamic {
1383			announcement,
1384			state: serve,
1385		})
1386	}
1387
1388	/// Returns a producer rooted at `root` and restricted to matching `patterns`.
1389	///
1390	/// `root` is relative to this producer's root, and `patterns` are relative to
1391	/// the new root. Returns [`Error::Unauthorized`] when the requested scope has
1392	/// no overlap with this producer's scope, or [`Error::BoundsExceeded`] when
1393	/// rooting the patterns would exceed the path limit.
1394	pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Producer, Error> {
1395		let root = self.root.join(root).to_owned();
1396		let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
1397		let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
1398		Ok(Producer {
1399			hop: self.hop,
1400			scope,
1401			root,
1402			shared: self.shared.clone(),
1403			pool: self.pool.clone(),
1404			cache_duration: self.cache_duration,
1405			default_max_age: self.default_max_age,
1406			stats: self.stats.clone(),
1407			peer: self.peer,
1408			tasks: self.tasks.clone(),
1409			timers: self.timers.clone(),
1410		})
1411	}
1412
1413	/// Cheap read handle over this origin's route table.
1414	///
1415	/// Use [`Consumer::announced`] to register interest and start receiving
1416	/// announcement events; the consumer itself does not allocate any channels.
1417	pub fn consume(&self) -> Consumer {
1418		// Untagged: a session tags the egress consumer separately via
1419		// `origin::Consumer::with_stats` (ingress and egress are distinct sides).
1420		Consumer::from_producer(self, stats::Session::default())
1421	}
1422
1423	/// Returns the root that is automatically stripped from all paths.
1424	pub fn root(&self) -> &Path<'_> {
1425		&self.root
1426	}
1427
1428	/// The patterns this producer may publish under, relative to its root.
1429	pub fn allowed(&self) -> Patterns {
1430		self.scope.relative(&self.root)
1431	}
1432
1433	/// Converts a relative path to an absolute path.
1434	pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
1435		self.root.join(path)
1436	}
1437}
1438
1439/// What it takes to insert a route: the prefixes it covers and the origin table
1440/// to insert them into. Built by [`Producer::announce`], [`Producer::dynamic`],
1441/// and [`Announcer`], which is the same advertisement re-issued from a broadcast.
1442struct Announcing {
1443	hop: Hop,
1444	shared: kio::Shared<OriginState>,
1445	/// The absolute advertised prefix, which also keys the ingress announce counters.
1446	requested: PathOwned,
1447	/// The prefix inserted into the table, with its [`prefix_claim`]. Pattern
1448	/// scopes decide visibility and request authorization without changing the
1449	/// route's prefix shape.
1450	prefixes: Vec<(PathOwned, Pattern)>,
1451	/// The absolute paths the producer is authorized to serve.
1452	scope: Patterns,
1453	local: bool,
1454	/// Whether the producer was marked [`Producer::peer`].
1455	peer: bool,
1456	stats: stats::Session,
1457}
1458
1459impl Announcing {
1460	/// The requested prefix as-is, refused when its subtree does not overlap the scope.
1461	fn new(producer: &Producer, prefix: impl AsPath) -> Result<Self, Error> {
1462		let requested = producer.root.join(prefix.as_path()).to_owned();
1463		if requested.parts().count() > Path::MAX_PARTS {
1464			return Err(BoundsExceeded.into());
1465		}
1466		let claim = prefix_claim(&requested)?;
1467		if !producer.scope.allowed.overlaps(&claim) {
1468			return Err(Error::Unauthorized);
1469		}
1470		Ok(Self {
1471			hop: producer.hop,
1472			shared: producer.shared.clone(),
1473			requested: requested.clone(),
1474			prefixes: vec![(requested, claim)],
1475			scope: producer.scope.allowed.clone(),
1476			local: false,
1477			peer: producer.peer,
1478			stats: producer.stats.clone(),
1479		})
1480	}
1481
1482	fn announce(&self, route: Route, serving: Serving) -> Result<AnnounceProducer, Error> {
1483		debug_assert!(
1484			!route.hops.contains(&self.hop),
1485			"announce called with a looping hop chain",
1486		);
1487
1488		let via = route.via;
1489
1490		let mut shared = self.shared.lock();
1491		if shared.closed {
1492			return Err(Error::Closed);
1493		}
1494
1495		let mut entries = Vec::with_capacity(self.prefixes.len());
1496		for (prefix, claim) in &self.prefixes {
1497			let id = shared.next_route;
1498			shared.next_route += 1;
1499			shared.routes.insert(RouteEntry {
1500				id,
1501				prefix: prefix.clone(),
1502				scope: self.scope.clone(),
1503				hops: route.hops.clone(),
1504				cost: route.cost,
1505				via,
1506				local: self.local,
1507				peer: self.peer,
1508				server: serving.server.clone(),
1509				source: serving.source.clone(),
1510				advertised: serving.advertised,
1511				claim: claim.clone(),
1512			});
1513			shared.sync_route(prefix, claim);
1514			entries.push((prefix.clone(), id));
1515		}
1516		drop(shared);
1517
1518		// Ingress announce guard: held while the route is advertised.
1519		let guard = serving
1520			.advertised
1521			.then(|| self.stats.ingress(&self.requested).announce());
1522
1523		Ok(AnnounceProducer {
1524			shared: self.shared.clone(),
1525			entries,
1526			guard,
1527		})
1528	}
1529}
1530
1531/// What a route entry serves and whether cursors see it.
1532struct Serving {
1533	server: Option<kio::Shared<ServeState>>,
1534	source: Option<broadcast::Consumer>,
1535	advertised: bool,
1536}
1537
1538/// The table entry a broadcast owns: its exact path, advertised and withdrawn
1539/// through [`broadcast::Producer::announce`] and
1540/// [`broadcast::Producer::unannounce`], and removed when the broadcast ends.
1541///
1542/// Handed to the broadcast by [`Producer::create_broadcast`], so a standalone
1543/// broadcast has none and cannot announce.
1544pub(crate) struct Announcer {
1545	entry: AnnounceProducer,
1546	/// The ingress counters an advertised interval's announce guard comes from.
1547	ingress: stats::Scope,
1548	/// A published broadcast is lifecycle work: the origin's driver keeps
1549	/// running for as long as one lives, even once every producer handle is
1550	/// gone, so a session handed a producer can drop it and keep serving.
1551	_keepalive: Keepalive,
1552}
1553
1554impl Announcer {
1555	/// Advertise the broadcast's path with `route`, or re-price it in place.
1556	pub(crate) fn announce(&mut self, route: Route) -> Result<(), Error> {
1557		self.entry.update(route)?;
1558		if self.entry.guard.is_none() {
1559			self.entry.guard = Some(self.ingress.announce());
1560		}
1561		Ok(())
1562	}
1563
1564	/// Withdraw the advertisement from local and remote consumers alike.
1565	pub(crate) fn withdraw(&mut self) {
1566		self.entry.withdraw();
1567		self.entry.guard = None;
1568	}
1569}
1570
1571/// The write half of an advertisement: a live claim that paths under a
1572/// [`Pattern`] can be served.
1573///
1574/// Held by a [`Dynamic`] and by a broadcast's [`Announcer`]; dropping it
1575/// retracts the route, which [`AnnounceConsumer`]s observe and sessions withdraw
1576/// from their peers.
1577#[must_use = "dropping an announcement retracts the route"]
1578pub(crate) struct AnnounceProducer {
1579	shared: kio::Shared<OriginState>,
1580	/// The table entries this advertisement created, by prefix and id. A prefix
1581	/// remains unchanged; pattern scopes only filter its visibility and requests.
1582	entries: Vec<(PathOwned, u64)>,
1583	/// Ingress announce stats guard, held only while the entries are advertised.
1584	guard: Option<stats::Announce>,
1585}
1586
1587impl AnnounceProducer {
1588	/// Re-price the route in place: replace its hops and cost.
1589	///
1590	/// Consumers observe another active update for the same prefix; sessions
1591	/// forward it as a restart, so route churn never looks like new content. The
1592	/// prefix is fixed at announce time and a [`Route`] cannot name one: to move
1593	/// an advertisement, drop this and announce again. Fails with
1594	/// [`Error::Closed`] once the origin's [`Driver`] has been dropped.
1595	pub fn update(&self, route: Route) -> Result<(), Error> {
1596		let mut shared = self.shared.lock();
1597		if shared.closed {
1598			return Err(Error::Closed);
1599		}
1600		for (prefix, id) in &self.entries {
1601			// Each entry keeps its advertised prefix; only the metadata moves.
1602			let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1603				return Err(Error::Closed);
1604			};
1605			entry.hops = route.hops.clone();
1606			entry.cost = route.cost;
1607			entry.via = route.via;
1608			entry.advertised = true;
1609			let claim = entry.claim.clone();
1610			shared.sync_route(prefix, &claim);
1611		}
1612		Ok(())
1613	}
1614
1615	/// Hide the entries from everyone, local and remote alike: cursors see a
1616	/// retraction and requests stop resolving through them. The entries stay,
1617	/// so announcing again restores the same route. What
1618	/// [`broadcast::Producer::unannounce`] does.
1619	fn withdraw(&self) {
1620		let mut shared = self.shared.lock();
1621		for (prefix, id) in &self.entries {
1622			let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1623				continue;
1624			};
1625			if !entry.advertised {
1626				continue;
1627			}
1628			entry.advertised = false;
1629			let claim = entry.claim.clone();
1630			shared.sync_route(prefix, &claim);
1631		}
1632	}
1633
1634	/// Retract the route now: remove its table entries and reject anything still
1635	/// waiting on its queue. Idempotent, and what dropping the advertisement does.
1636	fn retract(&self) {
1637		let mut shared = self.shared.lock();
1638		for (prefix, id) in &self.entries {
1639			let Some(entry) = shared.routes.remove(prefix, *id) else {
1640				continue;
1641			};
1642			// Reject anything still waiting on this route's server; a request
1643			// already handed to the handler resolves through its own `Request`.
1644			if let Some(server) = &entry.server {
1645				let mut server = server.lock();
1646				server.closed = true;
1647				for producer in server.requests.drain_all() {
1648					if let Ok(mut request) = producer.write() {
1649						request.resolved.get_or_insert(Err(Error::Unroutable));
1650					}
1651				}
1652			}
1653			shared.sync_route(&entry.prefix, &entry.claim);
1654		}
1655	}
1656}
1657
1658impl Drop for AnnounceProducer {
1659	fn drop(&mut self) {
1660		self.retract();
1661	}
1662}
1663
1664/// Drives origin lifecycle work and cache expiration with caller-supplied time.
1665///
1666/// Returned by [`Producer::new`]. Poll on external activity or at the deadline
1667/// it returns, supplying nondecreasing instants. Route changes, track serving, linger,
1668/// failover, and teardown run here; the route table and announce cursors update
1669/// synchronously when a route is announced or retracted.
1670///
1671/// It holds no [`Producer`] clone, so it never keeps the origin alive. Dropping
1672/// it aborts active fronts, rejects pending requests, ends announcements, and
1673/// makes subsequent producer mutations fail with [`Error::Closed`].
1674/// `moq_tokio::origin::spawn` handles construction and driving for Tokio callers.
1675#[must_use = "poll the driver or the origin makes no progress"]
1676pub struct Driver {
1677	state: DriverState,
1678	// Shared by this origin's lifecycle tasks; advanced only when polled.
1679	timers: Clock,
1680	// The cache pool this origin's groups charge into, swept on a wall-clock
1681	// cadence so its idle window binds a track whose publisher stopped writing.
1682	pool: cache::Pool,
1683}
1684
1685/// Lifecycle work and the state it tears down.
1686struct DriverState {
1687	/// The front drivers: producers submit, this polls.
1688	set: TaskSet,
1689	/// The route table, announce cursors, and the remotely-served fronts, for
1690	/// ending everything on drop.
1691	shared: kio::Shared<OriginState>,
1692	/// Cached completion so a poll after `Ready` doesn't re-poll the drained set.
1693	done: bool,
1694}
1695
1696impl Driver {
1697	/// Process ready origin work using caller-supplied monotonic time.
1698	///
1699	/// See [`crate::time::Driver`] for the contract. Finishes with
1700	/// [`Error::Closed`] once every producer handle has dropped and the
1701	/// remaining lifecycle work has drained.
1702	pub fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1703		self.timers.advance(now);
1704		let result = self.state.poll(waiter);
1705		let gc = self.pool.gc(now);
1706		if result.is_ready() {
1707			return Err(Error::Closed);
1708		}
1709		Ok(self.timers.timeout().into_iter().chain(gc).min())
1710	}
1711}
1712
1713impl crate::time::Driver for Driver {
1714	fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1715		self.poll(now, waiter)
1716	}
1717}
1718
1719impl DriverState {
1720	fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
1721		// Never gates completion: the pool outlives this origin (a relay shares one
1722		// across every origin), so a sweep that is still due must not keep the driver
1723		// alive after its lifecycle work has drained.
1724		if !self.done {
1725			ready!(self.set.poll(waiter));
1726			self.done = true;
1727		}
1728		Poll::Ready(())
1729	}
1730
1731	/// Tear the origin down: cancel the lifecycle work, abort and unpublish every
1732	/// front, retract every route, end announcement cursors, and reject pending
1733	/// requests.
1734	fn teardown(&mut self) {
1735		// Cancel queued and running lifecycle work first, so no front serves
1736		// while the table is ended below.
1737		drop(std::mem::replace(&mut self.set, TaskSet::owned()));
1738
1739		// Refuse new work and take the pending requests, under the same lock
1740		// `create_broadcast` holds across its attach: a concurrent create either
1741		// finishes before this (the walk below cleans its entry up) or observes
1742		// `closed` and fails with `Closed`.
1743		let (servers, cursors, fronts) = {
1744			let mut shared = self.shared.lock();
1745			shared.closed = true;
1746			// Fronts and parked requesters observe `closed` on their next pass.
1747			shared.routes.poke_all();
1748			let servers: Vec<_> = shared
1749				.routes
1750				.entries()
1751				.filter_map(|entry| entry.server.clone())
1752				.collect();
1753			let cursors: Vec<_> = shared.cursors.values().map(|cursor| cursor.state.clone()).collect();
1754			let fronts: Vec<_> = shared.fronts.values().map(|front| front.request.clone()).collect();
1755			(servers, cursors, fronts)
1756		};
1757
1758		// Reject requesters still parked on a remote front's channel: its watcher
1759		// was cancelled above and will never resolve them.
1760		for producer in fronts {
1761			if let Ok(mut request) = producer.write() {
1762				request.resolved.get_or_insert(Err(Error::Dropped));
1763			}
1764		}
1765		// Reject every pending route request, including those already handed to a
1766		// handler: the teardown is terminal, so a handler resolving late must not
1767		// beat it (resolution is first-write-wins).
1768		for server in servers {
1769			let mut server = server.lock();
1770			server.closed = true;
1771			for producer in server.requests.drain_all() {
1772				if let Ok(mut request) = producer.write() {
1773					request.resolved.get_or_insert(Err(Error::Dropped));
1774				}
1775			}
1776		}
1777
1778		// End the announce cursors: each drains its pending updates, then reports
1779		// the end. Registrations stay (the cursors remove themselves on drop).
1780		for state in cursors {
1781			if let Ok(mut state) = state.write() {
1782				state.ended = true;
1783			}
1784		}
1785	}
1786}
1787
1788impl Drop for DriverState {
1789	fn drop(&mut self) {
1790		self.teardown();
1791	}
1792}
1793
1794/// How long a spliced track stays warm after its last reader leaves.
1795///
1796/// Within the window a returning viewer, or the next of a run of back-to-back
1797/// fetches, reads the groups the front already cached: no second round trip for
1798/// `TRACK_INFO`. Groups past that cached edge cost a fresh source splice. After
1799/// the window, the cached segment is released.
1800///
1801/// Sized above the fetch cadence of a segmented consumer: HLS polls every
1802/// `TARGETDURATION` seconds, commonly 6 or 10, so a shorter window would drop the
1803/// copy between every segment and re-request the track each time. A warm copy
1804/// holds no upstream subscription (that is canceled as soon as demand ends), so
1805/// waiting longer costs cached state, not a viewer.
1806const TRACK_IDLE_LINGER: Duration = Duration::from_secs(30);
1807
1808/// A local copy of groups the front already delivered, so resume stays spliced
1809/// after the source track is dropped. Cache misses stay pending while demand
1810/// re-splices the upstream source. Finished on drop so an idle linger does not
1811/// warn about an abandoned producer.
1812struct WarmCopy {
1813	track: track::Producer,
1814	_dynamic: track::Dynamic,
1815	/// The newest group, where the copy spliced after this one picks up (see
1816	/// [`TrackIo::head`]).
1817	edge: Option<WarmGroup>,
1818}
1819
1820impl Drop for WarmCopy {
1821	fn drop(&mut self) {
1822		let _ = self.track.finish();
1823	}
1824}
1825
1826/// A warm copy's newest group, which the copy spliced after it continues.
1827///
1828/// Kept past that splice: an open one must stay open for the continuation (dropping
1829/// an unfinished producer clears its frames), and either kind supplies the head the
1830/// continuation lacks when the next park rebuilds the group. Nothing will ever finish
1831/// an open one, so it aborts on drop.
1832struct WarmGroup(group::Producer);
1833
1834impl Drop for WarmGroup {
1835	fn drop(&mut self) {
1836		if !self.0.is_finished() {
1837			let _ = self.0.clone().abort(Error::Cancel);
1838		}
1839	}
1840}
1841
1842/// Cache what `source` delivered on a new local track the origin owns: its complete
1843/// groups, and its open live edge rebuilt from the frames already delivered.
1844///
1845/// `head` is the previous park's edge. A copy spliced after a warm cache continues its
1846/// edge group from the next frame, so its copy of that group lacks the head, which
1847/// `head` supplies.
1848fn warm_copy(source: &track::Consumer, head: Option<&WarmGroup>) -> Option<WarmCopy> {
1849	let info = source.cached_info()?;
1850	let mut track = track::Producer::new(Arc::new(source.broadcast().clone()), source.name(), info);
1851	let head = head.map(|head| &head.0);
1852	let groups = source.cached_groups();
1853	// Not `source.latest()`: datagrams share the sequence counter and can run past it.
1854	let latest = groups
1855		.iter()
1856		.filter(|(_, visible)| *visible)
1857		.map(|(group, _)| group.sequence)
1858		.max();
1859	let mut edge = None;
1860
1861	// A spliced copy hides the group it continued (its halves sit in two segments), so
1862	// carry the previous edge over when the copy has no version of it at all. First,
1863	// since it arrived before anything the copy holds.
1864	if let Some(head) = head
1865		&& !groups.iter().any(|(group, _)| group.sequence == head.sequence)
1866	{
1867		let is_latest = latest.is_none_or(|latest| head.sequence >= latest);
1868		if head.is_finished() {
1869			let _ = track.adopt_group(head.clone(), true);
1870			if is_latest {
1871				edge = Some(WarmGroup(head.clone()));
1872			}
1873		} else if is_latest {
1874			edge = warm_rebuild(&track, head, None);
1875		}
1876	}
1877
1878	for (group, visible) in groups {
1879		let finished = group.is_finished();
1880		let whole = group.live_first_frame() == Some(0);
1881		let is_latest = visible && Some(group.sequence) == latest;
1882		let warm = if finished && whole {
1883			let _ = track.adopt_group(group.clone(), visible);
1884			Some(WarmGroup(group))
1885		} else if (finished || is_latest) && (whole || head.is_some_and(|head| head.sequence == group.sequence)) {
1886			// Dropping the source copy resets an open live edge mid-transfer, so rebuild
1887			// it from the frames already delivered: the re-splice asks for the next
1888			// frame, and a group that stays open for good (a JSON log in group 0)
1889			// continues instead of being re-sent whole on every resume. A continuation
1890			// is rebuilt whole from the previous edge's head the same way.
1891			warm_rebuild(&track, &group, head)
1892		} else {
1893			// Mid-transfer backlog, or a continuation with no head to complete it.
1894			None
1895		};
1896		if is_latest {
1897			edge = warm;
1898		}
1899	}
1900	let dynamic = track.dynamic();
1901	Some(WarmCopy {
1902		track,
1903		_dynamic: dynamic,
1904		edge,
1905	})
1906}
1907
1908/// Rebuild `live` on `track` from its delivered frames, prefixed by `head`'s when `live`
1909/// only holds a continuation of it. Finished like `live`, or left open.
1910fn warm_rebuild(track: &track::Producer, live: &group::Producer, head: Option<&group::Producer>) -> Option<WarmGroup> {
1911	// A continuation holds nothing below its offset.
1912	let mut start = live.live_first_frame()? as u64;
1913	let mut tail = live.consume();
1914	tail.start_at(start);
1915	let mut frames = Vec::new();
1916	if start > 0
1917		&& let Some(head) = head.filter(|head| head.sequence == live.sequence)
1918		&& let Some(head_start) = head.live_first_frame()
1919	{
1920		let mut head = head.consume();
1921		head.start_at(head_start as u64);
1922		while head.index() < start {
1923			match head.poll_read_frame(&kio::Waiter::noop()) {
1924				Poll::Ready(Ok(Some(frame))) => frames.push(frame),
1925				_ => break,
1926			}
1927		}
1928		match head.index() == start {
1929			true => start = head_start as u64,
1930			// The head doesn't reach the continuation: keep only the continuation.
1931			false => frames.clear(),
1932		}
1933	}
1934	while let Poll::Ready(Ok(Some(frame))) = tail.poll_read_frame(&kio::Waiter::noop()) {
1935		frames.push(frame);
1936	}
1937	if frames.is_empty() {
1938		return None;
1939	}
1940
1941	// Wrapped first, so a failed write aborts it rather than dropping it unfinished.
1942	let rebuilt = WarmGroup(
1943		track
1944			.create_group(group::Info {
1945				sequence: live.sequence,
1946			})
1947			.ok()?,
1948	);
1949	let mut writer = rebuilt.0.clone();
1950	if start > 0 {
1951		writer.start_at(start).ok()?;
1952	}
1953	for frame in frames {
1954		writer.write_frame(frame.timestamp, frame.payload).ok()?;
1955	}
1956	if live.is_finished() {
1957		writer.finish().ok()?;
1958	}
1959	Some(rebuilt)
1960}
1961
1962/// Everything [`run_front`] owns, queued by [`Consumer::request_broadcast`].
1963struct FrontTask {
1964	/// The route table the front selects from.
1965	shared: kio::Shared<OriginState>,
1966	/// The spliced broadcast the front serves.
1967	broadcast: broadcast::Producer,
1968	/// Absolute path of the front.
1969	path: PathOwned,
1970	/// The requesters' horizon, applied to every (re)selection.
1971	horizon: Horizon,
1972	/// Wakes the front when a route covering its path changes.
1973	watch: Watch,
1974	/// Resolves the requesters parked on the front's channel.
1975	request: kio::Producer<PendingBroadcast>,
1976	/// Published for requesters once the first source fixes it; see [`RemoteFront::pin`].
1977	pin: kio::Lock<Pin>,
1978	timers: Clock,
1979}
1980
1981/// The driver's side of one logical track: the handles behind the names the
1982/// machine uses.
1983struct TrackIo {
1984	resume: super::resume::Producer,
1985	/// The copy whose info resolved, waiting for the machine to splice it.
1986	staged: Option<(u64, track::Consumer)>,
1987	/// A query in flight: the source asked, its copy, and the pending info.
1988	query: Option<(u64, track::Consumer, track::Querying)>,
1989	/// The spliced copy: its source and the track.
1990	copy: Option<(u64, track::Consumer)>,
1991	/// The delivered edge when the copy spliced in: a copy that dies without
1992	/// advancing it delivered nothing. Snapshotted per splice, not per wake, so an
1993	/// unrelated wake between the copy's last frame and its death cannot launder
1994	/// its progress away.
1995	edge: Option<track::Position>,
1996	/// Delivered groups kept after the copy was dropped, so resume stays spliced
1997	/// through the linger without pinning the source as a reader.
1998	warm: Option<WarmCopy>,
1999	/// The last warm copy's newest group, outliving it so the copy spliced after it
2000	/// can continue the group (see [`WarmGroup`]). Released at the next park.
2001	head: Option<WarmGroup>,
2002	/// Whether the track had a reader as of the last demand edge.
2003	used: bool,
2004}
2005
2006/// Drives one front: feeds the world's events to a [`Front`] and performs the
2007/// actions it returns, until the front ends. The decisions live in the machine;
2008/// this only waits and executes, so nothing here decides anything twice.
2009async fn run_front(task: FrontTask) {
2010	let FrontTask {
2011		shared,
2012		broadcast,
2013		path,
2014		horizon,
2015		watch,
2016		request,
2017		pin,
2018		timers,
2019	} = task;
2020
2021	/// What the wait below returns: one thing that happened.
2022	enum Step {
2023		Assigned(Arc<str>, super::resume::Producer),
2024		Resolved(u64, Result<broadcast::Consumer, Error>),
2025		SourceClosed(u64),
2026		Info(Arc<str>, u64, Result<track::Info, Error>),
2027		Ended(Arc<str>, u64, Result<(), Error>),
2028		Demand(Arc<str>),
2029		Deadline,
2030		Table,
2031	}
2032
2033	let mut front = Front::new(TRACK_IDLE_LINGER);
2034	let mut sources: HashMap<u64, broadcast::Consumer> = HashMap::new();
2035	let mut next_source = 0u64;
2036	// The in-flight upstream request: the route and its pending channel.
2037	let mut upstream: Option<(u64, kio::Consumer<PendingBroadcast>)> = None;
2038	let mut tracks: HashMap<Arc<str>, TrackIo> = HashMap::new();
2039	let mut deadline = crate::runtime::Deadline::new(&timers);
2040	// The watch generation the last selection saw.
2041	let mut seen = 0;
2042	let mut events: VecDeque<Event> = VecDeque::new();
2043
2044	// Read the table for the machine: the best qualifying route and whether
2045	// the serving source is on its way out. Also what the watch wakes for.
2046	let select = |front: &mut Front, sources: &HashMap<u64, broadcast::Consumer>, seen: &mut u64| -> Event {
2047		let table = shared.read();
2048		if table.closed {
2049			return Event::Closed;
2050		}
2051		// Read alongside the decision, under the lock a poke takes first.
2052		*seen = watch.seen();
2053		front.retain_routes(|route| table.routes.covers(&path.as_path(), route));
2054		let best = table
2055			.best_route(&path.as_path(), horizon, front.pin(), front.refused_routes())
2056			.map(|entry| Candidate {
2057				route: entry.id,
2058				first: entry.hops.iter().next().copied(),
2059				local: entry.local,
2060			});
2061		let serving_closing = front
2062			.serving()
2063			.and_then(|id| sources.get(&id))
2064			.is_some_and(|source| source.is_closing());
2065		Event::Selected { best, serving_closing }
2066	};
2067
2068	events.push_back(select(&mut front, &sources, &mut seen));
2069
2070	loop {
2071		while let Some(event) = events.pop_front() {
2072			for action in front.step(event) {
2073				match action {
2074					Action::Reselect => events.push_back(select(&mut front, &sources, &mut seen)),
2075					Action::Request { route } => {
2076						// The entry, its identity for the front, and what it serves.
2077						let found = {
2078							let table = shared.read();
2079							table
2080								.routes
2081								.covering(&path.as_path())
2082								.find(|entry| entry.id == route)
2083								.map(|entry| {
2084									(
2085										Candidate {
2086											route,
2087											first: entry.hops.iter().next().copied(),
2088											local: entry.local,
2089										},
2090										entry.source.clone(),
2091										entry.server.clone(),
2092									)
2093								})
2094						};
2095						let Some((candidate, source, server)) = found else {
2096							events.push_back(Event::Resolved {
2097								route,
2098								result: Err(Refusal {
2099									err: Error::Unroutable,
2100									standing: false,
2101								}),
2102							});
2103							continue;
2104						};
2105						front.identify(candidate);
2106						*pin.lock() = front.pin();
2107						if let Some(source) = source {
2108							let id = next_source;
2109							next_source += 1;
2110							sources.insert(id, source);
2111							events.push_back(Event::Resolved { route, result: Ok(id) });
2112							continue;
2113						}
2114						let Some(server) = server else {
2115							events.push_back(Event::Resolved {
2116								route,
2117								result: Err(Refusal {
2118									err: Error::Unroutable,
2119									standing: true,
2120								}),
2121							});
2122							continue;
2123						};
2124						let mut serve = server.lock();
2125						if serve.closed {
2126							// Retracted under us, or its handler dropped while the
2127							// announcement stands: it cannot serve.
2128							drop(serve);
2129							events.push_back(Event::Resolved {
2130								route,
2131								result: Err(Refusal {
2132									err: Error::Unroutable,
2133									standing: true,
2134								}),
2135							});
2136							continue;
2137						}
2138						// A source this route already materialized for the path
2139						// attaches without another upstream round trip.
2140						if let Some(weak) = serve.served.get(&path) {
2141							drop(serve);
2142							let id = next_source;
2143							next_source += 1;
2144							sources.insert(id, weak.consume());
2145							events.push_back(Event::Resolved { route, result: Ok(id) });
2146							continue;
2147						}
2148						let pending = match serve.requests.join(&path) {
2149							Some(producer) => producer.consume(),
2150							None => {
2151								let producer = kio::Producer::<PendingBroadcast>::default();
2152								let consumer = producer.consume();
2153								match serve.requests.insert(path.clone(), producer) {
2154									Ok(()) => consumer,
2155									// No live handler behind the route: it cannot
2156									// serve, whatever the table says.
2157									Err(_) => {
2158										drop(serve);
2159										events.push_back(Event::Resolved {
2160											route,
2161											result: Err(Refusal {
2162												err: Error::Unroutable,
2163												standing: true,
2164											}),
2165										});
2166										continue;
2167									}
2168								}
2169							}
2170						};
2171						upstream = Some((route, pending));
2172					}
2173					Action::Detach { source } => {
2174						sources.remove(&source);
2175						// Its copies go with it; the segments they delivered stay
2176						// spliced until a replacement resumes past them.
2177						for io in tracks.values_mut() {
2178							if io.copy.as_ref().is_some_and(|(s, _)| *s == source) {
2179								io.copy = None;
2180							}
2181							if io.query.as_ref().is_some_and(|(s, ..)| *s == source) {
2182								io.query = None;
2183							}
2184							if io.staged.as_ref().is_some_and(|(s, _)| *s == source) {
2185								io.staged = None;
2186							}
2187						}
2188					}
2189					Action::Resolve => {
2190						if let Ok(mut pending) = request.write() {
2191							pending.resolved.get_or_insert(Ok(broadcast.consume()));
2192						}
2193					}
2194					Action::Query { track: name, source } => {
2195						let Some(io) = tracks.get_mut(&name) else { continue };
2196						let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2197						match sources.get(&source).map(|s| s.track(&name)) {
2198							Some(Ok(copy)) => {
2199								// `into_inner` sheds the `Pending` future wrapper so only
2200								// the pollable (which is `Sync`) is held across the wait.
2201								let query = copy.query().into_inner();
2202								io.query = Some((source, copy, query));
2203							}
2204							Some(Err(err)) => events.push_back(Event::TrackInfo {
2205								track: name,
2206								source,
2207								closing,
2208								result: Err(err),
2209							}),
2210							None => {}
2211						}
2212					}
2213					Action::Splice { track: name, source } => {
2214						let Some(io) = tracks.get_mut(&name) else { continue };
2215						let Some((staged, copy)) = io.staged.take() else {
2216							continue;
2217						};
2218						if staged != source {
2219							continue;
2220						}
2221						if let Err(err) = io.resume.takeover(&copy) {
2222							// Closed means the logical track already ended. Anything
2223							// else is a boundary bug; abort rather than strand
2224							// subscribers on a track nobody serves.
2225							let _ = io.resume.abort(err);
2226							tracks.remove(&name);
2227							continue;
2228						}
2229						io.head = io.warm.take().and_then(|mut warm| warm.edge.take());
2230						// The new segment has produced nothing yet: this is the
2231						// edge the copy is asked to advance.
2232						io.edge = io.resume.resume_position();
2233						io.copy = Some((source, copy));
2234					}
2235					Action::Park { track: name } => {
2236						let Some(io) = tracks.get_mut(&name) else { continue };
2237						let Some((_, copy)) = io.copy.take() else { continue };
2238						// Drop the source copy so its producer goes idle at once; keep
2239						// the groups it delivered on a local track so resume stays
2240						// spliced until the linger expires.
2241						let warm = warm_copy(&copy, io.head.as_ref());
2242						drop(copy);
2243						io.head = None;
2244						let parked = match &warm {
2245							Some(warm) => io.resume.park(&warm.track),
2246							None => io.resume.release(),
2247						};
2248						if parked.is_err() {
2249							tracks.remove(&name);
2250							continue;
2251						}
2252						io.warm = warm;
2253					}
2254					Action::Release { track: name } => {
2255						let Some(io) = tracks.get_mut(&name) else { continue };
2256						// A local source releases straight from the spliced copy.
2257						io.copy = None;
2258						io.warm = None;
2259						io.head = None;
2260						if io.resume.release().is_err() {
2261							tracks.remove(&name);
2262						}
2263					}
2264					Action::Finish { track: name } => {
2265						if let Some(mut io) = tracks.remove(&name) {
2266							let _ = io.resume.finish();
2267						}
2268					}
2269					Action::Abort { track: name, err } => {
2270						if let Some(mut io) = tracks.remove(&name) {
2271							tracing::debug!(name = %name, %err, "aborting track");
2272							let _ = io.resume.abort(err);
2273						}
2274					}
2275					Action::Arm { at } => deadline.set(at),
2276					Action::End { err } => {
2277						if let Ok(mut pending) = request.write() {
2278							pending.resolved.get_or_insert(Err(err.clone()));
2279						}
2280						// Ending the broadcast only retracts it: no new requesters or
2281						// tracks, and a newcomer at the path gets a fresh front. Tracks
2282						// in flight carry on (moq-lite: retraction does not disturb
2283						// subscriptions already in flight): dropping their producers
2284						// leaves each reader on the copy it was spliced from, ending
2285						// when and as that copy ends.
2286						broadcast.close();
2287						broadcast.release_spliced(err.clone());
2288						for (_, mut io) in tracks.drain() {
2289							// A reader still waiting on its source's answer is in flight
2290							// too: splice the copy it asked, past any warm cache, so it
2291							// ends as that copy does.
2292							let waiting = io.staged.take().map(|(_, copy)| copy);
2293							let waiting = waiting.or_else(|| io.query.take().map(|(_, copy, _)| copy));
2294							if let Some(copy) = waiting
2295								&& io.resume.is_used()
2296							{
2297								if io.resume.takeover(&copy).is_err() {
2298									continue;
2299								}
2300								io.warm = None;
2301							}
2302							// Nothing in flight: unread, never spliced, or only a warm cache.
2303							if !io.resume.is_used() || !io.resume.is_spliced() || io.warm.is_some() {
2304								let _ = io.resume.abort(err.clone());
2305							}
2306						}
2307						return;
2308					}
2309				}
2310			}
2311		}
2312
2313		let step = kio::wait(|waiter| {
2314			if let Poll::Ready((name, resume)) = broadcast.poll_spliced_assigned(waiter) {
2315				return Poll::Ready(Step::Assigned(name, resume));
2316			}
2317			if let Some((route, pending)) = &upstream
2318				&& let Poll::Ready(result) = pending.poll(waiter, |p| match &p.resolved {
2319					Some(result) => Poll::Ready(result.clone()),
2320					None => Poll::Pending,
2321				}) {
2322				return Poll::Ready(Step::Resolved(
2323					*route,
2324					match result {
2325						Ok(resolved) => resolved,
2326						// The queue died unresolved (its handler dropped): the route
2327						// could not serve.
2328						Err(_closed) => Err(Error::Unroutable),
2329					},
2330				));
2331			}
2332			if let Some(id) = front.serving()
2333				&& let Some(source) = sources.get(&id)
2334				&& source.poll_closed(waiter).is_ready()
2335			{
2336				return Poll::Ready(Step::SourceClosed(id));
2337			}
2338			for (name, io) in &tracks {
2339				if let Some((source, _, query)) = &io.query
2340					&& let Poll::Ready(result) = query.poll(waiter)
2341				{
2342					return Poll::Ready(Step::Info(name.clone(), *source, result));
2343				}
2344				if let Some((source, copy)) = &io.copy
2345					&& let Poll::Ready(result) = copy.poll_complete(waiter)
2346				{
2347					return Poll::Ready(Step::Ended(name.clone(), *source, result));
2348				}
2349				// Watch the demand edge in whichever direction is unmet.
2350				let edge = match io.used {
2351					true => io.resume.poll_unused(waiter),
2352					false => io.resume.poll_used(waiter),
2353				};
2354				if edge.is_ready() {
2355					return Poll::Ready(Step::Demand(name.clone()));
2356				}
2357			}
2358			if deadline.poll(waiter).is_ready() {
2359				return Poll::Ready(Step::Deadline);
2360			}
2361			watch.poll_changed(waiter, seen).map(|()| Step::Table)
2362		})
2363		.await;
2364
2365		let event = match step {
2366			Step::Assigned(name, resume) => {
2367				tracks.insert(
2368					name.clone(),
2369					TrackIo {
2370						resume,
2371						staged: None,
2372						query: None,
2373						copy: None,
2374						edge: None,
2375						warm: None,
2376						head: None,
2377						used: false,
2378					},
2379				);
2380				Event::TrackAssigned { track: name }
2381			}
2382			Step::Resolved(route, result) => {
2383				upstream = None;
2384				match result {
2385					Ok(source) => {
2386						let id = next_source;
2387						next_source += 1;
2388						sources.insert(id, source);
2389						Event::Resolved { route, result: Ok(id) }
2390					}
2391					Err(err) => {
2392						// A retraction and a handler's rejection resolve alike, so
2393						// the table tells them apart: an `Unroutable` from a route
2394						// that still stands is the handler's answer.
2395						let standing =
2396							!matches!(err, Error::Unroutable) || shared.read().routes.covers(&path.as_path(), route);
2397						Event::Resolved {
2398							route,
2399							result: Err(Refusal { err, standing }),
2400						}
2401					}
2402				}
2403			}
2404			Step::SourceClosed(source) => Event::SourceClosed { source },
2405			Step::Info(name, source, result) => {
2406				let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2407				let Some(io) = tracks.get_mut(&name) else { continue };
2408				let Some((_, copy, _)) = io.query.take() else { continue };
2409				// A copy that is already aborted cannot be spliced; its error is
2410				// the source's answer for the track.
2411				let result = match result {
2412					Ok(info) => match copy.poll_complete(&kio::Waiter::noop()) {
2413						Poll::Ready(Err(err)) => Err(err),
2414						_ => Ok(info),
2415					},
2416					Err(err) => Err(err),
2417				};
2418				// Staged only while the track has a reader: without one the machine
2419				// will not splice, and a held copy would keep the source subscribed.
2420				if result.is_ok() && io.used {
2421					io.staged = Some((source, copy));
2422				}
2423				Event::TrackInfo {
2424					track: name,
2425					source,
2426					closing,
2427					result,
2428				}
2429			}
2430			Step::Ended(name, source, result) => {
2431				let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2432				let Some(io) = tracks.get_mut(&name) else { continue };
2433				io.copy = None;
2434				let delivered = io.resume.resume_position() != io.edge;
2435				Event::TrackEnded {
2436					track: name,
2437					source,
2438					closing,
2439					result,
2440					delivered,
2441				}
2442			}
2443			Step::Demand(name) => {
2444				let Some(io) = tracks.get_mut(&name) else { continue };
2445				io.used = io.resume.is_used();
2446				if !io.used {
2447					// Nothing will be spliced now: let go of the copies a query
2448					// holds, or the source stays subscribed with nobody reading.
2449					io.query = None;
2450					io.staged = None;
2451				}
2452				match io.used {
2453					true => Event::Used { track: name },
2454					false => Event::Unused {
2455						track: name,
2456						now: timers.now(),
2457					},
2458				}
2459			}
2460			Step::Deadline => {
2461				// Cleared here so a fired deadline cannot keep firing; the machine
2462				// re-arms what is still parked.
2463				deadline.set(None);
2464				Event::Deadline { now: timers.now() }
2465			}
2466			Step::Table => select(&mut front, &sources, &mut seen),
2467		};
2468		events.push_back(event);
2469	}
2470}
2471
2472/// The announced routes, keyed by prefix: a trie with one node per path
2473/// segment. Every question about a path walks its segments, so the cost of an
2474/// announcement, a cursor registration, or a request is bounded by the tree
2475/// around that path and never by the size of the table.
2476#[derive(Default)]
2477struct RouteTable {
2478	root: RouteNode,
2479}
2480
2481/// One prefix in the [`RouteTable`]: what is announced exactly there, which
2482/// cursors hang there, and the prefixes one segment below.
2483#[derive(Default)]
2484struct RouteNode {
2485	/// Routes announced exactly at this prefix.
2486	entries: Vec<RouteEntry>,
2487	/// Cursors with an interest head at this prefix (see [`interest_prefixes`]).
2488	cursors: Vec<ConsumerId>,
2489	/// Cursors at this node or below. An announcement walks only the subtrees
2490	/// that hold one, so a deep table of routes nobody watches costs nothing.
2491	cursors_below: usize,
2492	/// Who is waiting on the routes covering this prefix: the fronts serving it
2493	/// and the requesters parked on it (see [`Watch`]).
2494	watches: Vec<(u64, kio::Producer<Watched>)>,
2495	/// Watches at this node or below, so a route change walks only the subtrees
2496	/// holding one.
2497	watches_below: usize,
2498	children: HashMap<String, RouteNode>,
2499}
2500
2501/// What a [`Watch`] observes: bumped by every change to a route covering its
2502/// path (a broadcast published here is one) and by the origin's teardown.
2503#[derive(Default)]
2504struct Watched {
2505	generation: u64,
2506}
2507
2508/// A registration in the route table for changes to the routes covering one
2509/// path. The table pokes it; the holder waits on it, so an announcement wakes
2510/// only the fronts and requesters it can affect rather than every one of them.
2511/// Dropping it unregisters, which takes the table lock: never drop one while
2512/// holding it.
2513struct Watch {
2514	shared: kio::Shared<OriginState>,
2515	path: PathOwned,
2516	id: u64,
2517	signal: kio::Consumer<Watched>,
2518}
2519
2520impl Watch {
2521	/// The generation to wait past with [`Self::poll_changed`]. Read under the
2522	/// table lock, alongside the decision it guards, so a poke between the two
2523	/// cannot be missed: a poke takes that same lock first.
2524	fn seen(&self) -> u64 {
2525		self.signal.read().generation
2526	}
2527
2528	/// Ready once the routes covering the path moved past `seen`.
2529	fn poll_changed(&self, waiter: &kio::Waiter, seen: u64) -> Poll<()> {
2530		self.signal
2531			.poll(waiter, |watched| match watched.generation != seen {
2532				true => Poll::Ready(()),
2533				false => Poll::Pending,
2534			})
2535			.map(|_| ())
2536	}
2537}
2538
2539impl Drop for Watch {
2540	fn drop(&mut self) {
2541		self.shared.lock().routes.remove_watch(&self.path, self.id);
2542	}
2543}
2544
2545/// What a registration adds to the subtree counts on its walk.
2546#[derive(Clone, Copy)]
2547struct Below {
2548	cursors: usize,
2549	watches: usize,
2550}
2551
2552impl Below {
2553	const NONE: Self = Self { cursors: 0, watches: 0 };
2554	const CURSOR: Self = Self { cursors: 1, watches: 0 };
2555	const WATCH: Self = Self { cursors: 0, watches: 1 };
2556}
2557
2558impl RouteNode {
2559	/// Nothing here and nothing below: the node can be pruned.
2560	fn is_empty(&self) -> bool {
2561		self.entries.is_empty() && self.cursors.is_empty() && self.watches.is_empty() && self.children.is_empty()
2562	}
2563
2564	/// The node `parts` below this one, if the table has it.
2565	fn find<'a>(&self, mut parts: impl Iterator<Item = &'a str>) -> Option<&Self> {
2566		match parts.next() {
2567			None => Some(self),
2568			Some(part) => self.children.get(part)?.find(parts),
2569		}
2570	}
2571
2572	/// The node `parts` below this one, created along the way when missing.
2573	/// `below` is added to the subtree counts at every node on the walk.
2574	fn reach<'a>(&mut self, mut parts: impl Iterator<Item = &'a str>, below: Below) -> &mut Self {
2575		self.cursors_below += below.cursors;
2576		self.watches_below += below.watches;
2577		match parts.next() {
2578			None => self,
2579			Some(part) => self.children.entry(part.to_string()).or_default().reach(parts, below),
2580		}
2581	}
2582
2583	/// Run `f` on the node `parts` below this one, then prune every node the
2584	/// edit emptied. `below` is subtracted from the subtree counts at every node
2585	/// on the walk. `None` when the node does not exist, leaving the table as is.
2586	fn edit<'a, R>(
2587		&mut self,
2588		mut parts: impl Iterator<Item = &'a str>,
2589		below: Below,
2590		f: impl FnOnce(&mut Self) -> R,
2591	) -> Option<R> {
2592		let result = match parts.next() {
2593			None => f(self),
2594			Some(part) => {
2595				let child = self.children.get_mut(part)?;
2596				let result = child.edit(parts, below, f)?;
2597				if child.is_empty() {
2598					self.children.remove(part);
2599				}
2600				result
2601			}
2602		};
2603		self.cursors_below -= below.cursors;
2604		self.watches_below -= below.watches;
2605		Some(result)
2606	}
2607
2608	/// Wake the watches at this node.
2609	fn poke(&self) {
2610		for (_, watch) in &self.watches {
2611			if let Ok(mut watched) = watch.write() {
2612				watched.generation += 1;
2613			}
2614		}
2615	}
2616
2617	/// Wake the watches at this node and below: a route here covers every one
2618	/// of their paths. Skips subtrees holding none.
2619	fn poke_below(&self) {
2620		if self.watches_below == 0 {
2621			return;
2622		}
2623		self.poke();
2624		for child in self.children.values() {
2625			child.poke_below();
2626		}
2627	}
2628
2629	/// Visit this node and everything below it.
2630	fn walk<'a>(&'a self, visit: &mut impl FnMut(&'a Self)) {
2631		visit(self);
2632		for child in self.children.values() {
2633			child.walk(visit);
2634		}
2635	}
2636
2637	/// Collect the cursors at this node and below, skipping subtrees with none.
2638	fn collect_cursors(&self, out: &mut Vec<ConsumerId>) {
2639		if self.cursors_below == 0 {
2640			return;
2641		}
2642		out.extend(&self.cursors);
2643		for child in self.children.values() {
2644			child.collect_cursors(out);
2645		}
2646	}
2647}
2648
2649impl RouteTable {
2650	/// The nodes above `path` and the node at it, as far as the table has them.
2651	/// The entries of those nodes are exactly the routes covering `path`.
2652	fn split(&self, path: &Path) -> (Vec<&RouteNode>, Option<&RouteNode>) {
2653		let mut above = Vec::new();
2654		let mut node = &self.root;
2655		for part in path.parts() {
2656			above.push(node);
2657			match node.children.get(part) {
2658				Some(child) => node = child,
2659				None => return (above, None),
2660			}
2661		}
2662		(above, Some(node))
2663	}
2664
2665	/// The routes covering `path`: those announced at it and at every prefix of it.
2666	fn covering(&self, path: &Path) -> impl Iterator<Item = &RouteEntry> {
2667		let (above, at) = self.split(path);
2668		above.into_iter().chain(at).flat_map(|node| node.entries.iter())
2669	}
2670
2671	/// Whether the route `id` still covers `path`.
2672	fn covers(&self, path: &Path, id: u64) -> bool {
2673		self.covering(path).any(|entry| entry.id == id)
2674	}
2675
2676	/// The routes announced exactly at `prefix`.
2677	fn at(&self, prefix: &Path) -> impl Iterator<Item = &RouteEntry> {
2678		self.root
2679			.find(prefix.parts())
2680			.into_iter()
2681			.flat_map(|node| node.entries.iter())
2682	}
2683
2684	/// Every route in the table, for the teardown.
2685	fn entries(&self) -> impl Iterator<Item = &RouteEntry> {
2686		let mut nodes = Vec::new();
2687		self.root.walk(&mut |node| nodes.push(node));
2688		nodes.into_iter().flat_map(|node| node.entries.iter())
2689	}
2690
2691	/// Add a route at its prefix, creating the nodes down to it.
2692	fn insert(&mut self, entry: RouteEntry) {
2693		let node = self.root.reach(entry.prefix.parts(), Below::NONE);
2694		node.entries.push(entry);
2695	}
2696
2697	/// The route `id` announced at `prefix`, for a re-price in place.
2698	fn entry_mut(&mut self, prefix: &Path, id: u64) -> Option<&mut RouteEntry> {
2699		let mut node = &mut self.root;
2700		for part in prefix.parts() {
2701			node = node.children.get_mut(part)?;
2702		}
2703		node.entries.iter_mut().find(|entry| entry.id == id)
2704	}
2705
2706	/// Take the route `id` out of `prefix`, pruning the nodes it leaves empty.
2707	fn remove(&mut self, prefix: &Path, id: u64) -> Option<RouteEntry> {
2708		self.root
2709			.edit(prefix.parts(), Below::NONE, |node| {
2710				let index = node.entries.iter().position(|entry| entry.id == id)?;
2711				Some(node.entries.swap_remove(index))
2712			})
2713			.flatten()
2714	}
2715
2716	/// Hang a cursor at one of its heads, counting it down the walk.
2717	fn add_cursor(&mut self, head: &Path, id: ConsumerId) {
2718		self.root.reach(head.parts(), Below::CURSOR).cursors.push(id);
2719	}
2720
2721	/// Take a cursor off one of its heads, pruning the nodes it leaves empty. Only
2722	/// ever called for a head the cursor was added at, or the counts drift.
2723	fn remove_cursor(&mut self, head: &Path, id: ConsumerId) {
2724		self.root.edit(head.parts(), Below::CURSOR, |node| {
2725			node.cursors.retain(|cursor| *cursor != id)
2726		});
2727	}
2728
2729	/// Register a watch on the routes covering `path`; see [`Watch`].
2730	fn add_watch(&mut self, path: &Path, id: u64) -> kio::Consumer<Watched> {
2731		let producer = kio::Producer::<Watched>::default();
2732		let consumer = producer.consume();
2733		self.root.reach(path.parts(), Below::WATCH).watches.push((id, producer));
2734		consumer
2735	}
2736
2737	/// Take a watch off its path, pruning the nodes it leaves empty. Only ever
2738	/// called for a path the watch was added at, or the counts drift.
2739	fn remove_watch(&mut self, path: &Path, id: u64) {
2740		self.root.edit(path.parts(), Below::WATCH, |node| {
2741			node.watches.retain(|(watch, _)| *watch != id)
2742		});
2743	}
2744
2745	/// Wake the watches of every path a route at `prefix` covers.
2746	fn poke_below(&self, prefix: &Path) {
2747		if let (_, Some(node)) = self.split(prefix) {
2748			node.poke_below();
2749		}
2750	}
2751
2752	/// Wake every watch: the origin is tearing down.
2753	fn poke_all(&self) {
2754		self.root.walk(&mut |node| node.poke());
2755	}
2756
2757	/// The cursors a route at `prefix` can present on: a cursor sees a route
2758	/// only when one of its heads is on the walk down to the prefix or somewhere
2759	/// beneath it, so those are the only cursors visited.
2760	fn cursors_touching(&self, prefix: &Path) -> Vec<ConsumerId> {
2761		let (above, at) = self.split(prefix);
2762		let mut cursors: Vec<ConsumerId> = above.iter().flat_map(|node| node.cursors.iter().copied()).collect();
2763		if let Some(node) = at {
2764			node.collect_cursors(&mut cursors);
2765		}
2766		// A cursor with several heads can be reached more than once.
2767		cursors.sort_unstable();
2768		cursors.dedup();
2769		cursors
2770	}
2771}
2772
2773/// The origin's shared state: the route table, the announce cursors observing
2774/// it, and the remotely-served fronts.
2775///
2776/// Carried in a [`kio::Shared`], so producers, consumers, and handlers work
2777/// under one lock. Broadcasts published here are route table entries like the
2778/// routes announced from elsewhere; this holds everything that serves a path.
2779#[derive(Default)]
2780struct OriginState {
2781	// The announced routes, keyed by prefix. The table holds one entry per live
2782	// advertisement, not one per broadcast consumer.
2783	routes: RouteTable,
2784	next_route: u64,
2785	next_watch: u64,
2786
2787	// The registered announce cursors, each with its own coalescing buffer. Each
2788	// also hangs in the route table at its heads, which is how an announcement
2789	// finds the cursors it can present on.
2790	cursors: HashMap<ConsumerId, TableCursor>,
2791
2792	// The remotely-served fronts, keyed by absolute path and the requester's
2793	// split-horizon exclusion. Each is a spliced broadcast whose watcher task
2794	// materializes it from the best covering route and re-splices it through
2795	// routes sharing its first hop, so a route change the identity survives is
2796	// invisible to subscribers. Keyed per exclusion so a front's failover can
2797	// never adopt a route flowing back through one of its own readers. Weak, so
2798	// a front dies with its watcher and a later request re-creates it.
2799	fronts: WeakCache<FrontKey, RemoteFront>,
2800
2801	// Set when the origin's driver dropped: new requests fail with `Closed`
2802	// immediately and handlers observe the end instead of parking forever.
2803	closed: bool,
2804}
2805
2806impl OriginState {
2807	/// Re-deliver the best route at every presented prefix `prefix` maps to, on
2808	/// every cursor it can present on. Called after an entry covering `prefix`
2809	/// was added, updated, or removed. `claim` is `prefix`'s [`prefix_claim`],
2810	/// held by the entry that changed.
2811	fn sync_route(&mut self, prefix: &Path, claim: &Pattern) {
2812		// Split borrows: the recompute reads `routes` while mutating a cursor.
2813		let routes = &self.routes;
2814		for id in routes.cursors_touching(prefix) {
2815			let Some(cursor) = self.cursors.get_mut(&id) else {
2816				continue;
2817			};
2818			if let Some(presented) = cursor.presented(prefix, claim) {
2819				Self::sync_cursor(routes, cursor, &presented);
2820			}
2821		}
2822		// The fronts and requesters under the prefix re-select from the table.
2823		routes.poke_below(prefix);
2824	}
2825
2826	/// Register a [`Watch`] on the routes covering `path`.
2827	fn watch(&mut self, shared: &kio::Shared<OriginState>, path: &Path) -> Watch {
2828		let id = self.next_watch;
2829		self.next_watch += 1;
2830		let signal = self.routes.add_watch(path, id);
2831		Watch {
2832			shared: shared.clone(),
2833			path: path.to_owned(),
2834			id,
2835			signal,
2836		}
2837	}
2838
2839	/// Recompute the best visible route presenting at `presented` (relative) for
2840	/// one cursor and deliver the change, if any.
2841	fn sync_cursor(routes: &RouteTable, cursor: &mut TableCursor, presented: &PathOwned) {
2842		// The entries presenting here are the ones announced at the absolute
2843		// prefix, or, for the cursor's own root, at the root and every prefix
2844		// above it (all of which present as the empty path). Among them, the
2845		// longest prefix wins outright, so the metadata a cursor advertises
2846		// matches what a request through it actually resolves.
2847		let candidates: Vec<&RouteEntry> = match presented.is_empty() {
2848			true => routes
2849				.covering(&cursor.root)
2850				.filter(|entry| cursor.visible(entry))
2851				.collect(),
2852			false => {
2853				let absolute = cursor.root.join(presented);
2854				routes.at(&absolute).filter(|entry| cursor.visible(entry)).collect()
2855			}
2856		};
2857		let most = candidates.iter().map(|entry| entry.prefix.len()).max();
2858		let best = most.and_then(|most| {
2859			candidates
2860				.into_iter()
2861				.filter(|entry| entry.prefix.len() == most)
2862				.min_by_key(|entry| route_order(&entry.prefix, entry))
2863		});
2864
2865		match best {
2866			Some(entry) => {
2867				let meta = (entry.hops.clone(), entry.cost, entry.entered());
2868				let served = entry.server.is_some();
2869				let captures = cursor.captures(&entry.prefix);
2870				let previous = cursor
2871					.current
2872					.insert(presented.clone(), (entry.id, meta.clone(), served, captures.clone()));
2873				match previous {
2874					// Unchanged metadata and servability: nothing the consumer could
2875					// act on, even if the winning entry itself changed (a reconnect
2876					// under an identical route is invisible, which is the point). A
2877					// servability flip is delivered: a request that failed Unroutable
2878					// under an advertise-only route retries on the update, and hiding
2879					// it would park that waiter forever.
2880					Some((_, prev, prev_served, prev_captures))
2881						if prev == meta && prev_served == served && prev_captures == captures => {}
2882					// Captures are consumer identity, not route metadata. Replace the
2883					// old identity explicitly so capture-keyed consumers can remove it.
2884					Some((_, prev, _, prev_captures)) if prev_captures != captures => {
2885						if let Ok(mut state) = cursor.state.write() {
2886							state.apply_unannounce(presented.clone(), prev, prev_captures);
2887							state.apply_announce(presented.clone(), meta, captures);
2888						}
2889					}
2890					_ => {
2891						if let Ok(mut state) = cursor.state.write() {
2892							state.apply_announce(presented.clone(), meta, captures);
2893						}
2894					}
2895				}
2896			}
2897			None => {
2898				if let Some((_, last, _, captures)) = cursor.current.remove(presented)
2899					&& let Ok(mut state) = cursor.state.write()
2900				{
2901					state.apply_unannounce(presented.clone(), last, captures);
2902				}
2903			}
2904		}
2905	}
2906
2907	/// Register a cursor and replay the current best route per presented prefix.
2908	fn register_cursor(&mut self, id: ConsumerId, mut cursor: TableCursor) {
2909		// The routes a cursor can see sit on the walk down to one of its heads or
2910		// somewhere beneath it, so only those subtrees are replayed.
2911		let mut presented: BTreeSet<PathOwned> = BTreeSet::new();
2912		for head in &cursor.heads {
2913			let (above, at) = self.routes.split(head);
2914			let mut nodes = above;
2915			if let Some(node) = at {
2916				node.walk(&mut |node| nodes.push(node));
2917			}
2918			for entry in nodes.into_iter().flat_map(|node| node.entries.iter()) {
2919				if let Some(p) = cursor.presented(&entry.prefix, &entry.claim) {
2920					presented.insert(p);
2921				}
2922			}
2923		}
2924		for p in &presented {
2925			Self::sync_cursor(&self.routes, &mut cursor, p);
2926		}
2927		for head in &cursor.heads {
2928			self.routes.add_cursor(head, id);
2929		}
2930		self.cursors.insert(id, cursor);
2931	}
2932
2933	/// The best served route covering `path` (absolute) for a requester seeing
2934	/// `horizon`, skipping the `refused` entry ids.
2935	///
2936	/// The most specific covering prefix wins outright, so a narrow advertise-only
2937	/// announcement shadows a broad served one: requests under it resolve
2938	/// unroutable instead of being routed around it. Among routes at the winning
2939	/// prefix, the cheapest served one is picked by [`route_order`].
2940	///
2941	/// Only announced routes are candidates: an unannounced broadcast serves
2942	/// nobody, and does not shadow anything either. `pin` is the front's
2943	/// identity: only routes it admits are candidates, since a route from anyone
2944	/// else is different content rather than an alternate path (see [`Front`]).
2945	/// A broadcast published on this origin competes on cost like any other
2946	/// route and wins a tie.
2947	fn best_route(&self, path: &Path, horizon: Horizon, pin: Pin, refused: &HashSet<u64>) -> Option<&RouteEntry> {
2948		// Covering prefixes of one path form a chain, so the deepest node with a
2949		// candidate holds the unique longest prefix; walking down, the last such
2950		// node decides.
2951		let (above, at) = self.routes.split(path);
2952		let mut best = None;
2953		for node in above.into_iter().chain(at) {
2954			let mut candidates = node
2955				.entries
2956				.iter()
2957				.filter(|entry| entry.advertised)
2958				.filter(|entry| entry.scope.matches(path.as_str()))
2959				.filter(|entry| horizon.admits(entry))
2960				.filter(|entry| entry.qualifies(pin))
2961				.filter(|entry| !refused.contains(&entry.id))
2962				.peekable();
2963			if candidates.peek().is_some() {
2964				best = candidates
2965					.filter(|entry| entry.serves(path))
2966					.min_by_key(|entry| route_order(&entry.prefix, entry));
2967			}
2968		}
2969		best
2970	}
2971}
2972
2973/// One-shot result of a dynamic broadcast request.
2974///
2975/// Stays `None` until a handler [`accept`](Request::accept)s (yielding the served
2976/// broadcast) or [`reject`](Request::reject)s (yielding an error). The producer is
2977/// dropped right after writing, closing the channel; kio checks the value before the closed
2978/// flag, so an awaiting requester still observes the final result.
2979#[derive(Default)]
2980struct PendingBroadcast {
2981	resolved: Option<Result<broadcast::Consumer, Error>>,
2982}
2983
2984/// A served route, from [`Producer::dynamic`]: advertises a path prefix and
2985/// answers the [`Consumer::request_broadcast`] calls beneath it.
2986///
2987/// The origin-level analogue of [`broadcast::Dynamic`]: where that serves tracks
2988/// on demand within a broadcast, this serves whole broadcasts on demand within
2989/// an origin. A relay holds one per route a peer announces to it, materializing
2990/// a requested path from that peer; an application holds one to answer a
2991/// subtree it never publishes ahead of time.
2992///
2993/// Drop it to retract the route and reject the requests still waiting to be
2994/// served; [`update`](Self::update) re-prices it in place.
2995#[must_use = "dropping an origin::Dynamic retracts the route"]
2996pub struct Dynamic {
2997	/// The advertisement, retracted on drop.
2998	announcement: AnnounceProducer,
2999	state: kio::Shared<ServeState>,
3000}
3001
3002impl Dynamic {
3003	/// Re-price the route in place: replace its hops and cost.
3004	///
3005	/// Consumers observe another active update for the same prefix; sessions
3006	/// forward it as a restart, so route churn never looks like new content. The
3007	/// prefix is fixed at announce time: to move a route, drop this and call
3008	/// [`Producer::dynamic`] again. Fails with [`Error::Closed`] once the origin's
3009	/// [`Driver`] has been dropped.
3010	pub fn update(&self, route: Route) -> Result<(), Error> {
3011		self.announcement.update(route)
3012	}
3013
3014	/// Poll for the next requested path under this route, without blocking.
3015	///
3016	/// Returns [`Error::Closed`] once the origin's [`Driver`] has been dropped:
3017	/// no request will ever arrive again, so handler loops should end.
3018	pub fn poll_requested_broadcast(&self, waiter: &kio::Waiter) -> Poll<Result<Request, Error>> {
3019		let mut state = ready!(self.state.poll(waiter, |state| {
3020			if state.closed || state.requests.has_queued() {
3021				Poll::Ready(())
3022			} else {
3023				Poll::Pending
3024			}
3025		}));
3026
3027		// The teardown already drained the queue, so there is nothing left to pop.
3028		if state.closed {
3029			return Poll::Ready(Err(Error::Closed));
3030		}
3031
3032		let path = state.requests.pop().expect("predicate guaranteed a request");
3033		// The popped request stays pending, so a repeat request in the window between
3034		// hand-off and accept coalesces onto it instead of re-invoking the handler. The
3035		// producer is a shared clone; `Request::{accept, reject, drop}` removes the
3036		// entry. This mirrors how `poll_requested_track` keeps a served track
3037		// discoverable via the weak cache across the same window.
3038		let producer = state.requests.get(&path).expect("popped key must be pending").clone();
3039		Poll::Ready(Ok(Request {
3040			path,
3041			producer,
3042			home: self.state.clone(),
3043		}))
3044	}
3045
3046	/// Block until a consumer requests a path under this route, returning a
3047	/// [`Request`] to serve.
3048	///
3049	/// Takes `&self` so a handler can serve from one task while another re-prices
3050	/// the route; concurrent callers each receive distinct requests.
3051	pub async fn requested_broadcast(&self) -> Result<Request, Error> {
3052		kio::wait(|waiter| self.poll_requested_broadcast(waiter)).await
3053	}
3054}
3055
3056impl ServeState {
3057	/// Resolve a pending request: cache an accepted broadcast for repeat
3058	/// requests, remove the queue entry, and wake the requesters.
3059	///
3060	/// Resolved while the queue's lock is held, so this linearizes with the
3061	/// teardown: either the teardown ran first (the `closed` check returns, its
3062	/// rejection stands) or this write lands first and the teardown finds the
3063	/// entry already gone. The queue lock is released before the channel guard
3064	/// drops, so the requester wakes outside it: an inline executor re-entering
3065	/// `request_broadcast` from the wake must not find the non-reentrant lock
3066	/// still held.
3067	fn resolve(
3068		shared: &kio::Shared<Self>,
3069		path: &PathOwned,
3070		producer: &kio::Producer<PendingBroadcast>,
3071		result: Result<broadcast::Consumer, Error>,
3072	) {
3073		let mut state = shared.lock();
3074		if state.closed {
3075			return;
3076		}
3077		let resolved = match result {
3078			Ok(broadcast) => {
3079				// If a live broadcast was already served for this path while we were
3080				// fetching upstream, dedup onto it and drop ours rather than replace
3081				// a good entry with a duplicate subscription.
3082				let existing = state.served.insert(path.clone(), broadcast.weak());
3083				Ok(existing.map(|weak| weak.consume()).unwrap_or(broadcast))
3084			}
3085			Err(err) => Err(err),
3086		};
3087		state.requests.remove_if(path, |p| p.same_channel(producer));
3088		if let Ok(mut pending) = producer.write() {
3089			pending.resolved.get_or_insert(resolved);
3090			drop(state);
3091		}
3092	}
3093
3094	/// Drop the still-pending entry, if it is still ours.
3095	fn forget(shared: &kio::Shared<Self>, path: &PathOwned, producer: &kio::Producer<PendingBroadcast>) {
3096		shared.lock().requests.remove_if(path, |p| p.same_channel(producer));
3097	}
3098}
3099
3100/// A pending request for a broadcast to be served on demand.
3101///
3102/// Yielded by [`Dynamic::requested_broadcast`]. The requester is awaiting inside
3103/// [`Consumer::request_broadcast`]; [`accept`](Self::accept) resolves it with a live
3104/// broadcast (which the handler keeps producing into) and [`reject`](Self::reject) resolves
3105/// it with an error. Dropping the request without either rejects it.
3106pub struct Request {
3107	// Absolute path that was requested.
3108	path: PathOwned,
3109
3110	// Result channel back to the awaiting requester(s). Writing `resolved` and dropping
3111	// this wakes them with the outcome.
3112	producer: kio::Producer<PendingBroadcast>,
3113
3114	// The queue this request came from, so `accept` can cache the served
3115	// broadcast for repeat requests.
3116	home: kio::Shared<ServeState>,
3117}
3118
3119impl Request {
3120	/// The absolute path that was requested.
3121	pub fn path(&self) -> &Path<'_> {
3122		&self.path
3123	}
3124
3125	/// Accept the request, resolving every awaiting requester with `broadcast`.
3126	///
3127	/// The caller keeps producing into `broadcast` (e.g. a relay proxying tracks from
3128	/// upstream); the requesters receive a consumer for it. Repeat requests for the
3129	/// path share the served broadcast for as long as it stays live.
3130	pub fn accept(self, broadcast: impl Consume<broadcast::Consumer>) {
3131		let broadcast = broadcast.consume();
3132		ServeState::resolve(&self.home, &self.path, &self.producer, Ok(broadcast));
3133		// `self.producer` drops here, closing the channel; the value is still observable.
3134	}
3135
3136	/// Reject the request, resolving every awaiting requester with `err`.
3137	pub fn reject(self, err: Error) {
3138		ServeState::resolve(&self.home, &self.path, &self.producer, Err(err));
3139	}
3140}
3141
3142impl Drop for Request {
3143	fn drop(&mut self) {
3144		// Handed off but neither accepted nor rejected: drop the still-pending entry so its
3145		// producer clone (plus this one) closes the channel, resolving coalesced requesters to
3146		// `Unroutable` rather than hanging.
3147		//
3148		// The identity guard matters: `accept`/`reject` already removed our entry and released
3149		// the lock before we run, so a concurrent request for the same path may have registered
3150		// a *new* one here. Removing unconditionally would clobber it, stranding its requesters.
3151		ServeState::forget(&self.home, &self.path, &self.producer);
3152	}
3153}
3154
3155/// The pollable result of [`Consumer::request_broadcast`].
3156///
3157/// Awaited via the [`kio::Pending`] wrapper; resolves to the [`broadcast::Consumer`]
3158/// immediately when the broadcast was already announced, or once an [`Dynamic`]
3159/// handler serves the request. Resolves to an error if the request is rejected or every
3160/// handler drops before serving it.
3161pub struct Requesting {
3162	inner: RequestState,
3163	// The path the requester asked for, relative to its cursor's root. Stamped on the
3164	// resolved broadcast (see [`broadcast::Info::path`]) because a handler is free to
3165	// serve a broadcast created somewhere else entirely, or at no path at all.
3166	path: PathOwned,
3167	// Egress scope applied to the resolved broadcast, so its reads are attributed.
3168	// Empty (no-op) for an untagged consumer.
3169	stats: stats::Scope,
3170}
3171
3172enum RequestState {
3173	// Unroutable at request time: resolves immediately with this error. Baked in so
3174	// `request_broadcast` itself stays infallible.
3175	Failed(Error),
3176	// Awaiting a handler: resolves when the request's result channel is written.
3177	Pending(kio::Consumer<PendingBroadcast>),
3178}
3179
3180impl Requesting {
3181	fn failed(error: Error) -> Self {
3182		Self::new(RequestState::Failed(error))
3183	}
3184
3185	fn queued(consumer: kio::Consumer<PendingBroadcast>) -> Self {
3186		Self::new(RequestState::Pending(consumer))
3187	}
3188
3189	/// Whether the request was handed to a serving route, rather than decided on
3190	/// the spot.
3191	///
3192	/// Fixed at request time, so it distinguishes the two ways
3193	/// [`Error::Unroutable`] arises: a queued request that fails was killed by
3194	/// its serving route retracting, and the table may already hold a
3195	/// replacement worth retrying against ([`Consumer::routed_broadcast`] does),
3196	/// while an unqueued failure means nothing could serve the path at all.
3197	pub fn is_queued(&self) -> bool {
3198		matches!(self.inner, RequestState::Pending(_))
3199	}
3200
3201	fn new(inner: RequestState) -> Self {
3202		Self {
3203			inner,
3204			path: PathOwned::default(),
3205			stats: stats::Scope::default(),
3206		}
3207	}
3208
3209	fn with_path(mut self, path: PathOwned) -> Self {
3210		self.path = path;
3211		self
3212	}
3213
3214	/// The egress scope the resolved broadcast's reads are attributed to.
3215	fn with_stats(mut self, scope: stats::Scope) -> Self {
3216		self.stats = scope;
3217		self
3218	}
3219
3220	/// Stamp a resolved broadcast with the path this cursor asked for and its egress scope.
3221	fn hand_out(&self, broadcast: broadcast::Consumer) -> broadcast::Consumer {
3222		broadcast.with_path(self.path.clone()).with_stats(self.stats.clone())
3223	}
3224
3225	/// Poll for the requested broadcast without blocking.
3226	pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<broadcast::Consumer, Error>> {
3227		match &self.inner {
3228			RequestState::Failed(error) => Poll::Ready(Err(error.clone())),
3229			RequestState::Pending(consumer) => Poll::Ready(
3230				match ready!(consumer.poll(waiter, |state| match &state.resolved {
3231					Some(result) => Poll::Ready(result.clone()),
3232					None => Poll::Pending,
3233				})) {
3234					Ok(result) => result.map(|broadcast| self.hand_out(broadcast)),
3235					// Every handler dropped without resolving: nobody could route it.
3236					Err(_closed) => Err(Error::Unroutable),
3237				},
3238			),
3239		}
3240	}
3241}
3242
3243impl kio::Pollable for Requesting {
3244	type Output = Result<broadcast::Consumer, Error>;
3245
3246	fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
3247		self.poll_ok(waiter)
3248	}
3249}
3250
3251/// Derive a read view from a handle.
3252///
3253/// Lets APIs accept either a producer or a consumer (e.g.
3254/// [`Client::with_publisher`](crate::Client::with_publisher),
3255/// [`Request::accept`]). The blanket `&T` impl means you can
3256/// pass by value (`foo(x)`) to hand off ownership, or by reference (`foo(&x)`)
3257/// to keep it, without spelling out `.consume()`.
3258pub trait Consume<T> {
3259	/// Derive a read view (a consumer) from this handle.
3260	fn consume(&self) -> T;
3261}
3262
3263impl<T, U: Consume<T>> Consume<T> for &U {
3264	fn consume(&self) -> T {
3265		(**self).consume()
3266	}
3267}
3268
3269impl Consume<Consumer> for Producer {
3270	fn consume(&self) -> Consumer {
3271		// Mirrors the inherent `Producer::consume`; inlined to avoid the
3272		// inherent-vs-trait `consume` ambiguity. Untagged: egress is tagged
3273		// separately from ingress.
3274		Consumer::from_producer(self, stats::Session::default())
3275	}
3276}
3277
3278impl Consume<Consumer> for Consumer {
3279	fn consume(&self) -> Consumer {
3280		self.clone()
3281	}
3282}
3283
3284impl Consume<broadcast::Consumer> for broadcast::Producer {
3285	fn consume(&self) -> broadcast::Consumer {
3286		// The inherent `consume` shadows this trait method, so this delegates.
3287		self.consume()
3288	}
3289}
3290
3291impl Consume<broadcast::Consumer> for broadcast::Consumer {
3292	fn consume(&self) -> broadcast::Consumer {
3293		self.clone()
3294	}
3295}
3296
3297impl Consume<track::Consumer> for track::Producer {
3298	fn consume(&self) -> track::Consumer {
3299		self.consume()
3300	}
3301}
3302
3303impl Consume<track::Consumer> for track::Consumer {
3304	fn consume(&self) -> track::Consumer {
3305		self.clone()
3306	}
3307}
3308
3309/// Cheap read handle over an origin's route table.
3310///
3311/// Clones share the underlying state without allocating any per-cursor
3312/// resources. To receive route announcements, call [`Self::announced`]; to
3313/// resolve a path into a broadcast, call [`Self::request_broadcast`].
3314#[derive(Clone)]
3315pub struct Consumer {
3316	// Identity of the origin this consumer was derived from.
3317	hop: Hop,
3318	scope: OriginScope,
3319
3320	// A prefix that is automatically stripped from all paths.
3321	root: PathOwned,
3322
3323	// The origin's shared state: the route table, announce cursors, and the
3324	// remotely-served fronts.
3325	shared: kio::Shared<OriginState>,
3326
3327	// Egress stats context. Broadcasts handed out through this consumer (and any
3328	// handle derived from them) are attributed to it (reads counted on the
3329	// publisher/egress side). Empty (no-op) unless a session tagged this handle.
3330	stats: stats::Session,
3331
3332	// Split horizon: routes whose hop chain or announcing session (`via`) is the
3333	// excluded peer are invisible to `announced` and skipped by
3334	// `request_broadcast`, so a peer is never served (or advertised) its own
3335	// content back. A local view (`Self::local`) hides peer routes the same way.
3336	horizon: Horizon,
3337
3338	// Which routes beneath a hidden (`.`-prefixed) segment `announced` reports.
3339	hidden: Hidden,
3340
3341	// The cache policy remote fronts inherit, mirroring what
3342	// `create_broadcast` gives a local front.
3343	pool: cache::Pool,
3344	cache_duration: Duration,
3345
3346	// Non-owning submission handle to the origin's [`Driver`], for the front
3347	// watcher a routed `request_broadcast` spawns. Non-owning so a lingering
3348	// read handle never keeps the driver from finishing.
3349	tasks: TasksWeak,
3350
3351	// The driver's clock and timers, threaded into fronts for the track idle
3352	// linger.
3353	timers: Clock,
3354}
3355
3356impl Consumer {
3357	fn from_producer(producer: &Producer, stats: stats::Session) -> Self {
3358		Self {
3359			hop: producer.hop,
3360			scope: producer.scope.clone(),
3361			root: producer.root.clone(),
3362			shared: producer.shared.clone(),
3363			stats,
3364			horizon: Horizon::default(),
3365			hidden: Hidden::default(),
3366			pool: producer.pool.clone(),
3367			cache_duration: producer.cache_duration,
3368			tasks: producer.tasks.downgrade(),
3369			timers: producer.timers.clone(),
3370		}
3371	}
3372
3373	/// This origin's hop identity.
3374	pub fn hop(&self) -> Hop {
3375		self.hop
3376	}
3377
3378	/// A clone that never serves the given peer its own data: routes whose hop
3379	/// chain contains `peer`, or whose announcing session is `peer`, are invisible
3380	/// and never resolved from, matching what the announce loop advertises to them.
3381	/// Sessions apply this once they learn the peer's origin id. Hop 0 identifies
3382	/// nobody, so the announcing session's assigned identity is what keeps an
3383	/// anonymous route from echoing back. Pass [`Hop::UNKNOWN`] for an anonymous
3384	/// peer.
3385	pub(crate) fn excluding(mut self, peer: Hop) -> Self {
3386		self.horizon.exclude = Some(peer);
3387		self
3388	}
3389
3390	/// A view of the routes that entered here: every route a handle marked
3391	/// [`Producer::peer`] announced is hidden from [`Self::announced`] and never
3392	/// resolved by [`Self::request_broadcast`].
3393	///
3394	/// On a relay, this is what the relay ingests itself, from clients and
3395	/// in-process producers, as opposed to what its cluster peers forward.
3396	pub fn local(mut self) -> Self {
3397		self.horizon.local = true;
3398		self
3399	}
3400
3401	/// A clone whose [`announced`](Self::announced) also reports hidden routes:
3402	/// those with a segment starting with `.` below the requested prefix.
3403	/// Hidden routes are left out by default, so a platform can add `.`-named
3404	/// broadcasts without them turning up in apps that list everything.
3405	pub fn with_hidden(mut self, hidden: bool) -> Self {
3406		self.hidden.include = hidden;
3407		self
3408	}
3409
3410	/// A clone whose [`announced`](Self::announced) reports only the routes a feed
3411	/// from `outer` hides, for a stream topping up that feed.
3412	pub(crate) fn beyond(mut self, outer: &Consumer) -> Self {
3413		self.hidden.beyond = Some(interest_prefixes(&outer.scope.allowed));
3414		self
3415	}
3416
3417	/// Whether [`announced`](Self::announced) reports hidden routes too.
3418	pub(crate) fn includes_hidden(&self) -> bool {
3419		self.hidden.include
3420	}
3421
3422	/// Attach an egress stats context: broadcasts handed out through this handle (and
3423	/// any handle derived from it) are attributed to `session` on the publisher
3424	/// (egress) side. Pass [`stats::Session::default`] to opt out.
3425	pub fn with_stats(mut self, session: stats::Session) -> Self {
3426		self.stats = session;
3427		self
3428	}
3429
3430	/// A clone of this consumer with its stats context cleared, so an internal
3431	/// lookup stream (e.g. [`Self::routed`]) doesn't drive the egress
3432	/// announce guards; the caller re-attributes the result itself.
3433	fn untagged(&self) -> Self {
3434		Self {
3435			stats: stats::Session::default(),
3436			..self.clone()
3437		}
3438	}
3439
3440	/// A view with this consumer's identity and root but no scope:
3441	/// [`announced`](Self::announced) yields nothing. Used to answer a peer's
3442	/// announce-interest for a prefix outside our scope by announcing nothing,
3443	/// rather than tearing the stream down.
3444	pub(crate) fn empty(&self) -> Self {
3445		Self {
3446			scope: OriginScope::empty(),
3447			..self.clone()
3448		}
3449	}
3450
3451	/// Subscribe to route announcements for this consumer's scope.
3452	///
3453	/// Allocates a per-cursor coalescing buffer and replays the currently
3454	/// announced routes as initial updates. Routes stay prefixes and are named
3455	/// relative to this consumer's root; its patterns only filter visibility.
3456	/// Routes with a segment starting with `.` below the literal head of those
3457	/// patterns are hidden unless [`with_hidden`](Self::with_hidden) opted in.
3458	/// Drop the returned [`AnnounceConsumer`] to unregister.
3459	pub fn announced(&self) -> AnnounceConsumer {
3460		AnnounceConsumer::new(
3461			self.root.clone(),
3462			self.scope.allowed.clone(),
3463			self.stats.clone(),
3464			self.horizon,
3465			self.hidden.clone(),
3466			&self.shared,
3467		)
3468	}
3469
3470	/// Returns a cheap duplicate of this read handle.
3471	pub fn consume(&self) -> Self {
3472		self.clone()
3473	}
3474
3475	/// The newest broadcast published on this origin at exactly `path`, if any.
3476	/// Test-only: a request goes through the table like any other.
3477	#[cfg(test)]
3478	pub(crate) fn get_broadcast(&self, path: impl AsPath) -> Option<broadcast::Consumer> {
3479		let full = self.root.join(path).to_owned();
3480		if !self.scope.permits(&full) {
3481			return None;
3482		}
3483		let table = self.shared.lock();
3484		table
3485			.routes
3486			.at(&full)
3487			.filter(|entry| entry.local)
3488			.min_by_key(|entry| route_order(&entry.prefix, entry))
3489			.and_then(|entry| entry.source.clone())
3490	}
3491
3492	/// Block until an announced route covers `path`, and return it.
3493	///
3494	/// Covering means the route's prefix is a (segment-wise) prefix of `path`,
3495	/// including the exact path itself. Returns `None` if the path is outside this
3496	/// consumer's scope or the consumer is closed first.
3497	///
3498	/// To resolve a broadcast rather than inspect the route, use
3499	/// [`Self::routed_broadcast`]: pairing this with [`Self::request_broadcast`]
3500	/// leaves a gap where the covering route can retract.
3501	pub async fn routed(&self, path: impl AsPath) -> Option<Route> {
3502		let path = path.as_path();
3503
3504		// Scope a fresh consumer down to this path's subtree, so we only wake for
3505		// announcements that overlap the requested path.
3506		// A max-depth path cannot be spelled as `path/**` (`**` would be a 33rd
3507		// segment), so watch the existing stream and match covering claims instead.
3508		let consumer = match Pattern::subtree(path.as_str()) {
3509			Ok(subtree) => self.scope("", &Patterns::from(subtree)).ok()?,
3510			Err(InvalidPattern::TooManySegments) => self.clone(),
3511			Err(_) => return None,
3512		};
3513
3514		// `scope` keeps narrower permissions intact: if we ask for `foo` on a
3515		// consumer limited to `foo/specific`, `foo` itself is unauthorized. Bail
3516		// rather than loop forever.
3517		if !consumer.allowed().matches(path.as_str()) {
3518			return None;
3519		}
3520
3521		// Use an untagged stream: this is a lookup, not egress announce
3522		// forwarding, so it must not drive the announce guards. Hiding narrows
3523		// discovery, not lookup, so a hidden path resolves like any other.
3524		let mut announced = consumer.untagged().with_hidden(true).announced();
3525		loop {
3526			let update = announced.next().await?;
3527			if update.kind.is_active() && path.has_prefix(&update.prefix) {
3528				return Some(update.route);
3529			}
3530		}
3531	}
3532
3533	/// Block until `path` resolves to a broadcast: [`Self::request_broadcast`],
3534	/// retried whenever the routes covering the path change.
3535	///
3536	/// A request answers for the routes as they stand, so it can miss an
3537	/// announcement that has not arrived yet, lose its covering route to
3538	/// failover churn, find a route that covers the path while nothing serves it
3539	/// yet (an advertise-only announce racing its handler), or be turned down by
3540	/// a handler. This rides all of that out by watching the covering routes
3541	/// and asking again each time they move, which is what makes it the right
3542	/// call for resolving a path right after connecting. Returns
3543	/// [`Error::Unauthorized`] for a path outside this consumer's scope,
3544	/// [`Error::Closed`] once the origin closes, and any other resolution
3545	/// failure as-is.
3546	pub async fn routed_broadcast(&self, path: impl AsPath) -> Result<broadcast::Consumer, Error> {
3547		let path = path.as_path();
3548
3549		// `allowed` keeps narrower permissions intact: if the whole path is not
3550		// reachable, no route can ever cover it, so bail rather than loop forever.
3551		if !self.allowed().matches(path.as_str()) {
3552			return Err(Error::Unauthorized);
3553		}
3554		loop {
3555			// `Unroutable` is a verdict of the routes covering the path as they
3556			// stood when the request was made. Re-asking the same routes would
3557			// spin, so watch them before asking and wait for them to move (a
3558			// route arriving or retracting, an identical standby swapping in, a
3559			// local broadcast announcing at the path), then try again. A change
3560			// between the ask and the wait bumps the watch first, so that retry
3561			// is immediate; the teardown pokes every watch, so a closed origin
3562			// is observed on the next pass.
3563			let (watch, seen) = {
3564				let mut table = self.shared.lock();
3565				if table.closed {
3566					return Err(Error::Closed);
3567				}
3568				let watch = table.watch(&self.shared, &self.root.join(&path));
3569				let seen = watch.seen();
3570				(watch, seen)
3571			};
3572			match self.request_broadcast(&path).await {
3573				Ok(broadcast) => return Ok(broadcast),
3574				Err(Error::Unroutable) => {
3575					kio::wait(|waiter| watch.poll_changed(waiter, seen)).await;
3576				}
3577				// Teardown parks a pending request with `Dropped`; the contract is
3578				// `Closed` once the origin is gone.
3579				Err(Error::Dropped) if self.shared.lock().closed => return Err(Error::Closed),
3580				Err(err) => return Err(err),
3581			}
3582		}
3583	}
3584
3585	/// Returns a consumer rooted at `root` and restricted to matching `patterns`.
3586	///
3587	/// `root` is relative to this consumer's root, and `patterns` are relative to
3588	/// the new root. Returns [`Error::Unauthorized`] when the requested scope has
3589	/// no overlap with this consumer's scope, or [`Error::BoundsExceeded`] when
3590	/// rooting the patterns would exceed the path limit.
3591	pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Consumer, Error> {
3592		let root = self.root.join(root).to_owned();
3593		let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
3594		let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
3595		Ok(Consumer {
3596			scope,
3597			root,
3598			..self.clone()
3599		})
3600	}
3601
3602	/// Resolve a broadcast by exact path.
3603	///
3604	/// Returns a [`kio::Pending`] future, mirroring
3605	/// [`track::Consumer::fetch_group`](track::Consumer::fetch_group). Every
3606	/// path resolves through a front the origin's [`Driver`] runs: the request
3607	/// mints one or joins the one already serving the path, and the front picks
3608	/// the best announced route covering it (the most specific prefix, then the
3609	/// cheapest, a broadcast published on this origin winning ties) and
3610	/// materializes it, from the broadcast itself or from the peer that
3611	/// announced the route. When its serving source dies or a better qualifying
3612	/// route appears, the front re-splices through the best route sharing its
3613	/// first hop at a group boundary, invisibly to subscribers. A change that
3614	/// does not preserve the first hop ends the broadcast instead, as does its
3615	/// route retracting with no replacement, and the next request re-serves the
3616	/// path. Tracks already in flight carry on to their own end.
3617	///
3618	/// The returned future fails with [`Error::Unroutable`] at once when no
3619	/// announced route covers the path, including a broadcast created on this
3620	/// origin but not announced.
3621	/// A route claims capability, not inventory: resolving a covered path
3622	/// succeeds optimistically, and a path that names nothing surfaces as
3623	/// [`Error::NotFound`] on its tracks instead.
3624	pub fn request_broadcast(&self, path: impl AsPath) -> kio::Pending<Requesting> {
3625		let path = path.as_path();
3626
3627		// Key requests by absolute path so scoped/rooted consumers and handlers
3628		// (which may have a different root) agree on the same entry, and so the egress
3629		// counters resolve against the same broadcast the ingress side wrote.
3630		let absolute = self.root.join(&path).to_owned();
3631		let scope = self.stats.egress(&absolute);
3632		// The resolved handle is named by what *this* cursor asked for, not by the absolute
3633		// path: a rooted cursor cannot name anything above its own root, so that is what a
3634		// catalog it reads may reference.
3635		let requested = path.to_owned();
3636
3637		// Routes only cover paths within this consumer's scope.
3638		if !self.scope.permits(&absolute) {
3639			return kio::Pending::new(Requesting::failed(Error::Unauthorized));
3640		}
3641
3642		let mut state = self.shared.lock();
3643
3644		// The origin's driver dropped: nothing will ever serve this.
3645		if state.closed {
3646			return kio::Pending::new(Requesting::failed(Error::Closed));
3647		}
3648
3649		// Nothing serves the path: no announced broadcast and no served route.
3650		// Checked before joining a front, so a front still draining after its
3651		// route retracted takes no newcomers.
3652		if state
3653			.best_route(&absolute.as_path(), self.horizon, Pin::Any, &HashSet::new())
3654			.is_none()
3655		{
3656			return kio::Pending::new(Requesting::failed(Error::Unroutable));
3657		}
3658
3659		// Join the live front for this path and exclusion, if any: its watcher
3660		// resolves (or already resolved) the request channel with the front's
3661		// spliced broadcast, so repeat requests share one upstream
3662		// subscription. Only while the best route still serves the front's
3663		// content, though: once a different publisher wins (a cheaper route), a
3664		// newcomer gets a fresh front from it, and the old front keeps serving the
3665		// readers it has, since other content can't be spliced into it.
3666		let key = (absolute.clone(), self.horizon);
3667		if let Some(front) = state.fronts.get(&key) {
3668			let pin = *front.pin.lock();
3669			let current = state
3670				.best_route(&absolute.as_path(), self.horizon, Pin::Any, &HashSet::new())
3671				.is_some_and(|entry| entry.qualifies(pin));
3672			if current {
3673				let pending = Requesting::queued(front.request.consume())
3674					.with_path(requested)
3675					.with_stats(scope);
3676				return kio::Pending::new(pending);
3677			}
3678			state.fronts.remove(&key);
3679		}
3680
3681		// A route covers the path: mint the front and hand its watcher the
3682		// request. The watcher materializes the path from the best covering
3683		// route, resolves the channel, and re-splices the front through
3684		// routes sharing its first hop for as long as one serves.
3685		let broadcast = broadcast::Producer::new_spliced(broadcast::Info {
3686			pool: self.pool.clone(),
3687			cache_duration: self.cache_duration,
3688			path: absolute.clone(),
3689		});
3690		let request = kio::Producer::<PendingBroadcast>::default();
3691		let consumer = request.consume();
3692		let watch = state.watch(&self.shared, &absolute);
3693		let pin = kio::Lock::new(Pin::Any);
3694		state.fronts.insert(
3695			key,
3696			RemoteFront {
3697				request: request.clone(),
3698				broadcast: broadcast.consume().weak(),
3699				pin: pin.clone(),
3700			},
3701		);
3702		// Released before the push: a set whose handles are gone drops the task,
3703		// and the `Watch` it carries unregisters under this same lock.
3704		drop(state);
3705		self.tasks.push(run_front(FrontTask {
3706			shared: self.shared.clone(),
3707			broadcast,
3708			path: absolute,
3709			horizon: self.horizon,
3710			watch,
3711			request,
3712			pin,
3713			timers: self.timers.clone(),
3714		}));
3715		kio::Pending::new(Requesting::queued(consumer).with_path(requested).with_stats(scope))
3716	}
3717
3718	/// Returns the prefix that is automatically stripped from all paths.
3719	pub fn root(&self) -> &Path<'_> {
3720		&self.root
3721	}
3722
3723	/// The patterns this consumer may reach, relative to its root.
3724	pub fn allowed(&self) -> Patterns {
3725		self.scope.relative(&self.root)
3726	}
3727
3728	/// Converts a relative path to an absolute path.
3729	pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
3730		self.root.join(path)
3731	}
3732}
3733
3734/// Receives route announcements for a scope.
3735///
3736/// Created by [`Consumer::announced`].
3737/// Drop to unregister.
3738pub struct AnnounceConsumer {
3739	id: ConsumerId,
3740	shared: kio::Shared<OriginState>,
3741	root: PathOwned,
3742
3743	// Pending updates queued for this cursor. Coalesced so a slow consumer
3744	// can't accumulate redundant announce/retract pairs.
3745	state: kio::Producer<OriginConsumerState>,
3746
3747	// Egress stats context (empty for an untagged stream). Announce events drive the
3748	// per-prefix announce guards below.
3749	stats: stats::Session,
3750
3751	// Live egress announce guards, keyed by absolute prefix. An announce
3752	// opens one (bumping `announces_started` + `announced_bytes`); the matching retraction
3753	// drops it (bumping `announces_ended` + `announced_bytes`).
3754	guards: HashMap<PathOwned, stats::Announce>,
3755
3756	// Holds the waiter a `Stream` poll registered; disjoint from `state` so the
3757	// borrow never collides with the body's.
3758	park: kio::Park,
3759}
3760
3761impl AnnounceConsumer {
3762	fn new(
3763		root: PathOwned,
3764		allowed: Patterns,
3765		stats: stats::Session,
3766		horizon: Horizon,
3767		hidden: Hidden,
3768		shared: &kio::Shared<OriginState>,
3769	) -> Self {
3770		let state = kio::Producer::<OriginConsumerState>::default();
3771		let id = ConsumerId::new();
3772
3773		{
3774			let mut table = shared.lock();
3775			if table.closed {
3776				// A cursor on a dead origin is born ended.
3777				if let Ok(mut state) = state.write() {
3778					state.ended = true;
3779				}
3780			} else {
3781				table.register_cursor(
3782					id,
3783					TableCursor {
3784						root: root.clone(),
3785						heads: interest_prefixes(&allowed),
3786						allowed,
3787						horizon,
3788						hidden,
3789						state: state.clone(),
3790						current: HashMap::new(),
3791					},
3792				);
3793			}
3794		}
3795
3796		Self {
3797			id,
3798			shared: shared.clone(),
3799			root,
3800			state,
3801			stats,
3802			guards: HashMap::new(),
3803			park: kio::Park::default(),
3804		}
3805	}
3806
3807	/// Drive the egress announce guards for one update.
3808	fn hand_out(&mut self, update: AnnounceUpdate) -> AnnounceUpdate {
3809		let absolute = self.root.join(&update.prefix).to_owned();
3810		if update.kind.is_active() {
3811			let scope = self.stats.egress(&absolute);
3812			self.guards
3813				.entry(update.prefix.clone())
3814				.or_insert_with(|| scope.announce());
3815		} else {
3816			self.guards.remove(&update.prefix);
3817		}
3818		update
3819	}
3820
3821	/// Returns the next route announcement, update, or retraction, its prefix
3822	/// relative to this cursor's root.
3823	///
3824	/// A retraction is only delivered for a previously announced prefix, and a
3825	/// repeated announcement for the same prefix is a metadata update. Returns
3826	/// None if the cursor is closed. The consumer is also a [`futures::Stream`]
3827	/// of the same updates.
3828	pub async fn next(&mut self) -> Option<AnnounceUpdate> {
3829		kio::wait(|waiter| self.poll_next(waiter)).await
3830	}
3831
3832	/// Poll for the next update, without blocking.
3833	///
3834	/// Returns `Poll::Ready(Some(_))` for an update, `Poll::Ready(None)` if the
3835	/// cursor is closed, or `Poll::Pending` after registering `waiter` to be
3836	/// notified when the next update arrives.
3837	pub fn poll_next(&mut self, waiter: &kio::Waiter) -> Poll<Option<AnnounceUpdate>> {
3838		let update = {
3839			let mut state = match ready!(self.state.poll(waiter, |state| {
3840				if state.pending.is_empty() && !state.ended {
3841					Poll::Pending
3842				} else {
3843					Poll::Ready(())
3844				}
3845			})) {
3846				Ok(state) => state,
3847				// Closed: discard the Ref so its MutexGuard doesn't escape this call.
3848				Err(_) => return Poll::Ready(None),
3849			};
3850			match state.take() {
3851				Some(update) => update,
3852				None => {
3853					// Ended by the origin's teardown, pending updates already
3854					// drained; close the channel so every closure signal agrees.
3855					state.close();
3856					return Poll::Ready(None);
3857				}
3858			}
3859		};
3860		Poll::Ready(Some(self.hand_out(update)))
3861	}
3862
3863	/// Returns the next update without blocking.
3864	///
3865	/// Returns None if there is no update available; NOT because the cursor is closed.
3866	/// Use [`Self::is_closed`] to check if the cursor is closed.
3867	pub fn try_next(&mut self) -> Option<AnnounceUpdate> {
3868		let update = self.state.write().ok()?.take()?;
3869		Some(self.hand_out(update))
3870	}
3871
3872	/// Returns true if the cursor is closed (no more updates will arrive).
3873	pub fn is_closed(&self) -> bool {
3874		let state = self.state.read();
3875		state.is_closed() || state.ended
3876	}
3877
3878	/// Returns the root that is automatically stripped from emitted prefixes.
3879	pub fn root(&self) -> &Path<'_> {
3880		&self.root
3881	}
3882
3883	/// Converts an emitted prefix back to one rooted at the origin.
3884	pub fn absolute(&self, prefix: impl AsPath) -> Path<'_> {
3885		self.root.join(prefix)
3886	}
3887}
3888
3889impl futures::Stream for AnnounceConsumer {
3890	type Item = AnnounceUpdate;
3891
3892	fn poll_next(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Option<Self::Item>> {
3893		let this = self.get_mut();
3894		let waiter = this.park.hold(cx).clone();
3895		this.poll_next(&waiter)
3896	}
3897}
3898
3899impl Drop for AnnounceConsumer {
3900	fn drop(&mut self) {
3901		let mut shared = self.shared.lock();
3902		if let Some(cursor) = shared.cursors.remove(&self.id) {
3903			for head in &cursor.heads {
3904				shared.routes.remove_cursor(head, self.id);
3905			}
3906		}
3907	}
3908}
3909
3910#[cfg(test)]
3911use futures::FutureExt;
3912
3913#[cfg(test)]
3914#[allow(missing_docs)] // test-only assertion helpers
3915impl AnnounceConsumer {
3916	/// The next update must be an active route at `expected`; returns it.
3917	pub fn assert_next_active(&mut self, expected: impl AsPath) -> Route {
3918		let expected = expected.as_path();
3919		let update = self.next().now_or_never().expect("next blocked").expect("no next");
3920		assert_eq!(update.prefix, expected, "wrong prefix");
3921		assert!(update.kind.is_active(), "should be an active route");
3922		update.route
3923	}
3924
3925	/// The `try_next` counterpart of [`Self::assert_next_active`].
3926	pub fn assert_try_next_active(&mut self, expected: impl AsPath) -> Route {
3927		let expected = expected.as_path();
3928		let update = self.try_next().expect("no next");
3929		assert_eq!(update.prefix, expected, "wrong prefix");
3930		assert!(update.kind.is_active(), "should be an active route");
3931		update.route
3932	}
3933
3934	/// The next update must be a retraction at `expected`.
3935	pub fn assert_next_ended(&mut self, expected: impl AsPath) {
3936		let expected = expected.as_path();
3937		let update = self.next().now_or_never().expect("next blocked").expect("no next");
3938		assert_eq!(update.prefix, expected, "wrong prefix");
3939		assert_eq!(update.kind, AnnounceKind::Retracted, "should be a retraction");
3940	}
3941
3942	pub fn assert_next_wait(&mut self) {
3943		if let Some(res) = self.next().now_or_never() {
3944			panic!("next should block: got {:?}", res.map(|u| u.prefix));
3945		}
3946	}
3947}
3948
3949/// Test-only construction shorthand: build the producer and spawn its driver on
3950/// the ambient tokio runtime, mirroring what `moq_tokio::origin::spawn` does
3951/// for applications.
3952#[cfg(test)]
3953pub(crate) trait ProduceTest {
3954	fn produce(self) -> Producer;
3955}
3956
3957#[cfg(test)]
3958impl ProduceTest for Config {
3959	fn produce(self) -> Producer {
3960		let (producer, driver) = Producer::new(self);
3961		if tokio::runtime::Handle::try_current().is_ok() {
3962			tokio::spawn(crate::time::run(driver));
3963		} else {
3964			// A sync test: nothing polls the driver, and dropping it would tear
3965			// the origin down, so leak it and rely on the synchronous half.
3966			std::mem::forget(driver);
3967		}
3968		producer
3969	}
3970}
3971
3972#[cfg(test)]
3973impl ProduceTest for Hop {
3974	fn produce(self) -> Producer {
3975		Config::new(self).produce()
3976	}
3977}
3978
3979#[cfg(test)]
3980mod tests {
3981	use super::*;
3982	use futures::FutureExt;
3983
3984	fn origin(id: u64) -> Hop {
3985		Hop::new(id).unwrap()
3986	}
3987
3988	fn hops(ids: &[u64]) -> Hops {
3989		let mut list = Hops::new();
3990		for &id in ids {
3991			list.push(if id == 0 { Hop::UNKNOWN } else { origin(id) }).unwrap();
3992		}
3993		list
3994	}
3995
3996	/// The scope granting these prefixes: each spelled as its subtree pattern.
3997	fn scopes(prefixes: &[&str]) -> Patterns {
3998		prefixes
3999			.iter()
4000			.map(|prefix| Pattern::subtree(prefix).unwrap())
4001			.collect()
4002	}
4003
4004	#[test]
4005	fn default_config_mints_a_real_hop() {
4006		let config = Config::default();
4007		assert_ne!(config.hop, Hop::UNKNOWN);
4008		let (producer, _driver) = Producer::new(config.clone());
4009		assert_eq!(producer.hop(), config.hop);
4010		assert_eq!(producer.consume().hop(), config.hop);
4011	}
4012
4013	#[test]
4014	fn random_hops_fit_legacy_lite_clients() {
4015		for _ in 0..32 {
4016			assert!(Hop::random().id() < 1u64 << 53);
4017		}
4018	}
4019
4020	/// Yield to the driver until `check` passes, bounded so a bug fails instead
4021	/// of hanging.
4022	async fn settle(mut check: impl FnMut() -> bool) {
4023		for _ in 0..100 {
4024			if check() {
4025				return;
4026			}
4027			tokio::task::yield_now().await;
4028		}
4029		panic!("condition never settled");
4030	}
4031
4032	/// Yield to the driver until the server's front watcher delivers a request.
4033	async fn queued(server: &Dynamic) -> Request {
4034		let mut request = None;
4035		settle(|| match server.poll_requested_broadcast(&kio::Waiter::noop()) {
4036			Poll::Ready(Ok(popped)) => {
4037				request = Some(popped);
4038				true
4039			}
4040			_ => false,
4041		})
4042		.await;
4043		request.unwrap()
4044	}
4045
4046	/// Yield to the driver until the subscription has its next group or its end.
4047	async fn next_group(subscription: &mut crate::track::Subscriber) -> Result<Option<crate::group::Consumer>, Error> {
4048		let mut next = None;
4049		settle(|| match subscription.poll_recv_group(&kio::Waiter::noop()) {
4050			Poll::Ready(result) => {
4051				next = Some(result);
4052				true
4053			}
4054			Poll::Pending => false,
4055		})
4056		.await;
4057		next.unwrap()
4058	}
4059
4060	#[tokio::test]
4061	async fn announce_and_retract() {
4062		let producer = origin(1).produce();
4063		let consumer = producer.consume();
4064		let mut announced = consumer.announced();
4065		announced.assert_next_wait();
4066
4067		let announcement = producer.announce("room/alice", Route::default()).unwrap();
4068		let route = announced.assert_next_active("room/alice");
4069		assert!(route.hops.is_empty());
4070		assert_eq!(route.cost, Cost::default());
4071		announced.assert_next_wait();
4072
4073		drop(announcement);
4074		announced.assert_next_ended("room/alice");
4075		announced.assert_next_wait();
4076	}
4077
4078	/// A `.`-prefixed segment below the requested prefix hides a route from
4079	/// discovery unless the reader opts in; one inside the prefix does not.
4080	#[tokio::test]
4081	async fn hidden_routes_need_an_opt_in() {
4082		let producer = origin(1).produce();
4083		let consumer = producer.consume();
4084		let _visible = producer.announce("room/alice", Route::default()).unwrap();
4085		let _stats = producer.announce(".stats/node", Route::default()).unwrap();
4086		let _nested = producer.announce("room/.internal", Route::default()).unwrap();
4087		// Only a leading dot hides: a suffix is part of the name.
4088		let _suffix = producer.announce("room/catalog.pro", Route::default()).unwrap();
4089
4090		let mut announced = consumer.announced();
4091		announced.assert_next_active("room/alice");
4092		announced.assert_next_active("room/catalog.pro");
4093		announced.assert_next_wait();
4094
4095		let mut announced = consumer.clone().with_hidden(true).announced();
4096		announced.assert_next_active(".stats/node");
4097		announced.assert_next_active("room/.internal");
4098		announced.assert_next_active("room/alice");
4099		announced.assert_next_active("room/catalog.pro");
4100		announced.assert_next_wait();
4101
4102		// Naming the dot segment lists what is under it, by root or by pattern.
4103		let mut announced = consumer
4104			.scope(".stats", &Patterns::from(Pattern::all()))
4105			.unwrap()
4106			.announced();
4107		announced.assert_next_active("node");
4108		announced.assert_next_wait();
4109		let mut announced = consumer.scope("", &scopes(&["room/.internal"])).unwrap().announced();
4110		announced.assert_next_active("room/.internal");
4111		announced.assert_next_wait();
4112
4113		// A top-up feed reports only what a feed from the root hid, filtered by its own
4114		// prefix and opt-in.
4115		let mut announced = consumer.clone().with_hidden(true).beyond(&consumer).announced();
4116		announced.assert_next_active(".stats/node");
4117		announced.assert_next_active("room/.internal");
4118		announced.assert_next_wait();
4119		let room = consumer.scope("", &scopes(&["room"])).unwrap().beyond(&consumer);
4120		let mut announced = room.announced();
4121		announced.assert_next_wait();
4122		let stats = consumer.scope("", &scopes(&[".stats"])).unwrap().beyond(&consumer);
4123		let mut announced = stats.announced();
4124		announced.assert_next_active(".stats/node");
4125		announced.assert_next_wait();
4126	}
4127
4128	/// Hiding narrows discovery only: an exact request resolves without an opt-in.
4129	#[tokio::test]
4130	async fn hidden_broadcast_resolves_by_path() {
4131		let producer = origin(1).produce();
4132		let consumer = producer.consume();
4133		let broadcast = producer.create_broadcast(".stats/node").unwrap();
4134		broadcast.announce(Route::default()).unwrap();
4135
4136		consumer.announced().assert_next_wait();
4137		let resolved = consumer.request_broadcast(".stats/node").await.expect("resolves");
4138		assert_eq!(resolved.info().path.as_str(), ".stats/node");
4139	}
4140
4141	/// A route that turns up later is filtered the same way as the replay.
4142	#[tokio::test]
4143	async fn hidden_route_announced_later_stays_hidden() {
4144		let producer = origin(1).produce();
4145		let consumer = producer.consume();
4146		let mut announced = consumer.announced();
4147		let mut opted = consumer.clone().with_hidden(true).announced();
4148
4149		let hidden = producer.announce(".stats/node", Route::default()).unwrap();
4150		announced.assert_next_wait();
4151		opted.assert_next_active(".stats/node");
4152
4153		drop(hidden);
4154		announced.assert_next_wait();
4155		opted.assert_next_ended(".stats/node");
4156	}
4157
4158	#[tokio::test]
4159	async fn broadcast_announces_its_own_path() {
4160		let producer = origin(1).produce();
4161		let consumer = producer.consume();
4162		let mut announced = consumer.announced();
4163		let mut peer = consumer.clone().excluding(Hop::UNKNOWN).announced();
4164
4165		// Created but not announced: invisible to local and peer cursors alike.
4166		let broadcast = producer.create_broadcast("room/alice").unwrap();
4167		announced.assert_next_wait();
4168		peer.assert_next_wait();
4169
4170		broadcast.announce(Route::default().with_cost(3)).unwrap();
4171		assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(3));
4172		assert_eq!(peer.assert_next_active("room/alice").cost, Cost::new(3));
4173
4174		// Announcing again re-prices in place.
4175		broadcast.announce(Route::default().with_cost(1)).unwrap();
4176		assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(1));
4177		assert_eq!(peer.assert_next_active("room/alice").cost, Cost::new(1));
4178
4179		// Off the air: the route retracts for everyone and the path is unroutable.
4180		broadcast.unannounce();
4181		announced.assert_next_ended("room/alice");
4182		peer.assert_next_ended("room/alice");
4183		broadcast.unannounce();
4184		announced.assert_next_wait();
4185		let err = consumer.request_broadcast("room/alice").await.err().unwrap();
4186		assert!(matches!(err, Error::Unroutable));
4187
4188		// Back on the air, then the end of the broadcast retracts for good.
4189		broadcast.announce(Route::default()).unwrap();
4190		announced.assert_next_active("room/alice");
4191		peer.assert_next_active("room/alice");
4192		broadcast.close();
4193		announced.assert_next_ended("room/alice");
4194		peer.assert_next_ended("room/alice");
4195		assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
4196		announced.assert_next_wait();
4197	}
4198
4199	#[tokio::test]
4200	async fn broadcast_announcement_retracts_with_the_last_producer() {
4201		let producer = origin(1).produce();
4202		let consumer = producer.consume();
4203		let mut announced = consumer.announced();
4204
4205		let broadcast = producer.create_broadcast("room/alice").unwrap();
4206		let clone = broadcast.clone();
4207		broadcast.announce(Route::default()).unwrap();
4208		announced.assert_next_active("room/alice");
4209
4210		// A clone keeps the broadcast, and its advertisement, alive.
4211		drop(broadcast);
4212		announced.assert_next_wait();
4213		drop(clone);
4214		announced.assert_next_ended("room/alice");
4215	}
4216
4217	#[tokio::test]
4218	async fn publish_creates_and_announces_together() {
4219		let producer = origin(1).produce();
4220		let mut announced = producer.consume().announced();
4221		let _broadcast = producer.publish("room/alice", Route::default()).unwrap();
4222		announced.assert_next_active("room/alice");
4223	}
4224
4225	#[tokio::test]
4226	async fn standalone_broadcast_cannot_announce() {
4227		let broadcast = broadcast::Info::new().produce();
4228		assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
4229		// Harmless without an advertisement to retract.
4230		broadcast.unannounce();
4231	}
4232
4233	#[tokio::test]
4234	async fn announce_replays_to_late_cursor() {
4235		let producer = origin(1).produce();
4236		let _a = producer.announce("room/alice", Route::default()).unwrap();
4237		let _b = producer.announce("room/bob", Route::default()).unwrap();
4238
4239		let mut announced = producer.consume().announced();
4240		// BTreeMap order: lexicographic by prefix.
4241		announced.assert_next_active("room/alice");
4242		announced.assert_next_active("room/bob");
4243		announced.assert_next_wait();
4244	}
4245
4246	#[tokio::test]
4247	async fn announce_keeps_its_prefix_under_a_producer_scope() {
4248		let producer = origin(1).produce();
4249		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
4250
4251		// Prefix advertisements stay prefixes. The scope filters requests locally.
4252		let _a = scoped.announce("", Route::default()).unwrap();
4253		let mut announced = producer.consume().announced();
4254		announced.assert_next_active("");
4255
4256		// Disjoint prefixes cannot be claimed at all.
4257		assert!(matches!(
4258			scoped.announce("other", Route::default()),
4259			Err(Error::Unauthorized)
4260		));
4261	}
4262
4263	#[tokio::test]
4264	async fn cursor_keeps_an_overlapping_prefix_above_its_scope() {
4265		let producer = origin(1).produce();
4266		let _a = producer.announce("", Route::default()).unwrap();
4267
4268		let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
4269		let mut announced = consumer.announced();
4270		announced.assert_next_active("");
4271	}
4272
4273	#[tokio::test]
4274	async fn cursor_root_strips_prefix() {
4275		let producer = origin(1).produce();
4276		let _a = producer.announce("room/alice", Route::default()).unwrap();
4277
4278		let consumer = producer
4279			.consume()
4280			.scope("room", &Patterns::from(Pattern::all()))
4281			.unwrap();
4282		let mut announced = consumer.announced();
4283		announced.assert_next_active("alice");
4284	}
4285
4286	#[tokio::test]
4287	async fn best_route_wins_and_fails_over() {
4288		let producer = origin(1).produce();
4289		let mut announced = producer.consume().announced();
4290
4291		let expensive = producer
4292			.announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4293			.unwrap();
4294		let route = announced.assert_next_active("room");
4295		assert_eq!(route.cost, Cost::new(5));
4296
4297		// A cheaper route for the same prefix takes over in place.
4298		let cheap = producer
4299			.announce("room", Route::default().with_hops(hops(&[20])).with_cost(1))
4300			.unwrap();
4301		let route = announced.assert_next_active("room");
4302		assert_eq!(route.cost, Cost::new(1));
4303
4304		// Losing the winner falls back to the survivor, still in place.
4305		drop(cheap);
4306		let route = announced.assert_next_active("room");
4307		assert_eq!(route.cost, Cost::new(5));
4308
4309		// Losing the last retracts.
4310		drop(expensive);
4311		announced.assert_next_ended("room");
4312	}
4313
4314	#[tokio::test]
4315	async fn identical_reannounce_is_invisible() {
4316		let producer = origin(1).produce();
4317		let mut announced = producer.consume().announced();
4318
4319		let old = producer
4320			.announce("room", Route::default().with_hops(hops(&[10])))
4321			.unwrap();
4322		let first = announced.assert_next_active("room");
4323		assert_eq!(first.hops.as_slice(), hops(&[10]).as_slice());
4324
4325		// An identical route from a fresh announcement (a reconnect) changes
4326		// nothing a consumer could act on, so nothing is delivered; new requests
4327		// still prefer the newest entry.
4328		let _new = producer
4329			.announce("room", Route::default().with_hops(hops(&[10])))
4330			.unwrap();
4331		announced.assert_next_wait();
4332
4333		// Retracting the stale twin leaves the fresh one standing, still quietly.
4334		drop(old);
4335		announced.assert_next_wait();
4336	}
4337
4338	#[tokio::test]
4339	async fn exclude_hides_routes_through_the_peer() {
4340		let producer = origin(1).produce();
4341		let _a = producer
4342			.announce("room", Route::default().with_hops(hops(&[7])))
4343			.unwrap();
4344
4345		let mut hidden = producer.consume().excluding(origin(7)).announced();
4346		hidden.assert_next_wait();
4347
4348		let mut visible = producer.consume().excluding(origin(8)).announced();
4349		visible.assert_next_active("room");
4350	}
4351
4352	#[tokio::test]
4353	async fn exclude_matches_via_when_the_chain_is_anonymous() {
4354		let producer = origin(1).produce();
4355		let assigned = origin(777);
4356		let _echoed = producer
4357			.announce("echoed", Route::default().with_hops(hops(&[0])).with_via(assigned))
4358			.unwrap();
4359		let _local = producer
4360			.announce("local", Route::default().with_hops(hops(&[10])))
4361			.unwrap();
4362
4363		let mut hidden = producer.consume().excluding(assigned).announced();
4364		hidden.assert_next_active("local");
4365		hidden.assert_next_wait();
4366	}
4367
4368	#[tokio::test]
4369	async fn anonymous_route_loses_to_identified_at_any_cost() {
4370		let producer = origin(1).produce();
4371		let mut announced = producer.consume().announced();
4372
4373		let _anonymous = producer
4374			.announce("room", Route::default().with_hops(hops(&[0])).with_cost(1))
4375			.unwrap();
4376		let route = announced.assert_next_active("room");
4377		assert!(route.is_anonymous());
4378		assert_eq!(route.cost, Cost::new(1));
4379
4380		let _identified = producer
4381			.announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4382			.unwrap();
4383		let route = announced.assert_next_active("room");
4384		assert!(!route.is_anonymous());
4385		assert_eq!(route.cost, Cost::new(5));
4386	}
4387
4388	#[tokio::test]
4389	async fn anonymous_routes_order_by_cost() {
4390		let producer = origin(1).produce();
4391		let mut announced = producer.consume().announced();
4392
4393		let expensive = producer
4394			.announce("room", Route::default().with_hops(hops(&[0])).with_cost(5))
4395			.unwrap();
4396		let route = announced.assert_next_active("room");
4397		assert_eq!(route.cost, Cost::new(5));
4398
4399		let _cheap = producer
4400			.announce("room", Route::default().with_hops(hops(&[0, 7])).with_cost(1))
4401			.unwrap();
4402		let route = announced.assert_next_active("room");
4403		assert!(route.is_anonymous());
4404		assert_eq!(route.cost, Cost::new(1));
4405
4406		drop(expensive);
4407		announced.assert_next_wait();
4408	}
4409
4410	#[tokio::test]
4411	async fn anonymous_chain_from_identified_peer_still_ranks_last() {
4412		let producer = origin(1).produce();
4413		let mut announced = producer.consume().announced();
4414
4415		let _anonymous = producer
4416			.announce(
4417				"room",
4418				Route::default()
4419					.with_hops(hops(&[0, 7]))
4420					.with_cost(1)
4421					.with_via(origin(7)),
4422			)
4423			.unwrap();
4424		announced.assert_next_active("room");
4425
4426		let _identified = producer
4427			.announce("room", Route::default().with_hops(hops(&[10, 20])).with_cost(5))
4428			.unwrap();
4429		let route = announced.assert_next_active("room");
4430		assert!(!route.is_anonymous());
4431		assert_eq!(route.cost, Cost::new(5));
4432	}
4433
4434	#[tokio::test]
4435	async fn request_prefers_identified_over_cheaper_anonymous() {
4436		let producer = origin(1).produce();
4437		let consumer = producer.consume();
4438
4439		let anonymous = producer
4440			.dynamic("room", Route::default().with_hops(hops(&[0])).with_cost(1))
4441			.unwrap();
4442		let identified = producer
4443			.dynamic("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4444			.unwrap();
4445
4446		let _pending = consumer.request_broadcast("room/alice");
4447		let request = queued(&identified).await;
4448		assert_eq!(request.path().as_str(), "room/alice");
4449		assert!(
4450			anonymous.poll_requested_broadcast(&kio::Waiter::noop()).is_pending(),
4451			"the cheaper anonymous route must not serve"
4452		);
4453	}
4454
4455	#[tokio::test]
4456	async fn update_reprices_in_place() {
4457		let producer = origin(1).produce();
4458		let mut announced = producer.consume().announced();
4459
4460		let announcement = producer.announce("room", Route::default()).unwrap();
4461		announced.assert_next_active("room");
4462
4463		announcement.update(Route::default().with_cost(9)).unwrap();
4464		let route = announced.assert_next_active("room");
4465		assert_eq!(route.cost, Cost::new(9));
4466	}
4467
4468	#[tokio::test]
4469	async fn retract_after_undelivered_reprice_still_delivered() {
4470		let producer = origin(1).produce();
4471		let mut announced = producer.consume().announced();
4472
4473		let announcement = producer.announce("room", Route::default()).unwrap();
4474		announced.assert_next_active("room");
4475
4476		// Reprice, then retract before the consumer observes the reprice: the
4477		// pending metadata update must not cancel the retraction the delivered
4478		// announce still owes.
4479		announcement.update(Route::default().with_cost(9)).unwrap();
4480		drop(announcement);
4481		announced.assert_next_ended("room");
4482		announced.assert_next_wait();
4483	}
4484
4485	#[tokio::test]
4486	async fn scoped_cursor_advertises_most_specific_covering_route() {
4487		let producer = origin(1).produce();
4488		// Broad and cheap; narrow and expensive. Both present relative to a cursor
4489		// rooted below them, and the narrow one is what a request there resolves.
4490		let _broad = producer.announce("room", Route::default().with_cost(1)).unwrap();
4491		let _narrow = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
4492
4493		let consumer = producer
4494			.consume()
4495			.scope("room/alice", &Patterns::from(Pattern::all()))
4496			.unwrap();
4497		let mut announced = consumer.announced();
4498		let route = announced.assert_next_active("");
4499		assert_eq!(route.cost, Cost::new(9));
4500		announced.assert_next_wait();
4501	}
4502
4503	#[tokio::test]
4504	async fn capture_change_retracts_before_reannouncing_a_presented_prefix() {
4505		let producer = origin(1).produce();
4506		let _broad = producer.announce("room", Route::default()).unwrap();
4507		let exact = producer.announce("room/alice", Route::default()).unwrap();
4508		let consumer = producer
4509			.consume()
4510			.scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
4511			.unwrap()
4512			.scope("room/alice", &Patterns::from(Pattern::all()))
4513			.unwrap();
4514		let mut announced = consumer.announced();
4515
4516		let first = announced.next().now_or_never().expect("next").expect("announce");
4517		assert_eq!(first.prefix.as_str(), "");
4518		assert_eq!(first.kind, AnnounceKind::Announced);
4519		assert_eq!(first.captures, Some(Vec::new()));
4520
4521		drop(exact);
4522		let retracted = announced.next().now_or_never().expect("next").expect("retract");
4523		assert_eq!(retracted.prefix.as_str(), "");
4524		assert_eq!(retracted.kind, AnnounceKind::Retracted);
4525		assert_eq!(retracted.captures, Some(Vec::new()));
4526		let replacement = announced.next().now_or_never().expect("next").expect("announce");
4527		assert_eq!(replacement.prefix.as_str(), "");
4528		assert_eq!(replacement.kind, AnnounceKind::Announced);
4529		assert_eq!(replacement.captures, None);
4530	}
4531
4532	#[tokio::test]
4533	async fn routed_broadcast_resolves_once_announced() {
4534		let producer = origin(1).produce();
4535		let consumer = producer.consume();
4536
4537		// Asking before anything is announced parks instead of failing Unroutable.
4538		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4539		assert!((&mut resolving).now_or_never().is_none());
4540
4541		// Creating is not announcing: still parked.
4542		let broadcast = producer.create_broadcast("room/alice").unwrap();
4543		for _ in 0..20 {
4544			tokio::task::yield_now().await;
4545		}
4546		assert!((&mut resolving).now_or_never().is_none());
4547
4548		broadcast.announce(Route::default()).unwrap();
4549		let resolved = resolving.await.expect("resolves once announced");
4550		assert_eq!(resolved.info().path.as_str(), "room/alice");
4551		drop(broadcast);
4552	}
4553
4554	/// A local broadcast competes on its announced cost: a cheaper route at the
4555	/// same path wins, for cursors and requests alike.
4556	#[tokio::test]
4557	async fn cheaper_remote_route_beats_a_local_broadcast() {
4558		let producer = origin(1).produce();
4559		let consumer = producer.consume();
4560		let mut announced = consumer.announced();
4561
4562		let _local = producer.publish("room/alice", Route::default().with_cost(5)).unwrap();
4563		assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(5));
4564
4565		let server = producer
4566			.dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(1))
4567			.unwrap();
4568		let route = announced.assert_next_active("room/alice");
4569		assert_eq!(route.cost, Cost::new(1));
4570		assert_eq!(route.hops, hops(&[10]));
4571
4572		// The request goes upstream rather than to the local broadcast.
4573		let pending = consumer.request_broadcast("room/alice");
4574		let request = queued(&server).await;
4575		let upstream = broadcast::Info::new().produce();
4576		request.accept(&upstream);
4577		pending.await.expect("resolves through the cheaper route");
4578	}
4579
4580	/// A cheaper route that appears after a front was minted wins new requests too:
4581	/// the cached front serves other content, so a newcomer gets a fresh front from
4582	/// the winner, while the old front keeps serving the readers it already has.
4583	#[tokio::test]
4584	async fn cheaper_route_after_a_front_wins_new_requests() {
4585		let producer = origin(1).produce();
4586		let consumer = producer.consume();
4587
4588		let _local = producer.publish("room/alice", Route::default().with_cost(5)).unwrap();
4589		let first = consumer
4590			.request_broadcast("room/alice")
4591			.await
4592			.expect("resolves locally");
4593
4594		let server = producer
4595			.dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(1))
4596			.unwrap();
4597
4598		let pending = consumer.request_broadcast("room/alice");
4599		let request = queued(&server).await;
4600		let upstream = broadcast::Info::new().produce();
4601		request.accept(&upstream);
4602		let second = pending.await.expect("resolves through the cheaper route");
4603
4604		assert!(!first.is_closed(), "the old front must keep serving its readers");
4605		assert!(!first.is_clone(&second), "the newcomer must not join the old front");
4606	}
4607
4608	/// At equal cost the local broadcast wins even over a route with no hops of its
4609	/// own, such as a later claim on this origin: locality is the tie-break after
4610	/// cost, not the newest entry.
4611	#[tokio::test]
4612	async fn local_broadcast_wins_a_tie_with_a_hopless_route() {
4613		let producer = origin(1).produce();
4614		let consumer = producer.consume();
4615
4616		let _local = producer.publish("room/alice", Route::default()).unwrap();
4617		let server = producer.dynamic("room/alice", Route::default()).unwrap();
4618
4619		// Were the claim to win, the request would park on its handler forever.
4620		let resolved = tokio::time::timeout(Duration::from_secs(1), consumer.request_broadcast("room/alice"))
4621			.await
4622			.expect("the newer hopless route won the tie")
4623			.expect("resolves");
4624		assert_eq!(resolved.info().path.as_str(), "room/alice");
4625		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4626	}
4627
4628	/// Ingress announce stats count advertised intervals, not the broadcast's
4629	/// lifetime: nothing while hidden, one per announce, none for a re-price.
4630	#[tokio::test]
4631	async fn announce_stats_follow_the_advertisement() {
4632		let registry = stats::Registry::new(stats::Config::new());
4633		let producer = origin(1)
4634			.produce()
4635			.with_stats(registry.tier(stats::Tier::default()).session("root"));
4636		let announces = || {
4637			registry
4638				.snapshot()
4639				.traffic()
4640				.into_iter()
4641				.find(|(_, role, _)| *role == stats::Role::Subscriber)
4642				.map(|(_, _, traffic)| (traffic.announces_started, traffic.announces_ended))
4643				.unwrap_or_default()
4644		};
4645
4646		let broadcast = producer.create_broadcast("room/alice").unwrap();
4647		assert_eq!(announces(), (0, 0), "a hidden broadcast is not announced");
4648		broadcast.announce(Route::default()).unwrap();
4649		broadcast
4650			.announce(Route {
4651				cost: Cost::new(3),
4652				..Route::default()
4653			})
4654			.unwrap();
4655		assert_eq!(announces(), (1, 0), "a re-price is not another announce");
4656		broadcast.unannounce();
4657		assert_eq!(announces(), (1, 1));
4658		broadcast.announce(Route::default()).unwrap();
4659		drop(broadcast);
4660		assert_eq!(announces(), (2, 2));
4661	}
4662
4663	/// At equal cost the local broadcast wins.
4664	#[tokio::test]
4665	async fn local_broadcast_wins_a_cost_tie() {
4666		let producer = origin(1).produce();
4667		let consumer = producer.consume();
4668		let mut announced = consumer.announced();
4669
4670		let server = producer
4671			.dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(2))
4672			.unwrap();
4673		announced.assert_next_active("room/alice");
4674		let _local = producer.publish("room/alice", Route::default().with_cost(2)).unwrap();
4675		assert!(announced.assert_next_active("room/alice").hops.is_empty());
4676
4677		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4678		assert_eq!(resolved.info().path.as_str(), "room/alice");
4679		for _ in 0..20 {
4680			tokio::task::yield_now().await;
4681		}
4682		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4683	}
4684
4685	/// Unannouncing ends the front the origin served from the broadcast and
4686	/// refuses new requests at once, even before the front acts on it.
4687	#[tokio::test]
4688	async fn unannounce_ends_the_front() {
4689		let producer = origin(1).produce();
4690		let consumer = producer.consume();
4691
4692		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4693		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4694
4695		broadcast.unannounce();
4696		let err = consumer.request_broadcast("room/alice").await.err().unwrap();
4697		assert!(matches!(err, Error::Unroutable), "joined a retracted front: {err}");
4698		settle(|| resolved.is_closed()).await;
4699		assert!(!broadcast.consume().is_closed(), "the broadcast itself lives on");
4700
4701		// Announcing again serves a fresh front.
4702		broadcast.announce(Route::default()).unwrap();
4703		let again = consumer.request_broadcast("room/alice").await.expect("resolves again");
4704		assert!(!again.is_clone(&resolved));
4705	}
4706
4707	/// A subscriber still waiting on the source's track info is in flight too:
4708	/// unannouncing leaves it on the copy it asked for, which the source can
4709	/// still answer and finish.
4710	#[tokio::test]
4711	async fn unannounce_keeps_a_track_awaiting_its_info() {
4712		let producer = origin(1).produce();
4713		let consumer = producer.consume();
4714
4715		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4716		let mut dynamic = broadcast.dynamic();
4717		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4718		let track = resolved.track("video").unwrap();
4719		let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4720		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4721			.await
4722			.expect("the front asked the source")
4723			.expect("request");
4724
4725		broadcast.unannounce();
4726		settle(|| resolved.is_closed()).await;
4727
4728		let source = request.accept(None);
4729		let mut group = source.append_group().unwrap();
4730		group.write_frame(crate::Timestamp::ZERO, b"late".as_ref()).unwrap();
4731		group.finish().unwrap();
4732		source.finish().unwrap();
4733
4734		let mut subscription = subscribing.await.unwrap().expect("subscribe survives the retraction");
4735		let mut group = subscription.recv_group().await.unwrap().expect("the source's group");
4736		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"late");
4737		assert!(matches!(subscription.recv_group().await, Ok(None)), "ends cleanly");
4738	}
4739
4740	/// A front resolved through a served route, as a relay's upstream session serves
4741	/// one: the upstream broadcast's track requests arrive on the returned handle.
4742	async fn served_front() -> (Dynamic, broadcast::Producer, broadcast::Dynamic, broadcast::Consumer) {
4743		let producer = origin(1).produce();
4744		let consumer = producer.consume();
4745		let server = producer
4746			.dynamic("room/alice", Route::default().with_hops(hops(&[10])))
4747			.unwrap();
4748		let pending = consumer.request_broadcast("room/alice");
4749		let upstream = broadcast::Info::new().produce();
4750		let dynamic = upstream.dynamic();
4751		queued(&server).await.accept(&upstream);
4752		let resolved = pending.await.expect("resolves");
4753		(server, upstream, dynamic, resolved)
4754	}
4755
4756	/// A reader returning to a parked track waits for the fresh copy to resolve its
4757	/// start, and skips the warm cache when the copy resolves past it: the source
4758	/// judged the groups in between stale, so the older cache is stale too. Without the
4759	/// hold the reader was handed the whole warm cache first, seconds behind live.
4760	#[tokio::test]
4761	async fn returning_reader_skips_a_warm_cache_the_copy_resolved_past() {
4762		let ms = |v: u64| crate::Timestamp::from_millis(v).unwrap();
4763		let (_server, _upstream, mut dynamic, resolved) = served_front().await;
4764		let budget = track::Subscription::default().with_max_age(Duration::from_millis(100));
4765
4766		let track = resolved.track("audio").unwrap();
4767		let b = budget.clone();
4768		let subscribing = tokio::spawn(async move { track.subscribe(b).await });
4769		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4770			.await
4771			.expect("the front asked the source")
4772			.expect("request");
4773		let source = request.resolving_start().accept(None);
4774		for seq in 0..4u64 {
4775			let mut group = source.create_group(seq.into()).unwrap();
4776			group.write_frame(ms(seq * 20), b"old".as_ref()).unwrap();
4777			group.finish().unwrap();
4778		}
4779		let mut subscription = subscribing.await.unwrap().expect("subscribe");
4780		subscription.recv_group().await.unwrap().expect("the live group");
4781		drop(subscription);
4782		tokio::time::timeout(Duration::from_secs(1), source.unused())
4783			.await
4784			.expect("parked")
4785			.expect("source open");
4786		drop(source);
4787
4788		let track = resolved.track("audio").unwrap();
4789		let subscribing = tokio::spawn(async move { track.subscribe(budget).await });
4790		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4791			.await
4792			.expect("the front asked the source again")
4793			.expect("request");
4794		let mut source = request.resolving_start().accept(None);
4795		let mut subscription = subscribing.await.unwrap().expect("resubscribe");
4796
4797		// The copy has not resolved its start: nothing is handed out yet.
4798		assert!(
4799			tokio::time::timeout(Duration::from_millis(50), subscription.recv_group())
4800				.await
4801				.is_err(),
4802			"the warm cache was served before the copy resolved its start"
4803		);
4804
4805		// The source resolves past the floor (lite-06 skipped 4..20 as stale).
4806		source.start_at(20).unwrap();
4807		let mut group = source.create_group(20u64.into()).unwrap();
4808		group.write_frame(ms(2000), b"new".as_ref()).unwrap();
4809		group.finish().unwrap();
4810		let group = subscription.recv_group().await.unwrap().expect("the live group");
4811		assert_eq!(group.sequence, 20, "a stale warm group was served");
4812	}
4813
4814	/// A warm cache whose newest group finished still resumes when the source has
4815	/// nothing newer: the re-splice asks for that group's tail, which a source that
4816	/// resolves starts lazily (with its first served group) can answer at once. Asking
4817	/// past it left a returning catalog reader waiting for the next catalog change.
4818	#[tokio::test]
4819	async fn returning_reader_replays_a_current_warm_cache() {
4820		let (_server, _upstream, mut dynamic, resolved) = served_front().await;
4821
4822		let track = resolved.track("catalog").unwrap();
4823		let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4824		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4825			.await
4826			.expect("the front asked the source")
4827			.expect("request");
4828		let source = request.resolving_start().accept(None);
4829		let mut group = source.create_group(0u64.into()).unwrap();
4830		group.write_frame(crate::Timestamp::ZERO, b"snapshot".as_ref()).unwrap();
4831		group.finish().unwrap();
4832		let mut subscription = subscribing.await.unwrap().expect("subscribe");
4833		subscription.recv_group().await.unwrap().expect("the catalog");
4834		drop(subscription);
4835		tokio::time::timeout(Duration::from_secs(1), source.unused())
4836			.await
4837			.expect("parked")
4838			.expect("source open");
4839		drop(source);
4840
4841		let track = resolved.track("catalog").unwrap();
4842		let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4843		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4844			.await
4845			.expect("the front asked the source again")
4846			.expect("request");
4847		let mut source = request.resolving_start().accept(None);
4848		let mut subscription = subscribing.await.unwrap().expect("resubscribe");
4849
4850		// The source still has group 0 as its newest: it serves the empty tail, and
4851		// that is when its start resolves.
4852		let reading = tokio::spawn(async move {
4853			let mut group = subscription.recv_group().await.unwrap().expect("the catalog");
4854			assert_eq!(group.sequence, 0);
4855			group.read_frame().await.unwrap().expect("the snapshot").payload
4856		});
4857		tokio::task::yield_now().await;
4858		assert_eq!(
4859			source.subscription().and_then(|sub| sub.start),
4860			Some(track::Position { group: 0, frame: 1 }),
4861			"the re-splice asked past the cached catalog"
4862		);
4863		source.start_at(0).unwrap();
4864		let mut tail = source.create_group(0u64.into()).unwrap();
4865		tail.start_at(1).unwrap();
4866		tail.finish().unwrap();
4867		let payload = tokio::time::timeout(Duration::from_secs(1), reading)
4868			.await
4869			.expect("the returning reader never got the catalog")
4870			.unwrap();
4871		assert_eq!(&payload[..], b"snapshot");
4872	}
4873
4874	/// A group that stays open for good (a JSON log in group 0) survives a park: the
4875	/// returning reader gets the frames delivered before it from the warm cache, and the
4876	/// re-splice asks the source only for the frames after them, across repeated parks.
4877	/// A datagram sequenced past the group does not hide it as the live edge.
4878	#[tokio::test]
4879	async fn returning_reader_continues_an_open_warm_group() {
4880		let (_server, _upstream, mut dynamic, resolved) = served_front().await;
4881
4882		async fn read(group: &mut group::Consumer) -> Vec<u8> {
4883			let frame = tokio::time::timeout(Duration::from_secs(1), group.read_frame())
4884				.await
4885				.expect("frame")
4886				.unwrap()
4887				.expect("group ended");
4888			frame.payload.to_vec()
4889		}
4890
4891		let mut expect: Vec<&[u8]> = Vec::new();
4892		let mut floor: Option<track::Position> = None;
4893		for (round, payload) in [b"a".as_ref(), b"b", b"c"].into_iter().enumerate() {
4894			let track = resolved.track("log").unwrap();
4895			let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4896			let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4897				.await
4898				.expect("the front asked the source")
4899				.expect("request");
4900			let mut source = request.resolving_start().accept(None);
4901			let mut subscription = subscribing.await.unwrap().expect("subscribe");
4902
4903			// The source resolves at the floor's group, continuing group 0.
4904			source.start_at(0).unwrap();
4905			let mut group = source.create_group(0u64.into()).unwrap();
4906			if let Some(floor) = floor {
4907				group.start_at(floor.frame).unwrap();
4908			}
4909			group.write_frame(crate::Timestamp::ZERO, payload).unwrap();
4910			expect.push(payload);
4911			source
4912				.insert_datagram(10, crate::Timestamp::ZERO, b"datagram".as_ref())
4913				.unwrap();
4914
4915			let mut reading = tokio::time::timeout(Duration::from_secs(1), subscription.recv_group())
4916				.await
4917				.expect("group 0")
4918				.unwrap()
4919				.expect("track ended");
4920			assert_eq!(reading.sequence, 0);
4921			for frame in &expect {
4922				assert_eq!(read(&mut reading).await, *frame, "round {round}");
4923			}
4924			assert_eq!(
4925				source.subscription().and_then(|sub| sub.start),
4926				floor,
4927				"round {round} asked for the wrong continuation"
4928			);
4929
4930			drop(reading);
4931			drop(subscription);
4932			tokio::time::timeout(Duration::from_secs(1), source.unused())
4933				.await
4934				.expect("parked")
4935				.expect("source open");
4936			drop(group);
4937			drop(source);
4938			floor = Some(track::Position {
4939				group: 0,
4940				frame: expect.len() as u64,
4941			});
4942		}
4943	}
4944
4945	/// The same holds for a reader returning to a parked track: its warm cache
4946	/// does not stand in for the copy it is waiting on.
4947	#[tokio::test]
4948	async fn unannounce_keeps_a_returning_reader_awaiting_its_info() {
4949		let producer = origin(1).produce();
4950		let consumer = producer.consume();
4951
4952		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4953		let mut dynamic = broadcast.dynamic();
4954		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4955		let track = resolved.track("video").unwrap();
4956		let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4957		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4958			.await
4959			.expect("the front asked the source")
4960			.expect("request");
4961		let source = request.accept(None);
4962		let mut group = source.append_group().unwrap();
4963		group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
4964		group.finish().unwrap();
4965		let mut subscription = subscribing.await.unwrap().expect("subscribe");
4966		subscription.recv_group().await.unwrap().expect("the cached group");
4967		drop(subscription);
4968
4969		// Parked: the source copy goes, the delivered group stays warm. The source
4970		// then tears its idle track down, so a returning reader asks it afresh.
4971		tokio::time::timeout(Duration::from_secs(1), source.unused())
4972			.await
4973			.expect("parked")
4974			.expect("source open");
4975		drop(source);
4976		let track = resolved.track("video").unwrap();
4977		let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4978		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4979			.await
4980			.expect("the front asked the source again")
4981			.expect("request");
4982
4983		broadcast.unannounce();
4984		settle(|| resolved.is_closed()).await;
4985
4986		// A fresh source copy numbers groups past what the cache already delivered.
4987		let source = request.accept(None);
4988		let mut group = source.create_group(1u64.into()).unwrap();
4989		group.write_frame(crate::Timestamp::ZERO, b"late".as_ref()).unwrap();
4990		group.finish().unwrap();
4991		source.finish().unwrap();
4992
4993		let mut subscription = subscribing.await.unwrap().expect("subscribe survives the retraction");
4994		let mut payloads = Vec::new();
4995		while let Some(mut group) = subscription.recv_group().await.expect("ends cleanly") {
4996			payloads.push(group.read_frame().await.unwrap().unwrap().payload);
4997		}
4998		assert_eq!(payloads.last().map(|p| &p[..]), Some(&b"late"[..]));
4999	}
5000
5001	/// A re-announce that lands before the front acts on the retraction reuses
5002	/// the same route entry, so the front carries on.
5003	#[tokio::test]
5004	async fn reannounce_before_the_front_acts_keeps_it() {
5005		let producer = origin(1).produce();
5006		let consumer = producer.consume();
5007
5008		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
5009		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5010
5011		broadcast.unannounce();
5012		broadcast.announce(Route::default()).unwrap();
5013		for _ in 0..20 {
5014			tokio::task::yield_now().await;
5015		}
5016		assert!(!resolved.is_closed(), "the front ended across a reannouncement");
5017		let again = consumer.request_broadcast("room/alice").await.expect("resolves");
5018		assert!(again.is_clone(&resolved));
5019	}
5020
5021	#[tokio::test]
5022	async fn local_broadcast_resolves_once_announced() {
5023		let producer = origin(1).produce();
5024		let consumer = producer.consume();
5025
5026		// Created but not announced: nobody can reach it, locally included.
5027		let broadcast = producer.create_broadcast("room/alice").unwrap();
5028		let err = consumer
5029			.request_broadcast("room/alice")
5030			.now_or_never()
5031			.expect("unroutable is synchronous")
5032			.err()
5033			.unwrap();
5034		assert!(matches!(err, Error::Unroutable));
5035
5036		broadcast.announce(Route::default()).unwrap();
5037		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5038		assert_eq!(resolved.info().path.as_str(), "room/alice");
5039		drop(broadcast);
5040
5041		// Nothing covers an unknown path and no handler exists.
5042		let err = consumer
5043			.request_broadcast("room/bob")
5044			.now_or_never()
5045			.expect("unroutable is synchronous")
5046			.err()
5047			.unwrap();
5048		assert!(matches!(err, Error::Unroutable));
5049	}
5050
5051	#[test]
5052	fn create_broadcast_accepts_a_max_depth_path() {
5053		let producer = origin(1).produce();
5054		let path = vec!["a"; Path::MAX_PARTS].join("/");
5055		let _broadcast = producer.create_broadcast(path.as_str()).expect("max depth is allowed");
5056		let deeper = vec!["a"; Path::MAX_PARTS + 1].join("/");
5057		assert!(matches!(
5058			producer.create_broadcast(deeper.as_str()),
5059			Err(Error::BoundsExceeded(_))
5060		));
5061	}
5062
5063	#[tokio::test]
5064	async fn duplicate_routes_aggregate_until_the_last_leaves() {
5065		let producer = origin(1).produce();
5066		let first = producer.dynamic("live", Route::default().with_cost(3)).unwrap();
5067		let second = producer.dynamic("live", Route::default().with_cost(1)).unwrap();
5068
5069		let mut announced = producer.consume().announced();
5070		let update = announced.next().now_or_never().expect("next").expect("no next");
5071		assert_eq!(update.prefix.as_str(), "live");
5072		assert_eq!(update.kind, AnnounceKind::Announced);
5073		assert_eq!(update.route.cost, Cost::new(1));
5074		announced.assert_next_wait();
5075
5076		drop(second);
5077		let update = announced.next().now_or_never().expect("next").expect("no next");
5078		assert_eq!(update.prefix.as_str(), "live");
5079		assert_eq!(update.kind, AnnounceKind::Updated);
5080		assert_eq!(update.route.cost, Cost::new(3));
5081
5082		drop(first);
5083		announced.assert_next_ended("live");
5084		announced.assert_next_wait();
5085	}
5086
5087	#[test]
5088	fn dynamic_may_cover_a_scope_but_disjoint_prefixes_are_refused() {
5089		let producer = origin(1).produce();
5090		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
5091		let _broad = scoped
5092			.dynamic("", Route::default())
5093			.expect("an overlapping prefix is accepted");
5094
5095		let _ok = scoped
5096			.dynamic("room/alice", Route::default())
5097			.expect("a contained prefix is accepted");
5098		assert!(matches!(
5099			scoped.dynamic("other", Route::default()),
5100			Err(Error::Unauthorized)
5101		));
5102	}
5103
5104	#[tokio::test]
5105	async fn dynamic_route_keeps_its_producer_scope() {
5106		let producer = origin(1).produce();
5107		let scope = Patterns::from("*/chat".parse::<Pattern>().unwrap());
5108		let scoped = producer.scope("", &scope).unwrap();
5109		let dynamic = scoped.dynamic("", Route::default()).unwrap();
5110
5111		let mut matching = producer
5112			.consume()
5113			.scope("", &scopes(&["room/chat"]))
5114			.unwrap()
5115			.announced();
5116		matching.assert_next_active("");
5117		let mut outside = producer
5118			.consume()
5119			.scope("", &scopes(&["room/video"]))
5120			.unwrap()
5121			.announced();
5122		outside.assert_next_wait();
5123
5124		let refused = producer
5125			.consume()
5126			.request_broadcast("room/video")
5127			.now_or_never()
5128			.expect("an out-of-scope request must be refused synchronously");
5129		assert!(matches!(refused, Err(Error::Unroutable)));
5130		assert!(dynamic.requested_broadcast().now_or_never().is_none());
5131
5132		let _pending = producer.consume().request_broadcast("room/chat");
5133		let request = queued(&dynamic).await;
5134		assert_eq!(request.path().as_str(), "room/chat");
5135	}
5136
5137	#[tokio::test]
5138	async fn dynamic_accepts_a_max_depth_prefix() {
5139		let producer = origin(1).produce();
5140		let path = (0..Path::MAX_PARTS)
5141			.map(|i| format!("s{i}"))
5142			.collect::<Vec<_>>()
5143			.join("/");
5144		let mut announced = producer.consume().announced();
5145
5146		let dynamic = producer.dynamic(&path, Route::default()).expect("max depth is allowed");
5147		announced.assert_next_active(&path);
5148
5149		let _pending = producer.consume().request_broadcast(&path);
5150		let request = queued(&dynamic).await;
5151		assert_eq!(request.path().as_str(), path);
5152	}
5153
5154	#[tokio::test]
5155	async fn dynamic_exclusion_skips_routes_through_the_subscriber() {
5156		let producer = origin(1).produce();
5157		let _server = producer
5158			.dynamic("live", Route::default().with_hops(hops(&[7])))
5159			.unwrap();
5160
5161		let mut excluded = producer.consume().excluding(origin(7)).announced();
5162		excluded.assert_next_wait();
5163
5164		let mut clean = producer.consume().excluding(origin(8)).announced();
5165		clean.assert_next_active("live");
5166	}
5167
5168	/// The consumer is a `Stream` of the same updates as `next`.
5169	#[tokio::test]
5170	async fn announce_consumer_is_a_stream() {
5171		use futures::StreamExt;
5172		let producer = origin(1).produce();
5173		let server = producer.dynamic("live", Route::default()).unwrap();
5174		let mut announced = producer.consume().announced();
5175		let update = StreamExt::next(&mut announced)
5176			.now_or_never()
5177			.expect("next")
5178			.expect("no next");
5179		assert_eq!(update.prefix.as_str(), "live");
5180		assert_eq!(update.kind, AnnounceKind::Announced);
5181		assert!(StreamExt::next(&mut announced).now_or_never().is_none());
5182		drop(server);
5183		let update = StreamExt::next(&mut announced)
5184			.now_or_never()
5185			.expect("next")
5186			.expect("no next");
5187		assert_eq!(update.kind, AnnounceKind::Retracted);
5188	}
5189
5190	#[tokio::test]
5191	async fn dynamic_retracts() {
5192		let producer = origin(1).produce();
5193		let server = producer.dynamic("live", Route::default()).unwrap();
5194		let mut announced = producer.consume().announced();
5195		announced.assert_next_active("live");
5196
5197		drop(server);
5198		announced.assert_next_ended("live");
5199	}
5200
5201	#[test]
5202	fn charged_wildcard_cost_accumulates_across_hops() {
5203		let first = Cost::new(4).charged(1);
5204		let second = first.charged(2);
5205		assert_eq!(second, Cost { warm: 7, cold: 7 });
5206	}
5207
5208	#[tokio::test]
5209	async fn local_broadcast_is_invisible_until_announced() {
5210		let producer = origin(1).produce();
5211		let mut local = producer.consume().announced();
5212		let mut peer = producer.consume().excluding(Hop::UNKNOWN).announced();
5213		let broadcast = producer.create_broadcast("room/alice").unwrap();
5214		local.assert_next_wait();
5215		peer.assert_next_wait();
5216
5217		broadcast.announce(Route::default()).unwrap();
5218		local.assert_next_active("room/alice");
5219		peer.assert_next_active("room/alice");
5220
5221		drop(broadcast);
5222		local.assert_next_ended("room/alice");
5223		peer.assert_next_ended("room/alice");
5224	}
5225
5226	#[tokio::test]
5227	async fn served_route_materializes_on_demand() {
5228		let producer = origin(1).produce();
5229		let consumer = producer.consume();
5230
5231		let server = producer.dynamic("room", Route::default()).unwrap();
5232
5233		let pending = consumer.request_broadcast("room/alice");
5234		let request = queued(&server).await;
5235		assert_eq!(request.path().as_str(), "room/alice");
5236
5237		let source = broadcast::Info::new().produce();
5238		request.accept(&source);
5239
5240		let resolved = pending.await.expect("resolves");
5241		// The handle is named by what the requester asked for.
5242		assert_eq!(resolved.info().path.as_str(), "room/alice");
5243
5244		// A repeat request shares the served broadcast instead of re-asking.
5245		let again = consumer.request_broadcast("room/alice").await.expect("resolves");
5246		assert!(again.is_clone(&resolved));
5247	}
5248
5249	#[tokio::test]
5250	async fn served_requests_coalesce() {
5251		let producer = origin(1).produce();
5252		let consumer = producer.consume();
5253		let server = producer.dynamic("room", Route::default()).unwrap();
5254
5255		let first = consumer.request_broadcast("room/alice");
5256		let second = consumer.request_broadcast("room/alice");
5257
5258		let request = queued(&server).await;
5259		// Only one request reaches the server.
5260		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5261
5262		let source = broadcast::Info::new().produce();
5263		request.accept(&source);
5264
5265		let first = first.await.expect("resolves");
5266		let second = second.await.expect("resolves");
5267		assert!(first.is_clone(&second));
5268	}
5269
5270	#[tokio::test]
5271	async fn retract_rejects_pending_requests() {
5272		let producer = origin(1).produce();
5273		let consumer = producer.consume();
5274		let server = producer.dynamic("room", Route::default()).unwrap();
5275
5276		let pending = consumer.request_broadcast("room/alice");
5277		drop(server);
5278
5279		let err = pending.await.err().unwrap();
5280		assert!(matches!(err, Error::Unroutable));
5281
5282		// With the route gone, later requests are unroutable immediately.
5283		let err = consumer
5284			.request_broadcast("room/alice")
5285			.now_or_never()
5286			.expect("unroutable")
5287			.err()
5288			.unwrap();
5289		assert!(matches!(err, Error::Unroutable));
5290	}
5291
5292	#[tokio::test]
5293	async fn routed_broadcast_survives_serving_route_retraction() {
5294		let producer = origin(1).produce();
5295		let consumer = producer.consume();
5296
5297		// Three identical routes, oldest first: the newest identical route wins
5298		// requests, and swapping between them emits no announce update.
5299		let standby_server = producer.dynamic("room", Route::default()).unwrap();
5300		let second_server = producer.dynamic("room", Route::default()).unwrap();
5301		let incumbent_server = producer.dynamic("room", Route::default()).unwrap();
5302
5303		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5304		assert!((&mut resolving).now_or_never().is_none());
5305
5306		// Each incumbent dies with the front's request in flight on it: the
5307		// front's watcher observes the retraction and retries through the next
5308		// standby instead of parking on an announce update that never comes. Two
5309		// retractions in a row, so the announce stream's initial coverage replay
5310		// cannot paper over the missing retry.
5311		drop(incumbent_server);
5312		assert!((&mut resolving).now_or_never().is_none());
5313		drop(second_server);
5314		assert!((&mut resolving).now_or_never().is_none());
5315
5316		let request = queued(&standby_server).await;
5317		let source = broadcast::Info::new().produce();
5318		request.accept(&source);
5319
5320		let resolved = resolving.await.expect("resolves via the standby");
5321		assert_eq!(resolved.info().path.as_str(), "room/alice");
5322	}
5323
5324	#[tokio::test]
5325	async fn split_horizon_skips_routes_through_the_requester() {
5326		let producer = origin(1).produce();
5327		let _server = producer
5328			.dynamic("room", Route::default().with_hops(hops(&[7])))
5329			.unwrap();
5330
5331		// The requester's own bytes must not be served back to it.
5332		let excluded = producer.consume().excluding(origin(7));
5333		let err = excluded
5334			.request_broadcast("room/alice")
5335			.now_or_never()
5336			.expect("unroutable")
5337			.err()
5338			.unwrap();
5339		assert!(matches!(err, Error::Unroutable));
5340
5341		// A clean requester resolves through the route (the request queues).
5342		let clean = producer.consume().excluding(origin(8));
5343		let pending = clean.request_broadcast("room/alice");
5344		assert!(pending.now_or_never().is_none());
5345	}
5346
5347	#[tokio::test]
5348	async fn routes_report_where_they_entered() {
5349		let producer = origin(1).produce();
5350		let peer = producer.clone().peer();
5351		let mut announced = producer.consume().announced();
5352
5353		let _ingest = producer
5354			.dynamic("client", Route::default().with_hops(hops(&[5])).with_via(origin(5)))
5355			.unwrap();
5356		let _gateway = producer.publish("gateway", Route::default()).unwrap();
5357		let _forwarded = peer
5358			.dynamic(
5359				"forwarded",
5360				Route::default().with_hops(hops(&[5, 7])).with_via(origin(7)),
5361			)
5362			.unwrap();
5363
5364		assert_eq!(announced.assert_next_active("client").source(), Source::Local);
5365		assert_eq!(
5366			announced.assert_next_active("forwarded").source(),
5367			Source::Peer(origin(7))
5368		);
5369		assert_eq!(announced.assert_next_active("gateway").source(), Source::Local);
5370
5371		// The mark survives narrowing the handle.
5372		let scoped = peer.scope("room", &Patterns::from(Pattern::all())).unwrap();
5373		let _nested = scoped.dynamic("x", Route::default().with_via(origin(8))).unwrap();
5374		assert_eq!(announced.assert_next_active("room/x").source(), Source::Peer(origin(8)));
5375	}
5376
5377	/// A change of source alone is delivered: the same chain and cost arriving
5378	/// from a peer instead of a client is a different fact for the consumer.
5379	#[tokio::test]
5380	async fn source_change_is_an_update() {
5381		let producer = origin(1).produce();
5382		let peer = producer.clone().peer();
5383		let mut announced = producer.consume().announced();
5384
5385		let route = Route::default().with_hops(hops(&[7])).with_via(origin(7));
5386		let _forwarded = peer.dynamic("room", route.clone()).unwrap();
5387		assert_eq!(announced.assert_next_active("room").source(), Source::Peer(origin(7)));
5388
5389		// The newest identical route wins, so the local twin takes over.
5390		let local = producer.dynamic("room", route).unwrap();
5391		let update = announced.next().now_or_never().expect("next blocked").expect("no next");
5392		assert_eq!(update.kind, AnnounceKind::Updated);
5393		assert_eq!(update.route.source(), Source::Local);
5394
5395		drop(local);
5396		assert_eq!(announced.assert_next_active("room").source(), Source::Peer(origin(7)));
5397	}
5398
5399	#[tokio::test]
5400	async fn local_view_hides_peer_routes() {
5401		let producer = origin(1).produce();
5402		let peer = producer.clone().peer();
5403		let mut local = producer.consume().local().announced();
5404
5405		let _forwarded = peer
5406			.dynamic("remote", Route::default().with_hops(hops(&[7])).with_via(origin(7)))
5407			.unwrap();
5408		local.assert_next_wait();
5409
5410		// A path both ingested here and forwarded by a peer shows the local route,
5411		// and retracts from the local view when the local route goes, even though
5412		// the peer's still covers it.
5413		let _shadow = peer
5414			.dynamic("both", Route::default().with_hops(hops(&[7])).with_via(origin(7)))
5415			.unwrap();
5416		let ingest = producer
5417			.dynamic(
5418				"both",
5419				Route::default().with_hops(hops(&[5])).with_via(origin(5)).with_cost(9),
5420			)
5421			.unwrap();
5422		assert_eq!(local.assert_next_active("both").source(), Source::Local);
5423		drop(ingest);
5424		local.assert_next_ended("both");
5425
5426		// Resolution agrees with the cursor: a peer-only path is unroutable here,
5427		// while the full view queues the request on the peer's route.
5428		let err = producer
5429			.consume()
5430			.local()
5431			.request_broadcast("remote/alice")
5432			.now_or_never()
5433			.expect("unroutable")
5434			.err()
5435			.unwrap();
5436		assert!(matches!(err, Error::Unroutable));
5437		assert!(
5438			producer
5439				.consume()
5440				.request_broadcast("remote/alice")
5441				.now_or_never()
5442				.is_none()
5443		);
5444	}
5445
5446	/// A handler that rejects a path with `Unroutable` while its route stands
5447	/// gives the requester that answer; the front must not re-ask the same route
5448	/// forever, which would spin the origin driver.
5449	#[tokio::test]
5450	async fn handler_rejection_is_final() {
5451		let producer = origin(1).produce();
5452		let consumer = producer.consume();
5453		let server = producer.dynamic("room", Route::default()).unwrap();
5454
5455		let pending = consumer.request_broadcast("room/alice");
5456		let request = queued(&server).await;
5457		request.reject(Error::Unroutable);
5458		let err = tokio::time::timeout(Duration::from_secs(5), pending)
5459			.await
5460			.expect("the front must give up, not spin")
5461			.err()
5462			.unwrap();
5463		assert!(matches!(err, Error::Unroutable));
5464
5465		// The route still stands and serves the next path.
5466		let pending = consumer.request_broadcast("room/bob");
5467		let request = queued(&server).await;
5468		assert_eq!(request.path().as_str(), "room/bob");
5469		let served = broadcast::Info::new().produce();
5470		request.accept(&served);
5471		pending.await.expect("resolves");
5472	}
5473
5474	/// `routed_broadcast` treats a handler's rejection as the table's verdict:
5475	/// it waits for the table to move instead of re-asking the same route.
5476	#[tokio::test]
5477	async fn routed_broadcast_waits_out_a_rejection() {
5478		let producer = origin(1).produce();
5479		let consumer = producer.consume();
5480		let server = producer.dynamic("room", Route::default()).unwrap();
5481
5482		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5483		assert!((&mut resolving).now_or_never().is_none());
5484		let request = queued(&server).await;
5485		request.reject(Error::Unroutable);
5486
5487		// Parked: the route stands, so nothing changed that a retry could use.
5488		for _ in 0..20 {
5489			tokio::task::yield_now().await;
5490		}
5491		assert!((&mut resolving).now_or_never().is_none());
5492		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5493
5494		// A re-price moves the table: the retry reaches the handler, which serves it.
5495		server.update(Route::default().with_cost(2)).unwrap();
5496		assert!((&mut resolving).now_or_never().is_none());
5497		let request = queued(&server).await;
5498		let served = broadcast::Info::new().produce();
5499		request.accept(&served);
5500		resolving.await.expect("resolves");
5501	}
5502
5503	/// Teardown rejects a parked request with `Dropped`, but a destroyed origin
5504	/// is `Closed` to `routed_broadcast`'s callers.
5505	#[tokio::test]
5506	async fn routed_broadcast_reports_teardown_as_closed() {
5507		let (producer, driver) = Producer::new(Config::new(origin(1)));
5508		let consumer = producer.consume();
5509		let _server = producer.dynamic("room", Route::default()).unwrap();
5510
5511		// Park on the covering route, past the loop's closed check.
5512		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5513		assert!((&mut resolving).now_or_never().is_none());
5514
5515		drop(driver);
5516
5517		let err = tokio::time::timeout(Duration::from_secs(5), resolving)
5518			.await
5519			.expect("teardown resolves the wait")
5520			.err()
5521			.unwrap();
5522		assert!(matches!(err, Error::Closed), "unexpected end: {err}");
5523	}
5524
5525	/// A local broadcast announcing at the exact path is a table change too: a
5526	/// requester parked on a handler's rejection resolves to it.
5527	#[tokio::test]
5528	async fn routed_broadcast_wakes_for_a_local_broadcast() {
5529		let producer = origin(1).produce();
5530		let consumer = producer.consume();
5531		let server = producer.dynamic("room", Route::default()).unwrap();
5532
5533		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5534		assert!((&mut resolving).now_or_never().is_none());
5535		queued(&server).await.reject(Error::Unroutable);
5536		for _ in 0..20 {
5537			tokio::task::yield_now().await;
5538		}
5539		assert!((&mut resolving).now_or_never().is_none());
5540
5541		// The more specific route wins outright over the handler's prefix.
5542		let _local = producer.publish("room/alice", Route::default()).unwrap();
5543		let resolved = resolving.await.expect("resolves locally");
5544		assert_eq!(resolved.info().path.as_str(), "room/alice");
5545		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5546	}
5547
5548	/// A track first subscribed after the front is already serving another still
5549	/// replays what its source holds, like the first track did.
5550	#[tokio::test]
5551	async fn late_track_on_a_served_front_replays() {
5552		let producer = origin(1).produce();
5553		let consumer = producer.consume();
5554		let server = producer.dynamic("room", Route::default()).unwrap();
5555
5556		let source = broadcast::Info::new().produce();
5557		for name in ["a", "b"] {
5558			let track = source.create_track(name, None).unwrap();
5559			let mut group = track.append_group().unwrap();
5560			group.write_frame(crate::Timestamp::ZERO, name.as_bytes()).unwrap();
5561			group.finish().unwrap();
5562			// The producer stays alive: the track is open, like a live SI track.
5563			std::mem::forget(track);
5564		}
5565
5566		let pending = consumer.request_broadcast("room/alice");
5567		queued(&server).await.accept(&source);
5568		let resolved = pending.await.expect("resolves");
5569
5570		let budget = track::Subscription::default().with_max_age(Duration::from_secs(3600));
5571		for name in ["a", "b"] {
5572			let mut subscription = resolved
5573				.track(name)
5574				.unwrap()
5575				.subscribe(budget.clone())
5576				.await
5577				.expect("subscribe");
5578			let mut group = tokio::time::timeout(Duration::from_secs(5), subscription.recv_group())
5579				.await
5580				.expect("the late track must replay, not park")
5581				.expect("recv group")
5582				.expect("track ended early");
5583			let frame = group.read_frame().await.expect("read frame").expect("frame");
5584			assert_eq!(&frame.payload[..], name.as_bytes());
5585		}
5586	}
5587
5588	#[tokio::test]
5589	async fn most_specific_prefix_shadows() {
5590		let producer = origin(1).produce();
5591		let consumer = producer.consume();
5592
5593		let broad_server = producer.dynamic("", Route::default()).unwrap();
5594		// A narrow advertise-only claim: requests under it must NOT route to the
5595		// broad server; they fall through to the (absent) fallback handler.
5596		let _narrow = producer.announce(".dash", Route::default()).unwrap();
5597
5598		let err = consumer
5599			.request_broadcast(".dash/pid")
5600			.now_or_never()
5601			.expect("unroutable")
5602			.err()
5603			.unwrap();
5604		assert!(matches!(err, Error::Unroutable));
5605
5606		// Everything else still routes to the broad server.
5607		let _pending = consumer.request_broadcast("room/alice");
5608		let request = queued(&broad_server).await;
5609		assert_eq!(request.path().as_str(), "room/alice");
5610	}
5611
5612	#[tokio::test]
5613	async fn root_dynamic_serves_any_path() {
5614		let producer = origin(1).produce();
5615		let consumer = producer.consume();
5616		let mut announced = consumer.announced();
5617		let dynamic = producer.dynamic("", Route::default()).unwrap();
5618		// The root claim is advertised like any other prefix.
5619		announced.assert_next_active("");
5620
5621		let pending = consumer.request_broadcast("anything/at/all");
5622		let request = queued(&dynamic).await;
5623		assert_eq!(request.path().as_str(), "anything/at/all");
5624
5625		let source = broadcast::Info::new().produce();
5626		request.accept(&source);
5627		let resolved = pending.await.expect("resolves");
5628		assert_eq!(resolved.info().path.as_str(), "anything/at/all");
5629
5630		// Nothing serves an uncovered path once the handler is gone.
5631		drop(dynamic);
5632		announced.assert_next_ended("");
5633		let err = consumer
5634			.request_broadcast("something/else")
5635			.now_or_never()
5636			.expect("unroutable")
5637			.err()
5638			.unwrap();
5639		assert!(matches!(err, Error::Unroutable));
5640	}
5641
5642	/// A path outside the consumer's scope never reaches a live dynamic handler.
5643	///
5644	/// `scope` is authoritative, so an out-of-scope path is unauthorized before
5645	/// routing can send a request to the handler. A `Request` carries only a path,
5646	/// so the handler cannot tell who asked.
5647	#[tokio::test]
5648	async fn out_of_scope_request_never_reaches_the_dynamic_handler() {
5649		let producer = origin(1).produce();
5650		let dynamic = producer.dynamic("", Route::default()).unwrap();
5651		let scoped = producer.consume().scope("", &scopes(&["tenant-a"])).unwrap();
5652
5653		// `tenant-a-other` shares a character prefix but not a segment, so this
5654		// also pins that the check is segment-aware rather than textual.
5655		for path in ["tenant-b/live", "tenant-a-other/live"] {
5656			let refused = scoped
5657				.request_broadcast(path)
5658				.now_or_never()
5659				.expect("an out-of-scope request must be refused synchronously, not queued");
5660			assert!(matches!(refused, Err(Error::Unauthorized)));
5661			assert!(
5662				dynamic.requested_broadcast().now_or_never().is_none(),
5663				"the dynamic handler was asked to create a broadcast the requester may not read"
5664			);
5665		}
5666	}
5667
5668	#[tokio::test]
5669	async fn routed_waits_for_coverage() {
5670		let producer = origin(1).produce();
5671		let consumer = producer.consume();
5672
5673		let mut fut = consumer.routed("room/alice").boxed();
5674		assert!((&mut fut).now_or_never().is_none());
5675
5676		// A covering prefix resolves the wait.
5677		let _a = producer.announce("room", Route::default().with_cost(3)).unwrap();
5678		let route = fut.now_or_never().expect("covered").expect("routed");
5679		assert_eq!(route.cost, Cost::new(3));
5680
5681		// Already covered: resolves immediately.
5682		consumer
5683			.routed("room/alice/cam")
5684			.now_or_never()
5685			.expect("covered")
5686			.expect("routed");
5687	}
5688
5689	#[tokio::test]
5690	async fn routed_ignores_deeper_routes() {
5691		let producer = origin(1).produce();
5692		let consumer = producer.consume();
5693
5694		// A deeper route does not cover the shorter path.
5695		let _deep = producer.announce("room/alice/cam", Route::default()).unwrap();
5696		let mut fut = consumer.routed("room/alice").boxed();
5697		assert!((&mut fut).now_or_never().is_none());
5698
5699		let _exact = producer.announce("room/alice", Route::default()).unwrap();
5700		fut.now_or_never().expect("covered").expect("routed");
5701	}
5702
5703	#[tokio::test]
5704	async fn routed_accepts_a_max_depth_path() {
5705		let producer = origin(1).produce();
5706		let consumer = producer.consume();
5707		let path = (0..Path::MAX_PARTS)
5708			.map(|i| format!("s{i}"))
5709			.collect::<Vec<_>>()
5710			.join("/");
5711		assert_eq!(Path::new(&path).parts().count(), Path::MAX_PARTS);
5712
5713		assert!(consumer.allowed().matches(&path));
5714
5715		let mut fut = consumer.routed(&path).boxed();
5716		assert!((&mut fut).now_or_never().is_none());
5717
5718		// A covering root still resolves: the lookup must not require `path/**`.
5719		let _a = producer.announce("", Route::default()).unwrap();
5720		fut.now_or_never().expect("covered").expect("routed");
5721	}
5722
5723	#[tokio::test]
5724	async fn teardown_ends_everything() {
5725		let (producer, driver) = Producer::new(Config::new(origin(1)));
5726		let consumer = producer.consume();
5727		let _announcement = producer.announce("room", Route::default()).unwrap();
5728		let mut announced = consumer.announced();
5729		announced.assert_next_active("room");
5730
5731		let _server = producer.dynamic("served", Route::default()).unwrap();
5732		let pending = consumer.request_broadcast("served/path");
5733
5734		drop(driver);
5735
5736		// The cursor observes the end (after draining pending updates).
5737		announced.assert_next_active("served");
5738		assert!(announced.next().now_or_never().expect("ended").is_none());
5739
5740		// Pending requests reject; new work refuses.
5741		assert!(pending.now_or_never().expect("rejected").is_err());
5742		assert!(matches!(producer.announce("x", Route::default()), Err(Error::Closed)));
5743		assert!(matches!(producer.create_broadcast("x"), Err(Error::Closed)));
5744		let err = consumer
5745			.request_broadcast("y")
5746			.now_or_never()
5747			.expect("closed")
5748			.err()
5749			.unwrap();
5750		assert!(matches!(err, Error::Closed));
5751
5752		// A cursor born after the teardown is born ended.
5753		let mut late = consumer.announced();
5754		assert!(late.next().now_or_never().expect("ended").is_none());
5755	}
5756
5757	/// One live subscription reading a track through a remote front, plus the
5758	/// bookkeeping to kill and replace its serving route.
5759	struct ResumeRig {
5760		producer: Producer,
5761		resolved: broadcast::Consumer,
5762		subscription: track::Subscriber,
5763		/// Keeps the incumbent's track producing; dropping it would abort the
5764		/// track out from under the front mid-test.
5765		incumbent_track: track::Producer,
5766	}
5767
5768	impl ResumeRig {
5769		/// Announce a served route with `first` as its first hop, materialize
5770		/// "room/alice" through it with a one-group "before" track, and subscribe.
5771		async fn new(first: &[u64]) -> (Self, Dynamic, broadcast::Producer) {
5772			let producer = origin(1).produce();
5773			let consumer = producer.consume();
5774
5775			let server = producer
5776				.dynamic("room", Route::default().with_hops(hops(first)))
5777				.unwrap();
5778
5779			let pending = consumer.request_broadcast("room/alice");
5780			let request = queued(&server).await;
5781			let source = broadcast::Info::new().produce();
5782			let track = source.create_track("video", None).unwrap();
5783			let mut group = track.append_group().unwrap();
5784			group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5785			group.finish().unwrap();
5786			request.accept(&source);
5787
5788			let resolved = pending.await.expect("resolves");
5789			let mut subscription = resolved
5790				.track("video")
5791				.unwrap()
5792				.subscribe(None)
5793				.await
5794				.expect("subscribe");
5795			let mut group = subscription
5796				.recv_group()
5797				.await
5798				.expect("recv group")
5799				.expect("track ended early");
5800			let frame = group.read_frame().await.expect("read frame").expect("frame");
5801			assert_eq!(&frame.payload[..], b"before");
5802
5803			(
5804				Self {
5805					producer,
5806					resolved,
5807					subscription,
5808					incumbent_track: track,
5809				},
5810				server,
5811				source,
5812			)
5813		}
5814
5815		/// Stand up a second served route with `first` as its first hop and hand
5816		/// back its handle, ready to answer the front's re-request.
5817		fn standby(&self, first: &[u64]) -> Dynamic {
5818			self.producer
5819				.dynamic("room", Route::default().with_hops(hops(first)))
5820				.unwrap()
5821		}
5822	}
5823
5824	/// Accept the front's re-request on `server` with a source carrying the same
5825	/// content stream (the delivered group plus its successor) and prove the
5826	/// rig's subscription resumes onto it: the successor group is delivered on
5827	/// the same subscription, at the group boundary.
5828	async fn assert_resumes(rig: &mut ResumeRig, server: &Dynamic) {
5829		let request = queued(server).await;
5830		let replacement = broadcast::Info::new().produce();
5831		let track = replacement.create_track("video", None).unwrap();
5832		// The same content: group 0 was already delivered through the old route,
5833		// so the splice resumes at group 1.
5834		let mut group = track.append_group().unwrap();
5835		group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5836		group.finish().unwrap();
5837		request.accept(&replacement);
5838
5839		let mut group = track.append_group().unwrap();
5840		group.write_frame(crate::Timestamp::ZERO, b"resumed".as_ref()).unwrap();
5841		group.finish().unwrap();
5842
5843		let mut group = rig
5844			.subscription
5845			.recv_group()
5846			.await
5847			.expect("subscription survives the failover")
5848			.expect("track ended early");
5849		let frame = group.read_frame().await.expect("read frame").expect("frame");
5850		assert_eq!(&frame.payload[..], b"resumed");
5851	}
5852
5853	/// The driver's completion contract: it resolves once every producer handle
5854	/// drops, however many read handles remain.
5855	#[tokio::test]
5856	async fn driver_resolves_with_live_consumers() {
5857		let (producer, driver) = Producer::new(Config::new(origin(1)));
5858		let consumer = producer.consume();
5859		let run = crate::time::run(driver);
5860		drop(producer);
5861		tokio::time::timeout(Duration::from_secs(5), run)
5862			.await
5863			.expect("driver must finish once the producers are gone");
5864		drop(consumer);
5865	}
5866
5867	#[tokio::test]
5868	async fn remote_source_resumes_through_same_first_hop() {
5869		let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5870		let standby_server = rig.standby(&[10, 20]);
5871
5872		// The serving route dies: retraction plus source abort, like a session.
5873		drop(incumbent);
5874		drop(source);
5875
5876		// The standby shares the first hop, so the subscription resumes there.
5877		assert_resumes(&mut rig, &standby_server).await;
5878	}
5879
5880	/// A source claiming the same content cannot change immutable track metadata:
5881	/// the successor is refused instead of the subscriber's samples being read on
5882	/// a different grid, and the verdict outlives the aborted logical track.
5883	#[tokio::test]
5884	async fn incompatible_successor_is_refused() {
5885		for replacement in [
5886			track::Info::default().with_timescale(crate::Timescale::MICRO),
5887			track::Info::default().with_priority(7),
5888			track::Info::default().with_max_age(Duration::from_secs(7)),
5889		] {
5890			let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5891			let standby_server = rig.standby(&[10, 20]);
5892			drop(incumbent);
5893			drop(source);
5894
5895			// The standby shares the first hop, so the front re-requests through it,
5896			// but its copy of the track is on another grid.
5897			let request = queued(&standby_server).await;
5898			let successor = broadcast::Info::new().produce();
5899			let track = successor.create_track("video", replacement).unwrap();
5900			let mut group = track.append_group().unwrap();
5901			group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5902			group.finish().unwrap();
5903			request.accept(&successor);
5904
5905			assert!(
5906				matches!(rig.subscription.recv_group().await, Err(Error::Unsupported)),
5907				"the subscription must abort rather than resume onto incompatible metadata"
5908			);
5909
5910			// Reopening the aborted logical track must not forget the broadcast's metadata.
5911			let reopened = rig.resolved.track("video").unwrap();
5912			assert!(matches!(reopened.query().await, Err(Error::Unsupported)));
5913			assert!(matches!(reopened.subscribe(None).await, Err(Error::Unsupported)));
5914		}
5915	}
5916
5917	#[tokio::test]
5918	async fn different_first_hop_ends_the_subscription() {
5919		let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5920		// Another publisher entirely: same path, different first hop.
5921		let rival_server = rig.standby(&[11]);
5922
5923		// The incumbent's session dies, taking its track with it: a live copy
5924		// would otherwise keep serving after the front ends.
5925		drop(incumbent);
5926		drop(source);
5927		rig.incumbent_track.abort(Error::Dropped).unwrap();
5928
5929		// The subscription ends rather than splicing onto the rival's frames.
5930		let err = rig.subscription.recv_group().await.err().expect("subscription ends");
5931		assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5932
5933		// A fresh request resolves through the rival.
5934		let consumer = rig.producer.consume();
5935		let pending = consumer.request_broadcast("room/alice");
5936		let request = queued(&rival_server).await;
5937		let replacement = broadcast::Info::new().produce();
5938		request.accept(&replacement);
5939		pending.await.expect("re-request resolves through the rival");
5940	}
5941
5942	#[tokio::test]
5943	async fn anonymous_routes_never_resume() {
5944		// An empty hop chain identifies nobody, so two of them must not pass for
5945		// one publisher reconnecting.
5946		let (mut rig, incumbent, source) = ResumeRig::new(&[]).await;
5947		let _twin_server = rig.standby(&[]);
5948
5949		drop(incumbent);
5950		drop(source);
5951		rig.incumbent_track.abort(Error::Dropped).unwrap();
5952
5953		let err = rig.subscription.recv_group().await.err().expect("subscription ends");
5954		assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5955	}
5956
5957	/// An anonymous publisher that dies without unannouncing is replaced by the
5958	/// next anonymous session at the same path: a subscriber on a third session
5959	/// gets the newcomer's media immediately, not a lingering dead front.
5960	///
5961	/// The front closes with its last source, so the newcomer attaches a fresh
5962	/// one and the subscriber resolves it without parking.
5963	#[tokio::test]
5964	async fn anonymous_handoff_serves_the_newcomer_immediately() {
5965		let producer = origin(1).produce();
5966
5967		// Session A: an assigned anonymous hop serving the path.
5968		let server_a = producer
5969			.dynamic("room", Route::default().with_hops(hops(&[10])))
5970			.unwrap();
5971
5972		// Session C: a third anonymous session, excluding the hop the server
5973		// minted for it, the same split-horizon a live session applies.
5974		let consumer = producer.consume().excluding(origin(30));
5975		let pending = consumer.request_broadcast("room/alice");
5976		let request = queued(&server_a).await;
5977		let source_a = broadcast::Info::new().produce();
5978		let track_a = source_a.create_track("video", None).unwrap();
5979		let mut group = track_a.append_group().unwrap();
5980		group.write_frame(crate::Timestamp::ZERO, b"from-a".as_ref()).unwrap();
5981		group.finish().unwrap();
5982		request.accept(&source_a);
5983
5984		let resolved_a = pending.await.expect("resolves");
5985		let mut sub_a = resolved_a
5986			.track("video")
5987			.unwrap()
5988			.subscribe(None)
5989			.await
5990			.expect("subscribe");
5991		let mut group = sub_a
5992			.recv_group()
5993			.await
5994			.expect("recv group")
5995			.expect("track ended early");
5996		assert_eq!(
5997			&group.read_frame().await.expect("read frame").expect("frame").payload[..],
5998			b"from-a"
5999		);
6000
6001		// A dies without an unannounce: the source and its route drop together,
6002		// the way a lost session retracts rather than sending ANNOUNCE_END.
6003		drop(track_a);
6004		drop(source_a);
6005		drop(server_a);
6006
6007		// The front closed with A's last source.
6008		let err = sub_a.recv_group().await.err().expect("front closed");
6009		assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
6010
6011		// No stale front at the leaf, and a repeat request does not join the
6012		// corpse: nothing covers the path, so it is Unroutable rather than
6013		// parked on a linger or 404 `dropped` from the dead front.
6014		settle(|| consumer.get_broadcast("room/alice").is_none()).await;
6015		settle(|| {
6016			matches!(
6017				consumer.request_broadcast("room/alice").now_or_never(),
6018				Some(Err(Error::Unroutable))
6019			)
6020		})
6021		.await;
6022
6023		// Session B attaches at the same path. Its front is served immediately.
6024		let server_b = producer
6025			.dynamic("room", Route::default().with_hops(hops(&[20])))
6026			.unwrap();
6027		let pending = consumer.request_broadcast("room/alice");
6028		let request = queued(&server_b).await;
6029		let source_b = broadcast::Info::new().produce();
6030		let track_b = source_b.create_track("video", None).unwrap();
6031		let mut group = track_b.append_group().unwrap();
6032		group.write_frame(crate::Timestamp::ZERO, b"from-b".as_ref()).unwrap();
6033		group.finish().unwrap();
6034		request.accept(&source_b);
6035
6036		let resolved_b = pending.await.expect("B's front is served immediately");
6037		assert!(
6038			!resolved_b.is_clone(&resolved_a),
6039			"B must not splice into A's closed front"
6040		);
6041
6042		let mut sub_b = resolved_b
6043			.track("video")
6044			.unwrap()
6045			.subscribe(None)
6046			.await
6047			.expect("subscribe");
6048		let mut group = sub_b
6049			.recv_group()
6050			.await
6051			.expect("recv group")
6052			.expect("track ended early");
6053		assert_eq!(
6054			&group.read_frame().await.expect("read frame").expect("frame").payload[..],
6055			b"from-b"
6056		);
6057	}
6058
6059	#[tokio::test]
6060	async fn reprice_is_invisible_to_the_subscription() {
6061		let (rig, incumbent, source) = ResumeRig::new(&[10]).await;
6062
6063		// A metadata-only reprice of the only route: nothing re-requests and the
6064		// subscription keeps flowing from the same source.
6065		incumbent
6066			.update(Route::default().with_hops(hops(&[10])).with_cost(9))
6067			.unwrap();
6068
6069		let track = source.create_track("audio", None).unwrap();
6070		let mut group = track.append_group().unwrap();
6071		group.write_frame(crate::Timestamp::ZERO, b"steady".as_ref()).unwrap();
6072		group.finish().unwrap();
6073
6074		let mut audio = rig
6075			.resolved
6076			.track("audio")
6077			.unwrap()
6078			.subscribe(None)
6079			.await
6080			.expect("subscribe survives the reprice");
6081		let mut group = audio
6082			.recv_group()
6083			.await
6084			.expect("recv group")
6085			.expect("track ended early");
6086		let frame = group.read_frame().await.expect("read frame").expect("frame");
6087		assert_eq!(&frame.payload[..], b"steady");
6088	}
6089
6090	#[tokio::test]
6091	async fn drain_reprice_migrates_before_the_session_dies() {
6092		let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
6093		let standby_server = rig.standby(&[10, 20]);
6094
6095		// The serving route drains: repriced to the ceiling while its session
6096		// keeps serving. The front migrates to the standby without waiting for
6097		// the death.
6098		incumbent
6099			.update(Route::default().with_hops(hops(&[10])).with_cost(Cost::DRAIN))
6100			.unwrap();
6101
6102		assert_resumes(&mut rig, &standby_server).await;
6103
6104		// The drained source outlived the migration.
6105		drop(incumbent);
6106		drop(source);
6107	}
6108
6109	#[tokio::test]
6110	async fn local_sources_splice_newest_first() {
6111		let producer = origin(1).produce();
6112		let consumer = producer.consume();
6113
6114		let first = producer.publish("room/alice", Route::default()).unwrap();
6115		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6116
6117		// A second source at the same path joins the same front.
6118		let second = producer.publish("room/alice", Route::default()).unwrap();
6119		let again = consumer.request_broadcast("room/alice").await.expect("resolves");
6120		assert!(again.is_clone(&resolved));
6121
6122		// Losing one source keeps the front alive; losing both closes it.
6123		first.close();
6124		settle(|| consumer.get_broadcast("room/alice").is_some()).await;
6125		second.close();
6126		settle(|| consumer.get_broadcast("room/alice").is_none()).await;
6127
6128		// The path is free again for a fresh broadcast.
6129		let _third = producer.publish("room/alice", Route::default()).unwrap();
6130		assert!(consumer.get_broadcast("room/alice").is_some());
6131	}
6132
6133	/// The publisher finishes a track, then its broadcast. A subscription already in
6134	/// flight must conclude normally: the track's last group, then the end. moq-lite,
6135	/// ANNOUNCE_END: "Retraction does not disturb subscriptions already in flight,
6136	/// which conclude normally with SUBSCRIBE_END."
6137	///
6138	/// The runtime is single-threaded and the publisher's whole ending has no await in
6139	/// it, so the outcome does not depend on timing.
6140	#[tokio::test]
6141	async fn a_finished_broadcast_concludes_in_flight_subscriptions() {
6142		let producer = origin(1).produce();
6143		let consumer = producer.consume();
6144
6145		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
6146		let track = broadcast.create_track("video", None).unwrap();
6147
6148		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6149		let mut subscription = resolved
6150			.track("video")
6151			.unwrap()
6152			.subscribe(None)
6153			.await
6154			.expect("subscribe");
6155		// A textbook clean end, innermost first: the group, the track, the broadcast.
6156		let mut group = track.append_group().unwrap();
6157		group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
6158		group.finish().unwrap();
6159		track.finish().unwrap();
6160		drop(track);
6161		broadcast.close();
6162
6163		let mut group = next_group(&mut subscription)
6164			.await
6165			.expect("a cleanly finished track was served as an error")
6166			.expect("the track ended before its last group");
6167		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
6168		drop(group);
6169
6170		let end = next_group(&mut subscription)
6171			.await
6172			.expect("a cleanly finished track ended as an error");
6173		assert!(end.is_none(), "a group followed the final one");
6174	}
6175
6176	/// A route served from upstream is retracted (what the lite subscriber does on
6177	/// ANNOUNCE_END: finish the source it minted, drop the route) while the track's
6178	/// last group and end are still on their way. The subscription already in flight
6179	/// must still conclude normally. moq-lite, ANNOUNCE_END: "Retraction does not
6180	/// disturb subscriptions already in flight, which conclude normally with
6181	/// SUBSCRIBE_END."
6182	#[tokio::test]
6183	async fn a_retracted_route_concludes_in_flight_subscriptions() {
6184		let producer = origin(1).produce();
6185		let consumer = producer.consume();
6186		let server = producer
6187			.dynamic("room", Route::default().with_hops(hops(&[10])))
6188			.unwrap();
6189
6190		let pending = consumer.request_broadcast("room/alice");
6191		let request = queued(&server).await;
6192		let source = broadcast::Info::new().produce();
6193		let track = source.create_track("video", None).unwrap();
6194		request.accept(&source);
6195
6196		let resolved = pending.await.expect("resolves");
6197		let mut subscription = resolved
6198			.track("video")
6199			.unwrap()
6200			.subscribe(None)
6201			.await
6202			.expect("subscribe");
6203
6204		// ANNOUNCE_END overtakes the track's end: the route is retracted, and the front
6205		// has acted on it, before the track's last group and end arrive.
6206		source.close();
6207		drop(server);
6208		settle(|| resolved.is_closed()).await;
6209		let mut group = track.append_group().unwrap();
6210		group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
6211		group.finish().unwrap();
6212		track.finish().unwrap();
6213		drop(track);
6214
6215		let mut group = next_group(&mut subscription)
6216			.await
6217			.expect("a retracted route's track was served as an error")
6218			.expect("the track ended before its last group");
6219		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
6220		drop(group);
6221
6222		let end = next_group(&mut subscription)
6223			.await
6224			.expect("a cleanly finished track ended as an error");
6225		assert!(end.is_none(), "a group followed the final one");
6226	}
6227
6228	/// A standing route outlives the source it produced: the front ends instead of
6229	/// asking that route for the broadcast that just closed.
6230	#[tokio::test]
6231	async fn a_closed_source_is_not_requested_again_from_its_standing_route() {
6232		let producer = origin(1).produce();
6233		let server = producer
6234			.dynamic("room", Route::default().with_hops(hops(&[10])))
6235			.unwrap();
6236		let pending = producer.consume().request_broadcast("room/alice");
6237		let source = broadcast::Info::new().produce();
6238		queued(&server).await.accept(&source);
6239		let resolved = pending.await.unwrap();
6240
6241		source.close();
6242		settle(|| resolved.is_closed()).await;
6243		assert!(
6244			server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending(),
6245			"the closed source was requested again"
6246		);
6247	}
6248
6249	/// An origin front drops the source track as soon as its last reader leaves,
6250	/// so the publisher's `unused()` resolves far below `TRACK_IDLE_LINGER`.
6251	/// Cached groups stay on the front for the linger; a returning reader
6252	/// replays them and re-splices for groups past that edge.
6253	#[tokio::test]
6254	async fn origin_front_drops_the_source_when_unused() {
6255		let producer = origin(1).produce();
6256		let consumer = producer.consume();
6257
6258		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
6259		let track = broadcast.create_track("video", None).unwrap();
6260		let mut group = track.append_group().unwrap();
6261		group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
6262		group.finish().unwrap();
6263
6264		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6265		let mut subscription = resolved
6266			.track("video")
6267			.unwrap()
6268			.subscribe(None)
6269			.await
6270			.expect("subscribe");
6271		let mut group = subscription.recv_group().await.unwrap().unwrap();
6272		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6273		drop(group);
6274		drop(subscription);
6275
6276		tokio::time::timeout(Duration::from_secs(1), track.unused())
6277			.await
6278			.expect("source unused should resolve far below TRACK_IDLE_LINGER")
6279			.expect("source closed");
6280
6281		// Cached groups stay on the front for the linger; a returning reader
6282		// replays them without waiting out the window.
6283		let mut again = resolved
6284			.track("video")
6285			.unwrap()
6286			.subscribe(track::Subscription::default().with_max_age(Duration::from_secs(3600)))
6287			.await
6288			.expect("resubscribe");
6289		let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
6290			.await
6291			.expect("cached group is still on the front")
6292			.expect("recv group")
6293			.expect("track ended early");
6294		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6295
6296		tokio::time::timeout(Duration::from_secs(1), track.used())
6297			.await
6298			.expect("returning reader re-splices the source")
6299			.expect("source closed");
6300
6301		let mut group = track.append_group().unwrap();
6302		group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
6303		group.finish().unwrap();
6304		let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
6305			.await
6306			.expect("groups past the cached edge come from the re-splice")
6307			.expect("recv group")
6308			.expect("track ended early");
6309		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
6310	}
6311
6312	/// A returning reader judges the warm cache against the logical track's live
6313	/// edge, not the parked segment's own frozen one: groups the fresh source
6314	/// has left behind by more than the budget are skipped, exactly as they would
6315	/// be on one unspliced track.
6316	///
6317	/// A local source is released rather than parked (it keeps its own cache), so
6318	/// this goes through a served front, which is what actually holds the warm copy.
6319	/// The copy resolves at the cached edge, not past it: resolving past it drops
6320	/// the cache outright, which is a different case.
6321	#[tokio::test]
6322	async fn resumed_reader_skips_warm_groups_behind_the_new_edge() {
6323		let ms = |v: u64| crate::Timestamp::from_millis(v).unwrap();
6324		let (_server, _upstream, mut dynamic, resolved) = served_front().await;
6325		let budget = track::Subscription::default().with_max_age(Duration::from_millis(100));
6326		let write = |source: &track::Producer, sequence: u64, millis: u64| {
6327			let mut group = source.create_group(sequence.into()).unwrap();
6328			group.write_frame(ms(millis), b"x".as_ref()).unwrap();
6329			group.finish().unwrap();
6330		};
6331		let drain = |subscription: &mut track::Subscriber| {
6332			let mut sequences = Vec::new();
6333			while let Poll::Ready(group) = subscription.poll_recv_group(&kio::Waiter::noop()) {
6334				sequences.push(group.unwrap().expect("track ended").sequence);
6335			}
6336			sequences
6337		};
6338
6339		let track = resolved.track("video").unwrap();
6340		let first = budget.clone();
6341		let subscribing = tokio::spawn(async move { track.subscribe(first).await });
6342		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
6343			.await
6344			.expect("the front asked the source")
6345			.expect("request");
6346		let source = request.resolving_start().accept(None);
6347		for (sequence, millis) in [(0, 0), (1, 20), (2, 40), (3, 60)] {
6348			write(&source, sequence, millis);
6349		}
6350		let mut subscription = subscribing.await.unwrap().expect("subscribe");
6351		next_group(&mut subscription).await.unwrap().expect("a cached group");
6352		drain(&mut subscription);
6353		drop(subscription);
6354
6355		tokio::time::timeout(Duration::from_secs(1), source.unused())
6356			.await
6357			.expect("parked")
6358			.expect("source open");
6359		drop(source);
6360
6361		let track = resolved.track("video").unwrap();
6362		let second = budget.clone();
6363		let subscribing = tokio::spawn(async move { track.subscribe(second).await });
6364		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
6365			.await
6366			.expect("the front asked the source again")
6367			.expect("request");
6368		let mut source = request.resolving_start().accept(None);
6369		for (sequence, millis) in [(20, 400), (21, 420), (22, 440)] {
6370			write(&source, sequence, millis);
6371		}
6372		// At the cached edge, not past it, so the warm copy stays and the budget
6373		// decides. Past it, the copy has already judged the cache stale.
6374		source.start_at(3).unwrap();
6375		let mut subscription = subscribing.await.unwrap().expect("resubscribe");
6376		settle(|| subscription.latest() == Some(22)).await;
6377
6378		// Groups 0..=2 reach at most 60ms against an edge at 440ms. Group 3 reaches
6379		// where group 20 starts, 40ms behind that edge, inside the 100ms budget.
6380		assert_eq!(drain(&mut subscription), [3, 20, 21, 22]);
6381	}
6382
6383	/// A front serving from another front's spliced copy has no snapshot to keep:
6384	/// it still drops upstream on the unused edge, so the publisher's `unused()`
6385	/// resolves far below `TRACK_IDLE_LINGER` through the whole chain. The next
6386	/// reader re-splices, paying `TRACK_INFO` again.
6387	#[tokio::test]
6388	async fn chained_front_drops_the_source_when_unused() {
6389		let leaf = origin(1).produce();
6390		let leaf_consumer = leaf.consume();
6391
6392		let broadcast = leaf.publish("room/alice", Route::default()).unwrap();
6393		let track = broadcast.create_track("video", None).unwrap();
6394		let mut group = track.append_group().unwrap();
6395		group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
6396		group.finish().unwrap();
6397
6398		// The leaf's front view: a spliced broadcast, so any front serving from
6399		// it holds a spliced source copy with nothing to snapshot.
6400		let leaf_front = leaf_consumer.request_broadcast("room/alice").await.expect("resolves");
6401
6402		let mid = origin(2).produce();
6403		let mid_server = mid.dynamic("room", Route::default().with_hops(hops(&[10]))).unwrap();
6404		let mid_pending = mid.consume().request_broadcast("room/alice");
6405		queued(&mid_server).await.accept(&leaf_front);
6406		let mid_resolved = mid_pending.await.expect("mid resolves");
6407
6408		let edge = origin(3).produce();
6409		let edge_server = edge.dynamic("room", Route::default().with_hops(hops(&[20]))).unwrap();
6410		let edge_pending = edge.consume().request_broadcast("room/alice");
6411		queued(&edge_server).await.accept(&mid_resolved);
6412		let edge_resolved = edge_pending.await.expect("edge resolves");
6413
6414		let mut subscription = edge_resolved
6415			.track("video")
6416			.unwrap()
6417			.subscribe(None)
6418			.await
6419			.expect("subscribe");
6420		let mut group = subscription.recv_group().await.unwrap().unwrap();
6421		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6422		drop(group);
6423		drop(subscription);
6424
6425		tokio::time::timeout(Duration::from_secs(5), track.unused())
6426			.await
6427			.expect("chained unused should resolve far below TRACK_IDLE_LINGER")
6428			.expect("source closed");
6429
6430		let cached = edge_resolved.track("video").unwrap().cached_groups();
6431		assert_eq!(
6432			cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
6433			vec![0],
6434			"every front keeps the delivered groups after releasing its source"
6435		);
6436
6437		// A budget spanning the cache, so the returning reader replays it.
6438		let mut subscription = edge_resolved
6439			.track("video")
6440			.unwrap()
6441			.subscribe(track::Subscription::default().with_max_age(Duration::from_secs(3600)))
6442			.await
6443			.expect("resubscribe");
6444		tokio::time::timeout(Duration::from_secs(5), track.used())
6445			.await
6446			.expect("resubscribe should reach the leaf")
6447			.expect("source open");
6448		let mut group = track.append_group().unwrap();
6449		group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
6450		group.finish().unwrap();
6451		let mut group = subscription.recv_group().await.unwrap().unwrap();
6452		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6453		drop(group);
6454		let mut group = subscription.recv_group().await.unwrap().unwrap();
6455		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
6456		drop(group);
6457		drop(subscription);
6458
6459		tokio::time::timeout(Duration::from_secs(5), track.unused())
6460			.await
6461			.expect("second chained unused should resolve far below TRACK_IDLE_LINGER")
6462			.expect("source closed");
6463
6464		let cached = edge_resolved.track("video").unwrap().cached_groups();
6465		assert_eq!(
6466			cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
6467			vec![0, 1],
6468			"repeated demand keeps every complete group while releasing its source"
6469		);
6470
6471		let fetch = edge_resolved.track("video").unwrap().fetch_group(2, None);
6472		let mut fetch = std::pin::pin!(fetch);
6473		assert!(futures::poll!(fetch.as_mut()).is_pending(), "fetch should re-splice");
6474		tokio::time::timeout(Duration::from_secs(5), track.used())
6475			.await
6476			.expect("fetch should reach the leaf")
6477			.expect("source open");
6478		let mut group = track.append_group().unwrap();
6479		group.write_frame(crate::Timestamp::ZERO, b"fetched".as_ref()).unwrap();
6480		group.finish().unwrap();
6481		let mut group = tokio::time::timeout(Duration::from_secs(5), fetch)
6482			.await
6483			.expect("re-spliced source should answer the fetch")
6484			.expect("fetch succeeds");
6485		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"fetched");
6486	}
6487
6488	/// A newer local source wins dispatch the moment it attaches, but one whose copy
6489	/// of the track carries different metadata is refused: the incumbent keeps
6490	/// serving, and the refusal is never retried once the incumbent leaves.
6491	#[tokio::test]
6492	async fn incompatible_local_source_keeps_the_incumbent() {
6493		let producer = origin(1).produce();
6494		let consumer = producer.consume();
6495
6496		let first = producer.publish("room/alice", Route::default()).unwrap();
6497		let track = first.create_track("video", None).unwrap();
6498		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6499		let mut subscription = resolved
6500			.track("video")
6501			.unwrap()
6502			.subscribe(None)
6503			.await
6504			.expect("subscribe");
6505		let mut group = track.append_group().unwrap();
6506		group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
6507		group.finish().unwrap();
6508		let mut group = subscription.recv_group().await.unwrap().unwrap();
6509		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"before");
6510
6511		// The newest source is dispatched the track, and refused for its metadata.
6512		let second = producer.publish("room/alice", Route::default()).unwrap();
6513		let _incompatible = second
6514			.create_track("video", track::Info::default().with_timescale(crate::Timescale::MICRO))
6515			.unwrap();
6516		for _ in 0..10 {
6517			tokio::task::yield_now().await;
6518		}
6519
6520		// Still spliced to the incumbent, still delivering.
6521		let mut group = track.append_group().unwrap();
6522		group.write_frame(crate::Timestamp::ZERO, b"still".as_ref()).unwrap();
6523		group.finish().unwrap();
6524		let mut group = subscription.recv_group().await.unwrap().unwrap();
6525		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"still");
6526
6527		// The incumbent leaving exhausts the table: the refusal is never retried.
6528		drop(track);
6529		first.close();
6530		assert!(matches!(subscription.recv_group().await, Err(Error::Unsupported)));
6531	}
6532
6533	#[tokio::test]
6534	async fn multiple_scopes_present_one_broad_prefix() {
6535		let producer = origin(1).produce();
6536		let _a = producer.announce("", Route::default()).unwrap();
6537
6538		let consumer = producer.consume().scope("", &scopes(&["alpha", "beta"])).unwrap();
6539		let mut announced = consumer.announced();
6540		announced.assert_next_active("");
6541		announced.assert_next_wait();
6542	}
6543
6544	#[test]
6545	fn scope_accepts_every_pattern_union() {
6546		let producer = origin(1).produce();
6547
6548		// The root grant is `**`, the old empty prefix.
6549		let root = producer.scope("", &Patterns::from(Pattern::all())).unwrap();
6550		assert_eq!(root.allowed(), Patterns::from(Pattern::all()));
6551
6552		// `foo/**` keeps the old `foo` prefix meaning.
6553		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
6554		assert_eq!(scoped.allowed(), scopes(&["room"]));
6555
6556		// Multiple prefixes round-trip, with overlap collapsed.
6557		let multi = producer.scope("", &scopes(&["room", "room/chat", "anon"])).unwrap();
6558		assert_eq!(multi.allowed(), scopes(&["room", "anon"]));
6559
6560		// The consumer side reports the same way.
6561		let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
6562		assert_eq!(consumer.allowed(), scopes(&["room"]));
6563
6564		for text in ["room", "", "*room", "room/*", "*", "**/room", "room/**/chat", "*.hang"] {
6565			let union = Patterns::from(text.parse::<Pattern>().unwrap());
6566			assert_eq!(producer.scope("", &union).expect(text).allowed(), union, "{text}");
6567			assert_eq!(
6568				producer.consume().scope("", &union).expect(text).allowed(),
6569				union,
6570				"{text}"
6571			);
6572		}
6573
6574		let mixed: Patterns = ["room/**".parse().unwrap(), "other".parse().unwrap()]
6575			.into_iter()
6576			.collect();
6577		assert_eq!(producer.scope("", &mixed).unwrap().allowed(), mixed);
6578	}
6579
6580	#[test]
6581	fn route_table_prunes_to_empty() {
6582		let producer = origin(1).produce();
6583		let consumer = producer.consume();
6584
6585		// Routes and cursors hang at their prefixes; the nodes on the way exist
6586		// only while something is there.
6587		let cursor = consumer
6588			.scope("", &scopes(&["room/a", "other/deep/head"]))
6589			.unwrap()
6590			.announced();
6591		let route = producer.announce("room/a/b/c", Route::default()).unwrap();
6592		{
6593			let table = producer.shared.lock();
6594			assert!(table.routes.root.find(Path::new("room/a/b/c").parts()).is_some());
6595			assert!(table.routes.root.find(Path::new("other/deep/head").parts()).is_some());
6596			assert_eq!(table.routes.root.cursors_below, 2);
6597		}
6598
6599		drop(route);
6600		drop(cursor);
6601		let table = producer.shared.lock();
6602		assert!(table.routes.root.is_empty());
6603		assert_eq!(table.routes.root.cursors_below, 0);
6604	}
6605
6606	/// A session handed an `origin::Producer` drops it once it has its own
6607	/// handles, so the driver must keep running while a published broadcast
6608	/// lives, and finish once the last one is gone.
6609	#[test]
6610	fn a_published_broadcast_keeps_the_driver_running() {
6611		let (producer, mut driver) = Producer::new(Config::new(origin(1)));
6612		let waiter = kio::Waiter::noop();
6613		let broadcast = producer.create_broadcast("room/a").unwrap();
6614		drop(producer);
6615		assert!(
6616			driver.poll(Instant::now(), &waiter).is_ok(),
6617			"the broadcast is lifecycle work"
6618		);
6619		drop(broadcast);
6620		assert!(matches!(driver.poll(Instant::now(), &waiter), Err(Error::Closed)));
6621	}
6622
6623	#[test]
6624	fn watch_wakes_only_for_covering_changes() {
6625		let producer = origin(1).produce();
6626		let waiter = kio::Waiter::noop();
6627		let watch = producer.shared.lock().watch(&producer.shared, &Path::new("room/a"));
6628		let seen = watch.seen();
6629
6630		// A route beside the path or beneath it covers nothing at the path.
6631		let _other = producer.announce("other", Route::default()).unwrap();
6632		let _below = producer.announce("room/a/b", Route::default()).unwrap();
6633		assert!(watch.poll_changed(&waiter, seen).is_pending());
6634
6635		// A route above it does, and so does its retraction.
6636		let above = producer.announce("room", Route::default()).unwrap();
6637		assert!(watch.poll_changed(&waiter, seen).is_ready());
6638		let seen = watch.seen();
6639		drop(above);
6640		assert!(watch.poll_changed(&waiter, seen).is_ready());
6641		let seen = watch.seen();
6642
6643		// A local broadcast attaching at the exact path does; one beside it does not.
6644		let _beside = producer.create_broadcast("room/b").unwrap();
6645		assert!(watch.poll_changed(&waiter, seen).is_pending());
6646		let _here = producer.create_broadcast("room/a").unwrap();
6647		assert!(watch.poll_changed(&waiter, seen).is_ready());
6648
6649		// Dropping the watch takes it out of the table.
6650		drop(watch);
6651		let table = producer.shared.lock();
6652		let node = table
6653			.routes
6654			.root
6655			.find(Path::new("room/a").parts())
6656			.expect("route below keeps the node");
6657		assert!(node.watches.is_empty());
6658		assert_eq!(table.routes.root.watches_below, 0);
6659	}
6660
6661	#[test]
6662	fn a_discarded_front_task_unregisters_its_watch() {
6663		let (producer, _driver) = Producer::new(Config {
6664			hop: origin(1),
6665			..Default::default()
6666		});
6667		let consumer = producer.consume();
6668		let _served = producer.dynamic("room", Route::default()).unwrap();
6669		// A consumer outlives its producer by design, so the task set can refuse
6670		// submissions while the origin is still open. The front's task is then
6671		// dropped on the spot, taking its `Watch` with it: the request must not
6672		// still be holding the table lock the watch unregisters under.
6673		drop(producer);
6674		let _pending = consumer.request_broadcast("room/a");
6675	}
6676
6677	#[test]
6678	fn create_broadcast_refuses_a_path_no_pattern_can_spell() {
6679		let producer = origin(1).produce();
6680
6681		// A `*` segment is a valid path but an invalid literal, so its route could
6682		// never be built: refuse the broadcast instead of publishing one that
6683		// announces nowhere.
6684		assert!(matches!(
6685			producer.create_broadcast("room/*"),
6686			Err(Error::InvalidPath(_))
6687		));
6688		assert!(matches!(
6689			producer.announce("room/**", Route::default()),
6690			Err(Error::InvalidPath(_))
6691		));
6692	}
6693
6694	#[test]
6695	fn scope_empty_union_grants_nothing() {
6696		let producer = origin(1).produce();
6697
6698		// An empty union grants nothing: scoping is refused, like a disjoint prefix.
6699		assert!(matches!(producer.scope("", &Patterns::new()), Err(Error::Unauthorized)));
6700		assert!(matches!(
6701			producer.consume().scope("", &Patterns::new()),
6702			Err(Error::Unauthorized)
6703		));
6704	}
6705
6706	#[test]
6707	fn scope_nests_and_rebases_roots() {
6708		let producer = origin(1).produce();
6709
6710		// Narrowing twice intersects; the grant stays in the new vocabulary.
6711		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
6712		let nested = scoped.scope("", &scopes(&["room/chat"])).unwrap();
6713		assert_eq!(nested.allowed(), scopes(&["room/chat"]));
6714
6715		// A disjoint nesting is refused, not widened.
6716		assert!(matches!(
6717			scoped.scope("", &scopes(&["other"])),
6718			Err(Error::Unauthorized)
6719		));
6720
6721		// A literal root rebases the grant without changing its meaning.
6722		let rooted = nested.scope("room/chat", &Patterns::from(Pattern::all())).unwrap();
6723		assert_eq!(rooted.allowed(), scopes(&[""]));
6724
6725		// Publishing through the nested view lands where the root says.
6726		let broadcast = nested.create_broadcast("room/chat/live").unwrap();
6727		assert!(producer.consume().get_broadcast("room/chat/live").is_some());
6728		broadcast.close();
6729	}
6730
6731	#[test]
6732	fn scope_intersects_and_rebases_arbitrary_grants() {
6733		let producer = origin(1).produce();
6734		let rooms = producer
6735			.scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
6736			.unwrap();
6737		let chats = rooms
6738			.scope("", &Patterns::from("*/chat".parse::<Pattern>().unwrap()))
6739			.unwrap();
6740		assert_eq!(chats.allowed(), Patterns::from("room/chat".parse::<Pattern>().unwrap()));
6741
6742		let exact = producer
6743			.scope("", &Patterns::from("room/alice".parse::<Pattern>().unwrap()))
6744			.unwrap();
6745		let rooted = exact.scope("room", &Patterns::from(Pattern::all())).unwrap();
6746		assert_eq!(rooted.allowed(), Patterns::from("alice".parse::<Pattern>().unwrap()));
6747		assert!(matches!(
6748			exact.scope("room/bob", &Patterns::from(Pattern::all())),
6749			Err(Error::Unauthorized)
6750		));
6751
6752		let broadcast = exact.create_broadcast("room/alice").unwrap();
6753		assert!(matches!(
6754			exact.create_broadcast("room/alice/cam"),
6755			Err(Error::Unauthorized)
6756		));
6757		assert!(producer.consume().get_broadcast("room/alice").is_some());
6758		drop(broadcast);
6759	}
6760
6761	#[tokio::test]
6762	async fn wildcard_scope_filters_announcements_and_reports_captures() {
6763		let producer = origin(1).produce();
6764		let consumer = producer
6765			.consume()
6766			.scope("", &Patterns::from("room/*/chat".parse::<Pattern>().unwrap()))
6767			.unwrap();
6768		let mut announced = consumer.announced();
6769
6770		let alice = producer.create_broadcast("room/alice/chat").unwrap();
6771		alice.announce(Route::default()).unwrap();
6772		let update = announced.try_next().expect("alice's chat");
6773		assert_eq!(update.prefix.as_str(), "room/alice/chat");
6774		assert_eq!(update.captures, Some(vec!["alice".parse::<Pattern>().unwrap()]));
6775
6776		let audio = producer.create_broadcast("room/alice/audio").unwrap();
6777		audio.announce(Route::default()).unwrap();
6778		announced.assert_next_wait();
6779
6780		let broad = producer.announce("room", Route::default()).unwrap();
6781		let update = announced.try_next().expect("overlapping broad route");
6782		assert_eq!(update.prefix.as_str(), "room");
6783		assert_eq!(update.captures, None, "an overlap does not pin the wildcard");
6784
6785		drop(broad);
6786		drop(audio);
6787		drop(alice);
6788	}
6789
6790	#[tokio::test]
6791	async fn local_broadcast_wins_announcement_ties() {
6792		let producer = origin(1).produce();
6793		let remote = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
6794		let local = producer.create_broadcast("room/alice").unwrap();
6795		local.announce(Route::default()).unwrap();
6796
6797		let mut announced = producer.consume().announced();
6798		let update = announced.try_next().expect("one winning route");
6799		assert_eq!(update.prefix.as_str(), "room/alice");
6800		assert_eq!(update.route.cost, Cost::default());
6801		announced.assert_next_wait();
6802
6803		drop(local);
6804		drop(remote);
6805	}
6806
6807	/// Charging a link accumulates onto both halves, saturating rather than wrapping
6808	/// so a bogus peer sorts last, not first. The ceiling is the largest cost a
6809	/// varint can carry, so whatever a peer advertises, the sum we forward still
6810	/// encodes.
6811	#[test]
6812	fn cost_charge_saturates() {
6813		assert_eq!(Cost { warm: 4, cold: 6 }.charged(5), Cost { warm: 9, cold: 11 });
6814		assert_eq!(Cost::new(u64::MAX).charged(10), Cost::new(MAX_COST));
6815
6816		// An unknown cold path stays unknown however many links it crosses, so it
6817		// can never accumulate its way into outranking a path we actually know.
6818		assert_eq!(Cost::UNKNOWN.charged(3).cold, MAX_COST);
6819	}
6820
6821	/// Mint an origin whose pool reclaims idle content after `expiry`.
6822	fn expiring_origin(expiry: Duration) -> Producer {
6823		let pool = cache::Pool::new(cache::Config::default().with_expiry(expiry));
6824		Config {
6825			pool,
6826			..Config::default()
6827		}
6828		.produce()
6829	}
6830
6831	/// A publisher that stalls with a group still open runs no write path, so the
6832	/// track's own write-driven expiry never fires and a reader parked in that group
6833	/// is never told. The driver's wall-clock sweep is the bound.
6834	#[tokio::test(start_paused = true)]
6835	async fn stalled_publisher_open_group_is_reclaimed() {
6836		let expiry = Duration::from_secs(1);
6837		let origin = expiring_origin(expiry);
6838		let broadcast = origin.create_broadcast("test").unwrap();
6839		let track = broadcast.create_track("video", None).unwrap();
6840
6841		let mut stalled = track.append_group().unwrap();
6842		stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
6843		// A successor, so the stalled group is not the protected live edge. Its
6844		// timestamp is inside the retention budget, so subscription expiry keeps the
6845		// stalled group: only reclamation can bound it.
6846		let _successor = track.append_group().unwrap();
6847
6848		let mut reading = stalled.consume();
6849		assert!(reading.read_frame().await.unwrap().is_some());
6850
6851		// Production goes quiet: nothing writes to this track again.
6852		crate::model::clock::advance(expiry * 2);
6853
6854		// Bounded so a regression fails rather than parking forever, which is the
6855		// bug itself. Time is virtual, so the wait costs nothing.
6856		let reclaimed = tokio::time::timeout(Duration::from_secs(60), reading.read_frame()).await;
6857		assert!(
6858			matches!(reclaimed, Ok(Err(Error::Old))),
6859			"the sweep must reclaim an idle open group and surface the gap, got {reclaimed:?}"
6860		);
6861	}
6862
6863	/// Reclamation is the pool's policy, not the origin's: a pool with no expiry
6864	/// window keeps idle content until byte pressure takes it, sweep or no sweep.
6865	#[tokio::test(start_paused = true)]
6866	async fn sweep_respects_a_disabled_expiry() {
6867		let origin = Config {
6868			pool: cache::Pool::unbounded(),
6869			..Config::default()
6870		}
6871		.produce();
6872		let broadcast = origin.create_broadcast("test").unwrap();
6873		let track = broadcast.create_track("video", None).unwrap();
6874
6875		let mut stalled = track.append_group().unwrap();
6876		stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
6877		let _successor = track.append_group().unwrap();
6878
6879		let mut reading = stalled.consume();
6880		assert!(reading.read_frame().await.unwrap().is_some());
6881
6882		crate::model::clock::advance(Duration::from_secs(3600));
6883		tokio::time::advance(Duration::from_secs(3600)).await;
6884
6885		assert!(
6886			reading.read_frame().now_or_never().is_none(),
6887			"a pool without an expiry window never reclaims"
6888		);
6889	}
6890
6891	/// A draining cost still has to fit the wire, since the route keeps being
6892	/// announced downstream while it drains.
6893	#[test]
6894	fn drain_cost_is_encodable() {
6895		use crate::coding::Encode;
6896
6897		let mut buf = Vec::new();
6898		Cost::DRAIN
6899			.encode(&mut buf, crate::lite::Version::Lite06)
6900			.expect("a draining route is still forwarded, so its cost must encode");
6901	}
6902}