Skip to main content

moq_net/model/
origin.rs

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