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> {
1253 let path = path.as_path();
1254
1255 let full = self.root.join(&path).to_owned();
1256 if !self.scope.permits(&full) {
1257 return Err(Error::Unauthorized);
1258 }
1259 if full.parts().count() > Path::MAX_PARTS {
1263 return Err(BoundsExceeded.into());
1264 }
1265 let claim = prefix_claim(&full)?;
1268
1269 let ingress = self.stats.ingress(&full);
1271
1272 let announcing = Announcing {
1277 hop: self.hop,
1278 shared: self.shared.clone(),
1279 requested: full.clone(),
1280 prefixes: vec![(full.clone(), claim)],
1281 scope: self.scope.allowed.clone(),
1282 local: true,
1283 peer: self.peer,
1284 stats: self.stats.clone(),
1285 };
1286 let info = broadcast::Info {
1287 pool: self.pool.clone(),
1288 cache_duration: self.cache_duration,
1289 path: full,
1290 };
1291 let source = info.produce().with_stats(ingress.clone());
1292 let entry = announcing.announce(
1293 Route::default(),
1294 Serving {
1295 server: None,
1296 source: Some(source.consume()),
1297 advertised: false,
1298 },
1299 )?;
1300 Ok(source.with_announcer(Announcer {
1301 entry,
1302 ingress,
1303 _keepalive: self.tasks.keepalive(),
1304 }))
1305 }
1306
1307 pub fn publish(&self, path: impl AsPath, route: Route) -> Result<broadcast::Producer, Error> {
1309 let broadcast = self.create_broadcast(path)?;
1310 broadcast.announce(route)?;
1311 Ok(broadcast)
1312 }
1313
1314 pub(crate) fn create_source(&self, path: impl AsPath) -> broadcast::Producer {
1320 let path = path.as_path();
1321 let full = self.root.join(&path).to_owned();
1322 let ingress = self.stats.ingress(&full);
1323 broadcast::Info {
1324 pool: self.pool.clone(),
1325 cache_duration: self.cache_duration,
1326 path: full,
1327 }
1328 .produce()
1329 .with_stats(ingress)
1330 }
1331
1332 #[cfg(test)]
1341 pub(crate) fn announce(&self, prefix: impl AsPath, route: Route) -> Result<AnnounceProducer, Error> {
1342 Announcing::new(self, prefix)?.announce(
1343 route,
1344 Serving {
1345 server: None,
1346 source: None,
1347 advertised: true,
1348 },
1349 )
1350 }
1351
1352 pub fn dynamic(&self, prefix: impl AsPath, route: Route) -> Result<Dynamic, Error> {
1374 let announcing = Announcing::new(self, prefix)?;
1375 let serve = kio::Shared::<ServeState>::default();
1376 serve.lock().requests.add_handler();
1377 let announcement = announcing.announce(
1378 route,
1379 Serving {
1380 server: Some(serve.clone()),
1381 source: None,
1382 advertised: true,
1383 },
1384 )?;
1385 Ok(Dynamic {
1386 announcement,
1387 state: serve,
1388 })
1389 }
1390
1391 pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Producer, Error> {
1398 let root = self.root.join(root).to_owned();
1399 let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
1400 let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
1401 Ok(Producer {
1402 hop: self.hop,
1403 scope,
1404 root,
1405 shared: self.shared.clone(),
1406 pool: self.pool.clone(),
1407 cache_duration: self.cache_duration,
1408 default_max_age: self.default_max_age,
1409 stats: self.stats.clone(),
1410 peer: self.peer,
1411 tasks: self.tasks.clone(),
1412 timers: self.timers.clone(),
1413 })
1414 }
1415
1416 pub fn consume(&self) -> Consumer {
1421 Consumer::from_producer(self, stats::Session::default())
1424 }
1425
1426 pub fn root(&self) -> &Path<'_> {
1428 &self.root
1429 }
1430
1431 pub fn allowed(&self) -> Patterns {
1433 self.scope.relative(&self.root)
1434 }
1435
1436 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
1438 self.root.join(path)
1439 }
1440}
1441
1442struct Announcing {
1446 hop: Hop,
1447 shared: kio::Shared<OriginState>,
1448 requested: PathOwned,
1450 prefixes: Vec<(PathOwned, Pattern)>,
1454 scope: Patterns,
1456 local: bool,
1457 peer: bool,
1459 stats: stats::Session,
1460}
1461
1462impl Announcing {
1463 fn new(producer: &Producer, prefix: impl AsPath) -> Result<Self, Error> {
1465 let requested = producer.root.join(prefix.as_path()).to_owned();
1466 if requested.parts().count() > Path::MAX_PARTS {
1467 return Err(BoundsExceeded.into());
1468 }
1469 let claim = prefix_claim(&requested)?;
1470 if !producer.scope.allowed.overlaps(&claim) {
1471 return Err(Error::Unauthorized);
1472 }
1473 Ok(Self {
1474 hop: producer.hop,
1475 shared: producer.shared.clone(),
1476 requested: requested.clone(),
1477 prefixes: vec![(requested, claim)],
1478 scope: producer.scope.allowed.clone(),
1479 local: false,
1480 peer: producer.peer,
1481 stats: producer.stats.clone(),
1482 })
1483 }
1484
1485 fn announce(&self, route: Route, serving: Serving) -> Result<AnnounceProducer, Error> {
1486 debug_assert!(
1487 !route.hops.contains(&self.hop),
1488 "announce called with a looping hop chain",
1489 );
1490
1491 let via = route.via;
1492
1493 let mut shared = self.shared.lock();
1494 if shared.closed {
1495 return Err(Error::Closed);
1496 }
1497
1498 let mut entries = Vec::with_capacity(self.prefixes.len());
1499 for (prefix, claim) in &self.prefixes {
1500 let id = shared.next_route;
1501 shared.next_route += 1;
1502 shared.routes.insert(RouteEntry {
1503 id,
1504 prefix: prefix.clone(),
1505 scope: self.scope.clone(),
1506 hops: route.hops.clone(),
1507 cost: route.cost,
1508 via,
1509 local: self.local,
1510 peer: self.peer,
1511 server: serving.server.clone(),
1512 source: serving.source.clone(),
1513 advertised: serving.advertised,
1514 claim: claim.clone(),
1515 });
1516 shared.sync_route(prefix, claim);
1517 entries.push((prefix.clone(), id));
1518 }
1519 drop(shared);
1520
1521 let guard = serving
1523 .advertised
1524 .then(|| self.stats.ingress(&self.requested).announce());
1525
1526 Ok(AnnounceProducer {
1527 shared: self.shared.clone(),
1528 entries,
1529 guard,
1530 })
1531 }
1532}
1533
1534struct Serving {
1536 server: Option<kio::Shared<ServeState>>,
1537 source: Option<broadcast::Consumer>,
1538 advertised: bool,
1539}
1540
1541pub(crate) struct Announcer {
1548 entry: AnnounceProducer,
1549 ingress: stats::Scope,
1551 _keepalive: Keepalive,
1555}
1556
1557impl Announcer {
1558 pub(crate) fn announce(&mut self, route: Route) -> Result<(), Error> {
1560 self.entry.update(route)?;
1561 if self.entry.guard.is_none() {
1562 self.entry.guard = Some(self.ingress.announce());
1563 }
1564 Ok(())
1565 }
1566
1567 pub(crate) fn withdraw(&mut self) {
1569 self.entry.withdraw();
1570 self.entry.guard = None;
1571 }
1572}
1573
1574#[must_use = "dropping an announcement retracts the route"]
1581pub(crate) struct AnnounceProducer {
1582 shared: kio::Shared<OriginState>,
1583 entries: Vec<(PathOwned, u64)>,
1586 guard: Option<stats::Announce>,
1588}
1589
1590impl AnnounceProducer {
1591 pub fn update(&self, route: Route) -> Result<(), Error> {
1599 let mut shared = self.shared.lock();
1600 if shared.closed {
1601 return Err(Error::Closed);
1602 }
1603 for (prefix, id) in &self.entries {
1604 let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1606 return Err(Error::Closed);
1607 };
1608 entry.hops = route.hops.clone();
1609 entry.cost = route.cost;
1610 entry.via = route.via;
1611 entry.advertised = true;
1612 let claim = entry.claim.clone();
1613 shared.sync_route(prefix, &claim);
1614 }
1615 Ok(())
1616 }
1617
1618 fn withdraw(&self) {
1623 let mut shared = self.shared.lock();
1624 for (prefix, id) in &self.entries {
1625 let Some(entry) = shared.routes.entry_mut(prefix, *id) else {
1626 continue;
1627 };
1628 if !entry.advertised {
1629 continue;
1630 }
1631 entry.advertised = false;
1632 let claim = entry.claim.clone();
1633 shared.sync_route(prefix, &claim);
1634 }
1635 }
1636
1637 fn retract(&self) {
1640 let mut shared = self.shared.lock();
1641 for (prefix, id) in &self.entries {
1642 let Some(entry) = shared.routes.remove(prefix, *id) else {
1643 continue;
1644 };
1645 if let Some(server) = &entry.server {
1648 let mut server = server.lock();
1649 server.closed = true;
1650 for producer in server.requests.drain_all() {
1651 if let Ok(mut request) = producer.write() {
1652 request.resolved.get_or_insert(Err(Error::Unroutable));
1653 }
1654 }
1655 }
1656 shared.sync_route(&entry.prefix, &entry.claim);
1657 }
1658 }
1659}
1660
1661impl Drop for AnnounceProducer {
1662 fn drop(&mut self) {
1663 self.retract();
1664 }
1665}
1666
1667#[must_use = "poll the driver or the origin makes no progress"]
1679pub struct Driver {
1680 state: DriverState,
1681 timers: Clock,
1683 pool: cache::Pool,
1686}
1687
1688struct DriverState {
1690 set: TaskSet,
1692 shared: kio::Shared<OriginState>,
1695 done: bool,
1697}
1698
1699impl Driver {
1700 pub fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1706 self.timers.advance(now);
1707 let result = self.state.poll(waiter);
1708 let gc = self.pool.gc(now);
1709 if result.is_ready() {
1710 return Err(Error::Closed);
1711 }
1712 Ok(self.timers.timeout().into_iter().chain(gc).min())
1713 }
1714}
1715
1716impl crate::time::Driver for Driver {
1717 fn poll(&mut self, now: Instant, waiter: &kio::Waiter) -> Result<Option<Instant>, Error> {
1718 self.poll(now, waiter)
1719 }
1720}
1721
1722impl DriverState {
1723 fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
1724 if !self.done {
1728 ready!(self.set.poll(waiter));
1729 self.done = true;
1730 }
1731 Poll::Ready(())
1732 }
1733
1734 fn teardown(&mut self) {
1738 drop(std::mem::replace(&mut self.set, TaskSet::owned()));
1741
1742 let (servers, cursors, fronts) = {
1747 let mut shared = self.shared.lock();
1748 shared.closed = true;
1749 shared.routes.poke_all();
1751 let servers: Vec<_> = shared
1752 .routes
1753 .entries()
1754 .filter_map(|entry| entry.server.clone())
1755 .collect();
1756 let cursors: Vec<_> = shared.cursors.values().map(|cursor| cursor.state.clone()).collect();
1757 let fronts: Vec<_> = shared.fronts.values().map(|front| front.request.clone()).collect();
1758 (servers, cursors, fronts)
1759 };
1760
1761 for producer in fronts {
1764 if let Ok(mut request) = producer.write() {
1765 request.resolved.get_or_insert(Err(Error::Dropped));
1766 }
1767 }
1768 for server in servers {
1772 let mut server = server.lock();
1773 server.closed = true;
1774 for producer in server.requests.drain_all() {
1775 if let Ok(mut request) = producer.write() {
1776 request.resolved.get_or_insert(Err(Error::Dropped));
1777 }
1778 }
1779 }
1780
1781 for state in cursors {
1784 if let Ok(mut state) = state.write() {
1785 state.ended = true;
1786 }
1787 }
1788 }
1789}
1790
1791impl Drop for DriverState {
1792 fn drop(&mut self) {
1793 self.teardown();
1794 }
1795}
1796
1797const TRACK_IDLE_LINGER: Duration = Duration::from_secs(30);
1810
1811struct WarmCopy {
1816 track: track::Producer,
1817 _dynamic: track::Dynamic,
1818 edge: Option<WarmGroup>,
1821}
1822
1823impl Drop for WarmCopy {
1824 fn drop(&mut self) {
1825 let _ = self.track.finish();
1826 }
1827}
1828
1829struct WarmGroup(group::Producer);
1836
1837impl Drop for WarmGroup {
1838 fn drop(&mut self) {
1839 if !self.0.is_finished() {
1840 let _ = self.0.clone().abort(Error::Cancel);
1841 }
1842 }
1843}
1844
1845fn warm_copy(source: &track::Consumer, head: Option<&WarmGroup>) -> Option<WarmCopy> {
1852 let info = source.cached_info()?;
1853 let mut track = track::Producer::new(Arc::new(source.broadcast().clone()), source.name(), info);
1854 let head = head.map(|head| &head.0);
1855 let groups = source.cached_groups();
1856 let latest = groups
1858 .iter()
1859 .filter(|(_, visible)| *visible)
1860 .map(|(group, _)| group.sequence)
1861 .max();
1862 let mut edge = None;
1863
1864 if let Some(head) = head
1868 && !groups.iter().any(|(group, _)| group.sequence == head.sequence)
1869 {
1870 let is_latest = latest.is_none_or(|latest| head.sequence >= latest);
1871 if head.is_finished() {
1872 let _ = track.adopt_group(head.clone(), true);
1873 if is_latest {
1874 edge = Some(WarmGroup(head.clone()));
1875 }
1876 } else if is_latest {
1877 edge = warm_rebuild(&track, head, None);
1878 }
1879 }
1880
1881 for (group, visible) in groups {
1882 let finished = group.is_finished();
1883 let whole = group.live_first_frame() == Some(0);
1884 let is_latest = visible && Some(group.sequence) == latest;
1885 let warm = if finished && whole {
1886 let _ = track.adopt_group(group.clone(), visible);
1887 Some(WarmGroup(group))
1888 } else if (finished || is_latest) && (whole || head.is_some_and(|head| head.sequence == group.sequence)) {
1889 warm_rebuild(&track, &group, head)
1895 } else {
1896 None
1898 };
1899 if is_latest {
1900 edge = warm;
1901 }
1902 }
1903 let dynamic = track.dynamic();
1904 Some(WarmCopy {
1905 track,
1906 _dynamic: dynamic,
1907 edge,
1908 })
1909}
1910
1911fn warm_rebuild(track: &track::Producer, live: &group::Producer, head: Option<&group::Producer>) -> Option<WarmGroup> {
1914 let mut start = live.live_first_frame()? as u64;
1916 let mut tail = live.consume();
1917 tail.start_at(start);
1918 let mut frames = Vec::new();
1919 if start > 0
1920 && let Some(head) = head.filter(|head| head.sequence == live.sequence)
1921 && let Some(head_start) = head.live_first_frame()
1922 {
1923 let mut head = head.consume();
1924 head.start_at(head_start as u64);
1925 while head.index() < start {
1926 match head.poll_read_frame(&kio::Waiter::noop()) {
1927 Poll::Ready(Ok(Some(frame))) => frames.push(frame),
1928 _ => break,
1929 }
1930 }
1931 match head.index() == start {
1932 true => start = head_start as u64,
1933 false => frames.clear(),
1935 }
1936 }
1937 while let Poll::Ready(Ok(Some(frame))) = tail.poll_read_frame(&kio::Waiter::noop()) {
1938 frames.push(frame);
1939 }
1940 if frames.is_empty() {
1941 return None;
1942 }
1943
1944 let rebuilt = WarmGroup(
1946 track
1947 .create_group(group::Info {
1948 sequence: live.sequence,
1949 })
1950 .ok()?,
1951 );
1952 let mut writer = rebuilt.0.clone();
1953 if start > 0 {
1954 writer.start_at(start).ok()?;
1955 }
1956 for frame in frames {
1957 writer.write_frame(frame.timestamp, frame.payload).ok()?;
1958 }
1959 if live.is_finished() {
1960 writer.finish().ok()?;
1961 }
1962 Some(rebuilt)
1963}
1964
1965struct FrontTask {
1967 shared: kio::Shared<OriginState>,
1969 broadcast: broadcast::Producer,
1971 path: PathOwned,
1973 horizon: Horizon,
1975 watch: Watch,
1977 request: kio::Producer<PendingBroadcast>,
1979 pin: kio::Lock<Pin>,
1981 timers: Clock,
1982}
1983
1984struct TrackIo {
1987 resume: super::resume::Producer,
1988 staged: Option<(u64, track::Consumer)>,
1990 query: Option<(u64, track::Consumer, track::Querying)>,
1992 copy: Option<(u64, track::Consumer)>,
1994 edge: Option<track::Position>,
1999 warm: Option<WarmCopy>,
2002 head: Option<WarmGroup>,
2005 used: bool,
2007}
2008
2009async fn run_front(task: FrontTask) {
2013 let FrontTask {
2014 shared,
2015 broadcast,
2016 path,
2017 horizon,
2018 watch,
2019 request,
2020 pin,
2021 timers,
2022 } = task;
2023
2024 enum Step {
2026 Assigned(Arc<str>, super::resume::Producer),
2027 Resolved(u64, Result<broadcast::Consumer, Error>),
2028 SourceClosed(u64),
2029 Info(Arc<str>, u64, Result<track::Info, Error>),
2030 Ended(Arc<str>, u64, Result<(), Error>),
2031 Demand(Arc<str>),
2032 Deadline,
2033 Table,
2034 }
2035
2036 let mut front = Front::new(TRACK_IDLE_LINGER);
2037 let mut sources: HashMap<u64, broadcast::Consumer> = HashMap::new();
2038 let mut next_source = 0u64;
2039 let mut upstream: Option<(u64, kio::Consumer<PendingBroadcast>)> = None;
2041 let mut tracks: HashMap<Arc<str>, TrackIo> = HashMap::new();
2042 let mut deadline = crate::runtime::Deadline::new(&timers);
2043 let mut seen = 0;
2045 let mut events: VecDeque<Event> = VecDeque::new();
2046
2047 let select = |front: &mut Front, sources: &HashMap<u64, broadcast::Consumer>, seen: &mut u64| -> Event {
2050 let table = shared.read();
2051 if table.closed {
2052 return Event::Closed;
2053 }
2054 *seen = watch.seen();
2056 front.retain_routes(|route| table.routes.covers(&path.as_path(), route));
2057 let best = table
2058 .best_route(&path.as_path(), horizon, front.pin(), front.refused_routes())
2059 .map(|entry| Candidate {
2060 route: entry.id,
2061 first: entry.hops.iter().next().copied(),
2062 local: entry.local,
2063 });
2064 let serving_closing = front
2065 .serving()
2066 .and_then(|id| sources.get(&id))
2067 .is_some_and(|source| source.is_closing());
2068 Event::Selected { best, serving_closing }
2069 };
2070
2071 events.push_back(select(&mut front, &sources, &mut seen));
2072
2073 loop {
2074 while let Some(event) = events.pop_front() {
2075 for action in front.step(event) {
2076 match action {
2077 Action::Reselect => events.push_back(select(&mut front, &sources, &mut seen)),
2078 Action::Request { route } => {
2079 let found = {
2081 let table = shared.read();
2082 table
2083 .routes
2084 .covering(&path.as_path())
2085 .find(|entry| entry.id == route)
2086 .map(|entry| {
2087 (
2088 Candidate {
2089 route,
2090 first: entry.hops.iter().next().copied(),
2091 local: entry.local,
2092 },
2093 entry.source.clone(),
2094 entry.server.clone(),
2095 )
2096 })
2097 };
2098 let Some((candidate, source, server)) = found else {
2099 events.push_back(Event::Resolved {
2100 route,
2101 result: Err(Refusal {
2102 err: Error::Unroutable,
2103 standing: false,
2104 }),
2105 });
2106 continue;
2107 };
2108 front.identify(candidate);
2109 *pin.lock() = front.pin();
2110 if let Some(source) = source {
2111 let id = next_source;
2112 next_source += 1;
2113 sources.insert(id, source);
2114 events.push_back(Event::Resolved { route, result: Ok(id) });
2115 continue;
2116 }
2117 let Some(server) = server else {
2118 events.push_back(Event::Resolved {
2119 route,
2120 result: Err(Refusal {
2121 err: Error::Unroutable,
2122 standing: true,
2123 }),
2124 });
2125 continue;
2126 };
2127 let mut serve = server.lock();
2128 if serve.closed {
2129 drop(serve);
2132 events.push_back(Event::Resolved {
2133 route,
2134 result: Err(Refusal {
2135 err: Error::Unroutable,
2136 standing: true,
2137 }),
2138 });
2139 continue;
2140 }
2141 if let Some(weak) = serve.served.get(&path) {
2144 drop(serve);
2145 let id = next_source;
2146 next_source += 1;
2147 sources.insert(id, weak.consume());
2148 events.push_back(Event::Resolved { route, result: Ok(id) });
2149 continue;
2150 }
2151 let pending = match serve.requests.join(&path) {
2152 Some(producer) => producer.consume(),
2153 None => {
2154 let producer = kio::Producer::<PendingBroadcast>::default();
2155 let consumer = producer.consume();
2156 match serve.requests.insert(path.clone(), producer) {
2157 Ok(()) => consumer,
2158 Err(_) => {
2161 drop(serve);
2162 events.push_back(Event::Resolved {
2163 route,
2164 result: Err(Refusal {
2165 err: Error::Unroutable,
2166 standing: true,
2167 }),
2168 });
2169 continue;
2170 }
2171 }
2172 }
2173 };
2174 upstream = Some((route, pending));
2175 }
2176 Action::Detach { source } => {
2177 sources.remove(&source);
2178 for io in tracks.values_mut() {
2181 if io.copy.as_ref().is_some_and(|(s, _)| *s == source) {
2182 io.copy = None;
2183 }
2184 if io.query.as_ref().is_some_and(|(s, ..)| *s == source) {
2185 io.query = None;
2186 }
2187 if io.staged.as_ref().is_some_and(|(s, _)| *s == source) {
2188 io.staged = None;
2189 }
2190 }
2191 }
2192 Action::Resolve => {
2193 if let Ok(mut pending) = request.write() {
2194 pending.resolved.get_or_insert(Ok(broadcast.consume()));
2195 }
2196 }
2197 Action::Query { track: name, source } => {
2198 let Some(io) = tracks.get_mut(&name) else { continue };
2199 let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2200 match sources.get(&source).map(|s| s.track(&name)) {
2201 Some(Ok(copy)) => {
2202 let query = copy.query().into_inner();
2205 io.query = Some((source, copy, query));
2206 }
2207 Some(Err(err)) => events.push_back(Event::TrackInfo {
2208 track: name,
2209 source,
2210 closing,
2211 result: Err(err),
2212 }),
2213 None => {}
2214 }
2215 }
2216 Action::Splice { track: name, source } => {
2217 let Some(io) = tracks.get_mut(&name) else { continue };
2218 let Some((staged, copy)) = io.staged.take() else {
2219 continue;
2220 };
2221 if staged != source {
2222 continue;
2223 }
2224 if let Err(err) = io.resume.takeover(©) {
2225 let _ = io.resume.abort(err);
2229 tracks.remove(&name);
2230 continue;
2231 }
2232 io.head = io.warm.take().and_then(|mut warm| warm.edge.take());
2233 io.edge = io.resume.resume_position();
2236 io.copy = Some((source, copy));
2237 }
2238 Action::Park { track: name } => {
2239 let Some(io) = tracks.get_mut(&name) else { continue };
2240 let Some((_, copy)) = io.copy.take() else { continue };
2241 let warm = warm_copy(©, io.head.as_ref());
2245 drop(copy);
2246 io.head = None;
2247 let parked = match &warm {
2248 Some(warm) => io.resume.park(&warm.track),
2249 None => io.resume.release(),
2250 };
2251 if parked.is_err() {
2252 tracks.remove(&name);
2253 continue;
2254 }
2255 io.warm = warm;
2256 }
2257 Action::Release { track: name } => {
2258 let Some(io) = tracks.get_mut(&name) else { continue };
2259 io.copy = None;
2261 io.warm = None;
2262 io.head = None;
2263 if io.resume.release().is_err() {
2264 tracks.remove(&name);
2265 }
2266 }
2267 Action::Finish { track: name } => {
2268 if let Some(mut io) = tracks.remove(&name) {
2269 let _ = io.resume.finish();
2270 }
2271 }
2272 Action::Abort { track: name, err } => {
2273 if let Some(mut io) = tracks.remove(&name) {
2274 tracing::debug!(name = %name, %err, "aborting track");
2275 let _ = io.resume.abort(err);
2276 }
2277 }
2278 Action::Arm { at } => deadline.set(at),
2279 Action::End { err } => {
2280 if let Ok(mut pending) = request.write() {
2281 pending.resolved.get_or_insert(Err(err.clone()));
2282 }
2283 broadcast.finish();
2290 broadcast.release_spliced(err.clone());
2291 for (_, mut io) in tracks.drain() {
2292 let waiting = io.staged.take().map(|(_, copy)| copy);
2296 let waiting = waiting.or_else(|| io.query.take().map(|(_, copy, _)| copy));
2297 if let Some(copy) = waiting
2298 && io.resume.is_used()
2299 {
2300 if io.resume.takeover(©).is_err() {
2301 continue;
2302 }
2303 io.warm = None;
2304 }
2305 if !io.resume.is_used() || !io.resume.is_spliced() || io.warm.is_some() {
2307 let _ = io.resume.abort(err.clone());
2308 }
2309 }
2310 return;
2311 }
2312 }
2313 }
2314 }
2315
2316 let step = kio::wait(|waiter| {
2317 if let Poll::Ready((name, resume)) = broadcast.poll_spliced_assigned(waiter) {
2318 return Poll::Ready(Step::Assigned(name, resume));
2319 }
2320 if let Some((route, pending)) = &upstream
2321 && let Poll::Ready(result) = pending.poll(waiter, |p| match &p.resolved {
2322 Some(result) => Poll::Ready(result.clone()),
2323 None => Poll::Pending,
2324 }) {
2325 return Poll::Ready(Step::Resolved(
2326 *route,
2327 match result {
2328 Ok(resolved) => resolved,
2329 Err(_closed) => Err(Error::Unroutable),
2332 },
2333 ));
2334 }
2335 if let Some(id) = front.serving()
2336 && let Some(source) = sources.get(&id)
2337 && source.poll_closed(waiter).is_ready()
2338 {
2339 return Poll::Ready(Step::SourceClosed(id));
2340 }
2341 for (name, io) in &tracks {
2342 if let Some((source, _, query)) = &io.query
2343 && let Poll::Ready(result) = query.poll(waiter)
2344 {
2345 return Poll::Ready(Step::Info(name.clone(), *source, result));
2346 }
2347 if let Some((source, copy)) = &io.copy
2348 && let Poll::Ready(result) = copy.poll_complete(waiter)
2349 {
2350 return Poll::Ready(Step::Ended(name.clone(), *source, result));
2351 }
2352 let edge = match io.used {
2354 true => io.resume.poll_unused(waiter),
2355 false => io.resume.poll_used(waiter),
2356 };
2357 if edge.is_ready() {
2358 return Poll::Ready(Step::Demand(name.clone()));
2359 }
2360 }
2361 if deadline.poll(waiter).is_ready() {
2362 return Poll::Ready(Step::Deadline);
2363 }
2364 watch.poll_changed(waiter, seen).map(|()| Step::Table)
2365 })
2366 .await;
2367
2368 let event = match step {
2369 Step::Assigned(name, resume) => {
2370 tracks.insert(
2371 name.clone(),
2372 TrackIo {
2373 resume,
2374 staged: None,
2375 query: None,
2376 copy: None,
2377 edge: None,
2378 warm: None,
2379 head: None,
2380 used: false,
2381 },
2382 );
2383 Event::TrackAssigned { track: name }
2384 }
2385 Step::Resolved(route, result) => {
2386 upstream = None;
2387 match result {
2388 Ok(source) => {
2389 let id = next_source;
2390 next_source += 1;
2391 sources.insert(id, source);
2392 Event::Resolved { route, result: Ok(id) }
2393 }
2394 Err(err) => {
2395 let standing =
2399 !matches!(err, Error::Unroutable) || shared.read().routes.covers(&path.as_path(), route);
2400 Event::Resolved {
2401 route,
2402 result: Err(Refusal { err, standing }),
2403 }
2404 }
2405 }
2406 }
2407 Step::SourceClosed(source) => Event::SourceClosed { source },
2408 Step::Info(name, source, result) => {
2409 let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2410 let Some(io) = tracks.get_mut(&name) else { continue };
2411 let Some((_, copy, _)) = io.query.take() else { continue };
2412 let result = match result {
2415 Ok(info) => match copy.poll_complete(&kio::Waiter::noop()) {
2416 Poll::Ready(Err(err)) => Err(err),
2417 _ => Ok(info),
2418 },
2419 Err(err) => Err(err),
2420 };
2421 if result.is_ok() && io.used {
2424 io.staged = Some((source, copy));
2425 }
2426 Event::TrackInfo {
2427 track: name,
2428 source,
2429 closing,
2430 result,
2431 }
2432 }
2433 Step::Ended(name, source, result) => {
2434 let closing = sources.get(&source).is_some_and(|s| s.is_closing());
2435 let Some(io) = tracks.get_mut(&name) else { continue };
2436 io.copy = None;
2437 let delivered = io.resume.resume_position() != io.edge;
2438 Event::TrackEnded {
2439 track: name,
2440 source,
2441 closing,
2442 result,
2443 delivered,
2444 }
2445 }
2446 Step::Demand(name) => {
2447 let Some(io) = tracks.get_mut(&name) else { continue };
2448 io.used = io.resume.is_used();
2449 if !io.used {
2450 io.query = None;
2453 io.staged = None;
2454 }
2455 match io.used {
2456 true => Event::Used { track: name },
2457 false => Event::Unused {
2458 track: name,
2459 now: timers.now(),
2460 },
2461 }
2462 }
2463 Step::Deadline => {
2464 deadline.set(None);
2467 Event::Deadline { now: timers.now() }
2468 }
2469 Step::Table => select(&mut front, &sources, &mut seen),
2470 };
2471 events.push_back(event);
2472 }
2473}
2474
2475#[derive(Default)]
2480struct RouteTable {
2481 root: RouteNode,
2482}
2483
2484#[derive(Default)]
2487struct RouteNode {
2488 entries: Vec<RouteEntry>,
2490 cursors: Vec<ConsumerId>,
2492 cursors_below: usize,
2495 watches: Vec<(u64, kio::Producer<Watched>)>,
2498 watches_below: usize,
2501 children: HashMap<String, RouteNode>,
2502}
2503
2504#[derive(Default)]
2507struct Watched {
2508 generation: u64,
2509}
2510
2511struct Watch {
2517 shared: kio::Shared<OriginState>,
2518 path: PathOwned,
2519 id: u64,
2520 signal: kio::Consumer<Watched>,
2521}
2522
2523impl Watch {
2524 fn seen(&self) -> u64 {
2528 self.signal.read().generation
2529 }
2530
2531 fn poll_changed(&self, waiter: &kio::Waiter, seen: u64) -> Poll<()> {
2533 self.signal
2534 .poll(waiter, |watched| match watched.generation != seen {
2535 true => Poll::Ready(()),
2536 false => Poll::Pending,
2537 })
2538 .map(|_| ())
2539 }
2540}
2541
2542impl Drop for Watch {
2543 fn drop(&mut self) {
2544 self.shared.lock().routes.remove_watch(&self.path, self.id);
2545 }
2546}
2547
2548#[derive(Clone, Copy)]
2550struct Below {
2551 cursors: usize,
2552 watches: usize,
2553}
2554
2555impl Below {
2556 const NONE: Self = Self { cursors: 0, watches: 0 };
2557 const CURSOR: Self = Self { cursors: 1, watches: 0 };
2558 const WATCH: Self = Self { cursors: 0, watches: 1 };
2559}
2560
2561impl RouteNode {
2562 fn is_empty(&self) -> bool {
2564 self.entries.is_empty() && self.cursors.is_empty() && self.watches.is_empty() && self.children.is_empty()
2565 }
2566
2567 fn find<'a>(&self, mut parts: impl Iterator<Item = &'a str>) -> Option<&Self> {
2569 match parts.next() {
2570 None => Some(self),
2571 Some(part) => self.children.get(part)?.find(parts),
2572 }
2573 }
2574
2575 fn reach<'a>(&mut self, mut parts: impl Iterator<Item = &'a str>, below: Below) -> &mut Self {
2578 self.cursors_below += below.cursors;
2579 self.watches_below += below.watches;
2580 match parts.next() {
2581 None => self,
2582 Some(part) => self.children.entry(part.to_string()).or_default().reach(parts, below),
2583 }
2584 }
2585
2586 fn edit<'a, R>(
2590 &mut self,
2591 mut parts: impl Iterator<Item = &'a str>,
2592 below: Below,
2593 f: impl FnOnce(&mut Self) -> R,
2594 ) -> Option<R> {
2595 let result = match parts.next() {
2596 None => f(self),
2597 Some(part) => {
2598 let child = self.children.get_mut(part)?;
2599 let result = child.edit(parts, below, f)?;
2600 if child.is_empty() {
2601 self.children.remove(part);
2602 }
2603 result
2604 }
2605 };
2606 self.cursors_below -= below.cursors;
2607 self.watches_below -= below.watches;
2608 Some(result)
2609 }
2610
2611 fn poke(&self) {
2613 for (_, watch) in &self.watches {
2614 if let Ok(mut watched) = watch.write() {
2615 watched.generation += 1;
2616 }
2617 }
2618 }
2619
2620 fn poke_below(&self) {
2623 if self.watches_below == 0 {
2624 return;
2625 }
2626 self.poke();
2627 for child in self.children.values() {
2628 child.poke_below();
2629 }
2630 }
2631
2632 fn walk<'a>(&'a self, visit: &mut impl FnMut(&'a Self)) {
2634 visit(self);
2635 for child in self.children.values() {
2636 child.walk(visit);
2637 }
2638 }
2639
2640 fn collect_cursors(&self, out: &mut Vec<ConsumerId>) {
2642 if self.cursors_below == 0 {
2643 return;
2644 }
2645 out.extend(&self.cursors);
2646 for child in self.children.values() {
2647 child.collect_cursors(out);
2648 }
2649 }
2650}
2651
2652impl RouteTable {
2653 fn split(&self, path: &Path) -> (Vec<&RouteNode>, Option<&RouteNode>) {
2656 let mut above = Vec::new();
2657 let mut node = &self.root;
2658 for part in path.parts() {
2659 above.push(node);
2660 match node.children.get(part) {
2661 Some(child) => node = child,
2662 None => return (above, None),
2663 }
2664 }
2665 (above, Some(node))
2666 }
2667
2668 fn covering(&self, path: &Path) -> impl Iterator<Item = &RouteEntry> {
2670 let (above, at) = self.split(path);
2671 above.into_iter().chain(at).flat_map(|node| node.entries.iter())
2672 }
2673
2674 fn covers(&self, path: &Path, id: u64) -> bool {
2676 self.covering(path).any(|entry| entry.id == id)
2677 }
2678
2679 fn at(&self, prefix: &Path) -> impl Iterator<Item = &RouteEntry> {
2681 self.root
2682 .find(prefix.parts())
2683 .into_iter()
2684 .flat_map(|node| node.entries.iter())
2685 }
2686
2687 fn entries(&self) -> impl Iterator<Item = &RouteEntry> {
2689 let mut nodes = Vec::new();
2690 self.root.walk(&mut |node| nodes.push(node));
2691 nodes.into_iter().flat_map(|node| node.entries.iter())
2692 }
2693
2694 fn insert(&mut self, entry: RouteEntry) {
2696 let node = self.root.reach(entry.prefix.parts(), Below::NONE);
2697 node.entries.push(entry);
2698 }
2699
2700 fn entry_mut(&mut self, prefix: &Path, id: u64) -> Option<&mut RouteEntry> {
2702 let mut node = &mut self.root;
2703 for part in prefix.parts() {
2704 node = node.children.get_mut(part)?;
2705 }
2706 node.entries.iter_mut().find(|entry| entry.id == id)
2707 }
2708
2709 fn remove(&mut self, prefix: &Path, id: u64) -> Option<RouteEntry> {
2711 self.root
2712 .edit(prefix.parts(), Below::NONE, |node| {
2713 let index = node.entries.iter().position(|entry| entry.id == id)?;
2714 Some(node.entries.swap_remove(index))
2715 })
2716 .flatten()
2717 }
2718
2719 fn add_cursor(&mut self, head: &Path, id: ConsumerId) {
2721 self.root.reach(head.parts(), Below::CURSOR).cursors.push(id);
2722 }
2723
2724 fn remove_cursor(&mut self, head: &Path, id: ConsumerId) {
2727 self.root.edit(head.parts(), Below::CURSOR, |node| {
2728 node.cursors.retain(|cursor| *cursor != id)
2729 });
2730 }
2731
2732 fn add_watch(&mut self, path: &Path, id: u64) -> kio::Consumer<Watched> {
2734 let producer = kio::Producer::<Watched>::default();
2735 let consumer = producer.consume();
2736 self.root.reach(path.parts(), Below::WATCH).watches.push((id, producer));
2737 consumer
2738 }
2739
2740 fn remove_watch(&mut self, path: &Path, id: u64) {
2743 self.root.edit(path.parts(), Below::WATCH, |node| {
2744 node.watches.retain(|(watch, _)| *watch != id)
2745 });
2746 }
2747
2748 fn poke_below(&self, prefix: &Path) {
2750 if let (_, Some(node)) = self.split(prefix) {
2751 node.poke_below();
2752 }
2753 }
2754
2755 fn poke_all(&self) {
2757 self.root.walk(&mut |node| node.poke());
2758 }
2759
2760 fn cursors_touching(&self, prefix: &Path) -> Vec<ConsumerId> {
2764 let (above, at) = self.split(prefix);
2765 let mut cursors: Vec<ConsumerId> = above.iter().flat_map(|node| node.cursors.iter().copied()).collect();
2766 if let Some(node) = at {
2767 node.collect_cursors(&mut cursors);
2768 }
2769 cursors.sort_unstable();
2771 cursors.dedup();
2772 cursors
2773 }
2774}
2775
2776#[derive(Default)]
2783struct OriginState {
2784 routes: RouteTable,
2787 next_route: u64,
2788 next_watch: u64,
2789
2790 cursors: HashMap<ConsumerId, TableCursor>,
2794
2795 fronts: WeakCache<FrontKey, RemoteFront>,
2803
2804 closed: bool,
2807}
2808
2809impl OriginState {
2810 fn sync_route(&mut self, prefix: &Path, claim: &Pattern) {
2815 let routes = &self.routes;
2817 for id in routes.cursors_touching(prefix) {
2818 let Some(cursor) = self.cursors.get_mut(&id) else {
2819 continue;
2820 };
2821 if let Some(presented) = cursor.presented(prefix, claim) {
2822 Self::sync_cursor(routes, cursor, &presented);
2823 }
2824 }
2825 routes.poke_below(prefix);
2827 }
2828
2829 fn watch(&mut self, shared: &kio::Shared<OriginState>, path: &Path) -> Watch {
2831 let id = self.next_watch;
2832 self.next_watch += 1;
2833 let signal = self.routes.add_watch(path, id);
2834 Watch {
2835 shared: shared.clone(),
2836 path: path.to_owned(),
2837 id,
2838 signal,
2839 }
2840 }
2841
2842 fn sync_cursor(routes: &RouteTable, cursor: &mut TableCursor, presented: &PathOwned) {
2845 let candidates: Vec<&RouteEntry> = match presented.is_empty() {
2851 true => routes
2852 .covering(&cursor.root)
2853 .filter(|entry| cursor.visible(entry))
2854 .collect(),
2855 false => {
2856 let absolute = cursor.root.join(presented);
2857 routes.at(&absolute).filter(|entry| cursor.visible(entry)).collect()
2858 }
2859 };
2860 let most = candidates.iter().map(|entry| entry.prefix.len()).max();
2861 let best = most.and_then(|most| {
2862 candidates
2863 .into_iter()
2864 .filter(|entry| entry.prefix.len() == most)
2865 .min_by_key(|entry| route_order(&entry.prefix, entry))
2866 });
2867
2868 match best {
2869 Some(entry) => {
2870 let meta = (entry.hops.clone(), entry.cost, entry.entered());
2871 let served = entry.server.is_some();
2872 let captures = cursor.captures(&entry.prefix);
2873 let previous = cursor
2874 .current
2875 .insert(presented.clone(), (entry.id, meta.clone(), served, captures.clone()));
2876 match previous {
2877 Some((_, prev, prev_served, prev_captures))
2884 if prev == meta && prev_served == served && prev_captures == captures => {}
2885 Some((_, prev, _, prev_captures)) if prev_captures != captures => {
2888 if let Ok(mut state) = cursor.state.write() {
2889 state.apply_unannounce(presented.clone(), prev, prev_captures);
2890 state.apply_announce(presented.clone(), meta, captures);
2891 }
2892 }
2893 _ => {
2894 if let Ok(mut state) = cursor.state.write() {
2895 state.apply_announce(presented.clone(), meta, captures);
2896 }
2897 }
2898 }
2899 }
2900 None => {
2901 if let Some((_, last, _, captures)) = cursor.current.remove(presented)
2902 && let Ok(mut state) = cursor.state.write()
2903 {
2904 state.apply_unannounce(presented.clone(), last, captures);
2905 }
2906 }
2907 }
2908 }
2909
2910 fn register_cursor(&mut self, id: ConsumerId, mut cursor: TableCursor) {
2912 let mut presented: BTreeSet<PathOwned> = BTreeSet::new();
2915 for head in &cursor.heads {
2916 let (above, at) = self.routes.split(head);
2917 let mut nodes = above;
2918 if let Some(node) = at {
2919 node.walk(&mut |node| nodes.push(node));
2920 }
2921 for entry in nodes.into_iter().flat_map(|node| node.entries.iter()) {
2922 if let Some(p) = cursor.presented(&entry.prefix, &entry.claim) {
2923 presented.insert(p);
2924 }
2925 }
2926 }
2927 for p in &presented {
2928 Self::sync_cursor(&self.routes, &mut cursor, p);
2929 }
2930 for head in &cursor.heads {
2931 self.routes.add_cursor(head, id);
2932 }
2933 self.cursors.insert(id, cursor);
2934 }
2935
2936 fn best_route(&self, path: &Path, horizon: Horizon, pin: Pin, refused: &HashSet<u64>) -> Option<&RouteEntry> {
2951 let (above, at) = self.routes.split(path);
2955 let mut best = None;
2956 for node in above.into_iter().chain(at) {
2957 let mut candidates = node
2958 .entries
2959 .iter()
2960 .filter(|entry| entry.advertised)
2961 .filter(|entry| entry.scope.matches(path.as_str()))
2962 .filter(|entry| horizon.admits(entry))
2963 .filter(|entry| entry.qualifies(pin))
2964 .filter(|entry| !refused.contains(&entry.id))
2965 .peekable();
2966 if candidates.peek().is_some() {
2967 best = candidates
2968 .filter(|entry| entry.serves(path))
2969 .min_by_key(|entry| route_order(&entry.prefix, entry));
2970 }
2971 }
2972 best
2973 }
2974}
2975
2976#[derive(Default)]
2983struct PendingBroadcast {
2984 resolved: Option<Result<broadcast::Consumer, Error>>,
2985}
2986
2987#[must_use = "dropping an origin::Dynamic retracts the route"]
2999pub struct Dynamic {
3000 announcement: AnnounceProducer,
3002 state: kio::Shared<ServeState>,
3003}
3004
3005impl Dynamic {
3006 pub fn update(&self, route: Route) -> Result<(), Error> {
3014 self.announcement.update(route)
3015 }
3016
3017 pub fn poll_requested_broadcast(&self, waiter: &kio::Waiter) -> Poll<Result<Request, Error>> {
3022 let mut state = ready!(self.state.poll(waiter, |state| {
3023 if state.closed || state.requests.has_queued() {
3024 Poll::Ready(())
3025 } else {
3026 Poll::Pending
3027 }
3028 }));
3029
3030 if state.closed {
3032 return Poll::Ready(Err(Error::Closed));
3033 }
3034
3035 let path = state.requests.pop().expect("predicate guaranteed a request");
3036 let producer = state.requests.get(&path).expect("popped key must be pending").clone();
3042 Poll::Ready(Ok(Request {
3043 path,
3044 producer,
3045 home: self.state.clone(),
3046 }))
3047 }
3048
3049 pub async fn requested_broadcast(&self) -> Result<Request, Error> {
3055 kio::wait(|waiter| self.poll_requested_broadcast(waiter)).await
3056 }
3057}
3058
3059impl ServeState {
3060 fn resolve(
3071 shared: &kio::Shared<Self>,
3072 path: &PathOwned,
3073 producer: &kio::Producer<PendingBroadcast>,
3074 result: Result<broadcast::Consumer, Error>,
3075 ) {
3076 let mut state = shared.lock();
3077 if state.closed {
3078 return;
3079 }
3080 let resolved = match result {
3081 Ok(broadcast) => {
3082 let existing = state.served.insert(path.clone(), broadcast.weak());
3086 Ok(existing.map(|weak| weak.consume()).unwrap_or(broadcast))
3087 }
3088 Err(err) => Err(err),
3089 };
3090 state.requests.remove_if(path, |p| p.same_channel(producer));
3091 if let Ok(mut pending) = producer.write() {
3092 pending.resolved.get_or_insert(resolved);
3093 drop(state);
3094 }
3095 }
3096
3097 fn forget(shared: &kio::Shared<Self>, path: &PathOwned, producer: &kio::Producer<PendingBroadcast>) {
3099 shared.lock().requests.remove_if(path, |p| p.same_channel(producer));
3100 }
3101}
3102
3103pub struct Request {
3110 path: PathOwned,
3112
3113 producer: kio::Producer<PendingBroadcast>,
3116
3117 home: kio::Shared<ServeState>,
3120}
3121
3122impl Request {
3123 pub fn path(&self) -> &Path<'_> {
3125 &self.path
3126 }
3127
3128 pub fn accept(self, broadcast: impl Consume<broadcast::Consumer>) {
3134 let broadcast = broadcast.consume();
3135 ServeState::resolve(&self.home, &self.path, &self.producer, Ok(broadcast));
3136 }
3138
3139 pub fn reject(self, err: Error) {
3141 ServeState::resolve(&self.home, &self.path, &self.producer, Err(err));
3142 }
3143}
3144
3145impl Drop for Request {
3146 fn drop(&mut self) {
3147 ServeState::forget(&self.home, &self.path, &self.producer);
3155 }
3156}
3157
3158pub struct Requesting {
3165 inner: RequestState,
3166 path: PathOwned,
3170 stats: stats::Scope,
3173}
3174
3175enum RequestState {
3176 Failed(Error),
3179 Pending(kio::Consumer<PendingBroadcast>),
3181}
3182
3183impl Requesting {
3184 fn failed(error: Error) -> Self {
3185 Self::new(RequestState::Failed(error))
3186 }
3187
3188 fn queued(consumer: kio::Consumer<PendingBroadcast>) -> Self {
3189 Self::new(RequestState::Pending(consumer))
3190 }
3191
3192 pub fn is_queued(&self) -> bool {
3201 matches!(self.inner, RequestState::Pending(_))
3202 }
3203
3204 fn new(inner: RequestState) -> Self {
3205 Self {
3206 inner,
3207 path: PathOwned::default(),
3208 stats: stats::Scope::default(),
3209 }
3210 }
3211
3212 fn with_path(mut self, path: PathOwned) -> Self {
3213 self.path = path;
3214 self
3215 }
3216
3217 fn with_stats(mut self, scope: stats::Scope) -> Self {
3219 self.stats = scope;
3220 self
3221 }
3222
3223 fn hand_out(&self, broadcast: broadcast::Consumer) -> broadcast::Consumer {
3225 broadcast.with_path(self.path.clone()).with_stats(self.stats.clone())
3226 }
3227
3228 pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<broadcast::Consumer, Error>> {
3230 match &self.inner {
3231 RequestState::Failed(error) => Poll::Ready(Err(error.clone())),
3232 RequestState::Pending(consumer) => Poll::Ready(
3233 match ready!(consumer.poll(waiter, |state| match &state.resolved {
3234 Some(result) => Poll::Ready(result.clone()),
3235 None => Poll::Pending,
3236 })) {
3237 Ok(result) => result.map(|broadcast| self.hand_out(broadcast)),
3238 Err(_closed) => Err(Error::Unroutable),
3240 },
3241 ),
3242 }
3243 }
3244}
3245
3246impl kio::Pollable for Requesting {
3247 type Output = Result<broadcast::Consumer, Error>;
3248
3249 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
3250 self.poll_ok(waiter)
3251 }
3252}
3253
3254pub trait Consume<T> {
3262 fn consume(&self) -> T;
3264}
3265
3266impl<T, U: Consume<T>> Consume<T> for &U {
3267 fn consume(&self) -> T {
3268 (**self).consume()
3269 }
3270}
3271
3272impl Consume<Consumer> for Producer {
3273 fn consume(&self) -> Consumer {
3274 Consumer::from_producer(self, stats::Session::default())
3278 }
3279}
3280
3281impl Consume<Consumer> for Consumer {
3282 fn consume(&self) -> Consumer {
3283 self.clone()
3284 }
3285}
3286
3287impl Consume<broadcast::Consumer> for broadcast::Producer {
3288 fn consume(&self) -> broadcast::Consumer {
3289 self.consume()
3291 }
3292}
3293
3294impl Consume<broadcast::Consumer> for broadcast::Consumer {
3295 fn consume(&self) -> broadcast::Consumer {
3296 self.clone()
3297 }
3298}
3299
3300impl Consume<track::Consumer> for track::Producer {
3301 fn consume(&self) -> track::Consumer {
3302 self.consume()
3303 }
3304}
3305
3306impl Consume<track::Consumer> for track::Consumer {
3307 fn consume(&self) -> track::Consumer {
3308 self.clone()
3309 }
3310}
3311
3312#[derive(Clone)]
3318pub struct Consumer {
3319 hop: Hop,
3321 scope: OriginScope,
3322
3323 root: PathOwned,
3325
3326 shared: kio::Shared<OriginState>,
3329
3330 stats: stats::Session,
3334
3335 horizon: Horizon,
3340
3341 hidden: Hidden,
3343
3344 pool: cache::Pool,
3347 cache_duration: Duration,
3348
3349 tasks: TasksWeak,
3353
3354 timers: Clock,
3357}
3358
3359impl Consumer {
3360 fn from_producer(producer: &Producer, stats: stats::Session) -> Self {
3361 Self {
3362 hop: producer.hop,
3363 scope: producer.scope.clone(),
3364 root: producer.root.clone(),
3365 shared: producer.shared.clone(),
3366 stats,
3367 horizon: Horizon::default(),
3368 hidden: Hidden::default(),
3369 pool: producer.pool.clone(),
3370 cache_duration: producer.cache_duration,
3371 tasks: producer.tasks.downgrade(),
3372 timers: producer.timers.clone(),
3373 }
3374 }
3375
3376 pub fn hop(&self) -> Hop {
3378 self.hop
3379 }
3380
3381 pub(crate) fn excluding(mut self, peer: Hop) -> Self {
3389 self.horizon.exclude = Some(peer);
3390 self
3391 }
3392
3393 pub fn local(mut self) -> Self {
3400 self.horizon.local = true;
3401 self
3402 }
3403
3404 pub fn with_hidden(mut self, hidden: bool) -> Self {
3409 self.hidden.include = hidden;
3410 self
3411 }
3412
3413 pub(crate) fn beyond(mut self, outer: &Consumer) -> Self {
3416 self.hidden.beyond = Some(interest_prefixes(&outer.scope.allowed));
3417 self
3418 }
3419
3420 pub(crate) fn includes_hidden(&self) -> bool {
3422 self.hidden.include
3423 }
3424
3425 pub fn with_stats(mut self, session: stats::Session) -> Self {
3429 self.stats = session;
3430 self
3431 }
3432
3433 fn untagged(&self) -> Self {
3437 Self {
3438 stats: stats::Session::default(),
3439 ..self.clone()
3440 }
3441 }
3442
3443 pub(crate) fn empty(&self) -> Self {
3448 Self {
3449 scope: OriginScope::empty(),
3450 ..self.clone()
3451 }
3452 }
3453
3454 pub fn announced(&self) -> AnnounceConsumer {
3463 AnnounceConsumer::new(
3464 self.root.clone(),
3465 self.scope.allowed.clone(),
3466 self.stats.clone(),
3467 self.horizon,
3468 self.hidden.clone(),
3469 &self.shared,
3470 )
3471 }
3472
3473 pub fn consume(&self) -> Self {
3475 self.clone()
3476 }
3477
3478 #[cfg(test)]
3481 pub(crate) fn get_broadcast(&self, path: impl AsPath) -> Option<broadcast::Consumer> {
3482 let full = self.root.join(path).to_owned();
3483 if !self.scope.permits(&full) {
3484 return None;
3485 }
3486 let table = self.shared.lock();
3487 table
3488 .routes
3489 .at(&full)
3490 .filter(|entry| entry.local)
3491 .min_by_key(|entry| route_order(&entry.prefix, entry))
3492 .and_then(|entry| entry.source.clone())
3493 }
3494
3495 pub async fn routed(&self, path: impl AsPath) -> Option<Route> {
3505 let path = path.as_path();
3506
3507 let consumer = match Pattern::subtree(path.as_str()) {
3512 Ok(subtree) => self.scope("", &Patterns::from(subtree)).ok()?,
3513 Err(InvalidPattern::TooManySegments) => self.clone(),
3514 Err(_) => return None,
3515 };
3516
3517 if !consumer.allowed().matches(path.as_str()) {
3521 return None;
3522 }
3523
3524 let mut announced = consumer.untagged().with_hidden(true).announced();
3528 loop {
3529 let update = announced.next().await?;
3530 if update.kind.is_active() && path.has_prefix(&update.prefix) {
3531 return Some(update.route);
3532 }
3533 }
3534 }
3535
3536 pub async fn routed_broadcast(&self, path: impl AsPath) -> Result<broadcast::Consumer, Error> {
3550 let path = path.as_path();
3551
3552 if !self.allowed().matches(path.as_str()) {
3555 return Err(Error::Unauthorized);
3556 }
3557 loop {
3558 let (watch, seen) = {
3567 let mut table = self.shared.lock();
3568 if table.closed {
3569 return Err(Error::Closed);
3570 }
3571 let watch = table.watch(&self.shared, &self.root.join(&path));
3572 let seen = watch.seen();
3573 (watch, seen)
3574 };
3575 match self.request_broadcast(&path).await {
3576 Ok(broadcast) => return Ok(broadcast),
3577 Err(Error::Unroutable) => {
3578 kio::wait(|waiter| watch.poll_changed(waiter, seen)).await;
3579 }
3580 Err(Error::Dropped) if self.shared.lock().closed => return Err(Error::Closed),
3583 Err(err) => return Err(err),
3584 }
3585 }
3586 }
3587
3588 pub fn scope(&self, root: impl AsPath, patterns: &Patterns) -> Result<Consumer, Error> {
3595 let root = self.root.join(root).to_owned();
3596 let rooted = patterns.rooted(root.as_str()).map_err(|_| BoundsExceeded)?;
3597 let scope = self.scope.narrow(&rooted).ok_or(Error::Unauthorized)?;
3598 Ok(Consumer {
3599 scope,
3600 root,
3601 ..self.clone()
3602 })
3603 }
3604
3605 pub fn request_broadcast(&self, path: impl AsPath) -> kio::Pending<Requesting> {
3628 let path = path.as_path();
3629
3630 let absolute = self.root.join(&path).to_owned();
3634 let scope = self.stats.egress(&absolute);
3635 let requested = path.to_owned();
3639
3640 if !self.scope.permits(&absolute) {
3642 return kio::Pending::new(Requesting::failed(Error::Unauthorized));
3643 }
3644
3645 let mut state = self.shared.lock();
3646
3647 if state.closed {
3649 return kio::Pending::new(Requesting::failed(Error::Closed));
3650 }
3651
3652 if state
3656 .best_route(&absolute.as_path(), self.horizon, Pin::Any, &HashSet::new())
3657 .is_none()
3658 {
3659 return kio::Pending::new(Requesting::failed(Error::Unroutable));
3660 }
3661
3662 let key = (absolute.clone(), self.horizon);
3670 if let Some(front) = state.fronts.get(&key) {
3671 let pin = *front.pin.lock();
3672 let current = state
3673 .best_route(&absolute.as_path(), self.horizon, Pin::Any, &HashSet::new())
3674 .is_some_and(|entry| entry.qualifies(pin));
3675 if current {
3676 let pending = Requesting::queued(front.request.consume())
3677 .with_path(requested)
3678 .with_stats(scope);
3679 return kio::Pending::new(pending);
3680 }
3681 state.fronts.remove(&key);
3682 }
3683
3684 let broadcast = broadcast::Producer::new_spliced(broadcast::Info {
3689 pool: self.pool.clone(),
3690 cache_duration: self.cache_duration,
3691 path: absolute.clone(),
3692 });
3693 let request = kio::Producer::<PendingBroadcast>::default();
3694 let consumer = request.consume();
3695 let watch = state.watch(&self.shared, &absolute);
3696 let pin = kio::Lock::new(Pin::Any);
3697 state.fronts.insert(
3698 key,
3699 RemoteFront {
3700 request: request.clone(),
3701 broadcast: broadcast.consume().weak(),
3702 pin: pin.clone(),
3703 },
3704 );
3705 drop(state);
3708 self.tasks.push(run_front(FrontTask {
3709 shared: self.shared.clone(),
3710 broadcast,
3711 path: absolute,
3712 horizon: self.horizon,
3713 watch,
3714 request,
3715 pin,
3716 timers: self.timers.clone(),
3717 }));
3718 kio::Pending::new(Requesting::queued(consumer).with_path(requested).with_stats(scope))
3719 }
3720
3721 pub fn root(&self) -> &Path<'_> {
3723 &self.root
3724 }
3725
3726 pub fn allowed(&self) -> Patterns {
3728 self.scope.relative(&self.root)
3729 }
3730
3731 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
3733 self.root.join(path)
3734 }
3735}
3736
3737pub struct AnnounceConsumer {
3742 id: ConsumerId,
3743 shared: kio::Shared<OriginState>,
3744 root: PathOwned,
3745
3746 state: kio::Producer<OriginConsumerState>,
3749
3750 stats: stats::Session,
3753
3754 guards: HashMap<PathOwned, stats::Announce>,
3758
3759 park: kio::Park,
3762}
3763
3764impl AnnounceConsumer {
3765 fn new(
3766 root: PathOwned,
3767 allowed: Patterns,
3768 stats: stats::Session,
3769 horizon: Horizon,
3770 hidden: Hidden,
3771 shared: &kio::Shared<OriginState>,
3772 ) -> Self {
3773 let state = kio::Producer::<OriginConsumerState>::default();
3774 let id = ConsumerId::new();
3775
3776 {
3777 let mut table = shared.lock();
3778 if table.closed {
3779 if let Ok(mut state) = state.write() {
3781 state.ended = true;
3782 }
3783 } else {
3784 table.register_cursor(
3785 id,
3786 TableCursor {
3787 root: root.clone(),
3788 heads: interest_prefixes(&allowed),
3789 allowed,
3790 horizon,
3791 hidden,
3792 state: state.clone(),
3793 current: HashMap::new(),
3794 },
3795 );
3796 }
3797 }
3798
3799 Self {
3800 id,
3801 shared: shared.clone(),
3802 root,
3803 state,
3804 stats,
3805 guards: HashMap::new(),
3806 park: kio::Park::default(),
3807 }
3808 }
3809
3810 fn hand_out(&mut self, update: AnnounceUpdate) -> AnnounceUpdate {
3812 let absolute = self.root.join(&update.prefix).to_owned();
3813 if update.kind.is_active() {
3814 let scope = self.stats.egress(&absolute);
3815 self.guards
3816 .entry(update.prefix.clone())
3817 .or_insert_with(|| scope.announce());
3818 } else {
3819 self.guards.remove(&update.prefix);
3820 }
3821 update
3822 }
3823
3824 pub async fn next(&mut self) -> Option<AnnounceUpdate> {
3832 kio::wait(|waiter| self.poll_next(waiter)).await
3833 }
3834
3835 pub fn poll_next(&mut self, waiter: &kio::Waiter) -> Poll<Option<AnnounceUpdate>> {
3841 let update = {
3842 let mut state = match ready!(self.state.poll(waiter, |state| {
3843 if state.pending.is_empty() && !state.ended {
3844 Poll::Pending
3845 } else {
3846 Poll::Ready(())
3847 }
3848 })) {
3849 Ok(state) => state,
3850 Err(_) => return Poll::Ready(None),
3852 };
3853 match state.take() {
3854 Some(update) => update,
3855 None => {
3856 state.close();
3859 return Poll::Ready(None);
3860 }
3861 }
3862 };
3863 Poll::Ready(Some(self.hand_out(update)))
3864 }
3865
3866 pub fn try_next(&mut self) -> Option<AnnounceUpdate> {
3871 let update = self.state.write().ok()?.take()?;
3872 Some(self.hand_out(update))
3873 }
3874
3875 pub fn is_closed(&self) -> bool {
3877 let state = self.state.read();
3878 state.is_closed() || state.ended
3879 }
3880
3881 pub fn root(&self) -> &Path<'_> {
3883 &self.root
3884 }
3885
3886 pub fn absolute(&self, prefix: impl AsPath) -> Path<'_> {
3888 self.root.join(prefix)
3889 }
3890}
3891
3892impl futures::Stream for AnnounceConsumer {
3893 type Item = AnnounceUpdate;
3894
3895 fn poll_next(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Option<Self::Item>> {
3896 let this = self.get_mut();
3897 let waiter = this.park.hold(cx).clone();
3898 this.poll_next(&waiter)
3899 }
3900}
3901
3902impl Drop for AnnounceConsumer {
3903 fn drop(&mut self) {
3904 let mut shared = self.shared.lock();
3905 if let Some(cursor) = shared.cursors.remove(&self.id) {
3906 for head in &cursor.heads {
3907 shared.routes.remove_cursor(head, self.id);
3908 }
3909 }
3910 }
3911}
3912
3913#[cfg(test)]
3914use futures::FutureExt;
3915
3916#[cfg(test)]
3917#[allow(missing_docs)] impl AnnounceConsumer {
3919 pub fn assert_next_active(&mut self, expected: impl AsPath) -> Route {
3921 let expected = expected.as_path();
3922 let update = self.next().now_or_never().expect("next blocked").expect("no next");
3923 assert_eq!(update.prefix, expected, "wrong prefix");
3924 assert!(update.kind.is_active(), "should be an active route");
3925 update.route
3926 }
3927
3928 pub fn assert_try_next_active(&mut self, expected: impl AsPath) -> Route {
3930 let expected = expected.as_path();
3931 let update = self.try_next().expect("no next");
3932 assert_eq!(update.prefix, expected, "wrong prefix");
3933 assert!(update.kind.is_active(), "should be an active route");
3934 update.route
3935 }
3936
3937 pub fn assert_next_ended(&mut self, expected: impl AsPath) {
3939 let expected = expected.as_path();
3940 let update = self.next().now_or_never().expect("next blocked").expect("no next");
3941 assert_eq!(update.prefix, expected, "wrong prefix");
3942 assert_eq!(update.kind, AnnounceKind::Retracted, "should be a retraction");
3943 }
3944
3945 pub fn assert_next_wait(&mut self) {
3946 if let Some(res) = self.next().now_or_never() {
3947 panic!("next should block: got {:?}", res.map(|u| u.prefix));
3948 }
3949 }
3950}
3951
3952#[cfg(test)]
3956pub(crate) trait ProduceTest {
3957 fn produce(self) -> Producer;
3958}
3959
3960#[cfg(test)]
3961impl ProduceTest for Config {
3962 fn produce(self) -> Producer {
3963 let (producer, driver) = Producer::new(self);
3964 if tokio::runtime::Handle::try_current().is_ok() {
3965 tokio::spawn(crate::time::run(driver));
3966 } else {
3967 std::mem::forget(driver);
3970 }
3971 producer
3972 }
3973}
3974
3975#[cfg(test)]
3976impl ProduceTest for Hop {
3977 fn produce(self) -> Producer {
3978 Config::new(self).produce()
3979 }
3980}
3981
3982#[cfg(test)]
3983mod tests {
3984 use super::*;
3985 use futures::FutureExt;
3986
3987 fn origin(id: u64) -> Hop {
3988 Hop::new(id).unwrap()
3989 }
3990
3991 fn hops(ids: &[u64]) -> Hops {
3992 let mut list = Hops::new();
3993 for &id in ids {
3994 list.push(if id == 0 { Hop::UNKNOWN } else { origin(id) }).unwrap();
3995 }
3996 list
3997 }
3998
3999 fn scopes(prefixes: &[&str]) -> Patterns {
4001 prefixes
4002 .iter()
4003 .map(|prefix| Pattern::subtree(prefix).unwrap())
4004 .collect()
4005 }
4006
4007 #[test]
4008 fn default_config_mints_a_real_hop() {
4009 let config = Config::default();
4010 assert_ne!(config.hop, Hop::UNKNOWN);
4011 let (producer, _driver) = Producer::new(config.clone());
4012 assert_eq!(producer.hop(), config.hop);
4013 assert_eq!(producer.consume().hop(), config.hop);
4014 }
4015
4016 #[test]
4017 fn random_hops_fit_legacy_lite_clients() {
4018 for _ in 0..32 {
4019 assert!(Hop::random().id() < 1u64 << 53);
4020 }
4021 }
4022
4023 async fn settle(mut check: impl FnMut() -> bool) {
4026 for _ in 0..100 {
4027 if check() {
4028 return;
4029 }
4030 tokio::task::yield_now().await;
4031 }
4032 panic!("condition never settled");
4033 }
4034
4035 async fn queued(server: &Dynamic) -> Request {
4037 let mut request = None;
4038 settle(|| match server.poll_requested_broadcast(&kio::Waiter::noop()) {
4039 Poll::Ready(Ok(popped)) => {
4040 request = Some(popped);
4041 true
4042 }
4043 _ => false,
4044 })
4045 .await;
4046 request.unwrap()
4047 }
4048
4049 async fn next_group(subscription: &mut crate::track::Subscriber) -> Result<Option<crate::group::Consumer>, Error> {
4051 let mut next = None;
4052 settle(|| match subscription.poll_recv_group(&kio::Waiter::noop()) {
4053 Poll::Ready(result) => {
4054 next = Some(result);
4055 true
4056 }
4057 Poll::Pending => false,
4058 })
4059 .await;
4060 next.unwrap()
4061 }
4062
4063 #[tokio::test]
4064 async fn announce_and_retract() {
4065 let producer = origin(1).produce();
4066 let consumer = producer.consume();
4067 let mut announced = consumer.announced();
4068 announced.assert_next_wait();
4069
4070 let announcement = producer.announce("room/alice", Route::default()).unwrap();
4071 let route = announced.assert_next_active("room/alice");
4072 assert!(route.hops.is_empty());
4073 assert_eq!(route.cost, Cost::default());
4074 announced.assert_next_wait();
4075
4076 drop(announcement);
4077 announced.assert_next_ended("room/alice");
4078 announced.assert_next_wait();
4079 }
4080
4081 #[tokio::test]
4084 async fn hidden_routes_need_an_opt_in() {
4085 let producer = origin(1).produce();
4086 let consumer = producer.consume();
4087 let _visible = producer.announce("room/alice", Route::default()).unwrap();
4088 let _stats = producer.announce(".stats/node", Route::default()).unwrap();
4089 let _nested = producer.announce("room/.internal", Route::default()).unwrap();
4090 let _suffix = producer.announce("room/catalog.pro", Route::default()).unwrap();
4092
4093 let mut announced = consumer.announced();
4094 announced.assert_next_active("room/alice");
4095 announced.assert_next_active("room/catalog.pro");
4096 announced.assert_next_wait();
4097
4098 let mut announced = consumer.clone().with_hidden(true).announced();
4099 announced.assert_next_active(".stats/node");
4100 announced.assert_next_active("room/.internal");
4101 announced.assert_next_active("room/alice");
4102 announced.assert_next_active("room/catalog.pro");
4103 announced.assert_next_wait();
4104
4105 let mut announced = consumer
4107 .scope(".stats", &Patterns::from(Pattern::all()))
4108 .unwrap()
4109 .announced();
4110 announced.assert_next_active("node");
4111 announced.assert_next_wait();
4112 let mut announced = consumer.scope("", &scopes(&["room/.internal"])).unwrap().announced();
4113 announced.assert_next_active("room/.internal");
4114 announced.assert_next_wait();
4115
4116 let mut announced = consumer.clone().with_hidden(true).beyond(&consumer).announced();
4119 announced.assert_next_active(".stats/node");
4120 announced.assert_next_active("room/.internal");
4121 announced.assert_next_wait();
4122 let room = consumer.scope("", &scopes(&["room"])).unwrap().beyond(&consumer);
4123 let mut announced = room.announced();
4124 announced.assert_next_wait();
4125 let stats = consumer.scope("", &scopes(&[".stats"])).unwrap().beyond(&consumer);
4126 let mut announced = stats.announced();
4127 announced.assert_next_active(".stats/node");
4128 announced.assert_next_wait();
4129 }
4130
4131 #[tokio::test]
4133 async fn hidden_broadcast_resolves_by_path() {
4134 let producer = origin(1).produce();
4135 let consumer = producer.consume();
4136 let broadcast = producer.create_broadcast(".stats/node").unwrap();
4137 broadcast.announce(Route::default()).unwrap();
4138
4139 consumer.announced().assert_next_wait();
4140 let resolved = consumer.request_broadcast(".stats/node").await.expect("resolves");
4141 assert_eq!(resolved.info().path.as_str(), ".stats/node");
4142 }
4143
4144 #[tokio::test]
4146 async fn hidden_route_announced_later_stays_hidden() {
4147 let producer = origin(1).produce();
4148 let consumer = producer.consume();
4149 let mut announced = consumer.announced();
4150 let mut opted = consumer.clone().with_hidden(true).announced();
4151
4152 let hidden = producer.announce(".stats/node", Route::default()).unwrap();
4153 announced.assert_next_wait();
4154 opted.assert_next_active(".stats/node");
4155
4156 drop(hidden);
4157 announced.assert_next_wait();
4158 opted.assert_next_ended(".stats/node");
4159 }
4160
4161 #[tokio::test]
4162 async fn broadcast_announces_its_own_path() {
4163 let producer = origin(1).produce();
4164 let consumer = producer.consume();
4165 let mut announced = consumer.announced();
4166 let mut peer = consumer.clone().excluding(Hop::UNKNOWN).announced();
4167
4168 let broadcast = producer.create_broadcast("room/alice").unwrap();
4170 announced.assert_next_wait();
4171 peer.assert_next_wait();
4172
4173 broadcast.announce(Route::default().with_cost(3)).unwrap();
4174 assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(3));
4175 assert_eq!(peer.assert_next_active("room/alice").cost, Cost::new(3));
4176
4177 broadcast.announce(Route::default().with_cost(1)).unwrap();
4179 assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(1));
4180 assert_eq!(peer.assert_next_active("room/alice").cost, Cost::new(1));
4181
4182 broadcast.unannounce();
4184 announced.assert_next_ended("room/alice");
4185 peer.assert_next_ended("room/alice");
4186 broadcast.unannounce();
4187 announced.assert_next_wait();
4188 let err = consumer.request_broadcast("room/alice").await.err().unwrap();
4189 assert!(matches!(err, Error::Unroutable));
4190
4191 broadcast.announce(Route::default()).unwrap();
4193 announced.assert_next_active("room/alice");
4194 peer.assert_next_active("room/alice");
4195 broadcast.finish();
4196 announced.assert_next_ended("room/alice");
4197 peer.assert_next_ended("room/alice");
4198 assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
4199 announced.assert_next_wait();
4200 }
4201
4202 #[tokio::test]
4203 async fn broadcast_announcement_retracts_with_the_last_producer() {
4204 let producer = origin(1).produce();
4205 let consumer = producer.consume();
4206 let mut announced = consumer.announced();
4207
4208 let broadcast = producer.create_broadcast("room/alice").unwrap();
4209 let clone = broadcast.clone();
4210 broadcast.announce(Route::default()).unwrap();
4211 announced.assert_next_active("room/alice");
4212
4213 drop(broadcast);
4215 announced.assert_next_wait();
4216 drop(clone);
4217 announced.assert_next_ended("room/alice");
4218 }
4219
4220 #[tokio::test]
4221 async fn publish_creates_and_announces_together() {
4222 let producer = origin(1).produce();
4223 let mut announced = producer.consume().announced();
4224 let _broadcast = producer.publish("room/alice", Route::default()).unwrap();
4225 announced.assert_next_active("room/alice");
4226 }
4227
4228 #[tokio::test]
4229 async fn standalone_broadcast_cannot_announce() {
4230 let broadcast = broadcast::Info::new().produce();
4231 assert!(matches!(broadcast.announce(Route::default()), Err(Error::Closed)));
4232 broadcast.unannounce();
4234 }
4235
4236 #[tokio::test]
4237 async fn announce_replays_to_late_cursor() {
4238 let producer = origin(1).produce();
4239 let _a = producer.announce("room/alice", Route::default()).unwrap();
4240 let _b = producer.announce("room/bob", Route::default()).unwrap();
4241
4242 let mut announced = producer.consume().announced();
4243 announced.assert_next_active("room/alice");
4245 announced.assert_next_active("room/bob");
4246 announced.assert_next_wait();
4247 }
4248
4249 #[tokio::test]
4250 async fn announce_keeps_its_prefix_under_a_producer_scope() {
4251 let producer = origin(1).produce();
4252 let scoped = producer.scope("", &scopes(&["room"])).unwrap();
4253
4254 let _a = scoped.announce("", Route::default()).unwrap();
4256 let mut announced = producer.consume().announced();
4257 announced.assert_next_active("");
4258
4259 assert!(matches!(
4261 scoped.announce("other", Route::default()),
4262 Err(Error::Unauthorized)
4263 ));
4264 }
4265
4266 #[tokio::test]
4267 async fn cursor_keeps_an_overlapping_prefix_above_its_scope() {
4268 let producer = origin(1).produce();
4269 let _a = producer.announce("", Route::default()).unwrap();
4270
4271 let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
4272 let mut announced = consumer.announced();
4273 announced.assert_next_active("");
4274 }
4275
4276 #[tokio::test]
4277 async fn cursor_root_strips_prefix() {
4278 let producer = origin(1).produce();
4279 let _a = producer.announce("room/alice", Route::default()).unwrap();
4280
4281 let consumer = producer
4282 .consume()
4283 .scope("room", &Patterns::from(Pattern::all()))
4284 .unwrap();
4285 let mut announced = consumer.announced();
4286 announced.assert_next_active("alice");
4287 }
4288
4289 #[tokio::test]
4290 async fn best_route_wins_and_fails_over() {
4291 let producer = origin(1).produce();
4292 let mut announced = producer.consume().announced();
4293
4294 let expensive = producer
4295 .announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4296 .unwrap();
4297 let route = announced.assert_next_active("room");
4298 assert_eq!(route.cost, Cost::new(5));
4299
4300 let cheap = producer
4302 .announce("room", Route::default().with_hops(hops(&[20])).with_cost(1))
4303 .unwrap();
4304 let route = announced.assert_next_active("room");
4305 assert_eq!(route.cost, Cost::new(1));
4306
4307 drop(cheap);
4309 let route = announced.assert_next_active("room");
4310 assert_eq!(route.cost, Cost::new(5));
4311
4312 drop(expensive);
4314 announced.assert_next_ended("room");
4315 }
4316
4317 #[tokio::test]
4318 async fn identical_reannounce_is_invisible() {
4319 let producer = origin(1).produce();
4320 let mut announced = producer.consume().announced();
4321
4322 let old = producer
4323 .announce("room", Route::default().with_hops(hops(&[10])))
4324 .unwrap();
4325 let first = announced.assert_next_active("room");
4326 assert_eq!(first.hops.as_slice(), hops(&[10]).as_slice());
4327
4328 let _new = producer
4332 .announce("room", Route::default().with_hops(hops(&[10])))
4333 .unwrap();
4334 announced.assert_next_wait();
4335
4336 drop(old);
4338 announced.assert_next_wait();
4339 }
4340
4341 #[tokio::test]
4342 async fn exclude_hides_routes_through_the_peer() {
4343 let producer = origin(1).produce();
4344 let _a = producer
4345 .announce("room", Route::default().with_hops(hops(&[7])))
4346 .unwrap();
4347
4348 let mut hidden = producer.consume().excluding(origin(7)).announced();
4349 hidden.assert_next_wait();
4350
4351 let mut visible = producer.consume().excluding(origin(8)).announced();
4352 visible.assert_next_active("room");
4353 }
4354
4355 #[tokio::test]
4356 async fn exclude_matches_via_when_the_chain_is_anonymous() {
4357 let producer = origin(1).produce();
4358 let assigned = origin(777);
4359 let _echoed = producer
4360 .announce("echoed", Route::default().with_hops(hops(&[0])).with_via(assigned))
4361 .unwrap();
4362 let _local = producer
4363 .announce("local", Route::default().with_hops(hops(&[10])))
4364 .unwrap();
4365
4366 let mut hidden = producer.consume().excluding(assigned).announced();
4367 hidden.assert_next_active("local");
4368 hidden.assert_next_wait();
4369 }
4370
4371 #[tokio::test]
4372 async fn anonymous_route_loses_to_identified_at_any_cost() {
4373 let producer = origin(1).produce();
4374 let mut announced = producer.consume().announced();
4375
4376 let _anonymous = producer
4377 .announce("room", Route::default().with_hops(hops(&[0])).with_cost(1))
4378 .unwrap();
4379 let route = announced.assert_next_active("room");
4380 assert!(route.is_anonymous());
4381 assert_eq!(route.cost, Cost::new(1));
4382
4383 let _identified = producer
4384 .announce("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4385 .unwrap();
4386 let route = announced.assert_next_active("room");
4387 assert!(!route.is_anonymous());
4388 assert_eq!(route.cost, Cost::new(5));
4389 }
4390
4391 #[tokio::test]
4392 async fn anonymous_routes_order_by_cost() {
4393 let producer = origin(1).produce();
4394 let mut announced = producer.consume().announced();
4395
4396 let expensive = producer
4397 .announce("room", Route::default().with_hops(hops(&[0])).with_cost(5))
4398 .unwrap();
4399 let route = announced.assert_next_active("room");
4400 assert_eq!(route.cost, Cost::new(5));
4401
4402 let _cheap = producer
4403 .announce("room", Route::default().with_hops(hops(&[0, 7])).with_cost(1))
4404 .unwrap();
4405 let route = announced.assert_next_active("room");
4406 assert!(route.is_anonymous());
4407 assert_eq!(route.cost, Cost::new(1));
4408
4409 drop(expensive);
4410 announced.assert_next_wait();
4411 }
4412
4413 #[tokio::test]
4414 async fn anonymous_chain_from_identified_peer_still_ranks_last() {
4415 let producer = origin(1).produce();
4416 let mut announced = producer.consume().announced();
4417
4418 let _anonymous = producer
4419 .announce(
4420 "room",
4421 Route::default()
4422 .with_hops(hops(&[0, 7]))
4423 .with_cost(1)
4424 .with_via(origin(7)),
4425 )
4426 .unwrap();
4427 announced.assert_next_active("room");
4428
4429 let _identified = producer
4430 .announce("room", Route::default().with_hops(hops(&[10, 20])).with_cost(5))
4431 .unwrap();
4432 let route = announced.assert_next_active("room");
4433 assert!(!route.is_anonymous());
4434 assert_eq!(route.cost, Cost::new(5));
4435 }
4436
4437 #[tokio::test]
4438 async fn request_prefers_identified_over_cheaper_anonymous() {
4439 let producer = origin(1).produce();
4440 let consumer = producer.consume();
4441
4442 let anonymous = producer
4443 .dynamic("room", Route::default().with_hops(hops(&[0])).with_cost(1))
4444 .unwrap();
4445 let identified = producer
4446 .dynamic("room", Route::default().with_hops(hops(&[10])).with_cost(5))
4447 .unwrap();
4448
4449 let _pending = consumer.request_broadcast("room/alice");
4450 let request = queued(&identified).await;
4451 assert_eq!(request.path().as_str(), "room/alice");
4452 assert!(
4453 anonymous.poll_requested_broadcast(&kio::Waiter::noop()).is_pending(),
4454 "the cheaper anonymous route must not serve"
4455 );
4456 }
4457
4458 #[tokio::test]
4459 async fn update_reprices_in_place() {
4460 let producer = origin(1).produce();
4461 let mut announced = producer.consume().announced();
4462
4463 let announcement = producer.announce("room", Route::default()).unwrap();
4464 announced.assert_next_active("room");
4465
4466 announcement.update(Route::default().with_cost(9)).unwrap();
4467 let route = announced.assert_next_active("room");
4468 assert_eq!(route.cost, Cost::new(9));
4469 }
4470
4471 #[tokio::test]
4472 async fn retract_after_undelivered_reprice_still_delivered() {
4473 let producer = origin(1).produce();
4474 let mut announced = producer.consume().announced();
4475
4476 let announcement = producer.announce("room", Route::default()).unwrap();
4477 announced.assert_next_active("room");
4478
4479 announcement.update(Route::default().with_cost(9)).unwrap();
4483 drop(announcement);
4484 announced.assert_next_ended("room");
4485 announced.assert_next_wait();
4486 }
4487
4488 #[tokio::test]
4489 async fn scoped_cursor_advertises_most_specific_covering_route() {
4490 let producer = origin(1).produce();
4491 let _broad = producer.announce("room", Route::default().with_cost(1)).unwrap();
4494 let _narrow = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
4495
4496 let consumer = producer
4497 .consume()
4498 .scope("room/alice", &Patterns::from(Pattern::all()))
4499 .unwrap();
4500 let mut announced = consumer.announced();
4501 let route = announced.assert_next_active("");
4502 assert_eq!(route.cost, Cost::new(9));
4503 announced.assert_next_wait();
4504 }
4505
4506 #[tokio::test]
4507 async fn capture_change_retracts_before_reannouncing_a_presented_prefix() {
4508 let producer = origin(1).produce();
4509 let _broad = producer.announce("room", Route::default()).unwrap();
4510 let exact = producer.announce("room/alice", Route::default()).unwrap();
4511 let consumer = producer
4512 .consume()
4513 .scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
4514 .unwrap()
4515 .scope("room/alice", &Patterns::from(Pattern::all()))
4516 .unwrap();
4517 let mut announced = consumer.announced();
4518
4519 let first = announced.next().now_or_never().expect("next").expect("announce");
4520 assert_eq!(first.prefix.as_str(), "");
4521 assert_eq!(first.kind, AnnounceKind::Announced);
4522 assert_eq!(first.captures, Some(Vec::new()));
4523
4524 drop(exact);
4525 let retracted = announced.next().now_or_never().expect("next").expect("retract");
4526 assert_eq!(retracted.prefix.as_str(), "");
4527 assert_eq!(retracted.kind, AnnounceKind::Retracted);
4528 assert_eq!(retracted.captures, Some(Vec::new()));
4529 let replacement = announced.next().now_or_never().expect("next").expect("announce");
4530 assert_eq!(replacement.prefix.as_str(), "");
4531 assert_eq!(replacement.kind, AnnounceKind::Announced);
4532 assert_eq!(replacement.captures, None);
4533 }
4534
4535 #[tokio::test]
4536 async fn routed_broadcast_resolves_once_announced() {
4537 let producer = origin(1).produce();
4538 let consumer = producer.consume();
4539
4540 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
4542 assert!((&mut resolving).now_or_never().is_none());
4543
4544 let broadcast = producer.create_broadcast("room/alice").unwrap();
4546 for _ in 0..20 {
4547 tokio::task::yield_now().await;
4548 }
4549 assert!((&mut resolving).now_or_never().is_none());
4550
4551 broadcast.announce(Route::default()).unwrap();
4552 let resolved = resolving.await.expect("resolves once announced");
4553 assert_eq!(resolved.info().path.as_str(), "room/alice");
4554 drop(broadcast);
4555 }
4556
4557 #[tokio::test]
4560 async fn cheaper_remote_route_beats_a_local_broadcast() {
4561 let producer = origin(1).produce();
4562 let consumer = producer.consume();
4563 let mut announced = consumer.announced();
4564
4565 let _local = producer.publish("room/alice", Route::default().with_cost(5)).unwrap();
4566 assert_eq!(announced.assert_next_active("room/alice").cost, Cost::new(5));
4567
4568 let server = producer
4569 .dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(1))
4570 .unwrap();
4571 let route = announced.assert_next_active("room/alice");
4572 assert_eq!(route.cost, Cost::new(1));
4573 assert_eq!(route.hops, hops(&[10]));
4574
4575 let pending = consumer.request_broadcast("room/alice");
4577 let request = queued(&server).await;
4578 let upstream = broadcast::Info::new().produce();
4579 request.accept(&upstream);
4580 pending.await.expect("resolves through the cheaper route");
4581 }
4582
4583 #[tokio::test]
4587 async fn cheaper_route_after_a_front_wins_new_requests() {
4588 let producer = origin(1).produce();
4589 let consumer = producer.consume();
4590
4591 let _local = producer.publish("room/alice", Route::default().with_cost(5)).unwrap();
4592 let first = consumer
4593 .request_broadcast("room/alice")
4594 .await
4595 .expect("resolves locally");
4596
4597 let server = producer
4598 .dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(1))
4599 .unwrap();
4600
4601 let pending = consumer.request_broadcast("room/alice");
4602 let request = queued(&server).await;
4603 let upstream = broadcast::Info::new().produce();
4604 request.accept(&upstream);
4605 let second = pending.await.expect("resolves through the cheaper route");
4606
4607 assert!(!first.is_closed(), "the old front must keep serving its readers");
4608 assert!(!first.is_clone(&second), "the newcomer must not join the old front");
4609 }
4610
4611 #[tokio::test]
4615 async fn local_broadcast_wins_a_tie_with_a_hopless_route() {
4616 let producer = origin(1).produce();
4617 let consumer = producer.consume();
4618
4619 let _local = producer.publish("room/alice", Route::default()).unwrap();
4620 let server = producer.dynamic("room/alice", Route::default()).unwrap();
4621
4622 let resolved = tokio::time::timeout(Duration::from_secs(1), consumer.request_broadcast("room/alice"))
4624 .await
4625 .expect("the newer hopless route won the tie")
4626 .expect("resolves");
4627 assert_eq!(resolved.info().path.as_str(), "room/alice");
4628 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4629 }
4630
4631 #[tokio::test]
4634 async fn announce_stats_follow_the_advertisement() {
4635 let registry = stats::Registry::new(stats::Config::new());
4636 let producer = origin(1)
4637 .produce()
4638 .with_stats(registry.tier(stats::Tier::default()).session("root"));
4639 let announces = || {
4640 registry
4641 .snapshot()
4642 .traffic()
4643 .into_iter()
4644 .find(|(_, role, _)| *role == stats::Role::Subscriber)
4645 .map(|(_, _, traffic)| (traffic.announces_started, traffic.announces_ended))
4646 .unwrap_or_default()
4647 };
4648
4649 let broadcast = producer.create_broadcast("room/alice").unwrap();
4650 assert_eq!(announces(), (0, 0), "a hidden broadcast is not announced");
4651 broadcast.announce(Route::default()).unwrap();
4652 broadcast
4653 .announce(Route {
4654 cost: Cost::new(3),
4655 ..Route::default()
4656 })
4657 .unwrap();
4658 assert_eq!(announces(), (1, 0), "a re-price is not another announce");
4659 broadcast.unannounce();
4660 assert_eq!(announces(), (1, 1));
4661 broadcast.announce(Route::default()).unwrap();
4662 drop(broadcast);
4663 assert_eq!(announces(), (2, 2));
4664 }
4665
4666 #[tokio::test]
4668 async fn local_broadcast_wins_a_cost_tie() {
4669 let producer = origin(1).produce();
4670 let consumer = producer.consume();
4671 let mut announced = consumer.announced();
4672
4673 let server = producer
4674 .dynamic("room/alice", Route::default().with_hops(hops(&[10])).with_cost(2))
4675 .unwrap();
4676 announced.assert_next_active("room/alice");
4677 let _local = producer.publish("room/alice", Route::default().with_cost(2)).unwrap();
4678 assert!(announced.assert_next_active("room/alice").hops.is_empty());
4679
4680 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4681 assert_eq!(resolved.info().path.as_str(), "room/alice");
4682 for _ in 0..20 {
4683 tokio::task::yield_now().await;
4684 }
4685 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
4686 }
4687
4688 #[tokio::test]
4691 async fn unannounce_ends_the_front() {
4692 let producer = origin(1).produce();
4693 let consumer = producer.consume();
4694
4695 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4696 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4697
4698 broadcast.unannounce();
4699 let err = consumer.request_broadcast("room/alice").await.err().unwrap();
4700 assert!(matches!(err, Error::Unroutable), "joined a retracted front: {err}");
4701 settle(|| resolved.is_closed()).await;
4702 assert!(!broadcast.consume().is_closed(), "the broadcast itself lives on");
4703
4704 broadcast.announce(Route::default()).unwrap();
4706 let again = consumer.request_broadcast("room/alice").await.expect("resolves again");
4707 assert!(!again.is_clone(&resolved));
4708 }
4709
4710 #[tokio::test]
4714 async fn unannounce_keeps_a_track_awaiting_its_info() {
4715 let producer = origin(1).produce();
4716 let consumer = producer.consume();
4717
4718 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4719 let mut dynamic = broadcast.dynamic();
4720 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4721 let track = resolved.track("video").unwrap();
4722 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4723 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4724 .await
4725 .expect("the front asked the source")
4726 .expect("request");
4727
4728 broadcast.unannounce();
4729 settle(|| resolved.is_closed()).await;
4730
4731 let source = request.accept(None);
4732 let mut group = source.append_group().unwrap();
4733 group.write_frame(crate::Timestamp::ZERO, b"late".as_ref()).unwrap();
4734 group.finish().unwrap();
4735 source.finish().unwrap();
4736
4737 let mut subscription = subscribing.await.unwrap().expect("subscribe survives the retraction");
4738 let mut group = subscription.recv_group().await.unwrap().expect("the source's group");
4739 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"late");
4740 assert!(matches!(subscription.recv_group().await, Ok(None)), "ends cleanly");
4741 }
4742
4743 async fn served_front() -> (Dynamic, broadcast::Producer, broadcast::Dynamic, broadcast::Consumer) {
4746 let producer = origin(1).produce();
4747 let consumer = producer.consume();
4748 let server = producer
4749 .dynamic("room/alice", Route::default().with_hops(hops(&[10])))
4750 .unwrap();
4751 let pending = consumer.request_broadcast("room/alice");
4752 let upstream = broadcast::Info::new().produce();
4753 let dynamic = upstream.dynamic();
4754 queued(&server).await.accept(&upstream);
4755 let resolved = pending.await.expect("resolves");
4756 (server, upstream, dynamic, resolved)
4757 }
4758
4759 #[tokio::test]
4764 async fn returning_reader_skips_a_warm_cache_the_copy_resolved_past() {
4765 let ms = |v: u64| crate::Timestamp::from_millis(v).unwrap();
4766 let (_server, _upstream, mut dynamic, resolved) = served_front().await;
4767 let budget = track::Subscription::default().with_max_age(Duration::from_millis(100));
4768
4769 let track = resolved.track("audio").unwrap();
4770 let b = budget.clone();
4771 let subscribing = tokio::spawn(async move { track.subscribe(b).await });
4772 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4773 .await
4774 .expect("the front asked the source")
4775 .expect("request");
4776 let source = request.resolving_start().accept(None);
4777 for seq in 0..4u64 {
4778 let mut group = source.create_group(seq.into()).unwrap();
4779 group.write_frame(ms(seq * 20), b"old".as_ref()).unwrap();
4780 group.finish().unwrap();
4781 }
4782 let mut subscription = subscribing.await.unwrap().expect("subscribe");
4783 subscription.recv_group().await.unwrap().expect("the live group");
4784 drop(subscription);
4785 tokio::time::timeout(Duration::from_secs(1), source.unused())
4786 .await
4787 .expect("parked")
4788 .expect("source open");
4789 drop(source);
4790
4791 let track = resolved.track("audio").unwrap();
4792 let subscribing = tokio::spawn(async move { track.subscribe(budget).await });
4793 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4794 .await
4795 .expect("the front asked the source again")
4796 .expect("request");
4797 let mut source = request.resolving_start().accept(None);
4798 let mut subscription = subscribing.await.unwrap().expect("resubscribe");
4799
4800 assert!(
4802 tokio::time::timeout(Duration::from_millis(50), subscription.recv_group())
4803 .await
4804 .is_err(),
4805 "the warm cache was served before the copy resolved its start"
4806 );
4807
4808 source.start_at(20).unwrap();
4810 let mut group = source.create_group(20u64.into()).unwrap();
4811 group.write_frame(ms(2000), b"new".as_ref()).unwrap();
4812 group.finish().unwrap();
4813 let group = subscription.recv_group().await.unwrap().expect("the live group");
4814 assert_eq!(group.sequence, 20, "a stale warm group was served");
4815 }
4816
4817 #[tokio::test]
4822 async fn returning_reader_replays_a_current_warm_cache() {
4823 let (_server, _upstream, mut dynamic, resolved) = served_front().await;
4824
4825 let track = resolved.track("catalog").unwrap();
4826 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4827 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4828 .await
4829 .expect("the front asked the source")
4830 .expect("request");
4831 let source = request.resolving_start().accept(None);
4832 let mut group = source.create_group(0u64.into()).unwrap();
4833 group.write_frame(crate::Timestamp::ZERO, b"snapshot".as_ref()).unwrap();
4834 group.finish().unwrap();
4835 let mut subscription = subscribing.await.unwrap().expect("subscribe");
4836 subscription.recv_group().await.unwrap().expect("the catalog");
4837 drop(subscription);
4838 tokio::time::timeout(Duration::from_secs(1), source.unused())
4839 .await
4840 .expect("parked")
4841 .expect("source open");
4842 drop(source);
4843
4844 let track = resolved.track("catalog").unwrap();
4845 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4846 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4847 .await
4848 .expect("the front asked the source again")
4849 .expect("request");
4850 let mut source = request.resolving_start().accept(None);
4851 let mut subscription = subscribing.await.unwrap().expect("resubscribe");
4852
4853 let reading = tokio::spawn(async move {
4856 let mut group = subscription.recv_group().await.unwrap().expect("the catalog");
4857 assert_eq!(group.sequence, 0);
4858 group.read_frame().await.unwrap().expect("the snapshot").payload
4859 });
4860 tokio::task::yield_now().await;
4861 assert_eq!(
4862 source.subscription().and_then(|sub| sub.start),
4863 Some(track::Position { group: 0, frame: 1 }),
4864 "the re-splice asked past the cached catalog"
4865 );
4866 source.start_at(0).unwrap();
4867 let mut tail = source.create_group(0u64.into()).unwrap();
4868 tail.start_at(1).unwrap();
4869 tail.finish().unwrap();
4870 let payload = tokio::time::timeout(Duration::from_secs(1), reading)
4871 .await
4872 .expect("the returning reader never got the catalog")
4873 .unwrap();
4874 assert_eq!(&payload[..], b"snapshot");
4875 }
4876
4877 #[tokio::test]
4882 async fn returning_reader_continues_an_open_warm_group() {
4883 let (_server, _upstream, mut dynamic, resolved) = served_front().await;
4884
4885 async fn read(group: &mut group::Consumer) -> Vec<u8> {
4886 let frame = tokio::time::timeout(Duration::from_secs(1), group.read_frame())
4887 .await
4888 .expect("frame")
4889 .unwrap()
4890 .expect("group ended");
4891 frame.payload.to_vec()
4892 }
4893
4894 let mut expect: Vec<&[u8]> = Vec::new();
4895 let mut floor: Option<track::Position> = None;
4896 for (round, payload) in [b"a".as_ref(), b"b", b"c"].into_iter().enumerate() {
4897 let track = resolved.track("log").unwrap();
4898 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4899 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4900 .await
4901 .expect("the front asked the source")
4902 .expect("request");
4903 let mut source = request.resolving_start().accept(None);
4904 let mut subscription = subscribing.await.unwrap().expect("subscribe");
4905
4906 source.start_at(0).unwrap();
4908 let mut group = source.create_group(0u64.into()).unwrap();
4909 if let Some(floor) = floor {
4910 group.start_at(floor.frame).unwrap();
4911 }
4912 group.write_frame(crate::Timestamp::ZERO, payload).unwrap();
4913 expect.push(payload);
4914 source
4915 .insert_datagram(10, crate::Timestamp::ZERO, b"datagram".as_ref())
4916 .unwrap();
4917
4918 let mut reading = tokio::time::timeout(Duration::from_secs(1), subscription.recv_group())
4919 .await
4920 .expect("group 0")
4921 .unwrap()
4922 .expect("track ended");
4923 assert_eq!(reading.sequence, 0);
4924 for frame in &expect {
4925 assert_eq!(read(&mut reading).await, *frame, "round {round}");
4926 }
4927 assert_eq!(
4928 source.subscription().and_then(|sub| sub.start),
4929 floor,
4930 "round {round} asked for the wrong continuation"
4931 );
4932
4933 drop(reading);
4934 drop(subscription);
4935 tokio::time::timeout(Duration::from_secs(1), source.unused())
4936 .await
4937 .expect("parked")
4938 .expect("source open");
4939 drop(group);
4940 drop(source);
4941 floor = Some(track::Position {
4942 group: 0,
4943 frame: expect.len() as u64,
4944 });
4945 }
4946 }
4947
4948 #[tokio::test]
4951 async fn unannounce_keeps_a_returning_reader_awaiting_its_info() {
4952 let producer = origin(1).produce();
4953 let consumer = producer.consume();
4954
4955 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
4956 let mut dynamic = broadcast.dynamic();
4957 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
4958 let track = resolved.track("video").unwrap();
4959 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4960 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4961 .await
4962 .expect("the front asked the source")
4963 .expect("request");
4964 let source = request.accept(None);
4965 let mut group = source.append_group().unwrap();
4966 group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
4967 group.finish().unwrap();
4968 let mut subscription = subscribing.await.unwrap().expect("subscribe");
4969 subscription.recv_group().await.unwrap().expect("the cached group");
4970 drop(subscription);
4971
4972 tokio::time::timeout(Duration::from_secs(1), source.unused())
4975 .await
4976 .expect("parked")
4977 .expect("source open");
4978 drop(source);
4979 let track = resolved.track("video").unwrap();
4980 let subscribing = tokio::spawn(async move { track.subscribe(None).await });
4981 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
4982 .await
4983 .expect("the front asked the source again")
4984 .expect("request");
4985
4986 broadcast.unannounce();
4987 settle(|| resolved.is_closed()).await;
4988
4989 let source = request.accept(None);
4991 let mut group = source.create_group(1u64.into()).unwrap();
4992 group.write_frame(crate::Timestamp::ZERO, b"late".as_ref()).unwrap();
4993 group.finish().unwrap();
4994 source.finish().unwrap();
4995
4996 let mut subscription = subscribing.await.unwrap().expect("subscribe survives the retraction");
4997 let mut payloads = Vec::new();
4998 while let Some(mut group) = subscription.recv_group().await.expect("ends cleanly") {
4999 payloads.push(group.read_frame().await.unwrap().unwrap().payload);
5000 }
5001 assert_eq!(payloads.last().map(|p| &p[..]), Some(&b"late"[..]));
5002 }
5003
5004 #[tokio::test]
5007 async fn reannounce_before_the_front_acts_keeps_it() {
5008 let producer = origin(1).produce();
5009 let consumer = producer.consume();
5010
5011 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
5012 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5013
5014 broadcast.unannounce();
5015 broadcast.announce(Route::default()).unwrap();
5016 for _ in 0..20 {
5017 tokio::task::yield_now().await;
5018 }
5019 assert!(!resolved.is_closed(), "the front ended across a reannouncement");
5020 let again = consumer.request_broadcast("room/alice").await.expect("resolves");
5021 assert!(again.is_clone(&resolved));
5022 }
5023
5024 #[tokio::test]
5025 async fn local_broadcast_resolves_once_announced() {
5026 let producer = origin(1).produce();
5027 let consumer = producer.consume();
5028
5029 let broadcast = producer.create_broadcast("room/alice").unwrap();
5031 let err = consumer
5032 .request_broadcast("room/alice")
5033 .now_or_never()
5034 .expect("unroutable is synchronous")
5035 .err()
5036 .unwrap();
5037 assert!(matches!(err, Error::Unroutable));
5038
5039 broadcast.announce(Route::default()).unwrap();
5040 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
5041 assert_eq!(resolved.info().path.as_str(), "room/alice");
5042 drop(broadcast);
5043
5044 let err = consumer
5046 .request_broadcast("room/bob")
5047 .now_or_never()
5048 .expect("unroutable is synchronous")
5049 .err()
5050 .unwrap();
5051 assert!(matches!(err, Error::Unroutable));
5052 }
5053
5054 #[test]
5055 fn create_broadcast_accepts_a_max_depth_path() {
5056 let producer = origin(1).produce();
5057 let path = vec!["a"; Path::MAX_PARTS].join("/");
5058 let _broadcast = producer.create_broadcast(path.as_str()).expect("max depth is allowed");
5059 let deeper = vec!["a"; Path::MAX_PARTS + 1].join("/");
5060 assert!(matches!(
5061 producer.create_broadcast(deeper.as_str()),
5062 Err(Error::BoundsExceeded(_))
5063 ));
5064 }
5065
5066 #[tokio::test]
5067 async fn duplicate_routes_aggregate_until_the_last_leaves() {
5068 let producer = origin(1).produce();
5069 let first = producer.dynamic("live", Route::default().with_cost(3)).unwrap();
5070 let second = producer.dynamic("live", Route::default().with_cost(1)).unwrap();
5071
5072 let mut announced = producer.consume().announced();
5073 let update = announced.next().now_or_never().expect("next").expect("no next");
5074 assert_eq!(update.prefix.as_str(), "live");
5075 assert_eq!(update.kind, AnnounceKind::Announced);
5076 assert_eq!(update.route.cost, Cost::new(1));
5077 announced.assert_next_wait();
5078
5079 drop(second);
5080 let update = announced.next().now_or_never().expect("next").expect("no next");
5081 assert_eq!(update.prefix.as_str(), "live");
5082 assert_eq!(update.kind, AnnounceKind::Updated);
5083 assert_eq!(update.route.cost, Cost::new(3));
5084
5085 drop(first);
5086 announced.assert_next_ended("live");
5087 announced.assert_next_wait();
5088 }
5089
5090 #[test]
5091 fn dynamic_may_cover_a_scope_but_disjoint_prefixes_are_refused() {
5092 let producer = origin(1).produce();
5093 let scoped = producer.scope("", &scopes(&["room"])).unwrap();
5094 let _broad = scoped
5095 .dynamic("", Route::default())
5096 .expect("an overlapping prefix is accepted");
5097
5098 let _ok = scoped
5099 .dynamic("room/alice", Route::default())
5100 .expect("a contained prefix is accepted");
5101 assert!(matches!(
5102 scoped.dynamic("other", Route::default()),
5103 Err(Error::Unauthorized)
5104 ));
5105 }
5106
5107 #[tokio::test]
5108 async fn dynamic_route_keeps_its_producer_scope() {
5109 let producer = origin(1).produce();
5110 let scope = Patterns::from("*/chat".parse::<Pattern>().unwrap());
5111 let scoped = producer.scope("", &scope).unwrap();
5112 let dynamic = scoped.dynamic("", Route::default()).unwrap();
5113
5114 let mut matching = producer
5115 .consume()
5116 .scope("", &scopes(&["room/chat"]))
5117 .unwrap()
5118 .announced();
5119 matching.assert_next_active("");
5120 let mut outside = producer
5121 .consume()
5122 .scope("", &scopes(&["room/video"]))
5123 .unwrap()
5124 .announced();
5125 outside.assert_next_wait();
5126
5127 let refused = producer
5128 .consume()
5129 .request_broadcast("room/video")
5130 .now_or_never()
5131 .expect("an out-of-scope request must be refused synchronously");
5132 assert!(matches!(refused, Err(Error::Unroutable)));
5133 assert!(dynamic.requested_broadcast().now_or_never().is_none());
5134
5135 let _pending = producer.consume().request_broadcast("room/chat");
5136 let request = queued(&dynamic).await;
5137 assert_eq!(request.path().as_str(), "room/chat");
5138 }
5139
5140 #[tokio::test]
5141 async fn dynamic_accepts_a_max_depth_prefix() {
5142 let producer = origin(1).produce();
5143 let path = (0..Path::MAX_PARTS)
5144 .map(|i| format!("s{i}"))
5145 .collect::<Vec<_>>()
5146 .join("/");
5147 let mut announced = producer.consume().announced();
5148
5149 let dynamic = producer.dynamic(&path, Route::default()).expect("max depth is allowed");
5150 announced.assert_next_active(&path);
5151
5152 let _pending = producer.consume().request_broadcast(&path);
5153 let request = queued(&dynamic).await;
5154 assert_eq!(request.path().as_str(), path);
5155 }
5156
5157 #[tokio::test]
5158 async fn dynamic_exclusion_skips_routes_through_the_subscriber() {
5159 let producer = origin(1).produce();
5160 let _server = producer
5161 .dynamic("live", Route::default().with_hops(hops(&[7])))
5162 .unwrap();
5163
5164 let mut excluded = producer.consume().excluding(origin(7)).announced();
5165 excluded.assert_next_wait();
5166
5167 let mut clean = producer.consume().excluding(origin(8)).announced();
5168 clean.assert_next_active("live");
5169 }
5170
5171 #[tokio::test]
5173 async fn announce_consumer_is_a_stream() {
5174 use futures::StreamExt;
5175 let producer = origin(1).produce();
5176 let server = producer.dynamic("live", Route::default()).unwrap();
5177 let mut announced = producer.consume().announced();
5178 let update = StreamExt::next(&mut announced)
5179 .now_or_never()
5180 .expect("next")
5181 .expect("no next");
5182 assert_eq!(update.prefix.as_str(), "live");
5183 assert_eq!(update.kind, AnnounceKind::Announced);
5184 assert!(StreamExt::next(&mut announced).now_or_never().is_none());
5185 drop(server);
5186 let update = StreamExt::next(&mut announced)
5187 .now_or_never()
5188 .expect("next")
5189 .expect("no next");
5190 assert_eq!(update.kind, AnnounceKind::Retracted);
5191 }
5192
5193 #[tokio::test]
5194 async fn dynamic_retracts() {
5195 let producer = origin(1).produce();
5196 let server = producer.dynamic("live", Route::default()).unwrap();
5197 let mut announced = producer.consume().announced();
5198 announced.assert_next_active("live");
5199
5200 drop(server);
5201 announced.assert_next_ended("live");
5202 }
5203
5204 #[test]
5205 fn charged_wildcard_cost_accumulates_across_hops() {
5206 let first = Cost::new(4).charged(1);
5207 let second = first.charged(2);
5208 assert_eq!(second, Cost { warm: 7, cold: 7 });
5209 }
5210
5211 #[tokio::test]
5212 async fn local_broadcast_is_invisible_until_announced() {
5213 let producer = origin(1).produce();
5214 let mut local = producer.consume().announced();
5215 let mut peer = producer.consume().excluding(Hop::UNKNOWN).announced();
5216 let broadcast = producer.create_broadcast("room/alice").unwrap();
5217 local.assert_next_wait();
5218 peer.assert_next_wait();
5219
5220 broadcast.announce(Route::default()).unwrap();
5221 local.assert_next_active("room/alice");
5222 peer.assert_next_active("room/alice");
5223
5224 drop(broadcast);
5225 local.assert_next_ended("room/alice");
5226 peer.assert_next_ended("room/alice");
5227 }
5228
5229 #[tokio::test]
5230 async fn served_route_materializes_on_demand() {
5231 let producer = origin(1).produce();
5232 let consumer = producer.consume();
5233
5234 let server = producer.dynamic("room", Route::default()).unwrap();
5235
5236 let pending = consumer.request_broadcast("room/alice");
5237 let request = queued(&server).await;
5238 assert_eq!(request.path().as_str(), "room/alice");
5239
5240 let source = broadcast::Info::new().produce();
5241 request.accept(&source);
5242
5243 let resolved = pending.await.expect("resolves");
5244 assert_eq!(resolved.info().path.as_str(), "room/alice");
5246
5247 let again = consumer.request_broadcast("room/alice").await.expect("resolves");
5249 assert!(again.is_clone(&resolved));
5250 }
5251
5252 #[tokio::test]
5253 async fn served_requests_coalesce() {
5254 let producer = origin(1).produce();
5255 let consumer = producer.consume();
5256 let server = producer.dynamic("room", Route::default()).unwrap();
5257
5258 let first = consumer.request_broadcast("room/alice");
5259 let second = consumer.request_broadcast("room/alice");
5260
5261 let request = queued(&server).await;
5262 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5264
5265 let source = broadcast::Info::new().produce();
5266 request.accept(&source);
5267
5268 let first = first.await.expect("resolves");
5269 let second = second.await.expect("resolves");
5270 assert!(first.is_clone(&second));
5271 }
5272
5273 #[tokio::test]
5274 async fn retract_rejects_pending_requests() {
5275 let producer = origin(1).produce();
5276 let consumer = producer.consume();
5277 let server = producer.dynamic("room", Route::default()).unwrap();
5278
5279 let pending = consumer.request_broadcast("room/alice");
5280 drop(server);
5281
5282 let err = pending.await.err().unwrap();
5283 assert!(matches!(err, Error::Unroutable));
5284
5285 let err = consumer
5287 .request_broadcast("room/alice")
5288 .now_or_never()
5289 .expect("unroutable")
5290 .err()
5291 .unwrap();
5292 assert!(matches!(err, Error::Unroutable));
5293 }
5294
5295 #[tokio::test]
5296 async fn routed_broadcast_survives_serving_route_retraction() {
5297 let producer = origin(1).produce();
5298 let consumer = producer.consume();
5299
5300 let standby_server = producer.dynamic("room", Route::default()).unwrap();
5303 let second_server = producer.dynamic("room", Route::default()).unwrap();
5304 let incumbent_server = producer.dynamic("room", Route::default()).unwrap();
5305
5306 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5307 assert!((&mut resolving).now_or_never().is_none());
5308
5309 drop(incumbent_server);
5315 assert!((&mut resolving).now_or_never().is_none());
5316 drop(second_server);
5317 assert!((&mut resolving).now_or_never().is_none());
5318
5319 let request = queued(&standby_server).await;
5320 let source = broadcast::Info::new().produce();
5321 request.accept(&source);
5322
5323 let resolved = resolving.await.expect("resolves via the standby");
5324 assert_eq!(resolved.info().path.as_str(), "room/alice");
5325 }
5326
5327 #[tokio::test]
5328 async fn split_horizon_skips_routes_through_the_requester() {
5329 let producer = origin(1).produce();
5330 let _server = producer
5331 .dynamic("room", Route::default().with_hops(hops(&[7])))
5332 .unwrap();
5333
5334 let excluded = producer.consume().excluding(origin(7));
5336 let err = excluded
5337 .request_broadcast("room/alice")
5338 .now_or_never()
5339 .expect("unroutable")
5340 .err()
5341 .unwrap();
5342 assert!(matches!(err, Error::Unroutable));
5343
5344 let clean = producer.consume().excluding(origin(8));
5346 let pending = clean.request_broadcast("room/alice");
5347 assert!(pending.now_or_never().is_none());
5348 }
5349
5350 #[tokio::test]
5351 async fn routes_report_where_they_entered() {
5352 let producer = origin(1).produce();
5353 let peer = producer.clone().peer();
5354 let mut announced = producer.consume().announced();
5355
5356 let _ingest = producer
5357 .dynamic("client", Route::default().with_hops(hops(&[5])).with_via(origin(5)))
5358 .unwrap();
5359 let _gateway = producer.publish("gateway", Route::default()).unwrap();
5360 let _forwarded = peer
5361 .dynamic(
5362 "forwarded",
5363 Route::default().with_hops(hops(&[5, 7])).with_via(origin(7)),
5364 )
5365 .unwrap();
5366
5367 assert_eq!(announced.assert_next_active("client").source(), Source::Local);
5368 assert_eq!(
5369 announced.assert_next_active("forwarded").source(),
5370 Source::Peer(origin(7))
5371 );
5372 assert_eq!(announced.assert_next_active("gateway").source(), Source::Local);
5373
5374 let scoped = peer.scope("room", &Patterns::from(Pattern::all())).unwrap();
5376 let _nested = scoped.dynamic("x", Route::default().with_via(origin(8))).unwrap();
5377 assert_eq!(announced.assert_next_active("room/x").source(), Source::Peer(origin(8)));
5378 }
5379
5380 #[tokio::test]
5383 async fn source_change_is_an_update() {
5384 let producer = origin(1).produce();
5385 let peer = producer.clone().peer();
5386 let mut announced = producer.consume().announced();
5387
5388 let route = Route::default().with_hops(hops(&[7])).with_via(origin(7));
5389 let _forwarded = peer.dynamic("room", route.clone()).unwrap();
5390 assert_eq!(announced.assert_next_active("room").source(), Source::Peer(origin(7)));
5391
5392 let local = producer.dynamic("room", route).unwrap();
5394 let update = announced.next().now_or_never().expect("next blocked").expect("no next");
5395 assert_eq!(update.kind, AnnounceKind::Updated);
5396 assert_eq!(update.route.source(), Source::Local);
5397
5398 drop(local);
5399 assert_eq!(announced.assert_next_active("room").source(), Source::Peer(origin(7)));
5400 }
5401
5402 #[tokio::test]
5403 async fn local_view_hides_peer_routes() {
5404 let producer = origin(1).produce();
5405 let peer = producer.clone().peer();
5406 let mut local = producer.consume().local().announced();
5407
5408 let _forwarded = peer
5409 .dynamic("remote", Route::default().with_hops(hops(&[7])).with_via(origin(7)))
5410 .unwrap();
5411 local.assert_next_wait();
5412
5413 let _shadow = peer
5417 .dynamic("both", Route::default().with_hops(hops(&[7])).with_via(origin(7)))
5418 .unwrap();
5419 let ingest = producer
5420 .dynamic(
5421 "both",
5422 Route::default().with_hops(hops(&[5])).with_via(origin(5)).with_cost(9),
5423 )
5424 .unwrap();
5425 assert_eq!(local.assert_next_active("both").source(), Source::Local);
5426 drop(ingest);
5427 local.assert_next_ended("both");
5428
5429 let err = producer
5432 .consume()
5433 .local()
5434 .request_broadcast("remote/alice")
5435 .now_or_never()
5436 .expect("unroutable")
5437 .err()
5438 .unwrap();
5439 assert!(matches!(err, Error::Unroutable));
5440 assert!(
5441 producer
5442 .consume()
5443 .request_broadcast("remote/alice")
5444 .now_or_never()
5445 .is_none()
5446 );
5447 }
5448
5449 #[tokio::test]
5453 async fn handler_rejection_is_final() {
5454 let producer = origin(1).produce();
5455 let consumer = producer.consume();
5456 let server = producer.dynamic("room", Route::default()).unwrap();
5457
5458 let pending = consumer.request_broadcast("room/alice");
5459 let request = queued(&server).await;
5460 request.reject(Error::Unroutable);
5461 let err = tokio::time::timeout(Duration::from_secs(5), pending)
5462 .await
5463 .expect("the front must give up, not spin")
5464 .err()
5465 .unwrap();
5466 assert!(matches!(err, Error::Unroutable));
5467
5468 let pending = consumer.request_broadcast("room/bob");
5470 let request = queued(&server).await;
5471 assert_eq!(request.path().as_str(), "room/bob");
5472 let served = broadcast::Info::new().produce();
5473 request.accept(&served);
5474 pending.await.expect("resolves");
5475 }
5476
5477 #[tokio::test]
5480 async fn routed_broadcast_waits_out_a_rejection() {
5481 let producer = origin(1).produce();
5482 let consumer = producer.consume();
5483 let server = producer.dynamic("room", Route::default()).unwrap();
5484
5485 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5486 assert!((&mut resolving).now_or_never().is_none());
5487 let request = queued(&server).await;
5488 request.reject(Error::Unroutable);
5489
5490 for _ in 0..20 {
5492 tokio::task::yield_now().await;
5493 }
5494 assert!((&mut resolving).now_or_never().is_none());
5495 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5496
5497 server.update(Route::default().with_cost(2)).unwrap();
5499 assert!((&mut resolving).now_or_never().is_none());
5500 let request = queued(&server).await;
5501 let served = broadcast::Info::new().produce();
5502 request.accept(&served);
5503 resolving.await.expect("resolves");
5504 }
5505
5506 #[tokio::test]
5509 async fn routed_broadcast_reports_teardown_as_closed() {
5510 let (producer, driver) = Producer::new(Config::new(origin(1)));
5511 let consumer = producer.consume();
5512 let _server = producer.dynamic("room", Route::default()).unwrap();
5513
5514 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5516 assert!((&mut resolving).now_or_never().is_none());
5517
5518 drop(driver);
5519
5520 let err = tokio::time::timeout(Duration::from_secs(5), resolving)
5521 .await
5522 .expect("teardown resolves the wait")
5523 .err()
5524 .unwrap();
5525 assert!(matches!(err, Error::Closed), "unexpected end: {err}");
5526 }
5527
5528 #[tokio::test]
5531 async fn routed_broadcast_wakes_for_a_local_broadcast() {
5532 let producer = origin(1).produce();
5533 let consumer = producer.consume();
5534 let server = producer.dynamic("room", Route::default()).unwrap();
5535
5536 let mut resolving = Box::pin(consumer.routed_broadcast("room/alice"));
5537 assert!((&mut resolving).now_or_never().is_none());
5538 queued(&server).await.reject(Error::Unroutable);
5539 for _ in 0..20 {
5540 tokio::task::yield_now().await;
5541 }
5542 assert!((&mut resolving).now_or_never().is_none());
5543
5544 let _local = producer.publish("room/alice", Route::default()).unwrap();
5546 let resolved = resolving.await.expect("resolves locally");
5547 assert_eq!(resolved.info().path.as_str(), "room/alice");
5548 assert!(server.poll_requested_broadcast(&kio::Waiter::noop()).is_pending());
5549 }
5550
5551 #[tokio::test]
5554 async fn late_track_on_a_served_front_replays() {
5555 let producer = origin(1).produce();
5556 let consumer = producer.consume();
5557 let server = producer.dynamic("room", Route::default()).unwrap();
5558
5559 let source = broadcast::Info::new().produce();
5560 for name in ["a", "b"] {
5561 let track = source.create_track(name, None).unwrap();
5562 let mut group = track.append_group().unwrap();
5563 group.write_frame(crate::Timestamp::ZERO, name.as_bytes()).unwrap();
5564 group.finish().unwrap();
5565 std::mem::forget(track);
5567 }
5568
5569 let pending = consumer.request_broadcast("room/alice");
5570 queued(&server).await.accept(&source);
5571 let resolved = pending.await.expect("resolves");
5572
5573 let budget = track::Subscription::default().with_max_age(Duration::from_secs(3600));
5574 for name in ["a", "b"] {
5575 let mut subscription = resolved
5576 .track(name)
5577 .unwrap()
5578 .subscribe(budget.clone())
5579 .await
5580 .expect("subscribe");
5581 let mut group = tokio::time::timeout(Duration::from_secs(5), subscription.recv_group())
5582 .await
5583 .expect("the late track must replay, not park")
5584 .expect("recv group")
5585 .expect("track ended early");
5586 let frame = group.read_frame().await.expect("read frame").expect("frame");
5587 assert_eq!(&frame.payload[..], name.as_bytes());
5588 }
5589 }
5590
5591 #[tokio::test]
5592 async fn most_specific_prefix_shadows() {
5593 let producer = origin(1).produce();
5594 let consumer = producer.consume();
5595
5596 let broad_server = producer.dynamic("", Route::default()).unwrap();
5597 let _narrow = producer.announce(".dash", Route::default()).unwrap();
5600
5601 let err = consumer
5602 .request_broadcast(".dash/pid")
5603 .now_or_never()
5604 .expect("unroutable")
5605 .err()
5606 .unwrap();
5607 assert!(matches!(err, Error::Unroutable));
5608
5609 let _pending = consumer.request_broadcast("room/alice");
5611 let request = queued(&broad_server).await;
5612 assert_eq!(request.path().as_str(), "room/alice");
5613 }
5614
5615 #[tokio::test]
5616 async fn root_dynamic_serves_any_path() {
5617 let producer = origin(1).produce();
5618 let consumer = producer.consume();
5619 let mut announced = consumer.announced();
5620 let dynamic = producer.dynamic("", Route::default()).unwrap();
5621 announced.assert_next_active("");
5623
5624 let pending = consumer.request_broadcast("anything/at/all");
5625 let request = queued(&dynamic).await;
5626 assert_eq!(request.path().as_str(), "anything/at/all");
5627
5628 let source = broadcast::Info::new().produce();
5629 request.accept(&source);
5630 let resolved = pending.await.expect("resolves");
5631 assert_eq!(resolved.info().path.as_str(), "anything/at/all");
5632
5633 drop(dynamic);
5635 announced.assert_next_ended("");
5636 let err = consumer
5637 .request_broadcast("something/else")
5638 .now_or_never()
5639 .expect("unroutable")
5640 .err()
5641 .unwrap();
5642 assert!(matches!(err, Error::Unroutable));
5643 }
5644
5645 #[tokio::test]
5651 async fn out_of_scope_request_never_reaches_the_dynamic_handler() {
5652 let producer = origin(1).produce();
5653 let dynamic = producer.dynamic("", Route::default()).unwrap();
5654 let scoped = producer.consume().scope("", &scopes(&["tenant-a"])).unwrap();
5655
5656 for path in ["tenant-b/live", "tenant-a-other/live"] {
5659 let refused = scoped
5660 .request_broadcast(path)
5661 .now_or_never()
5662 .expect("an out-of-scope request must be refused synchronously, not queued");
5663 assert!(matches!(refused, Err(Error::Unauthorized)));
5664 assert!(
5665 dynamic.requested_broadcast().now_or_never().is_none(),
5666 "the dynamic handler was asked to create a broadcast the requester may not read"
5667 );
5668 }
5669 }
5670
5671 #[tokio::test]
5672 async fn routed_waits_for_coverage() {
5673 let producer = origin(1).produce();
5674 let consumer = producer.consume();
5675
5676 let mut fut = consumer.routed("room/alice").boxed();
5677 assert!((&mut fut).now_or_never().is_none());
5678
5679 let _a = producer.announce("room", Route::default().with_cost(3)).unwrap();
5681 let route = fut.now_or_never().expect("covered").expect("routed");
5682 assert_eq!(route.cost, Cost::new(3));
5683
5684 consumer
5686 .routed("room/alice/cam")
5687 .now_or_never()
5688 .expect("covered")
5689 .expect("routed");
5690 }
5691
5692 #[tokio::test]
5693 async fn routed_ignores_deeper_routes() {
5694 let producer = origin(1).produce();
5695 let consumer = producer.consume();
5696
5697 let _deep = producer.announce("room/alice/cam", Route::default()).unwrap();
5699 let mut fut = consumer.routed("room/alice").boxed();
5700 assert!((&mut fut).now_or_never().is_none());
5701
5702 let _exact = producer.announce("room/alice", Route::default()).unwrap();
5703 fut.now_or_never().expect("covered").expect("routed");
5704 }
5705
5706 #[tokio::test]
5707 async fn routed_accepts_a_max_depth_path() {
5708 let producer = origin(1).produce();
5709 let consumer = producer.consume();
5710 let path = (0..Path::MAX_PARTS)
5711 .map(|i| format!("s{i}"))
5712 .collect::<Vec<_>>()
5713 .join("/");
5714 assert_eq!(Path::new(&path).parts().count(), Path::MAX_PARTS);
5715
5716 assert!(consumer.allowed().matches(&path));
5717
5718 let mut fut = consumer.routed(&path).boxed();
5719 assert!((&mut fut).now_or_never().is_none());
5720
5721 let _a = producer.announce("", Route::default()).unwrap();
5723 fut.now_or_never().expect("covered").expect("routed");
5724 }
5725
5726 #[tokio::test]
5727 async fn teardown_ends_everything() {
5728 let (producer, driver) = Producer::new(Config::new(origin(1)));
5729 let consumer = producer.consume();
5730 let _announcement = producer.announce("room", Route::default()).unwrap();
5731 let mut announced = consumer.announced();
5732 announced.assert_next_active("room");
5733
5734 let _server = producer.dynamic("served", Route::default()).unwrap();
5735 let pending = consumer.request_broadcast("served/path");
5736
5737 drop(driver);
5738
5739 announced.assert_next_active("served");
5741 assert!(announced.next().now_or_never().expect("ended").is_none());
5742
5743 assert!(pending.now_or_never().expect("rejected").is_err());
5745 assert!(matches!(producer.announce("x", Route::default()), Err(Error::Closed)));
5746 assert!(matches!(producer.create_broadcast("x"), Err(Error::Closed)));
5747 let err = consumer
5748 .request_broadcast("y")
5749 .now_or_never()
5750 .expect("closed")
5751 .err()
5752 .unwrap();
5753 assert!(matches!(err, Error::Closed));
5754
5755 let mut late = consumer.announced();
5757 assert!(late.next().now_or_never().expect("ended").is_none());
5758 }
5759
5760 struct ResumeRig {
5763 producer: Producer,
5764 resolved: broadcast::Consumer,
5765 subscription: track::Subscriber,
5766 incumbent_track: track::Producer,
5769 }
5770
5771 impl ResumeRig {
5772 async fn new(first: &[u64]) -> (Self, Dynamic, broadcast::Producer) {
5775 let producer = origin(1).produce();
5776 let consumer = producer.consume();
5777
5778 let server = producer
5779 .dynamic("room", Route::default().with_hops(hops(first)))
5780 .unwrap();
5781
5782 let pending = consumer.request_broadcast("room/alice");
5783 let request = queued(&server).await;
5784 let source = broadcast::Info::new().produce();
5785 let track = source.create_track("video", None).unwrap();
5786 let mut group = track.append_group().unwrap();
5787 group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5788 group.finish().unwrap();
5789 request.accept(&source);
5790
5791 let resolved = pending.await.expect("resolves");
5792 let mut subscription = resolved
5793 .track("video")
5794 .unwrap()
5795 .subscribe(None)
5796 .await
5797 .expect("subscribe");
5798 let mut group = subscription
5799 .recv_group()
5800 .await
5801 .expect("recv group")
5802 .expect("track ended early");
5803 let frame = group.read_frame().await.expect("read frame").expect("frame");
5804 assert_eq!(&frame.payload[..], b"before");
5805
5806 (
5807 Self {
5808 producer,
5809 resolved,
5810 subscription,
5811 incumbent_track: track,
5812 },
5813 server,
5814 source,
5815 )
5816 }
5817
5818 fn standby(&self, first: &[u64]) -> Dynamic {
5821 self.producer
5822 .dynamic("room", Route::default().with_hops(hops(first)))
5823 .unwrap()
5824 }
5825 }
5826
5827 async fn assert_resumes(rig: &mut ResumeRig, server: &Dynamic) {
5832 let request = queued(server).await;
5833 let replacement = broadcast::Info::new().produce();
5834 let track = replacement.create_track("video", None).unwrap();
5835 let mut group = track.append_group().unwrap();
5838 group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5839 group.finish().unwrap();
5840 request.accept(&replacement);
5841
5842 let mut group = track.append_group().unwrap();
5843 group.write_frame(crate::Timestamp::ZERO, b"resumed".as_ref()).unwrap();
5844 group.finish().unwrap();
5845
5846 let mut group = rig
5847 .subscription
5848 .recv_group()
5849 .await
5850 .expect("subscription survives the failover")
5851 .expect("track ended early");
5852 let frame = group.read_frame().await.expect("read frame").expect("frame");
5853 assert_eq!(&frame.payload[..], b"resumed");
5854 }
5855
5856 #[tokio::test]
5859 async fn driver_resolves_with_live_consumers() {
5860 let (producer, driver) = Producer::new(Config::new(origin(1)));
5861 let consumer = producer.consume();
5862 let run = crate::time::run(driver);
5863 drop(producer);
5864 tokio::time::timeout(Duration::from_secs(5), run)
5865 .await
5866 .expect("driver must finish once the producers are gone");
5867 drop(consumer);
5868 }
5869
5870 #[tokio::test]
5871 async fn remote_source_resumes_through_same_first_hop() {
5872 let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5873 let standby_server = rig.standby(&[10, 20]);
5874
5875 drop(incumbent);
5877 drop(source);
5878
5879 assert_resumes(&mut rig, &standby_server).await;
5881 }
5882
5883 #[tokio::test]
5887 async fn incompatible_successor_is_refused() {
5888 for replacement in [
5889 track::Info::default().with_timescale(crate::Timescale::MICRO),
5890 track::Info::default().with_priority(7),
5891 track::Info::default().with_max_age(Duration::from_secs(7)),
5892 ] {
5893 let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5894 let standby_server = rig.standby(&[10, 20]);
5895 drop(incumbent);
5896 drop(source);
5897
5898 let request = queued(&standby_server).await;
5901 let successor = broadcast::Info::new().produce();
5902 let track = successor.create_track("video", replacement).unwrap();
5903 let mut group = track.append_group().unwrap();
5904 group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
5905 group.finish().unwrap();
5906 request.accept(&successor);
5907
5908 assert!(
5909 matches!(rig.subscription.recv_group().await, Err(Error::Unsupported)),
5910 "the subscription must abort rather than resume onto incompatible metadata"
5911 );
5912
5913 let reopened = rig.resolved.track("video").unwrap();
5915 assert!(matches!(reopened.query().await, Err(Error::Unsupported)));
5916 assert!(matches!(reopened.subscribe(None).await, Err(Error::Unsupported)));
5917 }
5918 }
5919
5920 #[tokio::test]
5921 async fn different_first_hop_ends_the_subscription() {
5922 let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
5923 let rival_server = rig.standby(&[11]);
5925
5926 drop(incumbent);
5929 drop(source);
5930 rig.incumbent_track.abort(Error::Dropped).unwrap();
5931
5932 let err = rig.subscription.recv_group().await.err().expect("subscription ends");
5934 assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5935
5936 let consumer = rig.producer.consume();
5938 let pending = consumer.request_broadcast("room/alice");
5939 let request = queued(&rival_server).await;
5940 let replacement = broadcast::Info::new().produce();
5941 request.accept(&replacement);
5942 pending.await.expect("re-request resolves through the rival");
5943 }
5944
5945 #[tokio::test]
5946 async fn anonymous_routes_never_resume() {
5947 let (mut rig, incumbent, source) = ResumeRig::new(&[]).await;
5950 let _twin_server = rig.standby(&[]);
5951
5952 drop(incumbent);
5953 drop(source);
5954 rig.incumbent_track.abort(Error::Dropped).unwrap();
5955
5956 let err = rig.subscription.recv_group().await.err().expect("subscription ends");
5957 assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
5958 }
5959
5960 #[tokio::test]
5967 async fn anonymous_handoff_serves_the_newcomer_immediately() {
5968 let producer = origin(1).produce();
5969
5970 let server_a = producer
5972 .dynamic("room", Route::default().with_hops(hops(&[10])))
5973 .unwrap();
5974
5975 let consumer = producer.consume().excluding(origin(30));
5978 let pending = consumer.request_broadcast("room/alice");
5979 let request = queued(&server_a).await;
5980 let source_a = broadcast::Info::new().produce();
5981 let track_a = source_a.create_track("video", None).unwrap();
5982 let mut group = track_a.append_group().unwrap();
5983 group.write_frame(crate::Timestamp::ZERO, b"from-a".as_ref()).unwrap();
5984 group.finish().unwrap();
5985 request.accept(&source_a);
5986
5987 let resolved_a = pending.await.expect("resolves");
5988 let mut sub_a = resolved_a
5989 .track("video")
5990 .unwrap()
5991 .subscribe(None)
5992 .await
5993 .expect("subscribe");
5994 let mut group = sub_a
5995 .recv_group()
5996 .await
5997 .expect("recv group")
5998 .expect("track ended early");
5999 assert_eq!(
6000 &group.read_frame().await.expect("read frame").expect("frame").payload[..],
6001 b"from-a"
6002 );
6003
6004 drop(track_a);
6007 drop(source_a);
6008 drop(server_a);
6009
6010 let err = sub_a.recv_group().await.err().expect("front closed");
6012 assert!(matches!(err, Error::Dropped), "unexpected end: {err}");
6013
6014 settle(|| consumer.get_broadcast("room/alice").is_none()).await;
6018 settle(|| {
6019 matches!(
6020 consumer.request_broadcast("room/alice").now_or_never(),
6021 Some(Err(Error::Unroutable))
6022 )
6023 })
6024 .await;
6025
6026 let server_b = producer
6028 .dynamic("room", Route::default().with_hops(hops(&[20])))
6029 .unwrap();
6030 let pending = consumer.request_broadcast("room/alice");
6031 let request = queued(&server_b).await;
6032 let source_b = broadcast::Info::new().produce();
6033 let track_b = source_b.create_track("video", None).unwrap();
6034 let mut group = track_b.append_group().unwrap();
6035 group.write_frame(crate::Timestamp::ZERO, b"from-b".as_ref()).unwrap();
6036 group.finish().unwrap();
6037 request.accept(&source_b);
6038
6039 let resolved_b = pending.await.expect("B's front is served immediately");
6040 assert!(
6041 !resolved_b.is_clone(&resolved_a),
6042 "B must not splice into A's closed front"
6043 );
6044
6045 let mut sub_b = resolved_b
6046 .track("video")
6047 .unwrap()
6048 .subscribe(None)
6049 .await
6050 .expect("subscribe");
6051 let mut group = sub_b
6052 .recv_group()
6053 .await
6054 .expect("recv group")
6055 .expect("track ended early");
6056 assert_eq!(
6057 &group.read_frame().await.expect("read frame").expect("frame").payload[..],
6058 b"from-b"
6059 );
6060 }
6061
6062 #[tokio::test]
6063 async fn reprice_is_invisible_to_the_subscription() {
6064 let (rig, incumbent, source) = ResumeRig::new(&[10]).await;
6065
6066 incumbent
6069 .update(Route::default().with_hops(hops(&[10])).with_cost(9))
6070 .unwrap();
6071
6072 let track = source.create_track("audio", None).unwrap();
6073 let mut group = track.append_group().unwrap();
6074 group.write_frame(crate::Timestamp::ZERO, b"steady".as_ref()).unwrap();
6075 group.finish().unwrap();
6076
6077 let mut audio = rig
6078 .resolved
6079 .track("audio")
6080 .unwrap()
6081 .subscribe(None)
6082 .await
6083 .expect("subscribe survives the reprice");
6084 let mut group = audio
6085 .recv_group()
6086 .await
6087 .expect("recv group")
6088 .expect("track ended early");
6089 let frame = group.read_frame().await.expect("read frame").expect("frame");
6090 assert_eq!(&frame.payload[..], b"steady");
6091 }
6092
6093 #[tokio::test]
6094 async fn drain_reprice_migrates_before_the_session_dies() {
6095 let (mut rig, incumbent, source) = ResumeRig::new(&[10]).await;
6096 let standby_server = rig.standby(&[10, 20]);
6097
6098 incumbent
6102 .update(Route::default().with_hops(hops(&[10])).with_cost(Cost::DRAIN))
6103 .unwrap();
6104
6105 assert_resumes(&mut rig, &standby_server).await;
6106
6107 drop(incumbent);
6109 drop(source);
6110 }
6111
6112 #[tokio::test]
6113 async fn local_sources_splice_newest_first() {
6114 let producer = origin(1).produce();
6115 let consumer = producer.consume();
6116
6117 let first = producer.publish("room/alice", Route::default()).unwrap();
6118 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6119
6120 let second = producer.publish("room/alice", Route::default()).unwrap();
6122 let again = consumer.request_broadcast("room/alice").await.expect("resolves");
6123 assert!(again.is_clone(&resolved));
6124
6125 first.finish();
6127 settle(|| consumer.get_broadcast("room/alice").is_some()).await;
6128 second.finish();
6129 settle(|| consumer.get_broadcast("room/alice").is_none()).await;
6130
6131 let _third = producer.publish("room/alice", Route::default()).unwrap();
6133 assert!(consumer.get_broadcast("room/alice").is_some());
6134 }
6135
6136 #[tokio::test]
6144 async fn a_finished_broadcast_concludes_in_flight_subscriptions() {
6145 let producer = origin(1).produce();
6146 let consumer = producer.consume();
6147
6148 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
6149 let track = broadcast.create_track("video", None).unwrap();
6150
6151 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6152 let mut subscription = resolved
6153 .track("video")
6154 .unwrap()
6155 .subscribe(None)
6156 .await
6157 .expect("subscribe");
6158 let mut group = track.append_group().unwrap();
6160 group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
6161 group.finish().unwrap();
6162 track.finish().unwrap();
6163 drop(track);
6164 broadcast.finish();
6165
6166 let mut group = next_group(&mut subscription)
6167 .await
6168 .expect("a cleanly finished track was served as an error")
6169 .expect("the track ended before its last group");
6170 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
6171 drop(group);
6172
6173 let end = next_group(&mut subscription)
6174 .await
6175 .expect("a cleanly finished track ended as an error");
6176 assert!(end.is_none(), "a group followed the final one");
6177 }
6178
6179 #[tokio::test]
6186 async fn a_retracted_route_concludes_in_flight_subscriptions() {
6187 let producer = origin(1).produce();
6188 let consumer = producer.consume();
6189 let server = producer
6190 .dynamic("room", Route::default().with_hops(hops(&[10])))
6191 .unwrap();
6192
6193 let pending = consumer.request_broadcast("room/alice");
6194 let request = queued(&server).await;
6195 let source = broadcast::Info::new().produce();
6196 let track = source.create_track("video", None).unwrap();
6197 request.accept(&source);
6198
6199 let resolved = pending.await.expect("resolves");
6200 let mut subscription = resolved
6201 .track("video")
6202 .unwrap()
6203 .subscribe(None)
6204 .await
6205 .expect("subscribe");
6206
6207 source.finish();
6210 drop(server);
6211 settle(|| resolved.is_closed()).await;
6212 let mut group = track.append_group().unwrap();
6213 group.write_frame(crate::Timestamp::ZERO, b"tail".as_ref()).unwrap();
6214 group.finish().unwrap();
6215 track.finish().unwrap();
6216 drop(track);
6217
6218 let mut group = next_group(&mut subscription)
6219 .await
6220 .expect("a retracted route's track was served as an error")
6221 .expect("the track ended before its last group");
6222 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"tail");
6223 drop(group);
6224
6225 let end = next_group(&mut subscription)
6226 .await
6227 .expect("a cleanly finished track ended as an error");
6228 assert!(end.is_none(), "a group followed the final one");
6229 }
6230
6231 #[tokio::test]
6236 async fn origin_front_drops_the_source_when_unused() {
6237 let producer = origin(1).produce();
6238 let consumer = producer.consume();
6239
6240 let broadcast = producer.publish("room/alice", Route::default()).unwrap();
6241 let track = broadcast.create_track("video", None).unwrap();
6242 let mut group = track.append_group().unwrap();
6243 group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
6244 group.finish().unwrap();
6245
6246 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6247 let mut subscription = resolved
6248 .track("video")
6249 .unwrap()
6250 .subscribe(None)
6251 .await
6252 .expect("subscribe");
6253 let mut group = subscription.recv_group().await.unwrap().unwrap();
6254 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6255 drop(group);
6256 drop(subscription);
6257
6258 tokio::time::timeout(Duration::from_secs(1), track.unused())
6259 .await
6260 .expect("source unused should resolve far below TRACK_IDLE_LINGER")
6261 .expect("source closed");
6262
6263 let mut again = resolved
6266 .track("video")
6267 .unwrap()
6268 .subscribe(track::Subscription::default().with_max_age(Duration::from_secs(3600)))
6269 .await
6270 .expect("resubscribe");
6271 let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
6272 .await
6273 .expect("cached group is still on the front")
6274 .expect("recv group")
6275 .expect("track ended early");
6276 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6277
6278 tokio::time::timeout(Duration::from_secs(1), track.used())
6279 .await
6280 .expect("returning reader re-splices the source")
6281 .expect("source closed");
6282
6283 let mut group = track.append_group().unwrap();
6284 group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
6285 group.finish().unwrap();
6286 let mut group = tokio::time::timeout(Duration::from_secs(1), again.recv_group())
6287 .await
6288 .expect("groups past the cached edge come from the re-splice")
6289 .expect("recv group")
6290 .expect("track ended early");
6291 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
6292 }
6293
6294 #[tokio::test]
6304 async fn resumed_reader_skips_warm_groups_behind_the_new_edge() {
6305 let ms = |v: u64| crate::Timestamp::from_millis(v).unwrap();
6306 let (_server, _upstream, mut dynamic, resolved) = served_front().await;
6307 let budget = track::Subscription::default().with_max_age(Duration::from_millis(100));
6308 let write = |source: &track::Producer, sequence: u64, millis: u64| {
6309 let mut group = source.create_group(sequence.into()).unwrap();
6310 group.write_frame(ms(millis), b"x".as_ref()).unwrap();
6311 group.finish().unwrap();
6312 };
6313 let drain = |subscription: &mut track::Subscriber| {
6314 let mut sequences = Vec::new();
6315 while let Poll::Ready(group) = subscription.poll_recv_group(&kio::Waiter::noop()) {
6316 sequences.push(group.unwrap().expect("track ended").sequence);
6317 }
6318 sequences
6319 };
6320
6321 let track = resolved.track("video").unwrap();
6322 let first = budget.clone();
6323 let subscribing = tokio::spawn(async move { track.subscribe(first).await });
6324 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
6325 .await
6326 .expect("the front asked the source")
6327 .expect("request");
6328 let source = request.resolving_start().accept(None);
6329 for (sequence, millis) in [(0, 0), (1, 20), (2, 40), (3, 60)] {
6330 write(&source, sequence, millis);
6331 }
6332 let mut subscription = subscribing.await.unwrap().expect("subscribe");
6333 next_group(&mut subscription).await.unwrap().expect("a cached group");
6334 drain(&mut subscription);
6335 drop(subscription);
6336
6337 tokio::time::timeout(Duration::from_secs(1), source.unused())
6338 .await
6339 .expect("parked")
6340 .expect("source open");
6341 drop(source);
6342
6343 let track = resolved.track("video").unwrap();
6344 let second = budget.clone();
6345 let subscribing = tokio::spawn(async move { track.subscribe(second).await });
6346 let request = tokio::time::timeout(Duration::from_secs(1), dynamic.requested_track())
6347 .await
6348 .expect("the front asked the source again")
6349 .expect("request");
6350 let mut source = request.resolving_start().accept(None);
6351 for (sequence, millis) in [(20, 400), (21, 420), (22, 440)] {
6352 write(&source, sequence, millis);
6353 }
6354 source.start_at(3).unwrap();
6357 let mut subscription = subscribing.await.unwrap().expect("resubscribe");
6358 settle(|| subscription.latest() == Some(22)).await;
6359
6360 assert_eq!(drain(&mut subscription), [3, 20, 21, 22]);
6363 }
6364
6365 #[tokio::test]
6370 async fn chained_front_drops_the_source_when_unused() {
6371 let leaf = origin(1).produce();
6372 let leaf_consumer = leaf.consume();
6373
6374 let broadcast = leaf.publish("room/alice", Route::default()).unwrap();
6375 let track = broadcast.create_track("video", None).unwrap();
6376 let mut group = track.append_group().unwrap();
6377 group.write_frame(crate::Timestamp::ZERO, b"cached".as_ref()).unwrap();
6378 group.finish().unwrap();
6379
6380 let leaf_front = leaf_consumer.request_broadcast("room/alice").await.expect("resolves");
6383
6384 let mid = origin(2).produce();
6385 let mid_server = mid.dynamic("room", Route::default().with_hops(hops(&[10]))).unwrap();
6386 let mid_pending = mid.consume().request_broadcast("room/alice");
6387 queued(&mid_server).await.accept(&leaf_front);
6388 let mid_resolved = mid_pending.await.expect("mid resolves");
6389
6390 let edge = origin(3).produce();
6391 let edge_server = edge.dynamic("room", Route::default().with_hops(hops(&[20]))).unwrap();
6392 let edge_pending = edge.consume().request_broadcast("room/alice");
6393 queued(&edge_server).await.accept(&mid_resolved);
6394 let edge_resolved = edge_pending.await.expect("edge resolves");
6395
6396 let mut subscription = edge_resolved
6397 .track("video")
6398 .unwrap()
6399 .subscribe(None)
6400 .await
6401 .expect("subscribe");
6402 let mut group = subscription.recv_group().await.unwrap().unwrap();
6403 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6404 drop(group);
6405 drop(subscription);
6406
6407 tokio::time::timeout(Duration::from_secs(5), track.unused())
6408 .await
6409 .expect("chained unused should resolve far below TRACK_IDLE_LINGER")
6410 .expect("source closed");
6411
6412 let cached = edge_resolved.track("video").unwrap().cached_groups();
6413 assert_eq!(
6414 cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
6415 vec![0],
6416 "every front keeps the delivered groups after releasing its source"
6417 );
6418
6419 let mut subscription = edge_resolved
6421 .track("video")
6422 .unwrap()
6423 .subscribe(track::Subscription::default().with_max_age(Duration::from_secs(3600)))
6424 .await
6425 .expect("resubscribe");
6426 tokio::time::timeout(Duration::from_secs(5), track.used())
6427 .await
6428 .expect("resubscribe should reach the leaf")
6429 .expect("source open");
6430 let mut group = track.append_group().unwrap();
6431 group.write_frame(crate::Timestamp::ZERO, b"live".as_ref()).unwrap();
6432 group.finish().unwrap();
6433 let mut group = subscription.recv_group().await.unwrap().unwrap();
6434 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"cached");
6435 drop(group);
6436 let mut group = subscription.recv_group().await.unwrap().unwrap();
6437 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"live");
6438 drop(group);
6439 drop(subscription);
6440
6441 tokio::time::timeout(Duration::from_secs(5), track.unused())
6442 .await
6443 .expect("second chained unused should resolve far below TRACK_IDLE_LINGER")
6444 .expect("source closed");
6445
6446 let cached = edge_resolved.track("video").unwrap().cached_groups();
6447 assert_eq!(
6448 cached.iter().map(|(group, _)| group.sequence).collect::<Vec<_>>(),
6449 vec![0, 1],
6450 "repeated demand keeps every complete group while releasing its source"
6451 );
6452
6453 let fetch = edge_resolved.track("video").unwrap().fetch_group(2, None);
6454 let mut fetch = std::pin::pin!(fetch);
6455 assert!(futures::poll!(fetch.as_mut()).is_pending(), "fetch should re-splice");
6456 tokio::time::timeout(Duration::from_secs(5), track.used())
6457 .await
6458 .expect("fetch should reach the leaf")
6459 .expect("source open");
6460 let mut group = track.append_group().unwrap();
6461 group.write_frame(crate::Timestamp::ZERO, b"fetched".as_ref()).unwrap();
6462 group.finish().unwrap();
6463 let mut group = tokio::time::timeout(Duration::from_secs(5), fetch)
6464 .await
6465 .expect("re-spliced source should answer the fetch")
6466 .expect("fetch succeeds");
6467 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"fetched");
6468 }
6469
6470 #[tokio::test]
6474 async fn incompatible_local_source_keeps_the_incumbent() {
6475 let producer = origin(1).produce();
6476 let consumer = producer.consume();
6477
6478 let first = producer.publish("room/alice", Route::default()).unwrap();
6479 let track = first.create_track("video", None).unwrap();
6480 let resolved = consumer.request_broadcast("room/alice").await.expect("resolves");
6481 let mut subscription = resolved
6482 .track("video")
6483 .unwrap()
6484 .subscribe(None)
6485 .await
6486 .expect("subscribe");
6487 let mut group = track.append_group().unwrap();
6488 group.write_frame(crate::Timestamp::ZERO, b"before".as_ref()).unwrap();
6489 group.finish().unwrap();
6490 let mut group = subscription.recv_group().await.unwrap().unwrap();
6491 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"before");
6492
6493 let second = producer.publish("room/alice", Route::default()).unwrap();
6495 let _incompatible = second
6496 .create_track("video", track::Info::default().with_timescale(crate::Timescale::MICRO))
6497 .unwrap();
6498 for _ in 0..10 {
6499 tokio::task::yield_now().await;
6500 }
6501
6502 let mut group = track.append_group().unwrap();
6504 group.write_frame(crate::Timestamp::ZERO, b"still".as_ref()).unwrap();
6505 group.finish().unwrap();
6506 let mut group = subscription.recv_group().await.unwrap().unwrap();
6507 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"still");
6508
6509 drop(track);
6511 first.finish();
6512 assert!(matches!(subscription.recv_group().await, Err(Error::Unsupported)));
6513 }
6514
6515 #[tokio::test]
6516 async fn multiple_scopes_present_one_broad_prefix() {
6517 let producer = origin(1).produce();
6518 let _a = producer.announce("", Route::default()).unwrap();
6519
6520 let consumer = producer.consume().scope("", &scopes(&["alpha", "beta"])).unwrap();
6521 let mut announced = consumer.announced();
6522 announced.assert_next_active("");
6523 announced.assert_next_wait();
6524 }
6525
6526 #[test]
6527 fn scope_accepts_every_pattern_union() {
6528 let producer = origin(1).produce();
6529
6530 let root = producer.scope("", &Patterns::from(Pattern::all())).unwrap();
6532 assert_eq!(root.allowed(), Patterns::from(Pattern::all()));
6533
6534 let scoped = producer.scope("", &scopes(&["room"])).unwrap();
6536 assert_eq!(scoped.allowed(), scopes(&["room"]));
6537
6538 let multi = producer.scope("", &scopes(&["room", "room/chat", "anon"])).unwrap();
6540 assert_eq!(multi.allowed(), scopes(&["room", "anon"]));
6541
6542 let consumer = producer.consume().scope("", &scopes(&["room"])).unwrap();
6544 assert_eq!(consumer.allowed(), scopes(&["room"]));
6545
6546 for text in ["room", "", "*room", "room/*", "*", "**/room", "room/**/chat", "*.hang"] {
6547 let union = Patterns::from(text.parse::<Pattern>().unwrap());
6548 assert_eq!(producer.scope("", &union).expect(text).allowed(), union, "{text}");
6549 assert_eq!(
6550 producer.consume().scope("", &union).expect(text).allowed(),
6551 union,
6552 "{text}"
6553 );
6554 }
6555
6556 let mixed: Patterns = ["room/**".parse().unwrap(), "other".parse().unwrap()]
6557 .into_iter()
6558 .collect();
6559 assert_eq!(producer.scope("", &mixed).unwrap().allowed(), mixed);
6560 }
6561
6562 #[test]
6563 fn route_table_prunes_to_empty() {
6564 let producer = origin(1).produce();
6565 let consumer = producer.consume();
6566
6567 let cursor = consumer
6570 .scope("", &scopes(&["room/a", "other/deep/head"]))
6571 .unwrap()
6572 .announced();
6573 let route = producer.announce("room/a/b/c", Route::default()).unwrap();
6574 {
6575 let table = producer.shared.lock();
6576 assert!(table.routes.root.find(Path::new("room/a/b/c").parts()).is_some());
6577 assert!(table.routes.root.find(Path::new("other/deep/head").parts()).is_some());
6578 assert_eq!(table.routes.root.cursors_below, 2);
6579 }
6580
6581 drop(route);
6582 drop(cursor);
6583 let table = producer.shared.lock();
6584 assert!(table.routes.root.is_empty());
6585 assert_eq!(table.routes.root.cursors_below, 0);
6586 }
6587
6588 #[test]
6592 fn a_published_broadcast_keeps_the_driver_running() {
6593 let (producer, mut driver) = Producer::new(Config::new(origin(1)));
6594 let waiter = kio::Waiter::noop();
6595 let broadcast = producer.create_broadcast("room/a").unwrap();
6596 drop(producer);
6597 assert!(
6598 driver.poll(Instant::now(), &waiter).is_ok(),
6599 "the broadcast is lifecycle work"
6600 );
6601 drop(broadcast);
6602 assert!(matches!(driver.poll(Instant::now(), &waiter), Err(Error::Closed)));
6603 }
6604
6605 #[test]
6606 fn watch_wakes_only_for_covering_changes() {
6607 let producer = origin(1).produce();
6608 let waiter = kio::Waiter::noop();
6609 let watch = producer.shared.lock().watch(&producer.shared, &Path::new("room/a"));
6610 let seen = watch.seen();
6611
6612 let _other = producer.announce("other", Route::default()).unwrap();
6614 let _below = producer.announce("room/a/b", Route::default()).unwrap();
6615 assert!(watch.poll_changed(&waiter, seen).is_pending());
6616
6617 let above = producer.announce("room", Route::default()).unwrap();
6619 assert!(watch.poll_changed(&waiter, seen).is_ready());
6620 let seen = watch.seen();
6621 drop(above);
6622 assert!(watch.poll_changed(&waiter, seen).is_ready());
6623 let seen = watch.seen();
6624
6625 let _beside = producer.create_broadcast("room/b").unwrap();
6627 assert!(watch.poll_changed(&waiter, seen).is_pending());
6628 let _here = producer.create_broadcast("room/a").unwrap();
6629 assert!(watch.poll_changed(&waiter, seen).is_ready());
6630
6631 drop(watch);
6633 let table = producer.shared.lock();
6634 let node = table
6635 .routes
6636 .root
6637 .find(Path::new("room/a").parts())
6638 .expect("route below keeps the node");
6639 assert!(node.watches.is_empty());
6640 assert_eq!(table.routes.root.watches_below, 0);
6641 }
6642
6643 #[test]
6644 fn a_discarded_front_task_unregisters_its_watch() {
6645 let (producer, _driver) = Producer::new(Config {
6646 hop: origin(1),
6647 ..Default::default()
6648 });
6649 let consumer = producer.consume();
6650 let _served = producer.dynamic("room", Route::default()).unwrap();
6651 drop(producer);
6656 let _pending = consumer.request_broadcast("room/a");
6657 }
6658
6659 #[test]
6660 fn create_broadcast_refuses_a_path_no_pattern_can_spell() {
6661 let producer = origin(1).produce();
6662
6663 assert!(matches!(
6667 producer.create_broadcast("room/*"),
6668 Err(Error::InvalidPath(_))
6669 ));
6670 assert!(matches!(
6671 producer.announce("room/**", Route::default()),
6672 Err(Error::InvalidPath(_))
6673 ));
6674 }
6675
6676 #[test]
6677 fn scope_empty_union_grants_nothing() {
6678 let producer = origin(1).produce();
6679
6680 assert!(matches!(producer.scope("", &Patterns::new()), Err(Error::Unauthorized)));
6682 assert!(matches!(
6683 producer.consume().scope("", &Patterns::new()),
6684 Err(Error::Unauthorized)
6685 ));
6686 }
6687
6688 #[test]
6689 fn scope_nests_and_rebases_roots() {
6690 let producer = origin(1).produce();
6691
6692 let scoped = producer.scope("", &scopes(&["room"])).unwrap();
6694 let nested = scoped.scope("", &scopes(&["room/chat"])).unwrap();
6695 assert_eq!(nested.allowed(), scopes(&["room/chat"]));
6696
6697 assert!(matches!(
6699 scoped.scope("", &scopes(&["other"])),
6700 Err(Error::Unauthorized)
6701 ));
6702
6703 let rooted = nested.scope("room/chat", &Patterns::from(Pattern::all())).unwrap();
6705 assert_eq!(rooted.allowed(), scopes(&[""]));
6706
6707 let broadcast = nested.create_broadcast("room/chat/live").unwrap();
6709 assert!(producer.consume().get_broadcast("room/chat/live").is_some());
6710 broadcast.finish();
6711 }
6712
6713 #[test]
6714 fn scope_intersects_and_rebases_arbitrary_grants() {
6715 let producer = origin(1).produce();
6716 let rooms = producer
6717 .scope("", &Patterns::from("room/*".parse::<Pattern>().unwrap()))
6718 .unwrap();
6719 let chats = rooms
6720 .scope("", &Patterns::from("*/chat".parse::<Pattern>().unwrap()))
6721 .unwrap();
6722 assert_eq!(chats.allowed(), Patterns::from("room/chat".parse::<Pattern>().unwrap()));
6723
6724 let exact = producer
6725 .scope("", &Patterns::from("room/alice".parse::<Pattern>().unwrap()))
6726 .unwrap();
6727 let rooted = exact.scope("room", &Patterns::from(Pattern::all())).unwrap();
6728 assert_eq!(rooted.allowed(), Patterns::from("alice".parse::<Pattern>().unwrap()));
6729 assert!(matches!(
6730 exact.scope("room/bob", &Patterns::from(Pattern::all())),
6731 Err(Error::Unauthorized)
6732 ));
6733
6734 let broadcast = exact.create_broadcast("room/alice").unwrap();
6735 assert!(matches!(
6736 exact.create_broadcast("room/alice/cam"),
6737 Err(Error::Unauthorized)
6738 ));
6739 assert!(producer.consume().get_broadcast("room/alice").is_some());
6740 drop(broadcast);
6741 }
6742
6743 #[tokio::test]
6744 async fn wildcard_scope_filters_announcements_and_reports_captures() {
6745 let producer = origin(1).produce();
6746 let consumer = producer
6747 .consume()
6748 .scope("", &Patterns::from("room/*/chat".parse::<Pattern>().unwrap()))
6749 .unwrap();
6750 let mut announced = consumer.announced();
6751
6752 let alice = producer.create_broadcast("room/alice/chat").unwrap();
6753 alice.announce(Route::default()).unwrap();
6754 let update = announced.try_next().expect("alice's chat");
6755 assert_eq!(update.prefix.as_str(), "room/alice/chat");
6756 assert_eq!(update.captures, Some(vec!["alice".parse::<Pattern>().unwrap()]));
6757
6758 let audio = producer.create_broadcast("room/alice/audio").unwrap();
6759 audio.announce(Route::default()).unwrap();
6760 announced.assert_next_wait();
6761
6762 let broad = producer.announce("room", Route::default()).unwrap();
6763 let update = announced.try_next().expect("overlapping broad route");
6764 assert_eq!(update.prefix.as_str(), "room");
6765 assert_eq!(update.captures, None, "an overlap does not pin the wildcard");
6766
6767 drop(broad);
6768 drop(audio);
6769 drop(alice);
6770 }
6771
6772 #[tokio::test]
6773 async fn local_broadcast_wins_announcement_ties() {
6774 let producer = origin(1).produce();
6775 let remote = producer.announce("room/alice", Route::default().with_cost(9)).unwrap();
6776 let local = producer.create_broadcast("room/alice").unwrap();
6777 local.announce(Route::default()).unwrap();
6778
6779 let mut announced = producer.consume().announced();
6780 let update = announced.try_next().expect("one winning route");
6781 assert_eq!(update.prefix.as_str(), "room/alice");
6782 assert_eq!(update.route.cost, Cost::default());
6783 announced.assert_next_wait();
6784
6785 drop(local);
6786 drop(remote);
6787 }
6788
6789 #[test]
6794 fn cost_charge_saturates() {
6795 assert_eq!(Cost { warm: 4, cold: 6 }.charged(5), Cost { warm: 9, cold: 11 });
6796 assert_eq!(Cost::new(u64::MAX).charged(10), Cost::new(MAX_COST));
6797
6798 assert_eq!(Cost::UNKNOWN.charged(3).cold, MAX_COST);
6801 }
6802
6803 fn expiring_origin(expiry: Duration) -> Producer {
6805 let pool = cache::Pool::new(cache::Config::default().with_expiry(expiry));
6806 Config {
6807 pool,
6808 ..Config::default()
6809 }
6810 .produce()
6811 }
6812
6813 #[tokio::test(start_paused = true)]
6817 async fn stalled_publisher_open_group_is_reclaimed() {
6818 let expiry = Duration::from_secs(1);
6819 let origin = expiring_origin(expiry);
6820 let broadcast = origin.create_broadcast("test").unwrap();
6821 let track = broadcast.create_track("video", None).unwrap();
6822
6823 let mut stalled = track.append_group().unwrap();
6824 stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
6825 let _successor = track.append_group().unwrap();
6829
6830 let mut reading = stalled.consume();
6831 assert!(reading.read_frame().await.unwrap().is_some());
6832
6833 crate::model::clock::advance(expiry * 2);
6835
6836 let reclaimed = tokio::time::timeout(Duration::from_secs(60), reading.read_frame()).await;
6839 assert!(
6840 matches!(reclaimed, Ok(Err(Error::Old))),
6841 "the sweep must reclaim an idle open group and surface the gap, got {reclaimed:?}"
6842 );
6843 }
6844
6845 #[tokio::test(start_paused = true)]
6848 async fn sweep_respects_a_disabled_expiry() {
6849 let origin = Config {
6850 pool: cache::Pool::unbounded(),
6851 ..Config::default()
6852 }
6853 .produce();
6854 let broadcast = origin.create_broadcast("test").unwrap();
6855 let track = broadcast.create_track("video", None).unwrap();
6856
6857 let mut stalled = track.append_group().unwrap();
6858 stalled.write_frame(crate::Timestamp::ZERO, b"x".as_slice()).unwrap();
6859 let _successor = track.append_group().unwrap();
6860
6861 let mut reading = stalled.consume();
6862 assert!(reading.read_frame().await.unwrap().is_some());
6863
6864 crate::model::clock::advance(Duration::from_secs(3600));
6865 tokio::time::advance(Duration::from_secs(3600)).await;
6866
6867 assert!(
6868 reading.read_frame().now_or_never().is_none(),
6869 "a pool without an expiry window never reclaims"
6870 );
6871 }
6872
6873 #[test]
6876 fn drain_cost_is_encodable() {
6877 use crate::coding::Encode;
6878
6879 let mut buf = Vec::new();
6880 Cost::DRAIN
6881 .encode(&mut buf, crate::lite::Version::Lite06)
6882 .expect("a draining route is still forwarded, so its cost must encode");
6883 }
6884}