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 hop length, then a
463	/// deterministic hash, and finally the most recently announced route. See [`Cost`].
464	pub cost: Cost,
465
466	/// The announcing session's declared or assigned identity.
467	///
468	/// Local selection state: split-horizon matches this as well as the chain, so a
469	/// route is never advertised back to the session it came from even when that
470	/// session withheld an identity (hop 0). Never forwarded.
471	pub(crate) via: Hop,
472}
473
474impl Default for Route {
475	fn default() -> Self {
476		Self {
477			hops: Hops::new(),
478			cost: Cost::default(),
479			via: Hop::UNKNOWN,
480		}
481	}
482}
483
484impl Route {
485	/// Replace the hop chain.
486	pub fn with_hops(mut self, hops: Hops) -> Self {
487		self.hops = hops;
488		self
489	}
490
491	/// Set the cost: lower wins among routes covering the same prefix and anonymity.
492	///
493	/// A bare `u64` prices the route undiscounted (both halves of [`Cost`] alike),
494	/// which is what a publisher seeding its production cost means.
495	pub fn with_cost(mut self, cost: impl Into<Cost>) -> Self {
496		self.cost = cost.into();
497		self
498	}
499
500	/// The announcing session's declared or assigned identity, for split-horizon.
501	///
502	/// Not part of the advertised route: an assigned identity is private selection
503	/// state and must not be forwarded.
504	pub(crate) fn with_via(mut self, via: Hop) -> Self {
505		self.via = via;
506		self
507	}
508
509	/// Whether this route passed through an anonymous hop.
510	///
511	/// True when the chain holds a 0 anywhere, including Lite03 hop-count
512	/// placeholders. An anonymous route ranks below every fully identified one,
513	/// whatever the costs say. An empty chain is a local announcement, not the
514	/// anonymous mark; ingress fills a received empty list with 0 before it
515	/// enters the table.
516	pub fn is_anonymous(&self) -> bool {
517		self.hops.iter().any(|hop| *hop == Hop::UNKNOWN)
518	}
519}
520
521static NEXT_CONSUMER_ID: AtomicU64 = AtomicU64::new(0);
522
523#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
524struct ConsumerId(u64);
525
526impl ConsumerId {
527	fn new() -> Self {
528		Self(NEXT_CONSUMER_ID.fetch_add(1, Ordering::Relaxed))
529	}
530}
531
532/// FNV-1a over a path and a sequence of origin ids.
533///
534/// FNV-1a, not the std hasher: its output is fixed across Rust versions and
535/// builds, which matters when nodes run mismatched binaries during a rolling
536/// deploy and still need to agree on the same route. SEED is a custom basis
537/// (any nonzero u64 works, the textbook one is just as arbitrary); FNV_PRIME is
538/// the standard FNV-64 prime and should stay put. Mixing the path in spreads
539/// equal routes across different upstreams rather than funneling onto one.
540fn fnv_key(name: &str, origins: impl IntoIterator<Item = Hop>) -> u64 {
541	const SEED: u64 = 0x420C0DECB00B; // 420 C0DEC B00B
542	const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
543
544	let mut hash = SEED;
545	for &byte in name.as_bytes() {
546		hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
547	}
548	for origin in origins {
549		for &byte in &origin.id().to_le_bytes() {
550			hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
551		}
552	}
553
554	hash
555}
556
557/// Ordering key for a route entry covering one prefix. Lower wins: an identified
558/// chain (no 0) outranks an anonymous one regardless of cost, then the cheapest
559/// cost, then the shortest hop chain, then a deterministic hash of the prefix and
560/// chain so every node converges on the same winner, and finally the newest
561/// announcement, so a reconnect under an otherwise identical route wins the
562/// moment it lands instead of after the transport retires the old session.
563fn route_order(prefix: &Path, entry: &RouteEntry) -> (bool, Cost, usize, u64, Reverse<u64>) {
564	(
565		entry.is_anonymous(),
566		entry.cost,
567		entry.hops.len(),
568		fnv_key(prefix.as_str(), entry.hops.iter().copied()),
569		Reverse(entry.id),
570	)
571}
572
573/// The `(hops, cost)` metadata an announce cursor delivers alongside a prefix.
574type RouteMeta = (Hops, Cost);
575
576/// One coalesced update queued for an `AnnounceConsumer`.
577///
578/// At most one entry exists per prefix, so a slow consumer's pending set is
579/// bounded by the number of distinct prefixes. A metadata change on a live route
580/// overwrites the pending `Announce` (or is delivered as another active update),
581/// while `UnannounceAnnounce` preserves a real retract-then-announce sequence.
582type AnnounceMeta = (RouteMeta, Option<Vec<Pattern>>);
583
584enum PendingUpdate {
585	Announce(AnnounceMeta),
586	Unannounce(AnnounceMeta),
587	UnannounceAnnounce { old: AnnounceMeta, new: AnnounceMeta },
588}
589
590/// Pending updates keyed by prefix. `BTreeMap` keeps memory strictly bounded by
591/// the number of distinct prefixes with outstanding work (collapsed pairs are
592/// fully erased) and gives a deterministic lexicographic delivery order so
593/// tests can predict it.
594#[derive(Default)]
595struct OriginConsumerState {
596	pending: BTreeMap<PathOwned, PendingUpdate>,
597	/// Prefixes whose most recently delivered update was an announce. A pending
598	/// `Announce` is ambiguous on its own: it is an unseen initial announce (a
599	/// retraction cancels it entirely) or a metadata update on a route the
600	/// consumer already observed (a retraction must still be delivered).
601	delivered: BTreeSet<PathOwned>,
602	/// Set by the origin's teardown: the cursor drains `pending`, then reports
603	/// the end instead of parking forever on a table that can never fire again.
604	ended: bool,
605}
606
607impl OriginConsumerState {
608	fn apply_announce(&mut self, prefix: PathOwned, meta: RouteMeta, captures: Option<Vec<Pattern>>) {
609		let meta = (meta, captures);
610		let new = match self.pending.remove(&prefix) {
611			// First announce, a stale announce being replaced, or a metadata update.
612			None | Some(PendingUpdate::Announce(_)) => PendingUpdate::Announce(meta),
613			// Consumer needs to observe the retraction before this announce.
614			Some(PendingUpdate::Unannounce(old) | PendingUpdate::UnannounceAnnounce { old, .. }) => {
615				PendingUpdate::UnannounceAnnounce { old, new: meta }
616			}
617		};
618		self.pending.insert(prefix, new);
619	}
620
621	fn apply_unannounce(&mut self, prefix: PathOwned, last: RouteMeta, captures: Option<Vec<Pattern>>) {
622		let last = (last, captures);
623		match self.pending.remove(&prefix) {
624			// The pending announce was never delivered and neither was any earlier
625			// one, so the pair cancels entirely.
626			Some(PendingUpdate::Announce(_)) if !self.delivered.contains(&prefix) => {}
627			// Either nothing is pending or the pending announce was a metadata
628			// update on a delivered route; the consumer still owes a retraction.
629			None | Some(PendingUpdate::Announce(_) | PendingUpdate::Unannounce(_)) => {
630				self.pending.insert(prefix, PendingUpdate::Unannounce(last));
631			}
632			// The embedded announce cancels with this retraction; the consumer still
633			// needs the leading one.
634			Some(PendingUpdate::UnannounceAnnounce { old, .. }) => {
635				self.pending.insert(prefix, PendingUpdate::Unannounce(old));
636			}
637		}
638	}
639
640	/// Take one update to deliver to the consumer, if any.
641	fn take(&mut self) -> Option<AnnounceUpdate> {
642		let prefix = self.pending.keys().next()?.clone();
643		let ((meta, captures), kind) = match self.pending.remove(&prefix).unwrap() {
644			PendingUpdate::Announce(meta) => {
645				// The consumer has seen this prefix before, so it is a metadata update.
646				let kind = match self.delivered.insert(prefix.clone()) {
647					true => AnnounceKind::Announced,
648					false => AnnounceKind::Updated,
649				};
650				(meta, kind)
651			}
652			PendingUpdate::Unannounce(meta) => {
653				self.delivered.remove(&prefix);
654				(meta, AnnounceKind::Retracted)
655			}
656			PendingUpdate::UnannounceAnnounce { old, new } => {
657				// Deliver the retraction now; leave the trailing announce pending so
658				// the next take returns it for the same prefix.
659				self.delivered.remove(&prefix);
660				self.pending.insert(prefix.clone(), PendingUpdate::Announce(new));
661				(old, AnnounceKind::Retracted)
662			}
663		};
664		Some(AnnounceUpdate {
665			prefix,
666			captures,
667			route: Route {
668				hops: meta.0,
669				cost: meta.1,
670				via: Hop::UNKNOWN,
671			},
672			kind,
673		})
674	}
675}
676
677/// One announced route in the origin's table, absolute prefix.
678struct RouteEntry {
679	id: u64,
680	prefix: PathOwned,
681	/// The absolute patterns the announcing producer may serve. The prefix is
682	/// only the wire-visible covering claim; this scope remains authoritative.
683	scope: Patterns,
684	hops: Hops,
685	cost: Cost,
686	/// The announcing session's declared or assigned identity. Split-horizon
687	/// matches this as well as [`Self::hops`], so an anonymous hop 0 still
688	/// cannot echo back to the session it came from.
689	via: Hop,
690	/// Whether this is an origin-owned broadcast, which wins announcement ties
691	/// just as it wins exact request resolution.
692	local: bool,
693	/// The queue requests under this route are served from, when the announcer
694	/// serves content on demand (a [`Dynamic`]). `None` for an advertise-only
695	/// announcement ([`Producer::announce`]) and for a local broadcast.
696	server: Option<kio::Shared<ServeState>>,
697	/// The broadcast published on this origin at exactly `prefix`, when the
698	/// entry is one: requests resolve to it directly, and the newest one at a
699	/// path wins through [`route_order`].
700	source: Option<broadcast::Consumer>,
701	/// Whether peer announce cursors see the entry. Local cursors also see an
702	/// origin-owned broadcast before its producer advertises it.
703	advertised: bool,
704	/// [`prefix_claim`] of [`Self::prefix`], built once at announce time.
705	///
706	/// The announce sync evaluates a route's claim once per (cursor, route) pair,
707	/// and building one allocates a segment vector and a canonical string. Holding
708	/// it makes that visit a comparison.
709	claim: Pattern,
710}
711
712impl RouteEntry {
713	fn is_anonymous(&self) -> bool {
714		self.hops.iter().any(|hop| *hop == Hop::UNKNOWN)
715	}
716
717	/// Whether a request for `path` can be served through this entry. A served
718	/// route covers everything beneath its prefix; a broadcast published here
719	/// is only itself, so it serves its exact path and shadows what is beneath.
720	fn serves(&self, path: &Path) -> bool {
721		self.server.is_some() || (self.source.is_some() && self.prefix == *path)
722	}
723
724	/// Whether `pin` admits this entry for a front's selection.
725	fn qualifies(&self, pin: Pin) -> bool {
726		match pin {
727			Pin::Any => true,
728			Pin::Local => self.local,
729			Pin::Publisher(first) => self.hops.iter().next() == Some(&first),
730			Pin::None => false,
731		}
732	}
733
734	/// Whether this entry may be observed or served to a requester excluding `peer`.
735	///
736	/// A non-zero peer is hidden when it is the announcing session (`via`) or
737	/// appears in the chain. Hop 0 identifies nobody, so it is never excluded.
738	fn visible_to(&self, exclude: Option<Hop>) -> bool {
739		match exclude {
740			Some(peer) if peer != Hop::UNKNOWN => self.via != peer && !self.hops.contains(&peer),
741			_ => true,
742		}
743	}
744
745	/// Whether this route and `allowed` share any path beneath the advertised prefix.
746	fn overlaps(&self, allowed: &Patterns) -> bool {
747		self.scope.iter().any(|scope| {
748			scope
749				.intersect(&self.claim)
750				.is_ok_and(|scoped| scoped.iter().any(|restriction| allowed.overlaps(restriction)))
751		})
752	}
753}
754
755/// The paths a prefix can cover, using an exact pattern at the path depth limit.
756fn prefix_claim(prefix: &Path) -> Result<Pattern, InvalidPattern> {
757	if prefix.parts().count() == Path::MAX_PARTS {
758		Pattern::literal(prefix.as_str())
759	} else {
760		Pattern::subtree(prefix.as_str())
761	}
762}
763
764/// A served route's request queue: what materializes a requested path on demand.
765///
766/// Shared by every requester resolving through the owning route and the
767/// [`Dynamic`] draining it, so both sides work under one lock.
768#[derive(Default)]
769struct ServeState {
770	// Result channels for pending requests, keyed by absolute path so concurrent
771	// `request_broadcast` calls for the same path coalesce onto one channel.
772	requests: Requests<PathOwned, kio::Producer<PendingBroadcast>>,
773
774	// Broadcasts the handler has already served, kept weakly so a repeat request for the
775	// same path resolves to a shared clone instead of re-invoking the handler (which would
776	// open a duplicate upstream subscription). Weak so a served broadcast still closes once
777	// its real consumers drop. The cache reclaims closed entries incrementally on insert, so a
778	// long-lived origin serving many distinct one-shot paths stays bounded by the live count.
779	served: WeakCache<PathOwned, broadcast::WeakConsumer>,
780
781	// Set when the announcement is retracted or the origin tears down: new requests
782	// fail immediately and the handler observes the end instead of parking forever.
783	closed: bool,
784}
785
786/// Key of a remotely-served front: the absolute path and the requester's
787/// split-horizon exclusion. Requesters excluding different peers get separate
788/// fronts, so a front's failover never adopts a route flowing back through one
789/// of its own readers.
790type FrontKey = (PathOwned, Option<Hop>);
791
792/// One remotely-served front in [`OriginState::fronts`]: the shared spliced
793/// broadcast at a path plus the channel requesters resolve through.
794#[derive(Clone)]
795struct RemoteFront {
796	/// Resolves requesters with the front's consumer (or the error that ended it
797	/// unresolved). The producer lives here so the teardown can reject requesters
798	/// still parked on a front whose watcher was cancelled.
799	request: kio::Producer<PendingBroadcast>,
800	/// The front's spliced broadcast, weak: dead once the front ends, so a
801	/// later request re-creates the front instead of joining a corpse.
802	broadcast: broadcast::WeakConsumer,
803}
804
805/// The last route a cursor observed: entry id, metadata, servability, and captures.
806type CursorRoute = (u64, RouteMeta, bool, Option<Vec<Pattern>>);
807
808impl WeakEntry for RemoteFront {
809	fn is_closed(&self) -> bool {
810		self.broadcast.is_closed()
811	}
812
813	fn same_channel(&self, other: &Self) -> bool {
814		self.broadcast.same_channel(&other.broadcast)
815	}
816}
817
818/// One registered announce cursor: which patterns it may see, how prefixes are
819/// re-rooted, and the per-cursor delivery buffer.
820struct TableCursor {
821	/// The prefix stripped from every delivered path.
822	root: PathOwned,
823	/// The absolute patterns this cursor is scoped to (its token / scope).
824	allowed: Patterns,
825	/// Where the cursor hangs in the [`RouteTable`]: the literal heads of
826	/// `allowed`. A route the cursor can see sits at or under one of them, or on
827	/// the walk down to one.
828	heads: Vec<PathOwned>,
829	/// Skip routes whose hop chain or announcing session (`via`) is this peer
830	/// (control-plane split horizon).
831	exclude: Option<Hop>,
832	/// The delivery buffer, drained by the cursor's `poll_next`.
833	state: kio::Producer<OriginConsumerState>,
834	/// The last delivered best route per presented (relative) prefix, for change
835	/// detection: `(entry id, hops, cost)`.
836	// entry id, metadata, and whether the entry could serve requests: the last
837	// is part of the dedupe key (see `sync_cursor`) but never leaves the model.
838	current: HashMap<PathOwned, CursorRoute>,
839}
840
841impl TableCursor {
842	/// Where `prefix` presents on this cursor, named relative to the cursor root.
843	/// The prefix stays a prefix; the pattern scope only decides visibility.
844	/// `claim` is the prefix's [`prefix_claim`], which the caller already holds:
845	/// building one allocates, and the sweeps below ask this per route per
846	/// cursor.
847	fn presented(&self, prefix: &Path, claim: &Pattern) -> Option<PathOwned> {
848		if !self.allowed.overlaps(claim) {
849			return None;
850		}
851
852		if let Some(relative) = prefix.strip_prefix(&self.root) {
853			return Some(relative.to_owned());
854		}
855		self.root.has_prefix(prefix).then(PathOwned::default)
856	}
857
858	/// What the cursor's most specific matching scope member captures from an
859	/// exact announced prefix. An overlap-only route does not pin every wildcard.
860	fn captures(&self, prefix: &Path) -> Option<Vec<Pattern>> {
861		let literal = Pattern::literal(prefix.as_str()).ok()?;
862		self.allowed
863			.iter()
864			.filter_map(|allowed| {
865				allowed
866					.captures(&literal)
867					.map(|captures| (allowed.specificity(), captures))
868			})
869			.max_by_key(|(specificity, _)| *specificity)
870			.map(|(_, captures)| captures)
871	}
872
873	/// Local broadcasts are discoverable here before peer advertisement.
874	/// Peer cursors carry an exclusion, including `Hop::UNKNOWN` for peers
875	/// without a declared identity.
876	fn visible(&self, entry: &RouteEntry) -> bool {
877		(entry.advertised || (entry.local && self.exclude.is_none()))
878			&& entry.visible_to(self.exclude)
879			&& entry.overlaps(&self.allowed)
880	}
881}
882
883/// A handle's view of an origin: the absolute patterns it may reach.
884#[derive(Clone)]
885struct OriginScope {
886	// The paths this handle may reach, absolute.
887	allowed: Patterns,
888}
889
890impl OriginScope {
891	/// A view that reaches nothing.
892	fn empty() -> Self {
893		Self {
894			allowed: Patterns::new(),
895		}
896	}
897
898	/// This view narrowed to the absolute `patterns`: the paths in both.
899	fn narrow(&self, patterns: &Patterns) -> Option<Self> {
900		let allowed = self.allowed.intersect(patterns).ok()?;
901		if allowed.is_empty() {
902			None
903		} else {
904			Some(Self { allowed })
905		}
906	}
907
908	/// Whether this view reaches the absolute `path`.
909	fn permits(&self, path: &Path) -> bool {
910		self.allowed.matches(path.as_str())
911	}
912
913	/// What this view reaches, named from `root`.
914	fn relative(&self, root: &Path) -> Patterns {
915		self.allowed.rebase(root.as_str())
916	}
917}
918
919impl Default for OriginScope {
920	fn default() -> Self {
921		Self {
922			allowed: Patterns::from(Pattern::all()),
923		}
924	}
925}
926
927/// The announce-interest prefixes that cover a pattern scope on a prefix-only
928/// wire: each member's literal head, minus heads another already covers.
929pub(crate) fn interest_prefixes(allowed: &Patterns) -> Vec<PathOwned> {
930	let mut heads: Vec<PathOwned> = allowed
931		.iter()
932		.map(|pattern| Path::new(pattern.head()).to_owned())
933		.collect();
934	heads.sort();
935	heads.dedup();
936	let covered = heads.clone();
937	heads.retain(|head| !covered.iter().any(|other| other != head && head.has_prefix(other)));
938	heads
939}
940
941/// What an [`AnnounceUpdate`] reports about its path.
942#[derive(Clone, Copy, Debug, PartialEq, Eq)]
943pub enum AnnounceKind {
944	/// A route now covers the path; the cursor had none there.
945	Announced,
946	/// The route covering the path changed hops or cost; it is delivered in place.
947	Updated,
948	/// No route covers the path any more.
949	Retracted,
950}
951
952impl AnnounceKind {
953	/// Whether a route covers the path after this update.
954	pub fn is_active(self) -> bool {
955		!matches!(self, Self::Retracted)
956	}
957}
958
959/// A route announcement, update, or retraction, delivered by [`AnnounceConsumer`].
960///
961/// An announcement is always a prefix, never a broadcast: it advertises that
962/// [`prefix`](Self::prefix) and every path beneath it are servable. A broadcast
963/// announces its own path, so the prefix usually names one, but resolve it with
964/// [`Consumer::request_broadcast`]; the application decides which paths name
965/// broadcasts, and filters with a [`Pattern`] locally when it wants a subset.
966#[derive(Clone, Debug)]
967pub struct AnnounceUpdate {
968	/// The prefix the route covers, relative to the consuming cursor's root.
969	pub prefix: PathOwned,
970	/// What the scope's wildcards stood for when the announced prefix pins all of
971	/// them. `None` for an overlap-only route or a scope without a complete match.
972	pub captures: Option<Vec<Pattern>>,
973	/// The route serving the prefix. On a retraction this carries its last
974	/// advertised metadata.
975	pub route: Route,
976	/// Whether the prefix was announced, re-priced, or retracted.
977	pub kind: AnnounceKind,
978}
979
980/// Publishes broadcasts and announces routes into an origin.
981#[derive(Clone)]
982pub struct Producer {
983	// Identity for this origin. Appended to route hops when re-announcing so
984	// downstream relays can detect loops and prefer the shortest path.
985	hop: Hop,
986
987	// The absolute patterns this handle may publish under.
988	scope: OriginScope,
989
990	// The prefix that is automatically stripped from all paths.
991	root: PathOwned,
992
993	// The origin's shared state: the route table, announce cursors, and the
994	// remotely-served fronts. Shared with every derived consumer.
995	shared: kio::Shared<OriginState>,
996
997	// The cache pool inherited by broadcasts created under this origin (sessions
998	// mint their remote broadcasts with it). Unbounded by default.
999	pool: cache::Pool,
1000
1001	// Retention ceiling inherited by broadcasts created under this origin (see
1002	// [`Config::cache_duration`]). `Duration::MAX` (no ceiling) by default.
1003	cache_duration: Duration,
1004
1005	// Retention window for a track whose publisher advertises none (see
1006	// [`Config::default_max_age`]).
1007	default_max_age: Duration,
1008
1009	// Ingress stats context. Broadcasts created through this producer are attributed
1010	// to it (writes counted on the subscriber/ingress side). Empty (no-op) unless a
1011	// session tagged this handle via [`Self::with_stats`].
1012	stats: stats::Session,
1013
1014	// Submission handle to the origin's [`Driver`]: source watchers, fronts, and
1015	// serve tasks queued here run when the driver is polled. Closed once the
1016	// driver drops, which is what makes later mutations fail with `Closed`.
1017	tasks: Tasks,
1018
1019	// The clock advanced by the origin driver.
1020	timers: Clock,
1021}
1022
1023impl Producer {
1024	/// Build a producer from a [`Config`] (identity + cache pool) with no scoped
1025	/// prefix and no pre-existing broadcasts, paired with the [`Driver`] that runs
1026	/// the origin's lifecycle work.
1027	///
1028	/// Poll the driver with caller-supplied time for the origin to make progress.
1029	/// `moq_tokio::origin::spawn` wraps this for tokio callers.
1030	pub fn new(config: Config) -> (Self, Driver) {
1031		let (tasks, set) = TaskSet::new();
1032		let scope = OriginScope::default();
1033		let shared = kio::Shared::<OriginState>::default();
1034		let timers = Clock::default();
1035		let pool = config.pool.clone();
1036		let producer = Self {
1037			hop: config.hop,
1038			scope: scope.clone(),
1039			root: PathOwned::default(),
1040			shared: shared.clone(),
1041			pool: config.pool,
1042			cache_duration: config.cache_duration,
1043			default_max_age: config.default_max_age,
1044			stats: stats::Session::default(),
1045			tasks,
1046			timers: timers.clone(),
1047		};
1048		let driver = Driver {
1049			state: DriverState {
1050				set,
1051				shared,
1052				done: false,
1053			},
1054			timers,
1055			pool,
1056		};
1057		(producer, driver)
1058	}
1059
1060	/// Attach an ingress stats context: broadcasts created through this handle (and
1061	/// any handle derived from it) are attributed to `session` on the subscriber
1062	/// (ingress) side. Pass [`stats::Session::default`] to opt out.
1063	pub fn with_stats(mut self, session: stats::Session) -> Self {
1064		self.stats = session;
1065		self
1066	}
1067
1068	/// This origin's construction config.
1069	pub fn config(&self) -> Config {
1070		Config {
1071			hop: self.hop,
1072			pool: self.pool.clone(),
1073			cache_duration: self.cache_duration,
1074			default_max_age: self.default_max_age,
1075		}
1076	}
1077
1078	/// This origin's hop identity.
1079	pub fn hop(&self) -> Hop {
1080		self.hop
1081	}
1082
1083	// The retention window for a track whose publisher advertises none (see
1084	// [`Config::default_max_age`]). Cheaper than `config()`, which clones the pool.
1085	pub(crate) fn default_max_age(&self) -> Duration {
1086		self.default_max_age
1087	}
1088
1089	/// A producer with *no* allowed prefixes: it can't publish anything and
1090	/// advertises no subscribe interest (its `allowed()` is empty, so the
1091	/// subscriber issues no ANNOUNCE_PLEASE). Used to fill an unset session half
1092	/// so both the publisher and subscriber loops still run.
1093	pub(crate) fn empty(hop: Hop) -> Self {
1094		// No allowed prefixes means no broadcast is ever created, so nothing will
1095		// ever be queued on the detached submission handle.
1096		let (tasks, _) = TaskSet::new();
1097		Self {
1098			hop,
1099			scope: OriginScope::empty(),
1100			root: PathOwned::default(),
1101			shared: kio::Shared::default(),
1102			pool: cache::Pool::default(),
1103			cache_duration: Duration::MAX,
1104			default_max_age: track::DEFAULT_MAX_AGE,
1105			stats: stats::Session::default(),
1106			tasks,
1107			timers: Clock::default(),
1108		}
1109	}
1110
1111	/// Create a broadcast at `path`, fed through the returned producer.
1112	///
1113	/// This is how local content enters an origin. The returned
1114	/// [`broadcast::Producer`] is a source: the origin owns the broadcast
1115	/// consumers actually see, and splices its tracks across every source created
1116	/// at the same path, preferring the newest. When the serving source changes,
1117	/// tracks resume from the replacement at the first missing group; consumers
1118	/// never observe the swap.
1119	///
1120	/// The path appears on this origin's local announce cursors immediately.
1121	/// It reaches peers only after [`broadcast::Producer::announce`]. A local
1122	/// consumer can discover and request it without any wire advertisement.
1123	///
1124	/// The broadcast is visible to exact lookups before this returns; only
1125	/// lifecycle work (track serving, teardown) waits for the [`Driver`] to be
1126	/// polled. Register a [`broadcast::Producer::dynamic`] handler right away, so
1127	/// the first consumer finds the tracks it serves.
1128	///
1129	/// End the broadcast with [`broadcast::Producer::finish`]; dropping it
1130	/// without finishing also works, but logs a warning. Either way the path
1131	/// closes once it was the last source; an unfinished drop additionally aborts
1132	/// the spliced tracks with an error, so consumers observe a failure rather
1133	/// than a clean end.
1134	///
1135	/// Fails with [`Error::Unauthorized`] if `path` is outside the prefixes this
1136	/// producer may publish under (after [`scope`](Self::scope)),
1137	/// [`Error::BoundsExceeded`] if the full rooted path exceeds
1138	/// [`Path::MAX_PARTS`], [`Error::InvalidPath`] if it holds a segment no
1139	/// pattern can spell (`*` or `**`), or [`Error::Closed`] once the origin's
1140	/// [`Driver`] has been dropped.
1141	pub fn create_broadcast(&self, path: impl AsPath) -> Result<broadcast::Producer, Error> {
1142		let path = path.as_path();
1143
1144		let full = self.root.join(&path).to_owned();
1145		if !self.scope.permits(&full) {
1146			return Err(Error::Unauthorized);
1147		}
1148		// A decoded prefix and suffix are each within the wire limit, but their
1149		// join might not be. Enforcing here bounds the table depth and guarantees the
1150		// path can be re-encoded when forwarded.
1151		if full.parts().count() > Path::MAX_PARTS {
1152			return Err(BoundsExceeded.into());
1153		}
1154		// A path only a pattern could spell (a `*` segment) advertises nowhere, so
1155		// refuse it here rather than publish a broadcast no cursor can see.
1156		let claim = prefix_claim(&full)?;
1157
1158		// Resolve the ingress counters once, keyed by the absolute broadcast path.
1159		let ingress = self.stats.ingress(&full);
1160
1161		// The broadcast is a route table entry at its exact path from the start,
1162		// so requests resolve to it and the newest publisher at a path wins;
1163		// local cursors see it immediately. The entry lives as long as the
1164		// broadcast: its announcer drops on finish, abort, or the last handle.
1165		let announcing = Announcing {
1166			hop: self.hop,
1167			shared: self.shared.clone(),
1168			requested: full.clone(),
1169			prefixes: vec![(full.clone(), claim)],
1170			scope: self.scope.allowed.clone(),
1171			local: true,
1172			stats: self.stats.clone(),
1173		};
1174		let info = broadcast::Info {
1175			pool: self.pool.clone(),
1176			cache_duration: self.cache_duration,
1177			path: full,
1178		};
1179		let source = info.produce().with_stats(ingress);
1180		let entry = announcing.announce(
1181			Route::default(),
1182			Serving {
1183				server: None,
1184				source: Some(source.consume()),
1185				advertised: false,
1186			},
1187		)?;
1188		Ok(source.with_announcer(Announcer {
1189			entry,
1190			_keepalive: self.tasks.keepalive(),
1191		}))
1192	}
1193
1194	/// Create and advertise a broadcast in one call.
1195	pub fn publish(&self, path: impl AsPath, route: Route) -> Result<broadcast::Producer, Error> {
1196		let broadcast = self.create_broadcast(path)?;
1197		broadcast.announce(route)?;
1198		Ok(broadcast)
1199	}
1200
1201	/// Mint a standalone source broadcast for a served-route request: it carries
1202	/// this origin's cache policy and ingress attribution, but
1203	/// is *not* entered into the route table. Sessions answer
1204	/// [`Dynamic`] requests with one of these; the requester already holds
1205	/// the request's result channel, so the table never needs to resolve it.
1206	pub(crate) fn create_source(&self, path: impl AsPath) -> broadcast::Producer {
1207		let path = path.as_path();
1208		let full = self.root.join(&path).to_owned();
1209		let ingress = self.stats.ingress(&full);
1210		broadcast::Info {
1211			pool: self.pool.clone(),
1212			cache_duration: self.cache_duration,
1213			path: full,
1214		}
1215		.produce()
1216		.with_stats(ingress)
1217	}
1218
1219	/// Advertise a route without serving it: a claim that paths under `prefix`
1220	/// can be served, answered by nothing.
1221	///
1222	/// A request under an advertise-only route resolves [`Error::Unroutable`]
1223	/// unless a local broadcast or a served route ([`Self::dynamic`]) covers the
1224	/// path too. Tests use it to shape the route table; everything else
1225	/// advertises through a broadcast ([`broadcast::Producer::announce`]) or a
1226	/// [`Dynamic`] handler, which serve what they claim.
1227	#[cfg(test)]
1228	pub(crate) fn announce(&self, prefix: impl AsPath, route: Route) -> Result<AnnounceProducer, Error> {
1229		Announcing::new(self, prefix)?.announce(
1230			route,
1231			Serving {
1232				server: None,
1233				source: None,
1234				advertised: true,
1235			},
1236		)
1237	}
1238
1239	/// Advertise a route over `prefix` and serve the requests beneath it.
1240	///
1241	/// A route is always a prefix: it claims `prefix` and every path beneath it
1242	/// (the empty prefix claims every path). A service that only serves some of
1243	/// them, say `pid/*.hang`, advertises the covering prefix and refuses the
1244	/// rest as they are requested; consumers narrow with a [`Pattern`] locally.
1245	/// This is the one shape every wire carries, so a route means the same
1246	/// thing on every hop.
1247	///
1248	/// The advertisement is visible to [`Consumer::announced`] and forwarded by
1249	/// sessions for as long as the returned [`Dynamic`] (and every clone) lives.
1250	/// A consumer resolving a path under it that no local broadcast covers is
1251	/// handed to the handler as a [`Request`] to materialize on demand. This is
1252	/// how a service answers a whole subtree without publishing each path, and
1253	/// how sessions land the routes a peer announces to them; a publisher that
1254	/// knows its broadcasts advertises each one's exact path with
1255	/// [`broadcast::Producer::announce`] instead, so subscribers can enumerate
1256	/// them.
1257	///
1258	/// The prefix must overlap this producer's pattern scope. Individual requests
1259	/// remain authoritative and are refused when they do not match the scope.
1260	pub fn dynamic(&self, prefix: impl AsPath, route: Route) -> Result<Dynamic, Error> {
1261		let announcing = Announcing::new(self, prefix)?;
1262		let serve = kio::Shared::<ServeState>::default();
1263		serve.lock().requests.add_handler();
1264		let announcement = announcing.announce(
1265			route,
1266			Serving {
1267				server: Some(serve.clone()),
1268				source: None,
1269				advertised: true,
1270			},
1271		)?;
1272		Ok(Dynamic {
1273			announcement,
1274			state: serve,
1275		})
1276	}
1277
1278	/// Returns a producer rooted at `root` and restricted to matching `patterns`.
1279	///
1280	/// `root` is relative to this producer's root, and `patterns` are relative to
1281	/// the new root. Returns [`Error::Unauthorized`] when the requested scope has
1282	/// no overlap with this producer's scope, or [`Error::BoundsExceeded`] when
1283	/// rooting the patterns would exceed the path limit.
1284	pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Producer, Error> {
1285		let root = self.root.join(root).to_owned();
1286		let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
1287		let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
1288		Ok(Producer {
1289			hop: self.hop,
1290			scope,
1291			root,
1292			shared: self.shared.clone(),
1293			pool: self.pool.clone(),
1294			cache_duration: self.cache_duration,
1295			default_max_age: self.default_max_age,
1296			stats: self.stats.clone(),
1297			tasks: self.tasks.clone(),
1298			timers: self.timers.clone(),
1299		})
1300	}
1301
1302	/// Cheap read handle over this origin's route table.
1303	///
1304	/// Use [`Consumer::announced`] to register interest and start receiving
1305	/// announcement events; the consumer itself does not allocate any channels.
1306	pub fn consume(&self) -> Consumer {
1307		// Untagged: a session tags the egress consumer separately via
1308		// `origin::Consumer::with_stats` (ingress and egress are distinct sides).
1309		Consumer::from_producer(self, stats::Session::default())
1310	}
1311
1312	/// Returns the root that is automatically stripped from all paths.
1313	pub fn root(&self) -> &Path<'_> {
1314		&self.root
1315	}
1316
1317	/// The patterns this producer may publish under, relative to its root.
1318	pub fn allowed(&self) -> Patterns {
1319		self.scope.relative(&self.root)
1320	}
1321
1322	/// Converts a relative path to an absolute path.
1323	pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
1324		self.root.join(path)
1325	}
1326}
1327
1328/// What it takes to insert a route: the prefixes it covers and the origin table
1329/// to insert them into. Built by [`Producer::announce`], [`Producer::dynamic`],
1330/// and [`Announcer`], which is the same advertisement re-issued from a broadcast.
1331struct Announcing {
1332	hop: Hop,
1333	shared: kio::Shared<OriginState>,
1334	/// The absolute advertised prefix, which also keys the ingress announce counters.
1335	requested: PathOwned,
1336	/// The prefix inserted into the table, with its [`prefix_claim`]. Pattern
1337	/// scopes decide visibility and request authorization without changing the
1338	/// route's prefix shape.
1339	prefixes: Vec<(PathOwned, Pattern)>,
1340	/// The absolute paths the producer is authorized to serve.
1341	scope: Patterns,
1342	local: bool,
1343	stats: stats::Session,
1344}
1345
1346impl Announcing {
1347	/// The requested prefix as-is, refused when its subtree does not overlap the scope.
1348	fn new(producer: &Producer, prefix: impl AsPath) -> Result<Self, Error> {
1349		let requested = producer.root.join(prefix.as_path()).to_owned();
1350		if requested.parts().count() > Path::MAX_PARTS {
1351			return Err(BoundsExceeded.into());
1352		}
1353		let claim = prefix_claim(&requested)?;
1354		if !producer.scope.allowed.overlaps(&claim) {
1355			return Err(Error::Unauthorized);
1356		}
1357		Ok(Self {
1358			hop: producer.hop,
1359			shared: producer.shared.clone(),
1360			requested: requested.clone(),
1361			prefixes: vec![(requested, claim)],
1362			scope: producer.scope.allowed.clone(),
1363			local: false,
1364			stats: producer.stats.clone(),
1365		})
1366	}
1367
1368	fn announce(&self, route: Route, serving: Serving) -> Result<AnnounceProducer, Error> {
1369		debug_assert!(
1370			!route.hops.contains(&self.hop),
1371			"announce called with a looping hop chain",
1372		);
1373
1374		let via = route.via;
1375		let meta: RouteMeta = (route.hops, route.cost);
1376
1377		let mut shared = self.shared.lock();
1378		if shared.closed {
1379			return Err(Error::Closed);
1380		}
1381
1382		let mut entries = Vec::with_capacity(self.prefixes.len());
1383		for (prefix, claim) in &self.prefixes {
1384			let id = shared.next_route;
1385			shared.next_route += 1;
1386			shared.routes.insert(RouteEntry {
1387				id,
1388				prefix: prefix.clone(),
1389				scope: self.scope.clone(),
1390				hops: meta.0.clone(),
1391				cost: meta.1,
1392				via,
1393				local: self.local,
1394				server: serving.server.clone(),
1395				source: serving.source.clone(),
1396				advertised: serving.advertised,
1397				claim: claim.clone(),
1398			});
1399			shared.sync_route(prefix, claim);
1400			entries.push((prefix.clone(), id));
1401		}
1402		drop(shared);
1403
1404		// Ingress announce guard: held for the advertisement's lifetime.
1405		let guard = self.stats.ingress(&self.requested).announce();
1406
1407		Ok(AnnounceProducer {
1408			shared: self.shared.clone(),
1409			entries,
1410			_guard: guard,
1411		})
1412	}
1413}
1414
1415/// What a route entry serves and whether cursors see it.
1416struct Serving {
1417	server: Option<kio::Shared<ServeState>>,
1418	source: Option<broadcast::Consumer>,
1419	advertised: bool,
1420}
1421
1422/// The table entry a broadcast owns: its exact path, advertised and withdrawn
1423/// through [`broadcast::Producer::announce`] and
1424/// [`broadcast::Producer::unannounce`], and removed when the broadcast ends.
1425///
1426/// Handed to the broadcast by [`Producer::create_broadcast`], so a standalone
1427/// broadcast has none and cannot announce.
1428pub(crate) struct Announcer {
1429	entry: AnnounceProducer,
1430	/// A published broadcast is lifecycle work: the origin's driver keeps
1431	/// running for as long as one lives, even once every producer handle is
1432	/// gone, so a session handed a producer can drop it and keep serving.
1433	_keepalive: Keepalive,
1434}
1435
1436impl Announcer {
1437	/// Advertise the broadcast's path with `route`, or re-price it in place.
1438	pub(crate) fn announce(&mut self, route: Route) -> Result<(), Error> {
1439		self.entry.update(route)
1440	}
1441
1442	/// Withdraw peer advertising while retaining local discovery.
1443	pub(crate) fn withdraw(&mut self) {
1444		self.entry.withdraw();
1445	}
1446}
1447
1448/// The write half of an advertisement: a live claim that paths under a
1449/// [`Pattern`] can be served.
1450///
1451/// Held by a [`Dynamic`] and by a broadcast's [`Announcer`]; dropping it
1452/// retracts the route, which [`AnnounceConsumer`]s observe and sessions withdraw
1453/// from their peers.
1454#[must_use = "dropping an announcement retracts the route"]
1455pub(crate) struct AnnounceProducer {
1456	shared: kio::Shared<OriginState>,
1457	/// The table entries this advertisement created, by prefix and id. A prefix
1458	/// remains unchanged; pattern scopes only filter its visibility and requests.
1459	entries: Vec<(PathOwned, u64)>,
1460	/// Ingress announce stats guard, held for the advertisement's lifetime.
1461	_guard: stats::Announce,
1462}
1463
1464impl AnnounceProducer {
1465	/// Re-price the route in place: replace its hops and cost.
1466	///
1467	/// Consumers observe another active update for the same prefix; sessions
1468	/// forward it as a restart, so route churn never looks like new content. The
1469	/// prefix is fixed at announce time and a [`Route`] cannot name one: to move
1470	/// an advertisement, drop this and announce again. Fails with
1471	/// [`Error::Closed`] once the origin's [`Driver`] has been dropped.
1472	pub fn update(&self, route: Route) -> Result<(), Error> {
1473		let mut shared = self.shared.lock();
1474		if shared.closed {
1475			return Err(Error::Closed);
1476		}
1477		for (prefix, id) in &self.entries {
1478			// Each entry keeps its advertised prefix; only the metadata moves.
1479			let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1480				return Err(Error::Closed);
1481			};
1482			entry.hops = route.hops.clone();
1483			entry.cost = route.cost;
1484			entry.via = route.via;
1485			entry.advertised = true;
1486			let claim = entry.claim.clone();
1487			shared.sync_route(prefix, &claim);
1488		}
1489		Ok(())
1490	}
1491
1492	/// Withdraw a local broadcast from peers while preserving its local route.
1493	fn withdraw(&self) {
1494		let mut shared = self.shared.lock();
1495		for (prefix, id) in &self.entries {
1496			let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1497				continue;
1498			};
1499			if !entry.advertised {
1500				continue;
1501			}
1502			entry.advertised = false;
1503			entry.hops = Hops::default();
1504			entry.cost = Cost::default();
1505			entry.via = Hop::UNKNOWN;
1506			let claim = entry.claim.clone();
1507			shared.sync_route(prefix, &claim);
1508		}
1509	}
1510
1511	/// Retract the route now: remove its table entries and reject anything still
1512	/// waiting on its queue. Idempotent, and what dropping the advertisement does.
1513	fn retract(&self) {
1514		let mut shared = self.shared.lock();
1515		for (prefix, id) in &self.entries {
1516			let Some(entry) = shared.routes.remove(prefix, *id) else {
1517				continue;
1518			};
1519			// Reject anything still waiting on this route's server; a request
1520			// already handed to the handler resolves through its own `Request`.
1521			if let Some(server) = &entry.server {
1522				let mut server = server.lock();
1523				server.closed = true;
1524				for producer in server.requests.drain_all() {
1525					if let Ok(mut request) = producer.write() {
1526						request.resolved.get_or_insert(Err(Error::Unroutable));
1527					}
1528				}
1529			}
1530			shared.sync_route(&entry.prefix, &entry.claim);
1531		}
1532	}
1533}
1534
1535impl Drop for AnnounceProducer {
1536	fn drop(&mut self) {
1537		self.retract();
1538	}
1539}
1540
1541/// Drives origin lifecycle work and cache expiration with caller-supplied time.
1542///
1543/// Returned by [`Producer::new`]. Poll on external activity or at the deadline
1544/// it returns, supplying nondecreasing instants. Route changes, track serving, linger,
1545/// failover, and teardown run here; exact lookups and eligible announcements
1546/// update synchronously in [`Producer::create_broadcast`].
1547///
1548/// It holds no [`Producer`] clone, so it never keeps the origin alive. Dropping
1549/// it aborts active fronts, rejects pending requests, ends announcements, and
1550/// makes subsequent producer mutations fail with [`Error::Closed`].
1551/// `moq_tokio::origin::spawn` handles construction and driving for Tokio callers.
1552#[must_use = "poll the driver or the origin makes no progress"]
1553pub struct Driver {
1554	state: DriverState,
1555	// Shared by this origin's lifecycle tasks; advanced only when polled.
1556	timers: Clock,
1557	// The cache pool this origin's groups charge into, swept on a wall-clock
1558	// cadence so its idle window binds a track whose publisher stopped writing.
1559	pool: cache::Pool,
1560}
1561
1562/// Lifecycle work and the state it tears down.
1563struct DriverState {
1564	/// The front drivers: producers submit, this polls.
1565	set: TaskSet,
1566	/// The route table, announce cursors, and the remotely-served fronts, for
1567	/// ending everything on drop.
1568	shared: kio::Shared<OriginState>,
1569	/// Cached completion so a poll after `Ready` doesn't re-poll the drained set.
1570	done: bool,
1571}
1572
1573impl Driver {
1574	/// Process ready origin work using caller-supplied monotonic time.
1575	///
1576	/// See [`crate::time::Driver`] for the contract. Finishes with
1577	/// [`Error::Closed`] once every producer handle has dropped and the
1578	/// remaining lifecycle work has drained.
1579	pub fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1580		self.timers.advance(now);
1581		let result = self.state.poll(waiter);
1582		let gc = self.pool.gc(now);
1583		if result.is_ready() {
1584			return Err(Error::Closed);
1585		}
1586		Ok(self.timers.timeout().into_iter().chain(gc).min())
1587	}
1588}
1589
1590impl crate::time::Driver for Driver {
1591	fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1592		self.poll(now, waiter)
1593	}
1594}
1595
1596impl DriverState {
1597	fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
1598		// Never gates completion: the pool outlives this origin (a relay shares one
1599		// across every origin), so a sweep that is still due must not keep the driver
1600		// alive after its lifecycle work has drained.
1601		if !self.done {
1602			ready!(self.set.poll(waiter));
1603			self.done = true;
1604		}
1605		Poll::Ready(())
1606	}
1607
1608	/// Tear the origin down: cancel the lifecycle work, abort and unpublish every
1609	/// front, retract every route, end announcement cursors, and reject pending
1610	/// requests.
1611	fn teardown(&mut self) {
1612		// Cancel queued and running lifecycle work first, so no front serves
1613		// while the table is ended below.
1614		drop(std::mem::replace(&mut self.set, TaskSet::owned()));
1615
1616		// Refuse new work and take the pending requests, under the same lock
1617		// `create_broadcast` holds across its attach: a concurrent create either
1618		// finishes before this (the walk below cleans its entry up) or observes
1619		// `closed` and fails with `Closed`.
1620		let (servers, cursors, fronts) = {
1621			let mut shared = self.shared.lock();
1622			shared.closed = true;
1623			// Fronts and parked requesters observe `closed` on their next pass.
1624			shared.routes.poke_all();
1625			let servers: Vec<_> = shared
1626				.routes
1627				.entries()
1628				.filter_map(|entry| entry.server.clone())
1629				.collect();
1630			let cursors: Vec<_> = shared.cursors.values().map(|cursor| cursor.state.clone()).collect();
1631			let fronts: Vec<_> = shared.fronts.values().map(|front| front.request.clone()).collect();
1632			(servers, cursors, fronts)
1633		};
1634
1635		// Reject requesters still parked on a remote front's channel: its watcher
1636		// was cancelled above and will never resolve them.
1637		for producer in fronts {
1638			if let Ok(mut request) = producer.write() {
1639				request.resolved.get_or_insert(Err(Error::Dropped));
1640			}
1641		}
1642		// Reject every pending route request, including those already handed to a
1643		// handler: the teardown is terminal, so a handler resolving late must not
1644		// beat it (resolution is first-write-wins).
1645		for server in servers {
1646			let mut server = server.lock();
1647			server.closed = true;
1648			for producer in server.requests.drain_all() {
1649				if let Ok(mut request) = producer.write() {
1650					request.resolved.get_or_insert(Err(Error::Dropped));
1651				}
1652			}
1653		}
1654
1655		// End the announce cursors: each drains its pending updates, then reports
1656		// the end. Registrations stay (the cursors remove themselves on drop).
1657		for state in cursors {
1658			if let Ok(mut state) = state.write() {
1659				state.ended = true;
1660			}
1661		}
1662	}
1663}
1664
1665impl Drop for DriverState {
1666	fn drop(&mut self) {
1667		self.teardown();
1668	}
1669}
1670
1671/// How long a spliced track stays warm after its last reader leaves.
1672///
1673/// Within the window a returning viewer, or the next of a run of back-to-back
1674/// fetches, reads the groups the front already cached: no second round trip for
1675/// `TRACK_INFO`. Groups past that cached edge cost a fresh source splice. After
1676/// the window, the cached segment is released.
1677///
1678/// Sized above the fetch cadence of a segmented consumer: HLS polls every
1679/// `TARGETDURATION` seconds, commonly 6 or 10, so a shorter window would drop the
1680/// copy between every segment and re-request the track each time. A warm copy
1681/// holds no upstream subscription (that is canceled as soon as demand ends), so
1682/// waiting longer costs cached state, not a viewer.
1683const TRACK_IDLE_LINGER: Duration = Duration::from_secs(30);
1684
1685/// A local copy of groups the front already delivered, so resume stays spliced
1686/// after the source track is dropped. Cache misses stay pending while demand
1687/// re-splices the upstream source. Finished on drop so an idle linger does not
1688/// warn about an abandoned producer.
1689struct WarmCopy {
1690	track: track::Producer,
1691	_dynamic: track::Dynamic,
1692}
1693
1694impl Drop for WarmCopy {
1695	fn drop(&mut self) {
1696		let _ = self.track.finish();
1697	}
1698}
1699
1700/// Cache `source`'s finished groups on a new local track the origin owns.
1701fn warm_copy(source: &track::Consumer) -> Option<WarmCopy> {
1702	let info = source.cached_info()?;
1703	let mut track = track::Producer::new(Arc::new(source.broadcast().clone()), source.name(), info);
1704	for (group, visible) in source.cached_groups() {
1705		// An open group is left for the re-splice to deliver whole. Dropping the source
1706		// copy resets it mid-transfer, and its dead head would anchor the next takeover
1707		// mid-group, asking upstream for a tail no returning reader can use.
1708		if group.is_finished() {
1709			let _ = track.adopt_group(group, visible);
1710		}
1711	}
1712	let dynamic = track.dynamic();
1713	Some(WarmCopy {
1714		track,
1715		_dynamic: dynamic,
1716	})
1717}
1718
1719/// Everything [`run_front`] owns, queued by [`Consumer::request_broadcast`].
1720struct FrontTask {
1721	/// The route table the front selects from.
1722	shared: kio::Shared<OriginState>,
1723	/// The spliced broadcast the front serves.
1724	broadcast: broadcast::Producer,
1725	/// Absolute path of the front.
1726	path: PathOwned,
1727	/// The requesters' split-horizon exclusion, applied to every (re)selection.
1728	exclude: Option<Hop>,
1729	/// Wakes the front when a route covering its path changes.
1730	watch: Watch,
1731	/// Resolves the requesters parked on the front's channel.
1732	request: kio::Producer<PendingBroadcast>,
1733	timers: Clock,
1734}
1735
1736/// The driver's side of one logical track: the handles behind the names the
1737/// machine uses.
1738struct TrackIo {
1739	resume: super::resume::Producer,
1740	/// The copy whose info resolved, waiting for the machine to splice it.
1741	staged: Option<(u64, track::Consumer)>,
1742	/// A query in flight: the source asked, its copy, and the pending info.
1743	query: Option<(u64, track::Consumer, track::Querying)>,
1744	/// The spliced copy: its source and the track.
1745	copy: Option<(u64, track::Consumer)>,
1746	/// The delivered edge when the copy spliced in: a copy that dies without
1747	/// advancing it delivered nothing. Snapshotted per splice, not per wake, so an
1748	/// unrelated wake between the copy's last frame and its death cannot launder
1749	/// its progress away.
1750	edge: Option<track::Position>,
1751	/// Delivered groups kept after the copy was dropped, so resume stays spliced
1752	/// through the linger without pinning the source as a reader.
1753	warm: Option<WarmCopy>,
1754	/// Whether the track had a reader as of the last demand edge.
1755	used: bool,
1756}
1757
1758/// Drives one front: feeds the world's events to a [`Front`] and performs the
1759/// actions it returns, until the front ends. The decisions live in the machine;
1760/// this only waits and executes, so nothing here decides anything twice.
1761async fn run_front(task: FrontTask) {
1762	let FrontTask {
1763		shared,
1764		broadcast,
1765		path,
1766		exclude,
1767		watch,
1768		request,
1769		timers,
1770	} = task;
1771
1772	/// What the wait below returns: one thing that happened.
1773	enum Step {
1774		Assigned(Arc<str>, super::resume::Producer),
1775		Resolved(u64, Result<broadcast::Consumer, Error>),
1776		SourceClosed(u64),
1777		Info(Arc<str>, u64, Result<track::Info, Error>),
1778		Ended(Arc<str>, u64, Result<(), Error>),
1779		Demand(Arc<str>),
1780		Deadline,
1781		Table,
1782	}
1783
1784	let mut front = Front::new(TRACK_IDLE_LINGER);
1785	let mut sources: HashMap<u64, broadcast::Consumer> = HashMap::new();
1786	let mut next_source = 0u64;
1787	// The in-flight upstream request: the route and its pending channel.
1788	let mut upstream: Option<(u64, kio::Consumer<PendingBroadcast>)> = None;
1789	let mut tracks: HashMap<Arc<str>, TrackIo> = HashMap::new();
1790	let mut deadline = crate::runtime::Deadline::new(&timers);
1791	// The watch generation the last selection saw.
1792	let mut seen = 0;
1793	let mut events: VecDeque<Event> = VecDeque::new();
1794
1795	// Read the table for the machine: the best qualifying route and whether
1796	// the serving source is on its way out. Also what the watch wakes for.
1797	let select = |front: &mut Front, sources: &HashMap<u64, broadcast::Consumer>, seen: &mut u64| -> Event {
1798		let table = shared.read();
1799		if table.closed {
1800			return Event::Closed;
1801		}
1802		// Read alongside the decision, under the lock a poke takes first.
1803		*seen = watch.seen();
1804		front.retain_routes(|route| table.routes.covers(&path.as_path(), route));
1805		let best = table
1806			.best_route(&path.as_path(), exclude, front.pin(), front.refused_routes())
1807			.map(|entry| Candidate {
1808				route: entry.id,
1809				first: entry.hops.iter().next().copied(),
1810				local: entry.local,
1811			});
1812		let serving_closing = front
1813			.serving()
1814			.and_then(|id| sources.get(&id))
1815			.is_some_and(|source| source.is_closing());
1816		Event::Selected { best, serving_closing }
1817	};
1818
1819	events.push_back(select(&mut front, &sources, &mut seen));
1820
1821	loop {
1822		while let Some(event) = events.pop_front() {
1823			for action in front.step(event) {
1824				match action {
1825					Action::Reselect => events.push_back(select(&mut front, &sources, &mut seen)),
1826					Action::Request { route } => {
1827						// The entry, its identity for the front, and what it serves.
1828						let found = {
1829							let table = shared.read();
1830							table
1831								.routes
1832								.covering(&path.as_path())
1833								.find(|entry| entry.id == route)
1834								.map(|entry| {
1835									(
1836										Candidate {
1837											route,
1838											first: entry.hops.iter().next().copied(),
1839											local: entry.local,
1840										},
1841										entry.source.clone(),
1842										entry.server.clone(),
1843									)
1844								})
1845						};
1846						let Some((candidate, source, server)) = found else {
1847							events.push_back(Event::Resolved {
1848								route,
1849								result: Err(Refusal {
1850									err: Error::Unroutable,
1851									standing: false,
1852								}),
1853							});
1854							continue;
1855						};
1856						front.identify(candidate);
1857						if let Some(source) = source {
1858							let id = next_source;
1859							next_source += 1;
1860							sources.insert(id, source);
1861							events.push_back(Event::Resolved { route, result: Ok(id) });
1862							continue;
1863						}
1864						let Some(server) = server else {
1865							events.push_back(Event::Resolved {
1866								route,
1867								result: Err(Refusal {
1868									err: Error::Unroutable,
1869									standing: true,
1870								}),
1871							});
1872							continue;
1873						};
1874						let mut serve = server.lock();
1875						if serve.closed {
1876							// Retracted under us, or its handler dropped while the
1877							// announcement stands: it cannot serve.
1878							drop(serve);
1879							events.push_back(Event::Resolved {
1880								route,
1881								result: Err(Refusal {
1882									err: Error::Unroutable,
1883									standing: true,
1884								}),
1885							});
1886							continue;
1887						}
1888						// A source this route already materialized for the path
1889						// attaches without another upstream round trip.
1890						if let Some(weak) = serve.served.get(&path) {
1891							drop(serve);
1892							let id = next_source;
1893							next_source += 1;
1894							sources.insert(id, weak.consume());
1895							events.push_back(Event::Resolved { route, result: Ok(id) });
1896							continue;
1897						}
1898						let pending = match serve.requests.join(&path) {
1899							Some(producer) => producer.consume(),
1900							None => {
1901								let producer = kio::Producer::<PendingBroadcast>::default();
1902								let consumer = producer.consume();
1903								match serve.requests.insert(path.clone(), producer) {
1904									Ok(()) => consumer,
1905									// No live handler behind the route: it cannot
1906									// serve, whatever the table says.
1907									Err(_) => {
1908										drop(serve);
1909										events.push_back(Event::Resolved {
1910											route,
1911											result: Err(Refusal {
1912												err: Error::Unroutable,
1913												standing: true,
1914											}),
1915										});
1916										continue;
1917									}
1918								}
1919							}
1920						};
1921						upstream = Some((route, pending));
1922					}
1923					Action::Detach { source } => {
1924						sources.remove(&source);
1925						// Its copies go with it; the segments they delivered stay
1926						// spliced until a replacement resumes past them.
1927						for io in tracks.values_mut() {
1928							if io.copy.as_ref().is_some_and(|(s, _)| *s == source) {
1929								io.copy = None;
1930							}
1931							if io.query.as_ref().is_some_and(|(s, ..)| *s == source) {
1932								io.query = None;
1933							}
1934							if io.staged.as_ref().is_some_and(|(s, _)| *s == source) {
1935								io.staged = None;
1936							}
1937						}
1938					}
1939					Action::Resolve => {
1940						if let Ok(mut pending) = request.write() {
1941							pending.resolved.get_or_insert(Ok(broadcast.consume()));
1942						}
1943					}
1944					Action::Query { track: name, source } => {
1945						let Some(io) = tracks.get_mut(&name) else { continue };
1946						let closing = sources.get(&source).is_some_and(|s| s.is_closing());
1947						match sources.get(&source).map(|s| s.track(&name)) {
1948							Some(Ok(copy)) => {
1949								// `into_inner` sheds the `Pending` future wrapper so only
1950								// the pollable (which is `Sync`) is held across the wait.
1951								let query = copy.query().into_inner();
1952								io.query = Some((source, copy, query));
1953							}
1954							Some(Err(err)) => events.push_back(Event::TrackInfo {
1955								track: name,
1956								source,
1957								closing,
1958								result: Err(err),
1959							}),
1960							None => {}
1961						}
1962					}
1963					Action::Splice { track: name, source } => {
1964						let Some(io) = tracks.get_mut(&name) else { continue };
1965						let Some((staged, copy)) = io.staged.take() else {
1966							continue;
1967						};
1968						if staged != source {
1969							continue;
1970						}
1971						if let Err(err) = io.resume.takeover(&copy) {
1972							// Closed means the logical track already ended. Anything
1973							// else is a boundary bug; abort rather than strand
1974							// subscribers on a track nobody serves.
1975							let _ = io.resume.abort(err);
1976							tracks.remove(&name);
1977							continue;
1978						}
1979						io.warm = None;
1980						// The new segment has produced nothing yet: this is the
1981						// edge the copy is asked to advance.
1982						io.edge = io.resume.resume_position();
1983						io.copy = Some((source, copy));
1984					}
1985					Action::Park { track: name } => {
1986						let Some(io) = tracks.get_mut(&name) else { continue };
1987						let Some((_, copy)) = io.copy.take() else { continue };
1988						// Drop the source copy so its producer goes idle at once; keep
1989						// the groups it delivered on a local track so resume stays
1990						// spliced until the linger expires.
1991						let warm = warm_copy(&copy);
1992						drop(copy);
1993						if io.resume.release().is_err() {
1994							tracks.remove(&name);
1995							continue;
1996						}
1997						if let Some(warm) = warm {
1998							if let Err(err) = io.resume.takeover(&warm.track) {
1999								let _ = io.resume.abort(err);
2000								tracks.remove(&name);
2001								continue;
2002							}
2003							io.warm = Some(warm);
2004						}
2005					}
2006					Action::Release { track: name } => {
2007						let Some(io) = tracks.get_mut(&name) else { continue };
2008						io.warm = None;
2009						if io.resume.release().is_err() {
2010							tracks.remove(&name);
2011						}
2012					}
2013					Action::Finish { track: name } => {
2014						if let Some(mut io) = tracks.remove(&name) {
2015							let _ = io.resume.finish();
2016						}
2017					}
2018					Action::Abort { track: name, err } => {
2019						if let Some(mut io) = tracks.remove(&name) {
2020							tracing::debug!(name = %name, %err, "aborting track");
2021							let _ = io.resume.abort(err);
2022						}
2023					}
2024					Action::Arm { at } => deadline.set(at),
2025					Action::End { err } => {
2026						if let Ok(mut pending) = request.write() {
2027							pending.resolved.get_or_insert(Err(err.clone()));
2028						}
2029						// Ending the broadcast only retracts it: no new requesters or
2030						// tracks, and a newcomer at the path gets a fresh front. Tracks
2031						// in flight carry on (moq-lite: retraction does not disturb
2032						// subscriptions already in flight): dropping their producers
2033						// leaves each reader on the copy it was spliced from, ending
2034						// when and as that copy ends.
2035						broadcast.finish();
2036						broadcast.release_spliced(err.clone());
2037						for (_, mut io) in tracks.drain() {
2038							// Nothing in flight: unread, never spliced, or only a warm cache.
2039							if !io.resume.is_used() || !io.resume.is_spliced() || io.warm.is_some() {
2040								let _ = io.resume.abort(err.clone());
2041							}
2042						}
2043						return;
2044					}
2045				}
2046			}
2047		}
2048
2049		let step = kio::wait(|waiter| {
2050			if let Poll::Ready((name, resume)) = broadcast.poll_spliced_assigned(waiter) {
2051				return Poll::Ready(Step::Assigned(name, resume));
2052			}
2053			if let Some((route, pending)) = &upstream
2054				&& let Poll::Ready(result) = pending.poll(waiter, |p| match &p.resolved {
2055					Some(result) => Poll::Ready(result.clone()),
2056					None => Poll::Pending,
2057				}) {
2058				return Poll::Ready(Step::Resolved(
2059					*route,
2060					match result {
2061						Ok(resolved) => resolved,
2062						// The queue died unresolved (its handler dropped): the route
2063						// could not serve.
2064						Err(_closed) => Err(Error::Unroutable),
2065					},
2066				));
2067			}
2068			if let Some(id) = front.serving()
2069				&& let Some(source) = sources.get(&id)
2070				&& source.poll_closed(waiter).is_ready()
2071			{
2072				return Poll::Ready(Step::SourceClosed(id));
2073			}
2074			for (name, io) in &tracks {
2075				if let Some((source, _, query)) = &io.query
2076					&& let Poll::Ready(result) = query.poll(waiter)
2077				{
2078					return Poll::Ready(Step::Info(name.clone(), *source, result));
2079				}
2080				if let Some((source, copy)) = &io.copy
2081					&& let Poll::Ready(result) = copy.poll_complete(waiter)
2082				{
2083					return Poll::Ready(Step::Ended(name.clone(), *source, result));
2084				}
2085				// Watch the demand edge in whichever direction is unmet.
2086				let edge = match io.used {
2087					true => io.resume.poll_unused(waiter),
2088					false => io.resume.poll_used(waiter),
2089				};
2090				if edge.is_ready() {
2091					return Poll::Ready(Step::Demand(name.clone()));
2092				}
2093			}
2094			if deadline.poll(waiter).is_ready() {
2095				return Poll::Ready(Step::Deadline);
2096			}
2097			watch.poll_changed(waiter, seen).map(|()| Step::Table)
2098		})
2099		.await;
2100
2101		let event = match step {
2102			Step::Assigned(name, resume) => {
2103				tracks.insert(
2104					name.clone(),
2105					TrackIo {
2106						resume,
2107						staged: None,
2108						query: None,
2109						copy: None,
2110						edge: None,
2111						warm: None,
2112						used: false,
2113					},
2114				);
2115				Event::TrackAssigned { track: name }
2116			}
2117			Step::Resolved(route, result) => {
2118				upstream = None;
2119				match result {
2120					Ok(source) => {
2121						let id = next_source;
2122						next_source += 1;
2123						sources.insert(id, source);
2124						Event::Resolved { route, result: Ok(id) }
2125					}
2126					Err(err) => {
2127						// A retraction and a handler's rejection resolve alike, so
2128						// the table tells them apart: an `Unroutable` from a route
2129						// that still stands is the handler's answer.
2130						let standing =
2131							!matches!(err, Error::Unroutable) || shared.read().routes.covers(&path.as_path(), route);
2132						Event::Resolved {
2133							route,
2134							result: Err(Refusal { err, standing }),
2135						}
2136					}
2137				}
2138			}
2139			Step::SourceClosed(source) => Event::SourceClosed { source },
2140			Step::Info(name, source, result) => {
2141				let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2142				let Some(io) = tracks.get_mut(&name) else { continue };
2143				let Some((_, copy, _)) = io.query.take() else { continue };
2144				// A copy that is already aborted cannot be spliced; its error is
2145				// the source's answer for the track.
2146				let result = match result {
2147					Ok(info) => match copy.poll_complete(&kio::Waiter::noop()) {
2148						Poll::Ready(Err(err)) => Err(err),
2149						_ => Ok(info),
2150					},
2151					Err(err) => Err(err),
2152				};
2153				// Staged only while the track has a reader: without one the machine
2154				// will not splice, and a held copy would keep the source subscribed.
2155				if result.is_ok() && io.used {
2156					io.staged = Some((source, copy));
2157				}
2158				Event::TrackInfo {
2159					track: name,
2160					source,
2161					closing,
2162					result,
2163				}
2164			}
2165			Step::Ended(name, source, result) => {
2166				let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2167				let Some(io) = tracks.get_mut(&name) else { continue };
2168				io.copy = None;
2169				let delivered = io.resume.resume_position() != io.edge;
2170				Event::TrackEnded {
2171					track: name,
2172					source,
2173					closing,
2174					result,
2175					delivered,
2176				}
2177			}
2178			Step::Demand(name) => {
2179				let Some(io) = tracks.get_mut(&name) else { continue };
2180				io.used = io.resume.is_used();
2181				if !io.used {
2182					// Nothing will be spliced now: let go of the copies a query
2183					// holds, or the source stays subscribed with nobody reading.
2184					io.query = None;
2185					io.staged = None;
2186				}
2187				match io.used {
2188					true => Event::Used { track: name },
2189					false => Event::Unused {
2190						track: name,
2191						now: timers.now(),
2192					},
2193				}
2194			}
2195			Step::Deadline => {
2196				// Cleared here so a fired deadline cannot keep firing; the machine
2197				// re-arms what is still parked.
2198				deadline.set(None);
2199				Event::Deadline { now: timers.now() }
2200			}
2201			Step::Table => select(&mut front, &sources, &mut seen),
2202		};
2203		events.push_back(event);
2204	}
2205}
2206
2207/// The announced routes, keyed by prefix: a trie with one node per path
2208/// segment. Every question about a path walks its segments, so the cost of an
2209/// announcement, a cursor registration, or a request is bounded by the tree
2210/// around that path and never by the size of the table.
2211#[derive(Default)]
2212struct RouteTable {
2213	root: RouteNode,
2214}
2215
2216/// One prefix in the [`RouteTable`]: what is announced exactly there, which
2217/// cursors hang there, and the prefixes one segment below.
2218#[derive(Default)]
2219struct RouteNode {
2220	/// Routes announced exactly at this prefix.
2221	entries: Vec<RouteEntry>,
2222	/// Cursors with an interest head at this prefix (see [`interest_prefixes`]).
2223	cursors: Vec<ConsumerId>,
2224	/// Cursors at this node or below. An announcement walks only the subtrees
2225	/// that hold one, so a deep table of routes nobody watches costs nothing.
2226	cursors_below: usize,
2227	/// Who is waiting on the routes covering this prefix: the fronts serving it
2228	/// and the requesters parked on it (see [`Watch`]).
2229	watches: Vec<(u64, kio::Producer<Watched>)>,
2230	/// Watches at this node or below, so a route change walks only the subtrees
2231	/// holding one.
2232	watches_below: usize,
2233	children: HashMap<String, RouteNode>,
2234}
2235
2236/// What a [`Watch`] observes: bumped by every change to a route covering its
2237/// path (a broadcast published here is one) and by the origin's teardown.
2238#[derive(Default)]
2239struct Watched {
2240	generation: u64,
2241}
2242
2243/// A registration in the route table for changes to the routes covering one
2244/// path. The table pokes it; the holder waits on it, so an announcement wakes
2245/// only the fronts and requesters it can affect rather than every one of them.
2246/// Dropping it unregisters, which takes the table lock: never drop one while
2247/// holding it.
2248struct Watch {
2249	shared: kio::Shared<OriginState>,
2250	path: PathOwned,
2251	id: u64,
2252	signal: kio::Consumer<Watched>,
2253}
2254
2255impl Watch {
2256	/// The generation to wait past with [`Self::poll_changed`]. Read under the
2257	/// table lock, alongside the decision it guards, so a poke between the two
2258	/// cannot be missed: a poke takes that same lock first.
2259	fn seen(&self) -> u64 {
2260		self.signal.read().generation
2261	}
2262
2263	/// Ready once the routes covering the path moved past `seen`.
2264	fn poll_changed(&self, waiter: &kio::Waiter, seen: u64) -> Poll<()> {
2265		self.signal
2266			.poll(waiter, |watched| match watched.generation != seen {
2267				true => Poll::Ready(()),
2268				false => Poll::Pending,
2269			})
2270			.map(|_| ())
2271	}
2272}
2273
2274impl Drop for Watch {
2275	fn drop(&mut self) {
2276		self.shared.lock().routes.remove_watch(&self.path, self.id);
2277	}
2278}
2279
2280/// What a registration adds to the subtree counts on its walk.
2281#[derive(Clone, Copy)]
2282struct Below {
2283	cursors: usize,
2284	watches: usize,
2285}
2286
2287impl Below {
2288	const NONE: Self = Self { cursors: 0, watches: 0 };
2289	const CURSOR: Self = Self { cursors: 1, watches: 0 };
2290	const WATCH: Self = Self { cursors: 0, watches: 1 };
2291}
2292
2293impl RouteNode {
2294	/// Nothing here and nothing below: the node can be pruned.
2295	fn is_empty(&self) -> bool {
2296		self.entries.is_empty() && self.cursors.is_empty() && self.watches.is_empty() && self.children.is_empty()
2297	}
2298
2299	/// The node `parts` below this one, if the table has it.
2300	fn find<'a>(&self, mut parts: impl Iterator<Item = &'a str>) -> Option<&Self> {
2301		match parts.next() {
2302			None => Some(self),
2303			Some(part) => self.children.get(part)?.find(parts),
2304		}
2305	}
2306
2307	/// The node `parts` below this one, created along the way when missing.
2308	/// `below` is added to the subtree counts at every node on the walk.
2309	fn reach<'a>(&mut self, mut parts: impl Iterator<Item = &'a str>, below: Below) -> &mut Self {
2310		self.cursors_below += below.cursors;
2311		self.watches_below += below.watches;
2312		match parts.next() {
2313			None => self,
2314			Some(part) => self.children.entry(part.to_string()).or_default().reach(parts, below),
2315		}
2316	}
2317
2318	/// Run `f` on the node `parts` below this one, then prune every node the
2319	/// edit emptied. `below` is subtracted from the subtree counts at every node
2320	/// on the walk. `None` when the node does not exist, leaving the table as is.
2321	fn edit<'a, R>(
2322		&mut self,
2323		mut parts: impl Iterator<Item = &'a str>,
2324		below: Below,
2325		f: impl FnOnce(&mut Self) -> R,
2326	) -> Option<R> {
2327		let result = match parts.next() {
2328			None => f(self),
2329			Some(part) => {
2330				let child = self.children.get_mut(part)?;
2331				let result = child.edit(parts, below, f)?;
2332				if child.is_empty() {
2333					self.children.remove(part);
2334				}
2335				result
2336			}
2337		};
2338		self.cursors_below -= below.cursors;
2339		self.watches_below -= below.watches;
2340		Some(result)
2341	}
2342
2343	/// Wake the watches at this node.
2344	fn poke(&self) {
2345		for (_, watch) in &self.watches {
2346			if let Ok(mut watched) = watch.write() {
2347				watched.generation += 1;
2348			}
2349		}
2350	}
2351
2352	/// Wake the watches at this node and below: a route here covers every one
2353	/// of their paths. Skips subtrees holding none.
2354	fn poke_below(&self) {
2355		if self.watches_below == 0 {
2356			return;
2357		}
2358		self.poke();
2359		for child in self.children.values() {
2360			child.poke_below();
2361		}
2362	}
2363
2364	/// Visit this node and everything below it.
2365	fn walk<'a>(&'a self, visit: &mut impl FnMut(&'a Self)) {
2366		visit(self);
2367		for child in self.children.values() {
2368			child.walk(visit);
2369		}
2370	}
2371
2372	/// Collect the cursors at this node and below, skipping subtrees with none.
2373	fn collect_cursors(&self, out: &mut Vec<ConsumerId>) {
2374		if self.cursors_below == 0 {
2375			return;
2376		}
2377		out.extend(&self.cursors);
2378		for child in self.children.values() {
2379			child.collect_cursors(out);
2380		}
2381	}
2382}
2383
2384impl RouteTable {
2385	/// The nodes above `path` and the node at it, as far as the table has them.
2386	/// The entries of those nodes are exactly the routes covering `path`.
2387	fn split(&self, path: &Path) -> (Vec<&RouteNode>, Option<&RouteNode>) {
2388		let mut above = Vec::new();
2389		let mut node = &self.root;
2390		for part in path.parts() {
2391			above.push(node);
2392			match node.children.get(part) {
2393				Some(child) => node = child,
2394				None => return (above, None),
2395			}
2396		}
2397		(above, Some(node))
2398	}
2399
2400	/// The routes covering `path`: those announced at it and at every prefix of it.
2401	fn covering(&self, path: &Path) -> impl Iterator<Item = &RouteEntry> {
2402		let (above, at) = self.split(path);
2403		above.into_iter().chain(at).flat_map(|node| node.entries.iter())
2404	}
2405
2406	/// Whether the route `id` still covers `path`.
2407	fn covers(&self, path: &Path, id: u64) -> bool {
2408		self.covering(path).any(|entry| entry.id == id)
2409	}
2410
2411	/// The routes announced exactly at `prefix`.
2412	fn at(&self, prefix: &Path) -> impl Iterator<Item = &RouteEntry> {
2413		self.root
2414			.find(prefix.parts())
2415			.into_iter()
2416			.flat_map(|node| node.entries.iter())
2417	}
2418
2419	/// Every route in the table, for the teardown.
2420	fn entries(&self) -> impl Iterator<Item = &RouteEntry> {
2421		let mut nodes = Vec::new();
2422		self.root.walk(&mut |node| nodes.push(node));
2423		nodes.into_iter().flat_map(|node| node.entries.iter())
2424	}
2425
2426	/// Add a route at its prefix, creating the nodes down to it.
2427	fn insert(&mut self, entry: RouteEntry) {
2428		let node = self.root.reach(entry.prefix.parts(), Below::NONE);
2429		node.entries.push(entry);
2430	}
2431
2432	/// The route `id` announced at `prefix`, for a re-price in place.
2433	fn entry_mut(&mut self, prefix: &Path, id: u64) -> Option<&mut RouteEntry> {
2434		let mut node = &mut self.root;
2435		for part in prefix.parts() {
2436			node = node.children.get_mut(part)?;
2437		}
2438		node.entries.iter_mut().find(|entry| entry.id == id)
2439	}
2440
2441	/// Take the route `id` out of `prefix`, pruning the nodes it leaves empty.
2442	fn remove(&mut self, prefix: &Path, id: u64) -> Option<RouteEntry> {
2443		self.root
2444			.edit(prefix.parts(), Below::NONE, |node| {
2445				let index = node.entries.iter().position(|entry| entry.id == id)?;
2446				Some(node.entries.swap_remove(index))
2447			})
2448			.flatten()
2449	}
2450
2451	/// Hang a cursor at one of its heads, counting it down the walk.
2452	fn add_cursor(&mut self, head: &Path, id: ConsumerId) {
2453		self.root.reach(head.parts(), Below::CURSOR).cursors.push(id);
2454	}
2455
2456	/// Take a cursor off one of its heads, pruning the nodes it leaves empty. Only
2457	/// ever called for a head the cursor was added at, or the counts drift.
2458	fn remove_cursor(&mut self, head: &Path, id: ConsumerId) {
2459		self.root.edit(head.parts(), Below::CURSOR, |node| {
2460			node.cursors.retain(|cursor| *cursor != id)
2461		});
2462	}
2463
2464	/// Register a watch on the routes covering `path`; see [`Watch`].
2465	fn add_watch(&mut self, path: &Path, id: u64) -> kio::Consumer<Watched> {
2466		let producer = kio::Producer::<Watched>::default();
2467		let consumer = producer.consume();
2468		self.root.reach(path.parts(), Below::WATCH).watches.push((id, producer));
2469		consumer
2470	}
2471
2472	/// Take a watch off its path, pruning the nodes it leaves empty. Only ever
2473	/// called for a path the watch was added at, or the counts drift.
2474	fn remove_watch(&mut self, path: &Path, id: u64) {
2475		self.root.edit(path.parts(), Below::WATCH, |node| {
2476			node.watches.retain(|(watch, _)| *watch != id)
2477		});
2478	}
2479
2480	/// Wake the watches of every path a route at `prefix` covers.
2481	fn poke_below(&self, prefix: &Path) {
2482		if let (_, Some(node)) = self.split(prefix) {
2483			node.poke_below();
2484		}
2485	}
2486
2487	/// Wake every watch: the origin is tearing down.
2488	fn poke_all(&self) {
2489		self.root.walk(&mut |node| node.poke());
2490	}
2491
2492	/// The cursors a route at `prefix` can present on: a cursor sees a route
2493	/// only when one of its heads is on the walk down to the prefix or somewhere
2494	/// beneath it, so those are the only cursors visited.
2495	fn cursors_touching(&self, prefix: &Path) -> Vec<ConsumerId> {
2496		let (above, at) = self.split(prefix);
2497		let mut cursors: Vec<ConsumerId> = above.iter().flat_map(|node| node.cursors.iter().copied()).collect();
2498		if let Some(node) = at {
2499			node.collect_cursors(&mut cursors);
2500		}
2501		// A cursor with several heads can be reached more than once.
2502		cursors.sort_unstable();
2503		cursors.dedup();
2504		cursors
2505	}
2506}
2507
2508/// The origin's shared state: the route table, the announce cursors observing
2509/// it, and the remotely-served fronts.
2510///
2511/// Carried in a [`kio::Shared`], so producers, consumers, and handlers work
2512/// under one lock. Broadcasts published here are route table entries like the
2513/// routes announced from elsewhere; this holds everything that serves a path.
2514#[derive(Default)]
2515struct OriginState {
2516	// The announced routes, keyed by prefix. The table holds one entry per live
2517	// advertisement, not one per broadcast consumer.
2518	routes: RouteTable,
2519	next_route: u64,
2520	next_watch: u64,
2521
2522	// The registered announce cursors, each with its own coalescing buffer. Each
2523	// also hangs in the route table at its heads, which is how an announcement
2524	// finds the cursors it can present on.
2525	cursors: HashMap<ConsumerId, TableCursor>,
2526
2527	// The remotely-served fronts, keyed by absolute path and the requester's
2528	// split-horizon exclusion. Each is a spliced broadcast whose watcher task
2529	// materializes it from the best covering route and re-splices it through
2530	// routes sharing its first hop, so a route change the identity survives is
2531	// invisible to subscribers. Keyed per exclusion so a front's failover can
2532	// never adopt a route flowing back through one of its own readers. Weak, so
2533	// a front dies with its watcher and a later request re-creates it.
2534	fronts: WeakCache<FrontKey, RemoteFront>,
2535
2536	// Set when the origin's driver dropped: new requests fail with `Closed`
2537	// immediately and handlers observe the end instead of parking forever.
2538	closed: bool,
2539}
2540
2541impl OriginState {
2542	/// Re-deliver the best route at every presented prefix `prefix` maps to, on
2543	/// every cursor it can present on. Called after an entry covering `prefix`
2544	/// was added, updated, or removed. `claim` is `prefix`'s [`prefix_claim`],
2545	/// held by the entry that changed.
2546	fn sync_route(&mut self, prefix: &Path, claim: &Pattern) {
2547		// Split borrows: the recompute reads `routes` while mutating a cursor.
2548		let routes = &self.routes;
2549		for id in routes.cursors_touching(prefix) {
2550			let Some(cursor) = self.cursors.get_mut(&id) else {
2551				continue;
2552			};
2553			if let Some(presented) = cursor.presented(prefix, claim) {
2554				Self::sync_cursor(routes, cursor, &presented);
2555			}
2556		}
2557		// The fronts and requesters under the prefix re-select from the table.
2558		routes.poke_below(prefix);
2559	}
2560
2561	/// Register a [`Watch`] on the routes covering `path`.
2562	fn watch(&mut self, shared: &kio::Shared<OriginState>, path: &Path) -> Watch {
2563		let id = self.next_watch;
2564		self.next_watch += 1;
2565		let signal = self.routes.add_watch(path, id);
2566		Watch {
2567			shared: shared.clone(),
2568			path: path.to_owned(),
2569			id,
2570			signal,
2571		}
2572	}
2573
2574	/// Recompute the best visible route presenting at `presented` (relative) for
2575	/// one cursor and deliver the change, if any.
2576	fn sync_cursor(routes: &RouteTable, cursor: &mut TableCursor, presented: &PathOwned) {
2577		// The entries presenting here are the ones announced at the absolute
2578		// prefix, or, for the cursor's own root, at the root and every prefix
2579		// above it (all of which present as the empty path). Among them, the
2580		// longest prefix wins outright, so the metadata a cursor advertises
2581		// matches what a request through it actually resolves.
2582		let candidates: Vec<&RouteEntry> = match presented.is_empty() {
2583			true => routes
2584				.covering(&cursor.root)
2585				.filter(|entry| cursor.visible(entry))
2586				.collect(),
2587			false => {
2588				let absolute = cursor.root.join(presented);
2589				routes.at(&absolute).filter(|entry| cursor.visible(entry)).collect()
2590			}
2591		};
2592		let most = candidates.iter().map(|entry| entry.prefix.len()).max();
2593		let best = most.and_then(|most| {
2594			candidates
2595				.into_iter()
2596				.filter(|entry| entry.prefix.len() == most)
2597				.min_by_key(|entry| (!entry.local, route_order(&entry.prefix, entry)))
2598		});
2599
2600		match best {
2601			Some(entry) => {
2602				let meta = (entry.hops.clone(), entry.cost);
2603				let served = entry.server.is_some();
2604				let captures = cursor.captures(&entry.prefix);
2605				let previous = cursor
2606					.current
2607					.insert(presented.clone(), (entry.id, meta.clone(), served, captures.clone()));
2608				match previous {
2609					// Unchanged metadata and servability: nothing the consumer could
2610					// act on, even if the winning entry itself changed (a reconnect
2611					// under an identical route is invisible, which is the point). A
2612					// servability flip is delivered: a request that failed Unroutable
2613					// under an advertise-only route retries on the update, and hiding
2614					// it would park that waiter forever.
2615					Some((_, prev, prev_served, prev_captures))
2616						if prev == meta && prev_served == served && prev_captures == captures => {}
2617					// Captures are consumer identity, not route metadata. Replace the
2618					// old identity explicitly so capture-keyed consumers can remove it.
2619					Some((_, prev, _, prev_captures)) if prev_captures != captures => {
2620						if let Ok(mut state) = cursor.state.write() {
2621							state.apply_unannounce(presented.clone(), prev, prev_captures);
2622							state.apply_announce(presented.clone(), meta, captures);
2623						}
2624					}
2625					_ => {
2626						if let Ok(mut state) = cursor.state.write() {
2627							state.apply_announce(presented.clone(), meta, captures);
2628						}
2629					}
2630				}
2631			}
2632			None => {
2633				if let Some((_, last, _, captures)) = cursor.current.remove(presented)
2634					&& let Ok(mut state) = cursor.state.write()
2635				{
2636					state.apply_unannounce(presented.clone(), last, captures);
2637				}
2638			}
2639		}
2640	}
2641
2642	/// Register a cursor and replay the current best route per presented prefix.
2643	fn register_cursor(&mut self, id: ConsumerId, mut cursor: TableCursor) {
2644		// The routes a cursor can see sit on the walk down to one of its heads or
2645		// somewhere beneath it, so only those subtrees are replayed.
2646		let mut presented: BTreeSet<PathOwned> = BTreeSet::new();
2647		for head in &cursor.heads {
2648			let (above, at) = self.routes.split(head);
2649			let mut nodes = above;
2650			if let Some(node) = at {
2651				node.walk(&mut |node| nodes.push(node));
2652			}
2653			for entry in nodes.into_iter().flat_map(|node| node.entries.iter()) {
2654				if let Some(p) = cursor.presented(&entry.prefix, &entry.claim) {
2655					presented.insert(p);
2656				}
2657			}
2658		}
2659		for p in &presented {
2660			Self::sync_cursor(&self.routes, &mut cursor, p);
2661		}
2662		for head in &cursor.heads {
2663			self.routes.add_cursor(head, id);
2664		}
2665		self.cursors.insert(id, cursor);
2666	}
2667
2668	/// The best served route covering `path` (absolute) for a requester excluding
2669	/// `exclude`, skipping the `refused` entry ids.
2670	///
2671	/// The most specific covering prefix wins outright, so a narrow advertise-only
2672	/// announcement shadows a broad served one: requests under it resolve
2673	/// unroutable instead of being routed around it. Among routes at the winning
2674	/// prefix, the cheapest served one is picked by [`route_order`].
2675	///
2676	/// `pin` is the front's identity: only routes it admits are candidates, since
2677	/// a route from anyone else is different content rather than an alternate
2678	/// path (see [`Front`]). A broadcast published on this origin outranks any
2679	/// remote route at the same prefix.
2680	fn best_route(&self, path: &Path, exclude: Option<Hop>, pin: Pin, refused: &HashSet<u64>) -> Option<&RouteEntry> {
2681		// Covering prefixes of one path form a chain, so the deepest node with a
2682		// candidate holds the unique longest prefix; walking down, the last such
2683		// node decides.
2684		let (above, at) = self.routes.split(path);
2685		let mut best = None;
2686		for node in above.into_iter().chain(at) {
2687			let mut candidates = node
2688				.entries
2689				.iter()
2690				.filter(|entry| entry.scope.matches(path.as_str()))
2691				.filter(|entry| entry.visible_to(exclude))
2692				.filter(|entry| entry.qualifies(pin))
2693				.filter(|entry| !refused.contains(&entry.id))
2694				.peekable();
2695			if candidates.peek().is_some() {
2696				best = candidates
2697					.filter(|entry| entry.serves(path))
2698					.min_by_key(|entry| (!entry.local, route_order(&entry.prefix, entry)));
2699			}
2700		}
2701		best
2702	}
2703}
2704
2705/// One-shot result of a dynamic broadcast request.
2706///
2707/// Stays `None` until a handler [`accept`](Request::accept)s (yielding the served
2708/// broadcast) or [`reject`](Request::reject)s (yielding an error). The producer is
2709/// dropped right after writing, closing the channel; kio checks the value before the closed
2710/// flag, so an awaiting requester still observes the final result.
2711#[derive(Default)]
2712struct PendingBroadcast {
2713	resolved: Option<Result<broadcast::Consumer, Error>>,
2714}
2715
2716/// A served route, from [`Producer::dynamic`]: advertises a path prefix and
2717/// answers the [`Consumer::request_broadcast`] calls beneath it.
2718///
2719/// The origin-level analogue of [`broadcast::Dynamic`]: where that serves tracks
2720/// on demand within a broadcast, this serves whole broadcasts on demand within
2721/// an origin. A relay holds one per route a peer announces to it, materializing
2722/// a requested path from that peer; an application holds one to answer a
2723/// subtree it never publishes ahead of time.
2724///
2725/// Drop it to retract the route and reject the requests still waiting to be
2726/// served; [`update`](Self::update) re-prices it in place.
2727#[must_use = "dropping an origin::Dynamic retracts the route"]
2728pub struct Dynamic {
2729	/// The advertisement, retracted on drop.
2730	announcement: AnnounceProducer,
2731	state: kio::Shared<ServeState>,
2732}
2733
2734impl Dynamic {
2735	/// Re-price the route in place: replace its hops and cost.
2736	///
2737	/// Consumers observe another active update for the same prefix; sessions
2738	/// forward it as a restart, so route churn never looks like new content. The
2739	/// prefix is fixed at announce time: to move a route, drop this and call
2740	/// [`Producer::dynamic`] again. Fails with [`Error::Closed`] once the origin's
2741	/// [`Driver`] has been dropped.
2742	pub fn update(&self, route: Route) -> Result<(), Error> {
2743		self.announcement.update(route)
2744	}
2745
2746	/// Poll for the next requested path under this route, without blocking.
2747	///
2748	/// Returns [`Error::Closed`] once the origin's [`Driver`] has been dropped:
2749	/// no request will ever arrive again, so handler loops should end.
2750	pub fn poll_requested_broadcast(&self, waiter: &kio::Waiter) -> Poll<Result<Request, Error>> {
2751		let mut state = ready!(self.state.poll(waiter, |state| {
2752			if state.closed || state.requests.has_queued() {
2753				Poll::Ready(())
2754			} else {
2755				Poll::Pending
2756			}
2757		}));
2758
2759		// The teardown already drained the queue, so there is nothing left to pop.
2760		if state.closed {
2761			return Poll::Ready(Err(Error::Closed));
2762		}
2763
2764		let path = state.requests.pop().expect("predicate guaranteed a request");
2765		// The popped request stays pending, so a repeat request in the window between
2766		// hand-off and accept coalesces onto it instead of re-invoking the handler. The
2767		// producer is a shared clone; `Request::{accept, reject, drop}` removes the
2768		// entry. This mirrors how `poll_requested_track` keeps a served track
2769		// discoverable via the weak cache across the same window.
2770		let producer = state.requests.get(&path).expect("popped key must be pending").clone();
2771		Poll::Ready(Ok(Request {
2772			path,
2773			producer,
2774			home: self.state.clone(),
2775		}))
2776	}
2777
2778	/// Block until a consumer requests a path under this route, returning a
2779	/// [`Request`] to serve.
2780	///
2781	/// Takes `&self` so a handler can serve from one task while another re-prices
2782	/// the route; concurrent callers each receive distinct requests.
2783	pub async fn requested_broadcast(&self) -> Result<Request, Error> {
2784		kio::wait(|waiter| self.poll_requested_broadcast(waiter)).await
2785	}
2786}
2787
2788impl ServeState {
2789	/// Resolve a pending request: cache an accepted broadcast for repeat
2790	/// requests, remove the queue entry, and wake the requesters.
2791	///
2792	/// Resolved while the queue's lock is held, so this linearizes with the
2793	/// teardown: either the teardown ran first (the `closed` check returns, its
2794	/// rejection stands) or this write lands first and the teardown finds the
2795	/// entry already gone. The queue lock is released before the channel guard
2796	/// drops, so the requester wakes outside it: an inline executor re-entering
2797	/// `request_broadcast` from the wake must not find the non-reentrant lock
2798	/// still held.
2799	fn resolve(
2800		shared: &kio::Shared<Self>,
2801		path: &PathOwned,
2802		producer: &kio::Producer<PendingBroadcast>,
2803		result: Result<broadcast::Consumer, Error>,
2804	) {
2805		let mut state = shared.lock();
2806		if state.closed {
2807			return;
2808		}
2809		let resolved = match result {
2810			Ok(broadcast) => {
2811				// If a live broadcast was already served for this path while we were
2812				// fetching upstream, dedup onto it and drop ours rather than replace
2813				// a good entry with a duplicate subscription.
2814				let existing = state.served.insert(path.clone(), broadcast.weak());
2815				Ok(existing.map(|weak| weak.consume()).unwrap_or(broadcast))
2816			}
2817			Err(err) => Err(err),
2818		};
2819		state.requests.remove_if(path, |p| p.same_channel(producer));
2820		if let Ok(mut pending) = producer.write() {
2821			pending.resolved.get_or_insert(resolved);
2822			drop(state);
2823		}
2824	}
2825
2826	/// Drop the still-pending entry, if it is still ours.
2827	fn forget(shared: &kio::Shared<Self>, path: &PathOwned, producer: &kio::Producer<PendingBroadcast>) {
2828		shared.lock().requests.remove_if(path, |p| p.same_channel(producer));
2829	}
2830}
2831
2832/// A pending request for a broadcast to be served on demand.
2833///
2834/// Yielded by [`Dynamic::requested_broadcast`]. The requester is awaiting inside
2835/// [`Consumer::request_broadcast`]; [`accept`](Self::accept) resolves it with a live
2836/// broadcast (which the handler keeps producing into) and [`reject`](Self::reject) resolves
2837/// it with an error. Dropping the request without either rejects it.
2838pub struct Request {
2839	// Absolute path that was requested.
2840	path: PathOwned,
2841
2842	// Result channel back to the awaiting requester(s). Writing `resolved` and dropping
2843	// this wakes them with the outcome.
2844	producer: kio::Producer<PendingBroadcast>,
2845
2846	// The queue this request came from, so `accept` can cache the served
2847	// broadcast for repeat requests.
2848	home: kio::Shared<ServeState>,
2849}
2850
2851impl Request {
2852	/// The absolute path that was requested.
2853	pub fn path(&self) -> &Path<'_> {
2854		&self.path
2855	}
2856
2857	/// Accept the request, resolving every awaiting requester with `broadcast`.
2858	///
2859	/// The caller keeps producing into `broadcast` (e.g. a relay proxying tracks from
2860	/// upstream); the requesters receive a consumer for it. Repeat requests for the
2861	/// path share the served broadcast for as long as it stays live.
2862	pub fn accept(self, broadcast: impl Consume<broadcast::Consumer>) {
2863		let broadcast = broadcast.consume();
2864		ServeState::resolve(&self.home, &self.path, &self.producer, Ok(broadcast));
2865		// `self.producer` drops here, closing the channel; the value is still observable.
2866	}
2867
2868	/// Reject the request, resolving every awaiting requester with `err`.
2869	pub fn reject(self, err: Error) {
2870		ServeState::resolve(&self.home, &self.path, &self.producer, Err(err));
2871	}
2872}
2873
2874impl Drop for Request {
2875	fn drop(&mut self) {
2876		// Handed off but neither accepted nor rejected: drop the still-pending entry so its
2877		// producer clone (plus this one) closes the channel, resolving coalesced requesters to
2878		// `Unroutable` rather than hanging.
2879		//
2880		// The identity guard matters: `accept`/`reject` already removed our entry and released
2881		// the lock before we run, so a concurrent request for the same path may have registered
2882		// a *new* one here. Removing unconditionally would clobber it, stranding its requesters.
2883		ServeState::forget(&self.home, &self.path, &self.producer);
2884	}
2885}
2886
2887/// The pollable result of [`Consumer::request_broadcast`].
2888///
2889/// Awaited via the [`kio::Pending`] wrapper; resolves to the [`broadcast::Consumer`]
2890/// immediately when the broadcast was already announced, or once an [`Dynamic`]
2891/// handler serves the request. Resolves to an error if the request is rejected or every
2892/// handler drops before serving it.
2893pub struct Requesting {
2894	inner: RequestState,
2895	// The path the requester asked for, relative to its cursor's root. Stamped on the
2896	// resolved broadcast (see [`broadcast::Info::path`]) because a handler is free to
2897	// serve a broadcast created somewhere else entirely, or at no path at all.
2898	path: PathOwned,
2899	// Egress scope applied to the resolved broadcast, so its reads are attributed.
2900	// Empty (no-op) for an untagged consumer.
2901	stats: stats::Scope,
2902}
2903
2904enum RequestState {
2905	// Unroutable at request time: resolves immediately with this error. Baked in so
2906	// `request_broadcast` itself stays infallible.
2907	Failed(Error),
2908	// Awaiting a handler: resolves when the request's result channel is written.
2909	Pending(kio::Consumer<PendingBroadcast>),
2910}
2911
2912impl Requesting {
2913	fn failed(error: Error) -> Self {
2914		Self::new(RequestState::Failed(error))
2915	}
2916
2917	fn queued(consumer: kio::Consumer<PendingBroadcast>) -> Self {
2918		Self::new(RequestState::Pending(consumer))
2919	}
2920
2921	/// Whether the request was handed to a serving route, rather than decided on
2922	/// the spot.
2923	///
2924	/// Fixed at request time, so it distinguishes the two ways
2925	/// [`Error::Unroutable`] arises: a queued request that fails was killed by
2926	/// its serving route retracting, and the table may already hold a
2927	/// replacement worth retrying against ([`Consumer::routed_broadcast`] does),
2928	/// while an unqueued failure means nothing could serve the path at all.
2929	pub fn is_queued(&self) -> bool {
2930		matches!(self.inner, RequestState::Pending(_))
2931	}
2932
2933	fn new(inner: RequestState) -> Self {
2934		Self {
2935			inner,
2936			path: PathOwned::default(),
2937			stats: stats::Scope::default(),
2938		}
2939	}
2940
2941	fn with_path(mut self, path: PathOwned) -> Self {
2942		self.path = path;
2943		self
2944	}
2945
2946	/// The egress scope the resolved broadcast's reads are attributed to.
2947	fn with_stats(mut self, scope: stats::Scope) -> Self {
2948		self.stats = scope;
2949		self
2950	}
2951
2952	/// Stamp a resolved broadcast with the path this cursor asked for and its egress scope.
2953	fn hand_out(&self, broadcast: broadcast::Consumer) -> broadcast::Consumer {
2954		broadcast.with_path(self.path.clone()).with_stats(self.stats.clone())
2955	}
2956
2957	/// Poll for the requested broadcast without blocking.
2958	pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<broadcast::Consumer, Error>> {
2959		match &self.inner {
2960			RequestState::Failed(error) => Poll::Ready(Err(error.clone())),
2961			RequestState::Pending(consumer) => Poll::Ready(
2962				match ready!(consumer.poll(waiter, |state| match &state.resolved {
2963					Some(result) => Poll::Ready(result.clone()),
2964					None => Poll::Pending,
2965				})) {
2966					Ok(result) => result.map(|broadcast| self.hand_out(broadcast)),
2967					// Every handler dropped without resolving: nobody could route it.
2968					Err(_closed) => Err(Error::Unroutable),
2969				},
2970			),
2971		}
2972	}
2973}
2974
2975impl kio::Pollable for Requesting {
2976	type Output = Result<broadcast::Consumer, Error>;
2977
2978	fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
2979		self.poll_ok(waiter)
2980	}
2981}
2982
2983/// Derive a read view from a handle.
2984///
2985/// Lets APIs accept either a producer or a consumer (e.g.
2986/// [`Client::with_publisher`](crate::Client::with_publisher),
2987/// [`Request::accept`]). The blanket `&T` impl means you can
2988/// pass by value (`foo(x)`) to hand off ownership, or by reference (`foo(&x)`)
2989/// to keep it, without spelling out `.consume()`.
2990pub trait Consume<T> {
2991	/// Derive a read view (a consumer) from this handle.
2992	fn consume(&self) -> T;
2993}
2994
2995impl<T, U: Consume<T>> Consume<T> for &U {
2996	fn consume(&self) -> T {
2997		(**self).consume()
2998	}
2999}
3000
3001impl Consume<Consumer> for Producer {
3002	fn consume(&self) -> Consumer {
3003		// Mirrors the inherent `Producer::consume`; inlined to avoid the
3004		// inherent-vs-trait `consume` ambiguity. Untagged: egress is tagged
3005		// separately from ingress.
3006		Consumer::from_producer(self, stats::Session::default())
3007	}
3008}
3009
3010impl Consume<Consumer> for Consumer {
3011	fn consume(&self) -> Consumer {
3012		self.clone()
3013	}
3014}
3015
3016impl Consume<broadcast::Consumer> for broadcast::Producer {
3017	fn consume(&self) -> broadcast::Consumer {
3018		// The inherent `consume` shadows this trait method, so this delegates.
3019		self.consume()
3020	}
3021}
3022
3023impl Consume<broadcast::Consumer> for broadcast::Consumer {
3024	fn consume(&self) -> broadcast::Consumer {
3025		self.clone()
3026	}
3027}
3028
3029impl Consume<track::Consumer> for track::Producer {
3030	fn consume(&self) -> track::Consumer {
3031		self.consume()
3032	}
3033}
3034
3035impl Consume<track::Consumer> for track::Consumer {
3036	fn consume(&self) -> track::Consumer {
3037		self.clone()
3038	}
3039}
3040
3041/// Cheap read handle over an origin's route table.
3042///
3043/// Clones share the underlying state without allocating any per-cursor
3044/// resources. To receive route announcements, call [`Self::announced`]; to
3045/// resolve a path into a broadcast, call [`Self::request_broadcast`].
3046#[derive(Clone)]
3047pub struct Consumer {
3048	// Identity of the origin this consumer was derived from.
3049	hop: Hop,
3050	scope: OriginScope,
3051
3052	// A prefix that is automatically stripped from all paths.
3053	root: PathOwned,
3054
3055	// The origin's shared state: the route table, announce cursors, and the
3056	// remotely-served fronts.
3057	shared: kio::Shared<OriginState>,
3058
3059	// Egress stats context. Broadcasts handed out through this consumer (and any
3060	// handle derived from them) are attributed to it (reads counted on the
3061	// publisher/egress side). Empty (no-op) unless a session tagged this handle.
3062	stats: stats::Session,
3063
3064	// Split horizon: routes whose hop chain or announcing session (`via`) is this
3065	// peer are invisible to `announced` and skipped by `request_broadcast`, so a
3066	// peer is never served (or advertised) its own content back. `Some(UNKNOWN)`
3067	// marks an anonymous peer: no hop is excluded, but local-only routes are not
3068	// advertised. `None` is a local cursor and filters nothing.
3069	exclude: Option<Hop>,
3070
3071	// The cache policy remote fronts inherit, mirroring what
3072	// `create_broadcast` gives a local front.
3073	pool: cache::Pool,
3074	cache_duration: Duration,
3075
3076	// Non-owning submission handle to the origin's [`Driver`], for the front
3077	// watcher a routed `request_broadcast` spawns. Non-owning so a lingering
3078	// read handle never keeps the driver from finishing.
3079	tasks: TasksWeak,
3080
3081	// The driver's clock and timers, threaded into fronts for the track idle
3082	// linger.
3083	timers: Clock,
3084}
3085
3086impl Consumer {
3087	fn from_producer(producer: &Producer, stats: stats::Session) -> Self {
3088		Self {
3089			hop: producer.hop,
3090			scope: producer.scope.clone(),
3091			root: producer.root.clone(),
3092			shared: producer.shared.clone(),
3093			stats,
3094			exclude: None,
3095			pool: producer.pool.clone(),
3096			cache_duration: producer.cache_duration,
3097			tasks: producer.tasks.downgrade(),
3098			timers: producer.timers.clone(),
3099		}
3100	}
3101
3102	/// This origin's hop identity.
3103	pub fn hop(&self) -> Hop {
3104		self.hop
3105	}
3106
3107	/// A clone that never serves the given peer its own data: routes whose hop
3108	/// chain contains `peer`, or whose announcing session is `peer`, are invisible
3109	/// and never resolved from, matching what the announce loop advertises to them.
3110	/// Sessions apply this once they learn the peer's origin id. Hop 0 identifies
3111	/// nobody, so the announcing session's assigned identity is what keeps an
3112	/// anonymous route from echoing back. Pass [`Hop::UNKNOWN`] for an anonymous
3113	/// peer so its cursor still excludes local-only announcements.
3114	pub(crate) fn excluding(mut self, peer: Hop) -> Self {
3115		self.exclude = Some(peer);
3116		self
3117	}
3118
3119	/// Attach an egress stats context: broadcasts handed out through this handle (and
3120	/// any handle derived from it) are attributed to `session` on the publisher
3121	/// (egress) side. Pass [`stats::Session::default`] to opt out.
3122	pub fn with_stats(mut self, session: stats::Session) -> Self {
3123		self.stats = session;
3124		self
3125	}
3126
3127	/// A clone of this consumer with its stats context cleared, so an internal
3128	/// lookup stream (e.g. [`Self::routed`]) doesn't drive the egress
3129	/// announce guards; the caller re-attributes the result itself.
3130	fn untagged(&self) -> Self {
3131		Self {
3132			stats: stats::Session::default(),
3133			..self.clone()
3134		}
3135	}
3136
3137	/// A view with this consumer's identity and root but no scope:
3138	/// [`announced`](Self::announced) yields nothing. Used to answer a peer's
3139	/// announce-interest for a prefix outside our scope by announcing nothing,
3140	/// rather than tearing the stream down.
3141	pub(crate) fn empty(&self) -> Self {
3142		Self {
3143			scope: OriginScope::empty(),
3144			..self.clone()
3145		}
3146	}
3147
3148	/// Subscribe to route announcements for this consumer's scope.
3149	///
3150	/// Allocates a per-cursor coalescing buffer and replays the currently
3151	/// announced routes as initial updates. Routes stay prefixes and are named
3152	/// relative to this consumer's root; its patterns only filter visibility.
3153	/// Drop the returned [`AnnounceConsumer`] to unregister.
3154	pub fn announced(&self) -> AnnounceConsumer {
3155		AnnounceConsumer::new(
3156			self.root.clone(),
3157			self.scope.allowed.clone(),
3158			self.stats.clone(),
3159			self.exclude,
3160			&self.shared,
3161		)
3162	}
3163
3164	/// Returns a cheap duplicate of this read handle.
3165	pub fn consume(&self) -> Self {
3166		self.clone()
3167	}
3168
3169	/// The newest broadcast published on this origin at exactly `path`, if any.
3170	/// Test-only: a request goes through the table like any other.
3171	#[cfg(test)]
3172	pub(crate) fn get_broadcast(&self, path: impl AsPath) -> Option<broadcast::Consumer> {
3173		let full = self.root.join(path).to_owned();
3174		if !self.scope.permits(&full) {
3175			return None;
3176		}
3177		let table = self.shared.lock();
3178		table
3179			.routes
3180			.at(&full)
3181			.filter(|entry| entry.local)
3182			.min_by_key(|entry| route_order(&entry.prefix, entry))
3183			.and_then(|entry| entry.source.clone())
3184	}
3185
3186	/// Block until an announced route covers `path`, and return it.
3187	///
3188	/// Covering means the route's prefix is a (segment-wise) prefix of `path`,
3189	/// including the exact path itself. Returns `None` if the path is outside this
3190	/// consumer's scope or the consumer is closed first.
3191	///
3192	/// To resolve a broadcast rather than inspect the route, use
3193	/// [`Self::routed_broadcast`]: pairing this with [`Self::request_broadcast`]
3194	/// leaves a gap where the covering route can retract, and misses a local
3195	/// broadcast that serves the path without announcing.
3196	pub async fn routed(&self, path: impl AsPath) -> Option<Route> {
3197		let path = path.as_path();
3198
3199		// Scope a fresh consumer down to this path's subtree, so we only wake for
3200		// announcements that overlap the requested path.
3201		// A max-depth path cannot be spelled as `path/**` (`**` would be a 33rd
3202		// segment), so watch the existing stream and match covering claims instead.
3203		let consumer = match Pattern::subtree(path.as_str()) {
3204			Ok(subtree) => self.scope("", &Patterns::from(subtree)).ok()?,
3205			Err(InvalidPattern::TooManySegments) => self.clone(),
3206			Err(_) => return None,
3207		};
3208
3209		// `scope` keeps narrower permissions intact: if we ask for `foo` on a
3210		// consumer limited to `foo/specific`, `foo` itself is unauthorized. Bail
3211		// rather than loop forever.
3212		if !consumer.allowed().matches(path.as_str()) {
3213			return None;
3214		}
3215
3216		// Use an untagged stream: this is a lookup, not egress announce
3217		// forwarding, so it must not drive the announce guards.
3218		let mut announced = consumer.untagged().announced();
3219		loop {
3220			let update = announced.next().await?;
3221			if update.kind.is_active() && path.has_prefix(&update.prefix) {
3222				return Some(update.route);
3223			}
3224		}
3225	}
3226
3227	/// Block until `path` resolves to a broadcast: [`Self::request_broadcast`],
3228	/// retried whenever the routes covering the path change.
3229	///
3230	/// A request answers for the routes as they stand, so it can miss an
3231	/// announcement that has not arrived yet, lose its covering route to
3232	/// failover churn, find a route that covers the path while nothing serves it
3233	/// yet (an advertise-only announce racing its handler), or be turned down by
3234	/// a handler. This rides all of that out by watching the covering routes
3235	/// and asking again each time they move, which is what makes it the right
3236	/// call for resolving a path right after connecting. Returns
3237	/// [`Error::Unauthorized`] for a path outside this consumer's scope,
3238	/// [`Error::Closed`] once the origin closes, and any other resolution
3239	/// failure as-is.
3240	pub async fn routed_broadcast(&self, path: impl AsPath) -> Result<broadcast::Consumer, Error> {
3241		let path = path.as_path();
3242
3243		// `allowed` keeps narrower permissions intact: if the whole path is not
3244		// reachable, no route can ever cover it, so bail rather than loop forever.
3245		if !self.allowed().matches(path.as_str()) {
3246			return Err(Error::Unauthorized);
3247		}
3248		loop {
3249			// `Unroutable` is a verdict of the routes covering the path as they
3250			// stood when the request was made. Re-asking the same routes would
3251			// spin, so watch them before asking and wait for them to move (a
3252			// route arriving or retracting, an identical standby swapping in, a
3253			// local broadcast attaching at the path), then try again. A change
3254			// between the ask and the wait bumps the watch first, so that retry
3255			// is immediate; the teardown pokes every watch, so a closed origin
3256			// is observed on the next pass.
3257			let (watch, seen) = {
3258				let mut table = self.shared.lock();
3259				if table.closed {
3260					return Err(Error::Closed);
3261				}
3262				let watch = table.watch(&self.shared, &self.root.join(&path));
3263				let seen = watch.seen();
3264				(watch, seen)
3265			};
3266			match self.request_broadcast(&path).await {
3267				Ok(broadcast) => return Ok(broadcast),
3268				Err(Error::Unroutable) => {
3269					kio::wait(|waiter| watch.poll_changed(waiter, seen)).await;
3270				}
3271				// Teardown parks a pending request with `Dropped`; the contract is
3272				// `Closed` once the origin is gone.
3273				Err(Error::Dropped) if self.shared.lock().closed => return Err(Error::Closed),
3274				Err(err) => return Err(err),
3275			}
3276		}
3277	}
3278
3279	/// Returns a consumer rooted at `root` and restricted to matching `patterns`.
3280	///
3281	/// `root` is relative to this consumer's root, and `patterns` are relative to
3282	/// the new root. Returns [`Error::Unauthorized`] when the requested scope has
3283	/// no overlap with this consumer's scope, or [`Error::BoundsExceeded`] when
3284	/// rooting the patterns would exceed the path limit.
3285	pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Consumer, Error> {
3286		let root = self.root.join(root).to_owned();
3287		let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
3288		let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
3289		Ok(Consumer {
3290			scope,
3291			root,
3292			..self.clone()
3293		})
3294	}
3295
3296	/// Resolve a broadcast by exact path.
3297	///
3298	/// Returns a [`kio::Pending`] future, mirroring
3299	/// [`track::Consumer::fetch_group`](track::Consumer::fetch_group). Every
3300	/// path resolves through a front the origin's [`Driver`] runs: the request
3301	/// mints one or joins the one already serving the path, and the front picks
3302	/// the best route covering it (a broadcast published on this origin at the
3303	/// exact path first, announced or not; then the most specific prefix, then
3304	/// the cheapest) and materializes it, from the broadcast itself or from the
3305	/// peer that announced the route. When its serving source dies or a better
3306	/// qualifying route appears, the front re-splices through the best route
3307	/// sharing its first hop at a group boundary, invisibly to subscribers. A
3308	/// change that does not preserve the first hop ends the broadcast instead,
3309	/// and the next request re-serves the path.
3310	///
3311	/// The returned future fails with [`Error::Unroutable`] at once when nothing
3312	/// covers the path.
3313	/// A route claims capability, not inventory: resolving a covered path
3314	/// succeeds optimistically, and a path that names nothing surfaces as
3315	/// [`Error::NotFound`] on its tracks instead.
3316	pub fn request_broadcast(&self, path: impl AsPath) -> kio::Pending<Requesting> {
3317		let path = path.as_path();
3318
3319		// Key requests by absolute path so scoped/rooted consumers and handlers
3320		// (which may have a different root) agree on the same entry, and so the egress
3321		// counters resolve against the same broadcast the ingress side wrote.
3322		let absolute = self.root.join(&path).to_owned();
3323		let scope = self.stats.egress(&absolute);
3324		// The resolved handle is named by what *this* cursor asked for, not by the absolute
3325		// path: a rooted cursor cannot name anything above its own root, so that is what a
3326		// catalog it reads may reference.
3327		let requested = path.to_owned();
3328
3329		// Routes only cover paths within this consumer's scope.
3330		if !self.scope.permits(&absolute) {
3331			return kio::Pending::new(Requesting::failed(Error::Unauthorized));
3332		}
3333
3334		let mut state = self.shared.lock();
3335
3336		// The origin's driver dropped: nothing will ever serve this.
3337		if state.closed {
3338			return kio::Pending::new(Requesting::failed(Error::Closed));
3339		}
3340
3341		// Join the live front for this path and exclusion, if any: its watcher
3342		// resolves (or already resolved) the request channel with the front's
3343		// spliced broadcast, so repeat requests share one upstream
3344		// subscription. A front whose route has since retracted still serves
3345		// for as long as its session does.
3346		let key = (absolute.clone(), self.exclude);
3347		if let Some(front) = state.fronts.get(&key) {
3348			let pending = Requesting::queued(front.request.consume())
3349				.with_path(requested)
3350				.with_stats(scope);
3351			return kio::Pending::new(pending);
3352		}
3353
3354		// Nothing serves the path: no local broadcast and no served route.
3355		if state
3356			.best_route(&absolute.as_path(), self.exclude, Pin::Any, &HashSet::new())
3357			.is_none()
3358		{
3359			return kio::Pending::new(Requesting::failed(Error::Unroutable));
3360		}
3361
3362		// A route covers the path: mint the front and hand its watcher the
3363		// request. The watcher materializes the path from the best covering
3364		// route, resolves the channel, and re-splices the front through
3365		// routes sharing its first hop for as long as one serves.
3366		let broadcast = broadcast::Producer::new_spliced(broadcast::Info {
3367			pool: self.pool.clone(),
3368			cache_duration: self.cache_duration,
3369			path: absolute.clone(),
3370		});
3371		let request = kio::Producer::<PendingBroadcast>::default();
3372		let consumer = request.consume();
3373		let watch = state.watch(&self.shared, &absolute);
3374		state.fronts.insert(
3375			key,
3376			RemoteFront {
3377				request: request.clone(),
3378				broadcast: broadcast.consume().weak(),
3379			},
3380		);
3381		// Released before the push: a set whose handles are gone drops the task,
3382		// and the `Watch` it carries unregisters under this same lock.
3383		drop(state);
3384		self.tasks.push(run_front(FrontTask {
3385			shared: self.shared.clone(),
3386			broadcast,
3387			path: absolute,
3388			exclude: self.exclude,
3389			watch,
3390			request,
3391			timers: self.timers.clone(),
3392		}));
3393		kio::Pending::new(Requesting::queued(consumer).with_path(requested).with_stats(scope))
3394	}
3395
3396	/// Returns the prefix that is automatically stripped from all paths.
3397	pub fn root(&self) -> &Path<'_> {
3398		&self.root
3399	}
3400
3401	/// The patterns this consumer may reach, relative to its root.
3402	pub fn allowed(&self) -> Patterns {
3403		self.scope.relative(&self.root)
3404	}
3405
3406	/// Converts a relative path to an absolute path.
3407	pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
3408		self.root.join(path)
3409	}
3410}
3411
3412/// Receives route announcements for a scope.
3413///
3414/// Created by [`Consumer::announced`].
3415/// Drop to unregister.
3416pub struct AnnounceConsumer {
3417	id: ConsumerId,
3418	shared: kio::Shared<OriginState>,
3419	root: PathOwned,
3420
3421	// Pending updates queued for this cursor. Coalesced so a slow consumer
3422	// can't accumulate redundant announce/retract pairs.
3423	state: kio::Producer<OriginConsumerState>,
3424
3425	// Egress stats context (empty for an untagged stream). Announce events drive the
3426	// per-prefix announce guards below.
3427	stats: stats::Session,
3428
3429	// Live egress announce guards, keyed by absolute prefix. An announce
3430	// opens one (bumping `announces_started` + `announced_bytes`); the matching retraction
3431	// drops it (bumping `announces_ended` + `announced_bytes`).
3432	guards: HashMap<PathOwned, stats::Announce>,
3433
3434	// Holds the waiter a `Stream` poll registered; disjoint from `state` so the
3435	// borrow never collides with the body's.
3436	park: kio::Park,
3437}
3438
3439impl AnnounceConsumer {
3440	fn new(
3441		root: PathOwned,
3442		allowed: Patterns,
3443		stats: stats::Session,
3444		exclude: Option<Hop>,
3445		shared: &kio::Shared<OriginState>,
3446	) -> Self {
3447		let state = kio::Producer::<OriginConsumerState>::default();
3448		let id = ConsumerId::new();
3449
3450		{
3451			let mut table = shared.lock();
3452			if table.closed {
3453				// A cursor on a dead origin is born ended.
3454				if let Ok(mut state) = state.write() {
3455					state.ended = true;
3456				}
3457			} else {
3458				table.register_cursor(
3459					id,
3460					TableCursor {
3461						root: root.clone(),
3462						heads: interest_prefixes(&allowed),
3463						allowed,
3464						exclude,
3465						state: state.clone(),
3466						current: HashMap::new(),
3467					},
3468				);
3469			}
3470		}
3471
3472		Self {
3473			id,
3474			shared: shared.clone(),
3475			root,
3476			state,
3477			stats,
3478			guards: HashMap::new(),
3479			park: kio::Park::default(),
3480		}
3481	}
3482
3483	/// Drive the egress announce guards for one update.
3484	fn hand_out(&mut self, update: AnnounceUpdate) -> AnnounceUpdate {
3485		let absolute = self.root.join(&update.prefix).to_owned();
3486		if update.kind.is_active() {
3487			let scope = self.stats.egress(&absolute);
3488			self.guards
3489				.entry(update.prefix.clone())
3490				.or_insert_with(|| scope.announce());
3491		} else {
3492			self.guards.remove(&update.prefix);
3493		}
3494		update
3495	}
3496
3497	/// Returns the next route announcement, update, or retraction, its prefix
3498	/// relative to this cursor's root.
3499	///
3500	/// A retraction is only delivered for a previously announced prefix, and a
3501	/// repeated announcement for the same prefix is a metadata update. Returns
3502	/// None if the cursor is closed. The consumer is also a [`futures::Stream`]
3503	/// of the same updates.
3504	pub async fn next(&mut self) -> Option<AnnounceUpdate> {
3505		kio::wait(|waiter| self.poll_next(waiter)).await
3506	}
3507
3508	/// Poll for the next update, without blocking.
3509	///
3510	/// Returns `Poll::Ready(Some(_))` for an update, `Poll::Ready(None)` if the
3511	/// cursor is closed, or `Poll::Pending` after registering `waiter` to be
3512	/// notified when the next update arrives.
3513	pub fn poll_next(&mut self, waiter: &kio::Waiter) -> Poll<Option<AnnounceUpdate>> {
3514		let update = {
3515			let mut state = match ready!(self.state.poll(waiter, |state| {
3516				if state.pending.is_empty() && !state.ended {
3517					Poll::Pending
3518				} else {
3519					Poll::Ready(())
3520				}
3521			})) {
3522				Ok(state) => state,
3523				// Closed: discard the Ref so its MutexGuard doesn't escape this call.
3524				Err(_) => return Poll::Ready(None),
3525			};
3526			match state.take() {
3527				Some(update) => update,
3528				None => {
3529					// Ended by the origin's teardown, pending updates already
3530					// drained; close the channel so every closure signal agrees.
3531					state.close();
3532					return Poll::Ready(None);
3533				}
3534			}
3535		};
3536		Poll::Ready(Some(self.hand_out(update)))
3537	}
3538
3539	/// Returns the next update without blocking.
3540	///
3541	/// Returns None if there is no update available; NOT because the cursor is closed.
3542	/// Use [`Self::is_closed`] to check if the cursor is closed.
3543	pub fn try_next(&mut self) -> Option<AnnounceUpdate> {
3544		let update = self.state.write().ok()?.take()?;
3545		Some(self.hand_out(update))
3546	}
3547
3548	/// Returns true if the cursor is closed (no more updates will arrive).
3549	pub fn is_closed(&self) -> bool {
3550		let state = self.state.read();
3551		state.is_closed() || state.ended
3552	}
3553
3554	/// Returns the root that is automatically stripped from emitted prefixes.
3555	pub fn root(&self) -> &Path<'_> {
3556		&self.root
3557	}
3558
3559	/// Converts an emitted prefix back to one rooted at the origin.
3560	pub fn absolute(&self, prefix: impl AsPath) -> Path<'_> {
3561		self.root.join(prefix)
3562	}
3563}
3564
3565impl futures::Stream for AnnounceConsumer {
3566	type Item = AnnounceUpdate;
3567
3568	fn poll_next(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Option<Self::Item>> {
3569		let this = self.get_mut();
3570		let waiter = this.park.hold(cx).clone();
3571		this.poll_next(&waiter)
3572	}
3573}
3574
3575impl Drop for AnnounceConsumer {
3576	fn drop(&mut self) {
3577		let mut shared = self.shared.lock();
3578		if let Some(cursor) = shared.cursors.remove(&self.id) {
3579			for head in &cursor.heads {
3580				shared.routes.remove_cursor(head, self.id);
3581			}
3582		}
3583	}
3584}
3585
3586#[cfg(test)]
3587use futures::FutureExt;
3588
3589#[cfg(test)]
3590#[allow(missing_docs)] // test-only assertion helpers
3591impl AnnounceConsumer {
3592	/// The next update must be an active route at `expected`; returns it.
3593	pub fn assert_next_active(&mut self, expected: impl AsPath) -> Route {
3594		let expected = expected.as_path();
3595		let update = self.next().now_or_never().expect("next blocked").expect("no next");
3596		assert_eq!(update.prefix, expected, "wrong prefix");
3597		assert!(update.kind.is_active(), "should be an active route");
3598		update.route
3599	}
3600
3601	/// The `try_next` counterpart of [`Self::assert_next_active`].
3602	pub fn assert_try_next_active(&mut self, expected: impl AsPath) -> Route {
3603		let expected = expected.as_path();
3604		let update = self.try_next().expect("no next");
3605		assert_eq!(update.prefix, expected, "wrong prefix");
3606		assert!(update.kind.is_active(), "should be an active route");
3607		update.route
3608	}
3609
3610	/// The next update must be a retraction at `expected`.
3611	pub fn assert_next_ended(&mut self, expected: impl AsPath) {
3612		let expected = expected.as_path();
3613		let update = self.next().now_or_never().expect("next blocked").expect("no next");
3614		assert_eq!(update.prefix, expected, "wrong prefix");
3615		assert_eq!(update.kind, AnnounceKind::Retracted, "should be a retraction");
3616	}
3617
3618	pub fn assert_next_wait(&mut self) {
3619		if let Some(res) = self.next().now_or_never() {
3620			panic!("next should block: got {:?}", res.map(|u| u.prefix));
3621		}
3622	}
3623}
3624
3625/// Test-only construction shorthand: build the producer and spawn its driver on
3626/// the ambient tokio runtime, mirroring what `moq_tokio::origin::spawn` does
3627/// for applications.
3628#[cfg(test)]
3629pub(crate) trait ProduceTest {
3630	fn produce(self) -> Producer;
3631}
3632
3633#[cfg(test)]
3634impl ProduceTest for Config {
3635	fn produce(self) -> Producer {
3636		let (producer, driver) = Producer::new(self);
3637		if tokio::runtime::Handle::try_current().is_ok() {
3638			tokio::spawn(crate::time::run(driver));
3639		} else {
3640			// A sync test: nothing polls the driver, and dropping it would tear
3641			// the origin down, so leak it and rely on the synchronous half.
3642			std::mem::forget(driver);
3643		}
3644		producer
3645	}
3646}
3647
3648#[cfg(test)]
3649impl ProduceTest for Hop {
3650	fn produce(self) -> Producer {
3651		Config::new(self).produce()
3652	}
3653}
3654
3655#[cfg(test)]
3656mod tests {
3657	use super::*;
3658	use futures::FutureExt;
3659
3660	fn origin(id: u64) -> Hop {
3661		Hop::new(id).unwrap()
3662	}
3663
3664	fn hops(ids: &[u64]) -> Hops {
3665		let mut list = Hops::new();
3666		for &id in ids {
3667			list.push(if id == 0 { Hop::UNKNOWN } else { origin(id) }).unwrap();
3668		}
3669		list
3670	}
3671
3672	/// The scope granting these prefixes: each spelled as its subtree pattern.
3673	fn scopes(prefixes: &[&str]) -> Patterns {
3674		prefixes
3675			.iter()
3676			.map(|prefix| Pattern::subtree(prefix).unwrap())
3677			.collect()
3678	}
3679
3680	#[test]
3681	fn default_config_mints_a_real_hop() {
3682		let config = Config::default();
3683		assert_ne!(config.hop, Hop::UNKNOWN);
3684		let (producer, _driver) = Producer::new(config.clone());
3685		assert_eq!(producer.hop(), config.hop);
3686		assert_eq!(producer.consume().hop(), config.hop);
3687	}
3688
3689	#[test]
3690	fn random_hops_fit_legacy_lite_clients() {
3691		for _ in 0..32 {
3692			assert!(Hop::random().id() < 1u64 << 53);
3693		}
3694	}
3695
3696	/// Yield to the driver until `check` passes, bounded so a bug fails instead
3697	/// of hanging.
3698	async fn settle(mut check: impl FnMut() -> bool) {
3699		for _ in 0..100 {
3700			if check() {
3701				return;
3702			}
3703			tokio::task::yield_now().await;
3704		}
3705		panic!("condition never settled");
3706	}
3707
3708	/// Yield to the driver until the server's front watcher delivers a request.
3709	async fn queued(server: &Dynamic) -> Request {
3710		let mut request = None;
3711		settle(|| match server.poll_requested_broadcast(&kio::Waiter::noop()) {
3712			Poll::Ready(Ok(popped)) => {
3713				request = Some(popped);
3714				true
3715			}
3716			_ => false,
3717		})
3718		.await;
3719		request.unwrap()
3720	}
3721
3722	/// Yield to the driver until the subscription has its next group or its end.
3723	async fn next_group(subscription: &mut crate::track::Subscriber) -> Result<Option<crate::group::Consumer>, Error> {
3724		let mut next = None;
3725		settle(|| match subscription.poll_recv_group(&kio::Waiter::noop()) {
3726			Poll::Ready(result) => {
3727				next = Some(result);
3728				true
3729			}
3730			Poll::Pending => false,
3731		})
3732		.await;
3733		next.unwrap()
3734	}
3735
3736	#[tokio::test]
3737	async fn announce_and_retract() {
3738		let producer = origin(1).produce();
3739		let consumer = producer.consume();
3740		let mut announced = consumer.announced();
3741		announced.assert_next_wait();
3742
3743		let announcement = producer.announce("room/alice", Route::default()).unwrap();
3744		let route = announced.assert_next_active("room/alice");
3745		assert!(route.hops.is_empty());
3746		assert_eq!(route.cost, Cost::default());
3747		announced.assert_next_wait();
3748
3749		drop(announcement);
3750		announced.assert_next_ended("room/alice");
3751		announced.assert_next_wait();
3752	}
3753
3754	#[tokio::test]
3755	async fn broadcast_announces_its_own_path() {
3756		let producer = origin(1).produce();
3757		let consumer = producer.consume();
3758		let mut announced = consumer.announced();
3759
3760		// Creation reaches local cursors, while peers still need an explicit announce.
3761		let broadcast = producer.create_broadcast("room/alice").unwrap();
3762		assert_eq!(announced.assert_next_active("room/alice").cost, Cost::default());
3763		let mut peer = consumer.clone().excluding(Hop::UNKNOWN).announced();
3764		peer.assert_next_wait();
3765		let local = consumer.request_broadcast("room/alice").await.expect("resolves");
3766		assert_eq!(local.info().path.as_str(), "room/alice");
3767
3768		broadcast.announce(Route::default().with_cost(3)).unwrap();
3769		let route = announced.assert_next_active("room/alice");
3770		assert_eq!(route.cost, Cost::new(3));
3771		assert_eq!(peer.assert_next_active("room/alice").cost, Cost::new(3));
3772
3773		// Announcing again re-prices in place.
3774		broadcast.announce(Route::default().with_cost(1)).unwrap();
3775		let route = announced.assert_next_active("room/alice");
3776		assert_eq!(route.cost, Cost::new(1));
3777
3778		// Off the air: only the peer route retracts. The local cursor
3779		// returns to its default route while the broadcast stays reachable.
3780		broadcast.unannounce();
3781		assert_eq!(announced.assert_next_active("room/alice").cost, Cost::default());
3782		peer.assert_next_ended("room/alice");
3783		broadcast.unannounce();
3784		announced.assert_next_wait();
3785		let local = consumer.request_broadcast("room/alice").await.expect("resolves");
3786		assert_eq!(local.info().path.as_str(), "room/alice");
3787
3788		// Back on the air, then the end of the broadcast retracts for good.
3789		broadcast.announce(Route::default()).unwrap();
3790		announced.assert_next_wait();
3791		peer.assert_next_active("room/alice");
3792		broadcast.finish();
3793		announced.assert_next_ended("room/alice");
3794		peer.assert_next_ended("room/alice");
3795		assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
3796		announced.assert_next_wait();
3797	}
3798
3799	#[tokio::test]
3800	async fn broadcast_announcement_retracts_with_the_last_producer() {
3801		let producer = origin(1).produce();
3802		let consumer = producer.consume();
3803		let mut announced = consumer.announced();
3804
3805		let broadcast = producer.create_broadcast("room/alice").unwrap();
3806		let clone = broadcast.clone();
3807		broadcast.announce(Route::default()).unwrap();
3808		announced.assert_next_active("room/alice");
3809
3810		// A clone keeps the broadcast, and its advertisement, alive.
3811		drop(broadcast);
3812		announced.assert_next_wait();
3813		drop(clone);
3814		announced.assert_next_ended("room/alice");
3815	}
3816
3817	#[tokio::test]
3818	async fn publish_creates_and_announces_together() {
3819		let producer = origin(1).produce();
3820		let mut announced = producer.consume().announced();
3821		let _broadcast = producer.publish("room/alice", Route::default()).unwrap();
3822		announced.assert_next_active("room/alice");
3823	}
3824
3825	#[tokio::test]
3826	async fn standalone_broadcast_cannot_announce() {
3827		let broadcast = broadcast::Info::new().produce();
3828		assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
3829		// Harmless without an advertisement to retract.
3830		broadcast.unannounce();
3831	}
3832
3833	#[tokio::test]
3834	async fn announce_replays_to_late_cursor() {
3835		let producer = origin(1).produce();
3836		let _a = producer.announce("room/alice", Route::default()).unwrap();
3837		let _b = producer.announce("room/bob", Route::default()).unwrap();
3838
3839		let mut announced = producer.consume().announced();
3840		// BTreeMap order: lexicographic by prefix.
3841		announced.assert_next_active("room/alice");
3842		announced.assert_next_active("room/bob");
3843		announced.assert_next_wait();
3844	}
3845
3846	#[tokio::test]
3847	async fn announce_keeps_its_prefix_under_a_producer_scope() {
3848		let producer = origin(1).produce();
3849		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
3850
3851		// Prefix advertisements stay prefixes. The scope filters requests locally.
3852		let _a = scoped.announce("", Route::default()).unwrap();
3853		let mut announced = producer.consume().announced();
3854		announced.assert_next_active("");
3855
3856		// Disjoint prefixes cannot be claimed at all.
3857		assert!(matches!(
3858			scoped.announce("other", Route::default()),
3859			Err(Error::Unauthorized)
3860		));
3861	}
3862
3863	#[tokio::test]
3864	async fn cursor_keeps_an_overlapping_prefix_above_its_scope() {
3865		let producer = origin(1).produce();
3866		let _a = producer.announce("", Route::default()).unwrap();
3867
3868		let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
3869		let mut announced = consumer.announced();
3870		announced.assert_next_active("");
3871	}
3872
3873	#[tokio::test]
3874	async fn cursor_root_strips_prefix() {
3875		let producer = origin(1).produce();
3876		let _a = producer.announce("room/alice", Route::default()).unwrap();
3877
3878		let consumer = producer
3879			.consume()
3880			.scope("room", &Patterns::from(Pattern::all()))
3881			.unwrap();
3882		let mut announced = consumer.announced();
3883		announced.assert_next_active("alice");
3884	}
3885
3886	#[tokio::test]
3887	async fn best_route_wins_and_fails_over() {
3888		let producer = origin(1).produce();
3889		let mut announced = producer.consume().announced();
3890
3891		let expensive = producer
3892			.announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
3893			.unwrap();
3894		let route = announced.assert_next_active("room");
3895		assert_eq!(route.cost, Cost::new(5));
3896
3897		// A cheaper route for the same prefix takes over in place.
3898		let cheap = producer
3899			.announce("room", Route::default().with_hops(hops(&[20])).with_cost(1))
3900			.unwrap();
3901		let route = announced.assert_next_active("room");
3902		assert_eq!(route.cost, Cost::new(1));
3903
3904		// Losing the winner falls back to the survivor, still in place.
3905		drop(cheap);
3906		let route = announced.assert_next_active("room");
3907		assert_eq!(route.cost, Cost::new(5));
3908
3909		// Losing the last retracts.
3910		drop(expensive);
3911		announced.assert_next_ended("room");
3912	}
3913
3914	#[tokio::test]
3915	async fn identical_reannounce_is_invisible() {
3916		let producer = origin(1).produce();
3917		let mut announced = producer.consume().announced();
3918
3919		let old = producer
3920			.announce("room", Route::default().with_hops(hops(&[10])))
3921			.unwrap();
3922		let first = announced.assert_next_active("room");
3923		assert_eq!(first.hops.as_slice(), hops(&[10]).as_slice());
3924
3925		// An identical route from a fresh announcement (a reconnect) changes
3926		// nothing a consumer could act on, so nothing is delivered; new requests
3927		// still prefer the newest entry.
3928		let _new = producer
3929			.announce("room", Route::default().with_hops(hops(&[10])))
3930			.unwrap();
3931		announced.assert_next_wait();
3932
3933		// Retracting the stale twin leaves the fresh one standing, still quietly.
3934		drop(old);
3935		announced.assert_next_wait();
3936	}
3937
3938	#[tokio::test]
3939	async fn exclude_hides_routes_through_the_peer() {
3940		let producer = origin(1).produce();
3941		let _a = producer
3942			.announce("room", Route::default().with_hops(hops(&[7])))
3943			.unwrap();
3944
3945		let mut hidden = producer.consume().excluding(origin(7)).announced();
3946		hidden.assert_next_wait();
3947
3948		let mut visible = producer.consume().excluding(origin(8)).announced();
3949		visible.assert_next_active("room");
3950	}
3951
3952	#[tokio::test]
3953	async fn exclude_matches_via_when_the_chain_is_anonymous() {
3954		let producer = origin(1).produce();
3955		let assigned = origin(777);
3956		let _echoed = producer
3957			.announce("echoed", Route::default().with_hops(hops(&[0])).with_via(assigned))
3958			.unwrap();
3959		let _local = producer
3960			.announce("local", Route::default().with_hops(hops(&[10])))
3961			.unwrap();
3962
3963		let mut hidden = producer.consume().excluding(assigned).announced();
3964		hidden.assert_next_active("local");
3965		hidden.assert_next_wait();
3966	}
3967
3968	#[tokio::test]
3969	async fn anonymous_route_loses_to_identified_at_any_cost() {
3970		let producer = origin(1).produce();
3971		let mut announced = producer.consume().announced();
3972
3973		let _anonymous = producer
3974			.announce("room", Route::default().with_hops(hops(&[0])).with_cost(1))
3975			.unwrap();
3976		let route = announced.assert_next_active("room");
3977		assert!(route.is_anonymous());
3978		assert_eq!(route.cost, Cost::new(1));
3979
3980		let _identified = producer
3981			.announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
3982			.unwrap();
3983		let route = announced.assert_next_active("room");
3984		assert!(!route.is_anonymous());
3985		assert_eq!(route.cost, Cost::new(5));
3986	}
3987
3988	#[tokio::test]
3989	async fn anonymous_routes_order_by_cost() {
3990		let producer = origin(1).produce();
3991		let mut announced = producer.consume().announced();
3992
3993		let expensive = producer
3994			.announce("room", Route::default().with_hops(hops(&[0])).with_cost(5))
3995			.unwrap();
3996		let route = announced.assert_next_active("room");
3997		assert_eq!(route.cost, Cost::new(5));
3998
3999		let _cheap = producer
4000			.announce("room", Route::default().with_hops(hops(&[0, 7])).with_cost(1))
4001			.unwrap();
4002		let route = announced.assert_next_active("room");
4003		assert!(route.is_anonymous());
4004		assert_eq!(route.cost, Cost::new(1));
4005
4006		drop(expensive);
4007		announced.assert_next_wait();
4008	}
4009
4010	#[tokio::test]
4011	async fn anonymous_chain_from_identified_peer_still_ranks_last() {
4012		let producer = origin(1).produce();
4013		let mut announced = producer.consume().announced();
4014
4015		let _anonymous = producer
4016			.announce(
4017				"room",
4018				Route::default()
4019					.with_hops(hops(&[0, 7]))
4020					.with_cost(1)
4021					.with_via(origin(7)),
4022			)
4023			.unwrap();
4024		announced.assert_next_active("room");
4025
4026		let _identified = producer
4027			.announce("room", Route::default().with_hops(hops(&[10, 20])).with_cost(5))
4028			.unwrap();
4029		let route = announced.assert_next_active("room");
4030		assert!(!route.is_anonymous());
4031		assert_eq!(route.cost, Cost::new(5));
4032	}
4033
4034	#[tokio::test]
4035	async fn request_prefers_identified_over_cheaper_anonymous() {
4036		let producer = origin(1).produce();
4037		let consumer = producer.consume();
4038
4039		let anonymous = producer
4040			.dynamic("room", Route::default().with_hops(hops(&[0])).with_cost(1))
4041			.unwrap();
4042		let identified = producer
4043			.dynamic("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4044			.unwrap();
4045
4046		let _pending = consumer.request_broadcast("room/alice");
4047		let request = queued(&identified).await;
4048		assert_eq!(request.path().as_str(), "room/alice");
4049		assert!(
4050			anonymous.poll_requested_broadcast(&kio::Waiter::noop()).is_pending(),
4051			"the cheaper anonymous route must not serve"
4052		);
4053	}
4054
4055	#[tokio::test]
4056	async fn update_reprices_in_place() {
4057		let producer = origin(1).produce();
4058		let mut announced = producer.consume().announced();
4059
4060		let announcement = producer.announce("room", Route::default()).unwrap();
4061		announced.assert_next_active("room");
4062
4063		announcement.update(Route::default().with_cost(9)).unwrap();
4064		let route = announced.assert_next_active("room");
4065		assert_eq!(route.cost, Cost::new(9));
4066	}
4067
4068	#[tokio::test]
4069	async fn retract_after_undelivered_reprice_still_delivered() {
4070		let producer = origin(1).produce();
4071		let mut announced = producer.consume().announced();
4072
4073		let announcement = producer.announce("room", Route::default()).unwrap();
4074		announced.assert_next_active("room");
4075
4076		// Reprice, then retract before the consumer observes the reprice: the
4077		// pending metadata update must not cancel the retraction the delivered
4078		// announce still owes.
4079		announcement.update(Route::default().with_cost(9)).unwrap();
4080		drop(announcement);
4081		announced.assert_next_ended("room");
4082		announced.assert_next_wait();
4083	}
4084
4085	#[tokio::test]
4086	async fn scoped_cursor_advertises_most_specific_covering_route() {
4087		let producer = origin(1).produce();
4088		// Broad and cheap; narrow and expensive. Both present relative to a cursor
4089		// rooted below them, and the narrow one is what a request there resolves.
4090		let _broad = producer.announce("room", Route::default().with_cost(1)).unwrap();
4091		let _narrow = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
4092
4093		let consumer = producer
4094			.consume()
4095			.scope("room/alice", &Patterns::from(Pattern::all()))
4096			.unwrap();
4097		let mut announced = consumer.announced();
4098		let route = announced.assert_next_active("");
4099		assert_eq!(route.cost, Cost::new(9));
4100		announced.assert_next_wait();
4101	}
4102
4103	#[tokio::test]
4104	async fn capture_change_retracts_before_reannouncing_a_presented_prefix() {
4105		let producer = origin(1).produce();
4106		let _broad = producer.announce("room", Route::default()).unwrap();
4107		let exact = producer.announce("room/alice", Route::default()).unwrap();
4108		let consumer = producer
4109			.consume()
4110			.scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
4111			.unwrap()
4112			.scope("room/alice", &Patterns::from(Pattern::all()))
4113			.unwrap();
4114		let mut announced = consumer.announced();
4115
4116		let first = announced.next().now_or_never().expect("next").expect("announce");
4117		assert_eq!(first.prefix.as_str(), "");
4118		assert_eq!(first.kind, AnnounceKind::Announced);
4119		assert_eq!(first.captures, Some(Vec::new()));
4120
4121		drop(exact);
4122		let retracted = announced.next().now_or_never().expect("next").expect("retract");
4123		assert_eq!(retracted.prefix.as_str(), "");
4124		assert_eq!(retracted.kind, AnnounceKind::Retracted);
4125		assert_eq!(retracted.captures, Some(Vec::new()));
4126		let replacement = announced.next().now_or_never().expect("next").expect("announce");
4127		assert_eq!(replacement.prefix.as_str(), "");
4128		assert_eq!(replacement.kind, AnnounceKind::Announced);
4129		assert_eq!(replacement.captures, None);
4130	}
4131
4132	#[tokio::test]
4133	async fn routed_broadcast_resolves_once_announced() {
4134		let producer = origin(1).produce();
4135		let consumer = producer.consume();
4136
4137		// Asking before anything is announced parks instead of failing Unroutable.
4138		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4139		assert!((&mut resolving).now_or_never().is_none());
4140
4141		let broadcast = producer.create_broadcast("room/alice").unwrap();
4142		let resolved = resolving.await.expect("resolves once created");
4143		assert_eq!(resolved.info().path.as_str(), "room/alice");
4144		drop(broadcast);
4145	}
4146
4147	#[tokio::test]
4148	async fn local_broadcast_resolves_by_exact_path() {
4149		let producer = origin(1).produce();
4150		let consumer = producer.consume();
4151
4152		let broadcast = producer.create_broadcast("room/alice").unwrap();
4153		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4154		assert_eq!(resolved.info().path.as_str(), "room/alice");
4155		drop(broadcast);
4156
4157		// Nothing covers an unknown path and no handler exists.
4158		let err = consumer
4159			.request_broadcast("room/bob")
4160			.now_or_never()
4161			.expect("unroutable is synchronous")
4162			.err()
4163			.unwrap();
4164		assert!(matches!(err, Error::Unroutable));
4165	}
4166
4167	#[test]
4168	fn create_broadcast_accepts_a_max_depth_path() {
4169		let producer = origin(1).produce();
4170		let path = vec!["a"; Path::MAX_PARTS].join("/");
4171		let _broadcast = producer.create_broadcast(path.as_str()).expect("max depth is allowed");
4172		let deeper = vec!["a"; Path::MAX_PARTS + 1].join("/");
4173		assert!(matches!(
4174			producer.create_broadcast(deeper.as_str()),
4175			Err(Error::BoundsExceeded(_))
4176		));
4177	}
4178
4179	#[tokio::test]
4180	async fn duplicate_routes_aggregate_until_the_last_leaves() {
4181		let producer = origin(1).produce();
4182		let first = producer.dynamic("live", Route::default().with_cost(3)).unwrap();
4183		let second = producer.dynamic("live", Route::default().with_cost(1)).unwrap();
4184
4185		let mut announced = producer.consume().announced();
4186		let update = announced.next().now_or_never().expect("next").expect("no next");
4187		assert_eq!(update.prefix.as_str(), "live");
4188		assert_eq!(update.kind, AnnounceKind::Announced);
4189		assert_eq!(update.route.cost, Cost::new(1));
4190		announced.assert_next_wait();
4191
4192		drop(second);
4193		let update = announced.next().now_or_never().expect("next").expect("no next");
4194		assert_eq!(update.prefix.as_str(), "live");
4195		assert_eq!(update.kind, AnnounceKind::Updated);
4196		assert_eq!(update.route.cost, Cost::new(3));
4197
4198		drop(first);
4199		announced.assert_next_ended("live");
4200		announced.assert_next_wait();
4201	}
4202
4203	#[test]
4204	fn dynamic_may_cover_a_scope_but_disjoint_prefixes_are_refused() {
4205		let producer = origin(1).produce();
4206		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
4207		let _broad = scoped
4208			.dynamic("", Route::default())
4209			.expect("an overlapping prefix is accepted");
4210
4211		let _ok = scoped
4212			.dynamic("room/alice", Route::default())
4213			.expect("a contained prefix is accepted");
4214		assert!(matches!(
4215			scoped.dynamic("other", Route::default()),
4216			Err(Error::Unauthorized)
4217		));
4218	}
4219
4220	#[tokio::test]
4221	async fn dynamic_route_keeps_its_producer_scope() {
4222		let producer = origin(1).produce();
4223		let scope = Patterns::from("*/chat".parse::<Pattern>().unwrap());
4224		let scoped = producer.scope("", &scope).unwrap();
4225		let dynamic = scoped.dynamic("", Route::default()).unwrap();
4226
4227		let mut matching = producer
4228			.consume()
4229			.scope("", &scopes(&["room/chat"]))
4230			.unwrap()
4231			.announced();
4232		matching.assert_next_active("");
4233		let mut outside = producer
4234			.consume()
4235			.scope("", &scopes(&["room/video"]))
4236			.unwrap()
4237			.announced();
4238		outside.assert_next_wait();
4239
4240		let refused = producer
4241			.consume()
4242			.request_broadcast("room/video")
4243			.now_or_never()
4244			.expect("an out-of-scope request must be refused synchronously");
4245		assert!(matches!(refused, Err(Error::Unroutable)));
4246		assert!(dynamic.requested_broadcast().now_or_never().is_none());
4247
4248		let _pending = producer.consume().request_broadcast("room/chat");
4249		let request = queued(&dynamic).await;
4250		assert_eq!(request.path().as_str(), "room/chat");
4251	}
4252
4253	#[tokio::test]
4254	async fn dynamic_accepts_a_max_depth_prefix() {
4255		let producer = origin(1).produce();
4256		let path = (0..Path::MAX_PARTS)
4257			.map(|i| format!("s{i}"))
4258			.collect::<Vec<_>>()
4259			.join("/");
4260		let mut announced = producer.consume().announced();
4261
4262		let dynamic = producer.dynamic(&path, Route::default()).expect("max depth is allowed");
4263		announced.assert_next_active(&path);
4264
4265		let _pending = producer.consume().request_broadcast(&path);
4266		let request = queued(&dynamic).await;
4267		assert_eq!(request.path().as_str(), path);
4268	}
4269
4270	#[tokio::test]
4271	async fn dynamic_exclusion_skips_routes_through_the_subscriber() {
4272		let producer = origin(1).produce();
4273		let _server = producer
4274			.dynamic("live", Route::default().with_hops(hops(&[7])))
4275			.unwrap();
4276
4277		let mut excluded = producer.consume().excluding(origin(7)).announced();
4278		excluded.assert_next_wait();
4279
4280		let mut clean = producer.consume().excluding(origin(8)).announced();
4281		clean.assert_next_active("live");
4282	}
4283
4284	/// The consumer is a `Stream` of the same updates as `next`.
4285	#[tokio::test]
4286	async fn announce_consumer_is_a_stream() {
4287		use futures::StreamExt;
4288		let producer = origin(1).produce();
4289		let server = producer.dynamic("live", Route::default()).unwrap();
4290		let mut announced = producer.consume().announced();
4291		let update = StreamExt::next(&mut announced)
4292			.now_or_never()
4293			.expect("next")
4294			.expect("no next");
4295		assert_eq!(update.prefix.as_str(), "live");
4296		assert_eq!(update.kind, AnnounceKind::Announced);
4297		assert!(StreamExt::next(&mut announced).now_or_never().is_none());
4298		drop(server);
4299		let update = StreamExt::next(&mut announced)
4300			.now_or_never()
4301			.expect("next")
4302			.expect("no next");
4303		assert_eq!(update.kind, AnnounceKind::Retracted);
4304	}
4305
4306	#[tokio::test]
4307	async fn dynamic_retracts() {
4308		let producer = origin(1).produce();
4309		let server = producer.dynamic("live", Route::default()).unwrap();
4310		let mut announced = producer.consume().announced();
4311		announced.assert_next_active("live");
4312
4313		drop(server);
4314		announced.assert_next_ended("live");
4315	}
4316
4317	#[test]
4318	fn charged_wildcard_cost_accumulates_across_hops() {
4319		let first = Cost::new(4).charged(1);
4320		let second = first.charged(2);
4321		assert_eq!(second, Cost { warm: 7, cold: 7 });
4322	}
4323
4324	#[tokio::test]
4325	async fn local_broadcast_is_announced_only_locally() {
4326		let producer = origin(1).produce();
4327		let mut local = producer.consume().announced();
4328		let mut peer = producer.consume().excluding(Hop::UNKNOWN).announced();
4329		let broadcast = producer.create_broadcast("room/alice").unwrap();
4330		local.assert_next_active("room/alice");
4331		peer.assert_next_wait();
4332		drop(broadcast);
4333		local.assert_next_ended("room/alice");
4334		peer.assert_next_wait();
4335	}
4336
4337	#[tokio::test]
4338	async fn served_route_materializes_on_demand() {
4339		let producer = origin(1).produce();
4340		let consumer = producer.consume();
4341
4342		let server = producer.dynamic("room", Route::default()).unwrap();
4343
4344		let pending = consumer.request_broadcast("room/alice");
4345		let request = queued(&server).await;
4346		assert_eq!(request.path().as_str(), "room/alice");
4347
4348		let source = broadcast::Info::new().produce();
4349		request.accept(&source);
4350
4351		let resolved = pending.await.expect("resolves");
4352		// The handle is named by what the requester asked for.
4353		assert_eq!(resolved.info().path.as_str(), "room/alice");
4354
4355		// A repeat request shares the served broadcast instead of re-asking.
4356		let again = consumer.request_broadcast("room/alice").await.expect("resolves");
4357		assert!(again.is_clone(&resolved));
4358	}
4359
4360	#[tokio::test]
4361	async fn served_requests_coalesce() {
4362		let producer = origin(1).produce();
4363		let consumer = producer.consume();
4364		let server = producer.dynamic("room", Route::default()).unwrap();
4365
4366		let first = consumer.request_broadcast("room/alice");
4367		let second = consumer.request_broadcast("room/alice");
4368
4369		let request = queued(&server).await;
4370		// Only one request reaches the server.
4371		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4372
4373		let source = broadcast::Info::new().produce();
4374		request.accept(&source);
4375
4376		let first = first.await.expect("resolves");
4377		let second = second.await.expect("resolves");
4378		assert!(first.is_clone(&second));
4379	}
4380
4381	#[tokio::test]
4382	async fn retract_rejects_pending_requests() {
4383		let producer = origin(1).produce();
4384		let consumer = producer.consume();
4385		let server = producer.dynamic("room", Route::default()).unwrap();
4386
4387		let pending = consumer.request_broadcast("room/alice");
4388		drop(server);
4389
4390		let err = pending.await.err().unwrap();
4391		assert!(matches!(err, Error::Unroutable));
4392
4393		// With the route gone, later requests are unroutable immediately.
4394		let err = consumer
4395			.request_broadcast("room/alice")
4396			.now_or_never()
4397			.expect("unroutable")
4398			.err()
4399			.unwrap();
4400		assert!(matches!(err, Error::Unroutable));
4401	}
4402
4403	#[tokio::test]
4404	async fn routed_broadcast_survives_serving_route_retraction() {
4405		let producer = origin(1).produce();
4406		let consumer = producer.consume();
4407
4408		// Three identical routes, oldest first: the newest identical route wins
4409		// requests, and swapping between them emits no announce update.
4410		let standby_server = producer.dynamic("room", Route::default()).unwrap();
4411		let second_server = producer.dynamic("room", Route::default()).unwrap();
4412		let incumbent_server = producer.dynamic("room", Route::default()).unwrap();
4413
4414		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4415		assert!((&mut resolving).now_or_never().is_none());
4416
4417		// Each incumbent dies with the front's request in flight on it: the
4418		// front's watcher observes the retraction and retries through the next
4419		// standby instead of parking on an announce update that never comes. Two
4420		// retractions in a row, so the announce stream's initial coverage replay
4421		// cannot paper over the missing retry.
4422		drop(incumbent_server);
4423		assert!((&mut resolving).now_or_never().is_none());
4424		drop(second_server);
4425		assert!((&mut resolving).now_or_never().is_none());
4426
4427		let request = queued(&standby_server).await;
4428		let source = broadcast::Info::new().produce();
4429		request.accept(&source);
4430
4431		let resolved = resolving.await.expect("resolves via the standby");
4432		assert_eq!(resolved.info().path.as_str(), "room/alice");
4433	}
4434
4435	#[tokio::test]
4436	async fn split_horizon_skips_routes_through_the_requester() {
4437		let producer = origin(1).produce();
4438		let _server = producer
4439			.dynamic("room", Route::default().with_hops(hops(&[7])))
4440			.unwrap();
4441
4442		// The requester's own bytes must not be served back to it.
4443		let excluded = producer.consume().excluding(origin(7));
4444		let err = excluded
4445			.request_broadcast("room/alice")
4446			.now_or_never()
4447			.expect("unroutable")
4448			.err()
4449			.unwrap();
4450		assert!(matches!(err, Error::Unroutable));
4451
4452		// A clean requester resolves through the route (the request queues).
4453		let clean = producer.consume().excluding(origin(8));
4454		let pending = clean.request_broadcast("room/alice");
4455		assert!(pending.now_or_never().is_none());
4456	}
4457
4458	/// A handler that rejects a path with `Unroutable` while its route stands
4459	/// gives the requester that answer; the front must not re-ask the same route
4460	/// forever, which would spin the origin driver.
4461	#[tokio::test]
4462	async fn handler_rejection_is_final() {
4463		let producer = origin(1).produce();
4464		let consumer = producer.consume();
4465		let server = producer.dynamic("room", Route::default()).unwrap();
4466
4467		let pending = consumer.request_broadcast("room/alice");
4468		let request = queued(&server).await;
4469		request.reject(Error::Unroutable);
4470		let err = tokio::time::timeout(Duration::from_secs(5), pending)
4471			.await
4472			.expect("the front must give up, not spin")
4473			.err()
4474			.unwrap();
4475		assert!(matches!(err, Error::Unroutable));
4476
4477		// The route still stands and serves the next path.
4478		let pending = consumer.request_broadcast("room/bob");
4479		let request = queued(&server).await;
4480		assert_eq!(request.path().as_str(), "room/bob");
4481		let served = broadcast::Info::new().produce();
4482		request.accept(&served);
4483		pending.await.expect("resolves");
4484	}
4485
4486	/// `routed_broadcast` treats a handler's rejection as the table's verdict:
4487	/// it waits for the table to move instead of re-asking the same route.
4488	#[tokio::test]
4489	async fn routed_broadcast_waits_out_a_rejection() {
4490		let producer = origin(1).produce();
4491		let consumer = producer.consume();
4492		let server = producer.dynamic("room", Route::default()).unwrap();
4493
4494		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4495		assert!((&mut resolving).now_or_never().is_none());
4496		let request = queued(&server).await;
4497		request.reject(Error::Unroutable);
4498
4499		// Parked: the route stands, so nothing changed that a retry could use.
4500		for _ in 0..20 {
4501			tokio::task::yield_now().await;
4502		}
4503		assert!((&mut resolving).now_or_never().is_none());
4504		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4505
4506		// A re-price moves the table: the retry reaches the handler, which serves it.
4507		server.update(Route::default().with_cost(2)).unwrap();
4508		assert!((&mut resolving).now_or_never().is_none());
4509		let request = queued(&server).await;
4510		let served = broadcast::Info::new().produce();
4511		request.accept(&served);
4512		resolving.await.expect("resolves");
4513	}
4514
4515	/// A local broadcast serves its path without announcing it, so there is no
4516	/// route to wait for: the request resolves on the first pass.
4517	#[tokio::test]
4518	async fn routed_broadcast_resolves_an_unannounced_local_broadcast() {
4519		let producer = origin(1).produce();
4520		let consumer = producer.consume();
4521
4522		let _local = producer.create_broadcast("room/alice").unwrap();
4523		let resolved = tokio::time::timeout(Duration::from_secs(5), consumer.routed_broadcast("room/alice"))
4524			.await
4525			.expect("resolves without an announce")
4526			.expect("resolves locally");
4527		assert_eq!(resolved.info().path.as_str(), "room/alice");
4528	}
4529
4530	/// Teardown rejects a parked request with `Dropped`, but a destroyed origin
4531	/// is `Closed` to `routed_broadcast`'s callers.
4532	#[tokio::test]
4533	async fn routed_broadcast_reports_teardown_as_closed() {
4534		let (producer, driver) = Producer::new(Config::new(origin(1)));
4535		let consumer = producer.consume();
4536		let _server = producer.dynamic("room", Route::default()).unwrap();
4537
4538		// Park on the covering route, past the loop's closed check.
4539		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4540		assert!((&mut resolving).now_or_never().is_none());
4541
4542		drop(driver);
4543
4544		let err = tokio::time::timeout(Duration::from_secs(5), resolving)
4545			.await
4546			.expect("teardown resolves the wait")
4547			.err()
4548			.unwrap();
4549		assert!(matches!(err, Error::Closed), "unexpected end: {err}");
4550	}
4551
4552	/// A local broadcast appearing at the exact path is a table change too: a
4553	/// requester parked on a handler's rejection resolves to it.
4554	#[tokio::test]
4555	async fn routed_broadcast_wakes_for_a_local_broadcast() {
4556		let producer = origin(1).produce();
4557		let consumer = producer.consume();
4558		let server = producer.dynamic("room", Route::default()).unwrap();
4559
4560		let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4561		assert!((&mut resolving).now_or_never().is_none());
4562		queued(&server).await.reject(Error::Unroutable);
4563		for _ in 0..20 {
4564			tokio::task::yield_now().await;
4565		}
4566		assert!((&mut resolving).now_or_never().is_none());
4567
4568		// Unannounced, so no route changes: the exact path itself is what moved.
4569		let _local = producer.create_broadcast("room/alice").unwrap();
4570		let resolved = resolving.await.expect("resolves locally");
4571		assert_eq!(resolved.info().path.as_str(), "room/alice");
4572		assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4573	}
4574
4575	/// A track first subscribed after the front is already serving another still
4576	/// replays what its source holds, like the first track did.
4577	#[tokio::test]
4578	async fn late_track_on_a_served_front_replays() {
4579		let producer = origin(1).produce();
4580		let consumer = producer.consume();
4581		let server = producer.dynamic("room", Route::default()).unwrap();
4582
4583		let source = broadcast::Info::new().produce();
4584		for name in ["a", "b"] {
4585			let track = source.create_track(name, None).unwrap();
4586			let mut group = track.append_group().unwrap();
4587			group.write_frame(crate::Timestamp::ZERO, name.as_bytes()).unwrap();
4588			group.finish().unwrap();
4589			// The producer stays alive: the track is open, like a live SI track.
4590			std::mem::forget(track);
4591		}
4592
4593		let pending = consumer.request_broadcast("room/alice");
4594		queued(&server).await.accept(&source);
4595		let resolved = pending.await.expect("resolves");
4596
4597		let budget = track::Subscription::default().with_max_age(Duration::from_secs(3600));
4598		for name in ["a", "b"] {
4599			let mut subscription = resolved
4600				.track(name)
4601				.unwrap()
4602				.subscribe(budget.clone())
4603				.await
4604				.expect("subscribe");
4605			let mut group = tokio::time::timeout(Duration::from_secs(5), subscription.recv_group())
4606				.await
4607				.expect("the late track must replay, not park")
4608				.expect("recv group")
4609				.expect("track ended early");
4610			let frame = group.read_frame().await.expect("read frame").expect("frame");
4611			assert_eq!(&frame.payload[..], name.as_bytes());
4612		}
4613	}
4614
4615	#[tokio::test]
4616	async fn most_specific_prefix_shadows() {
4617		let producer = origin(1).produce();
4618		let consumer = producer.consume();
4619
4620		let broad_server = producer.dynamic("", Route::default()).unwrap();
4621		// A narrow advertise-only claim: requests under it must NOT route to the
4622		// broad server; they fall through to the (absent) fallback handler.
4623		let _narrow = producer.announce(".dash", Route::default()).unwrap();
4624
4625		let err = consumer
4626			.request_broadcast(".dash/pid")
4627			.now_or_never()
4628			.expect("unroutable")
4629			.err()
4630			.unwrap();
4631		assert!(matches!(err, Error::Unroutable));
4632
4633		// Everything else still routes to the broad server.
4634		let _pending = consumer.request_broadcast("room/alice");
4635		let request = queued(&broad_server).await;
4636		assert_eq!(request.path().as_str(), "room/alice");
4637	}
4638
4639	#[tokio::test]
4640	async fn root_dynamic_serves_any_path() {
4641		let producer = origin(1).produce();
4642		let consumer = producer.consume();
4643		let mut announced = consumer.announced();
4644		let dynamic = producer.dynamic("", Route::default()).unwrap();
4645		// The root claim is advertised like any other prefix.
4646		announced.assert_next_active("");
4647
4648		let pending = consumer.request_broadcast("anything/at/all");
4649		let request = queued(&dynamic).await;
4650		assert_eq!(request.path().as_str(), "anything/at/all");
4651
4652		let source = broadcast::Info::new().produce();
4653		request.accept(&source);
4654		let resolved = pending.await.expect("resolves");
4655		assert_eq!(resolved.info().path.as_str(), "anything/at/all");
4656
4657		// Nothing serves an uncovered path once the handler is gone.
4658		drop(dynamic);
4659		announced.assert_next_ended("");
4660		let err = consumer
4661			.request_broadcast("something/else")
4662			.now_or_never()
4663			.expect("unroutable")
4664			.err()
4665			.unwrap();
4666		assert!(matches!(err, Error::Unroutable));
4667	}
4668
4669	/// A path outside the consumer's scope never reaches a live dynamic handler.
4670	///
4671	/// `scope` is authoritative, so an out-of-scope path is unauthorized before
4672	/// routing can send a request to the handler. A `Request` carries only a path,
4673	/// so the handler cannot tell who asked.
4674	#[tokio::test]
4675	async fn out_of_scope_request_never_reaches_the_dynamic_handler() {
4676		let producer = origin(1).produce();
4677		let dynamic = producer.dynamic("", Route::default()).unwrap();
4678		let scoped = producer.consume().scope("", &scopes(&["tenant-a"])).unwrap();
4679
4680		// `tenant-a-other` shares a character prefix but not a segment, so this
4681		// also pins that the check is segment-aware rather than textual.
4682		for path in ["tenant-b/live", "tenant-a-other/live"] {
4683			let refused = scoped
4684				.request_broadcast(path)
4685				.now_or_never()
4686				.expect("an out-of-scope request must be refused synchronously, not queued");
4687			assert!(matches!(refused, Err(Error::Unauthorized)));
4688			assert!(
4689				dynamic.requested_broadcast().now_or_never().is_none(),
4690				"the dynamic handler was asked to create a broadcast the requester may not read"
4691			);
4692		}
4693	}
4694
4695	#[tokio::test]
4696	async fn routed_waits_for_coverage() {
4697		let producer = origin(1).produce();
4698		let consumer = producer.consume();
4699
4700		let mut fut = consumer.routed("room/alice").boxed();
4701		assert!((&mut fut).now_or_never().is_none());
4702
4703		// A covering prefix resolves the wait.
4704		let _a = producer.announce("room", Route::default().with_cost(3)).unwrap();
4705		let route = fut.now_or_never().expect("covered").expect("routed");
4706		assert_eq!(route.cost, Cost::new(3));
4707
4708		// Already covered: resolves immediately.
4709		consumer
4710			.routed("room/alice/cam")
4711			.now_or_never()
4712			.expect("covered")
4713			.expect("routed");
4714	}
4715
4716	#[tokio::test]
4717	async fn routed_ignores_deeper_routes() {
4718		let producer = origin(1).produce();
4719		let consumer = producer.consume();
4720
4721		// A deeper route does not cover the shorter path.
4722		let _deep = producer.announce("room/alice/cam", Route::default()).unwrap();
4723		let mut fut = consumer.routed("room/alice").boxed();
4724		assert!((&mut fut).now_or_never().is_none());
4725
4726		let _exact = producer.announce("room/alice", Route::default()).unwrap();
4727		fut.now_or_never().expect("covered").expect("routed");
4728	}
4729
4730	#[tokio::test]
4731	async fn routed_accepts_a_max_depth_path() {
4732		let producer = origin(1).produce();
4733		let consumer = producer.consume();
4734		let path = (0..Path::MAX_PARTS)
4735			.map(|i| format!("s{i}"))
4736			.collect::<Vec<_>>()
4737			.join("/");
4738		assert_eq!(Path::new(&path).parts().count(), Path::MAX_PARTS);
4739
4740		assert!(consumer.allowed().matches(&path));
4741
4742		let mut fut = consumer.routed(&path).boxed();
4743		assert!((&mut fut).now_or_never().is_none());
4744
4745		// A covering root still resolves: the lookup must not require `path/**`.
4746		let _a = producer.announce("", Route::default()).unwrap();
4747		fut.now_or_never().expect("covered").expect("routed");
4748	}
4749
4750	#[tokio::test]
4751	async fn teardown_ends_everything() {
4752		let (producer, driver) = Producer::new(Config::new(origin(1)));
4753		let consumer = producer.consume();
4754		let _announcement = producer.announce("room", Route::default()).unwrap();
4755		let mut announced = consumer.announced();
4756		announced.assert_next_active("room");
4757
4758		let _server = producer.dynamic("served", Route::default()).unwrap();
4759		let pending = consumer.request_broadcast("served/path");
4760
4761		drop(driver);
4762
4763		// The cursor observes the end (after draining pending updates).
4764		announced.assert_next_active("served");
4765		assert!(announced.next().now_or_never().expect("ended").is_none());
4766
4767		// Pending requests reject; new work refuses.
4768		assert!(pending.now_or_never().expect("rejected").is_err());
4769		assert!(matches!(producer.announce("x", Route::default()), Err(Error::Closed)));
4770		assert!(matches!(producer.create_broadcast("x"), Err(Error::Closed)));
4771		let err = consumer
4772			.request_broadcast("y")
4773			.now_or_never()
4774			.expect("closed")
4775			.err()
4776			.unwrap();
4777		assert!(matches!(err, Error::Closed));
4778
4779		// A cursor born after the teardown is born ended.
4780		let mut late = consumer.announced();
4781		assert!(late.next().now_or_never().expect("ended").is_none());
4782	}
4783
4784	/// One live subscription reading a track through a remote front, plus the
4785	/// bookkeeping to kill and replace its serving route.
4786	struct ResumeRig {
4787		producer: Producer,
4788		resolved: broadcast::Consumer,
4789		subscription: track::Subscriber,
4790		/// Keeps the incumbent's track producing; dropping it would abort the
4791		/// track out from under the front mid-test.
4792		incumbent_track: track::Producer,
4793	}
4794
4795	impl ResumeRig {
4796		/// Announce a served route with `first` as its first hop, materialize
4797		/// "room/alice" through it with a one-group "before" track, and subscribe.
4798		async fn new(first: &[u64]) -> (Self, Dynamic, broadcast::Producer) {
4799			let producer = origin(1).produce();
4800			let consumer = producer.consume();
4801
4802			let server = producer
4803				.dynamic("room", Route::default().with_hops(hops(first)))
4804				.unwrap();
4805
4806			let pending = consumer.request_broadcast("room/alice");
4807			let request = queued(&server).await;
4808			let source = broadcast::Info::new().produce();
4809			let track = source.create_track("video", None).unwrap();
4810			let mut group = track.append_group().unwrap();
4811			group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
4812			group.finish().unwrap();
4813			request.accept(&source);
4814
4815			let resolved = pending.await.expect("resolves");
4816			let mut subscription = resolved
4817				.track("video")
4818				.unwrap()
4819				.subscribe(None)
4820				.await
4821				.expect("subscribe");
4822			let mut group = subscription
4823				.recv_group()
4824				.await
4825				.expect("recv group")
4826				.expect("track ended early");
4827			let frame = group.read_frame().await.expect("read frame").expect("frame");
4828			assert_eq!(&frame.payload[..], b"before");
4829
4830			(
4831				Self {
4832					producer,
4833					resolved,
4834					subscription,
4835					incumbent_track: track,
4836				},
4837				server,
4838				source,
4839			)
4840		}
4841
4842		/// Stand up a second served route with `first` as its first hop and hand
4843		/// back its handle, ready to answer the front's re-request.
4844		fn standby(&self, first: &[u64]) -> Dynamic {
4845			self.producer
4846				.dynamic("room", Route::default().with_hops(hops(first)))
4847				.unwrap()
4848		}
4849	}
4850
4851	/// Accept the front's re-request on `server` with a source carrying the same
4852	/// content stream (the delivered group plus its successor) and prove the
4853	/// rig's subscription resumes onto it: the successor group is delivered on
4854	/// the same subscription, at the group boundary.
4855	async fn assert_resumes(rig: &mut ResumeRig, server: &Dynamic) {
4856		let request = queued(server).await;
4857		let replacement = broadcast::Info::new().produce();
4858		let track = replacement.create_track("video", None).unwrap();
4859		// The same content: group 0 was already delivered through the old route,
4860		// so the splice resumes at group 1.
4861		let mut group = track.append_group().unwrap();
4862		group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
4863		group.finish().unwrap();
4864		request.accept(&replacement);
4865
4866		let mut group = track.append_group().unwrap();
4867		group.write_frame(crate::Timestamp::ZERO, b"resumed".as_ref()).unwrap();
4868		group.finish().unwrap();
4869
4870		let mut group = rig
4871			.subscription
4872			.recv_group()
4873			.await
4874			.expect("subscription survives the failover")
4875			.expect("track ended early");
4876		let frame = group.read_frame().await.expect("read frame").expect("frame");
4877		assert_eq!(&frame.payload[..], b"resumed");
4878	}
4879
4880	/// The driver's completion contract: it resolves once every producer handle
4881	/// drops, however many read handles remain.
4882	#[tokio::test]
4883	async fn driver_resolves_with_live_consumers() {
4884		let (producer, driver) = Producer::new(Config::new(origin(1)));
4885		let consumer = producer.consume();
4886		let run = crate::time::run(driver);
4887		drop(producer);
4888		tokio::time::timeout(Duration::from_secs(5), run)
4889			.await
4890			.expect("driver must finish once the producers are gone");
4891		drop(consumer);
4892	}
4893
4894	#[tokio::test]
4895	async fn remote_source_resumes_through_same_first_hop() {
4896		let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
4897		let standby_server = rig.standby(&[10, 20]);
4898
4899		// The serving route dies: retraction plus source abort, like a session.
4900		drop(incumbent);
4901		drop(source);
4902
4903		// The standby shares the first hop, so the subscription resumes there.
4904		assert_resumes(&mut rig, &standby_server).await;
4905	}
4906
4907	/// A source claiming the same content cannot change immutable track metadata:
4908	/// the successor is refused instead of the subscriber's samples being read on
4909	/// a different grid, and the verdict outlives the aborted logical track.
4910	#[tokio::test]
4911	async fn incompatible_successor_is_refused() {
4912		for replacement in [
4913			track::Info::default().with_timescale(crate::Timescale::MICRO),
4914			track::Info::default().with_priority(7),
4915			track::Info::default().with_max_age(Duration::from_secs(7)),
4916		] {
4917			let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
4918			let standby_server = rig.standby(&[10, 20]);
4919			drop(incumbent);
4920			drop(source);
4921
4922			// The standby shares the first hop, so the front re-requests through it,
4923			// but its copy of the track is on another grid.
4924			let request = queued(&standby_server).await;
4925			let successor = broadcast::Info::new().produce();
4926			let track = successor.create_track("video", replacement).unwrap();
4927			let mut group = track.append_group().unwrap();
4928			group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
4929			group.finish().unwrap();
4930			request.accept(&successor);
4931
4932			assert!(
4933				matches!(rig.subscription.recv_group().await, Err(Error::Unsupported)),
4934				"the subscription must abort rather than resume onto incompatible metadata"
4935			);
4936
4937			// Reopening the aborted logical track must not forget the broadcast's metadata.
4938			let reopened = rig.resolved.track("video").unwrap();
4939			assert!(matches!(reopened.query().await, Err(Error::Unsupported)));
4940			assert!(matches!(reopened.subscribe(None).await, Err(Error::Unsupported)));
4941		}
4942	}
4943
4944	#[tokio::test]
4945	async fn different_first_hop_ends_the_subscription() {
4946		let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
4947		// Another publisher entirely: same path, different first hop.
4948		let rival_server = rig.standby(&[11]);
4949
4950		// The incumbent's session dies, taking its track with it: a live copy
4951		// would otherwise keep serving after the front ends.
4952		drop(incumbent);
4953		drop(source);
4954		rig.incumbent_track.abort(Error::Dropped).unwrap();
4955
4956		// The subscription ends rather than splicing onto the rival's frames.
4957		let err = rig.subscription.recv_group().await.err().expect("subscription ends");
4958		assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
4959
4960		// A fresh request resolves through the rival.
4961		let consumer = rig.producer.consume();
4962		let pending = consumer.request_broadcast("room/alice");
4963		let request = queued(&rival_server).await;
4964		let replacement = broadcast::Info::new().produce();
4965		request.accept(&replacement);
4966		pending.await.expect("re-request resolves through the rival");
4967	}
4968
4969	#[tokio::test]
4970	async fn anonymous_routes_never_resume() {
4971		// An empty hop chain identifies nobody, so two of them must not pass for
4972		// one publisher reconnecting.
4973		let (mut rig, incumbent, source) = ResumeRig::new(&[]).await;
4974		let _twin_server = rig.standby(&[]);
4975
4976		drop(incumbent);
4977		drop(source);
4978		rig.incumbent_track.abort(Error::Dropped).unwrap();
4979
4980		let err = rig.subscription.recv_group().await.err().expect("subscription ends");
4981		assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
4982	}
4983
4984	/// An anonymous publisher that dies without unannouncing is replaced by the
4985	/// next anonymous session at the same path: a subscriber on a third session
4986	/// gets the newcomer's media immediately, not a lingering dead front.
4987	///
4988	/// The front closes with its last source, so the newcomer attaches a fresh
4989	/// one and the subscriber resolves it without parking.
4990	#[tokio::test]
4991	async fn anonymous_handoff_serves_the_newcomer_immediately() {
4992		let producer = origin(1).produce();
4993
4994		// Session A: an assigned anonymous hop serving the path.
4995		let server_a = producer
4996			.dynamic("room", Route::default().with_hops(hops(&[10])))
4997			.unwrap();
4998
4999		// Session C: a third anonymous session, excluding the hop the server
5000		// minted for it, the same split-horizon a live session applies.
5001		let consumer = producer.consume().excluding(origin(30));
5002		let pending = consumer.request_broadcast("room/alice");
5003		let request = queued(&server_a).await;
5004		let source_a = broadcast::Info::new().produce();
5005		let track_a = source_a.create_track("video", None).unwrap();
5006		let mut group = track_a.append_group().unwrap();
5007		group.write_frame(crate::Timestamp::ZERO, b"from-a".as_ref()).unwrap();
5008		group.finish().unwrap();
5009		request.accept(&source_a);
5010
5011		let resolved_a = pending.await.expect("resolves");
5012		let mut sub_a = resolved_a
5013			.track("video")
5014			.unwrap()
5015			.subscribe(None)
5016			.await
5017			.expect("subscribe");
5018		let mut group = sub_a
5019			.recv_group()
5020			.await
5021			.expect("recv group")
5022			.expect("track ended early");
5023		assert_eq!(
5024			&group.read_frame().await.expect("read frame").expect("frame").payload[..],
5025			b"from-a"
5026		);
5027
5028		// A dies without an unannounce: the source and its route drop together,
5029		// the way a lost session retracts rather than sending ANNOUNCE_END.
5030		drop(track_a);
5031		drop(source_a);
5032		drop(server_a);
5033
5034		// The front closed with A's last source.
5035		let err = sub_a.recv_group().await.err().expect("front closed");
5036		assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5037
5038		// No stale front at the leaf, and a repeat request does not join the
5039		// corpse: nothing covers the path, so it is Unroutable rather than
5040		// parked on a linger or 404 `dropped` from the dead front.
5041		settle(|| consumer.get_broadcast("room/alice").is_none()).await;
5042		settle(|| {
5043			matches!(
5044				consumer.request_broadcast("room/alice").now_or_never(),
5045				Some(Err(Error::Unroutable))
5046			)
5047		})
5048		.await;
5049
5050		// Session B attaches at the same path. Its front is served immediately.
5051		let server_b = producer
5052			.dynamic("room", Route::default().with_hops(hops(&[20])))
5053			.unwrap();
5054		let pending = consumer.request_broadcast("room/alice");
5055		let request = queued(&server_b).await;
5056		let source_b = broadcast::Info::new().produce();
5057		let track_b = source_b.create_track("video", None).unwrap();
5058		let mut group = track_b.append_group().unwrap();
5059		group.write_frame(crate::Timestamp::ZERO, b"from-b".as_ref()).unwrap();
5060		group.finish().unwrap();
5061		request.accept(&source_b);
5062
5063		let resolved_b = pending.await.expect("B's front is served immediately");
5064		assert!(
5065			!resolved_b.is_clone(&resolved_a),
5066			"B must not splice into A's closed front"
5067		);
5068
5069		let mut sub_b = resolved_b
5070			.track("video")
5071			.unwrap()
5072			.subscribe(None)
5073			.await
5074			.expect("subscribe");
5075		let mut group = sub_b
5076			.recv_group()
5077			.await
5078			.expect("recv group")
5079			.expect("track ended early");
5080		assert_eq!(
5081			&group.read_frame().await.expect("read frame").expect("frame").payload[..],
5082			b"from-b"
5083		);
5084	}
5085
5086	#[tokio::test]
5087	async fn reprice_is_invisible_to_the_subscription() {
5088		let (rig, incumbent, source) = ResumeRig::new(&[10]).await;
5089
5090		// A metadata-only reprice of the only route: nothing re-requests and the
5091		// subscription keeps flowing from the same source.
5092		incumbent
5093			.update(Route::default().with_hops(hops(&[10])).with_cost(9))
5094			.unwrap();
5095
5096		let track = source.create_track("audio", None).unwrap();
5097		let mut group = track.append_group().unwrap();
5098		group.write_frame(crate::Timestamp::ZERO, b"steady".as_ref()).unwrap();
5099		group.finish().unwrap();
5100
5101		let mut audio = rig
5102			.resolved
5103			.track("audio")
5104			.unwrap()
5105			.subscribe(None)
5106			.await
5107			.expect("subscribe survives the reprice");
5108		let mut group = audio
5109			.recv_group()
5110			.await
5111			.expect("recv group")
5112			.expect("track ended early");
5113		let frame = group.read_frame().await.expect("read frame").expect("frame");
5114		assert_eq!(&frame.payload[..], b"steady");
5115	}
5116
5117	#[tokio::test]
5118	async fn drain_reprice_migrates_before_the_session_dies() {
5119		let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5120		let standby_server = rig.standby(&[10, 20]);
5121
5122		// The serving route drains: repriced to the ceiling while its session
5123		// keeps serving. The front migrates to the standby without waiting for
5124		// the death.
5125		incumbent
5126			.update(Route::default().with_hops(hops(&[10])).with_cost(Cost::DRAIN))
5127			.unwrap();
5128
5129		assert_resumes(&mut rig, &standby_server).await;
5130
5131		// The drained source outlived the migration.
5132		drop(incumbent);
5133		drop(source);
5134	}
5135
5136	#[tokio::test]
5137	async fn local_sources_splice_newest_first() {
5138		let producer = origin(1).produce();
5139		let consumer = producer.consume();
5140
5141		let first = producer.create_broadcast("room/alice").unwrap();
5142		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5143
5144		// A second source at the same path joins the same front.
5145		let second = producer.create_broadcast("room/alice").unwrap();
5146		let again = consumer.request_broadcast("room/alice").await.expect("resolves");
5147		assert!(again.is_clone(&resolved));
5148
5149		// Losing one source keeps the front alive; losing both closes it.
5150		first.finish();
5151		settle(|| consumer.get_broadcast("room/alice").is_some()).await;
5152		second.finish();
5153		settle(|| consumer.get_broadcast("room/alice").is_none()).await;
5154
5155		// The path is free again for a fresh broadcast.
5156		let _third = producer.create_broadcast("room/alice").unwrap();
5157		assert!(consumer.get_broadcast("room/alice").is_some());
5158	}
5159
5160	/// The publisher finishes a track, then its broadcast. A subscription already in
5161	/// flight must conclude normally: the track's last group, then the end. moq-lite,
5162	/// ANNOUNCE_END: "Retraction does not disturb subscriptions already in flight,
5163	/// which conclude normally with SUBSCRIBE_END."
5164	///
5165	/// The runtime is single-threaded and the publisher's whole ending has no await in
5166	/// it, so the outcome does not depend on timing.
5167	#[tokio::test]
5168	async fn a_finished_broadcast_concludes_in_flight_subscriptions() {
5169		let producer = origin(1).produce();
5170		let consumer = producer.consume();
5171
5172		let broadcast = producer.create_broadcast("room/alice").unwrap();
5173		let track = broadcast.create_track("video", None).unwrap();
5174
5175		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5176		let mut subscription = resolved
5177			.track("video")
5178			.unwrap()
5179			.subscribe(None)
5180			.await
5181			.expect("subscribe");
5182		// A textbook clean end, innermost first: the group, the track, the broadcast.
5183		let mut group = track.append_group().unwrap();
5184		group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
5185		group.finish().unwrap();
5186		track.finish().unwrap();
5187		drop(track);
5188		broadcast.finish();
5189
5190		let mut group = next_group(&mut subscription)
5191			.await
5192			.expect("a cleanly finished track was served as an error")
5193			.expect("the track ended before its last group");
5194		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
5195		drop(group);
5196
5197		let end = next_group(&mut subscription)
5198			.await
5199			.expect("a cleanly finished track ended as an error");
5200		assert!(end.is_none(), "a group followed the final one");
5201	}
5202
5203	/// A route served from upstream is retracted (what the lite subscriber does on
5204	/// ANNOUNCE_END: finish the source it minted, drop the route) while the track's
5205	/// last group and end are still on their way. The subscription already in flight
5206	/// must still conclude normally. moq-lite, ANNOUNCE_END: "Retraction does not
5207	/// disturb subscriptions already in flight, which conclude normally with
5208	/// SUBSCRIBE_END."
5209	#[tokio::test]
5210	async fn a_retracted_route_concludes_in_flight_subscriptions() {
5211		let producer = origin(1).produce();
5212		let consumer = producer.consume();
5213		let server = producer
5214			.dynamic("room", Route::default().with_hops(hops(&[10])))
5215			.unwrap();
5216
5217		let pending = consumer.request_broadcast("room/alice");
5218		let request = queued(&server).await;
5219		let source = broadcast::Info::new().produce();
5220		let track = source.create_track("video", None).unwrap();
5221		request.accept(&source);
5222
5223		let resolved = pending.await.expect("resolves");
5224		let mut subscription = resolved
5225			.track("video")
5226			.unwrap()
5227			.subscribe(None)
5228			.await
5229			.expect("subscribe");
5230
5231		// ANNOUNCE_END overtakes the track's end: the route is retracted, and the front
5232		// has acted on it, before the track's last group and end arrive.
5233		source.finish();
5234		drop(server);
5235		settle(|| resolved.is_closed()).await;
5236		let mut group = track.append_group().unwrap();
5237		group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
5238		group.finish().unwrap();
5239		track.finish().unwrap();
5240		drop(track);
5241
5242		let mut group = next_group(&mut subscription)
5243			.await
5244			.expect("a retracted route's track was served as an error")
5245			.expect("the track ended before its last group");
5246		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
5247		drop(group);
5248
5249		let end = next_group(&mut subscription)
5250			.await
5251			.expect("a cleanly finished track ended as an error");
5252		assert!(end.is_none(), "a group followed the final one");
5253	}
5254
5255	/// An origin front drops the source track as soon as its last reader leaves,
5256	/// so the publisher's `unused()` resolves far below `TRACK_IDLE_LINGER`.
5257	/// Cached groups stay on the front for the linger; a returning reader
5258	/// replays them and re-splices for groups past that edge.
5259	#[tokio::test]
5260	async fn origin_front_drops_the_source_when_unused() {
5261		let producer = origin(1).produce();
5262		let consumer = producer.consume();
5263
5264		let broadcast = producer.create_broadcast("room/alice").unwrap();
5265		let track = broadcast.create_track("video", None).unwrap();
5266		let mut group = track.append_group().unwrap();
5267		group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
5268		group.finish().unwrap();
5269
5270		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5271		let mut subscription = resolved
5272			.track("video")
5273			.unwrap()
5274			.subscribe(None)
5275			.await
5276			.expect("subscribe");
5277		let mut group = subscription.recv_group().await.unwrap().unwrap();
5278		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
5279		drop(group);
5280		drop(subscription);
5281
5282		tokio::time::timeout(Duration::from_secs(1), track.unused())
5283			.await
5284			.expect("source unused should resolve far below TRACK_IDLE_LINGER")
5285			.expect("source closed");
5286
5287		// Cached groups stay on the front for the linger; a returning reader
5288		// replays them without waiting out the window.
5289		let mut again = resolved
5290			.track("video")
5291			.unwrap()
5292			.subscribe(track::Subscription::default().with_max_age(Duration::from_secs(3600)))
5293			.await
5294			.expect("resubscribe");
5295		let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
5296			.await
5297			.expect("cached group is still on the front")
5298			.expect("recv group")
5299			.expect("track ended early");
5300		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
5301
5302		tokio::time::timeout(Duration::from_secs(1), track.used())
5303			.await
5304			.expect("returning reader re-splices the source")
5305			.expect("source closed");
5306
5307		let mut group = track.append_group().unwrap();
5308		group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
5309		group.finish().unwrap();
5310		let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
5311			.await
5312			.expect("groups past the cached edge come from the re-splice")
5313			.expect("recv group")
5314			.expect("track ended early");
5315		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
5316	}
5317
5318	/// A front serving from another front's spliced copy has no snapshot to keep:
5319	/// it still drops upstream on the unused edge, so the publisher's `unused()`
5320	/// resolves far below `TRACK_IDLE_LINGER` through the whole chain. The next
5321	/// reader re-splices, paying `TRACK_INFO` again.
5322	#[tokio::test]
5323	async fn chained_front_drops_the_source_when_unused() {
5324		let leaf = origin(1).produce();
5325		let leaf_consumer = leaf.consume();
5326
5327		let broadcast = leaf.create_broadcast("room/alice").unwrap();
5328		let track = broadcast.create_track("video", None).unwrap();
5329		let mut group = track.append_group().unwrap();
5330		group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
5331		group.finish().unwrap();
5332
5333		// The leaf's front view: a spliced broadcast, so any front serving from
5334		// it holds a spliced source copy with nothing to snapshot.
5335		let leaf_front = leaf_consumer.request_broadcast("room/alice").await.expect("resolves");
5336
5337		let mid = origin(2).produce();
5338		let mid_server = mid.dynamic("room", Route::default().with_hops(hops(&[10]))).unwrap();
5339		let mid_pending = mid.consume().request_broadcast("room/alice");
5340		queued(&mid_server).await.accept(&leaf_front);
5341		let mid_resolved = mid_pending.await.expect("mid resolves");
5342
5343		let edge = origin(3).produce();
5344		let edge_server = edge.dynamic("room", Route::default().with_hops(hops(&[20]))).unwrap();
5345		let edge_pending = edge.consume().request_broadcast("room/alice");
5346		queued(&edge_server).await.accept(&mid_resolved);
5347		let edge_resolved = edge_pending.await.expect("edge resolves");
5348
5349		let mut subscription = edge_resolved
5350			.track("video")
5351			.unwrap()
5352			.subscribe(None)
5353			.await
5354			.expect("subscribe");
5355		let mut group = subscription.recv_group().await.unwrap().unwrap();
5356		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
5357		drop(group);
5358		drop(subscription);
5359
5360		tokio::time::timeout(Duration::from_secs(5), track.unused())
5361			.await
5362			.expect("chained unused should resolve far below TRACK_IDLE_LINGER")
5363			.expect("source closed");
5364
5365		let cached = edge_resolved.track("video").unwrap().cached_groups();
5366		assert_eq!(
5367			cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
5368			vec![0],
5369			"every front keeps the delivered groups after releasing its source"
5370		);
5371
5372		let mut subscription = edge_resolved
5373			.track("video")
5374			.unwrap()
5375			.subscribe(None)
5376			.await
5377			.expect("resubscribe");
5378		tokio::time::timeout(Duration::from_secs(5), track.used())
5379			.await
5380			.expect("resubscribe should reach the leaf")
5381			.expect("source open");
5382		let mut group = track.append_group().unwrap();
5383		group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
5384		group.finish().unwrap();
5385		let mut group = subscription.recv_group().await.unwrap().unwrap();
5386		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
5387		drop(group);
5388		let mut group = subscription.recv_group().await.unwrap().unwrap();
5389		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
5390		drop(group);
5391		drop(subscription);
5392
5393		tokio::time::timeout(Duration::from_secs(5), track.unused())
5394			.await
5395			.expect("second chained unused should resolve far below TRACK_IDLE_LINGER")
5396			.expect("source closed");
5397
5398		let cached = edge_resolved.track("video").unwrap().cached_groups();
5399		assert_eq!(
5400			cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
5401			vec![0, 1],
5402			"repeated demand keeps every complete group while releasing its source"
5403		);
5404
5405		let fetch = edge_resolved.track("video").unwrap().fetch_group(2, None);
5406		let mut fetch = std::pin::pin!(fetch);
5407		assert!(futures::poll!(fetch.as_mut()).is_pending(), "fetch should re-splice");
5408		tokio::time::timeout(Duration::from_secs(5), track.used())
5409			.await
5410			.expect("fetch should reach the leaf")
5411			.expect("source open");
5412		let mut group = track.append_group().unwrap();
5413		group.write_frame(crate::Timestamp::ZERO, b"fetched".as_ref()).unwrap();
5414		group.finish().unwrap();
5415		let mut group = tokio::time::timeout(Duration::from_secs(5), fetch)
5416			.await
5417			.expect("re-spliced source should answer the fetch")
5418			.expect("fetch succeeds");
5419		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"fetched");
5420	}
5421
5422	/// A newer local source wins dispatch the moment it attaches, but one whose copy
5423	/// of the track carries different metadata is refused: the incumbent keeps
5424	/// serving, and the refusal is never retried once the incumbent leaves.
5425	#[tokio::test]
5426	async fn incompatible_local_source_keeps_the_incumbent() {
5427		let producer = origin(1).produce();
5428		let consumer = producer.consume();
5429
5430		let first = producer.create_broadcast("room/alice").unwrap();
5431		let track = first.create_track("video", None).unwrap();
5432		let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5433		let mut subscription = resolved
5434			.track("video")
5435			.unwrap()
5436			.subscribe(None)
5437			.await
5438			.expect("subscribe");
5439		let mut group = track.append_group().unwrap();
5440		group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5441		group.finish().unwrap();
5442		let mut group = subscription.recv_group().await.unwrap().unwrap();
5443		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"before");
5444
5445		// The newest source is dispatched the track, and refused for its metadata.
5446		let second = producer.create_broadcast("room/alice").unwrap();
5447		let _incompatible = second
5448			.create_track("video", track::Info::default().with_timescale(crate::Timescale::MICRO))
5449			.unwrap();
5450		for _ in 0..10 {
5451			tokio::task::yield_now().await;
5452		}
5453
5454		// Still spliced to the incumbent, still delivering.
5455		let mut group = track.append_group().unwrap();
5456		group.write_frame(crate::Timestamp::ZERO, b"still".as_ref()).unwrap();
5457		group.finish().unwrap();
5458		let mut group = subscription.recv_group().await.unwrap().unwrap();
5459		assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"still");
5460
5461		// The incumbent leaving exhausts the table: the refusal is never retried.
5462		drop(track);
5463		first.finish();
5464		assert!(matches!(subscription.recv_group().await, Err(Error::Unsupported)));
5465	}
5466
5467	#[tokio::test]
5468	async fn multiple_scopes_present_one_broad_prefix() {
5469		let producer = origin(1).produce();
5470		let _a = producer.announce("", Route::default()).unwrap();
5471
5472		let consumer = producer.consume().scope("", &scopes(&["alpha", "beta"])).unwrap();
5473		let mut announced = consumer.announced();
5474		announced.assert_next_active("");
5475		announced.assert_next_wait();
5476	}
5477
5478	#[test]
5479	fn scope_accepts_every_pattern_union() {
5480		let producer = origin(1).produce();
5481
5482		// The root grant is `**`, the old empty prefix.
5483		let root = producer.scope("", &Patterns::from(Pattern::all())).unwrap();
5484		assert_eq!(root.allowed(), Patterns::from(Pattern::all()));
5485
5486		// `foo/**` keeps the old `foo` prefix meaning.
5487		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
5488		assert_eq!(scoped.allowed(), scopes(&["room"]));
5489
5490		// Multiple prefixes round-trip, with overlap collapsed.
5491		let multi = producer.scope("", &scopes(&["room", "room/chat", "anon"])).unwrap();
5492		assert_eq!(multi.allowed(), scopes(&["room", "anon"]));
5493
5494		// The consumer side reports the same way.
5495		let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
5496		assert_eq!(consumer.allowed(), scopes(&["room"]));
5497
5498		for text in ["room", "", "*room", "room/*", "*", "**/room", "room/**/chat", "*.hang"] {
5499			let union = Patterns::from(text.parse::<Pattern>().unwrap());
5500			assert_eq!(producer.scope("", &union).expect(text).allowed(), union, "{text}");
5501			assert_eq!(
5502				producer.consume().scope("", &union).expect(text).allowed(),
5503				union,
5504				"{text}"
5505			);
5506		}
5507
5508		let mixed: Patterns = ["room/**".parse().unwrap(), "other".parse().unwrap()]
5509			.into_iter()
5510			.collect();
5511		assert_eq!(producer.scope("", &mixed).unwrap().allowed(), mixed);
5512	}
5513
5514	#[test]
5515	fn route_table_prunes_to_empty() {
5516		let producer = origin(1).produce();
5517		let consumer = producer.consume();
5518
5519		// Routes and cursors hang at their prefixes; the nodes on the way exist
5520		// only while something is there.
5521		let cursor = consumer
5522			.scope("", &scopes(&["room/a", "other/deep/head"]))
5523			.unwrap()
5524			.announced();
5525		let route = producer.announce("room/a/b/c", Route::default()).unwrap();
5526		{
5527			let table = producer.shared.lock();
5528			assert!(table.routes.root.find(Path::new("room/a/b/c").parts()).is_some());
5529			assert!(table.routes.root.find(Path::new("other/deep/head").parts()).is_some());
5530			assert_eq!(table.routes.root.cursors_below, 2);
5531		}
5532
5533		drop(route);
5534		drop(cursor);
5535		let table = producer.shared.lock();
5536		assert!(table.routes.root.is_empty());
5537		assert_eq!(table.routes.root.cursors_below, 0);
5538	}
5539
5540	/// A session handed an `origin::Producer` drops it once it has its own
5541	/// handles, so the driver must keep running while a published broadcast
5542	/// lives, and finish once the last one is gone.
5543	#[test]
5544	fn a_published_broadcast_keeps_the_driver_running() {
5545		let (producer, mut driver) = Producer::new(Config::new(origin(1)));
5546		let waiter = kio::Waiter::noop();
5547		let broadcast = producer.create_broadcast("room/a").unwrap();
5548		drop(producer);
5549		assert!(
5550			driver.poll(Instant::now(), &waiter).is_ok(),
5551			"the broadcast is lifecycle work"
5552		);
5553		drop(broadcast);
5554		assert!(matches!(driver.poll(Instant::now(), &waiter), Err(Error::Closed)));
5555	}
5556
5557	#[test]
5558	fn watch_wakes_only_for_covering_changes() {
5559		let producer = origin(1).produce();
5560		let waiter = kio::Waiter::noop();
5561		let watch = producer.shared.lock().watch(&producer.shared, &Path::new("room/a"));
5562		let seen = watch.seen();
5563
5564		// A route beside the path or beneath it covers nothing at the path.
5565		let _other = producer.announce("other", Route::default()).unwrap();
5566		let _below = producer.announce("room/a/b", Route::default()).unwrap();
5567		assert!(watch.poll_changed(&waiter, seen).is_pending());
5568
5569		// A route above it does, and so does its retraction.
5570		let above = producer.announce("room", Route::default()).unwrap();
5571		assert!(watch.poll_changed(&waiter, seen).is_ready());
5572		let seen = watch.seen();
5573		drop(above);
5574		assert!(watch.poll_changed(&waiter, seen).is_ready());
5575		let seen = watch.seen();
5576
5577		// A local broadcast attaching at the exact path does; one beside it does not.
5578		let _beside = producer.create_broadcast("room/b").unwrap();
5579		assert!(watch.poll_changed(&waiter, seen).is_pending());
5580		let _here = producer.create_broadcast("room/a").unwrap();
5581		assert!(watch.poll_changed(&waiter, seen).is_ready());
5582
5583		// Dropping the watch takes it out of the table.
5584		drop(watch);
5585		let table = producer.shared.lock();
5586		let node = table
5587			.routes
5588			.root
5589			.find(Path::new("room/a").parts())
5590			.expect("route below keeps the node");
5591		assert!(node.watches.is_empty());
5592		assert_eq!(table.routes.root.watches_below, 0);
5593	}
5594
5595	#[test]
5596	fn a_discarded_front_task_unregisters_its_watch() {
5597		let (producer, _driver) = Producer::new(Config {
5598			hop: origin(1),
5599			..Default::default()
5600		});
5601		let consumer = producer.consume();
5602		let _served = producer.dynamic("room", Route::default()).unwrap();
5603		// A consumer outlives its producer by design, so the task set can refuse
5604		// submissions while the origin is still open. The front's task is then
5605		// dropped on the spot, taking its `Watch` with it: the request must not
5606		// still be holding the table lock the watch unregisters under.
5607		drop(producer);
5608		let _pending = consumer.request_broadcast("room/a");
5609	}
5610
5611	#[test]
5612	fn create_broadcast_refuses_a_path_no_pattern_can_spell() {
5613		let producer = origin(1).produce();
5614
5615		// A `*` segment is a valid path but an invalid literal, so its route could
5616		// never be built: refuse the broadcast instead of publishing one that
5617		// announces nowhere.
5618		assert!(matches!(
5619			producer.create_broadcast("room/*"),
5620			Err(Error::InvalidPath(_))
5621		));
5622		assert!(matches!(
5623			producer.announce("room/**", Route::default()),
5624			Err(Error::InvalidPath(_))
5625		));
5626	}
5627
5628	#[test]
5629	fn scope_empty_union_grants_nothing() {
5630		let producer = origin(1).produce();
5631
5632		// An empty union grants nothing: scoping is refused, like a disjoint prefix.
5633		assert!(matches!(producer.scope("", &Patterns::new()), Err(Error::Unauthorized)));
5634		assert!(matches!(
5635			producer.consume().scope("", &Patterns::new()),
5636			Err(Error::Unauthorized)
5637		));
5638	}
5639
5640	#[test]
5641	fn scope_nests_and_rebases_roots() {
5642		let producer = origin(1).produce();
5643
5644		// Narrowing twice intersects; the grant stays in the new vocabulary.
5645		let scoped = producer.scope("", &scopes(&["room"])).unwrap();
5646		let nested = scoped.scope("", &scopes(&["room/chat"])).unwrap();
5647		assert_eq!(nested.allowed(), scopes(&["room/chat"]));
5648
5649		// A disjoint nesting is refused, not widened.
5650		assert!(matches!(
5651			scoped.scope("", &scopes(&["other"])),
5652			Err(Error::Unauthorized)
5653		));
5654
5655		// A literal root rebases the grant without changing its meaning.
5656		let rooted = nested.scope("room/chat", &Patterns::from(Pattern::all())).unwrap();
5657		assert_eq!(rooted.allowed(), scopes(&[""]));
5658
5659		// Publishing through the nested view lands where the root says.
5660		let broadcast = nested.create_broadcast("room/chat/live").unwrap();
5661		assert!(producer.consume().get_broadcast("room/chat/live").is_some());
5662		broadcast.finish();
5663	}
5664
5665	#[test]
5666	fn scope_intersects_and_rebases_arbitrary_grants() {
5667		let producer = origin(1).produce();
5668		let rooms = producer
5669			.scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
5670			.unwrap();
5671		let chats = rooms
5672			.scope("", &Patterns::from("*/chat".parse::<Pattern>().unwrap()))
5673			.unwrap();
5674		assert_eq!(chats.allowed(), Patterns::from("room/chat".parse::<Pattern>().unwrap()));
5675
5676		let exact = producer
5677			.scope("", &Patterns::from("room/alice".parse::<Pattern>().unwrap()))
5678			.unwrap();
5679		let rooted = exact.scope("room", &Patterns::from(Pattern::all())).unwrap();
5680		assert_eq!(rooted.allowed(), Patterns::from("alice".parse::<Pattern>().unwrap()));
5681		assert!(matches!(
5682			exact.scope("room/bob", &Patterns::from(Pattern::all())),
5683			Err(Error::Unauthorized)
5684		));
5685
5686		let broadcast = exact.create_broadcast("room/alice").unwrap();
5687		assert!(matches!(
5688			exact.create_broadcast("room/alice/cam"),
5689			Err(Error::Unauthorized)
5690		));
5691		assert!(producer.consume().get_broadcast("room/alice").is_some());
5692		drop(broadcast);
5693	}
5694
5695	#[tokio::test]
5696	async fn wildcard_scope_filters_announcements_and_reports_captures() {
5697		let producer = origin(1).produce();
5698		let consumer = producer
5699			.consume()
5700			.scope("", &Patterns::from("room/*/chat".parse::<Pattern>().unwrap()))
5701			.unwrap();
5702		let mut announced = consumer.announced();
5703
5704		let alice = producer.create_broadcast("room/alice/chat").unwrap();
5705		alice.announce(Route::default()).unwrap();
5706		let update = announced.try_next().expect("alice's chat");
5707		assert_eq!(update.prefix.as_str(), "room/alice/chat");
5708		assert_eq!(update.captures, Some(vec!["alice".parse::<Pattern>().unwrap()]));
5709
5710		let audio = producer.create_broadcast("room/alice/audio").unwrap();
5711		audio.announce(Route::default()).unwrap();
5712		announced.assert_next_wait();
5713
5714		let broad = producer.announce("room", Route::default()).unwrap();
5715		let update = announced.try_next().expect("overlapping broad route");
5716		assert_eq!(update.prefix.as_str(), "room");
5717		assert_eq!(update.captures, None, "an overlap does not pin the wildcard");
5718
5719		drop(broad);
5720		drop(audio);
5721		drop(alice);
5722	}
5723
5724	#[tokio::test]
5725	async fn local_broadcast_wins_announcement_ties() {
5726		let producer = origin(1).produce();
5727		let remote = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
5728		let local = producer.create_broadcast("room/alice").unwrap();
5729		local.announce(Route::default()).unwrap();
5730
5731		let mut announced = producer.consume().announced();
5732		let update = announced.try_next().expect("one winning route");
5733		assert_eq!(update.prefix.as_str(), "room/alice");
5734		assert_eq!(update.route.cost, Cost::default());
5735		announced.assert_next_wait();
5736
5737		drop(local);
5738		drop(remote);
5739	}
5740
5741	/// Charging a link accumulates onto both halves, saturating rather than wrapping
5742	/// so a bogus peer sorts last, not first. The ceiling is the largest cost a
5743	/// varint can carry, so whatever a peer advertises, the sum we forward still
5744	/// encodes.
5745	#[test]
5746	fn cost_charge_saturates() {
5747		assert_eq!(Cost { warm: 4, cold: 6 }.charged(5), Cost { warm: 9, cold: 11 });
5748		assert_eq!(Cost::new(u64::MAX).charged(10), Cost::new(MAX_COST));
5749
5750		// An unknown cold path stays unknown however many links it crosses, so it
5751		// can never accumulate its way into outranking a path we actually know.
5752		assert_eq!(Cost::UNKNOWN.charged(3).cold, MAX_COST);
5753	}
5754
5755	/// Mint an origin whose pool reclaims idle content after `expiry`.
5756	fn expiring_origin(expiry: Duration) -> Producer {
5757		let pool = cache::Pool::new(cache::Config::default().with_expiry(expiry));
5758		Config {
5759			pool,
5760			..Config::default()
5761		}
5762		.produce()
5763	}
5764
5765	/// A publisher that stalls with a group still open runs no write path, so the
5766	/// track's own write-driven expiry never fires and a reader parked in that group
5767	/// is never told. The driver's wall-clock sweep is the bound.
5768	#[tokio::test(start_paused = true)]
5769	async fn stalled_publisher_open_group_is_reclaimed() {
5770		let expiry = Duration::from_secs(1);
5771		let origin = expiring_origin(expiry);
5772		let broadcast = origin.create_broadcast("test").unwrap();
5773		let track = broadcast.create_track("video", None).unwrap();
5774
5775		let mut stalled = track.append_group().unwrap();
5776		stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
5777		// A successor, so the stalled group is not the protected live edge. Its
5778		// timestamp is inside the retention budget, so subscription expiry keeps the
5779		// stalled group: only reclamation can bound it.
5780		let _successor = track.append_group().unwrap();
5781
5782		let mut reading = stalled.consume();
5783		assert!(reading.read_frame().await.unwrap().is_some());
5784
5785		// Production goes quiet: nothing writes to this track again.
5786		crate::model::clock::advance(expiry * 2);
5787
5788		// Bounded so a regression fails rather than parking forever, which is the
5789		// bug itself. Time is virtual, so the wait costs nothing.
5790		let reclaimed = tokio::time::timeout(Duration::from_secs(60), reading.read_frame()).await;
5791		assert!(
5792			matches!(reclaimed, Ok(Err(Error::Old))),
5793			"the sweep must reclaim an idle open group and surface the gap, got {reclaimed:?}"
5794		);
5795	}
5796
5797	/// Reclamation is the pool's policy, not the origin's: a pool with no expiry
5798	/// window keeps idle content until byte pressure takes it, sweep or no sweep.
5799	#[tokio::test(start_paused = true)]
5800	async fn sweep_respects_a_disabled_expiry() {
5801		let origin = Config {
5802			pool: cache::Pool::unbounded(),
5803			..Config::default()
5804		}
5805		.produce();
5806		let broadcast = origin.create_broadcast("test").unwrap();
5807		let track = broadcast.create_track("video", None).unwrap();
5808
5809		let mut stalled = track.append_group().unwrap();
5810		stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
5811		let _successor = track.append_group().unwrap();
5812
5813		let mut reading = stalled.consume();
5814		assert!(reading.read_frame().await.unwrap().is_some());
5815
5816		crate::model::clock::advance(Duration::from_secs(3600));
5817		tokio::time::advance(Duration::from_secs(3600)).await;
5818
5819		assert!(
5820			reading.read_frame().now_or_never().is_none(),
5821			"a pool without an expiry window never reclaims"
5822		);
5823	}
5824
5825	/// A draining cost still has to fit the wire, since the route keeps being
5826	/// announced downstream while it drains.
5827	#[test]
5828	fn drain_cost_is_encodable() {
5829		use crate::coding::Encode;
5830
5831		let mut buf = Vec::new();
5832		Cost::DRAIN
5833			.encode(&mut buf, crate::lite::Version::Lite06)
5834			.expect("a draining route is still forwarded, so its cost must encode");
5835	}
5836}