Skip to main content

moq_net/model/
origin.rs

1use crate::{broadcast, cache, 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::finish`]; dropping it
1241	/// without finishing also works, but logs a warning. Either way the path
1242	/// closes once it was the last source; an unfinished drop additionally aborts
1243	/// the spliced tracks with an error, so consumers observe a failure rather
1244	/// than a clean end.
1245	///
1246	/// Fails with [`Error::Unauthorized`] if `path` is outside the prefixes this
1247	/// producer may publish under (after [`scope`](Self::scope)),
1248	/// [`Error::BoundsExceeded`] if the full rooted path exceeds
1249	/// [`Path::MAX_PARTS`], [`Error::InvalidPath`] if it holds a segment no
1250	/// pattern can spell (`*` or `**`), or [`Error::Closed`] once the origin's
1251	/// [`Driver`] has been dropped.
1252	pub fn create_broadcast(&self, path: impl AsPath) -> Result<broadcast::Producer, Error> {
1253		let path = path.as_path();
1254
1255		let full = self.root.join(&path).to_owned();
1256		if !self.scope.permits(&full) {
1257			return Err(Error::Unauthorized);
1258		}
1259		// A decoded prefix and suffix are each within the wire limit, but their
1260		// join might not be. Enforcing here bounds the table depth and guarantees the
1261		// path can be re-encoded when forwarded.
1262		if full.parts().count() > Path::MAX_PARTS {
1263			return Err(BoundsExceeded.into());
1264		}
1265		// A path only a pattern could spell (a `*` segment) advertises nowhere, so
1266		// refuse it here rather than publish a broadcast no cursor can see.
1267		let claim = prefix_claim(&full)?;
1268
1269		// Resolve the ingress counters once, keyed by the absolute broadcast path.
1270		let ingress = self.stats.ingress(&full);
1271
1272		// The broadcast is a route table entry at its exact path from the start,
1273		// hidden from cursors and requests until it announces. The entry lives
1274		// as long as the broadcast: its announcer drops on finish, abort, or the
1275		// last handle.
1276		let announcing = Announcing {
1277			hop: self.hop,
1278			shared: self.shared.clone(),
1279			requested: full.clone(),
1280			prefixes: vec![(full.clone(), claim)],
1281			scope: self.scope.allowed.clone(),
1282			local: true,
1283			peer: self.peer,
1284			stats: self.stats.clone(),
1285		};
1286		let info = broadcast::Info {
1287			pool: self.pool.clone(),
1288			cache_duration: self.cache_duration,
1289			path: full,
1290		};
1291		let source = info.produce().with_stats(ingress.clone());
1292		let entry = announcing.announce(
1293			Route::default(),
1294			Serving {
1295				server: None,
1296				source: Some(source.consume()),
1297				advertised: false,
1298			},
1299		)?;
1300		Ok(source.with_announcer(Announcer {
1301			entry,
1302			ingress,
1303			_keepalive: self.tasks.keepalive(),
1304		}))
1305	}
1306
1307	/// Create and advertise a broadcast in one call.
1308	pub fn publish(&self, path: impl AsPath, route: Route) -> Result<broadcast::Producer, Error> {
1309		let broadcast = self.create_broadcast(path)?;
1310		broadcast.announce(route)?;
1311		Ok(broadcast)
1312	}
1313
1314	/// Mint a standalone source broadcast for a served-route request: it carries
1315	/// this origin's cache policy and ingress attribution, but
1316	/// is *not* entered into the route table. Sessions answer
1317	/// [`Dynamic`] requests with one of these; the requester already holds
1318	/// the request's result channel, so the table never needs to resolve it.
1319	pub(crate) fn create_source(&self, path: impl AsPath) -> broadcast::Producer {
1320		let path = path.as_path();
1321		let full = self.root.join(&path).to_owned();
1322		let ingress = self.stats.ingress(&full);
1323		broadcast::Info {
1324			pool: self.pool.clone(),
1325			cache_duration: self.cache_duration,
1326			path: full,
1327		}
1328		.produce()
1329		.with_stats(ingress)
1330	}
1331
1332	/// Advertise a route without serving it: a claim that paths under `prefix`
1333	/// can be served, answered by nothing.
1334	///
1335	/// A request under an advertise-only route resolves [`Error::Unroutable`]
1336	/// unless an announced broadcast or a served route ([`Self::dynamic`]) covers
1337	/// the path too. Tests use it to shape the route table; everything else
1338	/// advertises through a broadcast ([`broadcast::Producer::announce`]) or a
1339	/// [`Dynamic`] handler, which serve what they claim.
1340	#[cfg(test)]
1341	pub(crate) fn announce(&self, prefix: impl AsPath, route: Route) -> Result<AnnounceProducer, Error> {
1342		Announcing::new(self, prefix)?.announce(
1343			route,
1344			Serving {
1345				server: None,
1346				source: None,
1347				advertised: true,
1348			},
1349		)
1350	}
1351
1352	/// Advertise a route over `prefix` and serve the requests beneath it.
1353	///
1354	/// A route is always a prefix: it claims `prefix` and every path beneath it
1355	/// (the empty prefix claims every path). A service that only serves some of
1356	/// them, say `pid/*.hang`, advertises the covering prefix and refuses the
1357	/// rest as they are requested; consumers narrow with a [`Pattern`] locally.
1358	/// This is the one shape every wire carries, so a route means the same
1359	/// thing on every hop.
1360	///
1361	/// The advertisement is visible to [`Consumer::announced`] and forwarded by
1362	/// sessions for as long as the returned [`Dynamic`] (and every clone) lives.
1363	/// A consumer resolving a path under it through this route is handed to the
1364	/// handler as a [`Request`] to materialize on demand. This is how a service
1365	/// answers a whole subtree without publishing each path, and how sessions
1366	/// land the routes a peer announces to them; a publisher that
1367	/// knows its broadcasts advertises each one's exact path with
1368	/// [`broadcast::Producer::announce`] instead, so subscribers can enumerate
1369	/// them.
1370	///
1371	/// The prefix must overlap this producer's pattern scope. Individual requests
1372	/// remain authoritative and are refused when they do not match the scope.
1373	pub fn dynamic(&self, prefix: impl AsPath, route: Route) -> Result<Dynamic, Error> {
1374		let announcing = Announcing::new(self, prefix)?;
1375		let serve = kio::Shared::<ServeState>::default();
1376		serve.lock().requests.add_handler();
1377		let announcement = announcing.announce(
1378			route,
1379			Serving {
1380				server: Some(serve.clone()),
1381				source: None,
1382				advertised: true,
1383			},
1384		)?;
1385		Ok(Dynamic {
1386			announcement,
1387			state: serve,
1388		})
1389	}
1390
1391	/// Returns a producer rooted at `root` and restricted to matching `patterns`.
1392	///
1393	/// `root` is relative to this producer's root, and `patterns` are relative to
1394	/// the new root. Returns [`Error::Unauthorized`] when the requested scope has
1395	/// no overlap with this producer's scope, or [`Error::BoundsExceeded`] when
1396	/// rooting the patterns would exceed the path limit.
1397	pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Producer, Error> {
1398		let root = self.root.join(root).to_owned();
1399		let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
1400		let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
1401		Ok(Producer {
1402			hop: self.hop,
1403			scope,
1404			root,
1405			shared: self.shared.clone(),
1406			pool: self.pool.clone(),
1407			cache_duration: self.cache_duration,
1408			default_max_age: self.default_max_age,
1409			stats: self.stats.clone(),
1410			peer: self.peer,
1411			tasks: self.tasks.clone(),
1412			timers: self.timers.clone(),
1413		})
1414	}
1415
1416	/// Cheap read handle over this origin's route table.
1417	///
1418	/// Use [`Consumer::announced`] to register interest and start receiving
1419	/// announcement events; the consumer itself does not allocate any channels.
1420	pub fn consume(&self) -> Consumer {
1421		// Untagged: a session tags the egress consumer separately via
1422		// `origin::Consumer::with_stats` (ingress and egress are distinct sides).
1423		Consumer::from_producer(self, stats::Session::default())
1424	}
1425
1426	/// Returns the root that is automatically stripped from all paths.
1427	pub fn root(&self) -> &Path<'_> {
1428		&self.root
1429	}
1430
1431	/// The patterns this producer may publish under, relative to its root.
1432	pub fn allowed(&self) -> Patterns {
1433		self.scope.relative(&self.root)
1434	}
1435
1436	/// Converts a relative path to an absolute path.
1437	pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
1438		self.root.join(path)
1439	}
1440}
1441
1442/// What it takes to insert a route: the prefixes it covers and the origin table
1443/// to insert them into. Built by [`Producer::announce`], [`Producer::dynamic`],
1444/// and [`Announcer`], which is the same advertisement re-issued from a broadcast.
1445struct Announcing {
1446	hop: Hop,
1447	shared: kio::Shared<OriginState>,
1448	/// The absolute advertised prefix, which also keys the ingress announce counters.
1449	requested: PathOwned,
1450	/// The prefix inserted into the table, with its [`prefix_claim`]. Pattern
1451	/// scopes decide visibility and request authorization without changing the
1452	/// route's prefix shape.
1453	prefixes: Vec<(PathOwned, Pattern)>,
1454	/// The absolute paths the producer is authorized to serve.
1455	scope: Patterns,
1456	local: bool,
1457	/// Whether the producer was marked [`Producer::peer`].
1458	peer: bool,
1459	stats: stats::Session,
1460}
1461
1462impl Announcing {
1463	/// The requested prefix as-is, refused when its subtree does not overlap the scope.
1464	fn new(producer: &Producer, prefix: impl AsPath) -> Result<Self, Error> {
1465		let requested = producer.root.join(prefix.as_path()).to_owned();
1466		if requested.parts().count() > Path::MAX_PARTS {
1467			return Err(BoundsExceeded.into());
1468		}
1469		let claim = prefix_claim(&requested)?;
1470		if !producer.scope.allowed.overlaps(&claim) {
1471			return Err(Error::Unauthorized);
1472		}
1473		Ok(Self {
1474			hop: producer.hop,
1475			shared: producer.shared.clone(),
1476			requested: requested.clone(),
1477			prefixes: vec![(requested, claim)],
1478			scope: producer.scope.allowed.clone(),
1479			local: false,
1480			peer: producer.peer,
1481			stats: producer.stats.clone(),
1482		})
1483	}
1484
1485	fn announce(&self, route: Route, serving: Serving) -> Result<AnnounceProducer, Error> {
1486		debug_assert!(
1487			!route.hops.contains(&self.hop),
1488			"announce called with a looping hop chain",
1489		);
1490
1491		let via = route.via;
1492
1493		let mut shared = self.shared.lock();
1494		if shared.closed {
1495			return Err(Error::Closed);
1496		}
1497
1498		let mut entries = Vec::with_capacity(self.prefixes.len());
1499		for (prefix, claim) in &self.prefixes {
1500			let id = shared.next_route;
1501			shared.next_route += 1;
1502			shared.routes.insert(RouteEntry {
1503				id,
1504				prefix: prefix.clone(),
1505				scope: self.scope.clone(),
1506				hops: route.hops.clone(),
1507				cost: route.cost,
1508				via,
1509				local: self.local,
1510				peer: self.peer,
1511				server: serving.server.clone(),
1512				source: serving.source.clone(),
1513				advertised: serving.advertised,
1514				claim: claim.clone(),
1515			});
1516			shared.sync_route(prefix, claim);
1517			entries.push((prefix.clone(), id));
1518		}
1519		drop(shared);
1520
1521		// Ingress announce guard: held while the route is advertised.
1522		let guard = serving
1523			.advertised
1524			.then(|| self.stats.ingress(&self.requested).announce());
1525
1526		Ok(AnnounceProducer {
1527			shared: self.shared.clone(),
1528			entries,
1529			guard,
1530		})
1531	}
1532}
1533
1534/// What a route entry serves and whether cursors see it.
1535struct Serving {
1536	server: Option<kio::Shared<ServeState>>,
1537	source: Option<broadcast::Consumer>,
1538	advertised: bool,
1539}
1540
1541/// The table entry a broadcast owns: its exact path, advertised and withdrawn
1542/// through [`broadcast::Producer::announce`] and
1543/// [`broadcast::Producer::unannounce`], and removed when the broadcast ends.
1544///
1545/// Handed to the broadcast by [`Producer::create_broadcast`], so a standalone
1546/// broadcast has none and cannot announce.
1547pub(crate) struct Announcer {
1548	entry: AnnounceProducer,
1549	/// The ingress counters an advertised interval's announce guard comes from.
1550	ingress: stats::Scope,
1551	/// A published broadcast is lifecycle work: the origin's driver keeps
1552	/// running for as long as one lives, even once every producer handle is
1553	/// gone, so a session handed a producer can drop it and keep serving.
1554	_keepalive: Keepalive,
1555}
1556
1557impl Announcer {
1558	/// Advertise the broadcast's path with `route`, or re-price it in place.
1559	pub(crate) fn announce(&mut self, route: Route) -> Result<(), Error> {
1560		self.entry.update(route)?;
1561		if self.entry.guard.is_none() {
1562			self.entry.guard = Some(self.ingress.announce());
1563		}
1564		Ok(())
1565	}
1566
1567	/// Withdraw the advertisement from local and remote consumers alike.
1568	pub(crate) fn withdraw(&mut self) {
1569		self.entry.withdraw();
1570		self.entry.guard = None;
1571	}
1572}
1573
1574/// The write half of an advertisement: a live claim that paths under a
1575/// [`Pattern`] can be served.
1576///
1577/// Held by a [`Dynamic`] and by a broadcast's [`Announcer`]; dropping it
1578/// retracts the route, which [`AnnounceConsumer`]s observe and sessions withdraw
1579/// from their peers.
1580#[must_use = "dropping an announcement retracts the route"]
1581pub(crate) struct AnnounceProducer {
1582	shared: kio::Shared<OriginState>,
1583	/// The table entries this advertisement created, by prefix and id. A prefix
1584	/// remains unchanged; pattern scopes only filter its visibility and requests.
1585	entries: Vec<(PathOwned, u64)>,
1586	/// Ingress announce stats guard, held only while the entries are advertised.
1587	guard: Option<stats::Announce>,
1588}
1589
1590impl AnnounceProducer {
1591	/// Re-price the route in place: replace its hops and cost.
1592	///
1593	/// Consumers observe another active update for the same prefix; sessions
1594	/// forward it as a restart, so route churn never looks like new content. The
1595	/// prefix is fixed at announce time and a [`Route`] cannot name one: to move
1596	/// an advertisement, drop this and announce again. Fails with
1597	/// [`Error::Closed`] once the origin's [`Driver`] has been dropped.
1598	pub fn update(&self, route: Route) -> Result<(), Error> {
1599		let mut shared = self.shared.lock();
1600		if shared.closed {
1601			return Err(Error::Closed);
1602		}
1603		for (prefix, id) in &self.entries {
1604			// Each entry keeps its advertised prefix; only the metadata moves.
1605			let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1606				return Err(Error::Closed);
1607			};
1608			entry.hops = route.hops.clone();
1609			entry.cost = route.cost;
1610			entry.via = route.via;
1611			entry.advertised = true;
1612			let claim = entry.claim.clone();
1613			shared.sync_route(prefix, &claim);
1614		}
1615		Ok(())
1616	}
1617
1618	/// Hide the entries from everyone, local and remote alike: cursors see a
1619	/// retraction and requests stop resolving through them. The entries stay,
1620	/// so announcing again restores the same route. What
1621	/// [`broadcast::Producer::unannounce`] does.
1622	fn withdraw(&self) {
1623		let mut shared = self.shared.lock();
1624		for (prefix, id) in &self.entries {
1625			let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1626				continue;
1627			};
1628			if !entry.advertised {
1629				continue;
1630			}
1631			entry.advertised = false;
1632			let claim = entry.claim.clone();
1633			shared.sync_route(prefix, &claim);
1634		}
1635	}
1636
1637	/// Retract the route now: remove its table entries and reject anything still
1638	/// waiting on its queue. Idempotent, and what dropping the advertisement does.
1639	fn retract(&self) {
1640		let mut shared = self.shared.lock();
1641		for (prefix, id) in &self.entries {
1642			let Some(entry) = shared.routes.remove(prefix, *id) else {
1643				continue;
1644			};
1645			// Reject anything still waiting on this route's server; a request
1646			// already handed to the handler resolves through its own `Request`.
1647			if let Some(server) = &entry.server {
1648				let mut server = server.lock();
1649				server.closed = true;
1650				for producer in server.requests.drain_all() {
1651					if let Ok(mut request) = producer.write() {
1652						request.resolved.get_or_insert(Err(Error::Unroutable));
1653					}
1654				}
1655			}
1656			shared.sync_route(&entry.prefix, &entry.claim);
1657		}
1658	}
1659}
1660
1661impl Drop for AnnounceProducer {
1662	fn drop(&mut self) {
1663		self.retract();
1664	}
1665}
1666
1667/// Drives origin lifecycle work and cache expiration with caller-supplied time.
1668///
1669/// Returned by [`Producer::new`]. Poll on external activity or at the deadline
1670/// it returns, supplying nondecreasing instants. Route changes, track serving, linger,
1671/// failover, and teardown run here; the route table and announce cursors update
1672/// synchronously when a route is announced or retracted.
1673///
1674/// It holds no [`Producer`] clone, so it never keeps the origin alive. Dropping
1675/// it aborts active fronts, rejects pending requests, ends announcements, and
1676/// makes subsequent producer mutations fail with [`Error::Closed`].
1677/// `moq_tokio::origin::spawn` handles construction and driving for Tokio callers.
1678#[must_use = "poll the driver or the origin makes no progress"]
1679pub struct Driver {
1680	state: DriverState,
1681	// Shared by this origin's lifecycle tasks; advanced only when polled.
1682	timers: Clock,
1683	// The cache pool this origin's groups charge into, swept on a wall-clock
1684	// cadence so its idle window binds a track whose publisher stopped writing.
1685	pool: cache::Pool,
1686}
1687
1688/// Lifecycle work and the state it tears down.
1689struct DriverState {
1690	/// The front drivers: producers submit, this polls.
1691	set: TaskSet,
1692	/// The route table, announce cursors, and the remotely-served fronts, for
1693	/// ending everything on drop.
1694	shared: kio::Shared<OriginState>,
1695	/// Cached completion so a poll after `Ready` doesn't re-poll the drained set.
1696	done: bool,
1697}
1698
1699impl Driver {
1700	/// Process ready origin work using caller-supplied monotonic time.
1701	///
1702	/// See [`crate::time::Driver`] for the contract. Finishes with
1703	/// [`Error::Closed`] once every producer handle has dropped and the
1704	/// remaining lifecycle work has drained.
1705	pub fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1706		self.timers.advance(now);
1707		let result = self.state.poll(waiter);
1708		let gc = self.pool.gc(now);
1709		if result.is_ready() {
1710			return Err(Error::Closed);
1711		}
1712		Ok(self.timers.timeout().into_iter().chain(gc).min())
1713	}
1714}
1715
1716impl crate::time::Driver for Driver {
1717	fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1718		self.poll(now, waiter)
1719	}
1720}
1721
1722impl DriverState {
1723	fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
1724		// Never gates completion: the pool outlives this origin (a relay shares one
1725		// across every origin), so a sweep that is still due must not keep the driver
1726		// alive after its lifecycle work has drained.
1727		if !self.done {
1728			ready!(self.set.poll(waiter));
1729			self.done = true;
1730		}
1731		Poll::Ready(())
1732	}
1733
1734	/// Tear the origin down: cancel the lifecycle work, abort and unpublish every
1735	/// front, retract every route, end announcement cursors, and reject pending
1736	/// requests.
1737	fn teardown(&mut self) {
1738		// Cancel queued and running lifecycle work first, so no front serves
1739		// while the table is ended below.
1740		drop(std::mem::replace(&mut self.set, TaskSet::owned()));
1741
1742		// Refuse new work and take the pending requests, under the same lock
1743		// `create_broadcast` holds across its attach: a concurrent create either
1744		// finishes before this (the walk below cleans its entry up) or observes
1745		// `closed` and fails with `Closed`.
1746		let (servers, cursors, fronts) = {
1747			let mut shared = self.shared.lock();
1748			shared.closed = true;
1749			// Fronts and parked requesters observe `closed` on their next pass.
1750			shared.routes.poke_all();
1751			let servers: Vec<_> = shared
1752				.routes
1753				.entries()
1754				.filter_map(|entry| entry.server.clone())
1755				.collect();
1756			let cursors: Vec<_> = shared.cursors.values().map(|cursor| cursor.state.clone()).collect();
1757			let fronts: Vec<_> = shared.fronts.values().map(|front| front.request.clone()).collect();
1758			(servers, cursors, fronts)
1759		};
1760
1761		// Reject requesters still parked on a remote front's channel: its watcher
1762		// was cancelled above and will never resolve them.
1763		for producer in fronts {
1764			if let Ok(mut request) = producer.write() {
1765				request.resolved.get_or_insert(Err(Error::Dropped));
1766			}
1767		}
1768		// Reject every pending route request, including those already handed to a
1769		// handler: the teardown is terminal, so a handler resolving late must not
1770		// beat it (resolution is first-write-wins).
1771		for server in servers {
1772			let mut server = server.lock();
1773			server.closed = true;
1774			for producer in server.requests.drain_all() {
1775				if let Ok(mut request) = producer.write() {
1776					request.resolved.get_or_insert(Err(Error::Dropped));
1777				}
1778			}
1779		}
1780
1781		// End the announce cursors: each drains its pending updates, then reports
1782		// the end. Registrations stay (the cursors remove themselves on drop).
1783		for state in cursors {
1784			if let Ok(mut state) = state.write() {
1785				state.ended = true;
1786			}
1787		}
1788	}
1789}
1790
1791impl Drop for DriverState {
1792	fn drop(&mut self) {
1793		self.teardown();
1794	}
1795}
1796
1797/// How long a spliced track stays warm after its last reader leaves.
1798///
1799/// Within the window a returning viewer, or the next of a run of back-to-back
1800/// fetches, reads the groups the front already cached: no second round trip for
1801/// `TRACK_INFO`. Groups past that cached edge cost a fresh source splice. After
1802/// the window, the cached segment is released.
1803///
1804/// Sized above the fetch cadence of a segmented consumer: HLS polls every
1805/// `TARGETDURATION` seconds, commonly 6 or 10, so a shorter window would drop the
1806/// copy between every segment and re-request the track each time. A warm copy
1807/// holds no upstream subscription (that is canceled as soon as demand ends), so
1808/// waiting longer costs cached state, not a viewer.
1809const TRACK_IDLE_LINGER: Duration = Duration::from_secs(30);
1810
1811/// A local copy of groups the front already delivered, so resume stays spliced
1812/// after the source track is dropped. Cache misses stay pending while demand
1813/// re-splices the upstream source. Finished on drop so an idle linger does not
1814/// warn about an abandoned producer.
1815struct WarmCopy {
1816	track: track::Producer,
1817	_dynamic: track::Dynamic,
1818}
1819
1820impl Drop for WarmCopy {
1821	fn drop(&mut self) {
1822		let _ = self.track.finish();
1823	}
1824}
1825
1826/// Cache `source`'s finished groups on a new local track the origin owns.
1827fn warm_copy(source: &track::Consumer) -> Option<WarmCopy> {
1828	let info = source.cached_info()?;
1829	let mut track = track::Producer::new(Arc::new(source.broadcast().clone()), source.name(), info);
1830	for (group, visible) in source.cached_groups() {
1831		// An open group is left for the re-splice to deliver whole. Dropping the source
1832		// copy resets it mid-transfer, and its dead head would anchor the next takeover
1833		// mid-group, asking upstream for a tail no returning reader can use.
1834		if group.is_finished() {
1835			let _ = track.adopt_group(group, visible);
1836		}
1837	}
1838	let dynamic = track.dynamic();
1839	Some(WarmCopy {
1840		track,
1841		_dynamic: dynamic,
1842	})
1843}
1844
1845/// Everything [`run_front`] owns, queued by [`Consumer::request_broadcast`].
1846struct FrontTask {
1847	/// The route table the front selects from.
1848	shared: kio::Shared<OriginState>,
1849	/// The spliced broadcast the front serves.
1850	broadcast: broadcast::Producer,
1851	/// Absolute path of the front.
1852	path: PathOwned,
1853	/// The requesters' horizon, applied to every (re)selection.
1854	horizon: Horizon,
1855	/// Wakes the front when a route covering its path changes.
1856	watch: Watch,
1857	/// Resolves the requesters parked on the front's channel.
1858	request: kio::Producer<PendingBroadcast>,
1859	/// Published for requesters once the first source fixes it; see [`RemoteFront::pin`].
1860	pin: kio::Lock<Pin>,
1861	timers: Clock,
1862}
1863
1864/// The driver's side of one logical track: the handles behind the names the
1865/// machine uses.
1866struct TrackIo {
1867	resume: super::resume::Producer,
1868	/// The copy whose info resolved, waiting for the machine to splice it.
1869	staged: Option<(u64, track::Consumer)>,
1870	/// A query in flight: the source asked, its copy, and the pending info.
1871	query: Option<(u64, track::Consumer, track::Querying)>,
1872	/// The spliced copy: its source and the track.
1873	copy: Option<(u64, track::Consumer)>,
1874	/// The delivered edge when the copy spliced in: a copy that dies without
1875	/// advancing it delivered nothing. Snapshotted per splice, not per wake, so an
1876	/// unrelated wake between the copy's last frame and its death cannot launder
1877	/// its progress away.
1878	edge: Option<track::Position>,
1879	/// Delivered groups kept after the copy was dropped, so resume stays spliced
1880	/// through the linger without pinning the source as a reader.
1881	warm: Option<WarmCopy>,
1882	/// Whether the track had a reader as of the last demand edge.
1883	used: bool,
1884}
1885
1886/// Drives one front: feeds the world's events to a [`Front`] and performs the
1887/// actions it returns, until the front ends. The decisions live in the machine;
1888/// this only waits and executes, so nothing here decides anything twice.
1889async fn run_front(task: FrontTask) {
1890	let FrontTask {
1891		shared,
1892		broadcast,
1893		path,
1894		horizon,
1895		watch,
1896		request,
1897		pin,
1898		timers,
1899	} = task;
1900
1901	/// What the wait below returns: one thing that happened.
1902	enum Step {
1903		Assigned(Arc<str>, super::resume::Producer),
1904		Resolved(u64, Result<broadcast::Consumer, Error>),
1905		SourceClosed(u64),
1906		Info(Arc<str>, u64, Result<track::Info, Error>),
1907		Ended(Arc<str>, u64, Result<(), Error>),
1908		Demand(Arc<str>),
1909		Deadline,
1910		Table,
1911	}
1912
1913	let mut front = Front::new(TRACK_IDLE_LINGER);
1914	let mut sources: HashMap<u64, broadcast::Consumer> = HashMap::new();
1915	let mut next_source = 0u64;
1916	// The in-flight upstream request: the route and its pending channel.
1917	let mut upstream: Option<(u64, kio::Consumer<PendingBroadcast>)> = None;
1918	let mut tracks: HashMap<Arc<str>, TrackIo> = HashMap::new();
1919	let mut deadline = crate::runtime::Deadline::new(&timers);
1920	// The watch generation the last selection saw.
1921	let mut seen = 0;
1922	let mut events: VecDeque<Event> = VecDeque::new();
1923
1924	// Read the table for the machine: the best qualifying route and whether
1925	// the serving source is on its way out. Also what the watch wakes for.
1926	let select = |front: &mut Front, sources: &HashMap<u64, broadcast::Consumer>, seen: &mut u64| -> Event {
1927		let table = shared.read();
1928		if table.closed {
1929			return Event::Closed;
1930		}
1931		// Read alongside the decision, under the lock a poke takes first.
1932		*seen = watch.seen();
1933		front.retain_routes(|route| table.routes.covers(&path.as_path(), route));
1934		let best = table
1935			.best_route(&path.as_path(), horizon, front.pin(), front.refused_routes())
1936			.map(|entry| Candidate {
1937				route: entry.id,
1938				first: entry.hops.iter().next().copied(),
1939				local: entry.local,
1940			});
1941		let serving_closing = front
1942			.serving()
1943			.and_then(|id| sources.get(&id))
1944			.is_some_and(|source| source.is_closing());
1945		Event::Selected { best, serving_closing }
1946	};
1947
1948	events.push_back(select(&mut front, &sources, &mut seen));
1949
1950	loop {
1951		while let Some(event) = events.pop_front() {
1952			for action in front.step(event) {
1953				match action {
1954					Action::Reselect => events.push_back(select(&mut front, &sources, &mut seen)),
1955					Action::Request { route } => {
1956						// The entry, its identity for the front, and what it serves.
1957						let found = {
1958							let table = shared.read();
1959							table
1960								.routes
1961								.covering(&path.as_path())
1962								.find(|entry| entry.id == route)
1963								.map(|entry| {
1964									(
1965										Candidate {
1966											route,
1967											first: entry.hops.iter().next().copied(),
1968											local: entry.local,
1969										},
1970										entry.source.clone(),
1971										entry.server.clone(),
1972									)
1973								})
1974						};
1975						let Some((candidate, source, server)) = found else {
1976							events.push_back(Event::Resolved {
1977								route,
1978								result: Err(Refusal {
1979									err: Error::Unroutable,
1980									standing: false,
1981								}),
1982							});
1983							continue;
1984						};
1985						front.identify(candidate);
1986						*pin.lock() = front.pin();
1987						if let Some(source) = source {
1988							let id = next_source;
1989							next_source += 1;
1990							sources.insert(id, source);
1991							events.push_back(Event::Resolved { route, result: Ok(id) });
1992							continue;
1993						}
1994						let Some(server) = server else {
1995							events.push_back(Event::Resolved {
1996								route,
1997								result: Err(Refusal {
1998									err: Error::Unroutable,
1999									standing: true,
2000								}),
2001							});
2002							continue;
2003						};
2004						let mut serve = server.lock();
2005						if serve.closed {
2006							// Retracted under us, or its handler dropped while the
2007							// announcement stands: it cannot serve.
2008							drop(serve);
2009							events.push_back(Event::Resolved {
2010								route,
2011								result: Err(Refusal {
2012									err: Error::Unroutable,
2013									standing: true,
2014								}),
2015							});
2016							continue;
2017						}
2018						// A source this route already materialized for the path
2019						// attaches without another upstream round trip.
2020						if let Some(weak) = serve.served.get(&path) {
2021							drop(serve);
2022							let id = next_source;
2023							next_source += 1;
2024							sources.insert(id, weak.consume());
2025							events.push_back(Event::Resolved { route, result: Ok(id) });
2026							continue;
2027						}
2028						let pending = match serve.requests.join(&path) {
2029							Some(producer) => producer.consume(),
2030							None => {
2031								let producer = kio::Producer::<PendingBroadcast>::default();
2032								let consumer = producer.consume();
2033								match serve.requests.insert(path.clone(), producer) {
2034									Ok(()) => consumer,
2035									// No live handler behind the route: it cannot
2036									// serve, whatever the table says.
2037									Err(_) => {
2038										drop(serve);
2039										events.push_back(Event::Resolved {
2040											route,
2041											result: Err(Refusal {
2042												err: Error::Unroutable,
2043												standing: true,
2044											}),
2045										});
2046										continue;
2047									}
2048								}
2049							}
2050						};
2051						upstream = Some((route, pending));
2052					}
2053					Action::Detach { source } => {
2054						sources.remove(&source);
2055						// Its copies go with it; the segments they delivered stay
2056						// spliced until a replacement resumes past them.
2057						for io in tracks.values_mut() {
2058							if io.copy.as_ref().is_some_and(|(s, _)| *s == source) {
2059								io.copy = None;
2060							}
2061							if io.query.as_ref().is_some_and(|(s, ..)| *s == source) {
2062								io.query = None;
2063							}
2064							if io.staged.as_ref().is_some_and(|(s, _)| *s == source) {
2065								io.staged = None;
2066							}
2067						}
2068					}
2069					Action::Resolve => {
2070						if let Ok(mut pending) = request.write() {
2071							pending.resolved.get_or_insert(Ok(broadcast.consume()));
2072						}
2073					}
2074					Action::Query { track: name, source } => {
2075						let Some(io) = tracks.get_mut(&name) else { continue };
2076						let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2077						match sources.get(&source).map(|s| s.track(&name)) {
2078							Some(Ok(copy)) => {
2079								// `into_inner` sheds the `Pending` future wrapper so only
2080								// the pollable (which is `Sync`) is held across the wait.
2081								let query = copy.query().into_inner();
2082								io.query = Some((source, copy, query));
2083							}
2084							Some(Err(err)) => events.push_back(Event::TrackInfo {
2085								track: name,
2086								source,
2087								closing,
2088								result: Err(err),
2089							}),
2090							None => {}
2091						}
2092					}
2093					Action::Splice { track: name, source } => {
2094						let Some(io) = tracks.get_mut(&name) else { continue };
2095						let Some((staged, copy)) = io.staged.take() else {
2096							continue;
2097						};
2098						if staged != source {
2099							continue;
2100						}
2101						if let Err(err) = io.resume.takeover(&copy) {
2102							// Closed means the logical track already ended. Anything
2103							// else is a boundary bug; abort rather than strand
2104							// subscribers on a track nobody serves.
2105							let _ = io.resume.abort(err);
2106							tracks.remove(&name);
2107							continue;
2108						}
2109						io.warm = None;
2110						// The new segment has produced nothing yet: this is the
2111						// edge the copy is asked to advance.
2112						io.edge = io.resume.resume_position();
2113						io.copy = Some((source, copy));
2114					}
2115					Action::Park { track: name } => {
2116						let Some(io) = tracks.get_mut(&name) else { continue };
2117						let Some((_, copy)) = io.copy.take() else { continue };
2118						// Drop the source copy so its producer goes idle at once; keep
2119						// the groups it delivered on a local track so resume stays
2120						// spliced until the linger expires.
2121						let warm = warm_copy(&copy);
2122						drop(copy);
2123						if io.resume.release().is_err() {
2124							tracks.remove(&name);
2125							continue;
2126						}
2127						if let Some(warm) = warm {
2128							if let Err(err) = io.resume.takeover(&warm.track) {
2129								let _ = io.resume.abort(err);
2130								tracks.remove(&name);
2131								continue;
2132							}
2133							io.warm = Some(warm);
2134						}
2135					}
2136					Action::Release { track: name } => {
2137						let Some(io) = tracks.get_mut(&name) else { continue };
2138						io.warm = None;
2139						if io.resume.release().is_err() {
2140							tracks.remove(&name);
2141						}
2142					}
2143					Action::Finish { track: name } => {
2144						if let Some(mut io) = tracks.remove(&name) {
2145							let _ = io.resume.finish();
2146						}
2147					}
2148					Action::Abort { track: name, err } => {
2149						if let Some(mut io) = tracks.remove(&name) {
2150							tracing::debug!(name = %name, %err, "aborting track");
2151							let _ = io.resume.abort(err);
2152						}
2153					}
2154					Action::Arm { at } => deadline.set(at),
2155					Action::End { err } => {
2156						if let Ok(mut pending) = request.write() {
2157							pending.resolved.get_or_insert(Err(err.clone()));
2158						}
2159						// Ending the broadcast only retracts it: no new requesters or
2160						// tracks, and a newcomer at the path gets a fresh front. Tracks
2161						// in flight carry on (moq-lite: retraction does not disturb
2162						// subscriptions already in flight): dropping their producers
2163						// leaves each reader on the copy it was spliced from, ending
2164						// when and as that copy ends.
2165						broadcast.finish();
2166						broadcast.release_spliced(err.clone());
2167						for (_, mut io) in tracks.drain() {
2168							// A reader still waiting on its source's answer is in flight
2169							// too: splice the copy it asked, past any warm cache, so it
2170							// ends as that copy does.
2171							let waiting = io.staged.take().map(|(_, copy)| copy);
2172							let waiting = waiting.or_else(|| io.query.take().map(|(_, copy, _)| copy));
2173							if let Some(copy) = waiting
2174								&& io.resume.is_used()
2175							{
2176								if io.resume.takeover(&copy).is_err() {
2177									continue;
2178								}
2179								io.warm = None;
2180							}
2181							// Nothing in flight: unread, never spliced, or only a warm cache.
2182							if !io.resume.is_used() || !io.resume.is_spliced() || io.warm.is_some() {
2183								let _ = io.resume.abort(err.clone());
2184							}
2185						}
2186						return;
2187					}
2188				}
2189			}
2190		}
2191
2192		let step = kio::wait(|waiter| {
2193			if let Poll::Ready((name, resume)) = broadcast.poll_spliced_assigned(waiter) {
2194				return Poll::Ready(Step::Assigned(name, resume));
2195			}
2196			if let Some((route, pending)) = &upstream
2197				&& let Poll::Ready(result) = pending.poll(waiter, |p| match &p.resolved {
2198					Some(result) => Poll::Ready(result.clone()),
2199					None => Poll::Pending,
2200				}) {
2201				return Poll::Ready(Step::Resolved(
2202					*route,
2203					match result {
2204						Ok(resolved) => resolved,
2205						// The queue died unresolved (its handler dropped): the route
2206						// could not serve.
2207						Err(_closed) => Err(Error::Unroutable),
2208					},
2209				));
2210			}
2211			if let Some(id) = front.serving()
2212				&& let Some(source) = sources.get(&id)
2213				&& source.poll_closed(waiter).is_ready()
2214			{
2215				return Poll::Ready(Step::SourceClosed(id));
2216			}
2217			for (name, io) in &tracks {
2218				if let Some((source, _, query)) = &io.query
2219					&& let Poll::Ready(result) = query.poll(waiter)
2220				{
2221					return Poll::Ready(Step::Info(name.clone(), *source, result));
2222				}
2223				if let Some((source, copy)) = &io.copy
2224					&& let Poll::Ready(result) = copy.poll_complete(waiter)
2225				{
2226					return Poll::Ready(Step::Ended(name.clone(), *source, result));
2227				}
2228				// Watch the demand edge in whichever direction is unmet.
2229				let edge = match io.used {
2230					true => io.resume.poll_unused(waiter),
2231					false => io.resume.poll_used(waiter),
2232				};
2233				if edge.is_ready() {
2234					return Poll::Ready(Step::Demand(name.clone()));
2235				}
2236			}
2237			if deadline.poll(waiter).is_ready() {
2238				return Poll::Ready(Step::Deadline);
2239			}
2240			watch.poll_changed(waiter, seen).map(|()| Step::Table)
2241		})
2242		.await;
2243
2244		let event = match step {
2245			Step::Assigned(name, resume) => {
2246				tracks.insert(
2247					name.clone(),
2248					TrackIo {
2249						resume,
2250						staged: None,
2251						query: None,
2252						copy: None,
2253						edge: None,
2254						warm: None,
2255						used: false,
2256					},
2257				);
2258				Event::TrackAssigned { track: name }
2259			}
2260			Step::Resolved(route, result) => {
2261				upstream = None;
2262				match result {
2263					Ok(source) => {
2264						let id = next_source;
2265						next_source += 1;
2266						sources.insert(id, source);
2267						Event::Resolved { route, result: Ok(id) }
2268					}
2269					Err(err) => {
2270						// A retraction and a handler's rejection resolve alike, so
2271						// the table tells them apart: an `Unroutable` from a route
2272						// that still stands is the handler's answer.
2273						let standing =
2274							!matches!(err, Error::Unroutable) || shared.read().routes.covers(&path.as_path(), route);
2275						Event::Resolved {
2276							route,
2277							result: Err(Refusal { err, standing }),
2278						}
2279					}
2280				}
2281			}
2282			Step::SourceClosed(source) => Event::SourceClosed { source },
2283			Step::Info(name, source, result) => {
2284				let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2285				let Some(io) = tracks.get_mut(&name) else { continue };
2286				let Some((_, copy, _)) = io.query.take() else { continue };
2287				// A copy that is already aborted cannot be spliced; its error is
2288				// the source's answer for the track.
2289				let result = match result {
2290					Ok(info) => match copy.poll_complete(&kio::Waiter::noop()) {
2291						Poll::Ready(Err(err)) => Err(err),
2292						_ => Ok(info),
2293					},
2294					Err(err) => Err(err),
2295				};
2296				// Staged only while the track has a reader: without one the machine
2297				// will not splice, and a held copy would keep the source subscribed.
2298				if result.is_ok() && io.used {
2299					io.staged = Some((source, copy));
2300				}
2301				Event::TrackInfo {
2302					track: name,
2303					source,
2304					closing,
2305					result,
2306				}
2307			}
2308			Step::Ended(name, source, result) => {
2309				let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2310				let Some(io) = tracks.get_mut(&name) else { continue };
2311				io.copy = None;
2312				let delivered = io.resume.resume_position() != io.edge;
2313				Event::TrackEnded {
2314					track: name,
2315					source,
2316					closing,
2317					result,
2318					delivered,
2319				}
2320			}
2321			Step::Demand(name) => {
2322				let Some(io) = tracks.get_mut(&name) else { continue };
2323				io.used = io.resume.is_used();
2324				if !io.used {
2325					// Nothing will be spliced now: let go of the copies a query
2326					// holds, or the source stays subscribed with nobody reading.
2327					io.query = None;
2328					io.staged = None;
2329				}
2330				match io.used {
2331					true => Event::Used { track: name },
2332					false => Event::Unused {
2333						track: name,
2334						now: timers.now(),
2335					},
2336				}
2337			}
2338			Step::Deadline => {
2339				// Cleared here so a fired deadline cannot keep firing; the machine
2340				// re-arms what is still parked.
2341				deadline.set(None);
2342				Event::Deadline { now: timers.now() }
2343			}
2344			Step::Table => select(&mut front, &sources, &mut seen),
2345		};
2346		events.push_back(event);
2347	}
2348}
2349
2350/// The announced routes, keyed by prefix: a trie with one node per path
2351/// segment. Every question about a path walks its segments, so the cost of an
2352/// announcement, a cursor registration, or a request is bounded by the tree
2353/// around that path and never by the size of the table.
2354#[derive(Default)]
2355struct RouteTable {
2356	root: RouteNode,
2357}
2358
2359/// One prefix in the [`RouteTable`]: what is announced exactly there, which
2360/// cursors hang there, and the prefixes one segment below.
2361#[derive(Default)]
2362struct RouteNode {
2363	/// Routes announced exactly at this prefix.
2364	entries: Vec<RouteEntry>,
2365	/// Cursors with an interest head at this prefix (see [`interest_prefixes`]).
2366	cursors: Vec<ConsumerId>,
2367	/// Cursors at this node or below. An announcement walks only the subtrees
2368	/// that hold one, so a deep table of routes nobody watches costs nothing.
2369	cursors_below: usize,
2370	/// Who is waiting on the routes covering this prefix: the fronts serving it
2371	/// and the requesters parked on it (see [`Watch`]).
2372	watches: Vec<(u64, kio::Producer<Watched>)>,
2373	/// Watches at this node or below, so a route change walks only the subtrees
2374	/// holding one.
2375	watches_below: usize,
2376	children: HashMap<String, RouteNode>,
2377}
2378
2379/// What a [`Watch`] observes: bumped by every change to a route covering its
2380/// path (a broadcast published here is one) and by the origin's teardown.
2381#[derive(Default)]
2382struct Watched {
2383	generation: u64,
2384}
2385
2386/// A registration in the route table for changes to the routes covering one
2387/// path. The table pokes it; the holder waits on it, so an announcement wakes
2388/// only the fronts and requesters it can affect rather than every one of them.
2389/// Dropping it unregisters, which takes the table lock: never drop one while
2390/// holding it.
2391struct Watch {
2392	shared: kio::Shared<OriginState>,
2393	path: PathOwned,
2394	id: u64,
2395	signal: kio::Consumer<Watched>,
2396}
2397
2398impl Watch {
2399	/// The generation to wait past with [`Self::poll_changed`]. Read under the
2400	/// table lock, alongside the decision it guards, so a poke between the two
2401	/// cannot be missed: a poke takes that same lock first.
2402	fn seen(&self) -> u64 {
2403		self.signal.read().generation
2404	}
2405
2406	/// Ready once the routes covering the path moved past `seen`.
2407	fn poll_changed(&self, waiter: &kio::Waiter, seen: u64) -> Poll<()> {
2408		self.signal
2409			.poll(waiter, |watched| match watched.generation != seen {
2410				true => Poll::Ready(()),
2411				false => Poll::Pending,
2412			})
2413			.map(|_| ())
2414	}
2415}
2416
2417impl Drop for Watch {
2418	fn drop(&mut self) {
2419		self.shared.lock().routes.remove_watch(&self.path, self.id);
2420	}
2421}
2422
2423/// What a registration adds to the subtree counts on its walk.
2424#[derive(Clone, Copy)]
2425struct Below {
2426	cursors: usize,
2427	watches: usize,
2428}
2429
2430impl Below {
2431	const NONE: Self = Self { cursors: 0, watches: 0 };
2432	const CURSOR: Self = Self { cursors: 1, watches: 0 };
2433	const WATCH: Self = Self { cursors: 0, watches: 1 };
2434}
2435
2436impl RouteNode {
2437	/// Nothing here and nothing below: the node can be pruned.
2438	fn is_empty(&self) -> bool {
2439		self.entries.is_empty() && self.cursors.is_empty() && self.watches.is_empty() && self.children.is_empty()
2440	}
2441
2442	/// The node `parts` below this one, if the table has it.
2443	fn find<'a>(&self, mut parts: impl Iterator<Item = &'a str>) -> Option<&Self> {
2444		match parts.next() {
2445			None => Some(self),
2446			Some(part) => self.children.get(part)?.find(parts),
2447		}
2448	}
2449
2450	/// The node `parts` below this one, created along the way when missing.
2451	/// `below` is added to the subtree counts at every node on the walk.
2452	fn reach<'a>(&mut self, mut parts: impl Iterator<Item = &'a str>, below: Below) -> &mut Self {
2453		self.cursors_below += below.cursors;
2454		self.watches_below += below.watches;
2455		match parts.next() {
2456			None => self,
2457			Some(part) => self.children.entry(part.to_string()).or_default().reach(parts, below),
2458		}
2459	}
2460
2461	/// Run `f` on the node `parts` below this one, then prune every node the
2462	/// edit emptied. `below` is subtracted from the subtree counts at every node
2463	/// on the walk. `None` when the node does not exist, leaving the table as is.
2464	fn edit<'a, R>(
2465		&mut self,
2466		mut parts: impl Iterator<Item = &'a str>,
2467		below: Below,
2468		f: impl FnOnce(&mut Self) -> R,
2469	) -> Option<R> {
2470		let result = match parts.next() {
2471			None => f(self),
2472			Some(part) => {
2473				let child = self.children.get_mut(part)?;
2474				let result = child.edit(parts, below, f)?;
2475				if child.is_empty() {
2476					self.children.remove(part);
2477				}
2478				result
2479			}
2480		};
2481		self.cursors_below -= below.cursors;
2482		self.watches_below -= below.watches;
2483		Some(result)
2484	}
2485
2486	/// Wake the watches at this node.
2487	fn poke(&self) {
2488		for (_, watch) in &self.watches {
2489			if let Ok(mut watched) = watch.write() {
2490				watched.generation += 1;
2491			}
2492		}
2493	}
2494
2495	/// Wake the watches at this node and below: a route here covers every one
2496	/// of their paths. Skips subtrees holding none.
2497	fn poke_below(&self) {
2498		if self.watches_below == 0 {
2499			return;
2500		}
2501		self.poke();
2502		for child in self.children.values() {
2503			child.poke_below();
2504		}
2505	}
2506
2507	/// Visit this node and everything below it.
2508	fn walk<'a>(&'a self, visit: &mut impl FnMut(&'a Self)) {
2509		visit(self);
2510		for child in self.children.values() {
2511			child.walk(visit);
2512		}
2513	}
2514
2515	/// Collect the cursors at this node and below, skipping subtrees with none.
2516	fn collect_cursors(&self, out: &mut Vec<ConsumerId>) {
2517		if self.cursors_below == 0 {
2518			return;
2519		}
2520		out.extend(&self.cursors);
2521		for child in self.children.values() {
2522			child.collect_cursors(out);
2523		}
2524	}
2525}
2526
2527impl RouteTable {
2528	/// The nodes above `path` and the node at it, as far as the table has them.
2529	/// The entries of those nodes are exactly the routes covering `path`.
2530	fn split(&self, path: &Path) -> (Vec<&RouteNode>, Option<&RouteNode>) {
2531		let mut above = Vec::new();
2532		let mut node = &self.root;
2533		for part in path.parts() {
2534			above.push(node);
2535			match node.children.get(part) {
2536				Some(child) => node = child,
2537				None => return (above, None),
2538			}
2539		}
2540		(above, Some(node))
2541	}
2542
2543	/// The routes covering `path`: those announced at it and at every prefix of it.
2544	fn covering(&self, path: &Path) -> impl Iterator<Item = &RouteEntry> {
2545		let (above, at) = self.split(path);
2546		above.into_iter().chain(at).flat_map(|node| node.entries.iter())
2547	}
2548
2549	/// Whether the route `id` still covers `path`.
2550	fn covers(&self, path: &Path, id: u64) -> bool {
2551		self.covering(path).any(|entry| entry.id == id)
2552	}
2553
2554	/// The routes announced exactly at `prefix`.
2555	fn at(&self, prefix: &Path) -> impl Iterator<Item = &RouteEntry> {
2556		self.root
2557			.find(prefix.parts())
2558			.into_iter()
2559			.flat_map(|node| node.entries.iter())
2560	}
2561
2562	/// Every route in the table, for the teardown.
2563	fn entries(&self) -> impl Iterator<Item = &RouteEntry> {
2564		let mut nodes = Vec::new();
2565		self.root.walk(&mut |node| nodes.push(node));
2566		nodes.into_iter().flat_map(|node| node.entries.iter())
2567	}
2568
2569	/// Add a route at its prefix, creating the nodes down to it.
2570	fn insert(&mut self, entry: RouteEntry) {
2571		let node = self.root.reach(entry.prefix.parts(), Below::NONE);
2572		node.entries.push(entry);
2573	}
2574
2575	/// The route `id` announced at `prefix`, for a re-price in place.
2576	fn entry_mut(&mut self, prefix: &Path, id: u64) -> Option<&mut RouteEntry> {
2577		let mut node = &mut self.root;
2578		for part in prefix.parts() {
2579			node = node.children.get_mut(part)?;
2580		}
2581		node.entries.iter_mut().find(|entry| entry.id == id)
2582	}
2583
2584	/// Take the route `id` out of `prefix`, pruning the nodes it leaves empty.
2585	fn remove(&mut self, prefix: &Path, id: u64) -> Option<RouteEntry> {
2586		self.root
2587			.edit(prefix.parts(), Below::NONE, |node| {
2588				let index = node.entries.iter().position(|entry| entry.id == id)?;
2589				Some(node.entries.swap_remove(index))
2590			})
2591			.flatten()
2592	}
2593
2594	/// Hang a cursor at one of its heads, counting it down the walk.
2595	fn add_cursor(&mut self, head: &Path, id: ConsumerId) {
2596		self.root.reach(head.parts(), Below::CURSOR).cursors.push(id);
2597	}
2598
2599	/// Take a cursor off one of its heads, pruning the nodes it leaves empty. Only
2600	/// ever called for a head the cursor was added at, or the counts drift.
2601	fn remove_cursor(&mut self, head: &Path, id: ConsumerId) {
2602		self.root.edit(head.parts(), Below::CURSOR, |node| {
2603			node.cursors.retain(|cursor| *cursor != id)
2604		});
2605	}
2606
2607	/// Register a watch on the routes covering `path`; see [`Watch`].
2608	fn add_watch(&mut self, path: &Path, id: u64) -> kio::Consumer<Watched> {
2609		let producer = kio::Producer::<Watched>::default();
2610		let consumer = producer.consume();
2611		self.root.reach(path.parts(), Below::WATCH).watches.push((id, producer));
2612		consumer
2613	}
2614
2615	/// Take a watch off its path, pruning the nodes it leaves empty. Only ever
2616	/// called for a path the watch was added at, or the counts drift.
2617	fn remove_watch(&mut self, path: &Path, id: u64) {
2618		self.root.edit(path.parts(), Below::WATCH, |node| {
2619			node.watches.retain(|(watch, _)| *watch != id)
2620		});
2621	}
2622
2623	/// Wake the watches of every path a route at `prefix` covers.
2624	fn poke_below(&self, prefix: &Path) {
2625		if let (_, Some(node)) = self.split(prefix) {
2626			node.poke_below();
2627		}
2628	}
2629
2630	/// Wake every watch: the origin is tearing down.
2631	fn poke_all(&self) {
2632		self.root.walk(&mut |node| node.poke());
2633	}
2634
2635	/// The cursors a route at `prefix` can present on: a cursor sees a route
2636	/// only when one of its heads is on the walk down to the prefix or somewhere
2637	/// beneath it, so those are the only cursors visited.
2638	fn cursors_touching(&self, prefix: &Path) -> Vec<ConsumerId> {
2639		let (above, at) = self.split(prefix);
2640		let mut cursors: Vec<ConsumerId> = above.iter().flat_map(|node| node.cursors.iter().copied()).collect();
2641		if let Some(node) = at {
2642			node.collect_cursors(&mut cursors);
2643		}
2644		// A cursor with several heads can be reached more than once.
2645		cursors.sort_unstable();
2646		cursors.dedup();
2647		cursors
2648	}
2649}
2650
2651/// The origin's shared state: the route table, the announce cursors observing
2652/// it, and the remotely-served fronts.
2653///
2654/// Carried in a [`kio::Shared`], so producers, consumers, and handlers work
2655/// under one lock. Broadcasts published here are route table entries like the
2656/// routes announced from elsewhere; this holds everything that serves a path.
2657#[derive(Default)]
2658struct OriginState {
2659	// The announced routes, keyed by prefix. The table holds one entry per live
2660	// advertisement, not one per broadcast consumer.
2661	routes: RouteTable,
2662	next_route: u64,
2663	next_watch: u64,
2664
2665	// The registered announce cursors, each with its own coalescing buffer. Each
2666	// also hangs in the route table at its heads, which is how an announcement
2667	// finds the cursors it can present on.
2668	cursors: HashMap<ConsumerId, TableCursor>,
2669
2670	// The remotely-served fronts, keyed by absolute path and the requester's
2671	// split-horizon exclusion. Each is a spliced broadcast whose watcher task
2672	// materializes it from the best covering route and re-splices it through
2673	// routes sharing its first hop, so a route change the identity survives is
2674	// invisible to subscribers. Keyed per exclusion so a front's failover can
2675	// never adopt a route flowing back through one of its own readers. Weak, so
2676	// a front dies with its watcher and a later request re-creates it.
2677	fronts: WeakCache<FrontKey, RemoteFront>,
2678
2679	// Set when the origin's driver dropped: new requests fail with `Closed`
2680	// immediately and handlers observe the end instead of parking forever.
2681	closed: bool,
2682}
2683
2684impl OriginState {
2685	/// Re-deliver the best route at every presented prefix `prefix` maps to, on
2686	/// every cursor it can present on. Called after an entry covering `prefix`
2687	/// was added, updated, or removed. `claim` is `prefix`'s [`prefix_claim`],
2688	/// held by the entry that changed.
2689	fn sync_route(&mut self, prefix: &Path, claim: &Pattern) {
2690		// Split borrows: the recompute reads `routes` while mutating a cursor.
2691		let routes = &self.routes;
2692		for id in routes.cursors_touching(prefix) {
2693			let Some(cursor) = self.cursors.get_mut(&id) else {
2694				continue;
2695			};
2696			if let Some(presented) = cursor.presented(prefix, claim) {
2697				Self::sync_cursor(routes, cursor, &presented);
2698			}
2699		}
2700		// The fronts and requesters under the prefix re-select from the table.
2701		routes.poke_below(prefix);
2702	}
2703
2704	/// Register a [`Watch`] on the routes covering `path`.
2705	fn watch(&mut self, shared: &kio::Shared<OriginState>, path: &Path) -> Watch {
2706		let id = self.next_watch;
2707		self.next_watch += 1;
2708		let signal = self.routes.add_watch(path, id);
2709		Watch {
2710			shared: shared.clone(),
2711			path: path.to_owned(),
2712			id,
2713			signal,
2714		}
2715	}
2716
2717	/// Recompute the best visible route presenting at `presented` (relative) for
2718	/// one cursor and deliver the change, if any.
2719	fn sync_cursor(routes: &RouteTable, cursor: &mut TableCursor, presented: &PathOwned) {
2720		// The entries presenting here are the ones announced at the absolute
2721		// prefix, or, for the cursor's own root, at the root and every prefix
2722		// above it (all of which present as the empty path). Among them, the
2723		// longest prefix wins outright, so the metadata a cursor advertises
2724		// matches what a request through it actually resolves.
2725		let candidates: Vec<&RouteEntry> = match presented.is_empty() {
2726			true => routes
2727				.covering(&cursor.root)
2728				.filter(|entry| cursor.visible(entry))
2729				.collect(),
2730			false => {
2731				let absolute = cursor.root.join(presented);
2732				routes.at(&absolute).filter(|entry| cursor.visible(entry)).collect()
2733			}
2734		};
2735		let most = candidates.iter().map(|entry| entry.prefix.len()).max();
2736		let best = most.and_then(|most| {
2737			candidates
2738				.into_iter()
2739				.filter(|entry| entry.prefix.len() == most)
2740				.min_by_key(|entry| route_order(&entry.prefix, entry))
2741		});
2742
2743		match best {
2744			Some(entry) => {
2745				let meta = (entry.hops.clone(), entry.cost, entry.entered());
2746				let served = entry.server.is_some();
2747				let captures = cursor.captures(&entry.prefix);
2748				let previous = cursor
2749					.current
2750					.insert(presented.clone(), (entry.id, meta.clone(), served, captures.clone()));
2751				match previous {
2752					// Unchanged metadata and servability: nothing the consumer could
2753					// act on, even if the winning entry itself changed (a reconnect
2754					// under an identical route is invisible, which is the point). A
2755					// servability flip is delivered: a request that failed Unroutable
2756					// under an advertise-only route retries on the update, and hiding
2757					// it would park that waiter forever.
2758					Some((_, prev, prev_served, prev_captures))
2759						if prev == meta && prev_served == served && prev_captures == captures => {}
2760					// Captures are consumer identity, not route metadata. Replace the
2761					// old identity explicitly so capture-keyed consumers can remove it.
2762					Some((_, prev, _, prev_captures)) if prev_captures != captures => {
2763						if let Ok(mut state) = cursor.state.write() {
2764							state.apply_unannounce(presented.clone(), prev, prev_captures);
2765							state.apply_announce(presented.clone(), meta, captures);
2766						}
2767					}
2768					_ => {
2769						if let Ok(mut state) = cursor.state.write() {
2770							state.apply_announce(presented.clone(), meta, captures);
2771						}
2772					}
2773				}
2774			}
2775			None => {
2776				if let Some((_, last, _, captures)) = cursor.current.remove(presented)
2777					&& let Ok(mut state) = cursor.state.write()
2778				{
2779					state.apply_unannounce(presented.clone(), last, captures);
2780				}
2781			}
2782		}
2783	}
2784
2785	/// Register a cursor and replay the current best route per presented prefix.
2786	fn register_cursor(&mut self, id: ConsumerId, mut cursor: TableCursor) {
2787		// The routes a cursor can see sit on the walk down to one of its heads or
2788		// somewhere beneath it, so only those subtrees are replayed.
2789		let mut presented: BTreeSet<PathOwned> = BTreeSet::new();
2790		for head in &cursor.heads {
2791			let (above, at) = self.routes.split(head);
2792			let mut nodes = above;
2793			if let Some(node) = at {
2794				node.walk(&mut |node| nodes.push(node));
2795			}
2796			for entry in nodes.into_iter().flat_map(|node| node.entries.iter()) {
2797				if let Some(p) = cursor.presented(&entry.prefix, &entry.claim) {
2798					presented.insert(p);
2799				}
2800			}
2801		}
2802		for p in &presented {
2803			Self::sync_cursor(&self.routes, &mut cursor, p);
2804		}
2805		for head in &cursor.heads {
2806			self.routes.add_cursor(head, id);
2807		}
2808		self.cursors.insert(id, cursor);
2809	}
2810
2811	/// The best served route covering `path` (absolute) for a requester seeing
2812	/// `horizon`, skipping the `refused` entry ids.
2813	///
2814	/// The most specific covering prefix wins outright, so a narrow advertise-only
2815	/// announcement shadows a broad served one: requests under it resolve
2816	/// unroutable instead of being routed around it. Among routes at the winning
2817	/// prefix, the cheapest served one is picked by [`route_order`].
2818	///
2819	/// Only announced routes are candidates: an unannounced broadcast serves
2820	/// nobody, and does not shadow anything either. `pin` is the front's
2821	/// identity: only routes it admits are candidates, since a route from anyone
2822	/// else is different content rather than an alternate path (see [`Front`]).
2823	/// A broadcast published on this origin competes on cost like any other
2824	/// route and wins a tie.
2825	fn best_route(&self, path: &Path, horizon: Horizon, pin: Pin, refused: &HashSet<u64>) -> Option<&RouteEntry> {
2826		// Covering prefixes of one path form a chain, so the deepest node with a
2827		// candidate holds the unique longest prefix; walking down, the last such
2828		// node decides.
2829		let (above, at) = self.routes.split(path);
2830		let mut best = None;
2831		for node in above.into_iter().chain(at) {
2832			let mut candidates = node
2833				.entries
2834				.iter()
2835				.filter(|entry| entry.advertised)
2836				.filter(|entry| entry.scope.matches(path.as_str()))
2837				.filter(|entry| horizon.admits(entry))
2838				.filter(|entry| entry.qualifies(pin))
2839				.filter(|entry| !refused.contains(&entry.id))
2840				.peekable();
2841			if candidates.peek().is_some() {
2842				best = candidates
2843					.filter(|entry| entry.serves(path))
2844					.min_by_key(|entry| route_order(&entry.prefix, entry));
2845			}
2846		}
2847		best
2848	}
2849}
2850
2851/// One-shot result of a dynamic broadcast request.
2852///
2853/// Stays `None` until a handler [`accept`](Request::accept)s (yielding the served
2854/// broadcast) or [`reject`](Request::reject)s (yielding an error). The producer is
2855/// dropped right after writing, closing the channel; kio checks the value before the closed
2856/// flag, so an awaiting requester still observes the final result.
2857#[derive(Default)]
2858struct PendingBroadcast {
2859	resolved: Option<Result<broadcast::Consumer, Error>>,
2860}
2861
2862/// A served route, from [`Producer::dynamic`]: advertises a path prefix and
2863/// answers the [`Consumer::request_broadcast`] calls beneath it.
2864///
2865/// The origin-level analogue of [`broadcast::Dynamic`]: where that serves tracks
2866/// on demand within a broadcast, this serves whole broadcasts on demand within
2867/// an origin. A relay holds one per route a peer announces to it, materializing
2868/// a requested path from that peer; an application holds one to answer a
2869/// subtree it never publishes ahead of time.
2870///
2871/// Drop it to retract the route and reject the requests still waiting to be
2872/// served; [`update`](Self::update) re-prices it in place.
2873#[must_use = "dropping an origin::Dynamic retracts the route"]
2874pub struct Dynamic {
2875	/// The advertisement, retracted on drop.
2876	announcement: AnnounceProducer,
2877	state: kio::Shared<ServeState>,
2878}
2879
2880impl Dynamic {
2881	/// Re-price the route in place: replace its hops and cost.
2882	///
2883	/// Consumers observe another active update for the same prefix; sessions
2884	/// forward it as a restart, so route churn never looks like new content. The
2885	/// prefix is fixed at announce time: to move a route, drop this and call
2886	/// [`Producer::dynamic`] again. Fails with [`Error::Closed`] once the origin's
2887	/// [`Driver`] has been dropped.
2888	pub fn update(&self, route: Route) -> Result<(), Error> {
2889		self.announcement.update(route)
2890	}
2891
2892	/// Poll for the next requested path under this route, without blocking.
2893	///
2894	/// Returns [`Error::Closed`] once the origin's [`Driver`] has been dropped:
2895	/// no request will ever arrive again, so handler loops should end.
2896	pub fn poll_requested_broadcast(&self, waiter: &kio::Waiter) -> Poll<Result<Request, Error>> {
2897		let mut state = ready!(self.state.poll(waiter, |state| {
2898			if state.closed || state.requests.has_queued() {
2899				Poll::Ready(())
2900			} else {
2901				Poll::Pending
2902			}
2903		}));
2904
2905		// The teardown already drained the queue, so there is nothing left to pop.
2906		if state.closed {
2907			return Poll::Ready(Err(Error::Closed));
2908		}
2909
2910		let path = state.requests.pop().expect("predicate guaranteed a request");
2911		// The popped request stays pending, so a repeat request in the window between
2912		// hand-off and accept coalesces onto it instead of re-invoking the handler. The
2913		// producer is a shared clone; `Request::{accept, reject, drop}` removes the
2914		// entry. This mirrors how `poll_requested_track` keeps a served track
2915		// discoverable via the weak cache across the same window.
2916		let producer = state.requests.get(&path).expect("popped key must be pending").clone();
2917		Poll::Ready(Ok(Request {
2918			path,
2919			producer,
2920			home: self.state.clone(),
2921		}))
2922	}
2923
2924	/// Block until a consumer requests a path under this route, returning a
2925	/// [`Request`] to serve.
2926	///
2927	/// Takes `&self` so a handler can serve from one task while another re-prices
2928	/// the route; concurrent callers each receive distinct requests.
2929	pub async fn requested_broadcast(&self) -> Result<Request, Error> {
2930		kio::wait(|waiter| self.poll_requested_broadcast(waiter)).await
2931	}
2932}
2933
2934impl ServeState {
2935	/// Resolve a pending request: cache an accepted broadcast for repeat
2936	/// requests, remove the queue entry, and wake the requesters.
2937	///
2938	/// Resolved while the queue's lock is held, so this linearizes with the
2939	/// teardown: either the teardown ran first (the `closed` check returns, its
2940	/// rejection stands) or this write lands first and the teardown finds the
2941	/// entry already gone. The queue lock is released before the channel guard
2942	/// drops, so the requester wakes outside it: an inline executor re-entering
2943	/// `request_broadcast` from the wake must not find the non-reentrant lock
2944	/// still held.
2945	fn resolve(
2946		shared: &kio::Shared<Self>,
2947		path: &PathOwned,
2948		producer: &kio::Producer<PendingBroadcast>,
2949		result: Result<broadcast::Consumer, Error>,
2950	) {
2951		let mut state = shared.lock();
2952		if state.closed {
2953			return;
2954		}
2955		let resolved = match result {
2956			Ok(broadcast) => {
2957				// If a live broadcast was already served for this path while we were
2958				// fetching upstream, dedup onto it and drop ours rather than replace
2959				// a good entry with a duplicate subscription.
2960				let existing = state.served.insert(path.clone(), broadcast.weak());
2961				Ok(existing.map(|weak| weak.consume()).unwrap_or(broadcast))
2962			}
2963			Err(err) => Err(err),
2964		};
2965		state.requests.remove_if(path, |p| p.same_channel(producer));
2966		if let Ok(mut pending) = producer.write() {
2967			pending.resolved.get_or_insert(resolved);
2968			drop(state);
2969		}
2970	}
2971
2972	/// Drop the still-pending entry, if it is still ours.
2973	fn forget(shared: &kio::Shared<Self>, path: &PathOwned, producer: &kio::Producer<PendingBroadcast>) {
2974		shared.lock().requests.remove_if(path, |p| p.same_channel(producer));
2975	}
2976}
2977
2978/// A pending request for a broadcast to be served on demand.
2979///
2980/// Yielded by [`Dynamic::requested_broadcast`]. The requester is awaiting inside
2981/// [`Consumer::request_broadcast`]; [`accept`](Self::accept) resolves it with a live
2982/// broadcast (which the handler keeps producing into) and [`reject`](Self::reject) resolves
2983/// it with an error. Dropping the request without either rejects it.
2984pub struct Request {
2985	// Absolute path that was requested.
2986	path: PathOwned,
2987
2988	// Result channel back to the awaiting requester(s). Writing `resolved` and dropping
2989	// this wakes them with the outcome.
2990	producer: kio::Producer<PendingBroadcast>,
2991
2992	// The queue this request came from, so `accept` can cache the served
2993	// broadcast for repeat requests.
2994	home: kio::Shared<ServeState>,
2995}
2996
2997impl Request {
2998	/// The absolute path that was requested.
2999	pub fn path(&self) -> &Path<'_> {
3000		&self.path
3001	}
3002
3003	/// Accept the request, resolving every awaiting requester with `broadcast`.
3004	///
3005	/// The caller keeps producing into `broadcast` (e.g. a relay proxying tracks from
3006	/// upstream); the requesters receive a consumer for it. Repeat requests for the
3007	/// path share the served broadcast for as long as it stays live.
3008	pub fn accept(self, broadcast: impl Consume<broadcast::Consumer>) {
3009		let broadcast = broadcast.consume();
3010		ServeState::resolve(&self.home, &self.path, &self.producer, Ok(broadcast));
3011		// `self.producer` drops here, closing the channel; the value is still observable.
3012	}
3013
3014	/// Reject the request, resolving every awaiting requester with `err`.
3015	pub fn reject(self, err: Error) {
3016		ServeState::resolve(&self.home, &self.path, &self.producer, Err(err));
3017	}
3018}
3019
3020impl Drop for Request {
3021	fn drop(&mut self) {
3022		// Handed off but neither accepted nor rejected: drop the still-pending entry so its
3023		// producer clone (plus this one) closes the channel, resolving coalesced requesters to
3024		// `Unroutable` rather than hanging.
3025		//
3026		// The identity guard matters: `accept`/`reject` already removed our entry and released
3027		// the lock before we run, so a concurrent request for the same path may have registered
3028		// a *new* one here. Removing unconditionally would clobber it, stranding its requesters.
3029		ServeState::forget(&self.home, &self.path, &self.producer);
3030	}
3031}
3032
3033/// The pollable result of [`Consumer::request_broadcast`].
3034///
3035/// Awaited via the [`kio::Pending`] wrapper; resolves to the [`broadcast::Consumer`]
3036/// immediately when the broadcast was already announced, or once an [`Dynamic`]
3037/// handler serves the request. Resolves to an error if the request is rejected or every
3038/// handler drops before serving it.
3039pub struct Requesting {
3040	inner: RequestState,
3041	// The path the requester asked for, relative to its cursor's root. Stamped on the
3042	// resolved broadcast (see [`broadcast::Info::path`]) because a handler is free to
3043	// serve a broadcast created somewhere else entirely, or at no path at all.
3044	path: PathOwned,
3045	// Egress scope applied to the resolved broadcast, so its reads are attributed.
3046	// Empty (no-op) for an untagged consumer.
3047	stats: stats::Scope,
3048}
3049
3050enum RequestState {
3051	// Unroutable at request time: resolves immediately with this error. Baked in so
3052	// `request_broadcast` itself stays infallible.
3053	Failed(Error),
3054	// Awaiting a handler: resolves when the request's result channel is written.
3055	Pending(kio::Consumer<PendingBroadcast>),
3056}
3057
3058impl Requesting {
3059	fn failed(error: Error) -> Self {
3060		Self::new(RequestState::Failed(error))
3061	}
3062
3063	fn queued(consumer: kio::Consumer<PendingBroadcast>) -> Self {
3064		Self::new(RequestState::Pending(consumer))
3065	}
3066
3067	/// Whether the request was handed to a serving route, rather than decided on
3068	/// the spot.
3069	///
3070	/// Fixed at request time, so it distinguishes the two ways
3071	/// [`Error::Unroutable`] arises: a queued request that fails was killed by
3072	/// its serving route retracting, and the table may already hold a
3073	/// replacement worth retrying against ([`Consumer::routed_broadcast`] does),
3074	/// while an unqueued failure means nothing could serve the path at all.
3075	pub fn is_queued(&self) -> bool {
3076		matches!(self.inner, RequestState::Pending(_))
3077	}
3078
3079	fn new(inner: RequestState) -> Self {
3080		Self {
3081			inner,
3082			path: PathOwned::default(),
3083			stats: stats::Scope::default(),
3084		}
3085	}
3086
3087	fn with_path(mut self, path: PathOwned) -> Self {
3088		self.path = path;
3089		self
3090	}
3091
3092	/// The egress scope the resolved broadcast's reads are attributed to.
3093	fn with_stats(mut self, scope: stats::Scope) -> Self {
3094		self.stats = scope;
3095		self
3096	}
3097
3098	/// Stamp a resolved broadcast with the path this cursor asked for and its egress scope.
3099	fn hand_out(&self, broadcast: broadcast::Consumer) -> broadcast::Consumer {
3100		broadcast.with_path(self.path.clone()).with_stats(self.stats.clone())
3101	}
3102
3103	/// Poll for the requested broadcast without blocking.
3104	pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<broadcast::Consumer, Error>> {
3105		match &self.inner {
3106			RequestState::Failed(error) => Poll::Ready(Err(error.clone())),
3107			RequestState::Pending(consumer) => Poll::Ready(
3108				match ready!(consumer.poll(waiter, |state| match &state.resolved {
3109					Some(result) => Poll::Ready(result.clone()),
3110					None => Poll::Pending,
3111				})) {
3112					Ok(result) => result.map(|broadcast| self.hand_out(broadcast)),
3113					// Every handler dropped without resolving: nobody could route it.
3114					Err(_closed) => Err(Error::Unroutable),
3115				},
3116			),
3117		}
3118	}
3119}
3120
3121impl kio::Pollable for Requesting {
3122	type Output = Result<broadcast::Consumer, Error>;
3123
3124	fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
3125		self.poll_ok(waiter)
3126	}
3127}
3128
3129/// Derive a read view from a handle.
3130///
3131/// Lets APIs accept either a producer or a consumer (e.g.
3132/// [`Client::with_publisher`](crate::Client::with_publisher),
3133/// [`Request::accept`]). The blanket `&T` impl means you can
3134/// pass by value (`foo(x)`) to hand off ownership, or by reference (`foo(&x)`)
3135/// to keep it, without spelling out `.consume()`.
3136pub trait Consume<T> {
3137	/// Derive a read view (a consumer) from this handle.
3138	fn consume(&self) -> T;
3139}
3140
3141impl<T, U: Consume<T>> Consume<T> for &U {
3142	fn consume(&self) -> T {
3143		(**self).consume()
3144	}
3145}
3146
3147impl Consume<Consumer> for Producer {
3148	fn consume(&self) -> Consumer {
3149		// Mirrors the inherent `Producer::consume`; inlined to avoid the
3150		// inherent-vs-trait `consume` ambiguity. Untagged: egress is tagged
3151		// separately from ingress.
3152		Consumer::from_producer(self, stats::Session::default())
3153	}
3154}
3155
3156impl Consume<Consumer> for Consumer {
3157	fn consume(&self) -> Consumer {
3158		self.clone()
3159	}
3160}
3161
3162impl Consume<broadcast::Consumer> for broadcast::Producer {
3163	fn consume(&self) -> broadcast::Consumer {
3164		// The inherent `consume` shadows this trait method, so this delegates.
3165		self.consume()
3166	}
3167}
3168
3169impl Consume<broadcast::Consumer> for broadcast::Consumer {
3170	fn consume(&self) -> broadcast::Consumer {
3171		self.clone()
3172	}
3173}
3174
3175impl Consume<track::Consumer> for track::Producer {
3176	fn consume(&self) -> track::Consumer {
3177		self.consume()
3178	}
3179}
3180
3181impl Consume<track::Consumer> for track::Consumer {
3182	fn consume(&self) -> track::Consumer {
3183		self.clone()
3184	}
3185}
3186
3187/// Cheap read handle over an origin's route table.
3188///
3189/// Clones share the underlying state without allocating any per-cursor
3190/// resources. To receive route announcements, call [`Self::announced`]; to
3191/// resolve a path into a broadcast, call [`Self::request_broadcast`].
3192#[derive(Clone)]
3193pub struct Consumer {
3194	// Identity of the origin this consumer was derived from.
3195	hop: Hop,
3196	scope: OriginScope,
3197
3198	// A prefix that is automatically stripped from all paths.
3199	root: PathOwned,
3200
3201	// The origin's shared state: the route table, announce cursors, and the
3202	// remotely-served fronts.
3203	shared: kio::Shared<OriginState>,
3204
3205	// Egress stats context. Broadcasts handed out through this consumer (and any
3206	// handle derived from them) are attributed to it (reads counted on the
3207	// publisher/egress side). Empty (no-op) unless a session tagged this handle.
3208	stats: stats::Session,
3209
3210	// Split horizon: routes whose hop chain or announcing session (`via`) is the
3211	// excluded peer are invisible to `announced` and skipped by
3212	// `request_broadcast`, so a peer is never served (or advertised) its own
3213	// content back. A local view (`Self::local`) hides peer routes the same way.
3214	horizon: Horizon,
3215
3216	// Which routes beneath a hidden (`.`-prefixed) segment `announced` reports.
3217	hidden: Hidden,
3218
3219	// The cache policy remote fronts inherit, mirroring what
3220	// `create_broadcast` gives a local front.
3221	pool: cache::Pool,
3222	cache_duration: Duration,
3223
3224	// Non-owning submission handle to the origin's [`Driver`], for the front
3225	// watcher a routed `request_broadcast` spawns. Non-owning so a lingering
3226	// read handle never keeps the driver from finishing.
3227	tasks: TasksWeak,
3228
3229	// The driver's clock and timers, threaded into fronts for the track idle
3230	// linger.
3231	timers: Clock,
3232}
3233
3234impl Consumer {
3235	fn from_producer(producer: &Producer, stats: stats::Session) -> Self {
3236		Self {
3237			hop: producer.hop,
3238			scope: producer.scope.clone(),
3239			root: producer.root.clone(),
3240			shared: producer.shared.clone(),
3241			stats,
3242			horizon: Horizon::default(),
3243			hidden: Hidden::default(),
3244			pool: producer.pool.clone(),
3245			cache_duration: producer.cache_duration,
3246			tasks: producer.tasks.downgrade(),
3247			timers: producer.timers.clone(),
3248		}
3249	}
3250
3251	/// This origin's hop identity.
3252	pub fn hop(&self) -> Hop {
3253		self.hop
3254	}
3255
3256	/// A clone that never serves the given peer its own data: routes whose hop
3257	/// chain contains `peer`, or whose announcing session is `peer`, are invisible
3258	/// and never resolved from, matching what the announce loop advertises to them.
3259	/// Sessions apply this once they learn the peer's origin id. Hop 0 identifies
3260	/// nobody, so the announcing session's assigned identity is what keeps an
3261	/// anonymous route from echoing back. Pass [`Hop::UNKNOWN`] for an anonymous
3262	/// peer.
3263	pub(crate) fn excluding(mut self, peer: Hop) -> Self {
3264		self.horizon.exclude = Some(peer);
3265		self
3266	}
3267
3268	/// A view of the routes that entered here: every route a handle marked
3269	/// [`Producer::peer`] announced is hidden from [`Self::announced`] and never
3270	/// resolved by [`Self::request_broadcast`].
3271	///
3272	/// On a relay, this is what the relay ingests itself, from clients and
3273	/// in-process producers, as opposed to what its cluster peers forward.
3274	pub fn local(mut self) -> Self {
3275		self.horizon.local = true;
3276		self
3277	}
3278
3279	/// A clone whose [`announced`](Self::announced) also reports hidden routes:
3280	/// those with a segment starting with `.` below the requested prefix.
3281	/// Hidden routes are left out by default, so a platform can add `.`-named
3282	/// broadcasts without them turning up in apps that list everything.
3283	pub fn with_hidden(mut self, hidden: bool) -> Self {
3284		self.hidden.include = hidden;
3285		self
3286	}
3287
3288	/// A clone whose [`announced`](Self::announced) reports only the routes a feed
3289	/// from `outer` hides, for a stream topping up that feed.
3290	pub(crate) fn beyond(mut self, outer: &Consumer) -> Self {
3291		self.hidden.beyond = Some(interest_prefixes(&outer.scope.allowed));
3292		self
3293	}
3294
3295	/// Whether [`announced`](Self::announced) reports hidden routes too.
3296	pub(crate) fn includes_hidden(&self) -> bool {
3297		self.hidden.include
3298	}
3299
3300	/// Attach an egress stats context: broadcasts handed out through this handle (and
3301	/// any handle derived from it) are attributed to `session` on the publisher
3302	/// (egress) side. Pass [`stats::Session::default`] to opt out.
3303	pub fn with_stats(mut self, session: stats::Session) -> Self {
3304		self.stats = session;
3305		self
3306	}
3307
3308	/// A clone of this consumer with its stats context cleared, so an internal
3309	/// lookup stream (e.g. [`Self::routed`]) doesn't drive the egress
3310	/// announce guards; the caller re-attributes the result itself.
3311	fn untagged(&self) -> Self {
3312		Self {
3313			stats: stats::Session::default(),
3314			..self.clone()
3315		}
3316	}
3317
3318	/// A view with this consumer's identity and root but no scope:
3319	/// [`announced`](Self::announced) yields nothing. Used to answer a peer's
3320	/// announce-interest for a prefix outside our scope by announcing nothing,
3321	/// rather than tearing the stream down.
3322	pub(crate) fn empty(&self) -> Self {
3323		Self {
3324			scope: OriginScope::empty(),
3325			..self.clone()
3326		}
3327	}
3328
3329	/// Subscribe to route announcements for this consumer's scope.
3330	///
3331	/// Allocates a per-cursor coalescing buffer and replays the currently
3332	/// announced routes as initial updates. Routes stay prefixes and are named
3333	/// relative to this consumer's root; its patterns only filter visibility.
3334	/// Routes with a segment starting with `.` below the literal head of those
3335	/// patterns are hidden unless [`with_hidden`](Self::with_hidden) opted in.
3336	/// Drop the returned [`AnnounceConsumer`] to unregister.
3337	pub fn announced(&self) -> AnnounceConsumer {
3338		AnnounceConsumer::new(
3339			self.root.clone(),
3340			self.scope.allowed.clone(),
3341			self.stats.clone(),
3342			self.horizon,
3343			self.hidden.clone(),
3344			&self.shared,
3345		)
3346	}
3347
3348	/// Returns a cheap duplicate of this read handle.
3349	pub fn consume(&self) -> Self {
3350		self.clone()
3351	}
3352
3353	/// The newest broadcast published on this origin at exactly `path`, if any.
3354	/// Test-only: a request goes through the table like any other.
3355	#[cfg(test)]
3356	pub(crate) fn get_broadcast(&self, path: impl AsPath) -> Option<broadcast::Consumer> {
3357		let full = self.root.join(path).to_owned();
3358		if !self.scope.permits(&full) {
3359			return None;
3360		}
3361		let table = self.shared.lock();
3362		table
3363			.routes
3364			.at(&full)
3365			.filter(|entry| entry.local)
3366			.min_by_key(|entry| route_order(&entry.prefix, entry))
3367			.and_then(|entry| entry.source.clone())
3368	}
3369
3370	/// Block until an announced route covers `path`, and return it.
3371	///
3372	/// Covering means the route's prefix is a (segment-wise) prefix of `path`,
3373	/// including the exact path itself. Returns `None` if the path is outside this
3374	/// consumer's scope or the consumer is closed first.
3375	///
3376	/// To resolve a broadcast rather than inspect the route, use
3377	/// [`Self::routed_broadcast`]: pairing this with [`Self::request_broadcast`]
3378	/// leaves a gap where the covering route can retract.
3379	pub async fn routed(&self, path: impl AsPath) -> Option<Route> {
3380		let path = path.as_path();
3381
3382		// Scope a fresh consumer down to this path's subtree, so we only wake for
3383		// announcements that overlap the requested path.
3384		// A max-depth path cannot be spelled as `path/**` (`**` would be a 33rd
3385		// segment), so watch the existing stream and match covering claims instead.
3386		let consumer = match Pattern::subtree(path.as_str()) {
3387			Ok(subtree) => self.scope("", &Patterns::from(subtree)).ok()?,
3388			Err(InvalidPattern::TooManySegments) => self.clone(),
3389			Err(_) => return None,
3390		};
3391
3392		// `scope` keeps narrower permissions intact: if we ask for `foo` on a
3393		// consumer limited to `foo/specific`, `foo` itself is unauthorized. Bail
3394		// rather than loop forever.
3395		if !consumer.allowed().matches(path.as_str()) {
3396			return None;
3397		}
3398
3399		// Use an untagged stream: this is a lookup, not egress announce
3400		// forwarding, so it must not drive the announce guards. Hiding narrows
3401		// discovery, not lookup, so a hidden path resolves like any other.
3402		let mut announced = consumer.untagged().with_hidden(true).announced();
3403		loop {
3404			let update = announced.next().await?;
3405			if update.kind.is_active() && path.has_prefix(&update.prefix) {
3406				return Some(update.route);
3407			}
3408		}
3409	}
3410
3411	/// Block until `path` resolves to a broadcast: [`Self::request_broadcast`],
3412	/// retried whenever the routes covering the path change.
3413	///
3414	/// A request answers for the routes as they stand, so it can miss an
3415	/// announcement that has not arrived yet, lose its covering route to
3416	/// failover churn, find a route that covers the path while nothing serves it
3417	/// yet (an advertise-only announce racing its handler), or be turned down by
3418	/// a handler. This rides all of that out by watching the covering routes
3419	/// and asking again each time they move, which is what makes it the right
3420	/// call for resolving a path right after connecting. Returns
3421	/// [`Error::Unauthorized`] for a path outside this consumer's scope,
3422	/// [`Error::Closed`] once the origin closes, and any other resolution
3423	/// failure as-is.
3424	pub async fn routed_broadcast(&self, path: impl AsPath) -> Result<broadcast::Consumer, Error> {
3425		let path = path.as_path();
3426
3427		// `allowed` keeps narrower permissions intact: if the whole path is not
3428		// reachable, no route can ever cover it, so bail rather than loop forever.
3429		if !self.allowed().matches(path.as_str()) {
3430			return Err(Error::Unauthorized);
3431		}
3432		loop {
3433			// `Unroutable` is a verdict of the routes covering the path as they
3434			// stood when the request was made. Re-asking the same routes would
3435			// spin, so watch them before asking and wait for them to move (a
3436			// route arriving or retracting, an identical standby swapping in, a
3437			// local broadcast announcing at the path), then try again. A change
3438			// between the ask and the wait bumps the watch first, so that retry
3439			// is immediate; the teardown pokes every watch, so a closed origin
3440			// is observed on the next pass.
3441			let (watch, seen) = {
3442				let mut table = self.shared.lock();
3443				if table.closed {
3444					return Err(Error::Closed);
3445				}
3446				let watch = table.watch(&self.shared, &self.root.join(&path));
3447				let seen = watch.seen();
3448				(watch, seen)
3449			};
3450			match self.request_broadcast(&path).await {
3451				Ok(broadcast) => return Ok(broadcast),
3452				Err(Error::Unroutable) => {
3453					kio::wait(|waiter| watch.poll_changed(waiter, seen)).await;
3454				}
3455				// Teardown parks a pending request with `Dropped`; the contract is
3456				// `Closed` once the origin is gone.
3457				Err(Error::Dropped) if self.shared.lock().closed => return Err(Error::Closed),
3458				Err(err) => return Err(err),
3459			}
3460		}
3461	}
3462
3463	/// Returns a consumer rooted at `root` and restricted to matching `patterns`.
3464	///
3465	/// `root` is relative to this consumer's root, and `patterns` are relative to
3466	/// the new root. Returns [`Error::Unauthorized`] when the requested scope has
3467	/// no overlap with this consumer's scope, or [`Error::BoundsExceeded`] when
3468	/// rooting the patterns would exceed the path limit.
3469	pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Consumer, Error> {
3470		let root = self.root.join(root).to_owned();
3471		let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
3472		let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
3473		Ok(Consumer {
3474			scope,
3475			root,
3476			..self.clone()
3477		})
3478	}
3479
3480	/// Resolve a broadcast by exact path.
3481	///
3482	/// Returns a [`kio::Pending`] future, mirroring
3483	/// [`track::Consumer::fetch_group`](track::Consumer::fetch_group). Every
3484	/// path resolves through a front the origin's [`Driver`] runs: the request
3485	/// mints one or joins the one already serving the path, and the front picks
3486	/// the best announced route covering it (the most specific prefix, then the
3487	/// cheapest, a broadcast published on this origin winning ties) and
3488	/// materializes it, from the broadcast itself or from the peer that
3489	/// announced the route. When its serving source dies or a better qualifying
3490	/// route appears, the front re-splices through the best route sharing its
3491	/// first hop at a group boundary, invisibly to subscribers. A change that
3492	/// does not preserve the first hop ends the broadcast instead, as does its
3493	/// route retracting with no replacement, and the next request re-serves the
3494	/// path. Tracks already in flight carry on to their own end.
3495	///
3496	/// The returned future fails with [`Error::Unroutable`] at once when no
3497	/// announced route covers the path, including a broadcast created on this
3498	/// origin but not announced.
3499	/// A route claims capability, not inventory: resolving a covered path
3500	/// succeeds optimistically, and a path that names nothing surfaces as
3501	/// [`Error::NotFound`] on its tracks instead.
3502	pub fn request_broadcast(&self, path: impl AsPath) -> kio::Pending<Requesting> {
3503		let path = path.as_path();
3504
3505		// Key requests by absolute path so scoped/rooted consumers and handlers
3506		// (which may have a different root) agree on the same entry, and so the egress
3507		// counters resolve against the same broadcast the ingress side wrote.
3508		let absolute = self.root.join(&path).to_owned();
3509		let scope = self.stats.egress(&absolute);
3510		// The resolved handle is named by what *this* cursor asked for, not by the absolute
3511		// path: a rooted cursor cannot name anything above its own root, so that is what a
3512		// catalog it reads may reference.
3513		let requested = path.to_owned();
3514
3515		// Routes only cover paths within this consumer's scope.
3516		if !self.scope.permits(&absolute) {
3517			return kio::Pending::new(Requesting::failed(Error::Unauthorized));
3518		}
3519
3520		let mut state = self.shared.lock();
3521
3522		// The origin's driver dropped: nothing will ever serve this.
3523		if state.closed {
3524			return kio::Pending::new(Requesting::failed(Error::Closed));
3525		}
3526
3527		// Nothing serves the path: no announced broadcast and no served route.
3528		// Checked before joining a front, so a front still draining after its
3529		// route retracted takes no newcomers.
3530		if state
3531			.best_route(&absolute.as_path(), self.horizon, Pin::Any, &HashSet::new())
3532			.is_none()
3533		{
3534			return kio::Pending::new(Requesting::failed(Error::Unroutable));
3535		}
3536
3537		// Join the live front for this path and exclusion, if any: its watcher
3538		// resolves (or already resolved) the request channel with the front's
3539		// spliced broadcast, so repeat requests share one upstream
3540		// subscription. Only while the best route still serves the front's
3541		// content, though: once a different publisher wins (a cheaper route), a
3542		// newcomer gets a fresh front from it, and the old front keeps serving the
3543		// readers it has, since other content can't be spliced into it.
3544		let key = (absolute.clone(), self.horizon);
3545		if let Some(front) = state.fronts.get(&key) {
3546			let pin = *front.pin.lock();
3547			let current = state
3548				.best_route(&absolute.as_path(), self.horizon, Pin::Any, &HashSet::new())
3549				.is_some_and(|entry| entry.qualifies(pin));
3550			if current {
3551				let pending = Requesting::queued(front.request.consume())
3552					.with_path(requested)
3553					.with_stats(scope);
3554				return kio::Pending::new(pending);
3555			}
3556			state.fronts.remove(&key);
3557		}
3558
3559		// A route covers the path: mint the front and hand its watcher the
3560		// request. The watcher materializes the path from the best covering
3561		// route, resolves the channel, and re-splices the front through
3562		// routes sharing its first hop for as long as one serves.
3563		let broadcast = broadcast::Producer::new_spliced(broadcast::Info {
3564			pool: self.pool.clone(),
3565			cache_duration: self.cache_duration,
3566			path: absolute.clone(),
3567		});
3568		let request = kio::Producer::<PendingBroadcast>::default();
3569		let consumer = request.consume();
3570		let watch = state.watch(&self.shared, &absolute);
3571		let pin = kio::Lock::new(Pin::Any);
3572		state.fronts.insert(
3573			key,
3574			RemoteFront {
3575				request: request.clone(),
3576				broadcast: broadcast.consume().weak(),
3577				pin: pin.clone(),
3578			},
3579		);
3580		// Released before the push: a set whose handles are gone drops the task,
3581		// and the `Watch` it carries unregisters under this same lock.
3582		drop(state);
3583		self.tasks.push(run_front(FrontTask {
3584			shared: self.shared.clone(),
3585			broadcast,
3586			path: absolute,
3587			horizon: self.horizon,
3588			watch,
3589			request,
3590			pin,
3591			timers: self.timers.clone(),
3592		}));
3593		kio::Pending::new(Requesting::queued(consumer).with_path(requested).with_stats(scope))
3594	}
3595
3596	/// Returns the prefix that is automatically stripped from all paths.
3597	pub fn root(&self) -> &Path<'_> {
3598		&self.root
3599	}
3600
3601	/// The patterns this consumer may reach, relative to its root.
3602	pub fn allowed(&self) -> Patterns {
3603		self.scope.relative(&self.root)
3604	}
3605
3606	/// Converts a relative path to an absolute path.
3607	pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
3608		self.root.join(path)
3609	}
3610}
3611
3612/// Receives route announcements for a scope.
3613///
3614/// Created by [`Consumer::announced`].
3615/// Drop to unregister.
3616pub struct AnnounceConsumer {
3617	id: ConsumerId,
3618	shared: kio::Shared<OriginState>,
3619	root: PathOwned,
3620
3621	// Pending updates queued for this cursor. Coalesced so a slow consumer
3622	// can't accumulate redundant announce/retract pairs.
3623	state: kio::Producer<OriginConsumerState>,
3624
3625	// Egress stats context (empty for an untagged stream). Announce events drive the
3626	// per-prefix announce guards below.
3627	stats: stats::Session,
3628
3629	// Live egress announce guards, keyed by absolute prefix. An announce
3630	// opens one (bumping `announces_started` + `announced_bytes`); the matching retraction
3631	// drops it (bumping `announces_ended` + `announced_bytes`).
3632	guards: HashMap<PathOwned, stats::Announce>,
3633
3634	// Holds the waiter a `Stream` poll registered; disjoint from `state` so the
3635	// borrow never collides with the body's.
3636	park: kio::Park,
3637}
3638
3639impl AnnounceConsumer {
3640	fn new(
3641		root: PathOwned,
3642		allowed: Patterns,
3643		stats: stats::Session,
3644		horizon: Horizon,
3645		hidden: Hidden,
3646		shared: &kio::Shared<OriginState>,
3647	) -> Self {
3648		let state = kio::Producer::<OriginConsumerState>::default();
3649		let id = ConsumerId::new();
3650
3651		{
3652			let mut table = shared.lock();
3653			if table.closed {
3654				// A cursor on a dead origin is born ended.
3655				if let Ok(mut state) = state.write() {
3656					state.ended = true;
3657				}
3658			} else {
3659				table.register_cursor(
3660					id,
3661					TableCursor {
3662						root: root.clone(),
3663						heads: interest_prefixes(&allowed),
3664						allowed,
3665						horizon,
3666						hidden,
3667						state: state.clone(),
3668						current: HashMap::new(),
3669					},
3670				);
3671			}
3672		}
3673
3674		Self {
3675			id,
3676			shared: shared.clone(),
3677			root,
3678			state,
3679			stats,
3680			guards: HashMap::new(),
3681			park: kio::Park::default(),
3682		}
3683	}
3684
3685	/// Drive the egress announce guards for one update.
3686	fn hand_out(&mut self, update: AnnounceUpdate) -> AnnounceUpdate {
3687		let absolute = self.root.join(&update.prefix).to_owned();
3688		if update.kind.is_active() {
3689			let scope = self.stats.egress(&absolute);
3690			self.guards
3691				.entry(update.prefix.clone())
3692				.or_insert_with(|| scope.announce());
3693		} else {
3694			self.guards.remove(&update.prefix);
3695		}
3696		update
3697	}
3698
3699	/// Returns the next route announcement, update, or retraction, its prefix
3700	/// relative to this cursor's root.
3701	///
3702	/// A retraction is only delivered for a previously announced prefix, and a
3703	/// repeated announcement for the same prefix is a metadata update. Returns
3704	/// None if the cursor is closed. The consumer is also a [`futures::Stream`]
3705	/// of the same updates.
3706	pub async fn next(&mut self) -> Option<AnnounceUpdate> {
3707		kio::wait(|waiter| self.poll_next(waiter)).await
3708	}
3709
3710	/// Poll for the next update, without blocking.
3711	///
3712	/// Returns `Poll::Ready(Some(_))` for an update, `Poll::Ready(None)` if the
3713	/// cursor is closed, or `Poll::Pending` after registering `waiter` to be
3714	/// notified when the next update arrives.
3715	pub fn poll_next(&mut self, waiter: &kio::Waiter) -> Poll<Option<AnnounceUpdate>> {
3716		let update = {
3717			let mut state = match ready!(self.state.poll(waiter, |state| {
3718				if state.pending.is_empty() && !state.ended {
3719					Poll::Pending
3720				} else {
3721					Poll::Ready(())
3722				}
3723			})) {
3724				Ok(state) => state,
3725				// Closed: discard the Ref so its MutexGuard doesn't escape this call.
3726				Err(_) => return Poll::Ready(None),
3727			};
3728			match state.take() {
3729				Some(update) => update,
3730				None => {
3731					// Ended by the origin's teardown, pending updates already
3732					// drained; close the channel so every closure signal agrees.
3733					state.close();
3734					return Poll::Ready(None);
3735				}
3736			}
3737		};
3738		Poll::Ready(Some(self.hand_out(update)))
3739	}
3740
3741	/// Returns the next update without blocking.
3742	///
3743	/// Returns None if there is no update available; NOT because the cursor is closed.
3744	/// Use [`Self::is_closed`] to check if the cursor is closed.
3745	pub fn try_next(&mut self) -> Option<AnnounceUpdate> {
3746		let update = self.state.write().ok()?.take()?;
3747		Some(self.hand_out(update))
3748	}
3749
3750	/// Returns true if the cursor is closed (no more updates will arrive).
3751	pub fn is_closed(&self) -> bool {
3752		let state = self.state.read();
3753		state.is_closed() || state.ended
3754	}
3755
3756	/// Returns the root that is automatically stripped from emitted prefixes.
3757	pub fn root(&self) -> &Path<'_> {
3758		&self.root
3759	}
3760
3761	/// Converts an emitted prefix back to one rooted at the origin.
3762	pub fn absolute(&self, prefix: impl AsPath) -> Path<'_> {
3763		self.root.join(prefix)
3764	}
3765}
3766
3767impl futures::Stream for AnnounceConsumer {
3768	type Item = AnnounceUpdate;
3769
3770	fn poll_next(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Option<Self::Item>> {
3771		let this = self.get_mut();
3772		let waiter = this.park.hold(cx).clone();
3773		this.poll_next(&waiter)
3774	}
3775}
3776
3777impl Drop for AnnounceConsumer {
3778	fn drop(&mut self) {
3779		let mut shared = self.shared.lock();
3780		if let Some(cursor) = shared.cursors.remove(&self.id) {
3781			for head in &cursor.heads {
3782				shared.routes.remove_cursor(head, self.id);
3783			}
3784		}
3785	}
3786}
3787
3788#[cfg(test)]
3789use futures::FutureExt;
3790
3791#[cfg(test)]
3792#[allow(missing_docs)] // test-only assertion helpers
3793impl AnnounceConsumer {
3794	/// The next update must be an active route at `expected`; returns it.
3795	pub fn assert_next_active(&mut self, expected: impl AsPath) -> Route {
3796		let expected = expected.as_path();
3797		let update = self.next().now_or_never().expect("next blocked").expect("no next");
3798		assert_eq!(update.prefix, expected, "wrong prefix");
3799		assert!(update.kind.is_active(), "should be an active route");
3800		update.route
3801	}
3802
3803	/// The `try_next` counterpart of [`Self::assert_next_active`].
3804	pub fn assert_try_next_active(&mut self, expected: impl AsPath) -> Route {
3805		let expected = expected.as_path();
3806		let update = self.try_next().expect("no next");
3807		assert_eq!(update.prefix, expected, "wrong prefix");
3808		assert!(update.kind.is_active(), "should be an active route");
3809		update.route
3810	}
3811
3812	/// The next update must be a retraction at `expected`.
3813	pub fn assert_next_ended(&mut self, expected: impl AsPath) {
3814		let expected = expected.as_path();
3815		let update = self.next().now_or_never().expect("next blocked").expect("no next");
3816		assert_eq!(update.prefix, expected, "wrong prefix");
3817		assert_eq!(update.kind, AnnounceKind::Retracted, "should be a retraction");
3818	}
3819
3820	pub fn assert_next_wait(&mut self) {
3821		if let Some(res) = self.next().now_or_never() {
3822			panic!("next should block: got {:?}", res.map(|u| u.prefix));
3823		}
3824	}
3825}
3826
3827/// Test-only construction shorthand: build the producer and spawn its driver on
3828/// the ambient tokio runtime, mirroring what `moq_tokio::origin::spawn` does
3829/// for applications.
3830#[cfg(test)]
3831pub(crate) trait ProduceTest {
3832	fn produce(self) -> Producer;
3833}
3834
3835#[cfg(test)]
3836impl ProduceTest for Config {
3837	fn produce(self) -> Producer {
3838		let (producer, driver) = Producer::new(self);
3839		if tokio::runtime::Handle::try_current().is_ok() {
3840			tokio::spawn(crate::time::run(driver));
3841		} else {
3842			// A sync test: nothing polls the driver, and dropping it would tear
3843			// the origin down, so leak it and rely on the synchronous half.
3844			std::mem::forget(driver);
3845		}
3846		producer
3847	}
3848}
3849
3850#[cfg(test)]
3851impl ProduceTest for Hop {
3852	fn produce(self) -> Producer {
3853		Config::new(self).produce()
3854	}
3855}
3856
3857#[cfg(test)]
3858mod tests {
3859	use super::*;
3860	use futures::FutureExt;
3861
3862	fn origin(id: u64) -> Hop {
3863		Hop::new(id).unwrap()
3864	}
3865
3866	fn hops(ids: &[u64]) -> Hops {
3867		let mut list = Hops::new();
3868		for &id in ids {
3869			list.push(if id == 0 { Hop::UNKNOWN } else { origin(id) }).unwrap();
3870		}
3871		list
3872	}
3873
3874	/// The scope granting these prefixes: each spelled as its subtree pattern.
3875	fn scopes(prefixes: &[&str]) -> Patterns {
3876		prefixes
3877			.iter()
3878			.map(|prefix| Pattern::subtree(prefix).unwrap())
3879			.collect()
3880	}
3881
3882	#[test]
3883	fn default_config_mints_a_real_hop() {
3884		let config = Config::default();
3885		assert_ne!(config.hop, Hop::UNKNOWN);
3886		let (producer, _driver) = Producer::new(config.clone());
3887		assert_eq!(producer.hop(), config.hop);
3888		assert_eq!(producer.consume().hop(), config.hop);
3889	}
3890
3891	#[test]
3892	fn random_hops_fit_legacy_lite_clients() {
3893		for _ in 0..32 {
3894			assert!(Hop::random().id() < 1u64 << 53);
3895		}
3896	}
3897
3898	/// Yield to the driver until `check` passes, bounded so a bug fails instead
3899	/// of hanging.
3900	async fn settle(mut check: impl FnMut() -> bool) {
3901		for _ in 0..100 {
3902			if check() {
3903				return;
3904			}
3905			tokio::task::yield_now().await;
3906		}
3907		panic!("condition never settled");
3908	}
3909
3910	/// Yield to the driver until the server's front watcher delivers a request.
3911	async fn queued(server: &Dynamic) -> Request {
3912		let mut request = None;
3913		settle(|| match server.poll_requested_broadcast(&kio::Waiter::noop()) {
3914			Poll::Ready(Ok(popped)) => {
3915				request = Some(popped);
3916				true
3917			}
3918			_ => false,
3919		})
3920		.await;
3921		request.unwrap()
3922	}
3923
3924	/// Yield to the driver until the subscription has its next group or its end.
3925	async fn next_group(subscription: &mut crate::track::Subscriber) -> Result<Option<crate::group::Consumer>, Error> {
3926		let mut next = None;
3927		settle(|| match subscription.poll_recv_group(&kio::Waiter::noop()) {
3928			Poll::Ready(result) => {
3929				next = Some(result);
3930				true
3931			}
3932			Poll::Pending => false,
3933		})
3934		.await;
3935		next.unwrap()
3936	}
3937
3938	#[tokio::test]
3939	async fn announce_and_retract() {
3940		let producer = origin(1).produce();
3941		let consumer = producer.consume();
3942		let mut announced = consumer.announced();
3943		announced.assert_next_wait();
3944
3945		let announcement = producer.announce("room/alice", Route::default()).unwrap();
3946		let route = announced.assert_next_active("room/alice");
3947		assert!(route.hops.is_empty());
3948		assert_eq!(route.cost, Cost::default());
3949		announced.assert_next_wait();
3950
3951		drop(announcement);
3952		announced.assert_next_ended("room/alice");
3953		announced.assert_next_wait();
3954	}
3955
3956	/// A `.`-prefixed segment below the requested prefix hides a route from
3957	/// discovery unless the reader opts in; one inside the prefix does not.
3958	#[tokio::test]
3959	async fn hidden_routes_need_an_opt_in() {
3960		let producer = origin(1).produce();
3961		let consumer = producer.consume();
3962		let _visible = producer.announce("room/alice", Route::default()).unwrap();
3963		let _stats = producer.announce(".stats/node", Route::default()).unwrap();
3964		let _nested = producer.announce("room/.internal", Route::default()).unwrap();
3965		// Only a leading dot hides: a suffix is part of the name.
3966		let _suffix = producer.announce("room/catalog.pro", Route::default()).unwrap();
3967
3968		let mut announced = consumer.announced();
3969		announced.assert_next_active("room/alice");
3970		announced.assert_next_active("room/catalog.pro");
3971		announced.assert_next_wait();
3972
3973		let mut announced = consumer.clone().with_hidden(true).announced();
3974		announced.assert_next_active(".stats/node");
3975		announced.assert_next_active("room/.internal");
3976		announced.assert_next_active("room/alice");
3977		announced.assert_next_active("room/catalog.pro");
3978		announced.assert_next_wait();
3979
3980		// Naming the dot segment lists what is under it, by root or by pattern.
3981		let mut announced = consumer
3982			.scope(".stats", &Patterns::from(Pattern::all()))
3983			.unwrap()
3984			.announced();
3985		announced.assert_next_active("node");
3986		announced.assert_next_wait();
3987		let mut announced = consumer.scope("", &scopes(&["room/.internal"])).unwrap().announced();
3988		announced.assert_next_active("room/.internal");
3989		announced.assert_next_wait();
3990
3991		// A top-up feed reports only what a feed from the root hid, filtered by its own
3992		// prefix and opt-in.
3993		let mut announced = consumer.clone().with_hidden(true).beyond(&consumer).announced();
3994		announced.assert_next_active(".stats/node");
3995		announced.assert_next_active("room/.internal");
3996		announced.assert_next_wait();
3997		let room = consumer.scope("", &scopes(&["room"])).unwrap().beyond(&consumer);
3998		let mut announced = room.announced();
3999		announced.assert_next_wait();
4000		let stats = consumer.scope("", &scopes(&[".stats"])).unwrap().beyond(&consumer);
4001		let mut announced = stats.announced();
4002		announced.assert_next_active(".stats/node");
4003		announced.assert_next_wait();
4004	}
4005
4006	/// Hiding narrows discovery only: an exact request resolves without an opt-in.
4007	#[tokio::test]
4008	async fn hidden_broadcast_resolves_by_path() {
4009		let producer = origin(1).produce();
4010		let consumer = producer.consume();
4011		let broadcast = producer.create_broadcast(".stats/node").unwrap();
4012		broadcast.announce(Route::default()).unwrap();
4013
4014		consumer.announced().assert_next_wait();
4015		let resolved = consumer.request_broadcast(".stats/node").await.expect("resolves");
4016		assert_eq!(resolved.info().path.as_str(), ".stats/node");
4017	}
4018
4019	/// A route that turns up later is filtered the same way as the replay.
4020	#[tokio::test]
4021	async fn hidden_route_announced_later_stays_hidden() {
4022		let producer = origin(1).produce();
4023		let consumer = producer.consume();
4024		let mut announced = consumer.announced();
4025		let mut opted = consumer.clone().with_hidden(true).announced();
4026
4027		let hidden = producer.announce(".stats/node", Route::default()).unwrap();
4028		announced.assert_next_wait();
4029		opted.assert_next_active(".stats/node");
4030
4031		drop(hidden);
4032		announced.assert_next_wait();
4033		opted.assert_next_ended(".stats/node");
4034	}
4035
4036	#[tokio::test]
4037	async fn broadcast_announces_its_own_path() {
4038		let producer = origin(1).produce();
4039		let consumer = producer.consume();
4040		let mut announced = consumer.announced();
4041		let mut peer = consumer.clone().excluding(Hop::UNKNOWN).announced();
4042
4043		// Created but not announced: invisible to local and peer cursors alike.
4044		let broadcast = producer.create_broadcast("room/alice").unwrap();
4045		announced.assert_next_wait();
4046		peer.assert_next_wait();
4047
4048		broadcast.announce(Route::default().with_cost(3)).unwrap();
4049		assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(3));
4050		assert_eq!(peer.assert_next_active("room/alice").cost, Cost::new(3));
4051
4052		// Announcing again re-prices in place.
4053		broadcast.announce(Route::default().with_cost(1)).unwrap();
4054		assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(1));
4055		assert_eq!(peer.assert_next_active("room/alice").cost, Cost::new(1));
4056
4057		// Off the air: the route retracts for everyone and the path is unroutable.
4058		broadcast.unannounce();
4059		announced.assert_next_ended("room/alice");
4060		peer.assert_next_ended("room/alice");
4061		broadcast.unannounce();
4062		announced.assert_next_wait();
4063		let err = consumer.request_broadcast("room/alice").await.err().unwrap();
4064		assert!(matches!(err, Error::Unroutable));
4065
4066		// Back on the air, then the end of the broadcast retracts for good.
4067		broadcast.announce(Route::default()).unwrap();
4068		announced.assert_next_active("room/alice");
4069		peer.assert_next_active("room/alice");
4070		broadcast.finish();
4071		announced.assert_next_ended("room/alice");
4072		peer.assert_next_ended("room/alice");
4073		assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
4074		announced.assert_next_wait();
4075	}
4076
4077	#[tokio::test]
4078	async fn broadcast_announcement_retracts_with_the_last_producer() {
4079		let producer = origin(1).produce();
4080		let consumer = producer.consume();
4081		let mut announced = consumer.announced();
4082
4083		let broadcast = producer.create_broadcast("room/alice").unwrap();
4084		let clone = broadcast.clone();
4085		broadcast.announce(Route::default()).unwrap();
4086		announced.assert_next_active("room/alice");
4087
4088		// A clone keeps the broadcast, and its advertisement, alive.
4089		drop(broadcast);
4090		announced.assert_next_wait();
4091		drop(clone);
4092		announced.assert_next_ended("room/alice");
4093	}
4094
4095	#[tokio::test]
4096	async fn publish_creates_and_announces_together() {
4097		let producer = origin(1).produce();
4098		let mut announced = producer.consume().announced();
4099		let _broadcast = producer.publish("room/alice", Route::default()).unwrap();
4100		announced.assert_next_active("room/alice");
4101	}
4102
4103	#[tokio::test]
4104	async fn standalone_broadcast_cannot_announce() {
4105		let broadcast = broadcast::Info::new().produce();
4106		assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
4107		// Harmless without an advertisement to retract.
4108		broadcast.unannounce();
4109	}
4110
4111	#[tokio::test]
4112	async fn announce_replays_to_late_cursor() {
4113		let producer = origin(1).produce();
4114		let _a = producer.announce("room/alice", Route::default()).unwrap();
4115		let _b = producer.announce("room/bob", Route::default()).unwrap();
4116
4117		let mut announced = producer.consume().announced();
4118		// BTreeMap order: lexicographic by prefix.
4119		announced.assert_next_active("room/alice");
4120		announced.assert_next_active("room/bob");
4121		announced.assert_next_wait();
4122	}
4123
4124	#[tokio::test]
4125	async fn announce_keeps_its_prefix_under_a_producer_scope() {
4126		let producer = origin(1).produce();
4127		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
4128
4129		// Prefix advertisements stay prefixes. The scope filters requests locally.
4130		let _a = scoped.announce("", Route::default()).unwrap();
4131		let mut announced = producer.consume().announced();
4132		announced.assert_next_active("");
4133
4134		// Disjoint prefixes cannot be claimed at all.
4135		assert!(matches!(
4136			scoped.announce("other", Route::default()),
4137			Err(Error::Unauthorized)
4138		));
4139	}
4140
4141	#[tokio::test]
4142	async fn cursor_keeps_an_overlapping_prefix_above_its_scope() {
4143		let producer = origin(1).produce();
4144		let _a = producer.announce("", Route::default()).unwrap();
4145
4146		let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
4147		let mut announced = consumer.announced();
4148		announced.assert_next_active("");
4149	}
4150
4151	#[tokio::test]
4152	async fn cursor_root_strips_prefix() {
4153		let producer = origin(1).produce();
4154		let _a = producer.announce("room/alice", Route::default()).unwrap();
4155
4156		let consumer = producer
4157			.consume()
4158			.scope("room", &Patterns::from(Pattern::all()))
4159			.unwrap();
4160		let mut announced = consumer.announced();
4161		announced.assert_next_active("alice");
4162	}
4163
4164	#[tokio::test]
4165	async fn best_route_wins_and_fails_over() {
4166		let producer = origin(1).produce();
4167		let mut announced = producer.consume().announced();
4168
4169		let expensive = producer
4170			.announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4171			.unwrap();
4172		let route = announced.assert_next_active("room");
4173		assert_eq!(route.cost, Cost::new(5));
4174
4175		// A cheaper route for the same prefix takes over in place.
4176		let cheap = producer
4177			.announce("room", Route::default().with_hops(hops(&[20])).with_cost(1))
4178			.unwrap();
4179		let route = announced.assert_next_active("room");
4180		assert_eq!(route.cost, Cost::new(1));
4181
4182		// Losing the winner falls back to the survivor, still in place.
4183		drop(cheap);
4184		let route = announced.assert_next_active("room");
4185		assert_eq!(route.cost, Cost::new(5));
4186
4187		// Losing the last retracts.
4188		drop(expensive);
4189		announced.assert_next_ended("room");
4190	}
4191
4192	#[tokio::test]
4193	async fn identical_reannounce_is_invisible() {
4194		let producer = origin(1).produce();
4195		let mut announced = producer.consume().announced();
4196
4197		let old = producer
4198			.announce("room", Route::default().with_hops(hops(&[10])))
4199			.unwrap();
4200		let first = announced.assert_next_active("room");
4201		assert_eq!(first.hops.as_slice(), hops(&[10]).as_slice());
4202
4203		// An identical route from a fresh announcement (a reconnect) changes
4204		// nothing a consumer could act on, so nothing is delivered; new requests
4205		// still prefer the newest entry.
4206		let _new = producer
4207			.announce("room", Route::default().with_hops(hops(&[10])))
4208			.unwrap();
4209		announced.assert_next_wait();
4210
4211		// Retracting the stale twin leaves the fresh one standing, still quietly.
4212		drop(old);
4213		announced.assert_next_wait();
4214	}
4215
4216	#[tokio::test]
4217	async fn exclude_hides_routes_through_the_peer() {
4218		let producer = origin(1).produce();
4219		let _a = producer
4220			.announce("room", Route::default().with_hops(hops(&[7])))
4221			.unwrap();
4222
4223		let mut hidden = producer.consume().excluding(origin(7)).announced();
4224		hidden.assert_next_wait();
4225
4226		let mut visible = producer.consume().excluding(origin(8)).announced();
4227		visible.assert_next_active("room");
4228	}
4229
4230	#[tokio::test]
4231	async fn exclude_matches_via_when_the_chain_is_anonymous() {
4232		let producer = origin(1).produce();
4233		let assigned = origin(777);
4234		let _echoed = producer
4235			.announce("echoed", Route::default().with_hops(hops(&[0])).with_via(assigned))
4236			.unwrap();
4237		let _local = producer
4238			.announce("local", Route::default().with_hops(hops(&[10])))
4239			.unwrap();
4240
4241		let mut hidden = producer.consume().excluding(assigned).announced();
4242		hidden.assert_next_active("local");
4243		hidden.assert_next_wait();
4244	}
4245
4246	#[tokio::test]
4247	async fn anonymous_route_loses_to_identified_at_any_cost() {
4248		let producer = origin(1).produce();
4249		let mut announced = producer.consume().announced();
4250
4251		let _anonymous = producer
4252			.announce("room", Route::default().with_hops(hops(&[0])).with_cost(1))
4253			.unwrap();
4254		let route = announced.assert_next_active("room");
4255		assert!(route.is_anonymous());
4256		assert_eq!(route.cost, Cost::new(1));
4257
4258		let _identified = producer
4259			.announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4260			.unwrap();
4261		let route = announced.assert_next_active("room");
4262		assert!(!route.is_anonymous());
4263		assert_eq!(route.cost, Cost::new(5));
4264	}
4265
4266	#[tokio::test]
4267	async fn anonymous_routes_order_by_cost() {
4268		let producer = origin(1).produce();
4269		let mut announced = producer.consume().announced();
4270
4271		let expensive = producer
4272			.announce("room", Route::default().with_hops(hops(&[0])).with_cost(5))
4273			.unwrap();
4274		let route = announced.assert_next_active("room");
4275		assert_eq!(route.cost, Cost::new(5));
4276
4277		let _cheap = producer
4278			.announce("room", Route::default().with_hops(hops(&[0, 7])).with_cost(1))
4279			.unwrap();
4280		let route = announced.assert_next_active("room");
4281		assert!(route.is_anonymous());
4282		assert_eq!(route.cost, Cost::new(1));
4283
4284		drop(expensive);
4285		announced.assert_next_wait();
4286	}
4287
4288	#[tokio::test]
4289	async fn anonymous_chain_from_identified_peer_still_ranks_last() {
4290		let producer = origin(1).produce();
4291		let mut announced = producer.consume().announced();
4292
4293		let _anonymous = producer
4294			.announce(
4295				"room",
4296				Route::default()
4297					.with_hops(hops(&[0, 7]))
4298					.with_cost(1)
4299					.with_via(origin(7)),
4300			)
4301			.unwrap();
4302		announced.assert_next_active("room");
4303
4304		let _identified = producer
4305			.announce("room", Route::default().with_hops(hops(&[10, 20])).with_cost(5))
4306			.unwrap();
4307		let route = announced.assert_next_active("room");
4308		assert!(!route.is_anonymous());
4309		assert_eq!(route.cost, Cost::new(5));
4310	}
4311
4312	#[tokio::test]
4313	async fn request_prefers_identified_over_cheaper_anonymous() {
4314		let producer = origin(1).produce();
4315		let consumer = producer.consume();
4316
4317		let anonymous = producer
4318			.dynamic("room", Route::default().with_hops(hops(&[0])).with_cost(1))
4319			.unwrap();
4320		let identified = producer
4321			.dynamic("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4322			.unwrap();
4323
4324		let _pending = consumer.request_broadcast("room/alice");
4325		let request = queued(&identified).await;
4326		assert_eq!(request.path().as_str(), "room/alice");
4327		assert!(
4328			anonymous.poll_requested_broadcast(&kio::Waiter::noop()).is_pending(),
4329			"the cheaper anonymous route must not serve"
4330		);
4331	}
4332
4333	#[tokio::test]
4334	async fn update_reprices_in_place() {
4335		let producer = origin(1).produce();
4336		let mut announced = producer.consume().announced();
4337
4338		let announcement = producer.announce("room", Route::default()).unwrap();
4339		announced.assert_next_active("room");
4340
4341		announcement.update(Route::default().with_cost(9)).unwrap();
4342		let route = announced.assert_next_active("room");
4343		assert_eq!(route.cost, Cost::new(9));
4344	}
4345
4346	#[tokio::test]
4347	async fn retract_after_undelivered_reprice_still_delivered() {
4348		let producer = origin(1).produce();
4349		let mut announced = producer.consume().announced();
4350
4351		let announcement = producer.announce("room", Route::default()).unwrap();
4352		announced.assert_next_active("room");
4353
4354		// Reprice, then retract before the consumer observes the reprice: the
4355		// pending metadata update must not cancel the retraction the delivered
4356		// announce still owes.
4357		announcement.update(Route::default().with_cost(9)).unwrap();
4358		drop(announcement);
4359		announced.assert_next_ended("room");
4360		announced.assert_next_wait();
4361	}
4362
4363	#[tokio::test]
4364	async fn scoped_cursor_advertises_most_specific_covering_route() {
4365		let producer = origin(1).produce();
4366		// Broad and cheap; narrow and expensive. Both present relative to a cursor
4367		// rooted below them, and the narrow one is what a request there resolves.
4368		let _broad = producer.announce("room", Route::default().with_cost(1)).unwrap();
4369		let _narrow = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
4370
4371		let consumer = producer
4372			.consume()
4373			.scope("room/alice", &Patterns::from(Pattern::all()))
4374			.unwrap();
4375		let mut announced = consumer.announced();
4376		let route = announced.assert_next_active("");
4377		assert_eq!(route.cost, Cost::new(9));
4378		announced.assert_next_wait();
4379	}
4380
4381	#[tokio::test]
4382	async fn capture_change_retracts_before_reannouncing_a_presented_prefix() {
4383		let producer = origin(1).produce();
4384		let _broad = producer.announce("room", Route::default()).unwrap();
4385		let exact = producer.announce("room/alice", Route::default()).unwrap();
4386		let consumer = producer
4387			.consume()
4388			.scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
4389			.unwrap()
4390			.scope("room/alice", &Patterns::from(Pattern::all()))
4391			.unwrap();
4392		let mut announced = consumer.announced();
4393
4394		let first = announced.next().now_or_never().expect("next").expect("announce");
4395		assert_eq!(first.prefix.as_str(), "");
4396		assert_eq!(first.kind, AnnounceKind::Announced);
4397		assert_eq!(first.captures, Some(Vec::new()));
4398
4399		drop(exact);
4400		let retracted = announced.next().now_or_never().expect("next").expect("retract");
4401		assert_eq!(retracted.prefix.as_str(), "");
4402		assert_eq!(retracted.kind, AnnounceKind::Retracted);
4403		assert_eq!(retracted.captures, Some(Vec::new()));
4404		let replacement = announced.next().now_or_never().expect("next").expect("announce");
4405		assert_eq!(replacement.prefix.as_str(), "");
4406		assert_eq!(replacement.kind, AnnounceKind::Announced);
4407		assert_eq!(replacement.captures, None);
4408	}
4409
4410	#[tokio::test]
4411	async fn routed_broadcast_resolves_once_announced() {
4412		let producer = origin(1).produce();
4413		let consumer = producer.consume();
4414
4415		// Asking before anything is announced parks instead of failing Unroutable.
4416		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4417		assert!((&mut resolving).now_or_never().is_none());
4418
4419		// Creating is not announcing: still parked.
4420		let broadcast = producer.create_broadcast("room/alice").unwrap();
4421		for _ in 0..20 {
4422			tokio::task::yield_now().await;
4423		}
4424		assert!((&mut resolving).now_or_never().is_none());
4425
4426		broadcast.announce(Route::default()).unwrap();
4427		let resolved = resolving.await.expect("resolves once announced");
4428		assert_eq!(resolved.info().path.as_str(), "room/alice");
4429		drop(broadcast);
4430	}
4431
4432	/// A local broadcast competes on its announced cost: a cheaper route at the
4433	/// same path wins, for cursors and requests alike.
4434	#[tokio::test]
4435	async fn cheaper_remote_route_beats_a_local_broadcast() {
4436		let producer = origin(1).produce();
4437		let consumer = producer.consume();
4438		let mut announced = consumer.announced();
4439
4440		let _local = producer.publish("room/alice", Route::default().with_cost(5)).unwrap();
4441		assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(5));
4442
4443		let server = producer
4444			.dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(1))
4445			.unwrap();
4446		let route = announced.assert_next_active("room/alice");
4447		assert_eq!(route.cost, Cost::new(1));
4448		assert_eq!(route.hops, hops(&[10]));
4449
4450		// The request goes upstream rather than to the local broadcast.
4451		let pending = consumer.request_broadcast("room/alice");
4452		let request = queued(&server).await;
4453		let upstream = broadcast::Info::new().produce();
4454		request.accept(&upstream);
4455		pending.await.expect("resolves through the cheaper route");
4456	}
4457
4458	/// A cheaper route that appears after a front was minted wins new requests too:
4459	/// the cached front serves other content, so a newcomer gets a fresh front from
4460	/// the winner, while the old front keeps serving the readers it already has.
4461	#[tokio::test]
4462	async fn cheaper_route_after_a_front_wins_new_requests() {
4463		let producer = origin(1).produce();
4464		let consumer = producer.consume();
4465
4466		let _local = producer.publish("room/alice", Route::default().with_cost(5)).unwrap();
4467		let first = consumer
4468			.request_broadcast("room/alice")
4469			.await
4470			.expect("resolves locally");
4471
4472		let server = producer
4473			.dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(1))
4474			.unwrap();
4475
4476		let pending = consumer.request_broadcast("room/alice");
4477		let request = queued(&server).await;
4478		let upstream = broadcast::Info::new().produce();
4479		request.accept(&upstream);
4480		let second = pending.await.expect("resolves through the cheaper route");
4481
4482		assert!(!first.is_closed(), "the old front must keep serving its readers");
4483		assert!(!first.is_clone(&second), "the newcomer must not join the old front");
4484	}
4485
4486	/// At equal cost the local broadcast wins even over a route with no hops of its
4487	/// own, such as a later claim on this origin: locality is the tie-break after
4488	/// cost, not the newest entry.
4489	#[tokio::test]
4490	async fn local_broadcast_wins_a_tie_with_a_hopless_route() {
4491		let producer = origin(1).produce();
4492		let consumer = producer.consume();
4493
4494		let _local = producer.publish("room/alice", Route::default()).unwrap();
4495		let server = producer.dynamic("room/alice", Route::default()).unwrap();
4496
4497		// Were the claim to win, the request would park on its handler forever.
4498		let resolved = tokio::time::timeout(Duration::from_secs(1), consumer.request_broadcast("room/alice"))
4499			.await
4500			.expect("the newer hopless route won the tie")
4501			.expect("resolves");
4502		assert_eq!(resolved.info().path.as_str(), "room/alice");
4503		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4504	}
4505
4506	/// Ingress announce stats count advertised intervals, not the broadcast's
4507	/// lifetime: nothing while hidden, one per announce, none for a re-price.
4508	#[tokio::test]
4509	async fn announce_stats_follow_the_advertisement() {
4510		let registry = stats::Registry::new(stats::Config::new());
4511		let producer = origin(1)
4512			.produce()
4513			.with_stats(registry.tier(stats::Tier::default()).session("root"));
4514		let announces = || {
4515			registry
4516				.snapshot()
4517				.traffic()
4518				.into_iter()
4519				.find(|(_, role, _)| *role == stats::Role::Subscriber)
4520				.map(|(_, _, traffic)| (traffic.announces_started, traffic.announces_ended))
4521				.unwrap_or_default()
4522		};
4523
4524		let broadcast = producer.create_broadcast("room/alice").unwrap();
4525		assert_eq!(announces(), (0, 0), "a hidden broadcast is not announced");
4526		broadcast.announce(Route::default()).unwrap();
4527		broadcast
4528			.announce(Route {
4529				cost: Cost::new(3),
4530				..Route::default()
4531			})
4532			.unwrap();
4533		assert_eq!(announces(), (1, 0), "a re-price is not another announce");
4534		broadcast.unannounce();
4535		assert_eq!(announces(), (1, 1));
4536		broadcast.announce(Route::default()).unwrap();
4537		drop(broadcast);
4538		assert_eq!(announces(), (2, 2));
4539	}
4540
4541	/// At equal cost the local broadcast wins.
4542	#[tokio::test]
4543	async fn local_broadcast_wins_a_cost_tie() {
4544		let producer = origin(1).produce();
4545		let consumer = producer.consume();
4546		let mut announced = consumer.announced();
4547
4548		let server = producer
4549			.dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(2))
4550			.unwrap();
4551		announced.assert_next_active("room/alice");
4552		let _local = producer.publish("room/alice", Route::default().with_cost(2)).unwrap();
4553		assert!(announced.assert_next_active("room/alice").hops.is_empty());
4554
4555		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4556		assert_eq!(resolved.info().path.as_str(), "room/alice");
4557		for _ in 0..20 {
4558			tokio::task::yield_now().await;
4559		}
4560		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4561	}
4562
4563	/// Unannouncing ends the front the origin served from the broadcast and
4564	/// refuses new requests at once, even before the front acts on it.
4565	#[tokio::test]
4566	async fn unannounce_ends_the_front() {
4567		let producer = origin(1).produce();
4568		let consumer = producer.consume();
4569
4570		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4571		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4572
4573		broadcast.unannounce();
4574		let err = consumer.request_broadcast("room/alice").await.err().unwrap();
4575		assert!(matches!(err, Error::Unroutable), "joined a retracted front: {err}");
4576		settle(|| resolved.is_closed()).await;
4577		assert!(!broadcast.consume().is_closed(), "the broadcast itself lives on");
4578
4579		// Announcing again serves a fresh front.
4580		broadcast.announce(Route::default()).unwrap();
4581		let again = consumer.request_broadcast("room/alice").await.expect("resolves again");
4582		assert!(!again.is_clone(&resolved));
4583	}
4584
4585	/// A subscriber still waiting on the source's track info is in flight too:
4586	/// unannouncing leaves it on the copy it asked for, which the source can
4587	/// still answer and finish.
4588	#[tokio::test]
4589	async fn unannounce_keeps_a_track_awaiting_its_info() {
4590		let producer = origin(1).produce();
4591		let consumer = producer.consume();
4592
4593		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4594		let mut dynamic = broadcast.dynamic();
4595		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4596		let track = resolved.track("video").unwrap();
4597		let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4598		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4599			.await
4600			.expect("the front asked the source")
4601			.expect("request");
4602
4603		broadcast.unannounce();
4604		settle(|| resolved.is_closed()).await;
4605
4606		let source = request.accept(None);
4607		let mut group = source.append_group().unwrap();
4608		group.write_frame(crate::Timestamp::ZERO, b"late".as_ref()).unwrap();
4609		group.finish().unwrap();
4610		source.finish().unwrap();
4611
4612		let mut subscription = subscribing.await.unwrap().expect("subscribe survives the retraction");
4613		let mut group = subscription.recv_group().await.unwrap().expect("the source's group");
4614		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"late");
4615		assert!(matches!(subscription.recv_group().await, Ok(None)), "ends cleanly");
4616	}
4617
4618	/// The same holds for a reader returning to a parked track: its warm cache
4619	/// does not stand in for the copy it is waiting on.
4620	#[tokio::test]
4621	async fn unannounce_keeps_a_returning_reader_awaiting_its_info() {
4622		let producer = origin(1).produce();
4623		let consumer = producer.consume();
4624
4625		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4626		let mut dynamic = broadcast.dynamic();
4627		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4628		let track = resolved.track("video").unwrap();
4629		let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4630		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4631			.await
4632			.expect("the front asked the source")
4633			.expect("request");
4634		let source = request.accept(None);
4635		let mut group = source.append_group().unwrap();
4636		group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
4637		group.finish().unwrap();
4638		let mut subscription = subscribing.await.unwrap().expect("subscribe");
4639		subscription.recv_group().await.unwrap().expect("the cached group");
4640		drop(subscription);
4641
4642		// Parked: the source copy goes, the delivered group stays warm. The source
4643		// then tears its idle track down, so a returning reader asks it afresh.
4644		tokio::time::timeout(Duration::from_secs(1), source.unused())
4645			.await
4646			.expect("parked")
4647			.expect("source open");
4648		drop(source);
4649		let track = resolved.track("video").unwrap();
4650		let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4651		let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4652			.await
4653			.expect("the front asked the source again")
4654			.expect("request");
4655
4656		broadcast.unannounce();
4657		settle(|| resolved.is_closed()).await;
4658
4659		// A fresh source copy numbers groups past what the cache already delivered.
4660		let source = request.accept(None);
4661		let mut group = source.create_group(1u64.into()).unwrap();
4662		group.write_frame(crate::Timestamp::ZERO, b"late".as_ref()).unwrap();
4663		group.finish().unwrap();
4664		source.finish().unwrap();
4665
4666		let mut subscription = subscribing.await.unwrap().expect("subscribe survives the retraction");
4667		let mut payloads = Vec::new();
4668		while let Some(mut group) = subscription.recv_group().await.expect("ends cleanly") {
4669			payloads.push(group.read_frame().await.unwrap().unwrap().payload);
4670		}
4671		assert_eq!(payloads.last().map(|p| &p[..]), Some(&b"late"[..]));
4672	}
4673
4674	/// A re-announce that lands before the front acts on the retraction reuses
4675	/// the same route entry, so the front carries on.
4676	#[tokio::test]
4677	async fn reannounce_before_the_front_acts_keeps_it() {
4678		let producer = origin(1).produce();
4679		let consumer = producer.consume();
4680
4681		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4682		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4683
4684		broadcast.unannounce();
4685		broadcast.announce(Route::default()).unwrap();
4686		for _ in 0..20 {
4687			tokio::task::yield_now().await;
4688		}
4689		assert!(!resolved.is_closed(), "the front ended across a reannouncement");
4690		let again = consumer.request_broadcast("room/alice").await.expect("resolves");
4691		assert!(again.is_clone(&resolved));
4692	}
4693
4694	#[tokio::test]
4695	async fn local_broadcast_resolves_once_announced() {
4696		let producer = origin(1).produce();
4697		let consumer = producer.consume();
4698
4699		// Created but not announced: nobody can reach it, locally included.
4700		let broadcast = producer.create_broadcast("room/alice").unwrap();
4701		let err = consumer
4702			.request_broadcast("room/alice")
4703			.now_or_never()
4704			.expect("unroutable is synchronous")
4705			.err()
4706			.unwrap();
4707		assert!(matches!(err, Error::Unroutable));
4708
4709		broadcast.announce(Route::default()).unwrap();
4710		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4711		assert_eq!(resolved.info().path.as_str(), "room/alice");
4712		drop(broadcast);
4713
4714		// Nothing covers an unknown path and no handler exists.
4715		let err = consumer
4716			.request_broadcast("room/bob")
4717			.now_or_never()
4718			.expect("unroutable is synchronous")
4719			.err()
4720			.unwrap();
4721		assert!(matches!(err, Error::Unroutable));
4722	}
4723
4724	#[test]
4725	fn create_broadcast_accepts_a_max_depth_path() {
4726		let producer = origin(1).produce();
4727		let path = vec!["a"; Path::MAX_PARTS].join("/");
4728		let _broadcast = producer.create_broadcast(path.as_str()).expect("max depth is allowed");
4729		let deeper = vec!["a"; Path::MAX_PARTS + 1].join("/");
4730		assert!(matches!(
4731			producer.create_broadcast(deeper.as_str()),
4732			Err(Error::BoundsExceeded(_))
4733		));
4734	}
4735
4736	#[tokio::test]
4737	async fn duplicate_routes_aggregate_until_the_last_leaves() {
4738		let producer = origin(1).produce();
4739		let first = producer.dynamic("live", Route::default().with_cost(3)).unwrap();
4740		let second = producer.dynamic("live", Route::default().with_cost(1)).unwrap();
4741
4742		let mut announced = producer.consume().announced();
4743		let update = announced.next().now_or_never().expect("next").expect("no next");
4744		assert_eq!(update.prefix.as_str(), "live");
4745		assert_eq!(update.kind, AnnounceKind::Announced);
4746		assert_eq!(update.route.cost, Cost::new(1));
4747		announced.assert_next_wait();
4748
4749		drop(second);
4750		let update = announced.next().now_or_never().expect("next").expect("no next");
4751		assert_eq!(update.prefix.as_str(), "live");
4752		assert_eq!(update.kind, AnnounceKind::Updated);
4753		assert_eq!(update.route.cost, Cost::new(3));
4754
4755		drop(first);
4756		announced.assert_next_ended("live");
4757		announced.assert_next_wait();
4758	}
4759
4760	#[test]
4761	fn dynamic_may_cover_a_scope_but_disjoint_prefixes_are_refused() {
4762		let producer = origin(1).produce();
4763		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
4764		let _broad = scoped
4765			.dynamic("", Route::default())
4766			.expect("an overlapping prefix is accepted");
4767
4768		let _ok = scoped
4769			.dynamic("room/alice", Route::default())
4770			.expect("a contained prefix is accepted");
4771		assert!(matches!(
4772			scoped.dynamic("other", Route::default()),
4773			Err(Error::Unauthorized)
4774		));
4775	}
4776
4777	#[tokio::test]
4778	async fn dynamic_route_keeps_its_producer_scope() {
4779		let producer = origin(1).produce();
4780		let scope = Patterns::from("*/chat".parse::<Pattern>().unwrap());
4781		let scoped = producer.scope("", &scope).unwrap();
4782		let dynamic = scoped.dynamic("", Route::default()).unwrap();
4783
4784		let mut matching = producer
4785			.consume()
4786			.scope("", &scopes(&["room/chat"]))
4787			.unwrap()
4788			.announced();
4789		matching.assert_next_active("");
4790		let mut outside = producer
4791			.consume()
4792			.scope("", &scopes(&["room/video"]))
4793			.unwrap()
4794			.announced();
4795		outside.assert_next_wait();
4796
4797		let refused = producer
4798			.consume()
4799			.request_broadcast("room/video")
4800			.now_or_never()
4801			.expect("an out-of-scope request must be refused synchronously");
4802		assert!(matches!(refused, Err(Error::Unroutable)));
4803		assert!(dynamic.requested_broadcast().now_or_never().is_none());
4804
4805		let _pending = producer.consume().request_broadcast("room/chat");
4806		let request = queued(&dynamic).await;
4807		assert_eq!(request.path().as_str(), "room/chat");
4808	}
4809
4810	#[tokio::test]
4811	async fn dynamic_accepts_a_max_depth_prefix() {
4812		let producer = origin(1).produce();
4813		let path = (0..Path::MAX_PARTS)
4814			.map(|i| format!("s{i}"))
4815			.collect::<Vec<_>>()
4816			.join("/");
4817		let mut announced = producer.consume().announced();
4818
4819		let dynamic = producer.dynamic(&path, Route::default()).expect("max depth is allowed");
4820		announced.assert_next_active(&path);
4821
4822		let _pending = producer.consume().request_broadcast(&path);
4823		let request = queued(&dynamic).await;
4824		assert_eq!(request.path().as_str(), path);
4825	}
4826
4827	#[tokio::test]
4828	async fn dynamic_exclusion_skips_routes_through_the_subscriber() {
4829		let producer = origin(1).produce();
4830		let _server = producer
4831			.dynamic("live", Route::default().with_hops(hops(&[7])))
4832			.unwrap();
4833
4834		let mut excluded = producer.consume().excluding(origin(7)).announced();
4835		excluded.assert_next_wait();
4836
4837		let mut clean = producer.consume().excluding(origin(8)).announced();
4838		clean.assert_next_active("live");
4839	}
4840
4841	/// The consumer is a `Stream` of the same updates as `next`.
4842	#[tokio::test]
4843	async fn announce_consumer_is_a_stream() {
4844		use futures::StreamExt;
4845		let producer = origin(1).produce();
4846		let server = producer.dynamic("live", Route::default()).unwrap();
4847		let mut announced = producer.consume().announced();
4848		let update = StreamExt::next(&mut announced)
4849			.now_or_never()
4850			.expect("next")
4851			.expect("no next");
4852		assert_eq!(update.prefix.as_str(), "live");
4853		assert_eq!(update.kind, AnnounceKind::Announced);
4854		assert!(StreamExt::next(&mut announced).now_or_never().is_none());
4855		drop(server);
4856		let update = StreamExt::next(&mut announced)
4857			.now_or_never()
4858			.expect("next")
4859			.expect("no next");
4860		assert_eq!(update.kind, AnnounceKind::Retracted);
4861	}
4862
4863	#[tokio::test]
4864	async fn dynamic_retracts() {
4865		let producer = origin(1).produce();
4866		let server = producer.dynamic("live", Route::default()).unwrap();
4867		let mut announced = producer.consume().announced();
4868		announced.assert_next_active("live");
4869
4870		drop(server);
4871		announced.assert_next_ended("live");
4872	}
4873
4874	#[test]
4875	fn charged_wildcard_cost_accumulates_across_hops() {
4876		let first = Cost::new(4).charged(1);
4877		let second = first.charged(2);
4878		assert_eq!(second, Cost { warm: 7, cold: 7 });
4879	}
4880
4881	#[tokio::test]
4882	async fn local_broadcast_is_invisible_until_announced() {
4883		let producer = origin(1).produce();
4884		let mut local = producer.consume().announced();
4885		let mut peer = producer.consume().excluding(Hop::UNKNOWN).announced();
4886		let broadcast = producer.create_broadcast("room/alice").unwrap();
4887		local.assert_next_wait();
4888		peer.assert_next_wait();
4889
4890		broadcast.announce(Route::default()).unwrap();
4891		local.assert_next_active("room/alice");
4892		peer.assert_next_active("room/alice");
4893
4894		drop(broadcast);
4895		local.assert_next_ended("room/alice");
4896		peer.assert_next_ended("room/alice");
4897	}
4898
4899	#[tokio::test]
4900	async fn served_route_materializes_on_demand() {
4901		let producer = origin(1).produce();
4902		let consumer = producer.consume();
4903
4904		let server = producer.dynamic("room", Route::default()).unwrap();
4905
4906		let pending = consumer.request_broadcast("room/alice");
4907		let request = queued(&server).await;
4908		assert_eq!(request.path().as_str(), "room/alice");
4909
4910		let source = broadcast::Info::new().produce();
4911		request.accept(&source);
4912
4913		let resolved = pending.await.expect("resolves");
4914		// The handle is named by what the requester asked for.
4915		assert_eq!(resolved.info().path.as_str(), "room/alice");
4916
4917		// A repeat request shares the served broadcast instead of re-asking.
4918		let again = consumer.request_broadcast("room/alice").await.expect("resolves");
4919		assert!(again.is_clone(&resolved));
4920	}
4921
4922	#[tokio::test]
4923	async fn served_requests_coalesce() {
4924		let producer = origin(1).produce();
4925		let consumer = producer.consume();
4926		let server = producer.dynamic("room", Route::default()).unwrap();
4927
4928		let first = consumer.request_broadcast("room/alice");
4929		let second = consumer.request_broadcast("room/alice");
4930
4931		let request = queued(&server).await;
4932		// Only one request reaches the server.
4933		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4934
4935		let source = broadcast::Info::new().produce();
4936		request.accept(&source);
4937
4938		let first = first.await.expect("resolves");
4939		let second = second.await.expect("resolves");
4940		assert!(first.is_clone(&second));
4941	}
4942
4943	#[tokio::test]
4944	async fn retract_rejects_pending_requests() {
4945		let producer = origin(1).produce();
4946		let consumer = producer.consume();
4947		let server = producer.dynamic("room", Route::default()).unwrap();
4948
4949		let pending = consumer.request_broadcast("room/alice");
4950		drop(server);
4951
4952		let err = pending.await.err().unwrap();
4953		assert!(matches!(err, Error::Unroutable));
4954
4955		// With the route gone, later requests are unroutable immediately.
4956		let err = consumer
4957			.request_broadcast("room/alice")
4958			.now_or_never()
4959			.expect("unroutable")
4960			.err()
4961			.unwrap();
4962		assert!(matches!(err, Error::Unroutable));
4963	}
4964
4965	#[tokio::test]
4966	async fn routed_broadcast_survives_serving_route_retraction() {
4967		let producer = origin(1).produce();
4968		let consumer = producer.consume();
4969
4970		// Three identical routes, oldest first: the newest identical route wins
4971		// requests, and swapping between them emits no announce update.
4972		let standby_server = producer.dynamic("room", Route::default()).unwrap();
4973		let second_server = producer.dynamic("room", Route::default()).unwrap();
4974		let incumbent_server = producer.dynamic("room", Route::default()).unwrap();
4975
4976		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4977		assert!((&mut resolving).now_or_never().is_none());
4978
4979		// Each incumbent dies with the front's request in flight on it: the
4980		// front's watcher observes the retraction and retries through the next
4981		// standby instead of parking on an announce update that never comes. Two
4982		// retractions in a row, so the announce stream's initial coverage replay
4983		// cannot paper over the missing retry.
4984		drop(incumbent_server);
4985		assert!((&mut resolving).now_or_never().is_none());
4986		drop(second_server);
4987		assert!((&mut resolving).now_or_never().is_none());
4988
4989		let request = queued(&standby_server).await;
4990		let source = broadcast::Info::new().produce();
4991		request.accept(&source);
4992
4993		let resolved = resolving.await.expect("resolves via the standby");
4994		assert_eq!(resolved.info().path.as_str(), "room/alice");
4995	}
4996
4997	#[tokio::test]
4998	async fn split_horizon_skips_routes_through_the_requester() {
4999		let producer = origin(1).produce();
5000		let _server = producer
5001			.dynamic("room", Route::default().with_hops(hops(&[7])))
5002			.unwrap();
5003
5004		// The requester's own bytes must not be served back to it.
5005		let excluded = producer.consume().excluding(origin(7));
5006		let err = excluded
5007			.request_broadcast("room/alice")
5008			.now_or_never()
5009			.expect("unroutable")
5010			.err()
5011			.unwrap();
5012		assert!(matches!(err, Error::Unroutable));
5013
5014		// A clean requester resolves through the route (the request queues).
5015		let clean = producer.consume().excluding(origin(8));
5016		let pending = clean.request_broadcast("room/alice");
5017		assert!(pending.now_or_never().is_none());
5018	}
5019
5020	#[tokio::test]
5021	async fn routes_report_where_they_entered() {
5022		let producer = origin(1).produce();
5023		let peer = producer.clone().peer();
5024		let mut announced = producer.consume().announced();
5025
5026		let _ingest = producer
5027			.dynamic("client", Route::default().with_hops(hops(&[5])).with_via(origin(5)))
5028			.unwrap();
5029		let _gateway = producer.publish("gateway", Route::default()).unwrap();
5030		let _forwarded = peer
5031			.dynamic(
5032				"forwarded",
5033				Route::default().with_hops(hops(&[5, 7])).with_via(origin(7)),
5034			)
5035			.unwrap();
5036
5037		assert_eq!(announced.assert_next_active("client").source(), Source::Local);
5038		assert_eq!(
5039			announced.assert_next_active("forwarded").source(),
5040			Source::Peer(origin(7))
5041		);
5042		assert_eq!(announced.assert_next_active("gateway").source(), Source::Local);
5043
5044		// The mark survives narrowing the handle.
5045		let scoped = peer.scope("room", &Patterns::from(Pattern::all())).unwrap();
5046		let _nested = scoped.dynamic("x", Route::default().with_via(origin(8))).unwrap();
5047		assert_eq!(announced.assert_next_active("room/x").source(), Source::Peer(origin(8)));
5048	}
5049
5050	/// A change of source alone is delivered: the same chain and cost arriving
5051	/// from a peer instead of a client is a different fact for the consumer.
5052	#[tokio::test]
5053	async fn source_change_is_an_update() {
5054		let producer = origin(1).produce();
5055		let peer = producer.clone().peer();
5056		let mut announced = producer.consume().announced();
5057
5058		let route = Route::default().with_hops(hops(&[7])).with_via(origin(7));
5059		let _forwarded = peer.dynamic("room", route.clone()).unwrap();
5060		assert_eq!(announced.assert_next_active("room").source(), Source::Peer(origin(7)));
5061
5062		// The newest identical route wins, so the local twin takes over.
5063		let local = producer.dynamic("room", route).unwrap();
5064		let update = announced.next().now_or_never().expect("next blocked").expect("no next");
5065		assert_eq!(update.kind, AnnounceKind::Updated);
5066		assert_eq!(update.route.source(), Source::Local);
5067
5068		drop(local);
5069		assert_eq!(announced.assert_next_active("room").source(), Source::Peer(origin(7)));
5070	}
5071
5072	#[tokio::test]
5073	async fn local_view_hides_peer_routes() {
5074		let producer = origin(1).produce();
5075		let peer = producer.clone().peer();
5076		let mut local = producer.consume().local().announced();
5077
5078		let _forwarded = peer
5079			.dynamic("remote", Route::default().with_hops(hops(&[7])).with_via(origin(7)))
5080			.unwrap();
5081		local.assert_next_wait();
5082
5083		// A path both ingested here and forwarded by a peer shows the local route,
5084		// and retracts from the local view when the local route goes, even though
5085		// the peer's still covers it.
5086		let _shadow = peer
5087			.dynamic("both", Route::default().with_hops(hops(&[7])).with_via(origin(7)))
5088			.unwrap();
5089		let ingest = producer
5090			.dynamic(
5091				"both",
5092				Route::default().with_hops(hops(&[5])).with_via(origin(5)).with_cost(9),
5093			)
5094			.unwrap();
5095		assert_eq!(local.assert_next_active("both").source(), Source::Local);
5096		drop(ingest);
5097		local.assert_next_ended("both");
5098
5099		// Resolution agrees with the cursor: a peer-only path is unroutable here,
5100		// while the full view queues the request on the peer's route.
5101		let err = producer
5102			.consume()
5103			.local()
5104			.request_broadcast("remote/alice")
5105			.now_or_never()
5106			.expect("unroutable")
5107			.err()
5108			.unwrap();
5109		assert!(matches!(err, Error::Unroutable));
5110		assert!(
5111			producer
5112				.consume()
5113				.request_broadcast("remote/alice")
5114				.now_or_never()
5115				.is_none()
5116		);
5117	}
5118
5119	/// A handler that rejects a path with `Unroutable` while its route stands
5120	/// gives the requester that answer; the front must not re-ask the same route
5121	/// forever, which would spin the origin driver.
5122	#[tokio::test]
5123	async fn handler_rejection_is_final() {
5124		let producer = origin(1).produce();
5125		let consumer = producer.consume();
5126		let server = producer.dynamic("room", Route::default()).unwrap();
5127
5128		let pending = consumer.request_broadcast("room/alice");
5129		let request = queued(&server).await;
5130		request.reject(Error::Unroutable);
5131		let err = tokio::time::timeout(Duration::from_secs(5), pending)
5132			.await
5133			.expect("the front must give up, not spin")
5134			.err()
5135			.unwrap();
5136		assert!(matches!(err, Error::Unroutable));
5137
5138		// The route still stands and serves the next path.
5139		let pending = consumer.request_broadcast("room/bob");
5140		let request = queued(&server).await;
5141		assert_eq!(request.path().as_str(), "room/bob");
5142		let served = broadcast::Info::new().produce();
5143		request.accept(&served);
5144		pending.await.expect("resolves");
5145	}
5146
5147	/// `routed_broadcast` treats a handler's rejection as the table's verdict:
5148	/// it waits for the table to move instead of re-asking the same route.
5149	#[tokio::test]
5150	async fn routed_broadcast_waits_out_a_rejection() {
5151		let producer = origin(1).produce();
5152		let consumer = producer.consume();
5153		let server = producer.dynamic("room", Route::default()).unwrap();
5154
5155		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5156		assert!((&mut resolving).now_or_never().is_none());
5157		let request = queued(&server).await;
5158		request.reject(Error::Unroutable);
5159
5160		// Parked: the route stands, so nothing changed that a retry could use.
5161		for _ in 0..20 {
5162			tokio::task::yield_now().await;
5163		}
5164		assert!((&mut resolving).now_or_never().is_none());
5165		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5166
5167		// A re-price moves the table: the retry reaches the handler, which serves it.
5168		server.update(Route::default().with_cost(2)).unwrap();
5169		assert!((&mut resolving).now_or_never().is_none());
5170		let request = queued(&server).await;
5171		let served = broadcast::Info::new().produce();
5172		request.accept(&served);
5173		resolving.await.expect("resolves");
5174	}
5175
5176	/// Teardown rejects a parked request with `Dropped`, but a destroyed origin
5177	/// is `Closed` to `routed_broadcast`'s callers.
5178	#[tokio::test]
5179	async fn routed_broadcast_reports_teardown_as_closed() {
5180		let (producer, driver) = Producer::new(Config::new(origin(1)));
5181		let consumer = producer.consume();
5182		let _server = producer.dynamic("room", Route::default()).unwrap();
5183
5184		// Park on the covering route, past the loop's closed check.
5185		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5186		assert!((&mut resolving).now_or_never().is_none());
5187
5188		drop(driver);
5189
5190		let err = tokio::time::timeout(Duration::from_secs(5), resolving)
5191			.await
5192			.expect("teardown resolves the wait")
5193			.err()
5194			.unwrap();
5195		assert!(matches!(err, Error::Closed), "unexpected end: {err}");
5196	}
5197
5198	/// A local broadcast announcing at the exact path is a table change too: a
5199	/// requester parked on a handler's rejection resolves to it.
5200	#[tokio::test]
5201	async fn routed_broadcast_wakes_for_a_local_broadcast() {
5202		let producer = origin(1).produce();
5203		let consumer = producer.consume();
5204		let server = producer.dynamic("room", Route::default()).unwrap();
5205
5206		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5207		assert!((&mut resolving).now_or_never().is_none());
5208		queued(&server).await.reject(Error::Unroutable);
5209		for _ in 0..20 {
5210			tokio::task::yield_now().await;
5211		}
5212		assert!((&mut resolving).now_or_never().is_none());
5213
5214		// The more specific route wins outright over the handler's prefix.
5215		let _local = producer.publish("room/alice", Route::default()).unwrap();
5216		let resolved = resolving.await.expect("resolves locally");
5217		assert_eq!(resolved.info().path.as_str(), "room/alice");
5218		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5219	}
5220
5221	/// A track first subscribed after the front is already serving another still
5222	/// replays what its source holds, like the first track did.
5223	#[tokio::test]
5224	async fn late_track_on_a_served_front_replays() {
5225		let producer = origin(1).produce();
5226		let consumer = producer.consume();
5227		let server = producer.dynamic("room", Route::default()).unwrap();
5228
5229		let source = broadcast::Info::new().produce();
5230		for name in ["a", "b"] {
5231			let track = source.create_track(name, None).unwrap();
5232			let mut group = track.append_group().unwrap();
5233			group.write_frame(crate::Timestamp::ZERO, name.as_bytes()).unwrap();
5234			group.finish().unwrap();
5235			// The producer stays alive: the track is open, like a live SI track.
5236			std::mem::forget(track);
5237		}
5238
5239		let pending = consumer.request_broadcast("room/alice");
5240		queued(&server).await.accept(&source);
5241		let resolved = pending.await.expect("resolves");
5242
5243		let budget = track::Subscription::default().with_max_age(Duration::from_secs(3600));
5244		for name in ["a", "b"] {
5245			let mut subscription = resolved
5246				.track(name)
5247				.unwrap()
5248				.subscribe(budget.clone())
5249				.await
5250				.expect("subscribe");
5251			let mut group = tokio::time::timeout(Duration::from_secs(5), subscription.recv_group())
5252				.await
5253				.expect("the late track must replay, not park")
5254				.expect("recv group")
5255				.expect("track ended early");
5256			let frame = group.read_frame().await.expect("read frame").expect("frame");
5257			assert_eq!(&frame.payload[..], name.as_bytes());
5258		}
5259	}
5260
5261	#[tokio::test]
5262	async fn most_specific_prefix_shadows() {
5263		let producer = origin(1).produce();
5264		let consumer = producer.consume();
5265
5266		let broad_server = producer.dynamic("", Route::default()).unwrap();
5267		// A narrow advertise-only claim: requests under it must NOT route to the
5268		// broad server; they fall through to the (absent) fallback handler.
5269		let _narrow = producer.announce(".dash", Route::default()).unwrap();
5270
5271		let err = consumer
5272			.request_broadcast(".dash/pid")
5273			.now_or_never()
5274			.expect("unroutable")
5275			.err()
5276			.unwrap();
5277		assert!(matches!(err, Error::Unroutable));
5278
5279		// Everything else still routes to the broad server.
5280		let _pending = consumer.request_broadcast("room/alice");
5281		let request = queued(&broad_server).await;
5282		assert_eq!(request.path().as_str(), "room/alice");
5283	}
5284
5285	#[tokio::test]
5286	async fn root_dynamic_serves_any_path() {
5287		let producer = origin(1).produce();
5288		let consumer = producer.consume();
5289		let mut announced = consumer.announced();
5290		let dynamic = producer.dynamic("", Route::default()).unwrap();
5291		// The root claim is advertised like any other prefix.
5292		announced.assert_next_active("");
5293
5294		let pending = consumer.request_broadcast("anything/at/all");
5295		let request = queued(&dynamic).await;
5296		assert_eq!(request.path().as_str(), "anything/at/all");
5297
5298		let source = broadcast::Info::new().produce();
5299		request.accept(&source);
5300		let resolved = pending.await.expect("resolves");
5301		assert_eq!(resolved.info().path.as_str(), "anything/at/all");
5302
5303		// Nothing serves an uncovered path once the handler is gone.
5304		drop(dynamic);
5305		announced.assert_next_ended("");
5306		let err = consumer
5307			.request_broadcast("something/else")
5308			.now_or_never()
5309			.expect("unroutable")
5310			.err()
5311			.unwrap();
5312		assert!(matches!(err, Error::Unroutable));
5313	}
5314
5315	/// A path outside the consumer's scope never reaches a live dynamic handler.
5316	///
5317	/// `scope` is authoritative, so an out-of-scope path is unauthorized before
5318	/// routing can send a request to the handler. A `Request` carries only a path,
5319	/// so the handler cannot tell who asked.
5320	#[tokio::test]
5321	async fn out_of_scope_request_never_reaches_the_dynamic_handler() {
5322		let producer = origin(1).produce();
5323		let dynamic = producer.dynamic("", Route::default()).unwrap();
5324		let scoped = producer.consume().scope("", &scopes(&["tenant-a"])).unwrap();
5325
5326		// `tenant-a-other` shares a character prefix but not a segment, so this
5327		// also pins that the check is segment-aware rather than textual.
5328		for path in ["tenant-b/live", "tenant-a-other/live"] {
5329			let refused = scoped
5330				.request_broadcast(path)
5331				.now_or_never()
5332				.expect("an out-of-scope request must be refused synchronously, not queued");
5333			assert!(matches!(refused, Err(Error::Unauthorized)));
5334			assert!(
5335				dynamic.requested_broadcast().now_or_never().is_none(),
5336				"the dynamic handler was asked to create a broadcast the requester may not read"
5337			);
5338		}
5339	}
5340
5341	#[tokio::test]
5342	async fn routed_waits_for_coverage() {
5343		let producer = origin(1).produce();
5344		let consumer = producer.consume();
5345
5346		let mut fut = consumer.routed("room/alice").boxed();
5347		assert!((&mut fut).now_or_never().is_none());
5348
5349		// A covering prefix resolves the wait.
5350		let _a = producer.announce("room", Route::default().with_cost(3)).unwrap();
5351		let route = fut.now_or_never().expect("covered").expect("routed");
5352		assert_eq!(route.cost, Cost::new(3));
5353
5354		// Already covered: resolves immediately.
5355		consumer
5356			.routed("room/alice/cam")
5357			.now_or_never()
5358			.expect("covered")
5359			.expect("routed");
5360	}
5361
5362	#[tokio::test]
5363	async fn routed_ignores_deeper_routes() {
5364		let producer = origin(1).produce();
5365		let consumer = producer.consume();
5366
5367		// A deeper route does not cover the shorter path.
5368		let _deep = producer.announce("room/alice/cam", Route::default()).unwrap();
5369		let mut fut = consumer.routed("room/alice").boxed();
5370		assert!((&mut fut).now_or_never().is_none());
5371
5372		let _exact = producer.announce("room/alice", Route::default()).unwrap();
5373		fut.now_or_never().expect("covered").expect("routed");
5374	}
5375
5376	#[tokio::test]
5377	async fn routed_accepts_a_max_depth_path() {
5378		let producer = origin(1).produce();
5379		let consumer = producer.consume();
5380		let path = (0..Path::MAX_PARTS)
5381			.map(|i| format!("s{i}"))
5382			.collect::<Vec<_>>()
5383			.join("/");
5384		assert_eq!(Path::new(&path).parts().count(), Path::MAX_PARTS);
5385
5386		assert!(consumer.allowed().matches(&path));
5387
5388		let mut fut = consumer.routed(&path).boxed();
5389		assert!((&mut fut).now_or_never().is_none());
5390
5391		// A covering root still resolves: the lookup must not require `path/**`.
5392		let _a = producer.announce("", Route::default()).unwrap();
5393		fut.now_or_never().expect("covered").expect("routed");
5394	}
5395
5396	#[tokio::test]
5397	async fn teardown_ends_everything() {
5398		let (producer, driver) = Producer::new(Config::new(origin(1)));
5399		let consumer = producer.consume();
5400		let _announcement = producer.announce("room", Route::default()).unwrap();
5401		let mut announced = consumer.announced();
5402		announced.assert_next_active("room");
5403
5404		let _server = producer.dynamic("served", Route::default()).unwrap();
5405		let pending = consumer.request_broadcast("served/path");
5406
5407		drop(driver);
5408
5409		// The cursor observes the end (after draining pending updates).
5410		announced.assert_next_active("served");
5411		assert!(announced.next().now_or_never().expect("ended").is_none());
5412
5413		// Pending requests reject; new work refuses.
5414		assert!(pending.now_or_never().expect("rejected").is_err());
5415		assert!(matches!(producer.announce("x", Route::default()), Err(Error::Closed)));
5416		assert!(matches!(producer.create_broadcast("x"), Err(Error::Closed)));
5417		let err = consumer
5418			.request_broadcast("y")
5419			.now_or_never()
5420			.expect("closed")
5421			.err()
5422			.unwrap();
5423		assert!(matches!(err, Error::Closed));
5424
5425		// A cursor born after the teardown is born ended.
5426		let mut late = consumer.announced();
5427		assert!(late.next().now_or_never().expect("ended").is_none());
5428	}
5429
5430	/// One live subscription reading a track through a remote front, plus the
5431	/// bookkeeping to kill and replace its serving route.
5432	struct ResumeRig {
5433		producer: Producer,
5434		resolved: broadcast::Consumer,
5435		subscription: track::Subscriber,
5436		/// Keeps the incumbent's track producing; dropping it would abort the
5437		/// track out from under the front mid-test.
5438		incumbent_track: track::Producer,
5439	}
5440
5441	impl ResumeRig {
5442		/// Announce a served route with `first` as its first hop, materialize
5443		/// "room/alice" through it with a one-group "before" track, and subscribe.
5444		async fn new(first: &[u64]) -> (Self, Dynamic, broadcast::Producer) {
5445			let producer = origin(1).produce();
5446			let consumer = producer.consume();
5447
5448			let server = producer
5449				.dynamic("room", Route::default().with_hops(hops(first)))
5450				.unwrap();
5451
5452			let pending = consumer.request_broadcast("room/alice");
5453			let request = queued(&server).await;
5454			let source = broadcast::Info::new().produce();
5455			let track = source.create_track("video", None).unwrap();
5456			let mut group = track.append_group().unwrap();
5457			group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5458			group.finish().unwrap();
5459			request.accept(&source);
5460
5461			let resolved = pending.await.expect("resolves");
5462			let mut subscription = resolved
5463				.track("video")
5464				.unwrap()
5465				.subscribe(None)
5466				.await
5467				.expect("subscribe");
5468			let mut group = subscription
5469				.recv_group()
5470				.await
5471				.expect("recv group")
5472				.expect("track ended early");
5473			let frame = group.read_frame().await.expect("read frame").expect("frame");
5474			assert_eq!(&frame.payload[..], b"before");
5475
5476			(
5477				Self {
5478					producer,
5479					resolved,
5480					subscription,
5481					incumbent_track: track,
5482				},
5483				server,
5484				source,
5485			)
5486		}
5487
5488		/// Stand up a second served route with `first` as its first hop and hand
5489		/// back its handle, ready to answer the front's re-request.
5490		fn standby(&self, first: &[u64]) -> Dynamic {
5491			self.producer
5492				.dynamic("room", Route::default().with_hops(hops(first)))
5493				.unwrap()
5494		}
5495	}
5496
5497	/// Accept the front's re-request on `server` with a source carrying the same
5498	/// content stream (the delivered group plus its successor) and prove the
5499	/// rig's subscription resumes onto it: the successor group is delivered on
5500	/// the same subscription, at the group boundary.
5501	async fn assert_resumes(rig: &mut ResumeRig, server: &Dynamic) {
5502		let request = queued(server).await;
5503		let replacement = broadcast::Info::new().produce();
5504		let track = replacement.create_track("video", None).unwrap();
5505		// The same content: group 0 was already delivered through the old route,
5506		// so the splice resumes at group 1.
5507		let mut group = track.append_group().unwrap();
5508		group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5509		group.finish().unwrap();
5510		request.accept(&replacement);
5511
5512		let mut group = track.append_group().unwrap();
5513		group.write_frame(crate::Timestamp::ZERO, b"resumed".as_ref()).unwrap();
5514		group.finish().unwrap();
5515
5516		let mut group = rig
5517			.subscription
5518			.recv_group()
5519			.await
5520			.expect("subscription survives the failover")
5521			.expect("track ended early");
5522		let frame = group.read_frame().await.expect("read frame").expect("frame");
5523		assert_eq!(&frame.payload[..], b"resumed");
5524	}
5525
5526	/// The driver's completion contract: it resolves once every producer handle
5527	/// drops, however many read handles remain.
5528	#[tokio::test]
5529	async fn driver_resolves_with_live_consumers() {
5530		let (producer, driver) = Producer::new(Config::new(origin(1)));
5531		let consumer = producer.consume();
5532		let run = crate::time::run(driver);
5533		drop(producer);
5534		tokio::time::timeout(Duration::from_secs(5), run)
5535			.await
5536			.expect("driver must finish once the producers are gone");
5537		drop(consumer);
5538	}
5539
5540	#[tokio::test]
5541	async fn remote_source_resumes_through_same_first_hop() {
5542		let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5543		let standby_server = rig.standby(&[10, 20]);
5544
5545		// The serving route dies: retraction plus source abort, like a session.
5546		drop(incumbent);
5547		drop(source);
5548
5549		// The standby shares the first hop, so the subscription resumes there.
5550		assert_resumes(&mut rig, &standby_server).await;
5551	}
5552
5553	/// A source claiming the same content cannot change immutable track metadata:
5554	/// the successor is refused instead of the subscriber's samples being read on
5555	/// a different grid, and the verdict outlives the aborted logical track.
5556	#[tokio::test]
5557	async fn incompatible_successor_is_refused() {
5558		for replacement in [
5559			track::Info::default().with_timescale(crate::Timescale::MICRO),
5560			track::Info::default().with_priority(7),
5561			track::Info::default().with_max_age(Duration::from_secs(7)),
5562		] {
5563			let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5564			let standby_server = rig.standby(&[10, 20]);
5565			drop(incumbent);
5566			drop(source);
5567
5568			// The standby shares the first hop, so the front re-requests through it,
5569			// but its copy of the track is on another grid.
5570			let request = queued(&standby_server).await;
5571			let successor = broadcast::Info::new().produce();
5572			let track = successor.create_track("video", replacement).unwrap();
5573			let mut group = track.append_group().unwrap();
5574			group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5575			group.finish().unwrap();
5576			request.accept(&successor);
5577
5578			assert!(
5579				matches!(rig.subscription.recv_group().await, Err(Error::Unsupported)),
5580				"the subscription must abort rather than resume onto incompatible metadata"
5581			);
5582
5583			// Reopening the aborted logical track must not forget the broadcast's metadata.
5584			let reopened = rig.resolved.track("video").unwrap();
5585			assert!(matches!(reopened.query().await, Err(Error::Unsupported)));
5586			assert!(matches!(reopened.subscribe(None).await, Err(Error::Unsupported)));
5587		}
5588	}
5589
5590	#[tokio::test]
5591	async fn different_first_hop_ends_the_subscription() {
5592		let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5593		// Another publisher entirely: same path, different first hop.
5594		let rival_server = rig.standby(&[11]);
5595
5596		// The incumbent's session dies, taking its track with it: a live copy
5597		// would otherwise keep serving after the front ends.
5598		drop(incumbent);
5599		drop(source);
5600		rig.incumbent_track.abort(Error::Dropped).unwrap();
5601
5602		// The subscription ends rather than splicing onto the rival's frames.
5603		let err = rig.subscription.recv_group().await.err().expect("subscription ends");
5604		assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5605
5606		// A fresh request resolves through the rival.
5607		let consumer = rig.producer.consume();
5608		let pending = consumer.request_broadcast("room/alice");
5609		let request = queued(&rival_server).await;
5610		let replacement = broadcast::Info::new().produce();
5611		request.accept(&replacement);
5612		pending.await.expect("re-request resolves through the rival");
5613	}
5614
5615	#[tokio::test]
5616	async fn anonymous_routes_never_resume() {
5617		// An empty hop chain identifies nobody, so two of them must not pass for
5618		// one publisher reconnecting.
5619		let (mut rig, incumbent, source) = ResumeRig::new(&[]).await;
5620		let _twin_server = rig.standby(&[]);
5621
5622		drop(incumbent);
5623		drop(source);
5624		rig.incumbent_track.abort(Error::Dropped).unwrap();
5625
5626		let err = rig.subscription.recv_group().await.err().expect("subscription ends");
5627		assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5628	}
5629
5630	/// An anonymous publisher that dies without unannouncing is replaced by the
5631	/// next anonymous session at the same path: a subscriber on a third session
5632	/// gets the newcomer's media immediately, not a lingering dead front.
5633	///
5634	/// The front closes with its last source, so the newcomer attaches a fresh
5635	/// one and the subscriber resolves it without parking.
5636	#[tokio::test]
5637	async fn anonymous_handoff_serves_the_newcomer_immediately() {
5638		let producer = origin(1).produce();
5639
5640		// Session A: an assigned anonymous hop serving the path.
5641		let server_a = producer
5642			.dynamic("room", Route::default().with_hops(hops(&[10])))
5643			.unwrap();
5644
5645		// Session C: a third anonymous session, excluding the hop the server
5646		// minted for it, the same split-horizon a live session applies.
5647		let consumer = producer.consume().excluding(origin(30));
5648		let pending = consumer.request_broadcast("room/alice");
5649		let request = queued(&server_a).await;
5650		let source_a = broadcast::Info::new().produce();
5651		let track_a = source_a.create_track("video", None).unwrap();
5652		let mut group = track_a.append_group().unwrap();
5653		group.write_frame(crate::Timestamp::ZERO, b"from-a".as_ref()).unwrap();
5654		group.finish().unwrap();
5655		request.accept(&source_a);
5656
5657		let resolved_a = pending.await.expect("resolves");
5658		let mut sub_a = resolved_a
5659			.track("video")
5660			.unwrap()
5661			.subscribe(None)
5662			.await
5663			.expect("subscribe");
5664		let mut group = sub_a
5665			.recv_group()
5666			.await
5667			.expect("recv group")
5668			.expect("track ended early");
5669		assert_eq!(
5670			&group.read_frame().await.expect("read frame").expect("frame").payload[..],
5671			b"from-a"
5672		);
5673
5674		// A dies without an unannounce: the source and its route drop together,
5675		// the way a lost session retracts rather than sending ANNOUNCE_END.
5676		drop(track_a);
5677		drop(source_a);
5678		drop(server_a);
5679
5680		// The front closed with A's last source.
5681		let err = sub_a.recv_group().await.err().expect("front closed");
5682		assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5683
5684		// No stale front at the leaf, and a repeat request does not join the
5685		// corpse: nothing covers the path, so it is Unroutable rather than
5686		// parked on a linger or 404 `dropped` from the dead front.
5687		settle(|| consumer.get_broadcast("room/alice").is_none()).await;
5688		settle(|| {
5689			matches!(
5690				consumer.request_broadcast("room/alice").now_or_never(),
5691				Some(Err(Error::Unroutable))
5692			)
5693		})
5694		.await;
5695
5696		// Session B attaches at the same path. Its front is served immediately.
5697		let server_b = producer
5698			.dynamic("room", Route::default().with_hops(hops(&[20])))
5699			.unwrap();
5700		let pending = consumer.request_broadcast("room/alice");
5701		let request = queued(&server_b).await;
5702		let source_b = broadcast::Info::new().produce();
5703		let track_b = source_b.create_track("video", None).unwrap();
5704		let mut group = track_b.append_group().unwrap();
5705		group.write_frame(crate::Timestamp::ZERO, b"from-b".as_ref()).unwrap();
5706		group.finish().unwrap();
5707		request.accept(&source_b);
5708
5709		let resolved_b = pending.await.expect("B's front is served immediately");
5710		assert!(
5711			!resolved_b.is_clone(&resolved_a),
5712			"B must not splice into A's closed front"
5713		);
5714
5715		let mut sub_b = resolved_b
5716			.track("video")
5717			.unwrap()
5718			.subscribe(None)
5719			.await
5720			.expect("subscribe");
5721		let mut group = sub_b
5722			.recv_group()
5723			.await
5724			.expect("recv group")
5725			.expect("track ended early");
5726		assert_eq!(
5727			&group.read_frame().await.expect("read frame").expect("frame").payload[..],
5728			b"from-b"
5729		);
5730	}
5731
5732	#[tokio::test]
5733	async fn reprice_is_invisible_to_the_subscription() {
5734		let (rig, incumbent, source) = ResumeRig::new(&[10]).await;
5735
5736		// A metadata-only reprice of the only route: nothing re-requests and the
5737		// subscription keeps flowing from the same source.
5738		incumbent
5739			.update(Route::default().with_hops(hops(&[10])).with_cost(9))
5740			.unwrap();
5741
5742		let track = source.create_track("audio", None).unwrap();
5743		let mut group = track.append_group().unwrap();
5744		group.write_frame(crate::Timestamp::ZERO, b"steady".as_ref()).unwrap();
5745		group.finish().unwrap();
5746
5747		let mut audio = rig
5748			.resolved
5749			.track("audio")
5750			.unwrap()
5751			.subscribe(None)
5752			.await
5753			.expect("subscribe survives the reprice");
5754		let mut group = audio
5755			.recv_group()
5756			.await
5757			.expect("recv group")
5758			.expect("track ended early");
5759		let frame = group.read_frame().await.expect("read frame").expect("frame");
5760		assert_eq!(&frame.payload[..], b"steady");
5761	}
5762
5763	#[tokio::test]
5764	async fn drain_reprice_migrates_before_the_session_dies() {
5765		let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5766		let standby_server = rig.standby(&[10, 20]);
5767
5768		// The serving route drains: repriced to the ceiling while its session
5769		// keeps serving. The front migrates to the standby without waiting for
5770		// the death.
5771		incumbent
5772			.update(Route::default().with_hops(hops(&[10])).with_cost(Cost::DRAIN))
5773			.unwrap();
5774
5775		assert_resumes(&mut rig, &standby_server).await;
5776
5777		// The drained source outlived the migration.
5778		drop(incumbent);
5779		drop(source);
5780	}
5781
5782	#[tokio::test]
5783	async fn local_sources_splice_newest_first() {
5784		let producer = origin(1).produce();
5785		let consumer = producer.consume();
5786
5787		let first = producer.publish("room/alice", Route::default()).unwrap();
5788		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5789
5790		// A second source at the same path joins the same front.
5791		let second = producer.publish("room/alice", Route::default()).unwrap();
5792		let again = consumer.request_broadcast("room/alice").await.expect("resolves");
5793		assert!(again.is_clone(&resolved));
5794
5795		// Losing one source keeps the front alive; losing both closes it.
5796		first.finish();
5797		settle(|| consumer.get_broadcast("room/alice").is_some()).await;
5798		second.finish();
5799		settle(|| consumer.get_broadcast("room/alice").is_none()).await;
5800
5801		// The path is free again for a fresh broadcast.
5802		let _third = producer.publish("room/alice", Route::default()).unwrap();
5803		assert!(consumer.get_broadcast("room/alice").is_some());
5804	}
5805
5806	/// The publisher finishes a track, then its broadcast. A subscription already in
5807	/// flight must conclude normally: the track's last group, then the end. moq-lite,
5808	/// ANNOUNCE_END: "Retraction does not disturb subscriptions already in flight,
5809	/// which conclude normally with SUBSCRIBE_END."
5810	///
5811	/// The runtime is single-threaded and the publisher's whole ending has no await in
5812	/// it, so the outcome does not depend on timing.
5813	#[tokio::test]
5814	async fn a_finished_broadcast_concludes_in_flight_subscriptions() {
5815		let producer = origin(1).produce();
5816		let consumer = producer.consume();
5817
5818		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
5819		let track = broadcast.create_track("video", None).unwrap();
5820
5821		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5822		let mut subscription = resolved
5823			.track("video")
5824			.unwrap()
5825			.subscribe(None)
5826			.await
5827			.expect("subscribe");
5828		// A textbook clean end, innermost first: the group, the track, the broadcast.
5829		let mut group = track.append_group().unwrap();
5830		group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
5831		group.finish().unwrap();
5832		track.finish().unwrap();
5833		drop(track);
5834		broadcast.finish();
5835
5836		let mut group = next_group(&mut subscription)
5837			.await
5838			.expect("a cleanly finished track was served as an error")
5839			.expect("the track ended before its last group");
5840		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
5841		drop(group);
5842
5843		let end = next_group(&mut subscription)
5844			.await
5845			.expect("a cleanly finished track ended as an error");
5846		assert!(end.is_none(), "a group followed the final one");
5847	}
5848
5849	/// A route served from upstream is retracted (what the lite subscriber does on
5850	/// ANNOUNCE_END: finish the source it minted, drop the route) while the track's
5851	/// last group and end are still on their way. The subscription already in flight
5852	/// must still conclude normally. moq-lite, ANNOUNCE_END: "Retraction does not
5853	/// disturb subscriptions already in flight, which conclude normally with
5854	/// SUBSCRIBE_END."
5855	#[tokio::test]
5856	async fn a_retracted_route_concludes_in_flight_subscriptions() {
5857		let producer = origin(1).produce();
5858		let consumer = producer.consume();
5859		let server = producer
5860			.dynamic("room", Route::default().with_hops(hops(&[10])))
5861			.unwrap();
5862
5863		let pending = consumer.request_broadcast("room/alice");
5864		let request = queued(&server).await;
5865		let source = broadcast::Info::new().produce();
5866		let track = source.create_track("video", None).unwrap();
5867		request.accept(&source);
5868
5869		let resolved = pending.await.expect("resolves");
5870		let mut subscription = resolved
5871			.track("video")
5872			.unwrap()
5873			.subscribe(None)
5874			.await
5875			.expect("subscribe");
5876
5877		// ANNOUNCE_END overtakes the track's end: the route is retracted, and the front
5878		// has acted on it, before the track's last group and end arrive.
5879		source.finish();
5880		drop(server);
5881		settle(|| resolved.is_closed()).await;
5882		let mut group = track.append_group().unwrap();
5883		group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
5884		group.finish().unwrap();
5885		track.finish().unwrap();
5886		drop(track);
5887
5888		let mut group = next_group(&mut subscription)
5889			.await
5890			.expect("a retracted route's track was served as an error")
5891			.expect("the track ended before its last group");
5892		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
5893		drop(group);
5894
5895		let end = next_group(&mut subscription)
5896			.await
5897			.expect("a cleanly finished track ended as an error");
5898		assert!(end.is_none(), "a group followed the final one");
5899	}
5900
5901	/// An origin front drops the source track as soon as its last reader leaves,
5902	/// so the publisher's `unused()` resolves far below `TRACK_IDLE_LINGER`.
5903	/// Cached groups stay on the front for the linger; a returning reader
5904	/// replays them and re-splices for groups past that edge.
5905	#[tokio::test]
5906	async fn origin_front_drops_the_source_when_unused() {
5907		let producer = origin(1).produce();
5908		let consumer = producer.consume();
5909
5910		let broadcast = producer.publish("room/alice", Route::default()).unwrap();
5911		let track = broadcast.create_track("video", None).unwrap();
5912		let mut group = track.append_group().unwrap();
5913		group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
5914		group.finish().unwrap();
5915
5916		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5917		let mut subscription = resolved
5918			.track("video")
5919			.unwrap()
5920			.subscribe(None)
5921			.await
5922			.expect("subscribe");
5923		let mut group = subscription.recv_group().await.unwrap().unwrap();
5924		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
5925		drop(group);
5926		drop(subscription);
5927
5928		tokio::time::timeout(Duration::from_secs(1), track.unused())
5929			.await
5930			.expect("source unused should resolve far below TRACK_IDLE_LINGER")
5931			.expect("source closed");
5932
5933		// Cached groups stay on the front for the linger; a returning reader
5934		// replays them without waiting out the window.
5935		let mut again = resolved
5936			.track("video")
5937			.unwrap()
5938			.subscribe(track::Subscription::default().with_max_age(Duration::from_secs(3600)))
5939			.await
5940			.expect("resubscribe");
5941		let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
5942			.await
5943			.expect("cached group is still on the front")
5944			.expect("recv group")
5945			.expect("track ended early");
5946		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
5947
5948		tokio::time::timeout(Duration::from_secs(1), track.used())
5949			.await
5950			.expect("returning reader re-splices the source")
5951			.expect("source closed");
5952
5953		let mut group = track.append_group().unwrap();
5954		group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
5955		group.finish().unwrap();
5956		let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
5957			.await
5958			.expect("groups past the cached edge come from the re-splice")
5959			.expect("recv group")
5960			.expect("track ended early");
5961		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
5962	}
5963
5964	/// A front serving from another front's spliced copy has no snapshot to keep:
5965	/// it still drops upstream on the unused edge, so the publisher's `unused()`
5966	/// resolves far below `TRACK_IDLE_LINGER` through the whole chain. The next
5967	/// reader re-splices, paying `TRACK_INFO` again.
5968	#[tokio::test]
5969	async fn chained_front_drops_the_source_when_unused() {
5970		let leaf = origin(1).produce();
5971		let leaf_consumer = leaf.consume();
5972
5973		let broadcast = leaf.publish("room/alice", Route::default()).unwrap();
5974		let track = broadcast.create_track("video", None).unwrap();
5975		let mut group = track.append_group().unwrap();
5976		group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
5977		group.finish().unwrap();
5978
5979		// The leaf's front view: a spliced broadcast, so any front serving from
5980		// it holds a spliced source copy with nothing to snapshot.
5981		let leaf_front = leaf_consumer.request_broadcast("room/alice").await.expect("resolves");
5982
5983		let mid = origin(2).produce();
5984		let mid_server = mid.dynamic("room", Route::default().with_hops(hops(&[10]))).unwrap();
5985		let mid_pending = mid.consume().request_broadcast("room/alice");
5986		queued(&mid_server).await.accept(&leaf_front);
5987		let mid_resolved = mid_pending.await.expect("mid resolves");
5988
5989		let edge = origin(3).produce();
5990		let edge_server = edge.dynamic("room", Route::default().with_hops(hops(&[20]))).unwrap();
5991		let edge_pending = edge.consume().request_broadcast("room/alice");
5992		queued(&edge_server).await.accept(&mid_resolved);
5993		let edge_resolved = edge_pending.await.expect("edge resolves");
5994
5995		let mut subscription = edge_resolved
5996			.track("video")
5997			.unwrap()
5998			.subscribe(None)
5999			.await
6000			.expect("subscribe");
6001		let mut group = subscription.recv_group().await.unwrap().unwrap();
6002		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6003		drop(group);
6004		drop(subscription);
6005
6006		tokio::time::timeout(Duration::from_secs(5), track.unused())
6007			.await
6008			.expect("chained unused should resolve far below TRACK_IDLE_LINGER")
6009			.expect("source closed");
6010
6011		let cached = edge_resolved.track("video").unwrap().cached_groups();
6012		assert_eq!(
6013			cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
6014			vec![0],
6015			"every front keeps the delivered groups after releasing its source"
6016		);
6017
6018		let mut subscription = edge_resolved
6019			.track("video")
6020			.unwrap()
6021			.subscribe(None)
6022			.await
6023			.expect("resubscribe");
6024		tokio::time::timeout(Duration::from_secs(5), track.used())
6025			.await
6026			.expect("resubscribe should reach the leaf")
6027			.expect("source open");
6028		let mut group = track.append_group().unwrap();
6029		group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
6030		group.finish().unwrap();
6031		let mut group = subscription.recv_group().await.unwrap().unwrap();
6032		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6033		drop(group);
6034		let mut group = subscription.recv_group().await.unwrap().unwrap();
6035		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
6036		drop(group);
6037		drop(subscription);
6038
6039		tokio::time::timeout(Duration::from_secs(5), track.unused())
6040			.await
6041			.expect("second chained unused should resolve far below TRACK_IDLE_LINGER")
6042			.expect("source closed");
6043
6044		let cached = edge_resolved.track("video").unwrap().cached_groups();
6045		assert_eq!(
6046			cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
6047			vec![0, 1],
6048			"repeated demand keeps every complete group while releasing its source"
6049		);
6050
6051		let fetch = edge_resolved.track("video").unwrap().fetch_group(2, None);
6052		let mut fetch = std::pin::pin!(fetch);
6053		assert!(futures::poll!(fetch.as_mut()).is_pending(), "fetch should re-splice");
6054		tokio::time::timeout(Duration::from_secs(5), track.used())
6055			.await
6056			.expect("fetch should reach the leaf")
6057			.expect("source open");
6058		let mut group = track.append_group().unwrap();
6059		group.write_frame(crate::Timestamp::ZERO, b"fetched".as_ref()).unwrap();
6060		group.finish().unwrap();
6061		let mut group = tokio::time::timeout(Duration::from_secs(5), fetch)
6062			.await
6063			.expect("re-spliced source should answer the fetch")
6064			.expect("fetch succeeds");
6065		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"fetched");
6066	}
6067
6068	/// A newer local source wins dispatch the moment it attaches, but one whose copy
6069	/// of the track carries different metadata is refused: the incumbent keeps
6070	/// serving, and the refusal is never retried once the incumbent leaves.
6071	#[tokio::test]
6072	async fn incompatible_local_source_keeps_the_incumbent() {
6073		let producer = origin(1).produce();
6074		let consumer = producer.consume();
6075
6076		let first = producer.publish("room/alice", Route::default()).unwrap();
6077		let track = first.create_track("video", None).unwrap();
6078		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6079		let mut subscription = resolved
6080			.track("video")
6081			.unwrap()
6082			.subscribe(None)
6083			.await
6084			.expect("subscribe");
6085		let mut group = track.append_group().unwrap();
6086		group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
6087		group.finish().unwrap();
6088		let mut group = subscription.recv_group().await.unwrap().unwrap();
6089		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"before");
6090
6091		// The newest source is dispatched the track, and refused for its metadata.
6092		let second = producer.publish("room/alice", Route::default()).unwrap();
6093		let _incompatible = second
6094			.create_track("video", track::Info::default().with_timescale(crate::Timescale::MICRO))
6095			.unwrap();
6096		for _ in 0..10 {
6097			tokio::task::yield_now().await;
6098		}
6099
6100		// Still spliced to the incumbent, still delivering.
6101		let mut group = track.append_group().unwrap();
6102		group.write_frame(crate::Timestamp::ZERO, b"still".as_ref()).unwrap();
6103		group.finish().unwrap();
6104		let mut group = subscription.recv_group().await.unwrap().unwrap();
6105		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"still");
6106
6107		// The incumbent leaving exhausts the table: the refusal is never retried.
6108		drop(track);
6109		first.finish();
6110		assert!(matches!(subscription.recv_group().await, Err(Error::Unsupported)));
6111	}
6112
6113	#[tokio::test]
6114	async fn multiple_scopes_present_one_broad_prefix() {
6115		let producer = origin(1).produce();
6116		let _a = producer.announce("", Route::default()).unwrap();
6117
6118		let consumer = producer.consume().scope("", &scopes(&["alpha", "beta"])).unwrap();
6119		let mut announced = consumer.announced();
6120		announced.assert_next_active("");
6121		announced.assert_next_wait();
6122	}
6123
6124	#[test]
6125	fn scope_accepts_every_pattern_union() {
6126		let producer = origin(1).produce();
6127
6128		// The root grant is `**`, the old empty prefix.
6129		let root = producer.scope("", &Patterns::from(Pattern::all())).unwrap();
6130		assert_eq!(root.allowed(), Patterns::from(Pattern::all()));
6131
6132		// `foo/**` keeps the old `foo` prefix meaning.
6133		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
6134		assert_eq!(scoped.allowed(), scopes(&["room"]));
6135
6136		// Multiple prefixes round-trip, with overlap collapsed.
6137		let multi = producer.scope("", &scopes(&["room", "room/chat", "anon"])).unwrap();
6138		assert_eq!(multi.allowed(), scopes(&["room", "anon"]));
6139
6140		// The consumer side reports the same way.
6141		let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
6142		assert_eq!(consumer.allowed(), scopes(&["room"]));
6143
6144		for text in ["room", "", "*room", "room/*", "*", "**/room", "room/**/chat", "*.hang"] {
6145			let union = Patterns::from(text.parse::<Pattern>().unwrap());
6146			assert_eq!(producer.scope("", &union).expect(text).allowed(), union, "{text}");
6147			assert_eq!(
6148				producer.consume().scope("", &union).expect(text).allowed(),
6149				union,
6150				"{text}"
6151			);
6152		}
6153
6154		let mixed: Patterns = ["room/**".parse().unwrap(), "other".parse().unwrap()]
6155			.into_iter()
6156			.collect();
6157		assert_eq!(producer.scope("", &mixed).unwrap().allowed(), mixed);
6158	}
6159
6160	#[test]
6161	fn route_table_prunes_to_empty() {
6162		let producer = origin(1).produce();
6163		let consumer = producer.consume();
6164
6165		// Routes and cursors hang at their prefixes; the nodes on the way exist
6166		// only while something is there.
6167		let cursor = consumer
6168			.scope("", &scopes(&["room/a", "other/deep/head"]))
6169			.unwrap()
6170			.announced();
6171		let route = producer.announce("room/a/b/c", Route::default()).unwrap();
6172		{
6173			let table = producer.shared.lock();
6174			assert!(table.routes.root.find(Path::new("room/a/b/c").parts()).is_some());
6175			assert!(table.routes.root.find(Path::new("other/deep/head").parts()).is_some());
6176			assert_eq!(table.routes.root.cursors_below, 2);
6177		}
6178
6179		drop(route);
6180		drop(cursor);
6181		let table = producer.shared.lock();
6182		assert!(table.routes.root.is_empty());
6183		assert_eq!(table.routes.root.cursors_below, 0);
6184	}
6185
6186	/// A session handed an `origin::Producer` drops it once it has its own
6187	/// handles, so the driver must keep running while a published broadcast
6188	/// lives, and finish once the last one is gone.
6189	#[test]
6190	fn a_published_broadcast_keeps_the_driver_running() {
6191		let (producer, mut driver) = Producer::new(Config::new(origin(1)));
6192		let waiter = kio::Waiter::noop();
6193		let broadcast = producer.create_broadcast("room/a").unwrap();
6194		drop(producer);
6195		assert!(
6196			driver.poll(Instant::now(), &waiter).is_ok(),
6197			"the broadcast is lifecycle work"
6198		);
6199		drop(broadcast);
6200		assert!(matches!(driver.poll(Instant::now(), &waiter), Err(Error::Closed)));
6201	}
6202
6203	#[test]
6204	fn watch_wakes_only_for_covering_changes() {
6205		let producer = origin(1).produce();
6206		let waiter = kio::Waiter::noop();
6207		let watch = producer.shared.lock().watch(&producer.shared, &Path::new("room/a"));
6208		let seen = watch.seen();
6209
6210		// A route beside the path or beneath it covers nothing at the path.
6211		let _other = producer.announce("other", Route::default()).unwrap();
6212		let _below = producer.announce("room/a/b", Route::default()).unwrap();
6213		assert!(watch.poll_changed(&waiter, seen).is_pending());
6214
6215		// A route above it does, and so does its retraction.
6216		let above = producer.announce("room", Route::default()).unwrap();
6217		assert!(watch.poll_changed(&waiter, seen).is_ready());
6218		let seen = watch.seen();
6219		drop(above);
6220		assert!(watch.poll_changed(&waiter, seen).is_ready());
6221		let seen = watch.seen();
6222
6223		// A local broadcast attaching at the exact path does; one beside it does not.
6224		let _beside = producer.create_broadcast("room/b").unwrap();
6225		assert!(watch.poll_changed(&waiter, seen).is_pending());
6226		let _here = producer.create_broadcast("room/a").unwrap();
6227		assert!(watch.poll_changed(&waiter, seen).is_ready());
6228
6229		// Dropping the watch takes it out of the table.
6230		drop(watch);
6231		let table = producer.shared.lock();
6232		let node = table
6233			.routes
6234			.root
6235			.find(Path::new("room/a").parts())
6236			.expect("route below keeps the node");
6237		assert!(node.watches.is_empty());
6238		assert_eq!(table.routes.root.watches_below, 0);
6239	}
6240
6241	#[test]
6242	fn a_discarded_front_task_unregisters_its_watch() {
6243		let (producer, _driver) = Producer::new(Config {
6244			hop: origin(1),
6245			..Default::default()
6246		});
6247		let consumer = producer.consume();
6248		let _served = producer.dynamic("room", Route::default()).unwrap();
6249		// A consumer outlives its producer by design, so the task set can refuse
6250		// submissions while the origin is still open. The front's task is then
6251		// dropped on the spot, taking its `Watch` with it: the request must not
6252		// still be holding the table lock the watch unregisters under.
6253		drop(producer);
6254		let _pending = consumer.request_broadcast("room/a");
6255	}
6256
6257	#[test]
6258	fn create_broadcast_refuses_a_path_no_pattern_can_spell() {
6259		let producer = origin(1).produce();
6260
6261		// A `*` segment is a valid path but an invalid literal, so its route could
6262		// never be built: refuse the broadcast instead of publishing one that
6263		// announces nowhere.
6264		assert!(matches!(
6265			producer.create_broadcast("room/*"),
6266			Err(Error::InvalidPath(_))
6267		));
6268		assert!(matches!(
6269			producer.announce("room/**", Route::default()),
6270			Err(Error::InvalidPath(_))
6271		));
6272	}
6273
6274	#[test]
6275	fn scope_empty_union_grants_nothing() {
6276		let producer = origin(1).produce();
6277
6278		// An empty union grants nothing: scoping is refused, like a disjoint prefix.
6279		assert!(matches!(producer.scope("", &Patterns::new()), Err(Error::Unauthorized)));
6280		assert!(matches!(
6281			producer.consume().scope("", &Patterns::new()),
6282			Err(Error::Unauthorized)
6283		));
6284	}
6285
6286	#[test]
6287	fn scope_nests_and_rebases_roots() {
6288		let producer = origin(1).produce();
6289
6290		// Narrowing twice intersects; the grant stays in the new vocabulary.
6291		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
6292		let nested = scoped.scope("", &scopes(&["room/chat"])).unwrap();
6293		assert_eq!(nested.allowed(), scopes(&["room/chat"]));
6294
6295		// A disjoint nesting is refused, not widened.
6296		assert!(matches!(
6297			scoped.scope("", &scopes(&["other"])),
6298			Err(Error::Unauthorized)
6299		));
6300
6301		// A literal root rebases the grant without changing its meaning.
6302		let rooted = nested.scope("room/chat", &Patterns::from(Pattern::all())).unwrap();
6303		assert_eq!(rooted.allowed(), scopes(&[""]));
6304
6305		// Publishing through the nested view lands where the root says.
6306		let broadcast = nested.create_broadcast("room/chat/live").unwrap();
6307		assert!(producer.consume().get_broadcast("room/chat/live").is_some());
6308		broadcast.finish();
6309	}
6310
6311	#[test]
6312	fn scope_intersects_and_rebases_arbitrary_grants() {
6313		let producer = origin(1).produce();
6314		let rooms = producer
6315			.scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
6316			.unwrap();
6317		let chats = rooms
6318			.scope("", &Patterns::from("*/chat".parse::<Pattern>().unwrap()))
6319			.unwrap();
6320		assert_eq!(chats.allowed(), Patterns::from("room/chat".parse::<Pattern>().unwrap()));
6321
6322		let exact = producer
6323			.scope("", &Patterns::from("room/alice".parse::<Pattern>().unwrap()))
6324			.unwrap();
6325		let rooted = exact.scope("room", &Patterns::from(Pattern::all())).unwrap();
6326		assert_eq!(rooted.allowed(), Patterns::from("alice".parse::<Pattern>().unwrap()));
6327		assert!(matches!(
6328			exact.scope("room/bob", &Patterns::from(Pattern::all())),
6329			Err(Error::Unauthorized)
6330		));
6331
6332		let broadcast = exact.create_broadcast("room/alice").unwrap();
6333		assert!(matches!(
6334			exact.create_broadcast("room/alice/cam"),
6335			Err(Error::Unauthorized)
6336		));
6337		assert!(producer.consume().get_broadcast("room/alice").is_some());
6338		drop(broadcast);
6339	}
6340
6341	#[tokio::test]
6342	async fn wildcard_scope_filters_announcements_and_reports_captures() {
6343		let producer = origin(1).produce();
6344		let consumer = producer
6345			.consume()
6346			.scope("", &Patterns::from("room/*/chat".parse::<Pattern>().unwrap()))
6347			.unwrap();
6348		let mut announced = consumer.announced();
6349
6350		let alice = producer.create_broadcast("room/alice/chat").unwrap();
6351		alice.announce(Route::default()).unwrap();
6352		let update = announced.try_next().expect("alice's chat");
6353		assert_eq!(update.prefix.as_str(), "room/alice/chat");
6354		assert_eq!(update.captures, Some(vec!["alice".parse::<Pattern>().unwrap()]));
6355
6356		let audio = producer.create_broadcast("room/alice/audio").unwrap();
6357		audio.announce(Route::default()).unwrap();
6358		announced.assert_next_wait();
6359
6360		let broad = producer.announce("room", Route::default()).unwrap();
6361		let update = announced.try_next().expect("overlapping broad route");
6362		assert_eq!(update.prefix.as_str(), "room");
6363		assert_eq!(update.captures, None, "an overlap does not pin the wildcard");
6364
6365		drop(broad);
6366		drop(audio);
6367		drop(alice);
6368	}
6369
6370	#[tokio::test]
6371	async fn local_broadcast_wins_announcement_ties() {
6372		let producer = origin(1).produce();
6373		let remote = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
6374		let local = producer.create_broadcast("room/alice").unwrap();
6375		local.announce(Route::default()).unwrap();
6376
6377		let mut announced = producer.consume().announced();
6378		let update = announced.try_next().expect("one winning route");
6379		assert_eq!(update.prefix.as_str(), "room/alice");
6380		assert_eq!(update.route.cost, Cost::default());
6381		announced.assert_next_wait();
6382
6383		drop(local);
6384		drop(remote);
6385	}
6386
6387	/// Charging a link accumulates onto both halves, saturating rather than wrapping
6388	/// so a bogus peer sorts last, not first. The ceiling is the largest cost a
6389	/// varint can carry, so whatever a peer advertises, the sum we forward still
6390	/// encodes.
6391	#[test]
6392	fn cost_charge_saturates() {
6393		assert_eq!(Cost { warm: 4, cold: 6 }.charged(5), Cost { warm: 9, cold: 11 });
6394		assert_eq!(Cost::new(u64::MAX).charged(10), Cost::new(MAX_COST));
6395
6396		// An unknown cold path stays unknown however many links it crosses, so it
6397		// can never accumulate its way into outranking a path we actually know.
6398		assert_eq!(Cost::UNKNOWN.charged(3).cold, MAX_COST);
6399	}
6400
6401	/// Mint an origin whose pool reclaims idle content after `expiry`.
6402	fn expiring_origin(expiry: Duration) -> Producer {
6403		let pool = cache::Pool::new(cache::Config::default().with_expiry(expiry));
6404		Config {
6405			pool,
6406			..Config::default()
6407		}
6408		.produce()
6409	}
6410
6411	/// A publisher that stalls with a group still open runs no write path, so the
6412	/// track's own write-driven expiry never fires and a reader parked in that group
6413	/// is never told. The driver's wall-clock sweep is the bound.
6414	#[tokio::test(start_paused = true)]
6415	async fn stalled_publisher_open_group_is_reclaimed() {
6416		let expiry = Duration::from_secs(1);
6417		let origin = expiring_origin(expiry);
6418		let broadcast = origin.create_broadcast("test").unwrap();
6419		let track = broadcast.create_track("video", None).unwrap();
6420
6421		let mut stalled = track.append_group().unwrap();
6422		stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
6423		// A successor, so the stalled group is not the protected live edge. Its
6424		// timestamp is inside the retention budget, so subscription expiry keeps the
6425		// stalled group: only reclamation can bound it.
6426		let _successor = track.append_group().unwrap();
6427
6428		let mut reading = stalled.consume();
6429		assert!(reading.read_frame().await.unwrap().is_some());
6430
6431		// Production goes quiet: nothing writes to this track again.
6432		crate::model::clock::advance(expiry * 2);
6433
6434		// Bounded so a regression fails rather than parking forever, which is the
6435		// bug itself. Time is virtual, so the wait costs nothing.
6436		let reclaimed = tokio::time::timeout(Duration::from_secs(60), reading.read_frame()).await;
6437		assert!(
6438			matches!(reclaimed, Ok(Err(Error::Old))),
6439			"the sweep must reclaim an idle open group and surface the gap, got {reclaimed:?}"
6440		);
6441	}
6442
6443	/// Reclamation is the pool's policy, not the origin's: a pool with no expiry
6444	/// window keeps idle content until byte pressure takes it, sweep or no sweep.
6445	#[tokio::test(start_paused = true)]
6446	async fn sweep_respects_a_disabled_expiry() {
6447		let origin = Config {
6448			pool: cache::Pool::unbounded(),
6449			..Config::default()
6450		}
6451		.produce();
6452		let broadcast = origin.create_broadcast("test").unwrap();
6453		let track = broadcast.create_track("video", None).unwrap();
6454
6455		let mut stalled = track.append_group().unwrap();
6456		stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
6457		let _successor = track.append_group().unwrap();
6458
6459		let mut reading = stalled.consume();
6460		assert!(reading.read_frame().await.unwrap().is_some());
6461
6462		crate::model::clock::advance(Duration::from_secs(3600));
6463		tokio::time::advance(Duration::from_secs(3600)).await;
6464
6465		assert!(
6466			reading.read_frame().now_or_never().is_none(),
6467			"a pool without an expiry window never reclaims"
6468		);
6469	}
6470
6471	/// A draining cost still has to fit the wire, since the route keeps being
6472	/// announced downstream while it drains.
6473	#[test]
6474	fn drain_cost_is_encodable() {
6475		use crate::coding::Encode;
6476
6477		let mut buf = Vec::new();
6478		Cost::DRAIN
6479			.encode(&mut buf, crate::lite::Version::Lite06)
6480			.expect("a draining route is still forwarded, so its cost must encode");
6481	}
6482}