1use crate::{broadcast, cache, group, stats, track};
2use kio::Pollable;
3use std::{
4 cmp::Reverse,
5 collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
6 fmt,
7 sync::Arc,
8 sync::atomic::{AtomicU64, Ordering},
9 task::{Poll, ready},
10 time::Duration,
11};
12
13use rand::RngExt;
14
15use super::{
16 Requests, WeakCache, WeakEntry,
17 front::{Action, Candidate, Event, Front, Pin, Refusal},
18};
19use crate::{
20 AsPath, Error, InvalidPattern, Path, PathOwned, Pattern, Patterns,
21 coding::{BoundsExceeded, Decode, DecodeError, Encode, EncodeError},
22 runtime::{Instant, Timers},
23 time::Clock,
24 util::{Keepalive, TaskSet, Tasks, TasksWeak},
25};
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub struct Hop {
38 id: u64,
40}
41
42impl Hop {
43 pub const UNKNOWN: Self = Self { id: 0 };
50
51 pub fn new(id: u64) -> Result<Self, InvalidHop> {
57 if id == 0 || id >= 1u64 << 62 {
58 return Err(InvalidHop::Range);
59 }
60 Ok(Self { id })
61 }
62
63 pub fn random() -> Self {
70 let mut rng = rand::rng();
71 let id = rng.random_range(1..(1u64 << 53));
72 Self { id }
73 }
74
75 pub fn id(self) -> u64 {
77 self.id
78 }
79
80 pub(crate) fn from_wire(id: u64) -> Result<Self, DecodeError> {
82 if id >= 1u64 << 62 {
83 return Err(DecodeError::InvalidValue);
84 }
85 Ok(Self { id })
86 }
87}
88
89#[derive(Clone, Debug)]
95#[non_exhaustive]
96pub struct Config {
97 pub hop: Hop,
100
101 pub pool: cache::Pool,
108
109 pub cache_duration: Duration,
117
118 pub default_max_age: Duration,
128}
129
130impl Default for Config {
131 fn default() -> Self {
133 let pool = cache::Pool::new(cache::Config::default().with_expiry(cache::DEFAULT_EXPIRY));
134 Self {
135 hop: Hop::random(),
136 pool,
137 cache_duration: Duration::MAX,
138 default_max_age: track::DEFAULT_MAX_AGE,
139 }
140 }
141}
142
143impl Config {
144 pub fn new(hop: Hop) -> Self {
146 Self { hop, ..Self::default() }
147 }
148}
149
150impl From<Hop> for Config {
151 fn from(hop: Hop) -> Self {
153 Self::new(hop)
154 }
155}
156
157impl TryFrom<u64> for Hop {
158 type Error = InvalidHop;
159
160 fn try_from(id: u64) -> Result<Self, Self::Error> {
161 Self::new(id)
162 }
163}
164
165impl fmt::Display for Hop {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 self.id.fmt(f)
168 }
169}
170
171impl<V: Copy> Encode<V> for Hop
172where
173 u64: Encode<V>,
174{
175 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
176 self.id.encode(w, version)
177 }
178}
179
180impl<V: Copy> Decode<V> for Hop
181where
182 u64: Decode<V>,
183{
184 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
185 Self::from_wire(u64::decode(r, version)?)
186 }
187}
188
189pub(crate) const MAX_HOPS: usize = 32;
195
196#[derive(Debug, Clone, Default, PartialEq, Eq)]
204pub struct Hops(Vec<Hop>);
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208#[non_exhaustive]
209pub enum InvalidHop {
210 Range,
213
214 TooMany,
217
218 Duplicate,
222}
223
224impl fmt::Display for InvalidHop {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 match self {
227 Self::Range => write!(f, "local hop id must be non-zero and below 2^62"),
228 Self::TooMany => write!(f, "too many hops (max {MAX_HOPS})"),
229 Self::Duplicate => write!(f, "hop already in the chain"),
230 }
231 }
232}
233
234impl std::error::Error for InvalidHop {}
235
236impl From<InvalidHop> for DecodeError {
237 fn from(err: InvalidHop) -> Self {
238 match err {
239 InvalidHop::TooMany => DecodeError::BoundsExceeded,
240 InvalidHop::Range | InvalidHop::Duplicate => DecodeError::InvalidValue,
241 }
242 }
243}
244
245impl Hops {
246 pub fn new() -> Self {
248 Self(Vec::new())
249 }
250
251 pub fn push(&mut self, hop: Hop) -> Result<(), InvalidHop> {
257 if self.0.len() >= MAX_HOPS {
258 return Err(InvalidHop::TooMany);
259 }
260 if hop != Hop::UNKNOWN && self.0.contains(&hop) {
261 return Err(InvalidHop::Duplicate);
262 }
263 self.0.push(hop);
264 Ok(())
265 }
266
267 pub fn contains(&self, hop: &Hop) -> bool {
269 self.0.contains(hop)
270 }
271
272 pub fn len(&self) -> usize {
274 self.0.len()
275 }
276
277 pub fn is_empty(&self) -> bool {
279 self.0.is_empty()
280 }
281
282 pub fn iter(&self) -> std::slice::Iter<'_, Hop> {
284 self.0.iter()
285 }
286
287 pub fn as_slice(&self) -> &[Hop] {
289 &self.0
290 }
291}
292
293impl TryFrom<Vec<Hop>> for Hops {
294 type Error = InvalidHop;
295
296 fn try_from(v: Vec<Hop>) -> Result<Self, Self::Error> {
297 if v.len() > MAX_HOPS {
298 return Err(InvalidHop::TooMany);
299 }
300 for (i, hop) in v.iter().enumerate() {
302 if *hop != Hop::UNKNOWN && v[i + 1..].contains(hop) {
303 return Err(InvalidHop::Duplicate);
304 }
305 }
306 Ok(Self(v))
307 }
308}
309
310impl<'a> IntoIterator for &'a Hops {
311 type Item = &'a Hop;
312 type IntoIter = std::slice::Iter<'a, Hop>;
313
314 fn into_iter(self) -> Self::IntoIter {
315 self.iter()
316 }
317}
318
319impl<V: Copy> Encode<V> for Hops
320where
321 u64: Encode<V>,
322 Hop: Encode<V>,
323{
324 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
325 (self.0.len() as u64).encode(w, version)?;
326 for origin in &self.0 {
327 origin.encode(w, version)?;
328 }
329 Ok(())
330 }
331}
332
333impl<V: Copy> Decode<V> for Hops
334where
335 u64: Decode<V>,
336 Hop: Decode<V>,
337{
338 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
339 let count = u64::decode(r, version)? as usize;
340 if count > MAX_HOPS {
341 return Err(DecodeError::BoundsExceeded);
342 }
343 let mut list = Self(Vec::with_capacity(count));
346 for _ in 0..count {
347 list.push(Hop::decode(r, version)?)?;
348 }
349 Ok(list)
350 }
351}
352
353const MAX_COST: u64 = (1 << 62) - 1;
360
361#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
370pub struct Cost {
371 pub warm: u64,
380
381 pub cold: u64,
388}
389
390impl Cost {
391 pub const fn new(cost: u64) -> Self {
394 Self { warm: cost, cold: cost }
395 }
396
397 pub const MAX: Self = Self::new(MAX_COST);
404
405 pub const DRAIN: Self = Self::MAX;
408
409 pub(crate) const UNKNOWN: Self = Self {
413 warm: 0,
414 cold: MAX_COST,
415 };
416
417 pub(crate) fn charged(self, link_cost: u64) -> Self {
420 Self {
421 warm: self.warm.saturating_add(link_cost).min(MAX_COST),
422 cold: self.cold.saturating_add(link_cost).min(MAX_COST),
423 }
424 }
425
426 pub(crate) fn clamped(self) -> Self {
429 Self {
430 warm: self.warm.min(MAX_COST),
431 cold: self.cold.min(MAX_COST),
432 }
433 }
434}
435
436impl From<u64> for Cost {
437 fn from(cost: u64) -> Self {
438 Self::new(cost)
439 }
440}
441
442#[derive(Clone, Debug, PartialEq, Eq)]
453#[non_exhaustive]
454pub struct Route {
455 pub hops: Hops,
460
461 pub cost: Cost,
466
467 pub(crate) via: Hop,
473
474 pub(crate) source: Source,
476}
477
478impl Default for Route {
479 fn default() -> Self {
480 Self {
481 hops: Hops::new(),
482 cost: Cost::default(),
483 via: Hop::UNKNOWN,
484 source: Source::Local,
485 }
486 }
487}
488
489#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
495pub enum Source {
496 #[default]
498 Local,
499 Peer(Hop),
502}
503
504impl Route {
505 pub fn with_hops(mut self, hops: Hops) -> Self {
507 self.hops = hops;
508 self
509 }
510
511 pub fn with_cost(mut self, cost: impl Into<Cost>) -> Self {
516 self.cost = cost.into();
517 self
518 }
519
520 pub(crate) fn with_via(mut self, via: Hop) -> Self {
525 self.via = via;
526 self
527 }
528
529 pub fn is_anonymous(&self) -> bool {
537 self.hops.iter().any(|hop| *hop == Hop::UNKNOWN)
538 }
539
540 pub fn source(&self) -> Source {
545 self.source
546 }
547}
548
549static NEXT_CONSUMER_ID: AtomicU64 = AtomicU64::new(0);
550
551#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
552struct ConsumerId(u64);
553
554impl ConsumerId {
555 fn new() -> Self {
556 Self(NEXT_CONSUMER_ID.fetch_add(1, Ordering::Relaxed))
557 }
558}
559
560fn fnv_key(name: &str, origins: impl IntoIterator<Item = Hop>) -> u64 {
569 const SEED: u64 = 0x420C0DECB00B; const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
571
572 let mut hash = SEED;
573 for &byte in name.as_bytes() {
574 hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
575 }
576 for origin in origins {
577 for &byte in &origin.id().to_le_bytes() {
578 hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
579 }
580 }
581
582 hash
583}
584
585fn route_order(prefix: &Path, entry: &RouteEntry) -> (bool, Cost, bool, usize, u64, Reverse<u64>) {
593 (
594 entry.is_anonymous(),
595 entry.cost,
596 !entry.local,
597 entry.hops.len(),
598 fnv_key(prefix.as_str(), entry.hops.iter().copied()),
599 Reverse(entry.id),
600 )
601}
602
603type RouteMeta = (Hops, Cost, Source);
605
606type AnnounceMeta = (RouteMeta, Option<Vec<Pattern>>);
613
614enum PendingUpdate {
615 Announce(AnnounceMeta),
616 Unannounce(AnnounceMeta),
617 UnannounceAnnounce { old: AnnounceMeta, new: AnnounceMeta },
618}
619
620#[derive(Default)]
625struct OriginConsumerState {
626 pending: BTreeMap<PathOwned, PendingUpdate>,
627 delivered: BTreeSet<PathOwned>,
632 ended: bool,
635}
636
637impl OriginConsumerState {
638 fn apply_announce(&mut self, prefix: PathOwned, meta: RouteMeta, captures: Option<Vec<Pattern>>) {
639 let meta = (meta, captures);
640 let new = match self.pending.remove(&prefix) {
641 None | Some(PendingUpdate::Announce(_)) => PendingUpdate::Announce(meta),
643 Some(PendingUpdate::Unannounce(old) | PendingUpdate::UnannounceAnnounce { old, .. }) => {
645 PendingUpdate::UnannounceAnnounce { old, new: meta }
646 }
647 };
648 self.pending.insert(prefix, new);
649 }
650
651 fn apply_unannounce(&mut self, prefix: PathOwned, last: RouteMeta, captures: Option<Vec<Pattern>>) {
652 let last = (last, captures);
653 match self.pending.remove(&prefix) {
654 Some(PendingUpdate::Announce(_)) if !self.delivered.contains(&prefix) => {}
657 None | Some(PendingUpdate::Announce(_) | PendingUpdate::Unannounce(_)) => {
660 self.pending.insert(prefix, PendingUpdate::Unannounce(last));
661 }
662 Some(PendingUpdate::UnannounceAnnounce { old, .. }) => {
665 self.pending.insert(prefix, PendingUpdate::Unannounce(old));
666 }
667 }
668 }
669
670 fn take(&mut self) -> Option<AnnounceUpdate> {
672 let prefix = self.pending.keys().next()?.clone();
673 let ((meta, captures), kind) = match self.pending.remove(&prefix).unwrap() {
674 PendingUpdate::Announce(meta) => {
675 let kind = match self.delivered.insert(prefix.clone()) {
677 true => AnnounceKind::Announced,
678 false => AnnounceKind::Updated,
679 };
680 (meta, kind)
681 }
682 PendingUpdate::Unannounce(meta) => {
683 self.delivered.remove(&prefix);
684 (meta, AnnounceKind::Retracted)
685 }
686 PendingUpdate::UnannounceAnnounce { old, new } => {
687 self.delivered.remove(&prefix);
690 self.pending.insert(prefix.clone(), PendingUpdate::Announce(new));
691 (old, AnnounceKind::Retracted)
692 }
693 };
694 Some(AnnounceUpdate {
695 prefix,
696 captures,
697 route: Route {
698 hops: meta.0,
699 cost: meta.1,
700 via: Hop::UNKNOWN,
701 source: meta.2,
702 },
703 kind,
704 })
705 }
706}
707
708struct RouteEntry {
710 id: u64,
711 prefix: PathOwned,
712 scope: Patterns,
715 hops: Hops,
716 cost: Cost,
717 via: Hop,
721 local: bool,
724 peer: bool,
727 server: Option<kio::Shared<ServeState>>,
731 source: Option<broadcast::Consumer>,
735 advertised: bool,
739 claim: Pattern,
745}
746
747impl RouteEntry {
748 fn is_anonymous(&self) -> bool {
749 self.hops.iter().any(|hop| *hop == Hop::UNKNOWN)
750 }
751
752 fn entered(&self) -> Source {
754 match self.peer {
755 true => Source::Peer(self.via),
756 false => Source::Local,
757 }
758 }
759
760 fn serves(&self, path: &Path) -> bool {
764 self.server.is_some() || (self.source.is_some() && self.prefix == *path)
765 }
766
767 fn qualifies(&self, pin: Pin) -> bool {
769 match pin {
770 Pin::Any => true,
771 Pin::Local => self.local,
772 Pin::Publisher(first) => self.hops.iter().next() == Some(&first),
773 Pin::Route(id) => self.id == id,
774 }
775 }
776
777 fn visible_to(&self, exclude: Option<Hop>) -> bool {
782 match exclude {
783 Some(peer) if peer != Hop::UNKNOWN => self.via != peer && !self.hops.contains(&peer),
784 _ => true,
785 }
786 }
787
788 fn overlaps(&self, allowed: &Patterns) -> bool {
790 self.scope.iter().any(|scope| {
791 scope
792 .intersect(&self.claim)
793 .is_ok_and(|scoped| scoped.iter().any(|restriction| allowed.overlaps(restriction)))
794 })
795 }
796}
797
798fn prefix_claim(prefix: &Path) -> Result<Pattern, InvalidPattern> {
800 if prefix.parts().count() == Path::MAX_PARTS {
801 Pattern::literal(prefix.as_str())
802 } else {
803 Pattern::subtree(prefix.as_str())
804 }
805}
806
807#[derive(Default)]
812struct ServeState {
813 requests: Requests<PathOwned, kio::Producer<PendingBroadcast>>,
816
817 served: WeakCache<PathOwned, broadcast::WeakConsumer>,
823
824 closed: bool,
827}
828
829type FrontKey = (PathOwned, Horizon);
834
835#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
837struct Horizon {
838 exclude: Option<Hop>,
842 local: bool,
844}
845
846impl Horizon {
847 fn admits(&self, entry: &RouteEntry) -> bool {
849 !(self.local && entry.peer) && entry.visible_to(self.exclude)
850 }
851}
852
853#[derive(Clone)]
856struct RemoteFront {
857 request: kio::Producer<PendingBroadcast>,
861 broadcast: broadcast::WeakConsumer,
864 pin: kio::Lock<Pin>,
867}
868
869type CursorRoute = (u64, RouteMeta, bool, Option<Vec<Pattern>>);
871
872impl WeakEntry for RemoteFront {
873 fn is_closed(&self) -> bool {
874 self.broadcast.is_closed()
875 }
876
877 fn same_channel(&self, other: &Self) -> bool {
878 self.broadcast.same_channel(&other.broadcast)
879 }
880}
881
882struct TableCursor {
885 root: PathOwned,
887 allowed: Patterns,
889 heads: Vec<PathOwned>,
893 horizon: Horizon,
895 hidden: Hidden,
897 state: kio::Producer<OriginConsumerState>,
899 current: HashMap<PathOwned, CursorRoute>,
904}
905
906impl TableCursor {
907 fn presented(&self, prefix: &Path, claim: &Pattern) -> Option<PathOwned> {
913 if !self.allowed.overlaps(claim) {
914 return None;
915 }
916
917 if let Some(relative) = prefix.strip_prefix(&self.root) {
918 return Some(relative.to_owned());
919 }
920 self.root.has_prefix(prefix).then(PathOwned::default)
921 }
922
923 fn captures(&self, prefix: &Path) -> Option<Vec<Pattern>> {
926 let literal = Pattern::literal(prefix.as_str()).ok()?;
927 self.allowed
928 .iter()
929 .filter_map(|allowed| {
930 allowed
931 .captures(&literal)
932 .map(|captures| (allowed.specificity(), captures))
933 })
934 .max_by_key(|(specificity, _)| *specificity)
935 .map(|(_, captures)| captures)
936 }
937
938 fn visible(&self, entry: &RouteEntry) -> bool {
941 entry.advertised && self.horizon.admits(entry) && entry.overlaps(&self.allowed) && self.discovers(&entry.prefix)
942 }
943
944 fn discovers(&self, prefix: &Path) -> bool {
946 (self.hidden.include || !hides(&self.heads, prefix))
947 && self.hidden.beyond.as_ref().is_none_or(|outer| hides(outer, prefix))
948 }
949}
950
951#[derive(Clone)]
953struct OriginScope {
954 allowed: Patterns,
956}
957
958impl OriginScope {
959 fn empty() -> Self {
961 Self {
962 allowed: Patterns::new(),
963 }
964 }
965
966 fn narrow(&self, patterns: &Patterns) -> Option<Self> {
968 let allowed = self.allowed.intersect(patterns).ok()?;
969 if allowed.is_empty() {
970 None
971 } else {
972 Some(Self { allowed })
973 }
974 }
975
976 fn permits(&self, path: &Path) -> bool {
978 self.allowed.matches(path.as_str())
979 }
980
981 fn relative(&self, root: &Path) -> Patterns {
983 self.allowed.rebase(root.as_str())
984 }
985}
986
987impl Default for OriginScope {
988 fn default() -> Self {
989 Self {
990 allowed: Patterns::from(Pattern::all()),
991 }
992 }
993}
994
995pub(crate) fn interest_prefixes(allowed: &Patterns) -> Vec<PathOwned> {
998 let mut heads: Vec<PathOwned> = allowed
999 .iter()
1000 .map(|pattern| Path::new(pattern.head()).to_owned())
1001 .collect();
1002 heads.sort();
1003 heads.dedup();
1004 let covered = heads.clone();
1005 heads.retain(|head| !covered.iter().any(|other| other != head && head.has_prefix(other)));
1006 heads
1007}
1008
1009#[derive(Clone, Debug, Default, PartialEq, Eq)]
1016pub(crate) struct Hidden {
1017 include: bool,
1019 beyond: Option<Vec<PathOwned>>,
1022}
1023
1024fn hides(heads: &[PathOwned], prefix: &Path) -> bool {
1027 heads
1028 .iter()
1029 .any(|head| prefix.strip_prefix(head).is_some_and(|below| below.is_hidden()))
1030}
1031
1032#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1034pub enum AnnounceKind {
1035 Announced,
1037 Updated,
1039 Retracted,
1041}
1042
1043impl AnnounceKind {
1044 pub fn is_active(self) -> bool {
1046 !matches!(self, Self::Retracted)
1047 }
1048}
1049
1050#[derive(Clone, Debug)]
1058pub struct AnnounceUpdate {
1059 pub prefix: PathOwned,
1061 pub captures: Option<Vec<Pattern>>,
1064 pub route: Route,
1067 pub kind: AnnounceKind,
1069}
1070
1071#[derive(Clone)]
1073pub struct Producer {
1074 hop: Hop,
1077
1078 scope: OriginScope,
1080
1081 root: PathOwned,
1083
1084 shared: kio::Shared<OriginState>,
1087
1088 pool: cache::Pool,
1091
1092 cache_duration: Duration,
1095
1096 default_max_age: Duration,
1099
1100 stats: stats::Session,
1104
1105 peer: bool,
1108
1109 tasks: Tasks,
1113
1114 timers: Clock,
1116}
1117
1118impl Producer {
1119 pub fn new(config: Config) -> (Self, Driver) {
1126 let (tasks, set) = TaskSet::new();
1127 let scope = OriginScope::default();
1128 let shared = kio::Shared::<OriginState>::default();
1129 let timers = Clock::default();
1130 let pool = config.pool.clone();
1131 let producer = Self {
1132 hop: config.hop,
1133 scope: scope.clone(),
1134 root: PathOwned::default(),
1135 shared: shared.clone(),
1136 pool: config.pool,
1137 cache_duration: config.cache_duration,
1138 default_max_age: config.default_max_age,
1139 stats: stats::Session::default(),
1140 peer: false,
1141 tasks,
1142 timers: timers.clone(),
1143 };
1144 let driver = Driver {
1145 state: DriverState {
1146 set,
1147 shared,
1148 done: false,
1149 },
1150 timers,
1151 pool,
1152 };
1153 (producer, driver)
1154 }
1155
1156 pub fn with_stats(mut self, session: stats::Session) -> Self {
1160 self.stats = session;
1161 self
1162 }
1163
1164 pub fn peer(mut self) -> Self {
1172 self.peer = true;
1173 self
1174 }
1175
1176 pub fn config(&self) -> Config {
1178 Config {
1179 hop: self.hop,
1180 pool: self.pool.clone(),
1181 cache_duration: self.cache_duration,
1182 default_max_age: self.default_max_age,
1183 }
1184 }
1185
1186 pub fn hop(&self) -> Hop {
1188 self.hop
1189 }
1190
1191 pub(crate) fn default_max_age(&self) -> Duration {
1194 self.default_max_age
1195 }
1196
1197 pub(crate) fn empty(hop: Hop) -> Self {
1202 let (tasks, _) = TaskSet::new();
1205 Self {
1206 hop,
1207 scope: OriginScope::empty(),
1208 root: PathOwned::default(),
1209 shared: kio::Shared::default(),
1210 pool: cache::Pool::default(),
1211 cache_duration: Duration::MAX,
1212 default_max_age: track::DEFAULT_MAX_AGE,
1213 stats: stats::Session::default(),
1214 peer: false,
1215 tasks,
1216 timers: Clock::default(),
1217 }
1218 }
1219
1220 pub fn create_broadcast(&self, path: impl AsPath) -> Result<broadcast::Producer, Error> {
1250 let path = path.as_path();
1251
1252 let full = self.root.join(&path).to_owned();
1253 if !self.scope.permits(&full) {
1254 return Err(Error::Unauthorized);
1255 }
1256 if full.parts().count() > Path::MAX_PARTS {
1260 return Err(BoundsExceeded.into());
1261 }
1262 let claim = prefix_claim(&full)?;
1265
1266 let ingress = self.stats.ingress(&full);
1268
1269 let announcing = Announcing {
1274 hop: self.hop,
1275 shared: self.shared.clone(),
1276 requested: full.clone(),
1277 prefixes: vec![(full.clone(), claim)],
1278 scope: self.scope.allowed.clone(),
1279 local: true,
1280 peer: self.peer,
1281 stats: self.stats.clone(),
1282 };
1283 let info = broadcast::Info {
1284 pool: self.pool.clone(),
1285 cache_duration: self.cache_duration,
1286 path: full,
1287 };
1288 let source = info.produce().with_stats(ingress.clone());
1289 let entry = announcing.announce(
1290 Route::default(),
1291 Serving {
1292 server: None,
1293 source: Some(source.consume()),
1294 advertised: false,
1295 },
1296 )?;
1297 Ok(source.with_announcer(Announcer {
1298 entry,
1299 ingress,
1300 _keepalive: self.tasks.keepalive(),
1301 }))
1302 }
1303
1304 pub fn publish(&self, path: impl AsPath, route: Route) -> Result<broadcast::Producer, Error> {
1306 let broadcast = self.create_broadcast(path)?;
1307 broadcast.announce(route)?;
1308 Ok(broadcast)
1309 }
1310
1311 pub(crate) fn create_source(&self, path: impl AsPath) -> broadcast::Producer {
1317 let path = path.as_path();
1318 let full = self.root.join(&path).to_owned();
1319 let ingress = self.stats.ingress(&full);
1320 broadcast::Info {
1321 pool: self.pool.clone(),
1322 cache_duration: self.cache_duration,
1323 path: full,
1324 }
1325 .produce()
1326 .with_stats(ingress)
1327 }
1328
1329 #[cfg(test)]
1338 pub(crate) fn announce(&self, prefix: impl AsPath, route: Route) -> Result<AnnounceProducer, Error> {
1339 Announcing::new(self, prefix)?.announce(
1340 route,
1341 Serving {
1342 server: None,
1343 source: None,
1344 advertised: true,
1345 },
1346 )
1347 }
1348
1349 pub fn dynamic(&self, prefix: impl AsPath, route: Route) -> Result<Dynamic, Error> {
1371 let announcing = Announcing::new(self, prefix)?;
1372 let serve = kio::Shared::<ServeState>::default();
1373 serve.lock().requests.add_handler();
1374 let announcement = announcing.announce(
1375 route,
1376 Serving {
1377 server: Some(serve.clone()),
1378 source: None,
1379 advertised: true,
1380 },
1381 )?;
1382 Ok(Dynamic {
1383 announcement,
1384 state: serve,
1385 })
1386 }
1387
1388 pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Producer, Error> {
1395 let root = self.root.join(root).to_owned();
1396 let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
1397 let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
1398 Ok(Producer {
1399 hop: self.hop,
1400 scope,
1401 root,
1402 shared: self.shared.clone(),
1403 pool: self.pool.clone(),
1404 cache_duration: self.cache_duration,
1405 default_max_age: self.default_max_age,
1406 stats: self.stats.clone(),
1407 peer: self.peer,
1408 tasks: self.tasks.clone(),
1409 timers: self.timers.clone(),
1410 })
1411 }
1412
1413 pub fn consume(&self) -> Consumer {
1418 Consumer::from_producer(self, stats::Session::default())
1421 }
1422
1423 pub fn root(&self) -> &Path<'_> {
1425 &self.root
1426 }
1427
1428 pub fn allowed(&self) -> Patterns {
1430 self.scope.relative(&self.root)
1431 }
1432
1433 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
1435 self.root.join(path)
1436 }
1437}
1438
1439struct Announcing {
1443 hop: Hop,
1444 shared: kio::Shared<OriginState>,
1445 requested: PathOwned,
1447 prefixes: Vec<(PathOwned, Pattern)>,
1451 scope: Patterns,
1453 local: bool,
1454 peer: bool,
1456 stats: stats::Session,
1457}
1458
1459impl Announcing {
1460 fn new(producer: &Producer, prefix: impl AsPath) -> Result<Self, Error> {
1462 let requested = producer.root.join(prefix.as_path()).to_owned();
1463 if requested.parts().count() > Path::MAX_PARTS {
1464 return Err(BoundsExceeded.into());
1465 }
1466 let claim = prefix_claim(&requested)?;
1467 if !producer.scope.allowed.overlaps(&claim) {
1468 return Err(Error::Unauthorized);
1469 }
1470 Ok(Self {
1471 hop: producer.hop,
1472 shared: producer.shared.clone(),
1473 requested: requested.clone(),
1474 prefixes: vec![(requested, claim)],
1475 scope: producer.scope.allowed.clone(),
1476 local: false,
1477 peer: producer.peer,
1478 stats: producer.stats.clone(),
1479 })
1480 }
1481
1482 fn announce(&self, route: Route, serving: Serving) -> Result<AnnounceProducer, Error> {
1483 debug_assert!(
1484 !route.hops.contains(&self.hop),
1485 "announce called with a looping hop chain",
1486 );
1487
1488 let via = route.via;
1489
1490 let mut shared = self.shared.lock();
1491 if shared.closed {
1492 return Err(Error::Closed);
1493 }
1494
1495 let mut entries = Vec::with_capacity(self.prefixes.len());
1496 for (prefix, claim) in &self.prefixes {
1497 let id = shared.next_route;
1498 shared.next_route += 1;
1499 shared.routes.insert(RouteEntry {
1500 id,
1501 prefix: prefix.clone(),
1502 scope: self.scope.clone(),
1503 hops: route.hops.clone(),
1504 cost: route.cost,
1505 via,
1506 local: self.local,
1507 peer: self.peer,
1508 server: serving.server.clone(),
1509 source: serving.source.clone(),
1510 advertised: serving.advertised,
1511 claim: claim.clone(),
1512 });
1513 shared.sync_route(prefix, claim);
1514 entries.push((prefix.clone(), id));
1515 }
1516 drop(shared);
1517
1518 let guard = serving
1520 .advertised
1521 .then(|| self.stats.ingress(&self.requested).announce());
1522
1523 Ok(AnnounceProducer {
1524 shared: self.shared.clone(),
1525 entries,
1526 guard,
1527 })
1528 }
1529}
1530
1531struct Serving {
1533 server: Option<kio::Shared<ServeState>>,
1534 source: Option<broadcast::Consumer>,
1535 advertised: bool,
1536}
1537
1538pub(crate) struct Announcer {
1545 entry: AnnounceProducer,
1546 ingress: stats::Scope,
1548 _keepalive: Keepalive,
1552}
1553
1554impl Announcer {
1555 pub(crate) fn announce(&mut self, route: Route) -> Result<(), Error> {
1557 self.entry.update(route)?;
1558 if self.entry.guard.is_none() {
1559 self.entry.guard = Some(self.ingress.announce());
1560 }
1561 Ok(())
1562 }
1563
1564 pub(crate) fn withdraw(&mut self) {
1566 self.entry.withdraw();
1567 self.entry.guard = None;
1568 }
1569}
1570
1571#[must_use = "dropping an announcement retracts the route"]
1578pub(crate) struct AnnounceProducer {
1579 shared: kio::Shared<OriginState>,
1580 entries: Vec<(PathOwned, u64)>,
1583 guard: Option<stats::Announce>,
1585}
1586
1587impl AnnounceProducer {
1588 pub fn update(&self, route: Route) -> Result<(), Error> {
1596 let mut shared = self.shared.lock();
1597 if shared.closed {
1598 return Err(Error::Closed);
1599 }
1600 for (prefix, id) in &self.entries {
1601 let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1603 return Err(Error::Closed);
1604 };
1605 entry.hops = route.hops.clone();
1606 entry.cost = route.cost;
1607 entry.via = route.via;
1608 entry.advertised = true;
1609 let claim = entry.claim.clone();
1610 shared.sync_route(prefix, &claim);
1611 }
1612 Ok(())
1613 }
1614
1615 fn withdraw(&self) {
1620 let mut shared = self.shared.lock();
1621 for (prefix, id) in &self.entries {
1622 let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1623 continue;
1624 };
1625 if !entry.advertised {
1626 continue;
1627 }
1628 entry.advertised = false;
1629 let claim = entry.claim.clone();
1630 shared.sync_route(prefix, &claim);
1631 }
1632 }
1633
1634 fn retract(&self) {
1637 let mut shared = self.shared.lock();
1638 for (prefix, id) in &self.entries {
1639 let Some(entry) = shared.routes.remove(prefix, *id) else {
1640 continue;
1641 };
1642 if let Some(server) = &entry.server {
1645 let mut server = server.lock();
1646 server.closed = true;
1647 for producer in server.requests.drain_all() {
1648 if let Ok(mut request) = producer.write() {
1649 request.resolved.get_or_insert(Err(Error::Unroutable));
1650 }
1651 }
1652 }
1653 shared.sync_route(&entry.prefix, &entry.claim);
1654 }
1655 }
1656}
1657
1658impl Drop for AnnounceProducer {
1659 fn drop(&mut self) {
1660 self.retract();
1661 }
1662}
1663
1664#[must_use = "poll the driver or the origin makes no progress"]
1676pub struct Driver {
1677 state: DriverState,
1678 timers: Clock,
1680 pool: cache::Pool,
1683}
1684
1685struct DriverState {
1687 set: TaskSet,
1689 shared: kio::Shared<OriginState>,
1692 done: bool,
1694}
1695
1696impl Driver {
1697 pub fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1703 self.timers.advance(now);
1704 let result = self.state.poll(waiter);
1705 let gc = self.pool.gc(now);
1706 if result.is_ready() {
1707 return Err(Error::Closed);
1708 }
1709 Ok(self.timers.timeout().into_iter().chain(gc).min())
1710 }
1711}
1712
1713impl crate::time::Driver for Driver {
1714 fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1715 self.poll(now, waiter)
1716 }
1717}
1718
1719impl DriverState {
1720 fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
1721 if !self.done {
1725 ready!(self.set.poll(waiter));
1726 self.done = true;
1727 }
1728 Poll::Ready(())
1729 }
1730
1731 fn teardown(&mut self) {
1735 drop(std::mem::replace(&mut self.set, TaskSet::owned()));
1738
1739 let (servers, cursors, fronts) = {
1744 let mut shared = self.shared.lock();
1745 shared.closed = true;
1746 shared.routes.poke_all();
1748 let servers: Vec<_> = shared
1749 .routes
1750 .entries()
1751 .filter_map(|entry| entry.server.clone())
1752 .collect();
1753 let cursors: Vec<_> = shared.cursors.values().map(|cursor| cursor.state.clone()).collect();
1754 let fronts: Vec<_> = shared.fronts.values().map(|front| front.request.clone()).collect();
1755 (servers, cursors, fronts)
1756 };
1757
1758 for producer in fronts {
1761 if let Ok(mut request) = producer.write() {
1762 request.resolved.get_or_insert(Err(Error::Dropped));
1763 }
1764 }
1765 for server in servers {
1769 let mut server = server.lock();
1770 server.closed = true;
1771 for producer in server.requests.drain_all() {
1772 if let Ok(mut request) = producer.write() {
1773 request.resolved.get_or_insert(Err(Error::Dropped));
1774 }
1775 }
1776 }
1777
1778 for state in cursors {
1781 if let Ok(mut state) = state.write() {
1782 state.ended = true;
1783 }
1784 }
1785 }
1786}
1787
1788impl Drop for DriverState {
1789 fn drop(&mut self) {
1790 self.teardown();
1791 }
1792}
1793
1794const TRACK_IDLE_LINGER: Duration = Duration::from_secs(30);
1807
1808struct WarmCopy {
1813 track: track::Producer,
1814 _dynamic: track::Dynamic,
1815 edge: Option<WarmGroup>,
1818}
1819
1820impl Drop for WarmCopy {
1821 fn drop(&mut self) {
1822 let _ = self.track.finish();
1823 }
1824}
1825
1826struct WarmGroup(group::Producer);
1833
1834impl Drop for WarmGroup {
1835 fn drop(&mut self) {
1836 if !self.0.is_finished() {
1837 let _ = self.0.clone().abort(Error::Cancel);
1838 }
1839 }
1840}
1841
1842fn warm_copy(source: &track::Consumer, head: Option<&WarmGroup>) -> Option<WarmCopy> {
1849 let info = source.cached_info()?;
1850 let mut track = track::Producer::new(Arc::new(source.broadcast().clone()), source.name(), info);
1851 let head = head.map(|head| &head.0);
1852 let groups = source.cached_groups();
1853 let latest = groups
1855 .iter()
1856 .filter(|(_, visible)| *visible)
1857 .map(|(group, _)| group.sequence)
1858 .max();
1859 let mut edge = None;
1860
1861 if let Some(head) = head
1865 && !groups.iter().any(|(group, _)| group.sequence == head.sequence)
1866 {
1867 let is_latest = latest.is_none_or(|latest| head.sequence >= latest);
1868 if head.is_finished() {
1869 let _ = track.adopt_group(head.clone(), true);
1870 if is_latest {
1871 edge = Some(WarmGroup(head.clone()));
1872 }
1873 } else if is_latest {
1874 edge = warm_rebuild(&track, head, None);
1875 }
1876 }
1877
1878 for (group, visible) in groups {
1879 let finished = group.is_finished();
1880 let whole = group.live_first_frame() == Some(0);
1881 let is_latest = visible && Some(group.sequence) == latest;
1882 let warm = if finished && whole {
1883 let _ = track.adopt_group(group.clone(), visible);
1884 Some(WarmGroup(group))
1885 } else if (finished || is_latest) && (whole || head.is_some_and(|head| head.sequence == group.sequence)) {
1886 warm_rebuild(&track, &group, head)
1892 } else {
1893 None
1895 };
1896 if is_latest {
1897 edge = warm;
1898 }
1899 }
1900 let dynamic = track.dynamic();
1901 Some(WarmCopy {
1902 track,
1903 _dynamic: dynamic,
1904 edge,
1905 })
1906}
1907
1908fn warm_rebuild(track: &track::Producer, live: &group::Producer, head: Option<&group::Producer>) -> Option<WarmGroup> {
1911 let mut start = live.live_first_frame()? as u64;
1913 let mut tail = live.consume();
1914 tail.start_at(start);
1915 let mut frames = Vec::new();
1916 if start > 0
1917 && let Some(head) = head.filter(|head| head.sequence == live.sequence)
1918 && let Some(head_start) = head.live_first_frame()
1919 {
1920 let mut head = head.consume();
1921 head.start_at(head_start as u64);
1922 while head.index() < start {
1923 match head.poll_read_frame(&kio::Waiter::noop()) {
1924 Poll::Ready(Ok(Some(frame))) => frames.push(frame),
1925 _ => break,
1926 }
1927 }
1928 match head.index() == start {
1929 true => start = head_start as u64,
1930 false => frames.clear(),
1932 }
1933 }
1934 while let Poll::Ready(Ok(Some(frame))) = tail.poll_read_frame(&kio::Waiter::noop()) {
1935 frames.push(frame);
1936 }
1937 if frames.is_empty() {
1938 return None;
1939 }
1940
1941 let rebuilt = WarmGroup(
1943 track
1944 .create_group(group::Info {
1945 sequence: live.sequence,
1946 })
1947 .ok()?,
1948 );
1949 let mut writer = rebuilt.0.clone();
1950 if start > 0 {
1951 writer.start_at(start).ok()?;
1952 }
1953 for frame in frames {
1954 writer.write_frame(frame.timestamp, frame.payload).ok()?;
1955 }
1956 if live.is_finished() {
1957 writer.finish().ok()?;
1958 }
1959 Some(rebuilt)
1960}
1961
1962struct FrontTask {
1964 shared: kio::Shared<OriginState>,
1966 broadcast: broadcast::Producer,
1968 path: PathOwned,
1970 horizon: Horizon,
1972 watch: Watch,
1974 request: kio::Producer<PendingBroadcast>,
1976 pin: kio::Lock<Pin>,
1978 timers: Clock,
1979}
1980
1981struct TrackIo {
1984 resume: super::resume::Producer,
1985 staged: Option<(u64, track::Consumer)>,
1987 query: Option<(u64, track::Consumer, track::Querying)>,
1989 copy: Option<(u64, track::Consumer)>,
1991 edge: Option<track::Position>,
1996 warm: Option<WarmCopy>,
1999 head: Option<WarmGroup>,
2002 used: bool,
2004}
2005
2006async fn run_front(task: FrontTask) {
2010 let FrontTask {
2011 shared,
2012 broadcast,
2013 path,
2014 horizon,
2015 watch,
2016 request,
2017 pin,
2018 timers,
2019 } = task;
2020
2021 enum Step {
2023 Assigned(Arc<str>, super::resume::Producer),
2024 Resolved(u64, Result<broadcast::Consumer, Error>),
2025 SourceClosed(u64),
2026 Info(Arc<str>, u64, Result<track::Info, Error>),
2027 Ended(Arc<str>, u64, Result<(), Error>),
2028 Demand(Arc<str>),
2029 Deadline,
2030 Table,
2031 }
2032
2033 let mut front = Front::new(TRACK_IDLE_LINGER);
2034 let mut sources: HashMap<u64, broadcast::Consumer> = HashMap::new();
2035 let mut next_source = 0u64;
2036 let mut upstream: Option<(u64, kio::Consumer<PendingBroadcast>)> = None;
2038 let mut tracks: HashMap<Arc<str>, TrackIo> = HashMap::new();
2039 let mut deadline = crate::runtime::Deadline::new(&timers);
2040 let mut seen = 0;
2042 let mut events: VecDeque<Event> = VecDeque::new();
2043
2044 let select = |front: &mut Front, sources: &HashMap<u64, broadcast::Consumer>, seen: &mut u64| -> Event {
2047 let table = shared.read();
2048 if table.closed {
2049 return Event::Closed;
2050 }
2051 *seen = watch.seen();
2053 front.retain_routes(|route| table.routes.covers(&path.as_path(), route));
2054 let best = table
2055 .best_route(&path.as_path(), horizon, front.pin(), front.refused_routes())
2056 .map(|entry| Candidate {
2057 route: entry.id,
2058 first: entry.hops.iter().next().copied(),
2059 local: entry.local,
2060 });
2061 let serving_closing = front
2062 .serving()
2063 .and_then(|id| sources.get(&id))
2064 .is_some_and(|source| source.is_closing());
2065 Event::Selected { best, serving_closing }
2066 };
2067
2068 events.push_back(select(&mut front, &sources, &mut seen));
2069
2070 loop {
2071 while let Some(event) = events.pop_front() {
2072 for action in front.step(event) {
2073 match action {
2074 Action::Reselect => events.push_back(select(&mut front, &sources, &mut seen)),
2075 Action::Request { route } => {
2076 let found = {
2078 let table = shared.read();
2079 table
2080 .routes
2081 .covering(&path.as_path())
2082 .find(|entry| entry.id == route)
2083 .map(|entry| {
2084 (
2085 Candidate {
2086 route,
2087 first: entry.hops.iter().next().copied(),
2088 local: entry.local,
2089 },
2090 entry.source.clone(),
2091 entry.server.clone(),
2092 )
2093 })
2094 };
2095 let Some((candidate, source, server)) = found else {
2096 events.push_back(Event::Resolved {
2097 route,
2098 result: Err(Refusal {
2099 err: Error::Unroutable,
2100 standing: false,
2101 }),
2102 });
2103 continue;
2104 };
2105 front.identify(candidate);
2106 *pin.lock() = front.pin();
2107 if let Some(source) = source {
2108 let id = next_source;
2109 next_source += 1;
2110 sources.insert(id, source);
2111 events.push_back(Event::Resolved { route, result: Ok(id) });
2112 continue;
2113 }
2114 let Some(server) = server else {
2115 events.push_back(Event::Resolved {
2116 route,
2117 result: Err(Refusal {
2118 err: Error::Unroutable,
2119 standing: true,
2120 }),
2121 });
2122 continue;
2123 };
2124 let mut serve = server.lock();
2125 if serve.closed {
2126 drop(serve);
2129 events.push_back(Event::Resolved {
2130 route,
2131 result: Err(Refusal {
2132 err: Error::Unroutable,
2133 standing: true,
2134 }),
2135 });
2136 continue;
2137 }
2138 if let Some(weak) = serve.served.get(&path) {
2141 drop(serve);
2142 let id = next_source;
2143 next_source += 1;
2144 sources.insert(id, weak.consume());
2145 events.push_back(Event::Resolved { route, result: Ok(id) });
2146 continue;
2147 }
2148 let pending = match serve.requests.join(&path) {
2149 Some(producer) => producer.consume(),
2150 None => {
2151 let producer = kio::Producer::<PendingBroadcast>::default();
2152 let consumer = producer.consume();
2153 match serve.requests.insert(path.clone(), producer) {
2154 Ok(()) => consumer,
2155 Err(_) => {
2158 drop(serve);
2159 events.push_back(Event::Resolved {
2160 route,
2161 result: Err(Refusal {
2162 err: Error::Unroutable,
2163 standing: true,
2164 }),
2165 });
2166 continue;
2167 }
2168 }
2169 }
2170 };
2171 upstream = Some((route, pending));
2172 }
2173 Action::Detach { source } => {
2174 sources.remove(&source);
2175 for io in tracks.values_mut() {
2178 if io.copy.as_ref().is_some_and(|(s, _)| *s == source) {
2179 io.copy = None;
2180 }
2181 if io.query.as_ref().is_some_and(|(s, ..)| *s == source) {
2182 io.query = None;
2183 }
2184 if io.staged.as_ref().is_some_and(|(s, _)| *s == source) {
2185 io.staged = None;
2186 }
2187 }
2188 }
2189 Action::Resolve => {
2190 if let Ok(mut pending) = request.write() {
2191 pending.resolved.get_or_insert(Ok(broadcast.consume()));
2192 }
2193 }
2194 Action::Query { track: name, source } => {
2195 let Some(io) = tracks.get_mut(&name) else { continue };
2196 let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2197 match sources.get(&source).map(|s| s.track(&name)) {
2198 Some(Ok(copy)) => {
2199 let query = copy.query().into_inner();
2202 io.query = Some((source, copy, query));
2203 }
2204 Some(Err(err)) => events.push_back(Event::TrackInfo {
2205 track: name,
2206 source,
2207 closing,
2208 result: Err(err),
2209 }),
2210 None => {}
2211 }
2212 }
2213 Action::Splice { track: name, source } => {
2214 let Some(io) = tracks.get_mut(&name) else { continue };
2215 let Some((staged, copy)) = io.staged.take() else {
2216 continue;
2217 };
2218 if staged != source {
2219 continue;
2220 }
2221 if let Err(err) = io.resume.takeover(©) {
2222 let _ = io.resume.abort(err);
2226 tracks.remove(&name);
2227 continue;
2228 }
2229 io.head = io.warm.take().and_then(|mut warm| warm.edge.take());
2230 io.edge = io.resume.resume_position();
2233 io.copy = Some((source, copy));
2234 }
2235 Action::Park { track: name } => {
2236 let Some(io) = tracks.get_mut(&name) else { continue };
2237 let Some((_, copy)) = io.copy.take() else { continue };
2238 let warm = warm_copy(©, io.head.as_ref());
2242 drop(copy);
2243 io.head = None;
2244 let parked = match &warm {
2245 Some(warm) => io.resume.park(&warm.track),
2246 None => io.resume.release(),
2247 };
2248 if parked.is_err() {
2249 tracks.remove(&name);
2250 continue;
2251 }
2252 io.warm = warm;
2253 }
2254 Action::Release { track: name } => {
2255 let Some(io) = tracks.get_mut(&name) else { continue };
2256 io.copy = None;
2258 io.warm = None;
2259 io.head = None;
2260 if io.resume.release().is_err() {
2261 tracks.remove(&name);
2262 }
2263 }
2264 Action::Finish { track: name } => {
2265 if let Some(mut io) = tracks.remove(&name) {
2266 let _ = io.resume.finish();
2267 }
2268 }
2269 Action::Abort { track: name, err } => {
2270 if let Some(mut io) = tracks.remove(&name) {
2271 tracing::debug!(name = %name, %err, "aborting track");
2272 let _ = io.resume.abort(err);
2273 }
2274 }
2275 Action::Arm { at } => deadline.set(at),
2276 Action::End { err } => {
2277 if let Ok(mut pending) = request.write() {
2278 pending.resolved.get_or_insert(Err(err.clone()));
2279 }
2280 broadcast.close();
2287 broadcast.release_spliced(err.clone());
2288 for (_, mut io) in tracks.drain() {
2289 let waiting = io.staged.take().map(|(_, copy)| copy);
2293 let waiting = waiting.or_else(|| io.query.take().map(|(_, copy, _)| copy));
2294 if let Some(copy) = waiting
2295 && io.resume.is_used()
2296 {
2297 if io.resume.takeover(©).is_err() {
2298 continue;
2299 }
2300 io.warm = None;
2301 }
2302 if !io.resume.is_used() || !io.resume.is_spliced() || io.warm.is_some() {
2304 let _ = io.resume.abort(err.clone());
2305 }
2306 }
2307 return;
2308 }
2309 }
2310 }
2311 }
2312
2313 let step = kio::wait(|waiter| {
2314 if let Poll::Ready((name, resume)) = broadcast.poll_spliced_assigned(waiter) {
2315 return Poll::Ready(Step::Assigned(name, resume));
2316 }
2317 if let Some((route, pending)) = &upstream
2318 && let Poll::Ready(result) = pending.poll(waiter, |p| match &p.resolved {
2319 Some(result) => Poll::Ready(result.clone()),
2320 None => Poll::Pending,
2321 }) {
2322 return Poll::Ready(Step::Resolved(
2323 *route,
2324 match result {
2325 Ok(resolved) => resolved,
2326 Err(_closed) => Err(Error::Unroutable),
2329 },
2330 ));
2331 }
2332 if let Some(id) = front.serving()
2333 && let Some(source) = sources.get(&id)
2334 && source.poll_closed(waiter).is_ready()
2335 {
2336 return Poll::Ready(Step::SourceClosed(id));
2337 }
2338 for (name, io) in &tracks {
2339 if let Some((source, _, query)) = &io.query
2340 && let Poll::Ready(result) = query.poll(waiter)
2341 {
2342 return Poll::Ready(Step::Info(name.clone(), *source, result));
2343 }
2344 if let Some((source, copy)) = &io.copy
2345 && let Poll::Ready(result) = copy.poll_complete(waiter)
2346 {
2347 return Poll::Ready(Step::Ended(name.clone(), *source, result));
2348 }
2349 let edge = match io.used {
2351 true => io.resume.poll_unused(waiter),
2352 false => io.resume.poll_used(waiter),
2353 };
2354 if edge.is_ready() {
2355 return Poll::Ready(Step::Demand(name.clone()));
2356 }
2357 }
2358 if deadline.poll(waiter).is_ready() {
2359 return Poll::Ready(Step::Deadline);
2360 }
2361 watch.poll_changed(waiter, seen).map(|()| Step::Table)
2362 })
2363 .await;
2364
2365 let event = match step {
2366 Step::Assigned(name, resume) => {
2367 tracks.insert(
2368 name.clone(),
2369 TrackIo {
2370 resume,
2371 staged: None,
2372 query: None,
2373 copy: None,
2374 edge: None,
2375 warm: None,
2376 head: None,
2377 used: false,
2378 },
2379 );
2380 Event::TrackAssigned { track: name }
2381 }
2382 Step::Resolved(route, result) => {
2383 upstream = None;
2384 match result {
2385 Ok(source) => {
2386 let id = next_source;
2387 next_source += 1;
2388 sources.insert(id, source);
2389 Event::Resolved { route, result: Ok(id) }
2390 }
2391 Err(err) => {
2392 let standing =
2396 !matches!(err, Error::Unroutable) || shared.read().routes.covers(&path.as_path(), route);
2397 Event::Resolved {
2398 route,
2399 result: Err(Refusal { err, standing }),
2400 }
2401 }
2402 }
2403 }
2404 Step::SourceClosed(source) => Event::SourceClosed { source },
2405 Step::Info(name, source, result) => {
2406 let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2407 let Some(io) = tracks.get_mut(&name) else { continue };
2408 let Some((_, copy, _)) = io.query.take() else { continue };
2409 let result = match result {
2412 Ok(info) => match copy.poll_complete(&kio::Waiter::noop()) {
2413 Poll::Ready(Err(err)) => Err(err),
2414 _ => Ok(info),
2415 },
2416 Err(err) => Err(err),
2417 };
2418 if result.is_ok() && io.used {
2421 io.staged = Some((source, copy));
2422 }
2423 Event::TrackInfo {
2424 track: name,
2425 source,
2426 closing,
2427 result,
2428 }
2429 }
2430 Step::Ended(name, source, result) => {
2431 let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2432 let Some(io) = tracks.get_mut(&name) else { continue };
2433 io.copy = None;
2434 let delivered = io.resume.resume_position() != io.edge;
2435 Event::TrackEnded {
2436 track: name,
2437 source,
2438 closing,
2439 result,
2440 delivered,
2441 }
2442 }
2443 Step::Demand(name) => {
2444 let Some(io) = tracks.get_mut(&name) else { continue };
2445 io.used = io.resume.is_used();
2446 if !io.used {
2447 io.query = None;
2450 io.staged = None;
2451 }
2452 match io.used {
2453 true => Event::Used { track: name },
2454 false => Event::Unused {
2455 track: name,
2456 now: timers.now(),
2457 },
2458 }
2459 }
2460 Step::Deadline => {
2461 deadline.set(None);
2464 Event::Deadline { now: timers.now() }
2465 }
2466 Step::Table => select(&mut front, &sources, &mut seen),
2467 };
2468 events.push_back(event);
2469 }
2470}
2471
2472#[derive(Default)]
2477struct RouteTable {
2478 root: RouteNode,
2479}
2480
2481#[derive(Default)]
2484struct RouteNode {
2485 entries: Vec<RouteEntry>,
2487 cursors: Vec<ConsumerId>,
2489 cursors_below: usize,
2492 watches: Vec<(u64, kio::Producer<Watched>)>,
2495 watches_below: usize,
2498 children: HashMap<String, RouteNode>,
2499}
2500
2501#[derive(Default)]
2504struct Watched {
2505 generation: u64,
2506}
2507
2508struct Watch {
2514 shared: kio::Shared<OriginState>,
2515 path: PathOwned,
2516 id: u64,
2517 signal: kio::Consumer<Watched>,
2518}
2519
2520impl Watch {
2521 fn seen(&self) -> u64 {
2525 self.signal.read().generation
2526 }
2527
2528 fn poll_changed(&self, waiter: &kio::Waiter, seen: u64) -> Poll<()> {
2530 self.signal
2531 .poll(waiter, |watched| match watched.generation != seen {
2532 true => Poll::Ready(()),
2533 false => Poll::Pending,
2534 })
2535 .map(|_| ())
2536 }
2537}
2538
2539impl Drop for Watch {
2540 fn drop(&mut self) {
2541 self.shared.lock().routes.remove_watch(&self.path, self.id);
2542 }
2543}
2544
2545#[derive(Clone, Copy)]
2547struct Below {
2548 cursors: usize,
2549 watches: usize,
2550}
2551
2552impl Below {
2553 const NONE: Self = Self { cursors: 0, watches: 0 };
2554 const CURSOR: Self = Self { cursors: 1, watches: 0 };
2555 const WATCH: Self = Self { cursors: 0, watches: 1 };
2556}
2557
2558impl RouteNode {
2559 fn is_empty(&self) -> bool {
2561 self.entries.is_empty() && self.cursors.is_empty() && self.watches.is_empty() && self.children.is_empty()
2562 }
2563
2564 fn find<'a>(&self, mut parts: impl Iterator<Item = &'a str>) -> Option<&Self> {
2566 match parts.next() {
2567 None => Some(self),
2568 Some(part) => self.children.get(part)?.find(parts),
2569 }
2570 }
2571
2572 fn reach<'a>(&mut self, mut parts: impl Iterator<Item = &'a str>, below: Below) -> &mut Self {
2575 self.cursors_below += below.cursors;
2576 self.watches_below += below.watches;
2577 match parts.next() {
2578 None => self,
2579 Some(part) => self.children.entry(part.to_string()).or_default().reach(parts, below),
2580 }
2581 }
2582
2583 fn edit<'a, R>(
2587 &mut self,
2588 mut parts: impl Iterator<Item = &'a str>,
2589 below: Below,
2590 f: impl FnOnce(&mut Self) -> R,
2591 ) -> Option<R> {
2592 let result = match parts.next() {
2593 None => f(self),
2594 Some(part) => {
2595 let child = self.children.get_mut(part)?;
2596 let result = child.edit(parts, below, f)?;
2597 if child.is_empty() {
2598 self.children.remove(part);
2599 }
2600 result
2601 }
2602 };
2603 self.cursors_below -= below.cursors;
2604 self.watches_below -= below.watches;
2605 Some(result)
2606 }
2607
2608 fn poke(&self) {
2610 for (_, watch) in &self.watches {
2611 if let Ok(mut watched) = watch.write() {
2612 watched.generation += 1;
2613 }
2614 }
2615 }
2616
2617 fn poke_below(&self) {
2620 if self.watches_below == 0 {
2621 return;
2622 }
2623 self.poke();
2624 for child in self.children.values() {
2625 child.poke_below();
2626 }
2627 }
2628
2629 fn walk<'a>(&'a self, visit: &mut impl FnMut(&'a Self)) {
2631 visit(self);
2632 for child in self.children.values() {
2633 child.walk(visit);
2634 }
2635 }
2636
2637 fn collect_cursors(&self, out: &mut Vec<ConsumerId>) {
2639 if self.cursors_below == 0 {
2640 return;
2641 }
2642 out.extend(&self.cursors);
2643 for child in self.children.values() {
2644 child.collect_cursors(out);
2645 }
2646 }
2647}
2648
2649impl RouteTable {
2650 fn split(&self, path: &Path) -> (Vec<&RouteNode>, Option<&RouteNode>) {
2653 let mut above = Vec::new();
2654 let mut node = &self.root;
2655 for part in path.parts() {
2656 above.push(node);
2657 match node.children.get(part) {
2658 Some(child) => node = child,
2659 None => return (above, None),
2660 }
2661 }
2662 (above, Some(node))
2663 }
2664
2665 fn covering(&self, path: &Path) -> impl Iterator<Item = &RouteEntry> {
2667 let (above, at) = self.split(path);
2668 above.into_iter().chain(at).flat_map(|node| node.entries.iter())
2669 }
2670
2671 fn covers(&self, path: &Path, id: u64) -> bool {
2673 self.covering(path).any(|entry| entry.id == id)
2674 }
2675
2676 fn at(&self, prefix: &Path) -> impl Iterator<Item = &RouteEntry> {
2678 self.root
2679 .find(prefix.parts())
2680 .into_iter()
2681 .flat_map(|node| node.entries.iter())
2682 }
2683
2684 fn entries(&self) -> impl Iterator<Item = &RouteEntry> {
2686 let mut nodes = Vec::new();
2687 self.root.walk(&mut |node| nodes.push(node));
2688 nodes.into_iter().flat_map(|node| node.entries.iter())
2689 }
2690
2691 fn insert(&mut self, entry: RouteEntry) {
2693 let node = self.root.reach(entry.prefix.parts(), Below::NONE);
2694 node.entries.push(entry);
2695 }
2696
2697 fn entry_mut(&mut self, prefix: &Path, id: u64) -> Option<&mut RouteEntry> {
2699 let mut node = &mut self.root;
2700 for part in prefix.parts() {
2701 node = node.children.get_mut(part)?;
2702 }
2703 node.entries.iter_mut().find(|entry| entry.id == id)
2704 }
2705
2706 fn remove(&mut self, prefix: &Path, id: u64) -> Option<RouteEntry> {
2708 self.root
2709 .edit(prefix.parts(), Below::NONE, |node| {
2710 let index = node.entries.iter().position(|entry| entry.id == id)?;
2711 Some(node.entries.swap_remove(index))
2712 })
2713 .flatten()
2714 }
2715
2716 fn add_cursor(&mut self, head: &Path, id: ConsumerId) {
2718 self.root.reach(head.parts(), Below::CURSOR).cursors.push(id);
2719 }
2720
2721 fn remove_cursor(&mut self, head: &Path, id: ConsumerId) {
2724 self.root.edit(head.parts(), Below::CURSOR, |node| {
2725 node.cursors.retain(|cursor| *cursor != id)
2726 });
2727 }
2728
2729 fn add_watch(&mut self, path: &Path, id: u64) -> kio::Consumer<Watched> {
2731 let producer = kio::Producer::<Watched>::default();
2732 let consumer = producer.consume();
2733 self.root.reach(path.parts(), Below::WATCH).watches.push((id, producer));
2734 consumer
2735 }
2736
2737 fn remove_watch(&mut self, path: &Path, id: u64) {
2740 self.root.edit(path.parts(), Below::WATCH, |node| {
2741 node.watches.retain(|(watch, _)| *watch != id)
2742 });
2743 }
2744
2745 fn poke_below(&self, prefix: &Path) {
2747 if let (_, Some(node)) = self.split(prefix) {
2748 node.poke_below();
2749 }
2750 }
2751
2752 fn poke_all(&self) {
2754 self.root.walk(&mut |node| node.poke());
2755 }
2756
2757 fn cursors_touching(&self, prefix: &Path) -> Vec<ConsumerId> {
2761 let (above, at) = self.split(prefix);
2762 let mut cursors: Vec<ConsumerId> = above.iter().flat_map(|node| node.cursors.iter().copied()).collect();
2763 if let Some(node) = at {
2764 node.collect_cursors(&mut cursors);
2765 }
2766 cursors.sort_unstable();
2768 cursors.dedup();
2769 cursors
2770 }
2771}
2772
2773#[derive(Default)]
2780struct OriginState {
2781 routes: RouteTable,
2784 next_route: u64,
2785 next_watch: u64,
2786
2787 cursors: HashMap<ConsumerId, TableCursor>,
2791
2792 fronts: WeakCache<FrontKey, RemoteFront>,
2800
2801 closed: bool,
2804}
2805
2806impl OriginState {
2807 fn sync_route(&mut self, prefix: &Path, claim: &Pattern) {
2812 let routes = &self.routes;
2814 for id in routes.cursors_touching(prefix) {
2815 let Some(cursor) = self.cursors.get_mut(&id) else {
2816 continue;
2817 };
2818 if let Some(presented) = cursor.presented(prefix, claim) {
2819 Self::sync_cursor(routes, cursor, &presented);
2820 }
2821 }
2822 routes.poke_below(prefix);
2824 }
2825
2826 fn watch(&mut self, shared: &kio::Shared<OriginState>, path: &Path) -> Watch {
2828 let id = self.next_watch;
2829 self.next_watch += 1;
2830 let signal = self.routes.add_watch(path, id);
2831 Watch {
2832 shared: shared.clone(),
2833 path: path.to_owned(),
2834 id,
2835 signal,
2836 }
2837 }
2838
2839 fn sync_cursor(routes: &RouteTable, cursor: &mut TableCursor, presented: &PathOwned) {
2842 let candidates: Vec<&RouteEntry> = match presented.is_empty() {
2848 true => routes
2849 .covering(&cursor.root)
2850 .filter(|entry| cursor.visible(entry))
2851 .collect(),
2852 false => {
2853 let absolute = cursor.root.join(presented);
2854 routes.at(&absolute).filter(|entry| cursor.visible(entry)).collect()
2855 }
2856 };
2857 let most = candidates.iter().map(|entry| entry.prefix.len()).max();
2858 let best = most.and_then(|most| {
2859 candidates
2860 .into_iter()
2861 .filter(|entry| entry.prefix.len() == most)
2862 .min_by_key(|entry| route_order(&entry.prefix, entry))
2863 });
2864
2865 match best {
2866 Some(entry) => {
2867 let meta = (entry.hops.clone(), entry.cost, entry.entered());
2868 let served = entry.server.is_some();
2869 let captures = cursor.captures(&entry.prefix);
2870 let previous = cursor
2871 .current
2872 .insert(presented.clone(), (entry.id, meta.clone(), served, captures.clone()));
2873 match previous {
2874 Some((_, prev, prev_served, prev_captures))
2881 if prev == meta && prev_served == served && prev_captures == captures => {}
2882 Some((_, prev, _, prev_captures)) if prev_captures != captures => {
2885 if let Ok(mut state) = cursor.state.write() {
2886 state.apply_unannounce(presented.clone(), prev, prev_captures);
2887 state.apply_announce(presented.clone(), meta, captures);
2888 }
2889 }
2890 _ => {
2891 if let Ok(mut state) = cursor.state.write() {
2892 state.apply_announce(presented.clone(), meta, captures);
2893 }
2894 }
2895 }
2896 }
2897 None => {
2898 if let Some((_, last, _, captures)) = cursor.current.remove(presented)
2899 && let Ok(mut state) = cursor.state.write()
2900 {
2901 state.apply_unannounce(presented.clone(), last, captures);
2902 }
2903 }
2904 }
2905 }
2906
2907 fn register_cursor(&mut self, id: ConsumerId, mut cursor: TableCursor) {
2909 let mut presented: BTreeSet<PathOwned> = BTreeSet::new();
2912 for head in &cursor.heads {
2913 let (above, at) = self.routes.split(head);
2914 let mut nodes = above;
2915 if let Some(node) = at {
2916 node.walk(&mut |node| nodes.push(node));
2917 }
2918 for entry in nodes.into_iter().flat_map(|node| node.entries.iter()) {
2919 if let Some(p) = cursor.presented(&entry.prefix, &entry.claim) {
2920 presented.insert(p);
2921 }
2922 }
2923 }
2924 for p in &presented {
2925 Self::sync_cursor(&self.routes, &mut cursor, p);
2926 }
2927 for head in &cursor.heads {
2928 self.routes.add_cursor(head, id);
2929 }
2930 self.cursors.insert(id, cursor);
2931 }
2932
2933 fn best_route(&self, path: &Path, horizon: Horizon, pin: Pin, refused: &HashSet<u64>) -> Option<&RouteEntry> {
2948 let (above, at) = self.routes.split(path);
2952 let mut best = None;
2953 for node in above.into_iter().chain(at) {
2954 let mut candidates = node
2955 .entries
2956 .iter()
2957 .filter(|entry| entry.advertised)
2958 .filter(|entry| entry.scope.matches(path.as_str()))
2959 .filter(|entry| horizon.admits(entry))
2960 .filter(|entry| entry.qualifies(pin))
2961 .filter(|entry| !refused.contains(&entry.id))
2962 .peekable();
2963 if candidates.peek().is_some() {
2964 best = candidates
2965 .filter(|entry| entry.serves(path))
2966 .min_by_key(|entry| route_order(&entry.prefix, entry));
2967 }
2968 }
2969 best
2970 }
2971}
2972
2973#[derive(Default)]
2980struct PendingBroadcast {
2981 resolved: Option<Result<broadcast::Consumer, Error>>,
2982}
2983
2984#[must_use = "dropping an origin::Dynamic retracts the route"]
2996pub struct Dynamic {
2997 announcement: AnnounceProducer,
2999 state: kio::Shared<ServeState>,
3000}
3001
3002impl Dynamic {
3003 pub fn update(&self, route: Route) -> Result<(), Error> {
3011 self.announcement.update(route)
3012 }
3013
3014 pub fn poll_requested_broadcast(&self, waiter: &kio::Waiter) -> Poll<Result<Request, Error>> {
3019 let mut state = ready!(self.state.poll(waiter, |state| {
3020 if state.closed || state.requests.has_queued() {
3021 Poll::Ready(())
3022 } else {
3023 Poll::Pending
3024 }
3025 }));
3026
3027 if state.closed {
3029 return Poll::Ready(Err(Error::Closed));
3030 }
3031
3032 let path = state.requests.pop().expect("predicate guaranteed a request");
3033 let producer = state.requests.get(&path).expect("popped key must be pending").clone();
3039 Poll::Ready(Ok(Request {
3040 path,
3041 producer,
3042 home: self.state.clone(),
3043 }))
3044 }
3045
3046 pub async fn requested_broadcast(&self) -> Result<Request, Error> {
3052 kio::wait(|waiter| self.poll_requested_broadcast(waiter)).await
3053 }
3054}
3055
3056impl ServeState {
3057 fn resolve(
3068 shared: &kio::Shared<Self>,
3069 path: &PathOwned,
3070 producer: &kio::Producer<PendingBroadcast>,
3071 result: Result<broadcast::Consumer, Error>,
3072 ) {
3073 let mut state = shared.lock();
3074 if state.closed {
3075 return;
3076 }
3077 let resolved = match result {
3078 Ok(broadcast) => {
3079 let existing = state.served.insert(path.clone(), broadcast.weak());
3083 Ok(existing.map(|weak| weak.consume()).unwrap_or(broadcast))
3084 }
3085 Err(err) => Err(err),
3086 };
3087 state.requests.remove_if(path, |p| p.same_channel(producer));
3088 if let Ok(mut pending) = producer.write() {
3089 pending.resolved.get_or_insert(resolved);
3090 drop(state);
3091 }
3092 }
3093
3094 fn forget(shared: &kio::Shared<Self>, path: &PathOwned, producer: &kio::Producer<PendingBroadcast>) {
3096 shared.lock().requests.remove_if(path, |p| p.same_channel(producer));
3097 }
3098}
3099
3100pub struct Request {
3107 path: PathOwned,
3109
3110 producer: kio::Producer<PendingBroadcast>,
3113
3114 home: kio::Shared<ServeState>,
3117}
3118
3119impl Request {
3120 pub fn path(&self) -> &Path<'_> {
3122 &self.path
3123 }
3124
3125 pub fn accept(self, broadcast: impl Consume<broadcast::Consumer>) {
3131 let broadcast = broadcast.consume();
3132 ServeState::resolve(&self.home, &self.path, &self.producer, Ok(broadcast));
3133 }
3135
3136 pub fn reject(self, err: Error) {
3138 ServeState::resolve(&self.home, &self.path, &self.producer, Err(err));
3139 }
3140}
3141
3142impl Drop for Request {
3143 fn drop(&mut self) {
3144 ServeState::forget(&self.home, &self.path, &self.producer);
3152 }
3153}
3154
3155pub struct Requesting {
3162 inner: RequestState,
3163 path: PathOwned,
3167 stats: stats::Scope,
3170}
3171
3172enum RequestState {
3173 Failed(Error),
3176 Pending(kio::Consumer<PendingBroadcast>),
3178}
3179
3180impl Requesting {
3181 fn failed(error: Error) -> Self {
3182 Self::new(RequestState::Failed(error))
3183 }
3184
3185 fn queued(consumer: kio::Consumer<PendingBroadcast>) -> Self {
3186 Self::new(RequestState::Pending(consumer))
3187 }
3188
3189 pub fn is_queued(&self) -> bool {
3198 matches!(self.inner, RequestState::Pending(_))
3199 }
3200
3201 fn new(inner: RequestState) -> Self {
3202 Self {
3203 inner,
3204 path: PathOwned::default(),
3205 stats: stats::Scope::default(),
3206 }
3207 }
3208
3209 fn with_path(mut self, path: PathOwned) -> Self {
3210 self.path = path;
3211 self
3212 }
3213
3214 fn with_stats(mut self, scope: stats::Scope) -> Self {
3216 self.stats = scope;
3217 self
3218 }
3219
3220 fn hand_out(&self, broadcast: broadcast::Consumer) -> broadcast::Consumer {
3222 broadcast.with_path(self.path.clone()).with_stats(self.stats.clone())
3223 }
3224
3225 pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<broadcast::Consumer, Error>> {
3227 match &self.inner {
3228 RequestState::Failed(error) => Poll::Ready(Err(error.clone())),
3229 RequestState::Pending(consumer) => Poll::Ready(
3230 match ready!(consumer.poll(waiter, |state| match &state.resolved {
3231 Some(result) => Poll::Ready(result.clone()),
3232 None => Poll::Pending,
3233 })) {
3234 Ok(result) => result.map(|broadcast| self.hand_out(broadcast)),
3235 Err(_closed) => Err(Error::Unroutable),
3237 },
3238 ),
3239 }
3240 }
3241}
3242
3243impl kio::Pollable for Requesting {
3244 type Output = Result<broadcast::Consumer, Error>;
3245
3246 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
3247 self.poll_ok(waiter)
3248 }
3249}
3250
3251pub trait Consume<T> {
3259 fn consume(&self) -> T;
3261}
3262
3263impl<T, U: Consume<T>> Consume<T> for &U {
3264 fn consume(&self) -> T {
3265 (**self).consume()
3266 }
3267}
3268
3269impl Consume<Consumer> for Producer {
3270 fn consume(&self) -> Consumer {
3271 Consumer::from_producer(self, stats::Session::default())
3275 }
3276}
3277
3278impl Consume<Consumer> for Consumer {
3279 fn consume(&self) -> Consumer {
3280 self.clone()
3281 }
3282}
3283
3284impl Consume<broadcast::Consumer> for broadcast::Producer {
3285 fn consume(&self) -> broadcast::Consumer {
3286 self.consume()
3288 }
3289}
3290
3291impl Consume<broadcast::Consumer> for broadcast::Consumer {
3292 fn consume(&self) -> broadcast::Consumer {
3293 self.clone()
3294 }
3295}
3296
3297impl Consume<track::Consumer> for track::Producer {
3298 fn consume(&self) -> track::Consumer {
3299 self.consume()
3300 }
3301}
3302
3303impl Consume<track::Consumer> for track::Consumer {
3304 fn consume(&self) -> track::Consumer {
3305 self.clone()
3306 }
3307}
3308
3309#[derive(Clone)]
3315pub struct Consumer {
3316 hop: Hop,
3318 scope: OriginScope,
3319
3320 root: PathOwned,
3322
3323 shared: kio::Shared<OriginState>,
3326
3327 stats: stats::Session,
3331
3332 horizon: Horizon,
3337
3338 hidden: Hidden,
3340
3341 pool: cache::Pool,
3344 cache_duration: Duration,
3345
3346 tasks: TasksWeak,
3350
3351 timers: Clock,
3354}
3355
3356impl Consumer {
3357 fn from_producer(producer: &Producer, stats: stats::Session) -> Self {
3358 Self {
3359 hop: producer.hop,
3360 scope: producer.scope.clone(),
3361 root: producer.root.clone(),
3362 shared: producer.shared.clone(),
3363 stats,
3364 horizon: Horizon::default(),
3365 hidden: Hidden::default(),
3366 pool: producer.pool.clone(),
3367 cache_duration: producer.cache_duration,
3368 tasks: producer.tasks.downgrade(),
3369 timers: producer.timers.clone(),
3370 }
3371 }
3372
3373 pub fn hop(&self) -> Hop {
3375 self.hop
3376 }
3377
3378 pub(crate) fn excluding(mut self, peer: Hop) -> Self {
3386 self.horizon.exclude = Some(peer);
3387 self
3388 }
3389
3390 pub fn local(mut self) -> Self {
3397 self.horizon.local = true;
3398 self
3399 }
3400
3401 pub fn with_hidden(mut self, hidden: bool) -> Self {
3406 self.hidden.include = hidden;
3407 self
3408 }
3409
3410 pub(crate) fn beyond(mut self, outer: &Consumer) -> Self {
3413 self.hidden.beyond = Some(interest_prefixes(&outer.scope.allowed));
3414 self
3415 }
3416
3417 pub(crate) fn includes_hidden(&self) -> bool {
3419 self.hidden.include
3420 }
3421
3422 pub fn with_stats(mut self, session: stats::Session) -> Self {
3426 self.stats = session;
3427 self
3428 }
3429
3430 fn untagged(&self) -> Self {
3434 Self {
3435 stats: stats::Session::default(),
3436 ..self.clone()
3437 }
3438 }
3439
3440 pub(crate) fn empty(&self) -> Self {
3445 Self {
3446 scope: OriginScope::empty(),
3447 ..self.clone()
3448 }
3449 }
3450
3451 pub fn announced(&self) -> AnnounceConsumer {
3460 AnnounceConsumer::new(
3461 self.root.clone(),
3462 self.scope.allowed.clone(),
3463 self.stats.clone(),
3464 self.horizon,
3465 self.hidden.clone(),
3466 &self.shared,
3467 )
3468 }
3469
3470 pub fn consume(&self) -> Self {
3472 self.clone()
3473 }
3474
3475 #[cfg(test)]
3478 pub(crate) fn get_broadcast(&self, path: impl AsPath) -> Option<broadcast::Consumer> {
3479 let full = self.root.join(path).to_owned();
3480 if !self.scope.permits(&full) {
3481 return None;
3482 }
3483 let table = self.shared.lock();
3484 table
3485 .routes
3486 .at(&full)
3487 .filter(|entry| entry.local)
3488 .min_by_key(|entry| route_order(&entry.prefix, entry))
3489 .and_then(|entry| entry.source.clone())
3490 }
3491
3492 pub async fn routed(&self, path: impl AsPath) -> Option<Route> {
3502 let path = path.as_path();
3503
3504 let consumer = match Pattern::subtree(path.as_str()) {
3509 Ok(subtree) => self.scope("", &Patterns::from(subtree)).ok()?,
3510 Err(InvalidPattern::TooManySegments) => self.clone(),
3511 Err(_) => return None,
3512 };
3513
3514 if !consumer.allowed().matches(path.as_str()) {
3518 return None;
3519 }
3520
3521 let mut announced = consumer.untagged().with_hidden(true).announced();
3525 loop {
3526 let update = announced.next().await?;
3527 if update.kind.is_active() && path.has_prefix(&update.prefix) {
3528 return Some(update.route);
3529 }
3530 }
3531 }
3532
3533 pub async fn routed_broadcast(&self, path: impl AsPath) -> Result<broadcast::Consumer, Error> {
3547 let path = path.as_path();
3548
3549 if !self.allowed().matches(path.as_str()) {
3552 return Err(Error::Unauthorized);
3553 }
3554 loop {
3555 let (watch, seen) = {
3564 let mut table = self.shared.lock();
3565 if table.closed {
3566 return Err(Error::Closed);
3567 }
3568 let watch = table.watch(&self.shared, &self.root.join(&path));
3569 let seen = watch.seen();
3570 (watch, seen)
3571 };
3572 match self.request_broadcast(&path).await {
3573 Ok(broadcast) => return Ok(broadcast),
3574 Err(Error::Unroutable) => {
3575 kio::wait(|waiter| watch.poll_changed(waiter, seen)).await;
3576 }
3577 Err(Error::Dropped) if self.shared.lock().closed => return Err(Error::Closed),
3580 Err(err) => return Err(err),
3581 }
3582 }
3583 }
3584
3585 pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Consumer, Error> {
3592 let root = self.root.join(root).to_owned();
3593 let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
3594 let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
3595 Ok(Consumer {
3596 scope,
3597 root,
3598 ..self.clone()
3599 })
3600 }
3601
3602 pub fn request_broadcast(&self, path: impl AsPath) -> kio::Pending<Requesting> {
3625 let path = path.as_path();
3626
3627 let absolute = self.root.join(&path).to_owned();
3631 let scope = self.stats.egress(&absolute);
3632 let requested = path.to_owned();
3636
3637 if !self.scope.permits(&absolute) {
3639 return kio::Pending::new(Requesting::failed(Error::Unauthorized));
3640 }
3641
3642 let mut state = self.shared.lock();
3643
3644 if state.closed {
3646 return kio::Pending::new(Requesting::failed(Error::Closed));
3647 }
3648
3649 if state
3653 .best_route(&absolute.as_path(), self.horizon, Pin::Any, &HashSet::new())
3654 .is_none()
3655 {
3656 return kio::Pending::new(Requesting::failed(Error::Unroutable));
3657 }
3658
3659 let key = (absolute.clone(), self.horizon);
3667 if let Some(front) = state.fronts.get(&key) {
3668 let pin = *front.pin.lock();
3669 let current = state
3670 .best_route(&absolute.as_path(), self.horizon, Pin::Any, &HashSet::new())
3671 .is_some_and(|entry| entry.qualifies(pin));
3672 if current {
3673 let pending = Requesting::queued(front.request.consume())
3674 .with_path(requested)
3675 .with_stats(scope);
3676 return kio::Pending::new(pending);
3677 }
3678 state.fronts.remove(&key);
3679 }
3680
3681 let broadcast = broadcast::Producer::new_spliced(broadcast::Info {
3686 pool: self.pool.clone(),
3687 cache_duration: self.cache_duration,
3688 path: absolute.clone(),
3689 });
3690 let request = kio::Producer::<PendingBroadcast>::default();
3691 let consumer = request.consume();
3692 let watch = state.watch(&self.shared, &absolute);
3693 let pin = kio::Lock::new(Pin::Any);
3694 state.fronts.insert(
3695 key,
3696 RemoteFront {
3697 request: request.clone(),
3698 broadcast: broadcast.consume().weak(),
3699 pin: pin.clone(),
3700 },
3701 );
3702 drop(state);
3705 self.tasks.push(run_front(FrontTask {
3706 shared: self.shared.clone(),
3707 broadcast,
3708 path: absolute,
3709 horizon: self.horizon,
3710 watch,
3711 request,
3712 pin,
3713 timers: self.timers.clone(),
3714 }));
3715 kio::Pending::new(Requesting::queued(consumer).with_path(requested).with_stats(scope))
3716 }
3717
3718 pub fn root(&self) -> &Path<'_> {
3720 &self.root
3721 }
3722
3723 pub fn allowed(&self) -> Patterns {
3725 self.scope.relative(&self.root)
3726 }
3727
3728 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
3730 self.root.join(path)
3731 }
3732}
3733
3734pub struct AnnounceConsumer {
3739 id: ConsumerId,
3740 shared: kio::Shared<OriginState>,
3741 root: PathOwned,
3742
3743 state: kio::Producer<OriginConsumerState>,
3746
3747 stats: stats::Session,
3750
3751 guards: HashMap<PathOwned, stats::Announce>,
3755
3756 park: kio::Park,
3759}
3760
3761impl AnnounceConsumer {
3762 fn new(
3763 root: PathOwned,
3764 allowed: Patterns,
3765 stats: stats::Session,
3766 horizon: Horizon,
3767 hidden: Hidden,
3768 shared: &kio::Shared<OriginState>,
3769 ) -> Self {
3770 let state = kio::Producer::<OriginConsumerState>::default();
3771 let id = ConsumerId::new();
3772
3773 {
3774 let mut table = shared.lock();
3775 if table.closed {
3776 if let Ok(mut state) = state.write() {
3778 state.ended = true;
3779 }
3780 } else {
3781 table.register_cursor(
3782 id,
3783 TableCursor {
3784 root: root.clone(),
3785 heads: interest_prefixes(&allowed),
3786 allowed,
3787 horizon,
3788 hidden,
3789 state: state.clone(),
3790 current: HashMap::new(),
3791 },
3792 );
3793 }
3794 }
3795
3796 Self {
3797 id,
3798 shared: shared.clone(),
3799 root,
3800 state,
3801 stats,
3802 guards: HashMap::new(),
3803 park: kio::Park::default(),
3804 }
3805 }
3806
3807 fn hand_out(&mut self, update: AnnounceUpdate) -> AnnounceUpdate {
3809 let absolute = self.root.join(&update.prefix).to_owned();
3810 if update.kind.is_active() {
3811 let scope = self.stats.egress(&absolute);
3812 self.guards
3813 .entry(update.prefix.clone())
3814 .or_insert_with(|| scope.announce());
3815 } else {
3816 self.guards.remove(&update.prefix);
3817 }
3818 update
3819 }
3820
3821 pub async fn next(&mut self) -> Option<AnnounceUpdate> {
3829 kio::wait(|waiter| self.poll_next(waiter)).await
3830 }
3831
3832 pub fn poll_next(&mut self, waiter: &kio::Waiter) -> Poll<Option<AnnounceUpdate>> {
3838 let update = {
3839 let mut state = match ready!(self.state.poll(waiter, |state| {
3840 if state.pending.is_empty() && !state.ended {
3841 Poll::Pending
3842 } else {
3843 Poll::Ready(())
3844 }
3845 })) {
3846 Ok(state) => state,
3847 Err(_) => return Poll::Ready(None),
3849 };
3850 match state.take() {
3851 Some(update) => update,
3852 None => {
3853 state.close();
3856 return Poll::Ready(None);
3857 }
3858 }
3859 };
3860 Poll::Ready(Some(self.hand_out(update)))
3861 }
3862
3863 pub fn try_next(&mut self) -> Option<AnnounceUpdate> {
3868 let update = self.state.write().ok()?.take()?;
3869 Some(self.hand_out(update))
3870 }
3871
3872 pub fn is_closed(&self) -> bool {
3874 let state = self.state.read();
3875 state.is_closed() || state.ended
3876 }
3877
3878 pub fn root(&self) -> &Path<'_> {
3880 &self.root
3881 }
3882
3883 pub fn absolute(&self, prefix: impl AsPath) -> Path<'_> {
3885 self.root.join(prefix)
3886 }
3887}
3888
3889impl futures::Stream for AnnounceConsumer {
3890 type Item = AnnounceUpdate;
3891
3892 fn poll_next(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Option<Self::Item>> {
3893 let this = self.get_mut();
3894 let waiter = this.park.hold(cx).clone();
3895 this.poll_next(&waiter)
3896 }
3897}
3898
3899impl Drop for AnnounceConsumer {
3900 fn drop(&mut self) {
3901 let mut shared = self.shared.lock();
3902 if let Some(cursor) = shared.cursors.remove(&self.id) {
3903 for head in &cursor.heads {
3904 shared.routes.remove_cursor(head, self.id);
3905 }
3906 }
3907 }
3908}
3909
3910#[cfg(test)]
3911use futures::FutureExt;
3912
3913#[cfg(test)]
3914#[allow(missing_docs)] impl AnnounceConsumer {
3916 pub fn assert_next_active(&mut self, expected: impl AsPath) -> Route {
3918 let expected = expected.as_path();
3919 let update = self.next().now_or_never().expect("next blocked").expect("no next");
3920 assert_eq!(update.prefix, expected, "wrong prefix");
3921 assert!(update.kind.is_active(), "should be an active route");
3922 update.route
3923 }
3924
3925 pub fn assert_try_next_active(&mut self, expected: impl AsPath) -> Route {
3927 let expected = expected.as_path();
3928 let update = self.try_next().expect("no next");
3929 assert_eq!(update.prefix, expected, "wrong prefix");
3930 assert!(update.kind.is_active(), "should be an active route");
3931 update.route
3932 }
3933
3934 pub fn assert_next_ended(&mut self, expected: impl AsPath) {
3936 let expected = expected.as_path();
3937 let update = self.next().now_or_never().expect("next blocked").expect("no next");
3938 assert_eq!(update.prefix, expected, "wrong prefix");
3939 assert_eq!(update.kind, AnnounceKind::Retracted, "should be a retraction");
3940 }
3941
3942 pub fn assert_next_wait(&mut self) {
3943 if let Some(res) = self.next().now_or_never() {
3944 panic!("next should block: got {:?}", res.map(|u| u.prefix));
3945 }
3946 }
3947}
3948
3949#[cfg(test)]
3953pub(crate) trait ProduceTest {
3954 fn produce(self) -> Producer;
3955}
3956
3957#[cfg(test)]
3958impl ProduceTest for Config {
3959 fn produce(self) -> Producer {
3960 let (producer, driver) = Producer::new(self);
3961 if tokio::runtime::Handle::try_current().is_ok() {
3962 tokio::spawn(crate::time::run(driver));
3963 } else {
3964 std::mem::forget(driver);
3967 }
3968 producer
3969 }
3970}
3971
3972#[cfg(test)]
3973impl ProduceTest for Hop {
3974 fn produce(self) -> Producer {
3975 Config::new(self).produce()
3976 }
3977}
3978
3979#[cfg(test)]
3980mod tests {
3981 use super::*;
3982 use futures::FutureExt;
3983
3984 fn origin(id: u64) -> Hop {
3985 Hop::new(id).unwrap()
3986 }
3987
3988 fn hops(ids: &[u64]) -> Hops {
3989 let mut list = Hops::new();
3990 for &id in ids {
3991 list.push(if id == 0 { Hop::UNKNOWN } else { origin(id) }).unwrap();
3992 }
3993 list
3994 }
3995
3996 fn scopes(prefixes: &[&str]) -> Patterns {
3998 prefixes
3999 .iter()
4000 .map(|prefix| Pattern::subtree(prefix).unwrap())
4001 .collect()
4002 }
4003
4004 #[test]
4005 fn default_config_mints_a_real_hop() {
4006 let config = Config::default();
4007 assert_ne!(config.hop, Hop::UNKNOWN);
4008 let (producer, _driver) = Producer::new(config.clone());
4009 assert_eq!(producer.hop(), config.hop);
4010 assert_eq!(producer.consume().hop(), config.hop);
4011 }
4012
4013 #[test]
4014 fn random_hops_fit_legacy_lite_clients() {
4015 for _ in 0..32 {
4016 assert!(Hop::random().id() < 1u64 << 53);
4017 }
4018 }
4019
4020 async fn settle(mut check: impl FnMut() -> bool) {
4023 for _ in 0..100 {
4024 if check() {
4025 return;
4026 }
4027 tokio::task::yield_now().await;
4028 }
4029 panic!("condition never settled");
4030 }
4031
4032 async fn queued(server: &Dynamic) -> Request {
4034 let mut request = None;
4035 settle(|| match server.poll_requested_broadcast(&kio::Waiter::noop()) {
4036 Poll::Ready(Ok(popped)) => {
4037 request = Some(popped);
4038 true
4039 }
4040 _ => false,
4041 })
4042 .await;
4043 request.unwrap()
4044 }
4045
4046 async fn next_group(subscription: &mut crate::track::Subscriber) -> Result<Option<crate::group::Consumer>, Error> {
4048 let mut next = None;
4049 settle(|| match subscription.poll_recv_group(&kio::Waiter::noop()) {
4050 Poll::Ready(result) => {
4051 next = Some(result);
4052 true
4053 }
4054 Poll::Pending => false,
4055 })
4056 .await;
4057 next.unwrap()
4058 }
4059
4060 #[tokio::test]
4061 async fn announce_and_retract() {
4062 let producer = origin(1).produce();
4063 let consumer = producer.consume();
4064 let mut announced = consumer.announced();
4065 announced.assert_next_wait();
4066
4067 let announcement = producer.announce("room/alice", Route::default()).unwrap();
4068 let route = announced.assert_next_active("room/alice");
4069 assert!(route.hops.is_empty());
4070 assert_eq!(route.cost, Cost::default());
4071 announced.assert_next_wait();
4072
4073 drop(announcement);
4074 announced.assert_next_ended("room/alice");
4075 announced.assert_next_wait();
4076 }
4077
4078 #[tokio::test]
4081 async fn hidden_routes_need_an_opt_in() {
4082 let producer = origin(1).produce();
4083 let consumer = producer.consume();
4084 let _visible = producer.announce("room/alice", Route::default()).unwrap();
4085 let _stats = producer.announce(".stats/node", Route::default()).unwrap();
4086 let _nested = producer.announce("room/.internal", Route::default()).unwrap();
4087 let _suffix = producer.announce("room/catalog.pro", Route::default()).unwrap();
4089
4090 let mut announced = consumer.announced();
4091 announced.assert_next_active("room/alice");
4092 announced.assert_next_active("room/catalog.pro");
4093 announced.assert_next_wait();
4094
4095 let mut announced = consumer.clone().with_hidden(true).announced();
4096 announced.assert_next_active(".stats/node");
4097 announced.assert_next_active("room/.internal");
4098 announced.assert_next_active("room/alice");
4099 announced.assert_next_active("room/catalog.pro");
4100 announced.assert_next_wait();
4101
4102 let mut announced = consumer
4104 .scope(".stats", &Patterns::from(Pattern::all()))
4105 .unwrap()
4106 .announced();
4107 announced.assert_next_active("node");
4108 announced.assert_next_wait();
4109 let mut announced = consumer.scope("", &scopes(&["room/.internal"])).unwrap().announced();
4110 announced.assert_next_active("room/.internal");
4111 announced.assert_next_wait();
4112
4113 let mut announced = consumer.clone().with_hidden(true).beyond(&consumer).announced();
4116 announced.assert_next_active(".stats/node");
4117 announced.assert_next_active("room/.internal");
4118 announced.assert_next_wait();
4119 let room = consumer.scope("", &scopes(&["room"])).unwrap().beyond(&consumer);
4120 let mut announced = room.announced();
4121 announced.assert_next_wait();
4122 let stats = consumer.scope("", &scopes(&[".stats"])).unwrap().beyond(&consumer);
4123 let mut announced = stats.announced();
4124 announced.assert_next_active(".stats/node");
4125 announced.assert_next_wait();
4126 }
4127
4128 #[tokio::test]
4130 async fn hidden_broadcast_resolves_by_path() {
4131 let producer = origin(1).produce();
4132 let consumer = producer.consume();
4133 let broadcast = producer.create_broadcast(".stats/node").unwrap();
4134 broadcast.announce(Route::default()).unwrap();
4135
4136 consumer.announced().assert_next_wait();
4137 let resolved = consumer.request_broadcast(".stats/node").await.expect("resolves");
4138 assert_eq!(resolved.info().path.as_str(), ".stats/node");
4139 }
4140
4141 #[tokio::test]
4143 async fn hidden_route_announced_later_stays_hidden() {
4144 let producer = origin(1).produce();
4145 let consumer = producer.consume();
4146 let mut announced = consumer.announced();
4147 let mut opted = consumer.clone().with_hidden(true).announced();
4148
4149 let hidden = producer.announce(".stats/node", Route::default()).unwrap();
4150 announced.assert_next_wait();
4151 opted.assert_next_active(".stats/node");
4152
4153 drop(hidden);
4154 announced.assert_next_wait();
4155 opted.assert_next_ended(".stats/node");
4156 }
4157
4158 #[tokio::test]
4159 async fn broadcast_announces_its_own_path() {
4160 let producer = origin(1).produce();
4161 let consumer = producer.consume();
4162 let mut announced = consumer.announced();
4163 let mut peer = consumer.clone().excluding(Hop::UNKNOWN).announced();
4164
4165 let broadcast = producer.create_broadcast("room/alice").unwrap();
4167 announced.assert_next_wait();
4168 peer.assert_next_wait();
4169
4170 broadcast.announce(Route::default().with_cost(3)).unwrap();
4171 assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(3));
4172 assert_eq!(peer.assert_next_active("room/alice").cost, Cost::new(3));
4173
4174 broadcast.announce(Route::default().with_cost(1)).unwrap();
4176 assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(1));
4177 assert_eq!(peer.assert_next_active("room/alice").cost, Cost::new(1));
4178
4179 broadcast.unannounce();
4181 announced.assert_next_ended("room/alice");
4182 peer.assert_next_ended("room/alice");
4183 broadcast.unannounce();
4184 announced.assert_next_wait();
4185 let err = consumer.request_broadcast("room/alice").await.err().unwrap();
4186 assert!(matches!(err, Error::Unroutable));
4187
4188 broadcast.announce(Route::default()).unwrap();
4190 announced.assert_next_active("room/alice");
4191 peer.assert_next_active("room/alice");
4192 broadcast.close();
4193 announced.assert_next_ended("room/alice");
4194 peer.assert_next_ended("room/alice");
4195 assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
4196 announced.assert_next_wait();
4197 }
4198
4199 #[tokio::test]
4200 async fn broadcast_announcement_retracts_with_the_last_producer() {
4201 let producer = origin(1).produce();
4202 let consumer = producer.consume();
4203 let mut announced = consumer.announced();
4204
4205 let broadcast = producer.create_broadcast("room/alice").unwrap();
4206 let clone = broadcast.clone();
4207 broadcast.announce(Route::default()).unwrap();
4208 announced.assert_next_active("room/alice");
4209
4210 drop(broadcast);
4212 announced.assert_next_wait();
4213 drop(clone);
4214 announced.assert_next_ended("room/alice");
4215 }
4216
4217 #[tokio::test]
4218 async fn publish_creates_and_announces_together() {
4219 let producer = origin(1).produce();
4220 let mut announced = producer.consume().announced();
4221 let _broadcast = producer.publish("room/alice", Route::default()).unwrap();
4222 announced.assert_next_active("room/alice");
4223 }
4224
4225 #[tokio::test]
4226 async fn standalone_broadcast_cannot_announce() {
4227 let broadcast = broadcast::Info::new().produce();
4228 assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
4229 broadcast.unannounce();
4231 }
4232
4233 #[tokio::test]
4234 async fn announce_replays_to_late_cursor() {
4235 let producer = origin(1).produce();
4236 let _a = producer.announce("room/alice", Route::default()).unwrap();
4237 let _b = producer.announce("room/bob", Route::default()).unwrap();
4238
4239 let mut announced = producer.consume().announced();
4240 announced.assert_next_active("room/alice");
4242 announced.assert_next_active("room/bob");
4243 announced.assert_next_wait();
4244 }
4245
4246 #[tokio::test]
4247 async fn announce_keeps_its_prefix_under_a_producer_scope() {
4248 let producer = origin(1).produce();
4249 let scoped = producer.scope("", &scopes(&["room"])).unwrap();
4250
4251 let _a = scoped.announce("", Route::default()).unwrap();
4253 let mut announced = producer.consume().announced();
4254 announced.assert_next_active("");
4255
4256 assert!(matches!(
4258 scoped.announce("other", Route::default()),
4259 Err(Error::Unauthorized)
4260 ));
4261 }
4262
4263 #[tokio::test]
4264 async fn cursor_keeps_an_overlapping_prefix_above_its_scope() {
4265 let producer = origin(1).produce();
4266 let _a = producer.announce("", Route::default()).unwrap();
4267
4268 let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
4269 let mut announced = consumer.announced();
4270 announced.assert_next_active("");
4271 }
4272
4273 #[tokio::test]
4274 async fn cursor_root_strips_prefix() {
4275 let producer = origin(1).produce();
4276 let _a = producer.announce("room/alice", Route::default()).unwrap();
4277
4278 let consumer = producer
4279 .consume()
4280 .scope("room", &Patterns::from(Pattern::all()))
4281 .unwrap();
4282 let mut announced = consumer.announced();
4283 announced.assert_next_active("alice");
4284 }
4285
4286 #[tokio::test]
4287 async fn best_route_wins_and_fails_over() {
4288 let producer = origin(1).produce();
4289 let mut announced = producer.consume().announced();
4290
4291 let expensive = producer
4292 .announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4293 .unwrap();
4294 let route = announced.assert_next_active("room");
4295 assert_eq!(route.cost, Cost::new(5));
4296
4297 let cheap = producer
4299 .announce("room", Route::default().with_hops(hops(&[20])).with_cost(1))
4300 .unwrap();
4301 let route = announced.assert_next_active("room");
4302 assert_eq!(route.cost, Cost::new(1));
4303
4304 drop(cheap);
4306 let route = announced.assert_next_active("room");
4307 assert_eq!(route.cost, Cost::new(5));
4308
4309 drop(expensive);
4311 announced.assert_next_ended("room");
4312 }
4313
4314 #[tokio::test]
4315 async fn identical_reannounce_is_invisible() {
4316 let producer = origin(1).produce();
4317 let mut announced = producer.consume().announced();
4318
4319 let old = producer
4320 .announce("room", Route::default().with_hops(hops(&[10])))
4321 .unwrap();
4322 let first = announced.assert_next_active("room");
4323 assert_eq!(first.hops.as_slice(), hops(&[10]).as_slice());
4324
4325 let _new = producer
4329 .announce("room", Route::default().with_hops(hops(&[10])))
4330 .unwrap();
4331 announced.assert_next_wait();
4332
4333 drop(old);
4335 announced.assert_next_wait();
4336 }
4337
4338 #[tokio::test]
4339 async fn exclude_hides_routes_through_the_peer() {
4340 let producer = origin(1).produce();
4341 let _a = producer
4342 .announce("room", Route::default().with_hops(hops(&[7])))
4343 .unwrap();
4344
4345 let mut hidden = producer.consume().excluding(origin(7)).announced();
4346 hidden.assert_next_wait();
4347
4348 let mut visible = producer.consume().excluding(origin(8)).announced();
4349 visible.assert_next_active("room");
4350 }
4351
4352 #[tokio::test]
4353 async fn exclude_matches_via_when_the_chain_is_anonymous() {
4354 let producer = origin(1).produce();
4355 let assigned = origin(777);
4356 let _echoed = producer
4357 .announce("echoed", Route::default().with_hops(hops(&[0])).with_via(assigned))
4358 .unwrap();
4359 let _local = producer
4360 .announce("local", Route::default().with_hops(hops(&[10])))
4361 .unwrap();
4362
4363 let mut hidden = producer.consume().excluding(assigned).announced();
4364 hidden.assert_next_active("local");
4365 hidden.assert_next_wait();
4366 }
4367
4368 #[tokio::test]
4369 async fn anonymous_route_loses_to_identified_at_any_cost() {
4370 let producer = origin(1).produce();
4371 let mut announced = producer.consume().announced();
4372
4373 let _anonymous = producer
4374 .announce("room", Route::default().with_hops(hops(&[0])).with_cost(1))
4375 .unwrap();
4376 let route = announced.assert_next_active("room");
4377 assert!(route.is_anonymous());
4378 assert_eq!(route.cost, Cost::new(1));
4379
4380 let _identified = producer
4381 .announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4382 .unwrap();
4383 let route = announced.assert_next_active("room");
4384 assert!(!route.is_anonymous());
4385 assert_eq!(route.cost, Cost::new(5));
4386 }
4387
4388 #[tokio::test]
4389 async fn anonymous_routes_order_by_cost() {
4390 let producer = origin(1).produce();
4391 let mut announced = producer.consume().announced();
4392
4393 let expensive = producer
4394 .announce("room", Route::default().with_hops(hops(&[0])).with_cost(5))
4395 .unwrap();
4396 let route = announced.assert_next_active("room");
4397 assert_eq!(route.cost, Cost::new(5));
4398
4399 let _cheap = producer
4400 .announce("room", Route::default().with_hops(hops(&[0, 7])).with_cost(1))
4401 .unwrap();
4402 let route = announced.assert_next_active("room");
4403 assert!(route.is_anonymous());
4404 assert_eq!(route.cost, Cost::new(1));
4405
4406 drop(expensive);
4407 announced.assert_next_wait();
4408 }
4409
4410 #[tokio::test]
4411 async fn anonymous_chain_from_identified_peer_still_ranks_last() {
4412 let producer = origin(1).produce();
4413 let mut announced = producer.consume().announced();
4414
4415 let _anonymous = producer
4416 .announce(
4417 "room",
4418 Route::default()
4419 .with_hops(hops(&[0, 7]))
4420 .with_cost(1)
4421 .with_via(origin(7)),
4422 )
4423 .unwrap();
4424 announced.assert_next_active("room");
4425
4426 let _identified = producer
4427 .announce("room", Route::default().with_hops(hops(&[10, 20])).with_cost(5))
4428 .unwrap();
4429 let route = announced.assert_next_active("room");
4430 assert!(!route.is_anonymous());
4431 assert_eq!(route.cost, Cost::new(5));
4432 }
4433
4434 #[tokio::test]
4435 async fn request_prefers_identified_over_cheaper_anonymous() {
4436 let producer = origin(1).produce();
4437 let consumer = producer.consume();
4438
4439 let anonymous = producer
4440 .dynamic("room", Route::default().with_hops(hops(&[0])).with_cost(1))
4441 .unwrap();
4442 let identified = producer
4443 .dynamic("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4444 .unwrap();
4445
4446 let _pending = consumer.request_broadcast("room/alice");
4447 let request = queued(&identified).await;
4448 assert_eq!(request.path().as_str(), "room/alice");
4449 assert!(
4450 anonymous.poll_requested_broadcast(&kio::Waiter::noop()).is_pending(),
4451 "the cheaper anonymous route must not serve"
4452 );
4453 }
4454
4455 #[tokio::test]
4456 async fn update_reprices_in_place() {
4457 let producer = origin(1).produce();
4458 let mut announced = producer.consume().announced();
4459
4460 let announcement = producer.announce("room", Route::default()).unwrap();
4461 announced.assert_next_active("room");
4462
4463 announcement.update(Route::default().with_cost(9)).unwrap();
4464 let route = announced.assert_next_active("room");
4465 assert_eq!(route.cost, Cost::new(9));
4466 }
4467
4468 #[tokio::test]
4469 async fn retract_after_undelivered_reprice_still_delivered() {
4470 let producer = origin(1).produce();
4471 let mut announced = producer.consume().announced();
4472
4473 let announcement = producer.announce("room", Route::default()).unwrap();
4474 announced.assert_next_active("room");
4475
4476 announcement.update(Route::default().with_cost(9)).unwrap();
4480 drop(announcement);
4481 announced.assert_next_ended("room");
4482 announced.assert_next_wait();
4483 }
4484
4485 #[tokio::test]
4486 async fn scoped_cursor_advertises_most_specific_covering_route() {
4487 let producer = origin(1).produce();
4488 let _broad = producer.announce("room", Route::default().with_cost(1)).unwrap();
4491 let _narrow = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
4492
4493 let consumer = producer
4494 .consume()
4495 .scope("room/alice", &Patterns::from(Pattern::all()))
4496 .unwrap();
4497 let mut announced = consumer.announced();
4498 let route = announced.assert_next_active("");
4499 assert_eq!(route.cost, Cost::new(9));
4500 announced.assert_next_wait();
4501 }
4502
4503 #[tokio::test]
4504 async fn capture_change_retracts_before_reannouncing_a_presented_prefix() {
4505 let producer = origin(1).produce();
4506 let _broad = producer.announce("room", Route::default()).unwrap();
4507 let exact = producer.announce("room/alice", Route::default()).unwrap();
4508 let consumer = producer
4509 .consume()
4510 .scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
4511 .unwrap()
4512 .scope("room/alice", &Patterns::from(Pattern::all()))
4513 .unwrap();
4514 let mut announced = consumer.announced();
4515
4516 let first = announced.next().now_or_never().expect("next").expect("announce");
4517 assert_eq!(first.prefix.as_str(), "");
4518 assert_eq!(first.kind, AnnounceKind::Announced);
4519 assert_eq!(first.captures, Some(Vec::new()));
4520
4521 drop(exact);
4522 let retracted = announced.next().now_or_never().expect("next").expect("retract");
4523 assert_eq!(retracted.prefix.as_str(), "");
4524 assert_eq!(retracted.kind, AnnounceKind::Retracted);
4525 assert_eq!(retracted.captures, Some(Vec::new()));
4526 let replacement = announced.next().now_or_never().expect("next").expect("announce");
4527 assert_eq!(replacement.prefix.as_str(), "");
4528 assert_eq!(replacement.kind, AnnounceKind::Announced);
4529 assert_eq!(replacement.captures, None);
4530 }
4531
4532 #[tokio::test]
4533 async fn routed_broadcast_resolves_once_announced() {
4534 let producer = origin(1).produce();
4535 let consumer = producer.consume();
4536
4537 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4539 assert!((&mut resolving).now_or_never().is_none());
4540
4541 let broadcast = producer.create_broadcast("room/alice").unwrap();
4543 for _ in 0..20 {
4544 tokio::task::yield_now().await;
4545 }
4546 assert!((&mut resolving).now_or_never().is_none());
4547
4548 broadcast.announce(Route::default()).unwrap();
4549 let resolved = resolving.await.expect("resolves once announced");
4550 assert_eq!(resolved.info().path.as_str(), "room/alice");
4551 drop(broadcast);
4552 }
4553
4554 #[tokio::test]
4557 async fn cheaper_remote_route_beats_a_local_broadcast() {
4558 let producer = origin(1).produce();
4559 let consumer = producer.consume();
4560 let mut announced = consumer.announced();
4561
4562 let _local = producer.publish("room/alice", Route::default().with_cost(5)).unwrap();
4563 assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(5));
4564
4565 let server = producer
4566 .dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(1))
4567 .unwrap();
4568 let route = announced.assert_next_active("room/alice");
4569 assert_eq!(route.cost, Cost::new(1));
4570 assert_eq!(route.hops, hops(&[10]));
4571
4572 let pending = consumer.request_broadcast("room/alice");
4574 let request = queued(&server).await;
4575 let upstream = broadcast::Info::new().produce();
4576 request.accept(&upstream);
4577 pending.await.expect("resolves through the cheaper route");
4578 }
4579
4580 #[tokio::test]
4584 async fn cheaper_route_after_a_front_wins_new_requests() {
4585 let producer = origin(1).produce();
4586 let consumer = producer.consume();
4587
4588 let _local = producer.publish("room/alice", Route::default().with_cost(5)).unwrap();
4589 let first = consumer
4590 .request_broadcast("room/alice")
4591 .await
4592 .expect("resolves locally");
4593
4594 let server = producer
4595 .dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(1))
4596 .unwrap();
4597
4598 let pending = consumer.request_broadcast("room/alice");
4599 let request = queued(&server).await;
4600 let upstream = broadcast::Info::new().produce();
4601 request.accept(&upstream);
4602 let second = pending.await.expect("resolves through the cheaper route");
4603
4604 assert!(!first.is_closed(), "the old front must keep serving its readers");
4605 assert!(!first.is_clone(&second), "the newcomer must not join the old front");
4606 }
4607
4608 #[tokio::test]
4612 async fn local_broadcast_wins_a_tie_with_a_hopless_route() {
4613 let producer = origin(1).produce();
4614 let consumer = producer.consume();
4615
4616 let _local = producer.publish("room/alice", Route::default()).unwrap();
4617 let server = producer.dynamic("room/alice", Route::default()).unwrap();
4618
4619 let resolved = tokio::time::timeout(Duration::from_secs(1), consumer.request_broadcast("room/alice"))
4621 .await
4622 .expect("the newer hopless route won the tie")
4623 .expect("resolves");
4624 assert_eq!(resolved.info().path.as_str(), "room/alice");
4625 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4626 }
4627
4628 #[tokio::test]
4631 async fn announce_stats_follow_the_advertisement() {
4632 let registry = stats::Registry::new(stats::Config::new());
4633 let producer = origin(1)
4634 .produce()
4635 .with_stats(registry.tier(stats::Tier::default()).session("root"));
4636 let announces = || {
4637 registry
4638 .snapshot()
4639 .traffic()
4640 .into_iter()
4641 .find(|(_, role, _)| *role == stats::Role::Subscriber)
4642 .map(|(_, _, traffic)| (traffic.announces_started, traffic.announces_ended))
4643 .unwrap_or_default()
4644 };
4645
4646 let broadcast = producer.create_broadcast("room/alice").unwrap();
4647 assert_eq!(announces(), (0, 0), "a hidden broadcast is not announced");
4648 broadcast.announce(Route::default()).unwrap();
4649 broadcast
4650 .announce(Route {
4651 cost: Cost::new(3),
4652 ..Route::default()
4653 })
4654 .unwrap();
4655 assert_eq!(announces(), (1, 0), "a re-price is not another announce");
4656 broadcast.unannounce();
4657 assert_eq!(announces(), (1, 1));
4658 broadcast.announce(Route::default()).unwrap();
4659 drop(broadcast);
4660 assert_eq!(announces(), (2, 2));
4661 }
4662
4663 #[tokio::test]
4665 async fn local_broadcast_wins_a_cost_tie() {
4666 let producer = origin(1).produce();
4667 let consumer = producer.consume();
4668 let mut announced = consumer.announced();
4669
4670 let server = producer
4671 .dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(2))
4672 .unwrap();
4673 announced.assert_next_active("room/alice");
4674 let _local = producer.publish("room/alice", Route::default().with_cost(2)).unwrap();
4675 assert!(announced.assert_next_active("room/alice").hops.is_empty());
4676
4677 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4678 assert_eq!(resolved.info().path.as_str(), "room/alice");
4679 for _ in 0..20 {
4680 tokio::task::yield_now().await;
4681 }
4682 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4683 }
4684
4685 #[tokio::test]
4688 async fn unannounce_ends_the_front() {
4689 let producer = origin(1).produce();
4690 let consumer = producer.consume();
4691
4692 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4693 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4694
4695 broadcast.unannounce();
4696 let err = consumer.request_broadcast("room/alice").await.err().unwrap();
4697 assert!(matches!(err, Error::Unroutable), "joined a retracted front: {err}");
4698 settle(|| resolved.is_closed()).await;
4699 assert!(!broadcast.consume().is_closed(), "the broadcast itself lives on");
4700
4701 broadcast.announce(Route::default()).unwrap();
4703 let again = consumer.request_broadcast("room/alice").await.expect("resolves again");
4704 assert!(!again.is_clone(&resolved));
4705 }
4706
4707 #[tokio::test]
4711 async fn unannounce_keeps_a_track_awaiting_its_info() {
4712 let producer = origin(1).produce();
4713 let consumer = producer.consume();
4714
4715 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4716 let mut dynamic = broadcast.dynamic();
4717 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4718 let track = resolved.track("video").unwrap();
4719 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4720 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4721 .await
4722 .expect("the front asked the source")
4723 .expect("request");
4724
4725 broadcast.unannounce();
4726 settle(|| resolved.is_closed()).await;
4727
4728 let source = request.accept(None);
4729 let mut group = source.append_group().unwrap();
4730 group.write_frame(crate::Timestamp::ZERO, b"late".as_ref()).unwrap();
4731 group.finish().unwrap();
4732 source.finish().unwrap();
4733
4734 let mut subscription = subscribing.await.unwrap().expect("subscribe survives the retraction");
4735 let mut group = subscription.recv_group().await.unwrap().expect("the source's group");
4736 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"late");
4737 assert!(matches!(subscription.recv_group().await, Ok(None)), "ends cleanly");
4738 }
4739
4740 async fn served_front() -> (Dynamic, broadcast::Producer, broadcast::Dynamic, broadcast::Consumer) {
4743 let producer = origin(1).produce();
4744 let consumer = producer.consume();
4745 let server = producer
4746 .dynamic("room/alice", Route::default().with_hops(hops(&[10])))
4747 .unwrap();
4748 let pending = consumer.request_broadcast("room/alice");
4749 let upstream = broadcast::Info::new().produce();
4750 let dynamic = upstream.dynamic();
4751 queued(&server).await.accept(&upstream);
4752 let resolved = pending.await.expect("resolves");
4753 (server, upstream, dynamic, resolved)
4754 }
4755
4756 #[tokio::test]
4761 async fn returning_reader_skips_a_warm_cache_the_copy_resolved_past() {
4762 let ms = |v: u64| crate::Timestamp::from_millis(v).unwrap();
4763 let (_server, _upstream, mut dynamic, resolved) = served_front().await;
4764 let budget = track::Subscription::default().with_max_age(Duration::from_millis(100));
4765
4766 let track = resolved.track("audio").unwrap();
4767 let b = budget.clone();
4768 let subscribing = tokio::spawn(async move { track.subscribe(b).await });
4769 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4770 .await
4771 .expect("the front asked the source")
4772 .expect("request");
4773 let source = request.resolving_start().accept(None);
4774 for seq in 0..4u64 {
4775 let mut group = source.create_group(seq.into()).unwrap();
4776 group.write_frame(ms(seq * 20), b"old".as_ref()).unwrap();
4777 group.finish().unwrap();
4778 }
4779 let mut subscription = subscribing.await.unwrap().expect("subscribe");
4780 subscription.recv_group().await.unwrap().expect("the live group");
4781 drop(subscription);
4782 tokio::time::timeout(Duration::from_secs(1), source.unused())
4783 .await
4784 .expect("parked")
4785 .expect("source open");
4786 drop(source);
4787
4788 let track = resolved.track("audio").unwrap();
4789 let subscribing = tokio::spawn(async move { track.subscribe(budget).await });
4790 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4791 .await
4792 .expect("the front asked the source again")
4793 .expect("request");
4794 let mut source = request.resolving_start().accept(None);
4795 let mut subscription = subscribing.await.unwrap().expect("resubscribe");
4796
4797 assert!(
4799 tokio::time::timeout(Duration::from_millis(50), subscription.recv_group())
4800 .await
4801 .is_err(),
4802 "the warm cache was served before the copy resolved its start"
4803 );
4804
4805 source.start_at(20).unwrap();
4807 let mut group = source.create_group(20u64.into()).unwrap();
4808 group.write_frame(ms(2000), b"new".as_ref()).unwrap();
4809 group.finish().unwrap();
4810 let group = subscription.recv_group().await.unwrap().expect("the live group");
4811 assert_eq!(group.sequence, 20, "a stale warm group was served");
4812 }
4813
4814 #[tokio::test]
4819 async fn returning_reader_replays_a_current_warm_cache() {
4820 let (_server, _upstream, mut dynamic, resolved) = served_front().await;
4821
4822 let track = resolved.track("catalog").unwrap();
4823 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4824 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4825 .await
4826 .expect("the front asked the source")
4827 .expect("request");
4828 let source = request.resolving_start().accept(None);
4829 let mut group = source.create_group(0u64.into()).unwrap();
4830 group.write_frame(crate::Timestamp::ZERO, b"snapshot".as_ref()).unwrap();
4831 group.finish().unwrap();
4832 let mut subscription = subscribing.await.unwrap().expect("subscribe");
4833 subscription.recv_group().await.unwrap().expect("the catalog");
4834 drop(subscription);
4835 tokio::time::timeout(Duration::from_secs(1), source.unused())
4836 .await
4837 .expect("parked")
4838 .expect("source open");
4839 drop(source);
4840
4841 let track = resolved.track("catalog").unwrap();
4842 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4843 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4844 .await
4845 .expect("the front asked the source again")
4846 .expect("request");
4847 let mut source = request.resolving_start().accept(None);
4848 let mut subscription = subscribing.await.unwrap().expect("resubscribe");
4849
4850 let reading = tokio::spawn(async move {
4853 let mut group = subscription.recv_group().await.unwrap().expect("the catalog");
4854 assert_eq!(group.sequence, 0);
4855 group.read_frame().await.unwrap().expect("the snapshot").payload
4856 });
4857 tokio::task::yield_now().await;
4858 assert_eq!(
4859 source.subscription().and_then(|sub| sub.start),
4860 Some(track::Position { group: 0, frame: 1 }),
4861 "the re-splice asked past the cached catalog"
4862 );
4863 source.start_at(0).unwrap();
4864 let mut tail = source.create_group(0u64.into()).unwrap();
4865 tail.start_at(1).unwrap();
4866 tail.finish().unwrap();
4867 let payload = tokio::time::timeout(Duration::from_secs(1), reading)
4868 .await
4869 .expect("the returning reader never got the catalog")
4870 .unwrap();
4871 assert_eq!(&payload[..], b"snapshot");
4872 }
4873
4874 #[tokio::test]
4879 async fn returning_reader_continues_an_open_warm_group() {
4880 let (_server, _upstream, mut dynamic, resolved) = served_front().await;
4881
4882 async fn read(group: &mut group::Consumer) -> Vec<u8> {
4883 let frame = tokio::time::timeout(Duration::from_secs(1), group.read_frame())
4884 .await
4885 .expect("frame")
4886 .unwrap()
4887 .expect("group ended");
4888 frame.payload.to_vec()
4889 }
4890
4891 let mut expect: Vec<&[u8]> = Vec::new();
4892 let mut floor: Option<track::Position> = None;
4893 for (round, payload) in [b"a".as_ref(), b"b", b"c"].into_iter().enumerate() {
4894 let track = resolved.track("log").unwrap();
4895 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4896 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4897 .await
4898 .expect("the front asked the source")
4899 .expect("request");
4900 let mut source = request.resolving_start().accept(None);
4901 let mut subscription = subscribing.await.unwrap().expect("subscribe");
4902
4903 source.start_at(0).unwrap();
4905 let mut group = source.create_group(0u64.into()).unwrap();
4906 if let Some(floor) = floor {
4907 group.start_at(floor.frame).unwrap();
4908 }
4909 group.write_frame(crate::Timestamp::ZERO, payload).unwrap();
4910 expect.push(payload);
4911 source
4912 .insert_datagram(10, crate::Timestamp::ZERO, b"datagram".as_ref())
4913 .unwrap();
4914
4915 let mut reading = tokio::time::timeout(Duration::from_secs(1), subscription.recv_group())
4916 .await
4917 .expect("group 0")
4918 .unwrap()
4919 .expect("track ended");
4920 assert_eq!(reading.sequence, 0);
4921 for frame in &expect {
4922 assert_eq!(read(&mut reading).await, *frame, "round {round}");
4923 }
4924 assert_eq!(
4925 source.subscription().and_then(|sub| sub.start),
4926 floor,
4927 "round {round} asked for the wrong continuation"
4928 );
4929
4930 drop(reading);
4931 drop(subscription);
4932 tokio::time::timeout(Duration::from_secs(1), source.unused())
4933 .await
4934 .expect("parked")
4935 .expect("source open");
4936 drop(group);
4937 drop(source);
4938 floor = Some(track::Position {
4939 group: 0,
4940 frame: expect.len() as u64,
4941 });
4942 }
4943 }
4944
4945 #[tokio::test]
4948 async fn unannounce_keeps_a_returning_reader_awaiting_its_info() {
4949 let producer = origin(1).produce();
4950 let consumer = producer.consume();
4951
4952 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4953 let mut dynamic = broadcast.dynamic();
4954 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4955 let track = resolved.track("video").unwrap();
4956 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4957 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4958 .await
4959 .expect("the front asked the source")
4960 .expect("request");
4961 let source = request.accept(None);
4962 let mut group = source.append_group().unwrap();
4963 group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
4964 group.finish().unwrap();
4965 let mut subscription = subscribing.await.unwrap().expect("subscribe");
4966 subscription.recv_group().await.unwrap().expect("the cached group");
4967 drop(subscription);
4968
4969 tokio::time::timeout(Duration::from_secs(1), source.unused())
4972 .await
4973 .expect("parked")
4974 .expect("source open");
4975 drop(source);
4976 let track = resolved.track("video").unwrap();
4977 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4978 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4979 .await
4980 .expect("the front asked the source again")
4981 .expect("request");
4982
4983 broadcast.unannounce();
4984 settle(|| resolved.is_closed()).await;
4985
4986 let source = request.accept(None);
4988 let mut group = source.create_group(1u64.into()).unwrap();
4989 group.write_frame(crate::Timestamp::ZERO, b"late".as_ref()).unwrap();
4990 group.finish().unwrap();
4991 source.finish().unwrap();
4992
4993 let mut subscription = subscribing.await.unwrap().expect("subscribe survives the retraction");
4994 let mut payloads = Vec::new();
4995 while let Some(mut group) = subscription.recv_group().await.expect("ends cleanly") {
4996 payloads.push(group.read_frame().await.unwrap().unwrap().payload);
4997 }
4998 assert_eq!(payloads.last().map(|p| &p[..]), Some(&b"late"[..]));
4999 }
5000
5001 #[tokio::test]
5004 async fn reannounce_before_the_front_acts_keeps_it() {
5005 let producer = origin(1).produce();
5006 let consumer = producer.consume();
5007
5008 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
5009 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5010
5011 broadcast.unannounce();
5012 broadcast.announce(Route::default()).unwrap();
5013 for _ in 0..20 {
5014 tokio::task::yield_now().await;
5015 }
5016 assert!(!resolved.is_closed(), "the front ended across a reannouncement");
5017 let again = consumer.request_broadcast("room/alice").await.expect("resolves");
5018 assert!(again.is_clone(&resolved));
5019 }
5020
5021 #[tokio::test]
5022 async fn local_broadcast_resolves_once_announced() {
5023 let producer = origin(1).produce();
5024 let consumer = producer.consume();
5025
5026 let broadcast = producer.create_broadcast("room/alice").unwrap();
5028 let err = consumer
5029 .request_broadcast("room/alice")
5030 .now_or_never()
5031 .expect("unroutable is synchronous")
5032 .err()
5033 .unwrap();
5034 assert!(matches!(err, Error::Unroutable));
5035
5036 broadcast.announce(Route::default()).unwrap();
5037 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5038 assert_eq!(resolved.info().path.as_str(), "room/alice");
5039 drop(broadcast);
5040
5041 let err = consumer
5043 .request_broadcast("room/bob")
5044 .now_or_never()
5045 .expect("unroutable is synchronous")
5046 .err()
5047 .unwrap();
5048 assert!(matches!(err, Error::Unroutable));
5049 }
5050
5051 #[test]
5052 fn create_broadcast_accepts_a_max_depth_path() {
5053 let producer = origin(1).produce();
5054 let path = vec!["a"; Path::MAX_PARTS].join("/");
5055 let _broadcast = producer.create_broadcast(path.as_str()).expect("max depth is allowed");
5056 let deeper = vec!["a"; Path::MAX_PARTS + 1].join("/");
5057 assert!(matches!(
5058 producer.create_broadcast(deeper.as_str()),
5059 Err(Error::BoundsExceeded(_))
5060 ));
5061 }
5062
5063 #[tokio::test]
5064 async fn duplicate_routes_aggregate_until_the_last_leaves() {
5065 let producer = origin(1).produce();
5066 let first = producer.dynamic("live", Route::default().with_cost(3)).unwrap();
5067 let second = producer.dynamic("live", Route::default().with_cost(1)).unwrap();
5068
5069 let mut announced = producer.consume().announced();
5070 let update = announced.next().now_or_never().expect("next").expect("no next");
5071 assert_eq!(update.prefix.as_str(), "live");
5072 assert_eq!(update.kind, AnnounceKind::Announced);
5073 assert_eq!(update.route.cost, Cost::new(1));
5074 announced.assert_next_wait();
5075
5076 drop(second);
5077 let update = announced.next().now_or_never().expect("next").expect("no next");
5078 assert_eq!(update.prefix.as_str(), "live");
5079 assert_eq!(update.kind, AnnounceKind::Updated);
5080 assert_eq!(update.route.cost, Cost::new(3));
5081
5082 drop(first);
5083 announced.assert_next_ended("live");
5084 announced.assert_next_wait();
5085 }
5086
5087 #[test]
5088 fn dynamic_may_cover_a_scope_but_disjoint_prefixes_are_refused() {
5089 let producer = origin(1).produce();
5090 let scoped = producer.scope("", &scopes(&["room"])).unwrap();
5091 let _broad = scoped
5092 .dynamic("", Route::default())
5093 .expect("an overlapping prefix is accepted");
5094
5095 let _ok = scoped
5096 .dynamic("room/alice", Route::default())
5097 .expect("a contained prefix is accepted");
5098 assert!(matches!(
5099 scoped.dynamic("other", Route::default()),
5100 Err(Error::Unauthorized)
5101 ));
5102 }
5103
5104 #[tokio::test]
5105 async fn dynamic_route_keeps_its_producer_scope() {
5106 let producer = origin(1).produce();
5107 let scope = Patterns::from("*/chat".parse::<Pattern>().unwrap());
5108 let scoped = producer.scope("", &scope).unwrap();
5109 let dynamic = scoped.dynamic("", Route::default()).unwrap();
5110
5111 let mut matching = producer
5112 .consume()
5113 .scope("", &scopes(&["room/chat"]))
5114 .unwrap()
5115 .announced();
5116 matching.assert_next_active("");
5117 let mut outside = producer
5118 .consume()
5119 .scope("", &scopes(&["room/video"]))
5120 .unwrap()
5121 .announced();
5122 outside.assert_next_wait();
5123
5124 let refused = producer
5125 .consume()
5126 .request_broadcast("room/video")
5127 .now_or_never()
5128 .expect("an out-of-scope request must be refused synchronously");
5129 assert!(matches!(refused, Err(Error::Unroutable)));
5130 assert!(dynamic.requested_broadcast().now_or_never().is_none());
5131
5132 let _pending = producer.consume().request_broadcast("room/chat");
5133 let request = queued(&dynamic).await;
5134 assert_eq!(request.path().as_str(), "room/chat");
5135 }
5136
5137 #[tokio::test]
5138 async fn dynamic_accepts_a_max_depth_prefix() {
5139 let producer = origin(1).produce();
5140 let path = (0..Path::MAX_PARTS)
5141 .map(|i| format!("s{i}"))
5142 .collect::<Vec<_>>()
5143 .join("/");
5144 let mut announced = producer.consume().announced();
5145
5146 let dynamic = producer.dynamic(&path, Route::default()).expect("max depth is allowed");
5147 announced.assert_next_active(&path);
5148
5149 let _pending = producer.consume().request_broadcast(&path);
5150 let request = queued(&dynamic).await;
5151 assert_eq!(request.path().as_str(), path);
5152 }
5153
5154 #[tokio::test]
5155 async fn dynamic_exclusion_skips_routes_through_the_subscriber() {
5156 let producer = origin(1).produce();
5157 let _server = producer
5158 .dynamic("live", Route::default().with_hops(hops(&[7])))
5159 .unwrap();
5160
5161 let mut excluded = producer.consume().excluding(origin(7)).announced();
5162 excluded.assert_next_wait();
5163
5164 let mut clean = producer.consume().excluding(origin(8)).announced();
5165 clean.assert_next_active("live");
5166 }
5167
5168 #[tokio::test]
5170 async fn announce_consumer_is_a_stream() {
5171 use futures::StreamExt;
5172 let producer = origin(1).produce();
5173 let server = producer.dynamic("live", Route::default()).unwrap();
5174 let mut announced = producer.consume().announced();
5175 let update = StreamExt::next(&mut announced)
5176 .now_or_never()
5177 .expect("next")
5178 .expect("no next");
5179 assert_eq!(update.prefix.as_str(), "live");
5180 assert_eq!(update.kind, AnnounceKind::Announced);
5181 assert!(StreamExt::next(&mut announced).now_or_never().is_none());
5182 drop(server);
5183 let update = StreamExt::next(&mut announced)
5184 .now_or_never()
5185 .expect("next")
5186 .expect("no next");
5187 assert_eq!(update.kind, AnnounceKind::Retracted);
5188 }
5189
5190 #[tokio::test]
5191 async fn dynamic_retracts() {
5192 let producer = origin(1).produce();
5193 let server = producer.dynamic("live", Route::default()).unwrap();
5194 let mut announced = producer.consume().announced();
5195 announced.assert_next_active("live");
5196
5197 drop(server);
5198 announced.assert_next_ended("live");
5199 }
5200
5201 #[test]
5202 fn charged_wildcard_cost_accumulates_across_hops() {
5203 let first = Cost::new(4).charged(1);
5204 let second = first.charged(2);
5205 assert_eq!(second, Cost { warm: 7, cold: 7 });
5206 }
5207
5208 #[tokio::test]
5209 async fn local_broadcast_is_invisible_until_announced() {
5210 let producer = origin(1).produce();
5211 let mut local = producer.consume().announced();
5212 let mut peer = producer.consume().excluding(Hop::UNKNOWN).announced();
5213 let broadcast = producer.create_broadcast("room/alice").unwrap();
5214 local.assert_next_wait();
5215 peer.assert_next_wait();
5216
5217 broadcast.announce(Route::default()).unwrap();
5218 local.assert_next_active("room/alice");
5219 peer.assert_next_active("room/alice");
5220
5221 drop(broadcast);
5222 local.assert_next_ended("room/alice");
5223 peer.assert_next_ended("room/alice");
5224 }
5225
5226 #[tokio::test]
5227 async fn served_route_materializes_on_demand() {
5228 let producer = origin(1).produce();
5229 let consumer = producer.consume();
5230
5231 let server = producer.dynamic("room", Route::default()).unwrap();
5232
5233 let pending = consumer.request_broadcast("room/alice");
5234 let request = queued(&server).await;
5235 assert_eq!(request.path().as_str(), "room/alice");
5236
5237 let source = broadcast::Info::new().produce();
5238 request.accept(&source);
5239
5240 let resolved = pending.await.expect("resolves");
5241 assert_eq!(resolved.info().path.as_str(), "room/alice");
5243
5244 let again = consumer.request_broadcast("room/alice").await.expect("resolves");
5246 assert!(again.is_clone(&resolved));
5247 }
5248
5249 #[tokio::test]
5250 async fn served_requests_coalesce() {
5251 let producer = origin(1).produce();
5252 let consumer = producer.consume();
5253 let server = producer.dynamic("room", Route::default()).unwrap();
5254
5255 let first = consumer.request_broadcast("room/alice");
5256 let second = consumer.request_broadcast("room/alice");
5257
5258 let request = queued(&server).await;
5259 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5261
5262 let source = broadcast::Info::new().produce();
5263 request.accept(&source);
5264
5265 let first = first.await.expect("resolves");
5266 let second = second.await.expect("resolves");
5267 assert!(first.is_clone(&second));
5268 }
5269
5270 #[tokio::test]
5271 async fn retract_rejects_pending_requests() {
5272 let producer = origin(1).produce();
5273 let consumer = producer.consume();
5274 let server = producer.dynamic("room", Route::default()).unwrap();
5275
5276 let pending = consumer.request_broadcast("room/alice");
5277 drop(server);
5278
5279 let err = pending.await.err().unwrap();
5280 assert!(matches!(err, Error::Unroutable));
5281
5282 let err = consumer
5284 .request_broadcast("room/alice")
5285 .now_or_never()
5286 .expect("unroutable")
5287 .err()
5288 .unwrap();
5289 assert!(matches!(err, Error::Unroutable));
5290 }
5291
5292 #[tokio::test]
5293 async fn routed_broadcast_survives_serving_route_retraction() {
5294 let producer = origin(1).produce();
5295 let consumer = producer.consume();
5296
5297 let standby_server = producer.dynamic("room", Route::default()).unwrap();
5300 let second_server = producer.dynamic("room", Route::default()).unwrap();
5301 let incumbent_server = producer.dynamic("room", Route::default()).unwrap();
5302
5303 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5304 assert!((&mut resolving).now_or_never().is_none());
5305
5306 drop(incumbent_server);
5312 assert!((&mut resolving).now_or_never().is_none());
5313 drop(second_server);
5314 assert!((&mut resolving).now_or_never().is_none());
5315
5316 let request = queued(&standby_server).await;
5317 let source = broadcast::Info::new().produce();
5318 request.accept(&source);
5319
5320 let resolved = resolving.await.expect("resolves via the standby");
5321 assert_eq!(resolved.info().path.as_str(), "room/alice");
5322 }
5323
5324 #[tokio::test]
5325 async fn split_horizon_skips_routes_through_the_requester() {
5326 let producer = origin(1).produce();
5327 let _server = producer
5328 .dynamic("room", Route::default().with_hops(hops(&[7])))
5329 .unwrap();
5330
5331 let excluded = producer.consume().excluding(origin(7));
5333 let err = excluded
5334 .request_broadcast("room/alice")
5335 .now_or_never()
5336 .expect("unroutable")
5337 .err()
5338 .unwrap();
5339 assert!(matches!(err, Error::Unroutable));
5340
5341 let clean = producer.consume().excluding(origin(8));
5343 let pending = clean.request_broadcast("room/alice");
5344 assert!(pending.now_or_never().is_none());
5345 }
5346
5347 #[tokio::test]
5348 async fn routes_report_where_they_entered() {
5349 let producer = origin(1).produce();
5350 let peer = producer.clone().peer();
5351 let mut announced = producer.consume().announced();
5352
5353 let _ingest = producer
5354 .dynamic("client", Route::default().with_hops(hops(&[5])).with_via(origin(5)))
5355 .unwrap();
5356 let _gateway = producer.publish("gateway", Route::default()).unwrap();
5357 let _forwarded = peer
5358 .dynamic(
5359 "forwarded",
5360 Route::default().with_hops(hops(&[5, 7])).with_via(origin(7)),
5361 )
5362 .unwrap();
5363
5364 assert_eq!(announced.assert_next_active("client").source(), Source::Local);
5365 assert_eq!(
5366 announced.assert_next_active("forwarded").source(),
5367 Source::Peer(origin(7))
5368 );
5369 assert_eq!(announced.assert_next_active("gateway").source(), Source::Local);
5370
5371 let scoped = peer.scope("room", &Patterns::from(Pattern::all())).unwrap();
5373 let _nested = scoped.dynamic("x", Route::default().with_via(origin(8))).unwrap();
5374 assert_eq!(announced.assert_next_active("room/x").source(), Source::Peer(origin(8)));
5375 }
5376
5377 #[tokio::test]
5380 async fn source_change_is_an_update() {
5381 let producer = origin(1).produce();
5382 let peer = producer.clone().peer();
5383 let mut announced = producer.consume().announced();
5384
5385 let route = Route::default().with_hops(hops(&[7])).with_via(origin(7));
5386 let _forwarded = peer.dynamic("room", route.clone()).unwrap();
5387 assert_eq!(announced.assert_next_active("room").source(), Source::Peer(origin(7)));
5388
5389 let local = producer.dynamic("room", route).unwrap();
5391 let update = announced.next().now_or_never().expect("next blocked").expect("no next");
5392 assert_eq!(update.kind, AnnounceKind::Updated);
5393 assert_eq!(update.route.source(), Source::Local);
5394
5395 drop(local);
5396 assert_eq!(announced.assert_next_active("room").source(), Source::Peer(origin(7)));
5397 }
5398
5399 #[tokio::test]
5400 async fn local_view_hides_peer_routes() {
5401 let producer = origin(1).produce();
5402 let peer = producer.clone().peer();
5403 let mut local = producer.consume().local().announced();
5404
5405 let _forwarded = peer
5406 .dynamic("remote", Route::default().with_hops(hops(&[7])).with_via(origin(7)))
5407 .unwrap();
5408 local.assert_next_wait();
5409
5410 let _shadow = peer
5414 .dynamic("both", Route::default().with_hops(hops(&[7])).with_via(origin(7)))
5415 .unwrap();
5416 let ingest = producer
5417 .dynamic(
5418 "both",
5419 Route::default().with_hops(hops(&[5])).with_via(origin(5)).with_cost(9),
5420 )
5421 .unwrap();
5422 assert_eq!(local.assert_next_active("both").source(), Source::Local);
5423 drop(ingest);
5424 local.assert_next_ended("both");
5425
5426 let err = producer
5429 .consume()
5430 .local()
5431 .request_broadcast("remote/alice")
5432 .now_or_never()
5433 .expect("unroutable")
5434 .err()
5435 .unwrap();
5436 assert!(matches!(err, Error::Unroutable));
5437 assert!(
5438 producer
5439 .consume()
5440 .request_broadcast("remote/alice")
5441 .now_or_never()
5442 .is_none()
5443 );
5444 }
5445
5446 #[tokio::test]
5450 async fn handler_rejection_is_final() {
5451 let producer = origin(1).produce();
5452 let consumer = producer.consume();
5453 let server = producer.dynamic("room", Route::default()).unwrap();
5454
5455 let pending = consumer.request_broadcast("room/alice");
5456 let request = queued(&server).await;
5457 request.reject(Error::Unroutable);
5458 let err = tokio::time::timeout(Duration::from_secs(5), pending)
5459 .await
5460 .expect("the front must give up, not spin")
5461 .err()
5462 .unwrap();
5463 assert!(matches!(err, Error::Unroutable));
5464
5465 let pending = consumer.request_broadcast("room/bob");
5467 let request = queued(&server).await;
5468 assert_eq!(request.path().as_str(), "room/bob");
5469 let served = broadcast::Info::new().produce();
5470 request.accept(&served);
5471 pending.await.expect("resolves");
5472 }
5473
5474 #[tokio::test]
5477 async fn routed_broadcast_waits_out_a_rejection() {
5478 let producer = origin(1).produce();
5479 let consumer = producer.consume();
5480 let server = producer.dynamic("room", Route::default()).unwrap();
5481
5482 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5483 assert!((&mut resolving).now_or_never().is_none());
5484 let request = queued(&server).await;
5485 request.reject(Error::Unroutable);
5486
5487 for _ in 0..20 {
5489 tokio::task::yield_now().await;
5490 }
5491 assert!((&mut resolving).now_or_never().is_none());
5492 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5493
5494 server.update(Route::default().with_cost(2)).unwrap();
5496 assert!((&mut resolving).now_or_never().is_none());
5497 let request = queued(&server).await;
5498 let served = broadcast::Info::new().produce();
5499 request.accept(&served);
5500 resolving.await.expect("resolves");
5501 }
5502
5503 #[tokio::test]
5506 async fn routed_broadcast_reports_teardown_as_closed() {
5507 let (producer, driver) = Producer::new(Config::new(origin(1)));
5508 let consumer = producer.consume();
5509 let _server = producer.dynamic("room", Route::default()).unwrap();
5510
5511 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5513 assert!((&mut resolving).now_or_never().is_none());
5514
5515 drop(driver);
5516
5517 let err = tokio::time::timeout(Duration::from_secs(5), resolving)
5518 .await
5519 .expect("teardown resolves the wait")
5520 .err()
5521 .unwrap();
5522 assert!(matches!(err, Error::Closed), "unexpected end: {err}");
5523 }
5524
5525 #[tokio::test]
5528 async fn routed_broadcast_wakes_for_a_local_broadcast() {
5529 let producer = origin(1).produce();
5530 let consumer = producer.consume();
5531 let server = producer.dynamic("room", Route::default()).unwrap();
5532
5533 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5534 assert!((&mut resolving).now_or_never().is_none());
5535 queued(&server).await.reject(Error::Unroutable);
5536 for _ in 0..20 {
5537 tokio::task::yield_now().await;
5538 }
5539 assert!((&mut resolving).now_or_never().is_none());
5540
5541 let _local = producer.publish("room/alice", Route::default()).unwrap();
5543 let resolved = resolving.await.expect("resolves locally");
5544 assert_eq!(resolved.info().path.as_str(), "room/alice");
5545 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5546 }
5547
5548 #[tokio::test]
5551 async fn late_track_on_a_served_front_replays() {
5552 let producer = origin(1).produce();
5553 let consumer = producer.consume();
5554 let server = producer.dynamic("room", Route::default()).unwrap();
5555
5556 let source = broadcast::Info::new().produce();
5557 for name in ["a", "b"] {
5558 let track = source.create_track(name, None).unwrap();
5559 let mut group = track.append_group().unwrap();
5560 group.write_frame(crate::Timestamp::ZERO, name.as_bytes()).unwrap();
5561 group.finish().unwrap();
5562 std::mem::forget(track);
5564 }
5565
5566 let pending = consumer.request_broadcast("room/alice");
5567 queued(&server).await.accept(&source);
5568 let resolved = pending.await.expect("resolves");
5569
5570 let budget = track::Subscription::default().with_max_age(Duration::from_secs(3600));
5571 for name in ["a", "b"] {
5572 let mut subscription = resolved
5573 .track(name)
5574 .unwrap()
5575 .subscribe(budget.clone())
5576 .await
5577 .expect("subscribe");
5578 let mut group = tokio::time::timeout(Duration::from_secs(5), subscription.recv_group())
5579 .await
5580 .expect("the late track must replay, not park")
5581 .expect("recv group")
5582 .expect("track ended early");
5583 let frame = group.read_frame().await.expect("read frame").expect("frame");
5584 assert_eq!(&frame.payload[..], name.as_bytes());
5585 }
5586 }
5587
5588 #[tokio::test]
5589 async fn most_specific_prefix_shadows() {
5590 let producer = origin(1).produce();
5591 let consumer = producer.consume();
5592
5593 let broad_server = producer.dynamic("", Route::default()).unwrap();
5594 let _narrow = producer.announce(".dash", Route::default()).unwrap();
5597
5598 let err = consumer
5599 .request_broadcast(".dash/pid")
5600 .now_or_never()
5601 .expect("unroutable")
5602 .err()
5603 .unwrap();
5604 assert!(matches!(err, Error::Unroutable));
5605
5606 let _pending = consumer.request_broadcast("room/alice");
5608 let request = queued(&broad_server).await;
5609 assert_eq!(request.path().as_str(), "room/alice");
5610 }
5611
5612 #[tokio::test]
5613 async fn root_dynamic_serves_any_path() {
5614 let producer = origin(1).produce();
5615 let consumer = producer.consume();
5616 let mut announced = consumer.announced();
5617 let dynamic = producer.dynamic("", Route::default()).unwrap();
5618 announced.assert_next_active("");
5620
5621 let pending = consumer.request_broadcast("anything/at/all");
5622 let request = queued(&dynamic).await;
5623 assert_eq!(request.path().as_str(), "anything/at/all");
5624
5625 let source = broadcast::Info::new().produce();
5626 request.accept(&source);
5627 let resolved = pending.await.expect("resolves");
5628 assert_eq!(resolved.info().path.as_str(), "anything/at/all");
5629
5630 drop(dynamic);
5632 announced.assert_next_ended("");
5633 let err = consumer
5634 .request_broadcast("something/else")
5635 .now_or_never()
5636 .expect("unroutable")
5637 .err()
5638 .unwrap();
5639 assert!(matches!(err, Error::Unroutable));
5640 }
5641
5642 #[tokio::test]
5648 async fn out_of_scope_request_never_reaches_the_dynamic_handler() {
5649 let producer = origin(1).produce();
5650 let dynamic = producer.dynamic("", Route::default()).unwrap();
5651 let scoped = producer.consume().scope("", &scopes(&["tenant-a"])).unwrap();
5652
5653 for path in ["tenant-b/live", "tenant-a-other/live"] {
5656 let refused = scoped
5657 .request_broadcast(path)
5658 .now_or_never()
5659 .expect("an out-of-scope request must be refused synchronously, not queued");
5660 assert!(matches!(refused, Err(Error::Unauthorized)));
5661 assert!(
5662 dynamic.requested_broadcast().now_or_never().is_none(),
5663 "the dynamic handler was asked to create a broadcast the requester may not read"
5664 );
5665 }
5666 }
5667
5668 #[tokio::test]
5669 async fn routed_waits_for_coverage() {
5670 let producer = origin(1).produce();
5671 let consumer = producer.consume();
5672
5673 let mut fut = consumer.routed("room/alice").boxed();
5674 assert!((&mut fut).now_or_never().is_none());
5675
5676 let _a = producer.announce("room", Route::default().with_cost(3)).unwrap();
5678 let route = fut.now_or_never().expect("covered").expect("routed");
5679 assert_eq!(route.cost, Cost::new(3));
5680
5681 consumer
5683 .routed("room/alice/cam")
5684 .now_or_never()
5685 .expect("covered")
5686 .expect("routed");
5687 }
5688
5689 #[tokio::test]
5690 async fn routed_ignores_deeper_routes() {
5691 let producer = origin(1).produce();
5692 let consumer = producer.consume();
5693
5694 let _deep = producer.announce("room/alice/cam", Route::default()).unwrap();
5696 let mut fut = consumer.routed("room/alice").boxed();
5697 assert!((&mut fut).now_or_never().is_none());
5698
5699 let _exact = producer.announce("room/alice", Route::default()).unwrap();
5700 fut.now_or_never().expect("covered").expect("routed");
5701 }
5702
5703 #[tokio::test]
5704 async fn routed_accepts_a_max_depth_path() {
5705 let producer = origin(1).produce();
5706 let consumer = producer.consume();
5707 let path = (0..Path::MAX_PARTS)
5708 .map(|i| format!("s{i}"))
5709 .collect::<Vec<_>>()
5710 .join("/");
5711 assert_eq!(Path::new(&path).parts().count(), Path::MAX_PARTS);
5712
5713 assert!(consumer.allowed().matches(&path));
5714
5715 let mut fut = consumer.routed(&path).boxed();
5716 assert!((&mut fut).now_or_never().is_none());
5717
5718 let _a = producer.announce("", Route::default()).unwrap();
5720 fut.now_or_never().expect("covered").expect("routed");
5721 }
5722
5723 #[tokio::test]
5724 async fn teardown_ends_everything() {
5725 let (producer, driver) = Producer::new(Config::new(origin(1)));
5726 let consumer = producer.consume();
5727 let _announcement = producer.announce("room", Route::default()).unwrap();
5728 let mut announced = consumer.announced();
5729 announced.assert_next_active("room");
5730
5731 let _server = producer.dynamic("served", Route::default()).unwrap();
5732 let pending = consumer.request_broadcast("served/path");
5733
5734 drop(driver);
5735
5736 announced.assert_next_active("served");
5738 assert!(announced.next().now_or_never().expect("ended").is_none());
5739
5740 assert!(pending.now_or_never().expect("rejected").is_err());
5742 assert!(matches!(producer.announce("x", Route::default()), Err(Error::Closed)));
5743 assert!(matches!(producer.create_broadcast("x"), Err(Error::Closed)));
5744 let err = consumer
5745 .request_broadcast("y")
5746 .now_or_never()
5747 .expect("closed")
5748 .err()
5749 .unwrap();
5750 assert!(matches!(err, Error::Closed));
5751
5752 let mut late = consumer.announced();
5754 assert!(late.next().now_or_never().expect("ended").is_none());
5755 }
5756
5757 struct ResumeRig {
5760 producer: Producer,
5761 resolved: broadcast::Consumer,
5762 subscription: track::Subscriber,
5763 incumbent_track: track::Producer,
5766 }
5767
5768 impl ResumeRig {
5769 async fn new(first: &[u64]) -> (Self, Dynamic, broadcast::Producer) {
5772 let producer = origin(1).produce();
5773 let consumer = producer.consume();
5774
5775 let server = producer
5776 .dynamic("room", Route::default().with_hops(hops(first)))
5777 .unwrap();
5778
5779 let pending = consumer.request_broadcast("room/alice");
5780 let request = queued(&server).await;
5781 let source = broadcast::Info::new().produce();
5782 let track = source.create_track("video", None).unwrap();
5783 let mut group = track.append_group().unwrap();
5784 group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5785 group.finish().unwrap();
5786 request.accept(&source);
5787
5788 let resolved = pending.await.expect("resolves");
5789 let mut subscription = resolved
5790 .track("video")
5791 .unwrap()
5792 .subscribe(None)
5793 .await
5794 .expect("subscribe");
5795 let mut group = subscription
5796 .recv_group()
5797 .await
5798 .expect("recv group")
5799 .expect("track ended early");
5800 let frame = group.read_frame().await.expect("read frame").expect("frame");
5801 assert_eq!(&frame.payload[..], b"before");
5802
5803 (
5804 Self {
5805 producer,
5806 resolved,
5807 subscription,
5808 incumbent_track: track,
5809 },
5810 server,
5811 source,
5812 )
5813 }
5814
5815 fn standby(&self, first: &[u64]) -> Dynamic {
5818 self.producer
5819 .dynamic("room", Route::default().with_hops(hops(first)))
5820 .unwrap()
5821 }
5822 }
5823
5824 async fn assert_resumes(rig: &mut ResumeRig, server: &Dynamic) {
5829 let request = queued(server).await;
5830 let replacement = broadcast::Info::new().produce();
5831 let track = replacement.create_track("video", None).unwrap();
5832 let mut group = track.append_group().unwrap();
5835 group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5836 group.finish().unwrap();
5837 request.accept(&replacement);
5838
5839 let mut group = track.append_group().unwrap();
5840 group.write_frame(crate::Timestamp::ZERO, b"resumed".as_ref()).unwrap();
5841 group.finish().unwrap();
5842
5843 let mut group = rig
5844 .subscription
5845 .recv_group()
5846 .await
5847 .expect("subscription survives the failover")
5848 .expect("track ended early");
5849 let frame = group.read_frame().await.expect("read frame").expect("frame");
5850 assert_eq!(&frame.payload[..], b"resumed");
5851 }
5852
5853 #[tokio::test]
5856 async fn driver_resolves_with_live_consumers() {
5857 let (producer, driver) = Producer::new(Config::new(origin(1)));
5858 let consumer = producer.consume();
5859 let run = crate::time::run(driver);
5860 drop(producer);
5861 tokio::time::timeout(Duration::from_secs(5), run)
5862 .await
5863 .expect("driver must finish once the producers are gone");
5864 drop(consumer);
5865 }
5866
5867 #[tokio::test]
5868 async fn remote_source_resumes_through_same_first_hop() {
5869 let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5870 let standby_server = rig.standby(&[10, 20]);
5871
5872 drop(incumbent);
5874 drop(source);
5875
5876 assert_resumes(&mut rig, &standby_server).await;
5878 }
5879
5880 #[tokio::test]
5884 async fn incompatible_successor_is_refused() {
5885 for replacement in [
5886 track::Info::default().with_timescale(crate::Timescale::MICRO),
5887 track::Info::default().with_priority(7),
5888 track::Info::default().with_max_age(Duration::from_secs(7)),
5889 ] {
5890 let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5891 let standby_server = rig.standby(&[10, 20]);
5892 drop(incumbent);
5893 drop(source);
5894
5895 let request = queued(&standby_server).await;
5898 let successor = broadcast::Info::new().produce();
5899 let track = successor.create_track("video", replacement).unwrap();
5900 let mut group = track.append_group().unwrap();
5901 group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5902 group.finish().unwrap();
5903 request.accept(&successor);
5904
5905 assert!(
5906 matches!(rig.subscription.recv_group().await, Err(Error::Unsupported)),
5907 "the subscription must abort rather than resume onto incompatible metadata"
5908 );
5909
5910 let reopened = rig.resolved.track("video").unwrap();
5912 assert!(matches!(reopened.query().await, Err(Error::Unsupported)));
5913 assert!(matches!(reopened.subscribe(None).await, Err(Error::Unsupported)));
5914 }
5915 }
5916
5917 #[tokio::test]
5918 async fn different_first_hop_ends_the_subscription() {
5919 let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5920 let rival_server = rig.standby(&[11]);
5922
5923 drop(incumbent);
5926 drop(source);
5927 rig.incumbent_track.abort(Error::Dropped).unwrap();
5928
5929 let err = rig.subscription.recv_group().await.err().expect("subscription ends");
5931 assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5932
5933 let consumer = rig.producer.consume();
5935 let pending = consumer.request_broadcast("room/alice");
5936 let request = queued(&rival_server).await;
5937 let replacement = broadcast::Info::new().produce();
5938 request.accept(&replacement);
5939 pending.await.expect("re-request resolves through the rival");
5940 }
5941
5942 #[tokio::test]
5943 async fn anonymous_routes_never_resume() {
5944 let (mut rig, incumbent, source) = ResumeRig::new(&[]).await;
5947 let _twin_server = rig.standby(&[]);
5948
5949 drop(incumbent);
5950 drop(source);
5951 rig.incumbent_track.abort(Error::Dropped).unwrap();
5952
5953 let err = rig.subscription.recv_group().await.err().expect("subscription ends");
5954 assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5955 }
5956
5957 #[tokio::test]
5964 async fn anonymous_handoff_serves_the_newcomer_immediately() {
5965 let producer = origin(1).produce();
5966
5967 let server_a = producer
5969 .dynamic("room", Route::default().with_hops(hops(&[10])))
5970 .unwrap();
5971
5972 let consumer = producer.consume().excluding(origin(30));
5975 let pending = consumer.request_broadcast("room/alice");
5976 let request = queued(&server_a).await;
5977 let source_a = broadcast::Info::new().produce();
5978 let track_a = source_a.create_track("video", None).unwrap();
5979 let mut group = track_a.append_group().unwrap();
5980 group.write_frame(crate::Timestamp::ZERO, b"from-a".as_ref()).unwrap();
5981 group.finish().unwrap();
5982 request.accept(&source_a);
5983
5984 let resolved_a = pending.await.expect("resolves");
5985 let mut sub_a = resolved_a
5986 .track("video")
5987 .unwrap()
5988 .subscribe(None)
5989 .await
5990 .expect("subscribe");
5991 let mut group = sub_a
5992 .recv_group()
5993 .await
5994 .expect("recv group")
5995 .expect("track ended early");
5996 assert_eq!(
5997 &group.read_frame().await.expect("read frame").expect("frame").payload[..],
5998 b"from-a"
5999 );
6000
6001 drop(track_a);
6004 drop(source_a);
6005 drop(server_a);
6006
6007 let err = sub_a.recv_group().await.err().expect("front closed");
6009 assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
6010
6011 settle(|| consumer.get_broadcast("room/alice").is_none()).await;
6015 settle(|| {
6016 matches!(
6017 consumer.request_broadcast("room/alice").now_or_never(),
6018 Some(Err(Error::Unroutable))
6019 )
6020 })
6021 .await;
6022
6023 let server_b = producer
6025 .dynamic("room", Route::default().with_hops(hops(&[20])))
6026 .unwrap();
6027 let pending = consumer.request_broadcast("room/alice");
6028 let request = queued(&server_b).await;
6029 let source_b = broadcast::Info::new().produce();
6030 let track_b = source_b.create_track("video", None).unwrap();
6031 let mut group = track_b.append_group().unwrap();
6032 group.write_frame(crate::Timestamp::ZERO, b"from-b".as_ref()).unwrap();
6033 group.finish().unwrap();
6034 request.accept(&source_b);
6035
6036 let resolved_b = pending.await.expect("B's front is served immediately");
6037 assert!(
6038 !resolved_b.is_clone(&resolved_a),
6039 "B must not splice into A's closed front"
6040 );
6041
6042 let mut sub_b = resolved_b
6043 .track("video")
6044 .unwrap()
6045 .subscribe(None)
6046 .await
6047 .expect("subscribe");
6048 let mut group = sub_b
6049 .recv_group()
6050 .await
6051 .expect("recv group")
6052 .expect("track ended early");
6053 assert_eq!(
6054 &group.read_frame().await.expect("read frame").expect("frame").payload[..],
6055 b"from-b"
6056 );
6057 }
6058
6059 #[tokio::test]
6060 async fn reprice_is_invisible_to_the_subscription() {
6061 let (rig, incumbent, source) = ResumeRig::new(&[10]).await;
6062
6063 incumbent
6066 .update(Route::default().with_hops(hops(&[10])).with_cost(9))
6067 .unwrap();
6068
6069 let track = source.create_track("audio", None).unwrap();
6070 let mut group = track.append_group().unwrap();
6071 group.write_frame(crate::Timestamp::ZERO, b"steady".as_ref()).unwrap();
6072 group.finish().unwrap();
6073
6074 let mut audio = rig
6075 .resolved
6076 .track("audio")
6077 .unwrap()
6078 .subscribe(None)
6079 .await
6080 .expect("subscribe survives the reprice");
6081 let mut group = audio
6082 .recv_group()
6083 .await
6084 .expect("recv group")
6085 .expect("track ended early");
6086 let frame = group.read_frame().await.expect("read frame").expect("frame");
6087 assert_eq!(&frame.payload[..], b"steady");
6088 }
6089
6090 #[tokio::test]
6091 async fn drain_reprice_migrates_before_the_session_dies() {
6092 let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
6093 let standby_server = rig.standby(&[10, 20]);
6094
6095 incumbent
6099 .update(Route::default().with_hops(hops(&[10])).with_cost(Cost::DRAIN))
6100 .unwrap();
6101
6102 assert_resumes(&mut rig, &standby_server).await;
6103
6104 drop(incumbent);
6106 drop(source);
6107 }
6108
6109 #[tokio::test]
6110 async fn local_sources_splice_newest_first() {
6111 let producer = origin(1).produce();
6112 let consumer = producer.consume();
6113
6114 let first = producer.publish("room/alice", Route::default()).unwrap();
6115 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6116
6117 let second = producer.publish("room/alice", Route::default()).unwrap();
6119 let again = consumer.request_broadcast("room/alice").await.expect("resolves");
6120 assert!(again.is_clone(&resolved));
6121
6122 first.close();
6124 settle(|| consumer.get_broadcast("room/alice").is_some()).await;
6125 second.close();
6126 settle(|| consumer.get_broadcast("room/alice").is_none()).await;
6127
6128 let _third = producer.publish("room/alice", Route::default()).unwrap();
6130 assert!(consumer.get_broadcast("room/alice").is_some());
6131 }
6132
6133 #[tokio::test]
6141 async fn a_finished_broadcast_concludes_in_flight_subscriptions() {
6142 let producer = origin(1).produce();
6143 let consumer = producer.consume();
6144
6145 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
6146 let track = broadcast.create_track("video", None).unwrap();
6147
6148 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6149 let mut subscription = resolved
6150 .track("video")
6151 .unwrap()
6152 .subscribe(None)
6153 .await
6154 .expect("subscribe");
6155 let mut group = track.append_group().unwrap();
6157 group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
6158 group.finish().unwrap();
6159 track.finish().unwrap();
6160 drop(track);
6161 broadcast.close();
6162
6163 let mut group = next_group(&mut subscription)
6164 .await
6165 .expect("a cleanly finished track was served as an error")
6166 .expect("the track ended before its last group");
6167 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
6168 drop(group);
6169
6170 let end = next_group(&mut subscription)
6171 .await
6172 .expect("a cleanly finished track ended as an error");
6173 assert!(end.is_none(), "a group followed the final one");
6174 }
6175
6176 #[tokio::test]
6183 async fn a_retracted_route_concludes_in_flight_subscriptions() {
6184 let producer = origin(1).produce();
6185 let consumer = producer.consume();
6186 let server = producer
6187 .dynamic("room", Route::default().with_hops(hops(&[10])))
6188 .unwrap();
6189
6190 let pending = consumer.request_broadcast("room/alice");
6191 let request = queued(&server).await;
6192 let source = broadcast::Info::new().produce();
6193 let track = source.create_track("video", None).unwrap();
6194 request.accept(&source);
6195
6196 let resolved = pending.await.expect("resolves");
6197 let mut subscription = resolved
6198 .track("video")
6199 .unwrap()
6200 .subscribe(None)
6201 .await
6202 .expect("subscribe");
6203
6204 source.close();
6207 drop(server);
6208 settle(|| resolved.is_closed()).await;
6209 let mut group = track.append_group().unwrap();
6210 group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
6211 group.finish().unwrap();
6212 track.finish().unwrap();
6213 drop(track);
6214
6215 let mut group = next_group(&mut subscription)
6216 .await
6217 .expect("a retracted route's track was served as an error")
6218 .expect("the track ended before its last group");
6219 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
6220 drop(group);
6221
6222 let end = next_group(&mut subscription)
6223 .await
6224 .expect("a cleanly finished track ended as an error");
6225 assert!(end.is_none(), "a group followed the final one");
6226 }
6227
6228 #[tokio::test]
6231 async fn a_closed_source_is_not_requested_again_from_its_standing_route() {
6232 let producer = origin(1).produce();
6233 let server = producer
6234 .dynamic("room", Route::default().with_hops(hops(&[10])))
6235 .unwrap();
6236 let pending = producer.consume().request_broadcast("room/alice");
6237 let source = broadcast::Info::new().produce();
6238 queued(&server).await.accept(&source);
6239 let resolved = pending.await.unwrap();
6240
6241 source.close();
6242 settle(|| resolved.is_closed()).await;
6243 assert!(
6244 server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending(),
6245 "the closed source was requested again"
6246 );
6247 }
6248
6249 #[tokio::test]
6254 async fn origin_front_drops_the_source_when_unused() {
6255 let producer = origin(1).produce();
6256 let consumer = producer.consume();
6257
6258 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
6259 let track = broadcast.create_track("video", None).unwrap();
6260 let mut group = track.append_group().unwrap();
6261 group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
6262 group.finish().unwrap();
6263
6264 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6265 let mut subscription = resolved
6266 .track("video")
6267 .unwrap()
6268 .subscribe(None)
6269 .await
6270 .expect("subscribe");
6271 let mut group = subscription.recv_group().await.unwrap().unwrap();
6272 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6273 drop(group);
6274 drop(subscription);
6275
6276 tokio::time::timeout(Duration::from_secs(1), track.unused())
6277 .await
6278 .expect("source unused should resolve far below TRACK_IDLE_LINGER")
6279 .expect("source closed");
6280
6281 let mut again = resolved
6284 .track("video")
6285 .unwrap()
6286 .subscribe(track::Subscription::default().with_max_age(Duration::from_secs(3600)))
6287 .await
6288 .expect("resubscribe");
6289 let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
6290 .await
6291 .expect("cached group is still on the front")
6292 .expect("recv group")
6293 .expect("track ended early");
6294 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6295
6296 tokio::time::timeout(Duration::from_secs(1), track.used())
6297 .await
6298 .expect("returning reader re-splices the source")
6299 .expect("source closed");
6300
6301 let mut group = track.append_group().unwrap();
6302 group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
6303 group.finish().unwrap();
6304 let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
6305 .await
6306 .expect("groups past the cached edge come from the re-splice")
6307 .expect("recv group")
6308 .expect("track ended early");
6309 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
6310 }
6311
6312 #[tokio::test]
6322 async fn resumed_reader_skips_warm_groups_behind_the_new_edge() {
6323 let ms = |v: u64| crate::Timestamp::from_millis(v).unwrap();
6324 let (_server, _upstream, mut dynamic, resolved) = served_front().await;
6325 let budget = track::Subscription::default().with_max_age(Duration::from_millis(100));
6326 let write = |source: &track::Producer, sequence: u64, millis: u64| {
6327 let mut group = source.create_group(sequence.into()).unwrap();
6328 group.write_frame(ms(millis), b"x".as_ref()).unwrap();
6329 group.finish().unwrap();
6330 };
6331 let drain = |subscription: &mut track::Subscriber| {
6332 let mut sequences = Vec::new();
6333 while let Poll::Ready(group) = subscription.poll_recv_group(&kio::Waiter::noop()) {
6334 sequences.push(group.unwrap().expect("track ended").sequence);
6335 }
6336 sequences
6337 };
6338
6339 let track = resolved.track("video").unwrap();
6340 let first = budget.clone();
6341 let subscribing = tokio::spawn(async move { track.subscribe(first).await });
6342 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
6343 .await
6344 .expect("the front asked the source")
6345 .expect("request");
6346 let source = request.resolving_start().accept(None);
6347 for (sequence, millis) in [(0, 0), (1, 20), (2, 40), (3, 60)] {
6348 write(&source, sequence, millis);
6349 }
6350 let mut subscription = subscribing.await.unwrap().expect("subscribe");
6351 next_group(&mut subscription).await.unwrap().expect("a cached group");
6352 drain(&mut subscription);
6353 drop(subscription);
6354
6355 tokio::time::timeout(Duration::from_secs(1), source.unused())
6356 .await
6357 .expect("parked")
6358 .expect("source open");
6359 drop(source);
6360
6361 let track = resolved.track("video").unwrap();
6362 let second = budget.clone();
6363 let subscribing = tokio::spawn(async move { track.subscribe(second).await });
6364 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
6365 .await
6366 .expect("the front asked the source again")
6367 .expect("request");
6368 let mut source = request.resolving_start().accept(None);
6369 for (sequence, millis) in [(20, 400), (21, 420), (22, 440)] {
6370 write(&source, sequence, millis);
6371 }
6372 source.start_at(3).unwrap();
6375 let mut subscription = subscribing.await.unwrap().expect("resubscribe");
6376 settle(|| subscription.latest() == Some(22)).await;
6377
6378 assert_eq!(drain(&mut subscription), [3, 20, 21, 22]);
6381 }
6382
6383 #[tokio::test]
6388 async fn chained_front_drops_the_source_when_unused() {
6389 let leaf = origin(1).produce();
6390 let leaf_consumer = leaf.consume();
6391
6392 let broadcast = leaf.publish("room/alice", Route::default()).unwrap();
6393 let track = broadcast.create_track("video", None).unwrap();
6394 let mut group = track.append_group().unwrap();
6395 group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
6396 group.finish().unwrap();
6397
6398 let leaf_front = leaf_consumer.request_broadcast("room/alice").await.expect("resolves");
6401
6402 let mid = origin(2).produce();
6403 let mid_server = mid.dynamic("room", Route::default().with_hops(hops(&[10]))).unwrap();
6404 let mid_pending = mid.consume().request_broadcast("room/alice");
6405 queued(&mid_server).await.accept(&leaf_front);
6406 let mid_resolved = mid_pending.await.expect("mid resolves");
6407
6408 let edge = origin(3).produce();
6409 let edge_server = edge.dynamic("room", Route::default().with_hops(hops(&[20]))).unwrap();
6410 let edge_pending = edge.consume().request_broadcast("room/alice");
6411 queued(&edge_server).await.accept(&mid_resolved);
6412 let edge_resolved = edge_pending.await.expect("edge resolves");
6413
6414 let mut subscription = edge_resolved
6415 .track("video")
6416 .unwrap()
6417 .subscribe(None)
6418 .await
6419 .expect("subscribe");
6420 let mut group = subscription.recv_group().await.unwrap().unwrap();
6421 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6422 drop(group);
6423 drop(subscription);
6424
6425 tokio::time::timeout(Duration::from_secs(5), track.unused())
6426 .await
6427 .expect("chained unused should resolve far below TRACK_IDLE_LINGER")
6428 .expect("source closed");
6429
6430 let cached = edge_resolved.track("video").unwrap().cached_groups();
6431 assert_eq!(
6432 cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
6433 vec![0],
6434 "every front keeps the delivered groups after releasing its source"
6435 );
6436
6437 let mut subscription = edge_resolved
6439 .track("video")
6440 .unwrap()
6441 .subscribe(track::Subscription::default().with_max_age(Duration::from_secs(3600)))
6442 .await
6443 .expect("resubscribe");
6444 tokio::time::timeout(Duration::from_secs(5), track.used())
6445 .await
6446 .expect("resubscribe should reach the leaf")
6447 .expect("source open");
6448 let mut group = track.append_group().unwrap();
6449 group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
6450 group.finish().unwrap();
6451 let mut group = subscription.recv_group().await.unwrap().unwrap();
6452 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6453 drop(group);
6454 let mut group = subscription.recv_group().await.unwrap().unwrap();
6455 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
6456 drop(group);
6457 drop(subscription);
6458
6459 tokio::time::timeout(Duration::from_secs(5), track.unused())
6460 .await
6461 .expect("second chained unused should resolve far below TRACK_IDLE_LINGER")
6462 .expect("source closed");
6463
6464 let cached = edge_resolved.track("video").unwrap().cached_groups();
6465 assert_eq!(
6466 cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
6467 vec![0, 1],
6468 "repeated demand keeps every complete group while releasing its source"
6469 );
6470
6471 let fetch = edge_resolved.track("video").unwrap().fetch_group(2, None);
6472 let mut fetch = std::pin::pin!(fetch);
6473 assert!(futures::poll!(fetch.as_mut()).is_pending(), "fetch should re-splice");
6474 tokio::time::timeout(Duration::from_secs(5), track.used())
6475 .await
6476 .expect("fetch should reach the leaf")
6477 .expect("source open");
6478 let mut group = track.append_group().unwrap();
6479 group.write_frame(crate::Timestamp::ZERO, b"fetched".as_ref()).unwrap();
6480 group.finish().unwrap();
6481 let mut group = tokio::time::timeout(Duration::from_secs(5), fetch)
6482 .await
6483 .expect("re-spliced source should answer the fetch")
6484 .expect("fetch succeeds");
6485 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"fetched");
6486 }
6487
6488 #[tokio::test]
6492 async fn incompatible_local_source_keeps_the_incumbent() {
6493 let producer = origin(1).produce();
6494 let consumer = producer.consume();
6495
6496 let first = producer.publish("room/alice", Route::default()).unwrap();
6497 let track = first.create_track("video", None).unwrap();
6498 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6499 let mut subscription = resolved
6500 .track("video")
6501 .unwrap()
6502 .subscribe(None)
6503 .await
6504 .expect("subscribe");
6505 let mut group = track.append_group().unwrap();
6506 group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
6507 group.finish().unwrap();
6508 let mut group = subscription.recv_group().await.unwrap().unwrap();
6509 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"before");
6510
6511 let second = producer.publish("room/alice", Route::default()).unwrap();
6513 let _incompatible = second
6514 .create_track("video", track::Info::default().with_timescale(crate::Timescale::MICRO))
6515 .unwrap();
6516 for _ in 0..10 {
6517 tokio::task::yield_now().await;
6518 }
6519
6520 let mut group = track.append_group().unwrap();
6522 group.write_frame(crate::Timestamp::ZERO, b"still".as_ref()).unwrap();
6523 group.finish().unwrap();
6524 let mut group = subscription.recv_group().await.unwrap().unwrap();
6525 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"still");
6526
6527 drop(track);
6529 first.close();
6530 assert!(matches!(subscription.recv_group().await, Err(Error::Unsupported)));
6531 }
6532
6533 #[tokio::test]
6534 async fn multiple_scopes_present_one_broad_prefix() {
6535 let producer = origin(1).produce();
6536 let _a = producer.announce("", Route::default()).unwrap();
6537
6538 let consumer = producer.consume().scope("", &scopes(&["alpha", "beta"])).unwrap();
6539 let mut announced = consumer.announced();
6540 announced.assert_next_active("");
6541 announced.assert_next_wait();
6542 }
6543
6544 #[test]
6545 fn scope_accepts_every_pattern_union() {
6546 let producer = origin(1).produce();
6547
6548 let root = producer.scope("", &Patterns::from(Pattern::all())).unwrap();
6550 assert_eq!(root.allowed(), Patterns::from(Pattern::all()));
6551
6552 let scoped = producer.scope("", &scopes(&["room"])).unwrap();
6554 assert_eq!(scoped.allowed(), scopes(&["room"]));
6555
6556 let multi = producer.scope("", &scopes(&["room", "room/chat", "anon"])).unwrap();
6558 assert_eq!(multi.allowed(), scopes(&["room", "anon"]));
6559
6560 let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
6562 assert_eq!(consumer.allowed(), scopes(&["room"]));
6563
6564 for text in ["room", "", "*room", "room/*", "*", "**/room", "room/**/chat", "*.hang"] {
6565 let union = Patterns::from(text.parse::<Pattern>().unwrap());
6566 assert_eq!(producer.scope("", &union).expect(text).allowed(), union, "{text}");
6567 assert_eq!(
6568 producer.consume().scope("", &union).expect(text).allowed(),
6569 union,
6570 "{text}"
6571 );
6572 }
6573
6574 let mixed: Patterns = ["room/**".parse().unwrap(), "other".parse().unwrap()]
6575 .into_iter()
6576 .collect();
6577 assert_eq!(producer.scope("", &mixed).unwrap().allowed(), mixed);
6578 }
6579
6580 #[test]
6581 fn route_table_prunes_to_empty() {
6582 let producer = origin(1).produce();
6583 let consumer = producer.consume();
6584
6585 let cursor = consumer
6588 .scope("", &scopes(&["room/a", "other/deep/head"]))
6589 .unwrap()
6590 .announced();
6591 let route = producer.announce("room/a/b/c", Route::default()).unwrap();
6592 {
6593 let table = producer.shared.lock();
6594 assert!(table.routes.root.find(Path::new("room/a/b/c").parts()).is_some());
6595 assert!(table.routes.root.find(Path::new("other/deep/head").parts()).is_some());
6596 assert_eq!(table.routes.root.cursors_below, 2);
6597 }
6598
6599 drop(route);
6600 drop(cursor);
6601 let table = producer.shared.lock();
6602 assert!(table.routes.root.is_empty());
6603 assert_eq!(table.routes.root.cursors_below, 0);
6604 }
6605
6606 #[test]
6610 fn a_published_broadcast_keeps_the_driver_running() {
6611 let (producer, mut driver) = Producer::new(Config::new(origin(1)));
6612 let waiter = kio::Waiter::noop();
6613 let broadcast = producer.create_broadcast("room/a").unwrap();
6614 drop(producer);
6615 assert!(
6616 driver.poll(Instant::now(), &waiter).is_ok(),
6617 "the broadcast is lifecycle work"
6618 );
6619 drop(broadcast);
6620 assert!(matches!(driver.poll(Instant::now(), &waiter), Err(Error::Closed)));
6621 }
6622
6623 #[test]
6624 fn watch_wakes_only_for_covering_changes() {
6625 let producer = origin(1).produce();
6626 let waiter = kio::Waiter::noop();
6627 let watch = producer.shared.lock().watch(&producer.shared, &Path::new("room/a"));
6628 let seen = watch.seen();
6629
6630 let _other = producer.announce("other", Route::default()).unwrap();
6632 let _below = producer.announce("room/a/b", Route::default()).unwrap();
6633 assert!(watch.poll_changed(&waiter, seen).is_pending());
6634
6635 let above = producer.announce("room", Route::default()).unwrap();
6637 assert!(watch.poll_changed(&waiter, seen).is_ready());
6638 let seen = watch.seen();
6639 drop(above);
6640 assert!(watch.poll_changed(&waiter, seen).is_ready());
6641 let seen = watch.seen();
6642
6643 let _beside = producer.create_broadcast("room/b").unwrap();
6645 assert!(watch.poll_changed(&waiter, seen).is_pending());
6646 let _here = producer.create_broadcast("room/a").unwrap();
6647 assert!(watch.poll_changed(&waiter, seen).is_ready());
6648
6649 drop(watch);
6651 let table = producer.shared.lock();
6652 let node = table
6653 .routes
6654 .root
6655 .find(Path::new("room/a").parts())
6656 .expect("route below keeps the node");
6657 assert!(node.watches.is_empty());
6658 assert_eq!(table.routes.root.watches_below, 0);
6659 }
6660
6661 #[test]
6662 fn a_discarded_front_task_unregisters_its_watch() {
6663 let (producer, _driver) = Producer::new(Config {
6664 hop: origin(1),
6665 ..Default::default()
6666 });
6667 let consumer = producer.consume();
6668 let _served = producer.dynamic("room", Route::default()).unwrap();
6669 drop(producer);
6674 let _pending = consumer.request_broadcast("room/a");
6675 }
6676
6677 #[test]
6678 fn create_broadcast_refuses_a_path_no_pattern_can_spell() {
6679 let producer = origin(1).produce();
6680
6681 assert!(matches!(
6685 producer.create_broadcast("room/*"),
6686 Err(Error::InvalidPath(_))
6687 ));
6688 assert!(matches!(
6689 producer.announce("room/**", Route::default()),
6690 Err(Error::InvalidPath(_))
6691 ));
6692 }
6693
6694 #[test]
6695 fn scope_empty_union_grants_nothing() {
6696 let producer = origin(1).produce();
6697
6698 assert!(matches!(producer.scope("", &Patterns::new()), Err(Error::Unauthorized)));
6700 assert!(matches!(
6701 producer.consume().scope("", &Patterns::new()),
6702 Err(Error::Unauthorized)
6703 ));
6704 }
6705
6706 #[test]
6707 fn scope_nests_and_rebases_roots() {
6708 let producer = origin(1).produce();
6709
6710 let scoped = producer.scope("", &scopes(&["room"])).unwrap();
6712 let nested = scoped.scope("", &scopes(&["room/chat"])).unwrap();
6713 assert_eq!(nested.allowed(), scopes(&["room/chat"]));
6714
6715 assert!(matches!(
6717 scoped.scope("", &scopes(&["other"])),
6718 Err(Error::Unauthorized)
6719 ));
6720
6721 let rooted = nested.scope("room/chat", &Patterns::from(Pattern::all())).unwrap();
6723 assert_eq!(rooted.allowed(), scopes(&[""]));
6724
6725 let broadcast = nested.create_broadcast("room/chat/live").unwrap();
6727 assert!(producer.consume().get_broadcast("room/chat/live").is_some());
6728 broadcast.close();
6729 }
6730
6731 #[test]
6732 fn scope_intersects_and_rebases_arbitrary_grants() {
6733 let producer = origin(1).produce();
6734 let rooms = producer
6735 .scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
6736 .unwrap();
6737 let chats = rooms
6738 .scope("", &Patterns::from("*/chat".parse::<Pattern>().unwrap()))
6739 .unwrap();
6740 assert_eq!(chats.allowed(), Patterns::from("room/chat".parse::<Pattern>().unwrap()));
6741
6742 let exact = producer
6743 .scope("", &Patterns::from("room/alice".parse::<Pattern>().unwrap()))
6744 .unwrap();
6745 let rooted = exact.scope("room", &Patterns::from(Pattern::all())).unwrap();
6746 assert_eq!(rooted.allowed(), Patterns::from("alice".parse::<Pattern>().unwrap()));
6747 assert!(matches!(
6748 exact.scope("room/bob", &Patterns::from(Pattern::all())),
6749 Err(Error::Unauthorized)
6750 ));
6751
6752 let broadcast = exact.create_broadcast("room/alice").unwrap();
6753 assert!(matches!(
6754 exact.create_broadcast("room/alice/cam"),
6755 Err(Error::Unauthorized)
6756 ));
6757 assert!(producer.consume().get_broadcast("room/alice").is_some());
6758 drop(broadcast);
6759 }
6760
6761 #[tokio::test]
6762 async fn wildcard_scope_filters_announcements_and_reports_captures() {
6763 let producer = origin(1).produce();
6764 let consumer = producer
6765 .consume()
6766 .scope("", &Patterns::from("room/*/chat".parse::<Pattern>().unwrap()))
6767 .unwrap();
6768 let mut announced = consumer.announced();
6769
6770 let alice = producer.create_broadcast("room/alice/chat").unwrap();
6771 alice.announce(Route::default()).unwrap();
6772 let update = announced.try_next().expect("alice's chat");
6773 assert_eq!(update.prefix.as_str(), "room/alice/chat");
6774 assert_eq!(update.captures, Some(vec!["alice".parse::<Pattern>().unwrap()]));
6775
6776 let audio = producer.create_broadcast("room/alice/audio").unwrap();
6777 audio.announce(Route::default()).unwrap();
6778 announced.assert_next_wait();
6779
6780 let broad = producer.announce("room", Route::default()).unwrap();
6781 let update = announced.try_next().expect("overlapping broad route");
6782 assert_eq!(update.prefix.as_str(), "room");
6783 assert_eq!(update.captures, None, "an overlap does not pin the wildcard");
6784
6785 drop(broad);
6786 drop(audio);
6787 drop(alice);
6788 }
6789
6790 #[tokio::test]
6791 async fn local_broadcast_wins_announcement_ties() {
6792 let producer = origin(1).produce();
6793 let remote = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
6794 let local = producer.create_broadcast("room/alice").unwrap();
6795 local.announce(Route::default()).unwrap();
6796
6797 let mut announced = producer.consume().announced();
6798 let update = announced.try_next().expect("one winning route");
6799 assert_eq!(update.prefix.as_str(), "room/alice");
6800 assert_eq!(update.route.cost, Cost::default());
6801 announced.assert_next_wait();
6802
6803 drop(local);
6804 drop(remote);
6805 }
6806
6807 #[test]
6812 fn cost_charge_saturates() {
6813 assert_eq!(Cost { warm: 4, cold: 6 }.charged(5), Cost { warm: 9, cold: 11 });
6814 assert_eq!(Cost::new(u64::MAX).charged(10), Cost::new(MAX_COST));
6815
6816 assert_eq!(Cost::UNKNOWN.charged(3).cold, MAX_COST);
6819 }
6820
6821 fn expiring_origin(expiry: Duration) -> Producer {
6823 let pool = cache::Pool::new(cache::Config::default().with_expiry(expiry));
6824 Config {
6825 pool,
6826 ..Config::default()
6827 }
6828 .produce()
6829 }
6830
6831 #[tokio::test(start_paused = true)]
6835 async fn stalled_publisher_open_group_is_reclaimed() {
6836 let expiry = Duration::from_secs(1);
6837 let origin = expiring_origin(expiry);
6838 let broadcast = origin.create_broadcast("test").unwrap();
6839 let track = broadcast.create_track("video", None).unwrap();
6840
6841 let mut stalled = track.append_group().unwrap();
6842 stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
6843 let _successor = track.append_group().unwrap();
6847
6848 let mut reading = stalled.consume();
6849 assert!(reading.read_frame().await.unwrap().is_some());
6850
6851 crate::model::clock::advance(expiry * 2);
6853
6854 let reclaimed = tokio::time::timeout(Duration::from_secs(60), reading.read_frame()).await;
6857 assert!(
6858 matches!(reclaimed, Ok(Err(Error::Old))),
6859 "the sweep must reclaim an idle open group and surface the gap, got {reclaimed:?}"
6860 );
6861 }
6862
6863 #[tokio::test(start_paused = true)]
6866 async fn sweep_respects_a_disabled_expiry() {
6867 let origin = Config {
6868 pool: cache::Pool::unbounded(),
6869 ..Config::default()
6870 }
6871 .produce();
6872 let broadcast = origin.create_broadcast("test").unwrap();
6873 let track = broadcast.create_track("video", None).unwrap();
6874
6875 let mut stalled = track.append_group().unwrap();
6876 stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
6877 let _successor = track.append_group().unwrap();
6878
6879 let mut reading = stalled.consume();
6880 assert!(reading.read_frame().await.unwrap().is_some());
6881
6882 crate::model::clock::advance(Duration::from_secs(3600));
6883 tokio::time::advance(Duration::from_secs(3600)).await;
6884
6885 assert!(
6886 reading.read_frame().now_or_never().is_none(),
6887 "a pool without an expiry window never reclaims"
6888 );
6889 }
6890
6891 #[test]
6894 fn drain_cost_is_encodable() {
6895 use crate::coding::Encode;
6896
6897 let mut buf = Vec::new();
6898 Cost::DRAIN
6899 .encode(&mut buf, crate::lite::Version::Lite06)
6900 .expect("a draining route is still forwarded, so its cost must encode");
6901 }
6902}