1use crate::{broadcast, cache, stats, track};
2use kio::Pollable;
3use std::{
4 cmp::Reverse,
5 collections::{BTreeMap, HashMap, HashSet},
6 fmt,
7 sync::Arc,
8 sync::atomic::{AtomicU64, Ordering},
9 task::{Poll, ready},
10 time::Duration,
11};
12
13use rand::RngExt;
14use web_async::Lock;
15
16use super::{Requests, WeakCache};
17use crate::{
18 AsPath, Error, Path, PathOwned, PathPrefixes,
19 coding::{BoundsExceeded, Decode, DecodeError, Encode, EncodeError},
20};
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub struct Origin {
29 id: u64,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[non_exhaustive]
36pub struct InvalidOrigin;
37
38impl fmt::Display for InvalidOrigin {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "local origin id must be non-zero and below 2^62")
41 }
42}
43
44impl std::error::Error for InvalidOrigin {}
45
46impl Origin {
47 pub(crate) const UNKNOWN: Self = Self { id: 0 };
50
51 pub fn new(id: u64) -> Result<Self, InvalidOrigin> {
57 if id == 0 || id >= 1u64 << 62 {
58 return Err(InvalidOrigin);
59 }
60 Ok(Self { id })
61 }
62
63 pub fn random() -> Self {
72 let mut rng = rand::rng();
73 let id = rng.random_range(1..(1u64 << 53));
74 Self { id }
75 }
76
77 pub fn id(self) -> u64 {
79 self.id
80 }
81
82 pub fn produce(self) -> Producer {
85 Info::new(self).produce()
86 }
87}
88
89#[derive(Clone, Debug)]
99#[non_exhaustive]
100pub struct Info {
101 pub id: Origin,
104
105 pub pool: cache::Pool,
111
112 pub cache_duration: Duration,
119
120 pub latency_default: Duration,
130
131 pub linger: Duration,
140}
141
142impl Default for Info {
143 fn default() -> Self {
146 Self {
147 id: Origin::UNKNOWN,
148 pool: cache::Pool::default(),
149 cache_duration: Duration::MAX,
150 latency_default: track::DEFAULT_LATENCY_MAX,
151 linger: Duration::ZERO,
152 }
153 }
154}
155
156impl Info {
157 pub fn new(id: Origin) -> Self {
159 Self { id, ..Self::default() }
160 }
161
162 pub fn with_pool(mut self, pool: cache::Pool) -> Self {
164 self.pool = pool;
165 self
166 }
167
168 pub fn with_cache_duration(mut self, cache_duration: Duration) -> Self {
171 self.cache_duration = cache_duration;
172 self
173 }
174
175 pub fn with_latency_default(mut self, latency_default: Duration) -> Self {
178 self.latency_default = latency_default;
179 self
180 }
181
182 pub fn with_linger(mut self, linger: Duration) -> Self {
185 self.linger = linger;
186 self
187 }
188
189 pub fn produce(self) -> Producer {
191 Producer::new(self)
192 }
193}
194
195impl TryFrom<u64> for Origin {
196 type Error = InvalidOrigin;
197
198 fn try_from(id: u64) -> Result<Self, Self::Error> {
199 Self::new(id)
200 }
201}
202
203impl fmt::Display for Origin {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 self.id.fmt(f)
206 }
207}
208
209impl<V: Copy> Encode<V> for Origin
210where
211 u64: Encode<V>,
212{
213 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
214 self.id.encode(w, version)
215 }
216}
217
218impl<V: Copy> Decode<V> for Origin
219where
220 u64: Decode<V>,
221{
222 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
223 let id = u64::decode(r, version)?;
224 if id >= 1u64 << 62 {
225 return Err(DecodeError::InvalidValue);
226 }
227 Ok(Self { id })
228 }
229}
230
231pub(crate) const MAX_HOPS: usize = 32;
237
238#[derive(Debug, Clone, Default, PartialEq, Eq)]
243pub struct OriginList(Vec<Origin>);
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247#[non_exhaustive]
248pub struct TooManyOrigins;
249
250impl fmt::Display for TooManyOrigins {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 write!(f, "too many origins (max {MAX_HOPS})")
253 }
254}
255
256impl std::error::Error for TooManyOrigins {}
257
258impl From<TooManyOrigins> for DecodeError {
259 fn from(_: TooManyOrigins) -> Self {
260 DecodeError::BoundsExceeded
261 }
262}
263
264impl OriginList {
265 pub fn new() -> Self {
267 Self(Vec::new())
268 }
269
270 pub fn push(&mut self, origin: Origin) -> Result<(), TooManyOrigins> {
272 if self.0.len() >= MAX_HOPS {
273 return Err(TooManyOrigins);
274 }
275 self.0.push(origin);
276 Ok(())
277 }
278
279 pub fn replace_first(&mut self, target: Origin, replacement: Origin) -> bool {
282 for entry in &mut self.0 {
283 if *entry == target {
284 *entry = replacement;
285 return true;
286 }
287 }
288 false
289 }
290
291 pub fn contains(&self, origin: &Origin) -> bool {
293 self.0.contains(origin)
294 }
295
296 pub fn len(&self) -> usize {
298 self.0.len()
299 }
300
301 pub fn is_empty(&self) -> bool {
303 self.0.is_empty()
304 }
305
306 pub fn iter(&self) -> std::slice::Iter<'_, Origin> {
308 self.0.iter()
309 }
310
311 pub fn as_slice(&self) -> &[Origin] {
313 &self.0
314 }
315}
316
317impl TryFrom<Vec<Origin>> for OriginList {
318 type Error = TooManyOrigins;
319
320 fn try_from(v: Vec<Origin>) -> Result<Self, Self::Error> {
321 if v.len() > MAX_HOPS {
322 return Err(TooManyOrigins);
323 }
324 Ok(Self(v))
325 }
326}
327
328impl<'a> IntoIterator for &'a OriginList {
329 type Item = &'a Origin;
330 type IntoIter = std::slice::Iter<'a, Origin>;
331
332 fn into_iter(self) -> Self::IntoIter {
333 self.iter()
334 }
335}
336
337impl<V: Copy> Encode<V> for OriginList
338where
339 u64: Encode<V>,
340 Origin: Encode<V>,
341{
342 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
343 (self.0.len() as u64).encode(w, version)?;
344 for origin in &self.0 {
345 origin.encode(w, version)?;
346 }
347 Ok(())
348 }
349}
350
351impl<V: Copy> Decode<V> for OriginList
352where
353 u64: Decode<V>,
354 Origin: Decode<V>,
355{
356 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
357 let count = u64::decode(r, version)? as usize;
358 if count > MAX_HOPS {
359 return Err(DecodeError::BoundsExceeded);
360 }
361 let mut list = Vec::with_capacity(count);
362 for _ in 0..count {
363 list.push(Origin::decode(r, version)?);
364 }
365 Ok(Self(list))
366 }
367}
368
369static NEXT_CONSUMER_ID: AtomicU64 = AtomicU64::new(0);
370
371#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
372struct ConsumerId(u64);
373
374impl ConsumerId {
375 fn new() -> Self {
376 Self(NEXT_CONSUMER_ID.fetch_add(1, Ordering::Relaxed))
377 }
378}
379
380struct OriginBroadcast {
386 path: PathOwned,
387 broadcast: broadcast::Producer,
389 state: kio::Producer<FrontState>,
392 announced: bool,
393}
394
395fn route_key(name: &Path, hops: &OriginList) -> (usize, u64) {
404 (hops.len(), fnv_key(name, hops.iter().copied()))
405}
406
407fn fnv_key(name: &Path, origins: impl IntoIterator<Item = Origin>) -> u64 {
420 const SEED: u64 = 0x420C0DECB00B; const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
422
423 let mut hash = SEED;
424 for &byte in name.as_str().as_bytes() {
425 hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
426 }
427 for origin in origins {
428 for &byte in &origin.id().to_le_bytes() {
429 hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
430 }
431 }
432
433 hash
434}
435
436fn route_order(name: &Path, route: &FrontRoute) -> (bool, u64, usize, u64, Reverse<u64>) {
455 let (len, hash) = route_key(name, &route.route.hops);
456 (!route.route.announce, route.route.cost, len, hash, Reverse(route.id))
457}
458
459enum PendingUpdate {
468 Announce(broadcast::Consumer),
469 Unannounce,
470 UnannounceAnnounce(broadcast::Consumer),
471}
472
473#[derive(Default)]
478struct OriginConsumerState {
479 pending: BTreeMap<PathOwned, PendingUpdate>,
480}
481
482impl OriginConsumerState {
483 fn apply_announce(&mut self, path: PathOwned, broadcast: broadcast::Consumer) {
484 let new = match self.pending.remove(&path) {
485 None | Some(PendingUpdate::Announce(_)) => PendingUpdate::Announce(broadcast),
487 Some(PendingUpdate::Unannounce | PendingUpdate::UnannounceAnnounce(_)) => {
489 PendingUpdate::UnannounceAnnounce(broadcast)
490 }
491 };
492 self.pending.insert(path, new);
493 }
494
495 fn apply_unannounce(&mut self, path: PathOwned) {
496 match self.pending.remove(&path) {
497 Some(PendingUpdate::Announce(_)) => {}
499 None | Some(PendingUpdate::Unannounce) => {
500 self.pending.insert(path, PendingUpdate::Unannounce);
501 }
502 Some(PendingUpdate::UnannounceAnnounce(_)) => {
505 self.pending.insert(path, PendingUpdate::Unannounce);
506 }
507 }
508 }
509
510 fn take(&mut self) -> Option<OriginAnnounce> {
512 let path = self.pending.keys().next()?.clone();
513 let broadcast = match self.pending.remove(&path).unwrap() {
514 PendingUpdate::Announce(broadcast) => Some(broadcast),
515 PendingUpdate::Unannounce => None,
516 PendingUpdate::UnannounceAnnounce(broadcast) => {
517 self.pending.insert(path.clone(), PendingUpdate::Announce(broadcast));
520 None
521 }
522 };
523 Some(OriginAnnounce { path, broadcast })
524 }
525}
526
527#[derive(Clone)]
528struct AnnounceConsumerNotify {
529 root: PathOwned,
530 state: kio::Producer<OriginConsumerState>,
531 exclude: Option<Origin>,
536}
537
538impl AnnounceConsumerNotify {
539 fn announce(&self, path: impl AsPath, broadcast: broadcast::Consumer, front: &kio::Producer<FrontState>) {
540 let path = path.as_path().strip_prefix(&self.root).unwrap().to_owned();
541
542 let broadcast = match self.exclude.and_then(|peer| ExclusionGuard::new(front, peer)) {
547 Some(guard) => broadcast.with_exclusion(guard),
548 None => broadcast,
549 };
550
551 self.state
552 .write()
553 .ok()
554 .expect("consumer closed")
555 .apply_announce(path, broadcast);
556 }
557
558 fn unannounce(&self, path: impl AsPath) {
559 let path = path.as_path().strip_prefix(&self.root).unwrap().to_owned();
560 self.state.write().ok().expect("consumer closed").apply_unannounce(path);
561 }
562}
563
564struct NotifyNode {
565 parent: Option<Lock<NotifyNode>>,
566
567 consumers: HashMap<ConsumerId, AnnounceConsumerNotify>,
570}
571
572impl NotifyNode {
573 fn new(parent: Option<Lock<NotifyNode>>) -> Self {
574 Self {
575 parent,
576 consumers: HashMap::new(),
577 }
578 }
579
580 fn announce(&mut self, path: impl AsPath, broadcast: &broadcast::Consumer, state: &kio::Producer<FrontState>) {
584 for consumer in self.consumers.values() {
585 consumer.announce(path.as_path(), broadcast.clone(), state);
586 }
587
588 if let Some(parent) = &self.parent {
589 parent.lock().announce(path, broadcast, state);
590 }
591 }
592
593 fn unannounce(&mut self, path: impl AsPath) {
594 for consumer in self.consumers.values() {
595 consumer.unannounce(path.as_path());
596 }
597
598 if let Some(parent) = &self.parent {
599 parent.lock().unannounce(path);
600 }
601 }
602}
603
604pub(crate) struct ExclusionGuard {
611 state: kio::Producer<FrontState>,
612 peer: Origin,
613}
614
615impl ExclusionGuard {
616 fn new(state: &kio::Producer<FrontState>, peer: Origin) -> Option<Arc<Self>> {
619 let mut s = state.write().ok()?;
620 if s.closed {
621 return None;
622 }
623 *s.excluded.entry(peer).or_default() += 1;
624 drop(s);
625 Some(Arc::new(Self {
626 state: state.clone(),
627 peer,
628 }))
629 }
630}
631
632impl Drop for ExclusionGuard {
633 fn drop(&mut self) {
634 let Ok(mut state) = self.state.write() else { return };
635 if let std::collections::hash_map::Entry::Occupied(mut entry) = state.excluded.entry(self.peer) {
636 match entry.get() {
637 1 => drop(entry.remove()),
638 n => *entry.get_mut() = n - 1,
639 }
640 }
641 }
642}
643
644enum Resolved {
651 Found(broadcast::Consumer),
654 Excluded,
656 OutOfScope,
660 Missing,
662}
663
664struct OriginNode {
665 broadcast: Option<OriginBroadcast>,
668
669 sources: usize,
674
675 nested: HashMap<String, Lock<OriginNode>>,
677
678 notify: Lock<NotifyNode>,
680}
681
682impl OriginNode {
683 fn new(parent: Option<Lock<NotifyNode>>) -> Self {
684 Self {
685 broadcast: None,
686 sources: 0,
687 nested: HashMap::new(),
688 notify: Lock::new(NotifyNode::new(parent)),
689 }
690 }
691
692 fn leaf(&mut self, path: &Path) -> Lock<OriginNode> {
693 let (dir, rest) = path.next_part().expect("leaf called with empty path");
694
695 let next = self.entry(dir);
696 if rest.is_empty() { next } else { next.lock().leaf(&rest) }
697 }
698
699 fn entry(&mut self, dir: &str) -> Lock<OriginNode> {
700 match self.nested.get(dir) {
701 Some(next) => next.clone(),
702 None => {
703 let next = Lock::new(OriginNode::new(Some(self.notify.clone())));
704 self.nested.insert(dir.to_string(), next.clone());
705 next
706 }
707 }
708 }
709
710 fn set_announced(&mut self, expect: &kio::Producer<FrontState>, announce: bool) {
714 let Some(existing) = &mut self.broadcast else { return };
715 if !existing.state.same_channel(expect) || existing.announced == announce {
716 return;
717 }
718 existing.announced = announce;
719 let path = existing.path.clone();
720 let consumer = existing.broadcast.consume();
721 let state = existing.state.clone();
722 let mut notify = self.notify.lock();
723 if announce {
724 notify.announce(&path, &consumer, &state);
725 } else {
726 notify.unannounce(&path);
727 }
728 }
729
730 fn consume_at(&mut self, id: ConsumerId, notify: AnnounceConsumerNotify, relative: impl AsPath) {
737 let relative = relative.as_path();
738
739 let Some((dir, relative)) = relative.next_part() else {
740 return self.consume(id, notify);
741 };
742
743 let nested = self.entry(dir);
744 nested.lock().consume_at(id, notify, &relative);
745 }
746
747 fn consume(&mut self, id: ConsumerId, mut notify: AnnounceConsumerNotify) {
748 self.consume_initial(&mut notify);
749 self.notify.lock().consumers.insert(id, notify);
750 }
751
752 fn consume_initial(&mut self, notify: &mut AnnounceConsumerNotify) {
753 if let Some(broadcast) = &self.broadcast
756 && broadcast.announced
757 {
758 notify.announce(&broadcast.path, broadcast.broadcast.consume(), &broadcast.state);
759 }
760
761 for nested in self.nested.values() {
763 nested.lock().consume_initial(notify);
764 }
765 }
766
767 fn resolve_broadcast(&self, rest: impl AsPath, exclude: Option<Origin>) -> Resolved {
768 let rest = rest.as_path();
769
770 if let Some((dir, rest)) = rest.next_part() {
771 let Some(node) = self.nested.get(dir) else {
772 return Resolved::Missing;
773 };
774 let node = node.lock();
775 return node.resolve_broadcast(&rest, exclude);
776 }
777
778 let Some(broadcast) = self.broadcast.as_ref() else {
779 return Resolved::Missing;
780 };
781 let Some(origin) = exclude else {
782 return Resolved::Found(broadcast.broadcast.consume());
783 };
784
785 let state = broadcast.state.read();
801 if !state.routes.iter().any(|r| r.route.hops.contains(&origin)) {
802 drop(state);
803 let shared = broadcast.broadcast.consume();
804 return match ExclusionGuard::new(&broadcast.state, origin) {
805 Some(guard) => Resolved::Found(shared.with_exclusion(guard)),
806 None => Resolved::Found(shared),
809 };
810 }
811 match state
812 .dispatch(Some(origin))
813 .and_then(|clean| state.routes.iter().find(|r| r.id == clean))
814 {
815 Some(route) => Resolved::Found(route.source.clone()),
816 None => Resolved::Excluded,
817 }
818 }
819
820 fn detach(&mut self, claim: Claim, relative: impl AsPath) {
828 let relative = relative.as_path();
829
830 let Some((dir, relative)) = relative.next_part() else {
831 match claim {
832 Claim::Consumer(id) => {
833 self.notify.lock().consumers.remove(&id).expect("consumer not found");
834 }
835 Claim::Source => self.sources -= 1,
836 }
837 return;
838 };
839
840 let nested = self.nested.get(dir).expect("claimed node missing").clone();
843 let mut locked = nested.lock();
844 locked.detach(claim, &relative);
845
846 if locked.is_empty() {
847 drop(locked);
848 self.nested.remove(dir);
849 }
850 }
851
852 fn reserve(&mut self, relative: impl AsPath) {
855 let relative = relative.as_path();
856
857 let Some((dir, relative)) = relative.next_part() else {
858 self.sources += 1;
859 return;
860 };
861
862 let nested = self.entry(dir);
863 nested.lock().reserve(&relative);
864 }
865
866 fn remove(&mut self, expect: &kio::Producer<FrontState>, relative: impl AsPath) {
870 let relative = relative.as_path();
871
872 if let Some((dir, relative)) = relative.next_part() {
873 let Some(nested) = self.nested.get(dir) else { return };
874 let nested = nested.clone();
875 let mut locked = nested.lock();
876 locked.remove(expect, &relative);
877
878 if locked.is_empty() {
879 drop(locked);
880 self.nested.remove(dir);
881 }
882 } else if let Some(existing) = &self.broadcast
883 && existing.state.same_channel(expect)
884 {
885 let existing = self.broadcast.take().expect("checked above");
886 if existing.announced {
887 self.notify.lock().unannounce(&existing.path);
888 }
889 }
890 }
891
892 fn is_empty(&self) -> bool {
893 self.broadcast.is_none()
894 && self.sources == 0
895 && self.nested.is_empty()
896 && self.notify.lock().consumers.is_empty()
897 }
898
899 #[cfg(test)]
902 fn count(&self) -> usize {
903 1 + self.nested.values().map(|nested| nested.lock().count()).sum::<usize>()
904 }
905}
906
907#[derive(Clone, Copy)]
910enum Claim {
911 Consumer(ConsumerId),
913 Source,
915}
916
917struct SourceReservation {
929 tree: Lock<OriginNode>,
930 path: PathOwned,
931}
932
933impl SourceReservation {
934 fn new(tree: Lock<OriginNode>, path: PathOwned) -> Self {
935 tree.lock().reserve(&path);
936 Self { tree, path }
937 }
938}
939
940impl Drop for SourceReservation {
941 fn drop(&mut self) {
942 self.tree.lock().detach(Claim::Source, &self.path);
943 }
944}
945
946#[derive(Clone)]
954struct OriginNodes {
955 tree: Lock<OriginNode>,
958
959 nodes: Vec<(PathOwned, PathOwned)>,
962}
963
964impl OriginNodes {
965 fn empty() -> Self {
968 Self {
969 tree: Lock::new(OriginNode::new(None)),
970 nodes: Vec::new(),
971 }
972 }
973
974 pub fn select(&self, prefixes: &PathPrefixes) -> Option<Self> {
977 let mut roots = Vec::new();
978
979 for (root, absolute) in &self.nodes {
980 for prefix in prefixes {
981 if root.has_prefix(prefix) {
982 roots.push((root.to_owned(), absolute.clone()));
984 continue;
985 }
986
987 if let Some(suffix) = prefix.strip_prefix(root) {
988 roots.push((prefix.to_owned(), absolute.join(&suffix)));
990 }
991 }
992 }
993
994 if roots.is_empty() {
995 None
996 } else {
997 Some(self.with_nodes(roots))
998 }
999 }
1000
1001 pub fn root(&self, new_root: impl AsPath) -> Option<Self> {
1002 let new_root = new_root.as_path();
1003 let mut roots = Vec::new();
1004
1005 if new_root.is_empty() {
1006 return Some(self.clone());
1007 }
1008
1009 for (root, absolute) in &self.nodes {
1010 if let Some(suffix) = root.strip_prefix(&new_root) {
1011 roots.push((suffix.to_owned(), absolute.clone()));
1013 } else if let Some(suffix) = new_root.strip_prefix(root) {
1014 roots.push(("".into(), absolute.join(&suffix)));
1017 }
1018 }
1019
1020 if roots.is_empty() {
1021 None
1022 } else {
1023 Some(self.with_nodes(roots))
1024 }
1025 }
1026
1027 fn with_nodes(&self, nodes: Vec<(PathOwned, PathOwned)>) -> Self {
1028 Self {
1029 tree: self.tree.clone(),
1030 nodes,
1031 }
1032 }
1033
1034 pub fn get(&self, path: impl AsPath) -> Option<PathOwned> {
1036 let path = path.as_path();
1037
1038 for (root, absolute) in &self.nodes {
1039 if let Some(suffix) = path.strip_prefix(root) {
1040 return Some(absolute.join(&suffix));
1041 }
1042 }
1043
1044 None
1045 }
1046}
1047
1048impl Default for OriginNodes {
1049 fn default() -> Self {
1050 Self {
1051 tree: Lock::new(OriginNode::new(None)),
1052 nodes: vec![("".into(), "".into())],
1053 }
1054 }
1055}
1056
1057#[derive(Clone)]
1059pub struct OriginAnnounce {
1060 pub path: PathOwned,
1062 pub broadcast: Option<broadcast::Consumer>,
1068}
1069
1070#[derive(Clone)]
1072pub struct Producer {
1073 info: Origin,
1077
1078 nodes: OriginNodes,
1081
1082 root: PathOwned,
1084
1085 dynamic: kio::Shared<OriginDynamicState>,
1089
1090 pool: cache::Pool,
1093
1094 cache_duration: Duration,
1097
1098 latency_default: Duration,
1101
1102 linger: Duration,
1105
1106 stats: stats::Session,
1110}
1111
1112impl std::ops::Deref for Producer {
1113 type Target = Origin;
1114
1115 fn deref(&self) -> &Self::Target {
1116 &self.info
1117 }
1118}
1119
1120impl Producer {
1121 pub fn new(info: Info) -> Self {
1125 Self {
1126 info: info.id,
1127 nodes: OriginNodes::default(),
1128 root: PathOwned::default(),
1129 dynamic: kio::Shared::default(),
1130 pool: info.pool,
1131 cache_duration: info.cache_duration,
1132 latency_default: info.latency_default,
1133 linger: info.linger,
1134 stats: stats::Session::default(),
1135 }
1136 }
1137
1138 pub fn with_stats(mut self, session: stats::Session) -> Self {
1142 self.stats = session;
1143 self
1144 }
1145
1146 pub fn with_linger(mut self, linger: Duration) -> Self {
1155 self.linger = linger;
1156 self
1157 }
1158
1159 pub fn info(&self) -> Info {
1162 Info {
1163 id: self.info,
1164 pool: self.pool.clone(),
1165 cache_duration: self.cache_duration,
1166 latency_default: self.latency_default,
1167 linger: self.linger,
1168 }
1169 }
1170
1171 pub(crate) fn latency_default(&self) -> Duration {
1174 self.latency_default
1175 }
1176
1177 pub(crate) fn empty(info: Origin) -> Self {
1182 Self {
1183 info,
1184 nodes: OriginNodes::empty(),
1185 root: PathOwned::default(),
1186 dynamic: kio::Shared::default(),
1187 pool: cache::Pool::default(),
1188 cache_duration: Duration::MAX,
1189 latency_default: track::DEFAULT_LATENCY_MAX,
1190 linger: Duration::ZERO,
1191 stats: stats::Session::default(),
1192 }
1193 }
1194
1195 pub fn create_broadcast(&self, path: impl AsPath, route: broadcast::Route) -> Result<broadcast::Producer, Error> {
1246 let path = path.as_path();
1247
1248 debug_assert!(
1249 !route.hops.contains(&self.info),
1250 "create_broadcast called with a looping hop chain",
1251 );
1252
1253 let full = self.nodes.get(&path).ok_or(Error::Unauthorized)?;
1258 let tree = self.nodes.tree.clone();
1259
1260 if full.parts().count() > Path::MAX_PARTS {
1264 return Err(BoundsExceeded.into());
1265 }
1266
1267 let ingress = self.stats.ingress(&full);
1271
1272 let mut source = broadcast::Info { origin: self.info() }
1273 .produce()
1274 .with_stats(ingress.clone());
1275 source.set_route(route).expect("fresh producer");
1276
1277 let reservation = SourceReservation::new(tree.clone(), full.clone());
1280 web_async::spawn(run_source(
1281 self.info(),
1282 tree,
1283 full,
1284 source.consume(),
1285 ingress,
1286 reservation,
1287 ));
1288
1289 Ok(source)
1290 }
1291
1292 pub fn scope(&self, prefixes: &[Path]) -> Option<Producer> {
1298 let prefixes = PathPrefixes::new(prefixes);
1299 Some(Producer {
1300 info: self.info,
1301 nodes: self.nodes.select(&prefixes)?,
1302 root: self.root.clone(),
1303 dynamic: self.dynamic.clone(),
1304 pool: self.pool.clone(),
1305 cache_duration: self.cache_duration,
1306 latency_default: self.latency_default,
1307 linger: self.linger,
1308 stats: self.stats.clone(),
1309 })
1310 }
1311
1312 pub fn dynamic(&self) -> Dynamic {
1321 Dynamic::new(self.info, self.root.clone(), self.dynamic.clone())
1322 }
1323
1324 pub fn consume(&self) -> Consumer {
1329 Consumer::new(
1332 self.info,
1333 self.root.clone(),
1334 self.nodes.clone(),
1335 self.dynamic.clone(),
1336 stats::Session::default(),
1337 )
1338 }
1339
1340 pub fn announces(&self) -> AnnounceProducer {
1346 AnnounceProducer::new(self.root.clone(), self.nodes.clone())
1347 }
1348
1349 pub fn with_root(&self, prefix: impl AsPath) -> Option<Self> {
1354 let prefix = prefix.as_path();
1355
1356 Some(Self {
1357 info: self.info,
1358 root: self.root.join(&prefix).to_owned(),
1359 nodes: self.nodes.root(&prefix)?,
1360 dynamic: self.dynamic.clone(),
1361 pool: self.pool.clone(),
1362 cache_duration: self.cache_duration,
1363 latency_default: self.latency_default,
1364 linger: self.linger,
1365 stats: self.stats.clone(),
1366 })
1367 }
1368
1369 pub fn root(&self) -> &Path<'_> {
1371 &self.root
1372 }
1373
1374 pub fn allowed(&self) -> impl Iterator<Item = &Path<'_>> {
1377 self.nodes.nodes.iter().map(|(root, _)| root)
1378 }
1379
1380 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
1382 self.root.join(path)
1383 }
1384
1385 #[cfg(test)]
1387 pub(crate) fn node_count(&self) -> usize {
1388 self.nodes.tree.lock().count()
1389 }
1390}
1391
1392const TRACK_IDLE_LINGER: Duration = Duration::from_secs(30);
1405
1406struct FrontRoute {
1408 id: u64,
1409 route: broadcast::Route,
1412 source: broadcast::Consumer,
1414}
1415
1416struct FrontState {
1418 path: PathOwned,
1420 self_origin: Origin,
1422 publisher: Option<Origin>,
1430 next_route: u64,
1433 routes: Vec<FrontRoute>,
1434 track_info: HashMap<Arc<str>, track::Info>,
1437 excluded: HashMap<Origin, usize>,
1450 active: Option<u64>,
1452 linger: Duration,
1455 closed: bool,
1460}
1461
1462impl FrontState {
1463 fn accept_track_info(&mut self, name: &Arc<str>, info: track::Info) -> Result<(), Error> {
1465 if self.closed {
1466 return Err(Error::Closed);
1467 }
1468 if let Some(expected) = self.track_info.get(name) {
1469 let track::Info {
1470 timescale,
1471 latency_max,
1472 priority,
1473 ordered,
1474 } = info;
1475 if timescale != expected.timescale
1476 || latency_max != expected.latency_max
1477 || priority != expected.priority
1478 || ordered != expected.ordered
1479 {
1480 return Err(Error::Unsupported);
1481 }
1482 } else {
1483 self.track_info.insert(name.clone(), info);
1484 }
1485 Ok(())
1486 }
1487
1488 fn pick(&self, keep: impl Fn(&FrontRoute) -> bool, untainted: bool) -> Option<u64> {
1495 let candidates: Vec<&FrontRoute> = self.routes.iter().filter(|r| keep(r)).collect();
1496 let candidates = match untainted {
1497 true => self.prefer_untainted(&candidates),
1498 false => candidates,
1499 };
1500 candidates
1501 .into_iter()
1502 .min_by_key(|r| route_order(&self.path.as_path(), r))
1503 .map(|r| r.id)
1504 }
1505
1506 fn best_route(&self) -> Option<u64> {
1511 self.pick(|_| true, true)
1512 }
1513
1514 fn dispatch(&self, exclude: Option<Origin>) -> Option<u64> {
1521 self.pick(|r| exclude.is_none_or(|origin| !r.route.hops.contains(&origin)), false)
1522 }
1523
1524 fn taints_a_reader(&self, route: &broadcast::Route) -> bool {
1531 route.hops.iter().any(|hop| self.excluded.contains_key(hop))
1532 }
1533
1534 fn holds_local_publisher(&self) -> bool {
1543 self.publisher.is_none() && self.routes.iter().any(|r| r.route.announce)
1544 }
1545
1546 fn prefer_untainted<'a>(&self, candidates: &[&'a FrontRoute]) -> Vec<&'a FrontRoute> {
1556 if self.excluded.is_empty() {
1557 return candidates.to_vec();
1558 }
1559 let clean: Vec<&FrontRoute> = candidates
1560 .iter()
1561 .copied()
1562 .filter(|r| !self.taints_a_reader(&r.route))
1563 .collect();
1564 match clean.is_empty() {
1565 true => candidates.to_vec(),
1566 false => clean,
1567 }
1568 }
1569
1570 fn serve_route(&self, skip: impl Fn(u64) -> bool) -> Option<u64> {
1579 if let Some(active) = self.active
1580 && !skip(active)
1581 && let Some(route) = self.routes.iter().find(|r| r.id == active)
1582 && !self.taints_a_reader(&route.route)
1583 {
1584 return Some(active);
1585 }
1586 self.pick(|r| !skip(r.id), true)
1587 }
1588
1589 fn reselect(&mut self, carrying: bool) {
1607 let best = self.best_route();
1608 if carrying
1609 && let (Some(best_id), Some(cur_id)) = (best, self.active)
1610 && best_id != cur_id
1611 && let Some(candidate) = self.routes.iter().find(|r| r.id == best_id)
1612 && let Some(incumbent) = self.routes.iter().find(|r| r.id == cur_id)
1613 && incumbent.route.announce
1614 && candidate.route.cost < incumbent.route.cost
1615 && candidate.route.advertised == 0
1616 && candidate.route.hops.len() >= 2
1617 && !self.handover_allowed(&candidate.route)
1618 {
1619 return;
1621 }
1622 self.active = best;
1623 }
1624
1625 fn handover_allowed(&self, route: &broadcast::Route) -> bool {
1637 let name = self.path.as_path();
1638 match route.hops.iter().last() {
1639 Some(peer) => fnv_key(&name, [*peer]) < fnv_key(&name, [self.self_origin]),
1640 None => true,
1641 }
1642 }
1643
1644 fn routes_snapshot(&self) -> Vec<broadcast::Route> {
1650 let mut routes: Vec<&FrontRoute> = self.routes.iter().collect();
1651 routes.sort_by_key(|r| route_order(&self.path.as_path(), r));
1652 routes.sort_by_key(|r| Some(r.id) != self.active);
1653 routes.into_iter().map(|r| r.route.clone()).collect()
1654 }
1655}
1656
1657fn sync_front(state: &kio::Producer<FrontState>, broadcast: &broadcast::Producer, leaf: &Lock<OriginNode>) {
1667 let mut leaf_guard = leaf.lock();
1672 let routes = state.read().routes_snapshot();
1673 if let Some(advert) = routes.first() {
1674 let announce = advert.announce;
1675 broadcast.clone().set_routes(routes);
1676 leaf_guard.set_announced(state, announce);
1677 }
1678}
1679
1680fn detach_source(
1693 state: &kio::Producer<FrontState>,
1694 broadcast: &broadcast::Producer,
1695 leaf: &Lock<OriginNode>,
1696 id: u64,
1697 graceful: bool,
1698) {
1699 let close = {
1700 let carrying = broadcast.demand().is_used();
1704 let Ok(mut s) = state.write() else { return };
1705 let Some(pos) = s.routes.iter().position(|r| r.id == id) else {
1706 return;
1707 };
1708 s.routes.remove(pos);
1709 s.reselect(carrying);
1710 if s.routes.is_empty() && !s.closed && (graceful || s.linger.is_zero()) {
1711 s.closed = true;
1714 true
1715 } else {
1716 false
1717 }
1718 };
1719 if close {
1720 broadcast.abort_spliced(Error::Dropped);
1721 }
1722 sync_front(state, broadcast, leaf);
1723}
1724
1725fn sync_announce(guard: &mut Option<stats::Announce>, announced: bool, ingress: &stats::Scope) {
1728 match (announced, guard.is_some()) {
1729 (true, false) => *guard = Some(ingress.announce()),
1730 (false, true) => *guard = None,
1731 _ => {}
1732 }
1733}
1734
1735async fn run_source(
1745 origin: Info,
1746 tree: Lock<OriginNode>,
1747 full: PathOwned,
1748 mut source: broadcast::Consumer,
1749 ingress: stats::Scope,
1750 _reservation: SourceReservation,
1752) {
1753 let ctx = AttachContext {
1754 origin: &origin,
1755 tree: &tree,
1756 full: &full,
1757 };
1758
1759 let Ok(mut route) = source.route_changed().await else {
1763 return;
1765 };
1766
1767 let mut announce = route.announce.then(|| ingress.announce());
1772 let mut may_take_over = true;
1778
1779 let leaf = if full.is_empty() {
1783 tree.clone()
1784 } else {
1785 tree.lock().leaf(&full)
1786 };
1787
1788 'attach: loop {
1789 let (state, broadcast, id) = match attach_source(&ctx, &leaf, &source, route.clone(), may_take_over) {
1790 Attach::Ready(state, broadcast, id) => (state, broadcast, id),
1791 Attach::Parked(incumbent) => {
1792 tracing::debug!(
1793 broadcast = %full,
1794 "path already live with a different publisher; parking this source until it ends",
1795 );
1796 let held = incumbent.read().holds_local_publisher();
1801 let update = kio::wait(|waiter| {
1805 if let Poll::Ready(update) = source.poll_route_changed(waiter) {
1806 return Poll::Ready(Some(update));
1807 }
1808 match incumbent.poll(waiter, |s| {
1812 if s.closed || s.holds_local_publisher() != held {
1813 Poll::Ready(())
1814 } else {
1815 Poll::Pending
1816 }
1817 }) {
1818 Poll::Ready(_) => Poll::Ready(None),
1819 Poll::Pending => Poll::Pending,
1820 }
1821 })
1822 .await;
1823 match update {
1824 Some(Ok(update)) => {
1826 sync_announce(&mut announce, update.announce, &ingress);
1827 if update.hops.iter().next().copied() != route.hops.iter().next().copied() {
1832 may_take_over = true;
1833 }
1834 route = update;
1835 }
1836 Some(Err(_)) => return,
1838 None => {}
1840 }
1841 continue 'attach;
1842 }
1843 };
1844 let publisher = route.hops.iter().next().copied();
1845
1846 loop {
1847 let update = kio::wait(|waiter| {
1848 if state
1854 .poll_ref(waiter, |s| if s.closed { Poll::Ready(()) } else { Poll::Pending })
1855 .is_ready()
1856 {
1857 return Poll::Ready(None);
1858 }
1859 source.poll_route_changed(waiter).map(Some)
1860 })
1861 .await;
1862 match update {
1863 None => {
1864 may_take_over = false;
1867 continue 'attach;
1868 }
1869 Some(Ok(update)) => {
1870 let announced = update.announce;
1871 if update.hops.iter().next().copied() != publisher {
1882 detach_source(&state, &broadcast, &leaf, id, true);
1883 sync_announce(&mut announce, announced, &ingress);
1884 may_take_over = true;
1888 route = update;
1889 continue 'attach;
1890 }
1891 {
1892 let carrying = broadcast.demand().is_used();
1893 let Ok(mut s) = state.write() else { return };
1894 let Some(entry) = s.routes.iter_mut().find(|r| r.id == id) else {
1895 return;
1896 };
1897 if entry.route == update {
1898 continue;
1899 }
1900 entry.route = update;
1901 s.reselect(carrying);
1902 }
1903 sync_announce(&mut announce, announced, &ingress);
1905 sync_front(&state, &broadcast, &leaf);
1906 }
1907 Some(Err(_)) => {
1908 detach_source(&state, &broadcast, &leaf, id, source.is_finished());
1911 return;
1912 }
1913 }
1914 }
1915 }
1916}
1917
1918enum Attach {
1920 Ready(kio::Producer<FrontState>, broadcast::Producer, u64),
1923 Parked(kio::Producer<FrontState>),
1931}
1932
1933struct AttachContext<'a> {
1935 origin: &'a Info,
1936 tree: &'a Lock<OriginNode>,
1939 full: &'a PathOwned,
1942}
1943
1944fn same_publisher(a: Option<Origin>, b: Option<Origin>) -> bool {
1953 if a == Some(Origin::UNKNOWN) || b == Some(Origin::UNKNOWN) {
1954 return false;
1955 }
1956 a == b
1957}
1958
1959fn attach_source(
1988 ctx: &AttachContext,
1989 leaf: &Lock<OriginNode>,
1990 source: &broadcast::Consumer,
1991 route: broadcast::Route,
1992 may_take_over: bool,
1993) -> Attach {
1994 let publisher = route.hops.iter().next().copied();
1995 let mut leaf_guard = leaf.lock();
1996
1997 if let Some(existing) = &leaf_guard.broadcast {
2000 let mut joined = None;
2001 let carrying = existing.broadcast.demand().is_used();
2002 if let Ok(mut s) = existing.state.write()
2003 && !s.closed
2004 {
2005 if same_publisher(s.publisher, publisher) {
2006 let id = s.next_route;
2007 s.next_route += 1;
2008 s.routes.push(FrontRoute {
2009 id,
2010 route: route.clone(),
2011 source: source.clone(),
2012 });
2013 s.reselect(carrying);
2014 joined = Some(id);
2015 } else if !may_take_over || !route.announce || s.taints_a_reader(&route) || s.holds_local_publisher() {
2016 return Attach::Parked(existing.state.clone());
2017 } else {
2018 s.closed = true;
2026 tracing::warn!(broadcast = %ctx.full, "replacing a live broadcast from a different publisher");
2027 }
2028 }
2029 if let Some(id) = joined {
2030 let state = existing.state.clone();
2031 let broadcast = existing.broadcast.clone();
2032 drop(leaf_guard);
2033 sync_front(&state, &broadcast, leaf);
2034 return Attach::Ready(state, broadcast, id);
2035 }
2036 }
2037
2038 let announce = route.announce;
2040 let broadcast = broadcast::Producer::new_spliced(broadcast::Info {
2041 origin: ctx.origin.clone(),
2042 });
2043 let _ = broadcast.clone().set_route(route.clone());
2044 let state = kio::Producer::new(FrontState {
2045 path: ctx.full.clone(),
2046 self_origin: ctx.origin.id,
2047 publisher,
2048 next_route: 1,
2049 excluded: HashMap::new(),
2050 track_info: HashMap::new(),
2051 routes: vec![FrontRoute {
2052 id: 0,
2053 route,
2054 source: source.clone(),
2055 }],
2056 active: Some(0),
2057 linger: ctx.origin.linger,
2058 closed: false,
2059 });
2060
2061 if let Some(stale) = leaf_guard.broadcast.take()
2065 && stale.announced
2066 {
2067 leaf_guard.notify.lock().unannounce(&stale.path);
2068 }
2069 let entry = OriginBroadcast {
2070 path: ctx.full.clone(),
2071 broadcast: broadcast.clone(),
2072 state: state.clone(),
2073 announced: announce,
2074 };
2075 if entry.announced {
2076 leaf_guard
2077 .notify
2078 .lock()
2079 .announce(ctx.full, &broadcast.consume(), &state);
2080 }
2081 leaf_guard.broadcast = Some(entry);
2082 drop(leaf_guard);
2083
2084 web_async::spawn(run_front(
2085 state.clone(),
2086 broadcast.clone(),
2087 ctx.tree.clone(),
2088 ctx.full.clone(),
2089 ));
2090
2091 Attach::Ready(state, broadcast, 0)
2092}
2093
2094async fn run_front(
2097 state: kio::Producer<FrontState>,
2098 mut broadcast: broadcast::Producer,
2099 tree: Lock<OriginNode>,
2100 full: PathOwned,
2101) {
2102 enum Step {
2103 Serve(Arc<str>, super::resume::Producer),
2104 Changed,
2106 Expired,
2108 Closed,
2109 }
2110
2111 let linger = state.read().linger;
2112 let mut deadline = kio::time::Deadline::new();
2117
2118 loop {
2119 let empty = {
2120 let s = state.read();
2121 !s.closed && s.routes.is_empty()
2122 };
2123 deadline.set(match (empty, deadline.deadline()) {
2124 (true, None) => web_async::time::Instant::now().checked_add(linger),
2127 (true, at) => at,
2128 (false, _) => None,
2129 });
2130
2131 let step = {
2132 kio::wait(|waiter| {
2133 if let Poll::Ready((name, resume)) = broadcast.poll_spliced_assigned(waiter) {
2134 return Poll::Ready(Step::Serve(name, resume));
2135 }
2136 match state.poll(waiter, |s| {
2139 if s.closed || s.routes.is_empty() != empty {
2140 Poll::Ready(())
2141 } else {
2142 Poll::Pending
2143 }
2144 }) {
2145 Poll::Ready(Ok(guard)) => {
2146 return Poll::Ready(if guard.closed { Step::Closed } else { Step::Changed });
2147 }
2148 Poll::Ready(Err(_)) => return Poll::Ready(Step::Closed),
2149 Poll::Pending => {}
2150 }
2151 deadline.poll(waiter).map(|_| Step::Expired)
2152 })
2153 .await
2154 };
2155
2156 match step {
2157 Step::Serve(name, resume) => {
2158 web_async::spawn(serve_track(state.clone(), name, resume));
2161 }
2162 Step::Changed => {}
2163 Step::Expired => {
2164 let close = {
2168 let Ok(mut s) = state.write() else { break };
2169 if !s.closed && s.routes.is_empty() {
2170 s.closed = true;
2171 true
2172 } else {
2173 false
2174 }
2175 };
2176 if close {
2177 break;
2178 }
2179 }
2180 Step::Closed => break,
2181 }
2182 }
2183
2184 broadcast.abort_spliced(Error::Dropped);
2186
2187 broadcast.finish();
2189
2190 tree.lock().remove(&state, &full);
2193}
2194
2195async fn serve_track(state: kio::Producer<FrontState>, name: Arc<str>, mut resume: super::resume::Producer) {
2209 enum Step {
2210 Closed,
2211 Splice(u64, broadcast::Consumer),
2212 Complete,
2213 Failed(Error),
2214 NoRoute,
2219 Idle,
2221 Demand,
2223 }
2224
2225 let mut serving: Option<(u64, track::Consumer)> = None;
2227 let mut spliced_edge: Option<u64> = None;
2233 let mut refused: HashSet<u64> = HashSet::new();
2238 let mut refusal: Option<Error> = None;
2239 let mut dead: HashSet<u64> = HashSet::new();
2244 let mut idle_since: Option<web_async::time::Instant> = None;
2246 let mut deadline = kio::time::Deadline::new();
2247
2248 loop {
2249 let serving_id = serving.as_ref().map(|(id, _)| *id);
2250
2251 {
2258 let s = state.read();
2259 refused.retain(|id| s.routes.iter().any(|r| r.id == *id));
2260 dead.retain(|id| s.routes.iter().any(|r| r.id == *id));
2261 let exhausted = !s.routes.is_empty()
2262 && s.serve_route(|id| refused.contains(&id) || dead.contains(&id))
2263 .is_none();
2264 if exhausted && dead.is_empty() {
2265 drop(s);
2266 let err = refusal.take().unwrap_or(Error::NotFound);
2267 tracing::debug!(name = %name, %err, "every source refused track; aborting");
2268 let _ = resume.abort(err);
2269 return;
2270 }
2271 }
2272
2273 let used = resume.is_used();
2285 idle_since = match (resume.is_spliced(), used) {
2286 (true, false) => idle_since.or_else(|| Some(web_async::time::Instant::now())),
2287 _ => None,
2288 };
2289 deadline.set(idle_since.and_then(|at| at.checked_add(TRACK_IDLE_LINGER)));
2290
2291 let step = {
2292 let skip = |id: u64| refused.contains(&id) || dead.contains(&id);
2293 kio::wait(|waiter| {
2294 match state.poll(waiter, |s| {
2300 let gone = serving_id.is_some_and(|id| !s.routes.iter().any(|r| r.id == id));
2301 if s.closed
2302 || (used && (gone || matches!(s.serve_route(skip), Some(next) if Some(next) != serving_id)))
2303 {
2304 Poll::Ready(())
2305 } else {
2306 Poll::Pending
2307 }
2308 }) {
2309 Poll::Ready(Ok(guard)) => {
2310 if guard.closed {
2311 return Poll::Ready(Step::Closed);
2312 }
2313 let Some(next) = guard.serve_route(skip) else {
2314 return Poll::Ready(Step::NoRoute);
2315 };
2316 let source = guard
2317 .routes
2318 .iter()
2319 .find(|r| r.id == next)
2320 .expect("servable source in table")
2321 .source
2322 .clone();
2323 return Poll::Ready(Step::Splice(next, source));
2324 }
2325 Poll::Ready(Err(_)) => return Poll::Ready(Step::Closed),
2326 Poll::Pending => {}
2327 }
2328
2329 let edge = match used {
2334 true => resume.poll_unused(waiter),
2335 false => resume.poll_used(waiter),
2336 };
2337 if edge.is_ready() {
2338 return Poll::Ready(Step::Demand);
2339 }
2340
2341 if let Some((_, track)) = &serving
2344 && let Poll::Ready(result) = track.poll_complete(waiter)
2345 {
2346 return Poll::Ready(match result {
2347 Ok(()) => Step::Complete,
2348 Err(err) => Step::Failed(err),
2349 });
2350 }
2351
2352 deadline.poll(waiter).map(|_| Step::Idle)
2353 })
2354 .await
2355 };
2356
2357 match step {
2358 Step::Closed => return,
2360 Step::Complete => {
2361 let _ = resume.finish();
2362 return;
2363 }
2364 Step::Failed(err) => {
2365 if resume.latest() == spliced_edge
2373 && let Some(id) = serving_id
2374 {
2375 let closing = state
2376 .read()
2377 .routes
2378 .iter()
2379 .find(|r| r.id == id)
2380 .is_some_and(|r| r.source.is_closing());
2381 if closing {
2382 dead.insert(id);
2383 } else {
2384 refused.insert(id);
2385 refusal = Some(err);
2386 }
2387 }
2388 serving = None;
2389 }
2390 Step::Demand => {}
2392 Step::NoRoute => serving = None,
2398 Step::Idle => {
2399 if resume.release().is_err() {
2404 return;
2406 }
2407 serving = None;
2408 }
2409 Step::Splice(id, source) => {
2410 let attempt = match source.track(&name) {
2414 Ok(track) => {
2415 let query = track.info().into_inner();
2418 let skip = |id: u64| refused.contains(&id) || dead.contains(&id);
2419 let info = kio::wait(|waiter| {
2420 if let Poll::Ready(result) = query.poll(waiter) {
2421 return Poll::Ready(Some(result));
2422 }
2423 match state.poll(waiter, |s| {
2424 if s.closed || s.serve_route(skip) != Some(id) {
2425 Poll::Ready(())
2426 } else {
2427 Poll::Pending
2428 }
2429 }) {
2430 Poll::Ready(_) => Poll::Ready(None),
2431 Poll::Pending => Poll::Pending,
2432 }
2433 })
2434 .await;
2435 match info {
2436 None => continue,
2438 Some(Ok(info)) => match track.poll_complete(&kio::Waiter::noop()) {
2441 Poll::Ready(Err(err)) => Err(err),
2442 _ => match state.write() {
2443 Ok(mut state) => state.accept_track_info(&name, info).map(|()| track),
2444 Err(_) => Err(Error::Dropped),
2445 },
2446 },
2447 Some(Err(err)) => Err(err),
2448 }
2449 }
2450 Err(err) => Err(err),
2451 };
2452
2453 match attempt {
2454 Ok(track) => {
2455 if let Err(err) = resume.takeover(&track) {
2456 let _ = resume.abort(err);
2461 return;
2462 }
2463 spliced_edge = resume.latest();
2475 serving = Some((id, track));
2476 }
2477 Err(_) if source.is_closing() => {
2481 dead.insert(id);
2482 serving = None;
2483 }
2484 Err(err) => {
2490 tracing::debug!(name = %name, source = id, %err, "source refused track");
2491 refused.insert(id);
2492 refusal = Some(err);
2493 serving = None;
2494 }
2495 }
2496 }
2497 }
2498 }
2499}
2500
2501#[derive(Default)]
2507struct OriginDynamicState {
2508 requests: Requests<PathOwned, kio::Producer<PendingBroadcast>>,
2511
2512 served: WeakCache<PathOwned, broadcast::WeakConsumer>,
2518}
2519
2520#[derive(Default)]
2527struct PendingBroadcast {
2528 resolved: Option<Result<broadcast::Consumer, Error>>,
2529}
2530
2531pub struct Dynamic {
2542 info: Origin,
2543 root: PathOwned,
2544 state: kio::Shared<OriginDynamicState>,
2545}
2546
2547impl Clone for Dynamic {
2548 fn clone(&self) -> Self {
2549 self.state.lock().requests.add_handler();
2553
2554 Self {
2555 info: self.info,
2556 root: self.root.clone(),
2557 state: self.state.clone(),
2558 }
2559 }
2560}
2561
2562impl Dynamic {
2563 fn new(info: Origin, root: PathOwned, state: kio::Shared<OriginDynamicState>) -> Self {
2564 state.lock().requests.add_handler();
2565
2566 Self { info, root, state }
2567 }
2568
2569 pub fn info(&self) -> &Origin {
2571 &self.info
2572 }
2573
2574 pub fn poll_requested_broadcast(&mut self, waiter: &kio::Waiter) -> Poll<Result<Request, Error>> {
2576 let mut state = ready!(self.state.poll(waiter, |state| {
2577 if state.requests.has_queued() {
2578 Poll::Ready(())
2579 } else {
2580 Poll::Pending
2581 }
2582 }));
2583
2584 let path = state.requests.pop().expect("predicate guaranteed a request");
2585 let producer = state.requests.get(&path).expect("popped key must be pending").clone();
2591 Poll::Ready(Ok(Request {
2592 path,
2593 producer,
2594 state: self.state.clone(),
2595 }))
2596 }
2597
2598 pub async fn requested_broadcast(&mut self) -> Result<Request, Error> {
2601 kio::wait(|waiter| self.poll_requested_broadcast(waiter)).await
2602 }
2603
2604 pub fn root(&self) -> &Path<'_> {
2606 &self.root
2607 }
2608}
2609
2610impl Drop for Dynamic {
2611 fn drop(&mut self) {
2612 let mut state = self.state.lock();
2615 if state.requests.remove_handler() {
2616 state.requests.drain_queued();
2620 }
2621 }
2622}
2623
2624pub struct Request {
2631 path: PathOwned,
2633
2634 producer: kio::Producer<PendingBroadcast>,
2637
2638 state: kio::Shared<OriginDynamicState>,
2640}
2641
2642impl Request {
2643 pub fn path(&self) -> &Path<'_> {
2645 &self.path
2646 }
2647
2648 pub fn accept(self, broadcast: impl Consume<broadcast::Consumer>) {
2654 let broadcast = broadcast.consume();
2655
2656 let resolved = {
2662 let mut state = self.state.lock();
2663 let existing = state.served.insert(self.path.clone(), broadcast.weak());
2664 state
2665 .requests
2666 .remove_if(&self.path, |producer| producer.same_channel(&self.producer));
2667 existing.map(|weak| weak.consume()).unwrap_or(broadcast)
2668 };
2669
2670 if let Ok(mut pending) = self.producer.write() {
2671 pending.resolved = Some(Ok(resolved));
2672 }
2673 }
2675
2676 pub fn reject(self, err: Error) {
2678 self.state
2679 .lock()
2680 .requests
2681 .remove_if(&self.path, |producer| producer.same_channel(&self.producer));
2682 if let Ok(mut state) = self.producer.write() {
2683 state.resolved = Some(Err(err));
2684 }
2685 }
2686}
2687
2688impl Drop for Request {
2689 fn drop(&mut self) {
2690 self.state
2698 .lock()
2699 .requests
2700 .remove_if(&self.path, |producer| producer.same_channel(&self.producer));
2701 }
2702}
2703
2704pub struct Requesting {
2711 inner: RequestState,
2712 stats: stats::Scope,
2715}
2716
2717enum RequestState {
2718 Ready(broadcast::Consumer),
2720 Failed(Error),
2723 Pending(kio::Consumer<PendingBroadcast>),
2725}
2726
2727impl Requesting {
2728 fn ready(broadcast: broadcast::Consumer) -> Self {
2729 Self {
2730 inner: RequestState::Ready(broadcast),
2731 stats: stats::Scope::default(),
2732 }
2733 }
2734
2735 fn failed(error: Error) -> Self {
2736 Self {
2737 inner: RequestState::Failed(error),
2738 stats: stats::Scope::default(),
2739 }
2740 }
2741
2742 fn pending(consumer: kio::Consumer<PendingBroadcast>) -> Self {
2743 Self {
2744 inner: RequestState::Pending(consumer),
2745 stats: stats::Scope::default(),
2746 }
2747 }
2748
2749 fn with_stats(mut self, scope: stats::Scope) -> Self {
2750 self.stats = scope;
2751 self
2752 }
2753
2754 pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<broadcast::Consumer, Error>> {
2756 match &self.inner {
2757 RequestState::Ready(broadcast) => Poll::Ready(Ok(broadcast.clone().with_stats(self.stats.clone()))),
2758 RequestState::Failed(error) => Poll::Ready(Err(error.clone())),
2759 RequestState::Pending(consumer) => Poll::Ready(
2760 match ready!(consumer.poll(waiter, |state| match &state.resolved {
2761 Some(result) => Poll::Ready(result.clone()),
2762 None => Poll::Pending,
2763 })) {
2764 Ok(result) => result.map(|broadcast| broadcast.with_stats(self.stats.clone())),
2765 Err(_closed) => Err(Error::Unroutable),
2767 },
2768 ),
2769 }
2770 }
2771}
2772
2773impl kio::Pollable for Requesting {
2774 type Output = Result<broadcast::Consumer, Error>;
2775
2776 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
2777 self.poll_ok(waiter)
2778 }
2779}
2780
2781pub trait Consume<T> {
2789 fn consume(&self) -> T;
2791}
2792
2793impl<T, U: Consume<T>> Consume<T> for &U {
2794 fn consume(&self) -> T {
2795 (**self).consume()
2796 }
2797}
2798
2799impl Consume<Consumer> for Producer {
2800 fn consume(&self) -> Consumer {
2801 Consumer::new(
2805 self.info,
2806 self.root.clone(),
2807 self.nodes.clone(),
2808 self.dynamic.clone(),
2809 stats::Session::default(),
2810 )
2811 }
2812}
2813
2814impl Consume<Consumer> for Consumer {
2815 fn consume(&self) -> Consumer {
2816 self.clone()
2817 }
2818}
2819
2820impl Consume<broadcast::Consumer> for broadcast::Producer {
2821 fn consume(&self) -> broadcast::Consumer {
2822 self.consume()
2824 }
2825}
2826
2827impl Consume<broadcast::Consumer> for broadcast::Consumer {
2828 fn consume(&self) -> broadcast::Consumer {
2829 self.clone()
2830 }
2831}
2832
2833impl Consume<track::Consumer> for track::Producer {
2834 fn consume(&self) -> track::Consumer {
2835 self.consume()
2836 }
2837}
2838
2839impl Consume<track::Consumer> for track::Consumer {
2840 fn consume(&self) -> track::Consumer {
2841 self.clone()
2842 }
2843}
2844
2845#[derive(Clone)]
2851pub struct Consumer {
2852 info: Origin,
2854 nodes: OriginNodes,
2855
2856 root: PathOwned,
2858
2859 dynamic: kio::Shared<OriginDynamicState>,
2862
2863 stats: stats::Session,
2867
2868 exclude: Option<Origin>,
2872}
2873
2874impl std::ops::Deref for Consumer {
2875 type Target = Origin;
2876
2877 fn deref(&self) -> &Self::Target {
2878 &self.info
2879 }
2880}
2881
2882impl Consumer {
2883 fn new(
2884 info: Origin,
2885 root: PathOwned,
2886 nodes: OriginNodes,
2887 dynamic: kio::Shared<OriginDynamicState>,
2888 stats: stats::Session,
2889 ) -> Self {
2890 Self {
2891 info,
2892 nodes,
2893 root,
2894 dynamic,
2895 stats,
2896 exclude: None,
2897 }
2898 }
2899
2900 pub(crate) fn excluding(mut self, peer: Origin) -> Self {
2905 self.exclude = Some(peer);
2906 self
2907 }
2908
2909 pub fn with_stats(mut self, session: stats::Session) -> Self {
2913 self.stats = session;
2914 self
2915 }
2916
2917 fn untagged(&self) -> Self {
2921 Self {
2922 stats: stats::Session::default(),
2923 ..self.clone()
2924 }
2925 }
2926
2927 pub(crate) fn empty(&self) -> Self {
2932 Self {
2933 info: self.info,
2934 nodes: OriginNodes::empty(),
2935 root: self.root.clone(),
2936 dynamic: self.dynamic.clone(),
2937 stats: self.stats.clone(),
2938 exclude: self.exclude,
2939 }
2940 }
2941
2942 pub fn announced(&self) -> AnnounceConsumer {
2949 AnnounceConsumer::new(self.root.clone(), self.nodes.clone(), self.stats.clone(), self.exclude)
2950 }
2951
2952 pub fn consume(&self) -> Self {
2954 self.clone()
2955 }
2956
2957 fn resolve(&self, path: impl AsPath) -> Resolved {
2966 let path = path.as_path();
2967 let Some(rest) = self.nodes.get(&path) else {
2968 return Resolved::OutOfScope;
2969 };
2970 let state = self.nodes.tree.lock();
2971 state.resolve_broadcast(&rest, self.exclude)
2972 }
2973
2974 #[cfg(test)]
2976 pub(crate) fn get_broadcast(&self, path: impl AsPath) -> Option<broadcast::Consumer> {
2977 match self.resolve(path) {
2978 Resolved::Found(broadcast) => Some(broadcast),
2979 Resolved::Excluded | Resolved::OutOfScope | Resolved::Missing => None,
2980 }
2981 }
2982
2983 pub async fn announced_broadcast(&self, path: impl AsPath) -> Option<broadcast::Consumer> {
2994 let path = path.as_path();
2995
2996 let consumer = self.scope(std::slice::from_ref(&path))?;
2998
2999 if !consumer.allowed().any(|allowed| path.has_prefix(allowed)) {
3003 return None;
3004 }
3005
3006 let mut announced = consumer.untagged().announced();
3010 let scope = self.stats.egress(self.root.join(&path).to_owned());
3011 loop {
3012 let OriginAnnounce {
3013 path: announced_path,
3014 broadcast,
3015 } = announced.next().await?;
3016 if announced_path.as_path() == path
3018 && let Some(broadcast) = broadcast
3019 {
3020 return Some(broadcast.with_stats(scope));
3021 }
3022 }
3023 }
3024
3025 pub fn scope(&self, prefixes: &[Path]) -> Option<Consumer> {
3031 let prefixes = PathPrefixes::new(prefixes);
3032 Some(Consumer {
3033 info: self.info,
3034 root: self.root.clone(),
3035 nodes: self.nodes.select(&prefixes)?,
3036 dynamic: self.dynamic.clone(),
3037 stats: self.stats.clone(),
3038 exclude: self.exclude,
3039 })
3040 }
3041
3042 pub fn request_broadcast(&self, path: impl AsPath) -> kio::Pending<Requesting> {
3062 let path = path.as_path();
3063
3064 let absolute = self.root.join(&path).to_owned();
3068 let scope = self.stats.egress(&absolute);
3069
3070 match self.resolve(&path) {
3085 Resolved::Found(broadcast) => return kio::Pending::new(Requesting::ready(broadcast).with_stats(scope)),
3086 Resolved::Excluded => return kio::Pending::new(Requesting::failed(Error::Unroutable)),
3087 Resolved::OutOfScope => return kio::Pending::new(Requesting::failed(Error::Unauthorized)),
3088 Resolved::Missing => {}
3089 }
3090
3091 let mut state = self.dynamic.lock();
3092
3093 if let Some(weak) = state.served.get(&absolute) {
3097 return kio::Pending::new(Requesting::ready(weak.consume()).with_stats(scope));
3098 }
3099
3100 let consumer = if let Some(producer) = state.requests.join(&absolute) {
3103 producer.consume()
3104 } else {
3105 let producer = kio::Producer::<PendingBroadcast>::default();
3106 let consumer = producer.consume();
3107 if state.requests.insert(absolute, producer).is_err() {
3108 return kio::Pending::new(Requesting::failed(Error::Unroutable));
3109 }
3110 consumer
3111 };
3112
3113 kio::Pending::new(Requesting::pending(consumer).with_stats(scope))
3114 }
3115
3116 pub fn with_root(&self, prefix: impl AsPath) -> Option<Self> {
3121 let prefix = prefix.as_path();
3122
3123 Some(Self {
3124 info: self.info,
3125 root: self.root.join(&prefix).to_owned(),
3126 nodes: self.nodes.root(&prefix)?,
3127 dynamic: self.dynamic.clone(),
3128 stats: self.stats.clone(),
3129 exclude: self.exclude,
3130 })
3131 }
3132
3133 pub fn root(&self) -> &Path<'_> {
3135 &self.root
3136 }
3137
3138 pub fn allowed(&self) -> impl Iterator<Item = &Path<'_>> {
3141 self.nodes.nodes.iter().map(|(root, _)| root)
3142 }
3143
3144 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
3146 self.root.join(path)
3147 }
3148}
3149
3150#[derive(Clone)]
3155pub struct AnnounceProducer {
3156 nodes: OriginNodes,
3157 root: PathOwned,
3158}
3159
3160impl AnnounceProducer {
3161 fn new(root: PathOwned, nodes: OriginNodes) -> Self {
3162 Self { nodes, root }
3163 }
3164
3165 pub fn consume(&self) -> AnnounceConsumer {
3171 AnnounceConsumer::new(self.root.clone(), self.nodes.clone(), stats::Session::default(), None)
3174 }
3175
3176 pub fn root(&self) -> &Path<'_> {
3178 &self.root
3179 }
3180}
3181
3182pub struct AnnounceConsumer {
3187 id: ConsumerId,
3188 nodes: OriginNodes,
3189 root: PathOwned,
3190
3191 state: kio::Producer<OriginConsumerState>,
3194
3195 stats: stats::Session,
3198
3199 guards: HashMap<PathOwned, stats::Announce>,
3203}
3204
3205impl AnnounceConsumer {
3206 fn new(root: PathOwned, nodes: OriginNodes, stats: stats::Session, exclude: Option<Origin>) -> Self {
3207 let state = kio::Producer::<OriginConsumerState>::default();
3208 let id = ConsumerId::new();
3209
3210 for (_, absolute) in &nodes.nodes {
3211 let notify = AnnounceConsumerNotify {
3212 root: root.clone(),
3213 state: state.clone(),
3214 exclude,
3215 };
3216 nodes.tree.lock().consume_at(id, notify, absolute);
3217 }
3218
3219 Self {
3220 id,
3221 nodes,
3222 root,
3223 state,
3224 stats,
3225 guards: HashMap::new(),
3226 }
3227 }
3228
3229 fn attribute(&mut self, update: OriginAnnounce) -> OriginAnnounce {
3235 let OriginAnnounce { path, broadcast } = update;
3236 let absolute = self.root.join(&path).to_owned();
3237 match broadcast {
3238 Some(broadcast) => {
3239 let scope = self.stats.egress(&absolute);
3240 self.guards.entry(absolute).or_insert_with(|| scope.announce());
3241 OriginAnnounce {
3242 path,
3243 broadcast: Some(broadcast.with_stats(scope)),
3244 }
3245 }
3246 None => {
3247 self.guards.remove(&absolute);
3248 OriginAnnounce { path, broadcast: None }
3249 }
3250 }
3251 }
3252
3253 pub async fn next(&mut self) -> Option<OriginAnnounce> {
3260 kio::wait(|waiter| self.poll_next(waiter)).await
3261 }
3262
3263 pub fn poll_next(&mut self, waiter: &kio::Waiter) -> Poll<Option<OriginAnnounce>> {
3269 let update = {
3270 let mut state = match ready!(self.state.poll(waiter, |state| {
3271 if state.pending.is_empty() {
3272 Poll::Pending
3273 } else {
3274 Poll::Ready(())
3275 }
3276 })) {
3277 Ok(state) => state,
3278 Err(_) => return Poll::Ready(None),
3280 };
3281 state.take().expect("predicate guaranteed an update")
3282 };
3283 Poll::Ready(Some(self.attribute(update)))
3284 }
3285
3286 pub fn try_next(&mut self) -> Option<OriginAnnounce> {
3291 let update = self.state.write().ok()?.take()?;
3292 Some(self.attribute(update))
3293 }
3294
3295 pub fn is_closed(&self) -> bool {
3297 self.state.write().is_err()
3298 }
3299
3300 pub fn root(&self) -> &Path<'_> {
3302 &self.root
3303 }
3304
3305 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
3307 self.root.join(path)
3308 }
3309}
3310
3311impl Drop for AnnounceConsumer {
3312 fn drop(&mut self) {
3313 for (_, absolute) in &self.nodes.nodes {
3314 self.nodes.tree.lock().detach(Claim::Consumer(self.id), absolute);
3315 }
3316 }
3317}
3318
3319#[cfg(test)]
3320use futures::FutureExt;
3321
3322#[cfg(test)]
3323#[allow(missing_docs)] impl AnnounceConsumer {
3325 pub fn assert_next(&mut self, expected: impl AsPath, broadcast: &broadcast::Consumer) {
3326 let expected = expected.as_path();
3327 let announce = self.next().now_or_never().expect("next blocked").expect("no next");
3328 assert_eq!(announce.path, expected, "wrong path");
3329 let announced = announce.broadcast.expect("should be an active announce");
3330 assert!(announced.is_clone(broadcast), "should be the same broadcast");
3331 }
3332
3333 pub fn assert_next_some(&mut self, expected: impl AsPath) -> broadcast::Consumer {
3337 let expected = expected.as_path();
3338 let announce = self.next().now_or_never().expect("next blocked").expect("no next");
3339 assert_eq!(announce.path, expected, "wrong path");
3340 announce.broadcast.expect("should be an active announce")
3341 }
3342
3343 pub fn assert_try_next(&mut self, expected: impl AsPath, broadcast: &broadcast::Consumer) {
3344 let expected = expected.as_path();
3345 let announce = self.try_next().expect("no next");
3346 assert_eq!(announce.path, expected, "wrong path");
3347 let announced = announce.broadcast.expect("should be an active announce");
3348 assert!(announced.is_clone(broadcast), "should be the same broadcast");
3349 }
3350
3351 pub fn assert_try_next_some(&mut self, expected: impl AsPath) -> broadcast::Consumer {
3353 let expected = expected.as_path();
3354 let announce = self.try_next().expect("no next");
3355 assert_eq!(announce.path, expected, "wrong path");
3356 announce.broadcast.expect("should be an active announce")
3357 }
3358
3359 pub fn assert_next_none(&mut self, expected: impl AsPath) {
3360 let expected = expected.as_path();
3361 let announce = self.next().now_or_never().expect("next blocked").expect("no next");
3362 assert_eq!(announce.path, expected, "wrong path");
3363 assert!(announce.broadcast.is_none(), "should be unannounced");
3364 }
3365
3366 pub fn assert_next_wait(&mut self) {
3367 if let Some(res) = self.next().now_or_never() {
3368 panic!("next should block: got {:?}", res.map(|a| a.path));
3369 }
3370 }
3371
3372 }
3381
3382#[cfg(test)]
3383mod tests {
3384 use crate::Timescale;
3385 use crate::coding::Decode;
3386 use crate::group;
3387
3388 use super::*;
3389
3390 fn announce() -> broadcast::Route {
3392 broadcast::Route::new().with_announce(true)
3393 }
3394
3395 fn origin_keyed(name: &str, peer: Origin, above: bool) -> Origin {
3401 let name = Path::new(name);
3402 let peer_key = fnv_key(&name, [peer]);
3403 (100u64..)
3404 .map(|id| Origin::new(id).unwrap())
3405 .find(|origin| (fnv_key(&name, [*origin]) > peer_key) == above)
3406 .unwrap()
3407 }
3408
3409 fn front_state(self_origin: Origin, routes: Vec<broadcast::Route>) -> FrontState {
3412 let source = broadcast::Info::new().produce().consume();
3413 FrontState {
3414 path: Path::new("test").to_owned(),
3415 self_origin,
3416 publisher: routes.first().and_then(|r| r.hops.iter().next().copied()),
3417 next_route: routes.len() as u64,
3418 excluded: HashMap::new(),
3419 track_info: HashMap::new(),
3420 routes: routes
3421 .into_iter()
3422 .enumerate()
3423 .map(|(id, route)| FrontRoute {
3424 id: id as u64,
3425 route,
3426 source: source.clone(),
3427 })
3428 .collect(),
3429 active: Some(0),
3430 linger: Duration::ZERO,
3431 closed: false,
3432 }
3433 }
3434
3435 fn sibling_route(peer: Origin) -> broadcast::Route {
3438 let hops = OriginList::try_from(vec![Origin::new(90).unwrap(), peer]).unwrap();
3439 announce().with_hops(hops)
3440 }
3441
3442 fn upstream_route(cost: u64) -> broadcast::Route {
3444 let hops = OriginList::try_from(vec![Origin::new(90).unwrap()]).unwrap();
3445 announce().with_hops(hops).with_cost(cost)
3446 }
3447
3448 #[test]
3452 fn test_carrying_gate_keys() {
3453 let peer = Origin::new(3).unwrap();
3454
3455 let mut lost = front_state(
3457 origin_keyed("test", peer, false),
3458 vec![upstream_route(10), sibling_route(peer)],
3459 );
3460 lost.reselect(true);
3461 assert_eq!(
3462 lost.active,
3463 Some(0),
3464 "carrying front re-parented onto a higher-keyed peer"
3465 );
3466 lost.reselect(false);
3467 assert_eq!(lost.active, Some(1), "idle front must take the cheaper route");
3468
3469 let mut won = front_state(
3471 origin_keyed("test", peer, true),
3472 vec![upstream_route(10), sibling_route(peer)],
3473 );
3474 won.reselect(true);
3475 assert_eq!(won.active, Some(1), "carrying front must follow a lower-keyed peer");
3476 }
3477
3478 #[test]
3483 fn test_carrying_gate_symmetric_race() {
3484 let a = Origin::new(1).unwrap();
3485 let b = Origin::new(2).unwrap();
3486
3487 let mut a_view = front_state(a, vec![upstream_route(10), sibling_route(b)]);
3488 let mut b_view = front_state(b, vec![upstream_route(10), sibling_route(a)]);
3489 a_view.reselect(true);
3490 b_view.reselect(true);
3491
3492 let a_moved = a_view.active == Some(1);
3493 let b_moved = b_view.active == Some(1);
3494 assert!(
3495 a_moved != b_moved,
3496 "exactly one side must re-parent (a: {a_moved}, b: {b_moved})"
3497 );
3498 }
3499
3500 #[test]
3505 fn test_carrying_switches_to_benign_routes() {
3506 let peer = Origin::new(3).unwrap();
3507 let lost = origin_keyed("test", peer, false);
3508
3509 let mut forwarder = sibling_route(peer).with_cost(4);
3511 forwarder.advertised = 4;
3512 let mut state = front_state(lost, vec![upstream_route(10), forwarder]);
3513 state.reselect(true);
3514 assert_eq!(
3515 state.active,
3516 Some(1),
3517 "a cheaper forwarder path must win while carrying"
3518 );
3519
3520 let direct = announce().with_hops(OriginList::try_from(vec![peer]).unwrap());
3522 let mut state = front_state(lost, vec![upstream_route(10), direct]);
3523 state.reselect(true);
3524 assert_eq!(
3525 state.active,
3526 Some(1),
3527 "a direct publisher route must win while carrying"
3528 );
3529
3530 let mut state = front_state(lost, vec![sibling_route(peer), sibling_route(peer)]);
3535 state.reselect(true);
3536 assert_eq!(
3537 state.active,
3538 Some(1),
3539 "a reconnect on an identical chain must win while carrying"
3540 );
3541 }
3542
3543 #[test]
3546 fn test_carrying_gate_ignores_unannounced_incumbent() {
3547 let peer = Origin::new(3).unwrap();
3548 let unannounced = upstream_route(10).with_announce(false);
3549 let mut state = front_state(
3550 origin_keyed("test", peer, false),
3551 vec![unannounced, sibling_route(peer)],
3552 );
3553 state.reselect(true);
3554 assert_eq!(
3555 state.active,
3556 Some(1),
3557 "an unannounced incumbent must always be displaced"
3558 );
3559 }
3560
3561 #[test]
3566 fn test_reflection_through_an_exposed_peer_cannot_take_over() {
3567 let peer = Origin::new(42).unwrap();
3568 let upstream = OriginList::try_from(vec![Origin::new(7).unwrap()]).unwrap();
3569 let reflected = announce().with_hops(OriginList::try_from(vec![peer]).unwrap());
3570
3571 let mut state = front_state(Origin::new(1).unwrap(), vec![announce().with_hops(upstream)]);
3572
3573 assert!(!state.taints_a_reader(&reflected));
3575
3576 *state.excluded.entry(peer).or_default() += 1;
3579 assert!(state.taints_a_reader(&reflected));
3580 }
3581
3582 #[test]
3585 fn test_rival_publisher_through_another_peer_still_takes_over() {
3586 let peer = Origin::new(42).unwrap();
3587 let elsewhere = Origin::new(43).unwrap();
3588 let upstream = OriginList::try_from(vec![Origin::new(7).unwrap()]).unwrap();
3589 let rival = announce().with_hops(OriginList::try_from(vec![Origin::UNKNOWN, elsewhere]).unwrap());
3590
3591 let mut state = front_state(Origin::new(1).unwrap(), vec![announce().with_hops(upstream)]);
3592 *state.excluded.entry(peer).or_default() += 1;
3593
3594 assert!(!state.taints_a_reader(&rival), "only the peer we feed is a reflection");
3595 }
3596
3597 #[test]
3602 fn test_opaque_peer_understates_its_depth() {
3603 let us = Origin::new(1).unwrap();
3604
3605 let direct = || {
3607 announce()
3608 .with_hops(OriginList::try_from(vec![Origin::new(7).unwrap(), Origin::new(8).unwrap()]).unwrap())
3609 .with_cost(2)
3610 };
3611 let opaque = |cost| {
3614 announce()
3615 .with_hops(OriginList::try_from(vec![Origin::new(42).unwrap()]).unwrap())
3616 .with_cost(cost)
3617 };
3618
3619 let mut state = front_state(us, vec![direct(), opaque(1)]);
3621 state.reselect(false);
3622 assert_eq!(
3623 state.active,
3624 Some(1),
3625 "an unpriced opaque link out-ranks a shorter real path"
3626 );
3627
3628 let mut state = front_state(us, vec![direct(), opaque(16)]);
3630 state.reselect(false);
3631 assert_eq!(
3632 state.active,
3633 Some(0),
3634 "pricing the opaque link restores the intended order"
3635 );
3636 }
3637
3638 async fn settle() {
3641 tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
3642 }
3643
3644 fn origin_scoped(prefixes: &[Path]) -> Producer {
3646 Origin::random().produce().scope(prefixes).expect("in scope")
3647 }
3648
3649 async fn accept_track(dynamic: &mut broadcast::Dynamic, name: &str) -> track::Producer {
3652 let request = tokio::time::timeout(std::time::Duration::from_secs(1), dynamic.requested_track())
3653 .await
3654 .expect("timed out waiting for a track request")
3655 .expect("source closed");
3656 assert_eq!(request.name(), name, "unexpected track dispatched");
3657 request.accept(None)
3658 }
3659
3660 async fn accept_tracks(dynamic: &mut broadcast::Dynamic, count: usize) -> HashMap<String, track::Producer> {
3664 let mut accepted = HashMap::new();
3665 for _ in 0..count {
3666 let request = tokio::time::timeout(std::time::Duration::from_secs(1), dynamic.requested_track())
3667 .await
3668 .expect("timed out waiting for a track request")
3669 .expect("source closed");
3670 let name = request.name().to_string();
3671 accepted.insert(name, request.accept(None));
3672 }
3673 accepted
3674 }
3675
3676 #[tokio::test]
3680 async fn test_stats_tagged_end_to_end() {
3681 use crate::Timestamp;
3682 use crate::stats::{Config, Registry, Tier};
3683 use bytes::Bytes;
3684
3685 tokio::time::pause();
3686
3687 let registry = Registry::new(Config::new());
3688 let ctx = registry.tier(Tier::default()).session("acme");
3689
3690 let origin = Origin::random().produce();
3691 let ingress = origin.clone().with_stats(ctx.clone());
3692 let egress = origin.consume().with_stats(ctx.clone());
3693
3694 let mut announced = egress.announced();
3697
3698 let source = ingress.create_broadcast("demo", announce()).unwrap();
3700 let mut dynamic = source.dynamic();
3701 settle().await;
3702 settle().await;
3703
3704 let update = announced.next().await.unwrap();
3706 assert_eq!(update.path.as_str(), "demo");
3707 let broadcast = update.broadcast.unwrap();
3708
3709 let subscribing = broadcast.track("video").unwrap().subscribe(None);
3711 let mut producer = accept_track(&mut dynamic, "video").await;
3712 settle().await;
3713 let mut sub = subscribing.await.unwrap();
3714
3715 let mut group = producer.append_group().unwrap();
3717 group
3718 .write_frame(Timestamp::ZERO, Bytes::from_static(b"hello"))
3719 .unwrap();
3720 group
3721 .write_frame(Timestamp::ZERO, Bytes::from_static(b"world"))
3722 .unwrap();
3723 group.finish().unwrap();
3724
3725 let mut group_c = sub.recv_group().await.unwrap().unwrap();
3727 let mut frames = 0;
3728 while let Some(frame) = group_c.read_frame().await.unwrap() {
3729 assert_eq!(frame.payload.len(), 5);
3730 frames += 1;
3731 }
3732 assert_eq!(frames, 2);
3733 settle().await;
3734
3735 let report = registry.report();
3736 let entry = report
3737 .traffic
3738 .iter()
3739 .find(|e| e.path.as_str() == "demo")
3740 .expect("demo tracked");
3741 let path_len = "demo".len() as u64;
3742
3743 let egress = &entry.publisher;
3745 assert_eq!(egress.announced, 1, "one egress announce");
3746 assert_eq!(egress.announced_bytes, path_len);
3747 assert_eq!(egress.subscriptions, 1, "one egress subscription");
3748 assert_eq!(egress.broadcasts, 1, "one viewer");
3749 assert_eq!(egress.groups, 1);
3750 assert_eq!(egress.frames, 2);
3751 assert_eq!(egress.bytes, 10);
3752 assert_eq!(egress.fetches, 0);
3753
3754 let ingress = &entry.subscriber;
3756 assert_eq!(ingress.announced, 1, "one ingress announce");
3757 assert_eq!(ingress.announced_bytes, path_len);
3758 assert_eq!(ingress.subscriptions, 1, "one ingress track");
3759 assert_eq!(ingress.broadcasts, 0, "ingress has no viewer refcount");
3760 assert_eq!(ingress.groups, 1);
3761 assert_eq!(ingress.frames, 2);
3762 assert_eq!(ingress.bytes, 10);
3763
3764 let fetched = broadcast.track("video").unwrap().fetch_group(0, None).await.unwrap();
3766 let _ = fetched;
3767 settle().await;
3768 let report = registry.report();
3769 let entry = report.traffic.iter().find(|e| e.path.as_str() == "demo").unwrap();
3770 assert_eq!(entry.publisher.fetches, 1, "one fetch");
3771 assert_eq!(entry.publisher.subscriptions, 1, "fetch does not bump subscriptions");
3772 assert_eq!(entry.publisher.broadcasts, 1, "fetch does not bump the viewer refcount");
3773 assert_eq!(entry.subscriber.fetches, 0, "ingress cannot fetch");
3777 }
3778
3779 #[tokio::test]
3784 async fn test_stats_read_frame_counts_once() {
3785 use crate::Timestamp;
3786 use crate::stats::{Config, Registry, Tier};
3787 use bytes::Bytes;
3788
3789 tokio::time::pause();
3790
3791 let registry = Registry::new(Config::new());
3792 let ctx = registry.tier(Tier::default()).session("acme");
3793
3794 let origin = Origin::random().produce();
3795 let ingress = origin.clone().with_stats(ctx.clone());
3796 let egress = origin.consume().with_stats(ctx.clone());
3797
3798 let mut announced = egress.announced();
3799 let source = ingress.create_broadcast("demo", announce()).unwrap();
3800 let mut dynamic = source.dynamic();
3801 settle().await;
3802 settle().await;
3803
3804 let broadcast = announced.next().await.unwrap().broadcast.unwrap();
3805 let subscribing = broadcast.track("video").unwrap().subscribe(None);
3806 let mut producer = accept_track(&mut dynamic, "video").await;
3807 settle().await;
3808 let mut sub = subscribing.await.unwrap();
3809
3810 producer
3812 .write_frame(Timestamp::ZERO, Bytes::from_static(b"hello"))
3813 .unwrap();
3814
3815 let frame = sub.read_frame().await.unwrap().expect("frame");
3816 assert_eq!(frame.payload.len(), 5);
3817 settle().await;
3818
3819 let report = registry.report();
3820 let entry = report
3821 .traffic
3822 .iter()
3823 .find(|e| e.path.as_str() == "demo")
3824 .expect("demo tracked");
3825 assert_eq!(entry.publisher.groups, 1, "one group, counted once");
3826 assert_eq!(entry.publisher.frames, 1, "one frame, counted once");
3827 assert_eq!(
3828 entry.publisher.bytes, 5,
3829 "payload counted once, not zero and not doubled"
3830 );
3831 }
3832
3833 #[tokio::test]
3837 async fn test_stats_datagrams_counted_both_sides() {
3838 use crate::Timestamp;
3839 use crate::stats::{Config, Registry, Tier};
3840
3841 tokio::time::pause();
3842
3843 let registry = Registry::new(Config::new());
3844 let ctx = registry.tier(Tier::default()).session("acme");
3845
3846 let origin = Origin::random().produce();
3847 let ingress = origin.clone().with_stats(ctx.clone());
3848 let egress = origin.consume().with_stats(ctx.clone());
3849
3850 let mut announced = egress.announced();
3851 let source = ingress.create_broadcast("demo", announce()).unwrap();
3852 let mut dynamic = source.dynamic();
3853 settle().await;
3854 settle().await;
3855
3856 let broadcast = announced.next().await.unwrap().broadcast.unwrap();
3857 let subscribing = broadcast.track("video").unwrap().subscribe(None);
3858 let mut producer = accept_track(&mut dynamic, "video").await;
3859 settle().await;
3860 let mut sub = subscribing.await.unwrap();
3861
3862 producer.append_datagram(Timestamp::ZERO, &b"hello"[..]).unwrap();
3863 let datagram = sub.recv_datagram().await.unwrap().expect("datagram");
3864 assert_eq!(&datagram.payload[..], b"hello");
3865 settle().await;
3866
3867 let report = registry.report();
3868 let entry = report
3869 .traffic
3870 .iter()
3871 .find(|e| e.path.as_str() == "demo")
3872 .expect("demo tracked");
3873
3874 for (side, traffic) in [("egress", &entry.publisher), ("ingress", &entry.subscriber)] {
3875 assert_eq!(traffic.datagrams, 1, "{side}: one datagram");
3876 assert_eq!(traffic.groups, 1, "{side}: counted as its single-frame group");
3877 assert_eq!(traffic.frames, 1, "{side}: one frame");
3878 assert_eq!(traffic.bytes, 5, "{side}: payload counted once");
3879 }
3880 }
3881
3882 #[test]
3883 fn origin_rejects_reserved_ids() {
3884 assert!(Origin::new(0).is_err());
3885 assert!(Origin::new(1u64 << 62).is_err());
3886 assert_eq!(Origin::new(1).unwrap().id(), 1);
3887
3888 let mut zero = [0u8].as_slice();
3889 assert_eq!(
3890 Origin::decode(&mut zero, crate::lite::Version::Lite05).unwrap(),
3891 Origin::UNKNOWN
3892 );
3893 }
3894
3895 #[test]
3896 fn origin_list_push_fails_at_limit() {
3897 let mut list = OriginList::new();
3898 for _ in 0..MAX_HOPS {
3899 list.push(Origin::random()).unwrap();
3900 }
3901 assert_eq!(list.len(), MAX_HOPS);
3902 assert_eq!(list.push(Origin::random()), Err(TooManyOrigins));
3903 }
3904
3905 #[test]
3906 fn origin_list_replace_first() {
3907 let mut list = OriginList::new();
3908 for _ in 0..3 {
3909 list.push(Origin::UNKNOWN).unwrap();
3910 }
3911
3912 assert!(list.replace_first(Origin::UNKNOWN, Origin::new(7).unwrap()));
3914 assert_eq!(
3915 list.as_slice(),
3916 &[Origin::new(7).unwrap(), Origin::UNKNOWN, Origin::UNKNOWN]
3917 );
3918
3919 assert!(!list.replace_first(Origin::new(99).unwrap(), Origin::new(8).unwrap()));
3921 assert_eq!(list.len(), 3);
3922 }
3923
3924 #[test]
3925 fn origin_list_try_from_vec_enforces_limit() {
3926 let under: Vec<Origin> = (0..MAX_HOPS).map(|_| Origin::random()).collect();
3927 assert!(OriginList::try_from(under).is_ok());
3928
3929 let over: Vec<Origin> = (0..MAX_HOPS + 1).map(|_| Origin::random()).collect();
3930 assert_eq!(OriginList::try_from(over), Err(TooManyOrigins));
3931 }
3932
3933 #[tokio::test]
3937 async fn test_idle_announce_prunes_its_nodes() {
3938 tokio::time::pause();
3939
3940 let origin = Origin::random().produce();
3941 let consumer = origin.consume();
3942 let start = origin.node_count();
3943
3944 for i in 0..32 {
3945 let path = format!("channel{i}/chat");
3946 let scoped = consumer.scope(&[Path::new(path.as_str())]).expect("in scope");
3947
3948 let mut announced = scoped.announced();
3949 announced.assert_next_wait();
3950 assert_eq!(origin.node_count(), start + 2, "the subtree exists while subscribed");
3951
3952 drop(announced);
3953 drop(scoped);
3954 assert_eq!(origin.node_count(), start, "cycle {i} left a node behind");
3955 }
3956 }
3957
3958 #[tokio::test]
3961 async fn test_prune_spares_live_nodes() {
3962 tokio::time::pause();
3963
3964 let origin = Origin::random().produce();
3965 let consumer = origin.consume();
3966 let bare = origin.node_count();
3967
3968 let mut broadcast = origin.create_broadcast("channel/chat", announce()).unwrap();
3969 settle().await;
3970 let live = origin.node_count();
3971 assert_eq!(live, bare + 2, "the broadcast should have created its subtree");
3972
3973 let mut announced = consumer.announced();
3975 announced.assert_next_some("channel/chat");
3976 drop(announced);
3977 assert_eq!(origin.node_count(), live, "an announced path was pruned");
3978 assert!(consumer.get_broadcast("channel/chat").is_some());
3979
3980 let idle = consumer.scope(&[Path::new("idle")]).expect("in scope");
3982 let first = idle.announced();
3983 let second = idle.announced();
3984 assert_eq!(origin.node_count(), live + 1);
3985 drop(first);
3986 assert_eq!(origin.node_count(), live + 1, "a node with a consumer was pruned");
3987 drop(second);
3988 assert_eq!(origin.node_count(), live, "the last cursor left the node behind");
3989
3990 broadcast.finish();
3992 settle().await;
3993 assert_eq!(origin.node_count(), bare);
3994 }
3995
3996 #[tokio::test]
4000 async fn test_multi_prefix_cursor_prunes_both_branches() {
4001 tokio::time::pause();
4002
4003 let origin = Origin::random().produce();
4004 let consumer = origin.consume();
4005 let bare = origin.node_count();
4006
4007 let scoped = consumer
4008 .scope(&[Path::new("room/a"), Path::new("room/b")])
4009 .expect("in scope");
4010 assert_eq!(origin.node_count(), bare, "scoping should not create nodes");
4011 assert!(consumer.with_root("room/c").is_some());
4012 assert_eq!(origin.node_count(), bare, "rooting should not create nodes");
4013
4014 let announced = scoped.announced();
4015 assert_eq!(origin.node_count(), bare + 3, "room, room/a and room/b");
4016
4017 drop(announced);
4018 assert_eq!(origin.node_count(), bare, "a two-branch cursor left nodes behind");
4019 }
4020
4021 #[tokio::test]
4024 async fn test_prune_stops_at_a_live_ancestor() {
4025 tokio::time::pause();
4026
4027 let origin = Origin::random().produce();
4028 let consumer = origin.consume();
4029 let bare = origin.node_count();
4030
4031 let outer = consumer.scope(&[Path::new("room/a")]).expect("in scope").announced();
4032 let inner = consumer
4033 .scope(&[Path::new("room/a/deep/leaf")])
4034 .expect("in scope")
4035 .announced();
4036 assert_eq!(origin.node_count(), bare + 4, "room, a, deep and leaf");
4037
4038 drop(inner);
4039 assert_eq!(origin.node_count(), bare + 2, "pruning ran past the cursor above it");
4040
4041 drop(outer);
4042 assert_eq!(origin.node_count(), bare);
4043 }
4044
4045 #[tokio::test]
4050 async fn test_pending_source_holds_its_node() {
4051 tokio::time::pause();
4052
4053 let origin = Origin::random().produce();
4054 let consumer = origin.consume();
4055 let bare = origin.node_count();
4056
4057 let waiting = consumer
4059 .scope(&[Path::new("channel/chat")])
4060 .expect("in scope")
4061 .announced();
4062 assert_eq!(origin.node_count(), bare + 2);
4063
4064 let _broadcast = origin.create_broadcast("channel/chat", announce()).unwrap();
4065
4066 drop(waiting);
4068 assert_eq!(origin.node_count(), bare + 2, "a pending source's node was pruned");
4069
4070 settle().await;
4071 assert!(
4072 consumer.get_broadcast("channel/chat").is_some(),
4073 "the source attached into an orphan"
4074 );
4075 }
4076
4077 #[tokio::test]
4081 async fn test_scoped_handles_survive_a_prune() {
4082 tokio::time::pause();
4083
4084 let scope = [Path::new("channel")];
4085 let producer = origin_scoped(&scope);
4086 let consumer = producer.consume().scope(&scope).expect("in scope");
4087
4088 drop(consumer.announced());
4090
4091 let _broadcast = producer.create_broadcast("channel/chat", announce()).unwrap();
4092 settle().await;
4093
4094 let mut announced = consumer.announced();
4095 announced.assert_next_some("channel/chat");
4096 assert!(consumer.get_broadcast("channel/chat").is_some());
4097 }
4098
4099 #[tokio::test]
4100 async fn test_announce() {
4101 tokio::time::pause();
4102
4103 let origin = Origin::random().produce();
4104
4105 let mut consumer1 = origin.consume().announced();
4106 consumer1.assert_next_wait();
4107
4108 let mut broadcast1 = origin.create_broadcast("test1", announce()).unwrap();
4110 settle().await;
4111
4112 consumer1.assert_next_some("test1");
4113 consumer1.assert_next_wait();
4114
4115 let mut consumer2 = origin.consume().announced();
4118
4119 let mut broadcast2 = origin.create_broadcast("test2", announce()).unwrap();
4121 settle().await;
4122
4123 consumer1.assert_next_some("test2");
4124 consumer1.assert_next_wait();
4125
4126 consumer2.assert_next_some("test1");
4127 consumer2.assert_next_some("test2");
4128 consumer2.assert_next_wait();
4129
4130 broadcast1.finish();
4132 settle().await;
4133
4134 consumer1.assert_next_none("test1");
4136 consumer2.assert_next_none("test1");
4137 consumer1.assert_next_wait();
4138 consumer2.assert_next_wait();
4139
4140 let mut consumer3 = origin.consume().announced();
4142 consumer3.assert_next_some("test2");
4143 consumer3.assert_next_wait();
4144
4145 broadcast2.finish();
4146 settle().await;
4147
4148 consumer1.assert_next_none("test2");
4149 consumer2.assert_next_none("test2");
4150 consumer3.assert_next_none("test2");
4151 }
4152
4153 #[tokio::test]
4157 async fn test_duplicate() {
4158 tokio::time::pause();
4159
4160 let origin = Origin::random().produce();
4161 let consumer = origin.consume();
4162 let mut announced = consumer.announced();
4163
4164 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
4165 let mut broadcast2 = origin.create_broadcast("test", announce()).unwrap();
4166 let mut broadcast3 = origin.create_broadcast("test", announce()).unwrap();
4167 settle().await;
4168 assert!(consumer.get_broadcast("test").is_some());
4169
4170 announced.assert_next_some("test");
4171 announced.assert_next_wait();
4172
4173 broadcast2.finish();
4175 settle().await;
4176 assert!(consumer.get_broadcast("test").is_some());
4177 announced.assert_next_wait();
4178
4179 broadcast1.finish();
4181 settle().await;
4182 assert!(consumer.get_broadcast("test").is_some());
4183 announced.assert_next_wait();
4184
4185 broadcast3.finish();
4187 settle().await;
4188 assert!(consumer.get_broadcast("test").is_none());
4189
4190 announced.assert_next_none("test");
4191 announced.assert_next_wait();
4192 }
4193
4194 #[tokio::test]
4197 async fn test_route_failover() {
4198 tokio::time::pause();
4199
4200 let origin = Origin::random().produce();
4201 let consumer = origin.consume();
4202 let mut announced = consumer.announced();
4203
4204 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4207 let hops_b = OriginList::try_from(vec![Origin::new(1).unwrap(), Origin::new(3).unwrap()]).unwrap();
4208
4209 let source_a = origin.create_broadcast("test", announce().with_hops(hops_a)).unwrap();
4211 let mut dynamic_a = source_a.dynamic();
4212 settle().await;
4213 settle().await;
4214 let broadcast = consumer.request_broadcast("test").await.unwrap();
4215 announced.assert_next_some("test");
4216
4217 let source_b = origin.create_broadcast("test", announce().with_hops(hops_b)).unwrap();
4219 let mut dynamic_b = source_b.dynamic();
4220 settle().await;
4221 settle().await;
4222 announced.assert_next_wait();
4223
4224 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4226 let mut producer = accept_track(&mut dynamic_a, "video").await;
4227 settle().await;
4228 dynamic_b.assert_no_request();
4229
4230 let mut sub = subscribing.await.unwrap();
4231 sub.assert_no_group();
4234 assert_eq!(producer.subscription().unwrap().group_start, None);
4235
4236 producer.append_group().unwrap();
4237 producer.append_group().unwrap();
4238 assert_eq!(sub.assert_group().sequence, 0);
4239 assert_eq!(sub.assert_group().sequence, 1);
4240
4241 producer.abort(Error::Dropped).unwrap();
4245 source_a.abort(Error::Dropped).unwrap();
4246 drop(dynamic_a);
4247 settle().await;
4248 announced.assert_next_wait();
4249
4250 let mut producer = accept_track(&mut dynamic_b, "video").await;
4254 settle().await;
4255 sub.assert_no_group();
4256 assert_eq!(producer.subscription().unwrap().group_start, None);
4257 producer.create_group(group::Info { sequence: 1 }).unwrap();
4258 producer.create_group(group::Info { sequence: 2 }).unwrap();
4259 assert_eq!(sub.assert_group().sequence, 2, "groups below the boundary are filtered");
4260 sub.assert_not_closed();
4261 }
4262
4263 #[tokio::test]
4265 async fn test_failover_rejects_different_track_info() {
4266 tokio::time::pause();
4267
4268 for replacement in [
4269 track::Info::default().with_timescale(Timescale::MICRO),
4270 track::Info::default().with_priority(7),
4271 track::Info::default().with_ordered(true),
4272 track::Info::default().with_latency_max(Duration::from_secs(7)),
4273 ] {
4274 let origin = Origin::random().produce();
4275 let consumer = origin.consume();
4276
4277 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4279 let hops_b = OriginList::try_from(vec![Origin::new(1).unwrap(), Origin::new(3).unwrap()]).unwrap();
4280
4281 let source_a = origin.create_broadcast("test", announce().with_hops(hops_a)).unwrap();
4282 let mut dynamic_a = source_a.dynamic();
4283 settle().await;
4284 settle().await;
4285 let broadcast = consumer.request_broadcast("test").await.unwrap();
4286
4287 let source_b = origin.create_broadcast("test", announce().with_hops(hops_b)).unwrap();
4288 let mut dynamic_b = source_b.dynamic();
4289 settle().await;
4290 settle().await;
4291
4292 let track = broadcast.track("video").unwrap();
4295
4296 let querying = track.info();
4298 let mut producer = accept_track(&mut dynamic_a, "video").await;
4299 let info = tokio::time::timeout(std::time::Duration::from_secs(1), querying)
4300 .await
4301 .expect("timed out resolving the first source's info")
4302 .unwrap();
4303 assert_eq!(info.timescale, Timescale::default());
4304
4305 let mut reader = track.subscribe(None).await.unwrap();
4306 producer
4307 .create_group(group::Info { sequence: 0 })
4308 .unwrap()
4309 .finish()
4310 .unwrap();
4311 assert_eq!(reader.recv_group().await.unwrap().unwrap().sequence, 0);
4312
4313 let mut joining = track.subscribe(None).await.unwrap();
4315
4316 source_a.abort(Error::Dropped).unwrap();
4318 drop(dynamic_a);
4319 settle().await;
4320
4321 let request = tokio::time::timeout(std::time::Duration::from_secs(1), dynamic_b.requested_track())
4322 .await
4323 .expect("timed out waiting for a track request")
4324 .expect("source closed");
4325 let mut successor = request.accept(replacement);
4326 successor
4327 .create_group(group::Info { sequence: 1 })
4328 .unwrap()
4329 .finish()
4330 .unwrap();
4331 settle().await;
4332
4333 assert!(matches!(track.info().await, Err(Error::Unsupported)));
4334 let cached = joining.recv_group().await.unwrap().unwrap();
4335 assert_eq!(cached.sequence, 0);
4336 assert_eq!(cached.timescale(), info.timescale);
4337 assert!(matches!(joining.recv_group().await, Err(Error::Unsupported)));
4338 assert!(matches!(track.subscribe(None).await, Err(Error::Unsupported)));
4339 assert!(matches!(reader.recv_group().await, Err(Error::Unsupported)));
4340 assert!(matches!(track.fetch_group(1, None).await, Err(Error::Unsupported)));
4341
4342 let reopened = broadcast.track("video").unwrap();
4344 assert!(matches!(reopened.info().await, Err(Error::Unsupported)));
4345 }
4346 }
4347
4348 #[tokio::test]
4355 async fn test_route_failover_restores_every_track() {
4356 tokio::time::pause();
4357
4358 let origin = Origin::random().produce();
4359 let consumer = origin.consume();
4360
4361 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4363 let hops_b = OriginList::try_from(vec![Origin::new(1).unwrap(), Origin::new(3).unwrap()]).unwrap();
4364
4365 let source_a = origin.create_broadcast("test", announce().with_hops(hops_a)).unwrap();
4366 let mut dynamic_a = source_a.dynamic();
4367 settle().await;
4368 settle().await;
4369 let broadcast = consumer.request_broadcast("test").await.unwrap();
4370
4371 let source_b = origin.create_broadcast("test", announce().with_hops(hops_b)).unwrap();
4373 let mut dynamic_b = source_b.dynamic();
4374 settle().await;
4375 settle().await;
4376
4377 const TRACKS: [(&str, u64); 3] = [("video", 3), ("audio", 1), ("data", 2)];
4381
4382 let subscribing: Vec<_> = TRACKS
4383 .iter()
4384 .map(|(name, _)| broadcast.track(name).unwrap().subscribe(None))
4385 .collect();
4386 let mut producers_a = accept_tracks(&mut dynamic_a, TRACKS.len()).await;
4387 settle().await;
4388
4389 let mut subs = Vec::new();
4390 for ((name, groups), subscribing) in TRACKS.iter().zip(subscribing) {
4391 let mut sub = subscribing.await.unwrap();
4392 let producer = producers_a
4393 .get_mut(*name)
4394 .unwrap_or_else(|| panic!("{name} was never dispatched"));
4395 for expected in 0..*groups {
4396 producer.append_group().unwrap();
4397 assert_eq!(sub.assert_group().sequence, expected, "{name} did not start");
4398 }
4399 subs.push((*name, *groups, sub));
4400 }
4401
4402 for (_, producer) in producers_a.drain() {
4404 producer.abort(Error::Dropped).unwrap();
4405 }
4406 source_a.abort(Error::Dropped).unwrap();
4407 drop(dynamic_a);
4408 settle().await;
4409
4410 let mut producers_b = accept_tracks(&mut dynamic_b, TRACKS.len()).await;
4413 settle().await;
4414
4415 for (_, _, sub) in subs.iter_mut() {
4418 sub.assert_no_group();
4419 }
4420 settle().await;
4421
4422 for (name, groups, sub) in subs.iter_mut() {
4423 let producer = producers_b
4424 .get_mut(*name)
4425 .unwrap_or_else(|| panic!("{name} was never re-dispatched to the standby"));
4426 assert_eq!(
4429 producer
4430 .subscription()
4431 .unwrap_or_else(|| panic!("{name} resumed without a subscription"))
4432 .group_start,
4433 None,
4434 "{name} must keep the subscriber's live-edge demand"
4435 );
4436 let boundary = *groups;
4437
4438 producer.create_group(group::Info { sequence: boundary - 1 }).unwrap();
4440 producer.create_group(group::Info { sequence: boundary }).unwrap();
4441 assert_eq!(sub.assert_group().sequence, boundary, "{name} did not resume");
4442 sub.assert_not_closed();
4443 }
4444 }
4445
4446 #[tokio::test]
4449 async fn test_broadcast_route_watch() {
4450 let mut producer = broadcast::Info::new().produce();
4451 let mut consumer = producer.consume();
4452
4453 assert_eq!(consumer.route_changed().await.unwrap(), broadcast::Route::default());
4455
4456 producer.set_route(broadcast::Route::default()).unwrap();
4458 assert!(consumer.route_changed().now_or_never().is_none());
4459
4460 let mut hops = OriginList::new();
4461 hops.push(Origin::new(7).unwrap()).unwrap();
4462 let route = broadcast::Route::new().with_hops(hops).with_cost(3);
4463 producer.set_route(route.clone()).unwrap();
4464 assert_eq!(consumer.route_changed().await.unwrap(), route);
4465
4466 let mut fresh = producer.consume();
4468 assert_eq!(fresh.route_changed().await.unwrap(), route);
4469
4470 drop(producer);
4471 assert!(matches!(consumer.route_changed().await.unwrap_err(), Error::Dropped));
4472 }
4473
4474 #[tokio::test]
4478 async fn test_route_cost_update() {
4479 tokio::time::pause();
4480
4481 let origin = Info::new(origin_keyed("test", Origin::new(3).unwrap(), true)).produce();
4485 let consumer = origin.consume();
4486 let mut announced = consumer.announced();
4487
4488 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4491 let hops_b = OriginList::try_from(vec![Origin::new(1).unwrap(), Origin::new(3).unwrap()]).unwrap();
4492
4493 let mut source_a = origin
4495 .create_broadcast("test", announce().with_hops(hops_a.clone()))
4496 .unwrap();
4497 let mut dynamic_a = source_a.dynamic();
4498 settle().await;
4499 let broadcast = consumer.request_broadcast("test").await.unwrap();
4500 announced.assert_next_some("test");
4501
4502 let mut watch = broadcast.clone();
4503 assert_eq!(watch.route_changed().await.unwrap().hops, hops_a);
4504
4505 let mut source_b = origin
4506 .create_broadcast("test", announce().with_hops(hops_b.clone()))
4507 .unwrap();
4508 let mut dynamic_b = source_b.dynamic();
4509 settle().await;
4510 assert!(
4511 watch.route_changed().now_or_never().is_none(),
4512 "a losing standby must not change the advertised route"
4513 );
4514
4515 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4517 let mut producer = accept_track(&mut dynamic_a, "video").await;
4518 settle().await;
4519 let mut sub = subscribing.await.unwrap();
4520 producer.append_group().unwrap();
4521 assert_eq!(sub.assert_group().sequence, 0);
4522
4523 source_a
4526 .set_route(announce().with_hops(hops_a.clone()).with_cost(10))
4527 .unwrap();
4528 settle().await;
4529 assert_eq!(watch.route_changed().await.unwrap().hops, hops_b);
4530 announced.assert_next_wait();
4531
4532 let mut producer_b = accept_track(&mut dynamic_b, "video").await;
4533 settle().await;
4534 sub.assert_no_group();
4537 assert_eq!(producer_b.subscription().unwrap().group_start, None);
4538 producer_b.create_group(group::Info { sequence: 1 }).unwrap();
4539 assert_eq!(sub.assert_group().sequence, 1);
4540 sub.assert_not_closed();
4541
4542 source_b
4544 .set_route(announce().with_hops(hops_b.clone()).with_cost(5))
4545 .unwrap();
4546 settle().await;
4547 let advertised = watch.route_changed().await.unwrap();
4548 assert_eq!(advertised.hops, hops_b);
4549 assert_eq!(advertised.cost, 5);
4550 announced.assert_next_wait();
4551 }
4552
4553 #[tokio::test]
4556 async fn test_completed_track_survives_route_churn() {
4557 tokio::time::pause();
4558
4559 let origin = Origin::random().produce();
4560 let consumer = origin.consume();
4561
4562 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4564 let hops_b = OriginList::try_from(vec![Origin::new(1).unwrap(), Origin::new(3).unwrap()]).unwrap();
4565
4566 let source_a = origin.create_broadcast("test", announce().with_hops(hops_a)).unwrap();
4567 let mut dynamic_a = source_a.dynamic();
4568 settle().await;
4569 let source_b = origin.create_broadcast("test", announce().with_hops(hops_b)).unwrap();
4570 let mut dynamic_b = source_b.dynamic();
4571 settle().await;
4572 settle().await;
4573 let broadcast = consumer.request_broadcast("test").await.unwrap();
4574
4575 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4577 let mut producer = accept_track(&mut dynamic_a, "video").await;
4578 settle().await;
4579 let mut sub = subscribing.await.unwrap();
4580 producer.append_group().unwrap();
4581 assert_eq!(sub.assert_group().sequence, 0);
4582 producer.finish().unwrap();
4583 drop(producer);
4584 settle().await;
4585 sub.assert_closed();
4586
4587 source_a.abort(Error::Dropped).unwrap();
4589 drop(dynamic_a);
4590 settle().await;
4591 dynamic_b.assert_no_request();
4592
4593 let mut late = broadcast.track("video").unwrap().subscribe(None).await.unwrap();
4595 late.assert_closed();
4596 }
4597
4598 #[tokio::test]
4602 async fn test_refused_track_aborts_instantly() {
4603 tokio::time::pause();
4604
4605 let origin = Origin::random().produce();
4606 let consumer = origin.consume();
4607
4608 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4609 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
4610 let mut dynamic = source.dynamic();
4611 settle().await;
4612 settle().await;
4613 let broadcast = consumer.request_broadcast("test").await.unwrap();
4614
4615 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4616 let request = dynamic.requested_track().await.unwrap();
4617 request.reject(Error::NotFound);
4618 settle().await;
4619
4620 assert!(matches!(subscribing.await, Err(Error::NotFound)));
4622 dynamic.assert_no_request();
4623 }
4624
4625 #[tokio::test]
4630 async fn test_stale_rejection_does_not_abort_a_handover() {
4631 tokio::time::pause();
4632
4633 let origin = Origin::random().produce();
4634 let consumer = origin.consume();
4635
4636 let publisher = Origin::new(1).unwrap();
4637 let peer = Origin::new(5).unwrap();
4638 let via_peer = OriginList::try_from(vec![publisher, peer]).unwrap();
4639 let local = OriginList::try_from(vec![publisher]).unwrap();
4640
4641 let source_remote = origin
4643 .create_broadcast("test", announce().with_hops(via_peer).with_cost(2))
4644 .unwrap();
4645 let mut dynamic_remote = source_remote.dynamic();
4646 settle().await;
4647 settle().await;
4648 let broadcast = consumer.request_broadcast("test").await.unwrap();
4649 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4650 let request_remote = dynamic_remote.requested_track().await.unwrap();
4651
4652 let source_local = origin.create_broadcast("test", announce().with_hops(local)).unwrap();
4655 let mut dynamic_local = source_local.dynamic();
4656 request_remote.reject(Error::NotFound);
4657 settle().await;
4658
4659 let mut producer_local = accept_track(&mut dynamic_local, "video").await;
4661 settle().await;
4662 let mut sub = subscribing
4663 .await
4664 .expect("the handover must win over the stale rejection");
4665 producer_local.append_group().unwrap();
4666 assert_eq!(sub.assert_group().sequence, 0);
4667 sub.assert_not_closed();
4668 }
4669
4670 #[tokio::test]
4674 async fn test_route_handover() {
4675 tokio::time::pause();
4676
4677 let origin = Origin::random().produce();
4678 let consumer = origin.consume();
4679 let mut announced = consumer.announced();
4680
4681 let hops_long = OriginList::try_from(vec![Origin::new(1).unwrap(), Origin::new(3).unwrap()]).unwrap();
4683 let hops_short = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4684
4685 let source_a = origin
4686 .create_broadcast("test", announce().with_hops(hops_long))
4687 .unwrap();
4688 let mut dynamic_a = source_a.dynamic();
4689 settle().await;
4690 settle().await;
4691 let broadcast = consumer.request_broadcast("test").await.unwrap();
4692 announced.assert_next_some("test");
4693
4694 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4695 let mut producer_a = accept_track(&mut dynamic_a, "video").await;
4696 settle().await;
4697 let mut sub = subscribing.await.unwrap();
4698 producer_a.append_group().unwrap();
4699 producer_a.append_group().unwrap();
4700 assert_eq!(sub.assert_group().sequence, 0);
4701 assert_eq!(sub.assert_group().sequence, 1);
4702
4703 let source_b = origin
4706 .create_broadcast("test", announce().with_hops(hops_short))
4707 .unwrap();
4708 let mut dynamic_b = source_b.dynamic();
4709 settle().await;
4710 settle().await;
4711 announced.assert_next_wait();
4712
4713 let mut producer_b = accept_track(&mut dynamic_b, "video").await;
4714 settle().await;
4715
4716 sub.assert_no_group();
4720 assert_eq!(producer_a.subscription().unwrap().group_end, Some(1));
4721 assert_eq!(producer_b.subscription().unwrap().group_start, None);
4722
4723 producer_a.create_group(group::Info { sequence: 2 }).unwrap();
4725 producer_b.create_group(group::Info { sequence: 2 }).unwrap();
4726 producer_b.create_group(group::Info { sequence: 3 }).unwrap();
4727 assert_eq!(sub.assert_group().sequence, 2);
4728 assert_eq!(sub.assert_group().sequence, 3);
4729 sub.assert_no_group();
4730 sub.assert_not_closed();
4731 }
4732
4733 #[tokio::test(start_paused = true)]
4736 async fn test_route_unannounce_immediate() {
4737 let origin = Origin::random().produce();
4738 let consumer = origin.consume();
4739 let mut announced = consumer.announced();
4740
4741 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4742 let mut source = origin
4743 .create_broadcast("test", announce().with_hops(hops.clone()))
4744 .unwrap();
4745 settle().await;
4746 let broadcast = consumer.request_broadcast("test").await.unwrap();
4747 announced.assert_next_some("test");
4748
4749 source.finish();
4752 settle().await;
4753 announced.assert_next_none("test");
4754
4755 let _source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
4757 settle().await;
4758 let fresh = consumer.request_broadcast("test").await.unwrap();
4759 announced.assert_next_some("test");
4760 assert!(
4761 !fresh.is_clone(&broadcast),
4762 "re-create must not splice the old broadcast"
4763 );
4764 }
4765
4766 #[tokio::test(start_paused = true)]
4771 async fn test_route_detach_immediate() {
4772 let origin = Origin::random().produce();
4773 let consumer = origin.consume();
4774 let mut announced = consumer.announced();
4775
4776 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4777 let source = origin
4778 .create_broadcast("test", announce().with_hops(hops.clone()))
4779 .unwrap();
4780 let mut dynamic = source.dynamic();
4781 settle().await;
4782 settle().await;
4783 let broadcast = consumer.request_broadcast("test").await.unwrap();
4784 announced.assert_next_some("test");
4785
4786 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4787 let producer = accept_track(&mut dynamic, "video").await;
4788 settle().await;
4789 let mut sub = subscribing.await.unwrap();
4790
4791 drop(producer);
4793 source.abort(Error::Dropped).unwrap();
4794 drop(dynamic);
4795
4796 settle().await;
4797 announced.assert_next_none("test");
4798 sub.assert_error();
4799
4800 let _source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
4803 settle().await;
4804 settle().await;
4805 let fresh = consumer.request_broadcast("test").await.unwrap();
4806 announced.assert_next_some("test");
4807 assert!(
4808 !fresh.is_clone(&broadcast),
4809 "re-create must not splice the old broadcast"
4810 );
4811 }
4812
4813 #[tokio::test(start_paused = true)]
4818 async fn test_idle_track_releases_without_respinning() {
4819 for incompatible in [false, true] {
4820 let origin = Info::new(Origin::random()).produce();
4821 let consumer = origin.consume();
4822
4823 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4824 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
4825 let mut dynamic = source.dynamic();
4826 settle().await;
4827 let broadcast = consumer.request_broadcast("test").await.unwrap();
4828
4829 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4830 let producer = accept_track(&mut dynamic, "video").await;
4831 settle().await;
4832 let sub = subscribing.await.unwrap();
4833
4834 drop(sub);
4837 tokio::time::sleep(TRACK_IDLE_LINGER / 2).await;
4838 settle().await;
4839 assert!(
4840 producer.poll_unused(&kio::Waiter::noop()).is_pending(),
4841 "the copy must stay spliced inside the linger",
4842 );
4843
4844 tokio::time::sleep(TRACK_IDLE_LINGER).await;
4847 settle().await;
4848 assert!(
4849 producer.poll_unused(&kio::Waiter::noop()).is_ready(),
4850 "an idle copy must be released after the linger",
4851 );
4852
4853 for _ in 0..3 {
4857 tokio::time::sleep(TRACK_IDLE_LINGER).await;
4858 settle().await;
4859 assert!(
4860 producer.poll_unused(&kio::Waiter::noop()).is_ready(),
4861 "an unread copy must stay released, not be re-spliced",
4862 );
4863 }
4864 assert!(
4865 dynamic.requested_track().now_or_never().is_none(),
4866 "an unread track must not be re-requested",
4867 );
4868 drop(producer);
4869
4870 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4872 let request = dynamic.requested_track().await.unwrap();
4873 let info = track::Info::default().with_timescale(if incompatible {
4874 Timescale::MICRO
4875 } else {
4876 Timescale::MILLI
4877 });
4878 let mut producer = request.accept(info);
4879 if incompatible {
4880 assert!(matches!(subscribing.await, Err(Error::Unsupported)));
4881 continue;
4882 }
4883
4884 settle().await;
4885 let mut sub = subscribing.await.unwrap();
4886 producer.append_group().unwrap();
4887 assert_eq!(sub.assert_group().sequence, 0);
4888 }
4889 }
4890
4891 #[tokio::test(start_paused = true)]
4895 async fn test_back_to_back_fetches_reuse_the_track() {
4896 let origin = Info::new(Origin::random()).produce();
4897 let consumer = origin.consume();
4898
4899 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4900 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
4901 let mut dynamic = source.dynamic();
4902 settle().await;
4903 let broadcast = consumer.request_broadcast("test").await.unwrap();
4904
4905 let fetching = broadcast.track("video").unwrap().fetch_group(0, None);
4907 let mut producer = accept_track(&mut dynamic, "video").await;
4908 producer.append_group().unwrap().finish().unwrap();
4909 settle().await;
4910 let first = fetching.await.expect("first fetch");
4911 drop(first);
4912
4913 settle().await;
4915 let fetching = broadcast.track("video").unwrap().fetch_group(0, None);
4916 settle().await;
4917 assert!(
4918 dynamic.requested_track().now_or_never().is_none(),
4919 "a fetch inside the linger must reuse the track, not re-request it",
4920 );
4921 drop(fetching.await.expect("second fetch"));
4922
4923 tokio::time::sleep(TRACK_IDLE_LINGER * 2).await;
4925 settle().await;
4926 assert!(
4927 producer.poll_unused(&kio::Waiter::noop()).is_ready(),
4928 "the copy must be released once the fetches stop",
4929 );
4930 drop(producer);
4931
4932 settle().await;
4934 let fetching = broadcast.track("video").unwrap().fetch_group(0, None);
4935 let mut producer = accept_track(&mut dynamic, "video").await;
4936 producer.append_group().unwrap().finish().unwrap();
4937 settle().await;
4938 fetching.await.expect("fetch after the linger");
4939 }
4940
4941 #[tokio::test(start_paused = true)]
4945 async fn test_linger_reconnect_splices() {
4946 let origin = Info::new(Origin::random())
4947 .with_linger(Duration::from_secs(5))
4948 .produce();
4949 let consumer = origin.consume();
4950 let mut announced = consumer.announced();
4951
4952 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
4953 let source = origin
4954 .create_broadcast("test", announce().with_hops(hops.clone()))
4955 .unwrap();
4956 let mut dynamic = source.dynamic();
4957 settle().await;
4958 settle().await;
4959 let broadcast = consumer.request_broadcast("test").await.unwrap();
4960 announced.assert_next_some("test");
4961
4962 let subscribing = broadcast.track("video").unwrap().subscribe(None);
4963 let mut producer = accept_track(&mut dynamic, "video").await;
4964 settle().await;
4965 let mut sub = subscribing.await.unwrap();
4966
4967 producer.append_group().unwrap();
4968 producer.append_group().unwrap();
4969 assert_eq!(sub.assert_group().sequence, 0);
4970 assert_eq!(sub.assert_group().sequence, 1);
4971
4972 drop(producer);
4975 source.abort(Error::Dropped).unwrap();
4976 drop(dynamic);
4977 settle().await;
4978
4979 announced.assert_next_wait();
4981 sub.assert_no_group();
4982 sub.assert_not_closed();
4983
4984 let during = consumer.request_broadcast("test").await.unwrap();
4986 assert!(during.is_clone(&broadcast), "the lingering broadcast still resolves");
4987
4988 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
4991 let mut dynamic = source.dynamic();
4992 settle().await;
4993 settle().await;
4994 announced.assert_next_wait();
4995 let again = consumer.request_broadcast("test").await.unwrap();
4996 assert!(again.is_clone(&broadcast), "the reconnect must splice, not replace");
4997
4998 let mut producer = accept_track(&mut dynamic, "video").await;
5002 settle().await;
5003 sub.assert_no_group();
5004 assert_eq!(producer.subscription().unwrap().group_start, None);
5005 producer.create_group(group::Info { sequence: 2 }).unwrap();
5006 assert_eq!(sub.assert_group().sequence, 2);
5007 sub.assert_not_closed();
5008 }
5009
5010 #[tokio::test(start_paused = true)]
5013 async fn test_linger_expiry_closes() {
5014 let origin = Info::new(Origin::random())
5015 .with_linger(Duration::from_secs(5))
5016 .produce();
5017 let consumer = origin.consume();
5018 let mut announced = consumer.announced();
5019
5020 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5021 let source = origin
5022 .create_broadcast("test", announce().with_hops(hops.clone()))
5023 .unwrap();
5024 let mut dynamic = source.dynamic();
5025 settle().await;
5026 settle().await;
5027 let broadcast = consumer.request_broadcast("test").await.unwrap();
5028 announced.assert_next_some("test");
5029
5030 let subscribing = broadcast.track("video").unwrap().subscribe(None);
5031 let producer = accept_track(&mut dynamic, "video").await;
5032 settle().await;
5033 let mut sub = subscribing.await.unwrap();
5034
5035 drop(producer);
5036 source.abort(Error::Dropped).unwrap();
5037 drop(dynamic);
5038 settle().await;
5039 announced.assert_next_wait();
5040
5041 tokio::time::sleep(std::time::Duration::from_secs(6)).await;
5043 settle().await;
5044 announced.assert_next_none("test");
5045 sub.assert_error();
5046
5047 let _source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
5049 settle().await;
5050 settle().await;
5051 let fresh = consumer.request_broadcast("test").await.unwrap();
5052 announced.assert_next_some("test");
5053 assert!(
5054 !fresh.is_clone(&broadcast),
5055 "a late re-create must not splice the expired broadcast"
5056 );
5057 }
5058
5059 #[tokio::test(start_paused = true)]
5063 async fn test_linger_forever() {
5064 let origin = Info::new(Origin::random()).with_linger(Duration::MAX).produce();
5065 let consumer = origin.consume();
5066 let mut announced = consumer.announced();
5067
5068 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5069 let source = origin
5070 .create_broadcast("test", announce().with_hops(hops.clone()))
5071 .unwrap();
5072 settle().await;
5073 let broadcast = consumer.request_broadcast("test").await.unwrap();
5074 announced.assert_next_some("test");
5075
5076 source.abort(Error::Dropped).unwrap();
5077 settle().await;
5078
5079 tokio::time::sleep(std::time::Duration::from_secs(60 * 60 * 24 * 3)).await;
5081 announced.assert_next_wait();
5082 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
5083 settle().await;
5084 settle().await;
5085 let again = consumer.request_broadcast("test").await.unwrap();
5086 assert!(again.is_clone(&broadcast), "the reconnect must splice, not replace");
5087 drop(source);
5088 }
5089
5090 #[tokio::test(start_paused = true)]
5104 async fn test_linger_parks_a_live_subscription() {
5105 let origin = Info::new(Origin::random())
5106 .with_linger(Duration::from_secs(5))
5107 .produce();
5108 let consumer = origin.consume();
5109
5110 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5111 let source = origin
5112 .create_broadcast("test", announce().with_hops(hops.clone()))
5113 .unwrap();
5114 let mut dynamic = source.dynamic();
5115 settle().await;
5116 settle().await;
5117 let broadcast = consumer.request_broadcast("test").await.unwrap();
5118
5119 let subscribing = broadcast.track("catalog.json").unwrap().subscribe(None);
5122 let mut producer = accept_track(&mut dynamic, "catalog.json").await;
5123 settle().await;
5124 let mut sub = subscribing.await.unwrap();
5125 producer.append_group().unwrap();
5126 assert_eq!(sub.assert_group().sequence, 0);
5127
5128 source.abort(Error::Dropped).unwrap();
5133 settle().await;
5134 settle().await;
5135 sub.assert_not_closed();
5136
5137 tokio::time::sleep(Duration::from_secs(4)).await;
5140 settle().await;
5141 sub.assert_not_closed();
5142
5143 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
5145 let mut dynamic = source.dynamic();
5146 settle().await;
5147 let mut producer = accept_track(&mut dynamic, "catalog.json").await;
5148 settle().await;
5149 producer.create_group(group::Info { sequence: 1 }).unwrap();
5150 assert_eq!(sub.assert_group().sequence, 1);
5151 sub.assert_not_closed();
5152 }
5153
5154 #[tokio::test(start_paused = true)]
5165 async fn test_idle_release_survives_the_route_leaving() {
5166 let origin = Info::new(Origin::random())
5167 .with_linger(Duration::from_secs(600))
5168 .produce();
5169 let consumer = origin.consume();
5170
5171 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5172 let source = origin
5173 .create_broadcast("test", announce().with_hops(hops.clone()))
5174 .unwrap();
5175 let mut dynamic = source.dynamic();
5176 settle().await;
5177 settle().await;
5178 let broadcast = consumer.request_broadcast("test").await.unwrap();
5179
5180 let subscribing = broadcast.track("catalog.json").unwrap().subscribe(None);
5181 let mut producer = accept_track(&mut dynamic, "catalog.json").await;
5182 settle().await;
5183 let mut sub = subscribing.await.unwrap();
5184 producer.append_group().unwrap();
5185 assert_eq!(sub.assert_group().sequence, 0);
5186
5187 source.abort(Error::Dropped).unwrap();
5190 settle().await;
5191 settle().await;
5192 drop(sub);
5193 drop(producer);
5194 drop(dynamic);
5195 settle().await;
5196 tokio::time::sleep(TRACK_IDLE_LINGER + Duration::from_secs(1)).await;
5197 settle().await;
5198
5199 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
5202 let mut dynamic = source.dynamic();
5203 settle().await;
5204 settle().await;
5205 let broadcast = consumer.request_broadcast("test").await.unwrap();
5206 let subscribing = broadcast.track("catalog.json").unwrap().subscribe(None);
5207 let mut producer = accept_track(&mut dynamic, "catalog.json").await;
5208 settle().await;
5209 let mut sub = subscribing.await.unwrap();
5210 producer.append_group().unwrap();
5211 assert_eq!(
5212 sub.assert_group().sequence,
5213 0,
5214 "the reconnect's first group must not be filtered by a stale boundary"
5215 );
5216 }
5217
5218 #[tokio::test(start_paused = true)]
5221 async fn test_linger_skipped_on_finish() {
5222 let origin = Info::new(Origin::random())
5223 .with_linger(Duration::from_secs(5))
5224 .produce();
5225 let consumer = origin.consume();
5226 let mut announced = consumer.announced();
5227
5228 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5229 let mut source = origin
5230 .create_broadcast("test", announce().with_hops(hops.clone()))
5231 .unwrap();
5232 settle().await;
5233 let broadcast = consumer.request_broadcast("test").await.unwrap();
5234 announced.assert_next_some("test");
5235
5236 source.finish();
5239 settle().await;
5240 announced.assert_next_none("test");
5241
5242 let _source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
5244 settle().await;
5245 let fresh = consumer.request_broadcast("test").await.unwrap();
5246 announced.assert_next_some("test");
5247 assert!(
5248 !fresh.is_clone(&broadcast),
5249 "a finish must not leave a lingering broadcast to splice into"
5250 );
5251 }
5252
5253 #[tokio::test]
5256 async fn test_announce_toggle() {
5257 tokio::time::pause();
5258
5259 let origin = Origin::random().produce();
5260 let consumer = origin.consume();
5261 let mut announced = consumer.announced();
5262
5263 let mut source = origin.create_broadcast("test", broadcast::Route::new()).unwrap();
5264 settle().await;
5265
5266 announced.assert_next_wait();
5268 let broadcast = consumer
5269 .get_broadcast("test")
5270 .expect("offline broadcast is still routable");
5271 assert!(!broadcast.route().announce);
5272
5273 let requested = consumer.request_broadcast("test").await.unwrap();
5275 assert!(requested.is_clone(&broadcast));
5276
5277 source.set_route(announce()).unwrap();
5279 settle().await;
5280 let face = announced.assert_next_some("test");
5281 assert!(face.is_clone(&broadcast));
5282
5283 let mut fresh = origin.consume().announced();
5285 fresh.assert_next_some("test");
5286 fresh.assert_next_wait();
5287
5288 source.set_route(broadcast::Route::new()).unwrap();
5290 settle().await;
5291 announced.assert_next_none("test");
5292 assert!(consumer.get_broadcast("test").is_some());
5293 let mut fresh = origin.consume().announced();
5294 fresh.assert_next_wait();
5295
5296 source.finish();
5297 settle().await;
5298 assert!(consumer.get_broadcast("test").is_none());
5299 }
5300
5301 #[tokio::test]
5304 async fn test_announce_beats_offline() {
5305 tokio::time::pause();
5306
5307 let origin = Origin::random().produce();
5308 let consumer = origin.consume();
5309 let mut announced = consumer.announced();
5310
5311 let _offline = origin.create_broadcast("test", broadcast::Route::new()).unwrap();
5313 settle().await;
5314 announced.assert_next_wait();
5315
5316 let mut announced_source = origin.create_broadcast("test", announce().with_cost(10)).unwrap();
5319 settle().await;
5320 announced.assert_next_some("test");
5321 let face = consumer.get_broadcast("test").unwrap();
5322 assert!(face.route().announce);
5323 assert_eq!(face.route().cost, 10);
5324
5325 announced_source.finish();
5328 settle().await;
5329 announced.assert_next_none("test");
5330 assert!(consumer.get_broadcast("test").is_some());
5331 }
5332
5333 #[tokio::test]
5336 async fn test_better_source_no_churn() {
5337 tokio::time::pause();
5338
5339 let origin = Origin::random().produce();
5340 let mut announced = origin.consume().announced();
5341
5342 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap(), Origin::new(3).unwrap()]).unwrap();
5345 let hops_b = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5346 let _a = origin.create_broadcast("test", announce().with_hops(hops_a)).unwrap();
5347 settle().await;
5348 let face = announced.assert_next_some("test");
5349
5350 let _b = origin
5351 .create_broadcast("test", announce().with_hops(hops_b.clone()))
5352 .unwrap();
5353 settle().await;
5354 announced.assert_next_wait();
5355 let current = origin.consume().get_broadcast("test").unwrap();
5356 assert!(current.is_clone(&face), "the broadcast identity must not change");
5357 assert_eq!(current.route().hops, hops_b);
5359 }
5360
5361 #[tokio::test]
5367 async fn test_publisher_mismatch_replaces() {
5368 tokio::time::pause();
5369
5370 let origin = Origin::random().produce();
5371 let consumer = origin.consume();
5372 let mut announced = consumer.announced();
5373
5374 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5375 let hops_b = OriginList::try_from(vec![Origin::new(2).unwrap()]).unwrap();
5376
5377 let mut source_a = origin
5378 .create_broadcast("test", announce().with_hops(hops_a.clone()))
5379 .unwrap();
5380 settle().await;
5381 let face_a = announced.assert_next_some("test");
5382
5383 let _source_b = origin
5386 .create_broadcast("test", announce().with_hops(hops_b.clone()))
5387 .unwrap();
5388 settle().await;
5389 settle().await;
5390 announced.assert_next_none("test");
5391 let face_b = announced.assert_next_some("test");
5392 assert!(!face_b.is_clone(&face_a), "a replacement, never a splice");
5393 assert_eq!(consumer.get_broadcast("test").unwrap().route().hops, hops_b);
5394 assert!(face_a.is_closed(), "the displaced front must close");
5398
5399 source_a.finish();
5401 settle().await;
5402 settle().await;
5403 announced.assert_next_wait();
5404 assert_eq!(consumer.get_broadcast("test").unwrap().route().hops, hops_b);
5405 }
5406
5407 #[tokio::test]
5410 async fn test_displaced_publisher_reclaims_path_when_replacement_leaves() {
5411 tokio::time::pause();
5412
5413 let origin = Origin::random().produce();
5414 let consumer = origin.consume();
5415 let mut announced = consumer.announced();
5416
5417 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5418 let hops_b = OriginList::try_from(vec![Origin::new(2).unwrap()]).unwrap();
5419
5420 let mut source_a = origin
5422 .create_broadcast("test", announce().with_hops(hops_a.clone()))
5423 .unwrap();
5424 settle().await;
5425 announced.assert_next_some("test");
5426
5427 let mut source_b = origin.create_broadcast("test", announce().with_hops(hops_b)).unwrap();
5430 settle().await;
5431 settle().await;
5432 announced.assert_next_none("test");
5433 announced.assert_next_some("test");
5434 announced.assert_next_wait();
5437
5438 source_b.finish();
5440 settle().await;
5441 settle().await;
5442
5443 assert!(
5444 consumer.get_broadcast("test").is_some(),
5445 "the still-live publisher A should reclaim the path once its replacement leaves"
5446 );
5447 let recovered = consumer.request_broadcast("test").await.unwrap();
5448 assert_eq!(recovered.route().hops, hops_a);
5449
5450 announced.assert_next_none("test");
5451 announced.assert_next_some("test");
5452 announced.assert_next_wait();
5453
5454 source_a.finish();
5455 }
5456
5457 #[tokio::test]
5461 async fn test_reclaimed_publisher_can_still_replace_itself() {
5462 tokio::time::pause();
5463
5464 let origin = Origin::random().produce();
5465 let consumer = origin.consume();
5466
5467 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5468 let hops_b = OriginList::try_from(vec![Origin::new(2).unwrap()]).unwrap();
5469 let hops_c = OriginList::try_from(vec![Origin::new(3).unwrap()]).unwrap();
5470
5471 let mut source_a1 = origin
5473 .create_broadcast("test", announce().with_hops(hops_a.clone()))
5474 .unwrap();
5475 let mut source_a2 = origin
5476 .create_broadcast("test", announce().with_hops(hops_a.clone()))
5477 .unwrap();
5478 settle().await;
5479 settle().await;
5480 assert_eq!(consumer.get_broadcast("test").unwrap().route().hops, hops_a);
5481
5482 let mut source_b = origin.create_broadcast("test", announce().with_hops(hops_b)).unwrap();
5484 settle().await;
5485 settle().await;
5486
5487 source_b.finish();
5489 settle().await;
5490 settle().await;
5491 assert_eq!(consumer.get_broadcast("test").unwrap().route().hops, hops_a);
5492
5493 source_a1.set_route(announce().with_hops(hops_c.clone())).unwrap();
5497 settle().await;
5498 settle().await;
5499 assert_eq!(
5500 consumer.get_broadcast("test").unwrap().route().hops,
5501 hops_c,
5502 "the new publisher must take the path over, not stand by behind the old front"
5503 );
5504
5505 source_a1.finish();
5506 source_a2.finish();
5507 }
5508
5509 #[tokio::test]
5517 async fn test_remote_source_cannot_displace_a_local_publisher() {
5518 tokio::time::pause();
5519
5520 let origin = Origin::random().produce();
5521 let consumer = origin.consume();
5522 let mut announced = consumer.announced();
5523
5524 let mut local = origin.create_broadcast("test", announce()).unwrap();
5526 settle().await;
5527 let face = announced.assert_next_some("test");
5528
5529 let echo = OriginList::try_from(vec![Origin::UNKNOWN]).unwrap();
5531 let mut remote = origin.create_broadcast("test", announce().with_hops(echo)).unwrap();
5532 settle().await;
5533 settle().await;
5534 announced.assert_next_wait();
5535 assert!(
5536 consumer.get_broadcast("test").unwrap().is_clone(&face),
5537 "the local publisher must keep the path"
5538 );
5539 assert!(consumer.get_broadcast("test").unwrap().route().hops.is_empty());
5540
5541 remote.finish();
5543 settle().await;
5544 settle().await;
5545 announced.assert_next_wait();
5546
5547 local.finish();
5549 settle().await;
5550 settle().await;
5551 announced.assert_next_none("test");
5552 let echo = OriginList::try_from(vec![Origin::UNKNOWN]).unwrap();
5553 let _remote = origin
5554 .create_broadcast("test", announce().with_hops(echo.clone()))
5555 .unwrap();
5556 settle().await;
5557 settle().await;
5558 announced.assert_next_some("test");
5559 assert_eq!(consumer.get_broadcast("test").unwrap().route().hops, echo);
5560 }
5561
5562 #[tokio::test]
5567 async fn test_parked_remote_retries_when_local_unannounces() {
5568 tokio::time::pause();
5569
5570 let origin = Origin::random().produce();
5571 let consumer = origin.consume();
5572 let mut announced = consumer.announced();
5573
5574 let mut local = origin.create_broadcast("test", announce()).unwrap();
5575 settle().await;
5576 announced.assert_next_some("test");
5577
5578 let echo = OriginList::try_from(vec![Origin::UNKNOWN]).unwrap();
5580 let _remote = origin
5581 .create_broadcast("test", announce().with_hops(echo.clone()))
5582 .unwrap();
5583 settle().await;
5584 settle().await;
5585 announced.assert_next_wait();
5586
5587 local.set_route(broadcast::Route::new()).unwrap();
5590 settle().await;
5591 settle().await;
5592
5593 announced.assert_next_none("test");
5596 announced.assert_next_some("test");
5597 assert_eq!(consumer.get_broadcast("test").unwrap().route().hops, echo);
5598 }
5599
5600 #[tokio::test]
5603 async fn test_repricing_does_not_earn_a_takeover() {
5604 tokio::time::pause();
5605
5606 let origin = Origin::random().produce();
5607 let consumer = origin.consume();
5608 let mut announced = consumer.announced();
5609
5610 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5611 let hops_b = OriginList::try_from(vec![Origin::new(2).unwrap()]).unwrap();
5612
5613 let mut source_a = origin
5614 .create_broadcast("test", announce().with_hops(hops_a.clone()).with_cost(5))
5615 .unwrap();
5616 settle().await;
5617 announced.assert_next_some("test");
5618
5619 let mut source_b = origin
5621 .create_broadcast("test", announce().with_hops(hops_b.clone()))
5622 .unwrap();
5623 settle().await;
5624 settle().await;
5625 announced.assert_next_none("test");
5626 announced.assert_next_some("test");
5627 announced.assert_next_wait();
5628
5629 source_a
5632 .set_route(announce().with_hops(hops_a.clone()).with_cost(9))
5633 .unwrap();
5634 settle().await;
5635 settle().await;
5636 assert_eq!(
5637 consumer.get_broadcast("test").unwrap().route().hops,
5638 hops_b,
5639 "a repricing must not take the path back from the live front"
5640 );
5641 announced.assert_next_wait();
5642
5643 source_a.finish();
5644 source_b.finish();
5645 }
5646
5647 #[tokio::test]
5652 async fn test_reconnect_wins_over_stale_route() {
5653 tokio::time::pause();
5654
5655 let origin = Origin::random().produce();
5656 let consumer = origin.consume();
5657
5658 let publisher = Origin::new(1).unwrap();
5659 let hops = OriginList::try_from(vec![publisher]).unwrap();
5660
5661 let stale = origin
5664 .create_broadcast("test", announce().with_hops(hops.clone()))
5665 .unwrap();
5666 let mut stale_dynamic = stale.dynamic();
5667 settle().await;
5668
5669 let fresh = origin
5671 .create_broadcast("test", announce().with_hops(hops.clone()))
5672 .unwrap();
5673 let mut fresh_dynamic = fresh.dynamic();
5674 settle().await;
5675 settle().await;
5676
5677 let broadcast = consumer.request_broadcast("test").await.unwrap();
5679 let subscribing = broadcast.track("video").unwrap().subscribe(None);
5680 settle().await;
5681 let _producer = accept_track(&mut fresh_dynamic, "video").await;
5682 settle().await;
5683 subscribing.await.unwrap();
5684 stale_dynamic.assert_no_request();
5685 }
5686
5687 #[tokio::test]
5693 async fn test_carrying_reconnect_switches_immediately() {
5694 tokio::time::pause();
5695
5696 let origin = Origin::random().produce();
5697 let consumer = origin.consume();
5698
5699 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5700
5701 let stale = origin
5702 .create_broadcast("test", announce().with_hops(hops.clone()))
5703 .unwrap();
5704 let mut stale_dynamic = stale.dynamic();
5705 settle().await;
5706
5707 let broadcast = consumer.request_broadcast("test").await.unwrap();
5709 let subscribing = broadcast.track("video").unwrap().subscribe(None);
5710 settle().await;
5711 let _stale_producer = accept_track(&mut stale_dynamic, "video").await;
5712 settle().await;
5713 let _subscription = subscribing.await.unwrap();
5715
5716 let fresh = origin
5718 .create_broadcast("test", announce().with_hops(hops.clone()))
5719 .unwrap();
5720 let mut fresh_dynamic = fresh.dynamic();
5721 settle().await;
5722 settle().await;
5723
5724 let _fresh_producer = accept_track(&mut fresh_dynamic, "video").await;
5727 }
5728
5729 #[tokio::test]
5739 async fn test_offline_mismatch_never_evicts_a_live_front() {
5740 tokio::time::pause();
5741
5742 let origin = Origin::random().produce();
5743 let consumer = origin.consume();
5744 let mut announced = consumer.announced();
5745
5746 let hops_live = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
5747 let hops_cache = OriginList::try_from(vec![Origin::new(2).unwrap()]).unwrap();
5748
5749 let mut live = origin
5750 .create_broadcast("test", announce().with_hops(hops_live.clone()))
5751 .unwrap();
5752 let mut live_dynamic = live.dynamic();
5753 settle().await;
5754 let face = announced.assert_next_some("test");
5755
5756 let broadcast = consumer.request_broadcast("test").await.unwrap();
5758 let subscribing = broadcast.track("video").unwrap().subscribe(None);
5759 settle().await;
5760 let _producer = accept_track(&mut live_dynamic, "video").await;
5761 settle().await;
5762 subscribing.await.unwrap();
5763
5764 let cache = origin
5767 .create_broadcast("test", broadcast::Route::new().with_hops(hops_cache.clone()))
5768 .unwrap();
5769 settle().await;
5770 settle().await;
5771 announced.assert_next_wait();
5772 assert!(!face.is_closed(), "the live front must survive");
5773 assert_eq!(consumer.get_broadcast("test").unwrap().route().hops, hops_live);
5774
5775 live.finish();
5778 settle().await;
5779 settle().await;
5780 announced.assert_next_none("test");
5781 let taken = consumer
5782 .get_broadcast("test")
5783 .expect("the parked source must take over");
5784 assert_eq!(taken.route().hops, hops_cache);
5785 announced.assert_next_wait();
5787 drop(cache);
5788 }
5789
5790 #[tokio::test]
5796 async fn test_dispatch_excludes_requester() {
5797 tokio::time::pause();
5798
5799 let origin = Origin::random().produce();
5800 let consumer = origin.consume();
5801
5802 let peer = Origin::new(5).unwrap();
5803 let publisher = Origin::new(1).unwrap();
5804 let tainted = OriginList::try_from(vec![publisher, peer]).unwrap();
5806 let clean = OriginList::try_from(vec![publisher]).unwrap();
5807
5808 let source_a = origin.create_broadcast("test", announce().with_hops(tainted)).unwrap();
5809 let mut dynamic_a = source_a.dynamic();
5810 settle().await;
5811 let source_b = origin
5812 .create_broadcast("test", announce().with_hops(clean).with_cost(5))
5813 .unwrap();
5814 let mut dynamic_b = source_b.dynamic();
5815 settle().await;
5816 settle().await;
5817
5818 let shared = consumer.request_broadcast("test").await.unwrap();
5821 let subscribing = shared.track("video").unwrap().subscribe(None);
5822 let _producer_a = accept_track(&mut dynamic_a, "video").await;
5823 settle().await;
5824 subscribing.await.unwrap();
5825
5826 let scoped = consumer.clone().excluding(peer);
5831 let pinned = scoped.request_broadcast("test").await.unwrap();
5832 let subscribing = pinned.track("video").unwrap().subscribe(None);
5833 let _producer_b = accept_track(&mut dynamic_b, "video").await;
5834 settle().await;
5835 subscribing.await.unwrap();
5836 dynamic_a.assert_no_request();
5837 }
5838
5839 #[tokio::test]
5844 async fn test_unknown_publishers_do_not_splice() {
5845 tokio::time::pause();
5846
5847 let origin = Origin::random().produce();
5848 let consumer = origin.consume();
5849 let mut announced = consumer.announced();
5850
5851 let unknown_a = OriginList::try_from(vec![Origin::UNKNOWN]).unwrap();
5852 let unknown_b = OriginList::try_from(vec![Origin::UNKNOWN]).unwrap();
5853
5854 let mut source_a = origin
5855 .create_broadcast("test", announce().with_hops(unknown_a.clone()))
5856 .unwrap();
5857 settle().await;
5858 settle().await;
5859 announced.assert_next_some("test");
5860
5861 let source_b = origin
5865 .create_broadcast("test", announce().with_hops(unknown_b))
5866 .unwrap();
5867 settle().await;
5868 settle().await;
5869 announced.assert_next_none("test");
5870 let live = announced.assert_next_some("test");
5871
5872 source_a
5875 .set_route(announce().with_hops(unknown_a).with_cost(9))
5876 .unwrap();
5877 settle().await;
5878 settle().await;
5879 assert!(
5880 consumer.get_broadcast("test").unwrap().is_clone(&live),
5881 "UNKNOWN-to-UNKNOWN repricing must not replace the live front"
5882 );
5883 announced.assert_next_wait();
5884
5885 drop(source_a);
5886 drop(source_b);
5887 }
5888
5889 #[tokio::test]
5892 async fn test_known_publishers_still_splice() {
5893 tokio::time::pause();
5894
5895 let origin = Origin::random().produce();
5896 let consumer = origin.consume();
5897 let mut announced = consumer.announced();
5898
5899 let publisher = Origin::new(1).unwrap();
5900 let hops_a = OriginList::try_from(vec![publisher]).unwrap();
5901 let hops_b = OriginList::try_from(vec![publisher, Origin::new(3).unwrap()]).unwrap();
5902
5903 let source_a = origin.create_broadcast("test", announce().with_hops(hops_a)).unwrap();
5904 settle().await;
5905 settle().await;
5906 announced.assert_next_some("test");
5907
5908 let source_b = origin.create_broadcast("test", announce().with_hops(hops_b)).unwrap();
5910 settle().await;
5911 settle().await;
5912 announced.assert_next_wait();
5913
5914 drop(source_a);
5915 drop(source_b);
5916 }
5917
5918 #[tokio::test]
5924 async fn test_standby_join_splices_live_subscriber() {
5925 tokio::time::pause();
5926
5927 let origin = Origin::random().produce();
5928 let consumer = origin.consume();
5929
5930 let publisher = Origin::new(1).unwrap();
5931 let peer = Origin::new(5).unwrap();
5932 let via_peer = OriginList::try_from(vec![publisher, peer]).unwrap();
5933 let local = OriginList::try_from(vec![publisher]).unwrap();
5934
5935 let source_remote = origin
5937 .create_broadcast("test", announce().with_hops(via_peer).with_cost(2))
5938 .unwrap();
5939 let mut dynamic_remote = source_remote.dynamic();
5940 settle().await;
5941 settle().await;
5942 let broadcast = consumer.request_broadcast("test").await.unwrap();
5943 let subscribing = broadcast.track("video").unwrap().subscribe(None);
5944 let mut producer_remote = accept_track(&mut dynamic_remote, "video").await;
5945 settle().await;
5946 let mut sub = subscribing.await.unwrap();
5947 producer_remote.append_group().unwrap();
5948 assert_eq!(sub.assert_group().sequence, 0);
5949
5950 let source_local = origin.create_broadcast("test", announce().with_hops(local)).unwrap();
5954 let mut dynamic_local = source_local.dynamic();
5955 settle().await;
5956 let mut producer_local = accept_track(&mut dynamic_local, "video").await;
5957 settle().await;
5958 sub.assert_no_group();
5959 assert_eq!(producer_local.subscription().unwrap().group_start, None);
5960 producer_local.create_group(group::Info { sequence: 1 }).unwrap();
5961 assert_eq!(sub.assert_group().sequence, 1);
5962 sub.assert_not_closed();
5963 }
5964
5965 #[tokio::test]
5972 async fn test_standby_with_a_partial_track_list_splits_per_track() {
5973 tokio::time::pause();
5974
5975 let origin = Origin::random().produce();
5976 let consumer = origin.consume();
5977
5978 let publisher = Origin::new(1).unwrap();
5979 let peer = Origin::new(5).unwrap();
5980 let via_peer = OriginList::try_from(vec![publisher, peer]).unwrap();
5981 let local = OriginList::try_from(vec![publisher]).unwrap();
5982
5983 let source_remote = origin
5985 .create_broadcast("test", announce().with_hops(via_peer).with_cost(2))
5986 .unwrap();
5987 let mut dynamic_remote = source_remote.dynamic();
5988 settle().await;
5989 settle().await;
5990 let broadcast = consumer.request_broadcast("test").await.unwrap();
5991
5992 let subscribing_video = broadcast.track("video").unwrap().subscribe(None);
5993 let subscribing_audio = broadcast.track("audio").unwrap().subscribe(None);
5994 let mut producers_remote = accept_tracks(&mut dynamic_remote, 2).await;
5995 settle().await;
5996
5997 let mut sub_video = subscribing_video.await.unwrap();
5998 let mut sub_audio = subscribing_audio.await.unwrap();
5999 for name in ["video", "audio"] {
6000 producers_remote.get_mut(name).unwrap().append_group().unwrap();
6001 }
6002 assert_eq!(sub_video.assert_group().sequence, 0);
6003 assert_eq!(sub_audio.assert_group().sequence, 0);
6004
6005 let source_local = origin.create_broadcast("test", announce().with_hops(local)).unwrap();
6007 let mut dynamic_local = source_local.dynamic();
6008 settle().await;
6009
6010 let mut producer_local = None;
6011 for _ in 0..2 {
6012 let request = tokio::time::timeout(std::time::Duration::from_secs(1), dynamic_local.requested_track())
6013 .await
6014 .expect("timed out waiting for a track request")
6015 .expect("source closed");
6016 match request.name() {
6017 "video" => producer_local = Some(request.accept(None)),
6018 "audio" => request.reject(Error::NotFound),
6019 other => panic!("unexpected track dispatched: {other}"),
6020 }
6021 }
6022 settle().await;
6023 let mut producer_local = producer_local.expect("the standby was never asked for video");
6024
6025 sub_video.assert_no_group();
6028 assert_eq!(producer_local.subscription().unwrap().group_start, None);
6029 producer_local.create_group(group::Info { sequence: 1 }).unwrap();
6030 assert_eq!(
6031 sub_video.assert_group().sequence,
6032 1,
6033 "video did not move to the standby"
6034 );
6035
6036 producers_remote.get_mut("audio").unwrap().append_group().unwrap();
6038 assert_eq!(
6039 sub_audio.assert_group().sequence,
6040 1,
6041 "audio did not stay on the incumbent"
6042 );
6043 sub_video.assert_not_closed();
6044 sub_audio.assert_not_closed();
6045 }
6046
6047 #[tokio::test]
6054 async fn test_standby_refusal_keeps_incumbent() {
6055 tokio::time::pause();
6056 for incompatible in [false, true] {
6057 let origin = Origin::random().produce();
6058 let consumer = origin.consume();
6059
6060 let publisher = Origin::new(1).unwrap();
6061 let peer = Origin::new(5).unwrap();
6062 let via_peer = OriginList::try_from(vec![publisher, peer]).unwrap();
6063 let local = OriginList::try_from(vec![publisher]).unwrap();
6064
6065 let source_remote = origin
6067 .create_broadcast("test", announce().with_hops(via_peer).with_cost(2))
6068 .unwrap();
6069 let mut dynamic_remote = source_remote.dynamic();
6070 settle().await;
6071 settle().await;
6072 let broadcast = consumer.request_broadcast("test").await.unwrap();
6073 let subscribing = broadcast.track("audio").unwrap().subscribe(None);
6074 let mut producer_remote = accept_track(&mut dynamic_remote, "audio").await;
6075 settle().await;
6076 let mut sub = subscribing.await.unwrap();
6077 producer_remote.append_group().unwrap();
6078 assert_eq!(sub.assert_group().sequence, 0);
6079
6080 let source_local = origin.create_broadcast("test", announce().with_hops(local)).unwrap();
6083 let mut dynamic_local = source_local.dynamic();
6084 settle().await;
6085 let request = dynamic_local.requested_track().await.unwrap();
6086 assert_eq!(request.name(), "audio");
6087 let incompatible_track = if incompatible {
6088 Some(request.accept(track::Info::default().with_timescale(Timescale::MICRO)))
6089 } else {
6090 request.reject(Error::NotFound);
6091 None
6092 };
6093 settle().await;
6094
6095 producer_remote.append_group().unwrap();
6097 assert_eq!(sub.assert_group().sequence, 1);
6098 sub.assert_not_closed();
6099
6100 source_remote.abort(Error::Dropped).unwrap();
6103 settle().await;
6104 settle().await;
6105 sub.assert_closed();
6106 dynamic_local.assert_no_request();
6107
6108 drop(incompatible_track);
6109 let retry = broadcast.track("audio").unwrap().subscribe(None);
6111 let mut producer_local = accept_track(&mut dynamic_local, "audio").await;
6112 settle().await;
6113 let mut sub = retry.await.expect("a fresh request must reach the standby");
6114 producer_local.create_group(group::Info { sequence: 2 }).unwrap();
6115 assert_eq!(sub.assert_group().sequence, 2);
6116 }
6117 }
6118
6119 #[tokio::test]
6124 async fn test_unservable_track_retried_by_a_later_request() {
6125 tokio::time::pause();
6126
6127 let origin = Origin::random().produce();
6128 let consumer = origin.consume();
6129
6130 let source = origin.create_broadcast("test", announce()).unwrap();
6131 let mut dynamic = source.dynamic();
6132 settle().await;
6133 settle().await;
6134 let broadcast = consumer.request_broadcast("test").await.unwrap();
6135
6136 let subscribing = broadcast.track("audio").unwrap().subscribe(None);
6138 let request = dynamic.requested_track().await.unwrap();
6139 request.reject(Error::NotFound);
6140 settle().await;
6141 assert!(matches!(subscribing.await, Err(Error::NotFound)));
6142
6143 let retry = broadcast.track("audio").unwrap().subscribe(None);
6145 let mut producer = accept_track(&mut dynamic, "audio").await;
6146 settle().await;
6147 let mut sub = retry.await.expect("a fresh request must reach the source");
6148 producer.append_group().unwrap();
6149 assert_eq!(sub.assert_group().sequence, 0);
6150 }
6151
6152 #[tokio::test]
6158 async fn test_track_dying_without_progress_aborts() {
6159 tokio::time::pause();
6160
6161 let origin = Origin::random().produce();
6162 let consumer = origin.consume();
6163
6164 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
6165 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
6166 let mut dynamic = source.dynamic();
6167 settle().await;
6168 settle().await;
6169 let broadcast = consumer.request_broadcast("test").await.unwrap();
6170
6171 let subscribing = broadcast.track("video").unwrap().subscribe(None);
6172 let producer = accept_track(&mut dynamic, "video").await;
6173 settle().await;
6174 let mut sub = subscribing.await.unwrap();
6175
6176 drop(producer);
6179 settle().await;
6180 sub.assert_closed();
6181 dynamic.assert_no_request();
6182
6183 let retry = broadcast.track("video").unwrap().subscribe(None);
6185 let mut producer = accept_track(&mut dynamic, "video").await;
6186 settle().await;
6187 let mut sub = retry.await.expect("a fresh request must reach the source");
6188 producer.append_group().unwrap();
6189 assert_eq!(sub.assert_group().sequence, 0);
6190 }
6191
6192 #[tokio::test]
6197 async fn test_delivered_copy_death_survives_unrelated_wakes() {
6198 tokio::time::pause();
6199
6200 let origin = Origin::random().produce();
6201 let consumer = origin.consume();
6202
6203 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
6204 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
6205 let mut dynamic = source.dynamic();
6206 settle().await;
6207 settle().await;
6208 let broadcast = consumer.request_broadcast("test").await.unwrap();
6209
6210 let subscribing = broadcast.track("video").unwrap().subscribe(None);
6211 let mut producer = accept_track(&mut dynamic, "video").await;
6212 settle().await;
6213 let mut sub = subscribing.await.unwrap();
6214 producer.append_group().unwrap();
6215 assert_eq!(sub.assert_group().sequence, 0);
6216
6217 drop(sub);
6220 settle().await;
6221 let resubscribing = broadcast.track("video").unwrap().subscribe(None);
6222 settle().await;
6223 let mut sub = resubscribing.await.unwrap();
6224 assert_eq!(sub.assert_group().sequence, 0, "cached group re-served");
6225
6226 drop(producer);
6229 let mut producer = accept_track(&mut dynamic, "video").await;
6230 settle().await;
6231 producer.create_group(group::Info { sequence: 1 }).unwrap();
6232 assert_eq!(sub.assert_group().sequence, 1);
6233 sub.assert_not_closed();
6234 }
6235
6236 #[tokio::test]
6242 async fn test_per_track_fallback_respects_exclusion() {
6243 tokio::time::pause();
6244
6245 let origin = Origin::random().produce();
6246 let consumer = origin.consume();
6247
6248 let publisher = Origin::new(1).unwrap();
6249 let peer = Origin::new(5).unwrap();
6250 let via_peer = OriginList::try_from(vec![publisher, peer]).unwrap();
6251 let local = OriginList::try_from(vec![publisher]).unwrap();
6252
6253 let source_tainted = origin
6255 .create_broadcast("test", announce().with_hops(via_peer).with_cost(2))
6256 .unwrap();
6257 let mut dynamic_tainted = source_tainted.dynamic();
6258 settle().await;
6259 settle().await;
6260
6261 let source_clean = origin.create_broadcast("test", announce().with_hops(local)).unwrap();
6264 let mut dynamic_clean = source_clean.dynamic();
6265 settle().await;
6266
6267 let scoped = consumer.clone().excluding(peer);
6268 let broadcast = scoped.request_broadcast("test").await.unwrap();
6269 let _subscribing = broadcast.track("video").unwrap().subscribe(None);
6270 settle().await;
6271
6272 let request = dynamic_clean.requested_track().await.unwrap();
6276 request.reject(Error::NotFound);
6277 settle().await;
6278 dynamic_tainted.assert_no_request();
6279 }
6280
6281 #[tokio::test]
6285 async fn test_exclusion_survives_failover_onto_a_tainted_route() {
6286 tokio::time::pause();
6287
6288 let origin = Origin::random().produce();
6289 let consumer = origin.consume();
6290
6291 let publisher = Origin::new(1).unwrap();
6292 let peer = Origin::new(5).unwrap();
6293 let via_peer = OriginList::try_from(vec![publisher, peer]).unwrap();
6294 let local = OriginList::try_from(vec![publisher]).unwrap();
6295
6296 let source_tainted = origin
6297 .create_broadcast("test", announce().with_hops(via_peer).with_cost(2))
6298 .unwrap();
6299 let mut dynamic_tainted = source_tainted.dynamic();
6300 settle().await;
6301 settle().await;
6302 let source_clean = origin.create_broadcast("test", announce().with_hops(local)).unwrap();
6303 let mut dynamic_clean = source_clean.dynamic();
6304 settle().await;
6305
6306 let scoped = consumer.clone().excluding(peer);
6307 let broadcast = scoped.request_broadcast("test").await.unwrap();
6308 let _subscribing = broadcast.track("video").unwrap().subscribe(None);
6309 let _clean = accept_track(&mut dynamic_clean, "video").await;
6310 settle().await;
6311
6312 source_clean.abort(Error::Dropped).unwrap();
6314 settle().await;
6315 settle().await;
6316 dynamic_tainted.assert_no_request();
6317
6318 assert!(matches!(scoped.request_broadcast("test").await, Err(Error::Unroutable)));
6321 }
6322
6323 #[tokio::test]
6328 async fn test_exclusion_holds_when_a_tainted_route_attaches_later() {
6329 tokio::time::pause();
6330
6331 let origin = Origin::random().produce();
6332 let consumer = origin.consume();
6333
6334 let publisher = Origin::new(1).unwrap();
6335 let peer = Origin::new(5).unwrap();
6336 let local = OriginList::try_from(vec![publisher]).unwrap();
6337 let via_peer = OriginList::try_from(vec![publisher, peer]).unwrap();
6338
6339 let source_clean = origin
6343 .create_broadcast("test", announce().with_hops(local).with_cost(5))
6344 .unwrap();
6345 let mut dynamic_clean = source_clean.dynamic();
6346 settle().await;
6347 settle().await;
6348 let scoped = consumer.clone().excluding(peer);
6349 let broadcast = scoped.request_broadcast("test").await.unwrap();
6350 let subscribing = broadcast.track("video").unwrap().subscribe(None);
6351 let mut producer_clean = accept_track(&mut dynamic_clean, "video").await;
6352 settle().await;
6353 let mut sub = subscribing.await.unwrap();
6354 producer_clean.append_group().unwrap();
6355 assert_eq!(sub.assert_group().sequence, 0);
6356
6357 let mut tainted = announce().with_hops(via_peer.clone()).with_cost(0);
6364 tainted.advertised = 1;
6365 let mut source_tainted = origin.create_broadcast("test", tainted).unwrap();
6366 let mut dynamic_tainted = source_tainted.dynamic();
6367 settle().await;
6368 settle().await;
6369 dynamic_tainted.assert_no_request();
6370 producer_clean.append_group().unwrap();
6371 assert_eq!(sub.assert_group().sequence, 1);
6372 sub.assert_not_closed();
6373
6374 drop(sub);
6377 drop(broadcast);
6378 drop(scoped);
6379 settle().await;
6380 let mut bumped = announce().with_hops(via_peer).with_cost(1);
6381 bumped.advertised = 1;
6382 source_tainted.set_route(bumped).unwrap();
6383 settle().await;
6384 let plain = consumer.request_broadcast("test").await.unwrap();
6385 let _plain_track = plain.track("video").unwrap().subscribe(None);
6386 settle().await;
6387 settle().await;
6388 assert!(
6389 dynamic_tainted.requested_track().now_or_never().is_some(),
6390 "the front must be free to use the route again once the peer is gone"
6391 );
6392 }
6393
6394 #[tokio::test]
6398 async fn test_excluded_path_never_reaches_the_dynamic_handler() {
6399 tokio::time::pause();
6400
6401 let origin = Origin::random().produce();
6402 let consumer = origin.consume();
6403 let mut dynamic = origin.dynamic();
6404
6405 let peer = Origin::new(5).unwrap();
6406 let tainted = OriginList::try_from(vec![Origin::new(1).unwrap(), peer]).unwrap();
6407 let _source = origin.create_broadcast("test", announce().with_hops(tainted)).unwrap();
6408 settle().await;
6409 settle().await;
6410
6411 let scoped = consumer.clone().excluding(peer);
6412 assert!(matches!(scoped.request_broadcast("test").await, Err(Error::Unroutable)));
6413 assert!(
6414 dynamic.requested_broadcast().now_or_never().is_none(),
6415 "the dynamic handler was asked to route around the exclusion"
6416 );
6417
6418 let _pending = scoped.request_broadcast("other");
6420 settle().await;
6421 assert!(
6422 dynamic.requested_broadcast().now_or_never().is_some(),
6423 "a genuinely missing path must still fall back"
6424 );
6425 }
6426
6427 #[tokio::test]
6435 async fn test_out_of_scope_path_never_reaches_the_dynamic_handler() {
6436 let origin = Origin::random().produce();
6437 let mut dynamic = origin.dynamic();
6438
6439 let scoped = origin.consume().scope(&["tenant-a".into()]).unwrap();
6440
6441 for path in ["tenant-b/live", "tenant-a-other/live"] {
6444 let refused = scoped
6448 .request_broadcast(path)
6449 .now_or_never()
6450 .expect("an out-of-scope request must be refused synchronously, not queued");
6451 assert!(matches!(refused, Err(Error::Unauthorized)));
6452 assert!(
6453 dynamic.requested_broadcast().now_or_never().is_none(),
6454 "the dynamic handler was asked to create a broadcast the requester may not read"
6455 );
6456 }
6457 }
6458
6459 #[tokio::test]
6462 async fn test_out_of_scope_path_is_unauthorized_without_a_handler() {
6463 let origin = Origin::random().produce();
6464 let scoped = origin.consume().scope(&["tenant-a".into()]).unwrap();
6465
6466 let refused = scoped
6467 .request_broadcast("tenant-b/live")
6468 .now_or_never()
6469 .expect("an out-of-scope request must be refused synchronously, not queued");
6470 assert!(matches!(refused, Err(Error::Unauthorized)));
6471 }
6472
6473 #[tokio::test]
6476 async fn test_in_scope_path_still_reaches_the_dynamic_handler() {
6477 let origin = Origin::random().produce();
6478 let mut dynamic = origin.dynamic();
6479
6480 let scoped = origin.consume().scope(&["tenant-a".into()]).unwrap();
6481 let requested = scoped.request_broadcast("tenant-a/live");
6482
6483 let served = broadcast::Info::new().produce();
6487 let request = dynamic
6488 .requested_broadcast()
6489 .now_or_never()
6490 .expect("an in-scope request must reach the handler")
6491 .unwrap();
6492 assert_eq!(request.path(), &Path::new("tenant-a/live"));
6493 request.accept(&served);
6494
6495 let broadcast = requested.now_or_never().expect("served synchronously").unwrap();
6496 assert!(broadcast.is_clone(&served.consume()));
6497 }
6498
6499 #[tokio::test]
6506 async fn test_whole_tree_scope_still_reaches_the_dynamic_handler() {
6507 let origin = Origin::random().produce();
6508 let mut dynamic = origin.dynamic();
6509
6510 for (consumer, path) in [
6511 (origin.consume(), "tenant-a/live"),
6512 (origin.consume().scope(&["".into()]).unwrap(), "tenant-b/live"),
6513 ] {
6514 let requested = consumer.request_broadcast(path);
6515
6516 let served = broadcast::Info::new().produce();
6517 let request = dynamic
6518 .requested_broadcast()
6519 .now_or_never()
6520 .expect("a whole-tree request must reach the handler")
6521 .unwrap();
6522 assert_eq!(request.path(), &Path::new(path));
6523 request.accept(&served);
6524
6525 let broadcast = requested.now_or_never().expect("served synchronously").unwrap();
6526 assert!(broadcast.is_clone(&served.consume()));
6527 }
6528 }
6529
6530 #[tokio::test]
6533 async fn test_dispatch_all_tainted_unroutable() {
6534 tokio::time::pause();
6535
6536 let origin = Origin::random().produce();
6537 let consumer = origin.consume();
6538
6539 let peer = Origin::new(5).unwrap();
6540 let tainted = OriginList::try_from(vec![Origin::new(1).unwrap(), peer]).unwrap();
6541 let _source = origin.create_broadcast("test", announce().with_hops(tainted)).unwrap();
6542 settle().await;
6543 settle().await;
6544
6545 let scoped = consumer.clone().excluding(peer);
6546 match scoped.request_broadcast("test").await {
6547 Err(Error::Unroutable) => {}
6548 Err(err) => panic!("expected Unroutable, got {err:?}"),
6549 Ok(_) => panic!("expected Unroutable, got a broadcast"),
6550 }
6551
6552 consumer.request_broadcast("test").await.unwrap();
6554 }
6555
6556 #[tokio::test]
6557 async fn test_duplicate_reverse() {
6558 tokio::time::pause();
6559
6560 let origin = Origin::random().produce();
6561
6562 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
6563 let mut broadcast2 = origin.create_broadcast("test", announce()).unwrap();
6564 settle().await;
6565 assert!(origin.consume().get_broadcast("test").is_some());
6566
6567 broadcast2.finish();
6569 settle().await;
6570 assert!(origin.consume().get_broadcast("test").is_some());
6571
6572 broadcast1.finish();
6573 settle().await;
6574 assert!(origin.consume().get_broadcast("test").is_none());
6575 }
6576
6577 #[tokio::test]
6578 async fn test_deterministic_tiebreak() {
6579 tokio::time::pause();
6580
6581 fn hops(ids: &[u64]) -> OriginList {
6582 OriginList::try_from(
6583 ids.iter()
6584 .copied()
6585 .map(|id| Origin::new(id).unwrap())
6586 .collect::<Vec<_>>(),
6587 )
6588 .unwrap()
6589 }
6590
6591 async fn winner(first: &[u64], second: &[u64]) -> OriginList {
6594 let origin = Origin::random().produce();
6595 let _a = origin
6596 .create_broadcast("test", announce().with_hops(hops(first)))
6597 .unwrap();
6598 let _b = origin
6599 .create_broadcast("test", announce().with_hops(hops(second)))
6600 .unwrap();
6601 settle().await;
6602 origin.consume().get_broadcast("test").unwrap().route().hops
6603 }
6604
6605 let forward = winner(&[5, 20], &[5, 40]).await;
6609 let reverse = winner(&[5, 40], &[5, 20]).await;
6610 assert_eq!(forward, reverse, "tie-break must not depend on publish order");
6611
6612 assert_eq!(winner(&[5, 20], &[5]).await.len(), 1);
6614 assert_eq!(winner(&[5], &[5, 20]).await.len(), 1);
6615 }
6616
6617 #[tokio::test]
6622 async fn test_many_announces() {
6623 let origin = Origin::random().produce();
6624
6625 let mut consumer = origin.consume().announced();
6626 let mut broadcasts = Vec::new();
6628 for i in 0..256 {
6629 broadcasts.push(origin.create_broadcast(format!("test{i:03}"), announce()).unwrap());
6630 settle().await;
6631 }
6632
6633 for i in 0..256 {
6634 consumer.assert_next_some(format!("test{i:03}"));
6635 }
6636 consumer.assert_next_wait();
6637 }
6638
6639 #[tokio::test]
6640 async fn test_many_announces_try() {
6641 let origin = Origin::random().produce();
6642
6643 let mut consumer = origin.consume().announced();
6644 let mut broadcasts = Vec::new();
6646 for i in 0..256 {
6647 broadcasts.push(origin.create_broadcast(format!("test{i:03}"), announce()).unwrap());
6648 settle().await;
6649 }
6650
6651 for i in 0..256 {
6652 consumer.assert_try_next_some(format!("test{i:03}"));
6653 }
6654 }
6655
6656 #[tokio::test]
6657 async fn test_with_root_basic() {
6658 let origin = Origin::random().produce();
6659
6660 let foo_producer = origin.with_root("foo").expect("should create root");
6662 assert_eq!(foo_producer.root().as_str(), "foo");
6663
6664 let mut consumer = origin.consume().announced();
6665
6666 let _broadcast = foo_producer
6668 .create_broadcast("bar/baz", announce())
6669 .expect("publish allowed");
6670 settle().await;
6671 consumer.assert_next_some("foo/bar/baz");
6673
6674 let mut foo_consumer = foo_producer.consume().announced();
6676 foo_consumer.assert_next_some("bar/baz");
6677 }
6678
6679 #[tokio::test]
6680 async fn test_with_root_nested() {
6681 let origin = Origin::random().produce();
6682
6683 let foo_producer = origin.with_root("foo").expect("should create foo root");
6685 let foo_bar_producer = foo_producer.with_root("bar").expect("should create bar root");
6686 assert_eq!(foo_bar_producer.root().as_str(), "foo/bar");
6687
6688 let mut consumer = origin.consume().announced();
6689
6690 let _broadcast = foo_bar_producer
6692 .create_broadcast("baz", announce())
6693 .expect("publish allowed");
6694 settle().await;
6695 consumer.assert_next_some("foo/bar/baz");
6697
6698 let mut foo_bar_consumer = foo_bar_producer.consume().announced();
6700 foo_bar_consumer.assert_next_some("baz");
6701 }
6702
6703 #[tokio::test]
6704 async fn test_publish_scope_allows() {
6705 let origin = Origin::random().produce();
6706
6707 let limited_producer = origin
6709 .scope(&["allowed/path1".into(), "allowed/path2".into()])
6710 .expect("should create limited producer");
6711
6712 let _broadcast = limited_producer
6714 .create_broadcast("allowed/path1", announce())
6715 .expect("publish allowed");
6716 let _keep2 = limited_producer
6717 .create_broadcast("allowed/path1/nested", announce())
6718 .expect("publish allowed");
6719 let _keep3 = limited_producer
6720 .create_broadcast("allowed/path2", announce())
6721 .expect("publish allowed");
6722 settle().await;
6723
6724 assert!(limited_producer.create_broadcast("notallowed", announce()).is_err());
6726 assert!(limited_producer.create_broadcast("allowed", announce()).is_err()); assert!(limited_producer.create_broadcast("other/path", announce()).is_err());
6728 }
6729
6730 #[tokio::test]
6731 async fn test_publish_max_parts() {
6732 let origin = Origin::random().produce();
6733
6734 let at_limit = (0..Path::MAX_PARTS)
6735 .map(|i| i.to_string())
6736 .collect::<Vec<_>>()
6737 .join("/");
6738 let _broadcast = origin
6739 .create_broadcast(at_limit.as_str(), announce())
6740 .expect("publish allowed");
6741 settle().await;
6742
6743 let too_deep = format!("{at_limit}/extra");
6744 assert!(origin.create_broadcast(too_deep.as_str(), announce()).is_err());
6745
6746 let rooted = origin.with_root("root").expect("wildcard allows any root");
6748 assert!(rooted.create_broadcast(at_limit.as_str(), announce()).is_err());
6749 }
6750
6751 #[tokio::test]
6752 async fn test_publish_scope_empty() {
6753 let origin = Origin::random().produce();
6754
6755 assert!(origin.scope(&[]).is_none());
6757 }
6758
6759 #[tokio::test]
6760 async fn test_consume_scope_filters() {
6761 let origin = Origin::random().produce();
6762
6763 let mut consumer = origin.consume().announced();
6764
6765 let _broadcast1 = origin.create_broadcast("allowed", announce()).unwrap();
6767 let _broadcast2 = origin.create_broadcast("allowed/nested", announce()).unwrap();
6768 let _broadcast3 = origin.create_broadcast("notallowed", announce()).unwrap();
6769 settle().await;
6770
6771 let mut limited_consumer = origin
6773 .consume()
6774 .scope(&["allowed".into()])
6775 .expect("should create limited consumer")
6776 .announced();
6777
6778 limited_consumer.assert_next_some("allowed");
6780 limited_consumer.assert_next_some("allowed/nested");
6781 limited_consumer.assert_next_wait(); consumer.assert_next_some("allowed");
6785 consumer.assert_next_some("allowed/nested");
6786 consumer.assert_next_some("notallowed");
6787 }
6788
6789 #[tokio::test]
6790 async fn test_consume_scope_multiple_prefixes() {
6791 let origin = Origin::random().produce();
6792
6793 let _broadcast1 = origin.create_broadcast("foo/test", announce()).unwrap();
6794 let _broadcast2 = origin.create_broadcast("bar/test", announce()).unwrap();
6795 let _broadcast3 = origin.create_broadcast("baz/test", announce()).unwrap();
6796 settle().await;
6797
6798 let mut limited_consumer = origin
6800 .consume()
6801 .scope(&["foo".into(), "bar".into()])
6802 .expect("should create limited consumer")
6803 .announced();
6804
6805 limited_consumer.assert_next_some("bar/test");
6807 limited_consumer.assert_next_some("foo/test");
6808 limited_consumer.assert_next_wait(); }
6810
6811 #[tokio::test]
6812 async fn test_with_root_and_publish_scope() {
6813 let origin = Origin::random().produce();
6814
6815 let foo_producer = origin.with_root("foo").expect("should create foo root");
6817
6818 let limited_producer = foo_producer
6820 .scope(&["bar".into(), "goop/pee".into()])
6821 .expect("should create limited producer");
6822
6823 let mut consumer = origin.consume().announced();
6824
6825 let _broadcast = limited_producer
6827 .create_broadcast("bar", announce())
6828 .expect("publish allowed");
6829 let _keep2 = limited_producer
6830 .create_broadcast("bar/nested", announce())
6831 .expect("publish allowed");
6832 let _keep3 = limited_producer
6833 .create_broadcast("goop/pee", announce())
6834 .expect("publish allowed");
6835 let _keep4 = limited_producer
6836 .create_broadcast("goop/pee/nested", announce())
6837 .expect("publish allowed");
6838 settle().await;
6839
6840 assert!(limited_producer.create_broadcast("baz", announce()).is_err());
6842 assert!(limited_producer.create_broadcast("goop", announce()).is_err()); assert!(limited_producer.create_broadcast("goop/other", announce()).is_err());
6844
6845 consumer.assert_next_some("foo/bar");
6847 consumer.assert_next_some("foo/bar/nested");
6848 consumer.assert_next_some("foo/goop/pee");
6849 consumer.assert_next_some("foo/goop/pee/nested");
6850 }
6851
6852 #[tokio::test]
6853 async fn test_with_root_and_consume_scope() {
6854 let origin = Origin::random().produce();
6855
6856 let _broadcast1 = origin.create_broadcast("foo/bar/test", announce()).unwrap();
6858 let _broadcast2 = origin.create_broadcast("foo/goop/pee/test", announce()).unwrap();
6859 let _broadcast3 = origin.create_broadcast("foo/other/test", announce()).unwrap();
6860 settle().await;
6861
6862 let foo_producer = origin.with_root("foo").expect("should create foo root");
6864
6865 let mut limited_consumer = foo_producer
6867 .consume()
6868 .scope(&["bar".into(), "goop/pee".into()])
6869 .expect("should create limited consumer")
6870 .announced();
6871
6872 limited_consumer.assert_next_some("bar/test");
6874 limited_consumer.assert_next_some("goop/pee/test");
6875 limited_consumer.assert_next_wait(); }
6877
6878 #[tokio::test]
6879 async fn test_with_root_unauthorized() {
6880 let origin = Origin::random().produce();
6881
6882 let limited_producer = origin
6884 .scope(&["allowed".into()])
6885 .expect("should create limited producer");
6886
6887 assert!(limited_producer.with_root("notallowed").is_none());
6889
6890 let allowed_root = limited_producer
6892 .with_root("allowed")
6893 .expect("should create allowed root");
6894 assert_eq!(allowed_root.root().as_str(), "allowed");
6895 }
6896
6897 #[tokio::test]
6898 async fn test_wildcard_permission() {
6899 let origin = Origin::random().produce();
6900
6901 let root_producer = origin.clone();
6903
6904 let _broadcast = root_producer
6906 .create_broadcast("any/path", announce())
6907 .expect("publish allowed");
6908 let _keep2 = root_producer
6909 .create_broadcast("other/path", announce())
6910 .expect("publish allowed");
6911 settle().await;
6912
6913 let foo_producer = root_producer.with_root("foo").expect("should create any root");
6915 assert_eq!(foo_producer.root().as_str(), "foo");
6916 }
6917
6918 #[tokio::test]
6919 async fn test_consume_broadcast_with_permissions() {
6920 let origin = Origin::random().produce();
6921
6922 let _broadcast1 = origin.create_broadcast("allowed/test", announce()).unwrap();
6923 let _broadcast2 = origin.create_broadcast("notallowed/test", announce()).unwrap();
6924 settle().await;
6925
6926 let limited_consumer = origin
6928 .consume()
6929 .scope(&["allowed".into()])
6930 .expect("should create limited consumer");
6931
6932 let result = limited_consumer.get_broadcast("allowed/test");
6934 assert!(result.is_some());
6935 assert!(
6936 result
6937 .unwrap()
6938 .is_clone(&origin.consume().get_broadcast("allowed/test").unwrap())
6939 );
6940
6941 assert!(limited_consumer.get_broadcast("notallowed/test").is_none());
6943
6944 let consumer = origin.consume();
6946 assert!(consumer.get_broadcast("allowed/test").is_some());
6947 assert!(consumer.get_broadcast("notallowed/test").is_some());
6948 }
6949
6950 #[tokio::test]
6951 async fn test_nested_paths_with_permissions() {
6952 let origin = Origin::random().produce();
6953
6954 let limited_producer = origin.scope(&["a/b/c".into()]).expect("should create limited producer");
6956
6957 let _broadcast = limited_producer
6959 .create_broadcast("a/b/c", announce())
6960 .expect("publish allowed");
6961 let _keep2 = limited_producer
6962 .create_broadcast("a/b/c/d", announce())
6963 .expect("publish allowed");
6964 let _keep3 = limited_producer
6965 .create_broadcast("a/b/c/d/e", announce())
6966 .expect("publish allowed");
6967 settle().await;
6968
6969 assert!(limited_producer.create_broadcast("a", announce()).is_err());
6971 assert!(limited_producer.create_broadcast("a/b", announce()).is_err());
6972 assert!(limited_producer.create_broadcast("a/b/other", announce()).is_err());
6973 }
6974
6975 #[tokio::test]
6976 async fn test_multiple_consumers_with_different_permissions() {
6977 let origin = Origin::random().produce();
6978
6979 let _broadcast1 = origin.create_broadcast("foo/test", announce()).unwrap();
6981 let _broadcast2 = origin.create_broadcast("bar/test", announce()).unwrap();
6982 let _broadcast3 = origin.create_broadcast("baz/test", announce()).unwrap();
6983 settle().await;
6984
6985 let mut foo_consumer = origin
6987 .consume()
6988 .scope(&["foo".into()])
6989 .expect("should create foo consumer")
6990 .announced();
6991
6992 let mut bar_consumer = origin
6993 .consume()
6994 .scope(&["bar".into()])
6995 .expect("should create bar consumer")
6996 .announced();
6997
6998 let mut foobar_consumer = origin
6999 .consume()
7000 .scope(&["foo".into(), "bar".into()])
7001 .expect("should create foobar consumer")
7002 .announced();
7003
7004 foo_consumer.assert_next_some("foo/test");
7006 foo_consumer.assert_next_wait();
7007
7008 bar_consumer.assert_next_some("bar/test");
7009 bar_consumer.assert_next_wait();
7010
7011 foobar_consumer.assert_next_some("bar/test");
7012 foobar_consumer.assert_next_some("foo/test");
7013 foobar_consumer.assert_next_wait();
7014 }
7015
7016 #[tokio::test]
7017 async fn test_select_with_empty_prefix() {
7018 let origin = Origin::random().produce();
7019
7020 let demo_producer = origin.with_root("demo").expect("should create demo root");
7022 let limited_producer = demo_producer
7023 .scope(&["worm-node".into(), "foobar".into()])
7024 .expect("should create limited producer");
7025
7026 let _broadcast1 = limited_producer
7028 .create_broadcast("worm-node/test", announce())
7029 .expect("publish allowed");
7030 let _broadcast2 = limited_producer
7031 .create_broadcast("foobar/test", announce())
7032 .expect("publish allowed");
7033 settle().await;
7034
7035 let mut consumer = limited_producer
7037 .consume()
7038 .scope(&["".into()])
7039 .expect("should create consumer with empty prefix")
7040 .announced();
7041
7042 let a1 = consumer.try_next().expect("expected first announcement");
7044 let a2 = consumer.try_next().expect("expected second announcement");
7045 consumer.assert_next_wait();
7046
7047 let mut paths: Vec<_> = [&a1, &a2].iter().map(|a| a.path.to_string()).collect();
7048 paths.sort();
7049 assert_eq!(paths, ["foobar/test", "worm-node/test"]);
7050 }
7051
7052 #[tokio::test]
7053 async fn test_select_narrowing_scope() {
7054 let origin = Origin::random().produce();
7055
7056 let demo_producer = origin.with_root("demo").expect("should create demo root");
7058 let limited_producer = demo_producer
7059 .scope(&["worm-node".into(), "foobar".into()])
7060 .expect("should create limited producer");
7061
7062 let _broadcast1 = limited_producer
7064 .create_broadcast("worm-node", announce())
7065 .expect("publish allowed");
7066 let _broadcast2 = limited_producer
7067 .create_broadcast("worm-node/foo", announce())
7068 .expect("publish allowed");
7069 let _broadcast3 = limited_producer
7070 .create_broadcast("foobar/bar", announce())
7071 .expect("publish allowed");
7072 settle().await;
7073
7074 let mut worm_consumer = limited_producer
7076 .consume()
7077 .scope(&["worm-node".into()])
7078 .expect("should create worm-node consumer")
7079 .announced();
7080
7081 worm_consumer.assert_next_some("worm-node");
7083 worm_consumer.assert_next_some("worm-node/foo");
7084 worm_consumer.assert_next_wait(); let mut foo_consumer = limited_producer
7088 .consume()
7089 .scope(&["worm-node/foo".into()])
7090 .expect("should create worm-node/foo consumer")
7091 .announced();
7092
7093 foo_consumer.assert_next_some("worm-node/foo");
7094 foo_consumer.assert_next_wait(); }
7096
7097 #[tokio::test]
7098 async fn test_select_multiple_roots_with_empty_prefix() {
7099 let origin = Origin::random().produce();
7100
7101 let limited_producer = origin
7103 .scope(&["app1".into(), "app2".into(), "shared".into()])
7104 .expect("should create limited producer");
7105
7106 let _broadcast1 = limited_producer
7108 .create_broadcast("app1/data", announce())
7109 .expect("publish allowed");
7110 let _broadcast2 = limited_producer
7111 .create_broadcast("app2/config", announce())
7112 .expect("publish allowed");
7113 let _broadcast3 = limited_producer
7114 .create_broadcast("shared/resource", announce())
7115 .expect("publish allowed");
7116 settle().await;
7117
7118 let mut consumer = limited_producer
7120 .consume()
7121 .scope(&["".into()])
7122 .expect("should create consumer with empty prefix")
7123 .announced();
7124
7125 consumer.assert_next_some("app1/data");
7127 consumer.assert_next_some("app2/config");
7128 consumer.assert_next_some("shared/resource");
7129 consumer.assert_next_wait();
7130 }
7131
7132 #[tokio::test]
7133 async fn test_publish_scope_with_empty_prefix() {
7134 let origin = Origin::random().produce();
7135
7136 let limited_producer = origin
7138 .scope(&["services/api".into(), "services/web".into()])
7139 .expect("should create limited producer");
7140
7141 let same_producer = limited_producer
7143 .scope(&["".into()])
7144 .expect("should create producer with empty prefix");
7145
7146 let _broadcast = same_producer
7148 .create_broadcast("services/api", announce())
7149 .expect("publish allowed");
7150 let _keep2 = same_producer
7151 .create_broadcast("services/web", announce())
7152 .expect("publish allowed");
7153 assert!(same_producer.create_broadcast("services/db", announce()).is_err());
7154 assert!(same_producer.create_broadcast("other", announce()).is_err());
7155 }
7156
7157 #[tokio::test]
7158 async fn test_select_narrowing_to_deeper_path() {
7159 let origin = Origin::random().produce();
7160
7161 let limited_producer = origin.scope(&["org".into()]).expect("should create limited producer");
7163
7164 let _broadcast1 = limited_producer
7166 .create_broadcast("org/team1/project1", announce())
7167 .expect("publish allowed");
7168 let _broadcast2 = limited_producer
7169 .create_broadcast("org/team1/project2", announce())
7170 .expect("publish allowed");
7171 let _broadcast3 = limited_producer
7172 .create_broadcast("org/team2/project1", announce())
7173 .expect("publish allowed");
7174 settle().await;
7175
7176 let mut team2_consumer = limited_producer
7178 .consume()
7179 .scope(&["org/team2".into()])
7180 .expect("should create team2 consumer")
7181 .announced();
7182
7183 team2_consumer.assert_next_some("org/team2/project1");
7184 team2_consumer.assert_next_wait(); let mut project1_consumer = limited_producer
7188 .consume()
7189 .scope(&["org/team1/project1".into()])
7190 .expect("should create project1 consumer")
7191 .announced();
7192
7193 project1_consumer.assert_next_some("org/team1/project1");
7195 project1_consumer.assert_next_wait();
7196 }
7197
7198 #[tokio::test]
7199 async fn test_select_with_non_matching_prefix() {
7200 let origin = Origin::random().produce();
7201
7202 let limited_producer = origin
7204 .scope(&["allowed/path".into()])
7205 .expect("should create limited producer");
7206
7207 assert!(limited_producer.consume().scope(&["different/path".into()]).is_none());
7209
7210 assert!(limited_producer.scope(&["other/path".into()]).is_none());
7212 }
7213
7214 #[tokio::test]
7217 async fn test_with_root_trailing_slash_consumer() {
7218 let origin = Origin::random().produce();
7219
7220 let prefix = "some_prefix/".to_string();
7222 let mut consumer = origin.consume().with_root(prefix).unwrap().announced();
7223
7224 let _b = origin.create_broadcast("some_prefix/test", announce()).unwrap();
7225 settle().await;
7226 consumer.assert_next_some("test");
7227 }
7228
7229 #[tokio::test]
7231 async fn test_with_root_trailing_slash_producer() {
7232 let origin = Origin::random().produce();
7233
7234 let prefix = "some_prefix/".to_string();
7236 let rooted = origin.with_root(prefix).unwrap();
7237
7238 let _b = rooted.create_broadcast("test", announce()).unwrap();
7239 settle().await;
7240
7241 let mut consumer = rooted.consume().announced();
7242 consumer.assert_next_some("test");
7243 }
7244
7245 #[tokio::test]
7247 async fn test_with_root_trailing_slash_unannounce() {
7248 tokio::time::pause();
7249
7250 let origin = Origin::random().produce();
7251
7252 let prefix = "some_prefix/".to_string();
7253 let mut consumer = origin.consume().with_root(prefix).unwrap().announced();
7254
7255 let mut b = origin.create_broadcast("some_prefix/test", announce()).unwrap();
7256 settle().await;
7257 consumer.assert_next_some("test");
7258
7259 b.finish();
7261 settle().await;
7262
7263 consumer.assert_next_none("test");
7265 }
7266
7267 #[tokio::test]
7268 async fn test_select_maintains_access_with_wider_prefix() {
7269 let origin = Origin::random().produce();
7270
7271 let demo_producer = origin.with_root("demo").expect("should create demo root");
7273 let user_producer = demo_producer
7274 .scope(&["worm-node".into(), "foobar".into()])
7275 .expect("should create user producer");
7276
7277 let _broadcast1 = user_producer
7279 .create_broadcast("worm-node/data", announce())
7280 .expect("publish allowed");
7281 let _broadcast2 = user_producer
7282 .create_broadcast("foobar", announce())
7283 .expect("publish allowed");
7284 settle().await;
7285
7286 let mut consumer = user_producer
7288 .consume()
7289 .scope(&["".into()])
7290 .expect("scope with empty prefix should not fail when user has specific permissions")
7291 .announced();
7292
7293 let a1 = consumer.try_next().expect("expected first announcement");
7295 let a2 = consumer.try_next().expect("expected second announcement");
7296 consumer.assert_next_wait();
7297
7298 let mut paths: Vec<_> = [&a1, &a2].iter().map(|a| a.path.to_string()).collect();
7299 paths.sort();
7300 assert_eq!(paths, ["foobar", "worm-node/data"]);
7301
7302 let mut narrow_consumer = user_producer
7304 .consume()
7305 .scope(&["worm-node".into()])
7306 .expect("should be able to narrow scope to worm-node")
7307 .announced();
7308
7309 narrow_consumer.assert_next_some("worm-node/data");
7310 narrow_consumer.assert_next_wait(); }
7312
7313 #[tokio::test]
7314 async fn test_duplicate_prefixes_deduped() {
7315 let origin = Origin::random().produce();
7316
7317 let producer = origin
7319 .scope(&["demo".into(), "demo".into()])
7320 .expect("should create producer");
7321
7322 let _broadcast = producer
7323 .create_broadcast("demo/stream", announce())
7324 .expect("publish allowed");
7325 settle().await;
7326
7327 let mut consumer = producer.consume().announced();
7328 consumer.assert_next_some("demo/stream");
7329 consumer.assert_next_wait();
7330 }
7331
7332 #[tokio::test]
7333 async fn test_overlapping_prefixes_deduped() {
7334 let origin = Origin::random().produce();
7335
7336 let producer = origin
7338 .scope(&["demo".into(), "demo/foo".into()])
7339 .expect("should create producer");
7340
7341 let _broadcast = producer
7343 .create_broadcast("demo/bar/stream", announce())
7344 .expect("publish allowed");
7345 settle().await;
7346
7347 let mut consumer = producer.consume().announced();
7348 consumer.assert_next_some("demo/bar/stream");
7349 consumer.assert_next_wait();
7350 }
7351
7352 #[tokio::test]
7353 async fn test_overlapping_prefixes_no_duplicate_announcements() {
7354 let origin = Origin::random().produce();
7355
7356 let producer = origin
7358 .scope(&["demo".into(), "demo/foo".into()])
7359 .expect("should create producer");
7360
7361 let _broadcast = producer
7362 .create_broadcast("demo/foo/stream", announce())
7363 .expect("publish allowed");
7364 settle().await;
7365
7366 let mut consumer = producer.consume().announced();
7367 consumer.assert_next_some("demo/foo/stream");
7369 consumer.assert_next_wait();
7370 }
7371
7372 #[tokio::test]
7373 async fn test_allowed_returns_deduped_prefixes() {
7374 let origin = Origin::random().produce();
7375
7376 let producer = origin
7377 .scope(&["demo".into(), "demo/foo".into(), "anon".into()])
7378 .expect("should create producer");
7379
7380 let allowed: Vec<_> = producer.allowed().collect();
7381 assert_eq!(allowed.len(), 2, "demo/foo should be subsumed by demo");
7382 }
7383
7384 #[tokio::test]
7385 async fn test_announced_broadcast_already_announced() {
7386 let origin = Origin::random().produce();
7387
7388 let _broadcast = origin.create_broadcast("test", announce()).unwrap();
7389 settle().await;
7390
7391 let consumer = origin.consume();
7392 let result = consumer.announced_broadcast("test").await.expect("should find it");
7393 assert!(result.is_clone(&consumer.get_broadcast("test").unwrap()));
7394 }
7395
7396 #[tokio::test]
7397 async fn test_announced_broadcast_delayed() {
7398 tokio::time::pause();
7399
7400 let origin = Origin::random().produce();
7401
7402 let consumer = origin.consume();
7403
7404 let wait = tokio::spawn({
7406 let consumer = consumer.clone();
7407 async move { consumer.announced_broadcast("test").await }
7408 });
7409
7410 tokio::task::yield_now().await;
7412
7413 let _broadcast = origin.create_broadcast("test", announce()).unwrap();
7414 settle().await;
7415
7416 let result = wait.await.unwrap().expect("should find it");
7417 assert!(result.is_clone(&consumer.get_broadcast("test").unwrap()));
7418 }
7419
7420 #[tokio::test]
7421 async fn test_announced_broadcast_ignores_unrelated_paths() {
7422 tokio::time::pause();
7423
7424 let origin = Origin::random().produce();
7425
7426 let consumer = origin.consume();
7427
7428 let wait = tokio::spawn({
7429 let consumer = consumer.clone();
7430 async move { consumer.announced_broadcast("target").await }
7431 });
7432
7433 tokio::task::yield_now().await;
7434
7435 let _other = origin.create_broadcast("other", announce()).unwrap();
7437 settle().await;
7438 tokio::task::yield_now().await;
7439 assert!(!wait.is_finished(), "must not resolve on unrelated path");
7440
7441 let _target = origin.create_broadcast("target", announce()).unwrap();
7442 settle().await;
7443 let result = wait.await.unwrap().expect("should find target");
7444 assert!(result.is_clone(&consumer.get_broadcast("target").unwrap()));
7445 }
7446
7447 #[tokio::test]
7448 async fn test_announced_broadcast_skips_nested_paths() {
7449 tokio::time::pause();
7450
7451 let origin = Origin::random().produce();
7452
7453 let consumer = origin.consume();
7454
7455 let wait = tokio::spawn({
7456 let consumer = consumer.clone();
7457 async move { consumer.announced_broadcast("foo").await }
7458 });
7459
7460 tokio::task::yield_now().await;
7461
7462 let _nested = origin.create_broadcast("foo/bar", announce()).unwrap();
7464 settle().await;
7465 tokio::task::yield_now().await;
7466 assert!(!wait.is_finished(), "must not resolve on a nested path");
7467
7468 let _exact = origin.create_broadcast("foo", announce()).unwrap();
7469 settle().await;
7470 let result = wait.await.unwrap().expect("should find foo exactly");
7471 assert!(result.is_clone(&consumer.get_broadcast("foo").unwrap()));
7472 }
7473
7474 #[tokio::test]
7475 async fn test_announced_broadcast_disallowed() {
7476 let origin = Origin::random().produce();
7477 let limited = origin
7478 .consume()
7479 .scope(&["allowed".into()])
7480 .expect("should create limited");
7481
7482 assert!(limited.announced_broadcast("notallowed").await.is_none());
7484 }
7485
7486 #[tokio::test]
7487 async fn test_announced_broadcast_scope_too_narrow() {
7488 let origin = Origin::random().produce();
7491 let limited = origin
7492 .consume()
7493 .scope(&["foo/specific".into()])
7494 .expect("should create limited");
7495
7496 let result = limited
7498 .announced_broadcast("foo")
7499 .now_or_never()
7500 .expect("must not block");
7501 assert!(result.is_none());
7502 }
7503
7504 #[tokio::test]
7508 async fn test_coalesce_announce_then_unannounce() {
7509 tokio::time::pause();
7511
7512 let origin = Origin::random().produce();
7513 let mut announced = origin.consume().announced();
7514
7515 let mut broadcast = origin.create_broadcast("test", announce()).unwrap();
7516 settle().await;
7517 broadcast.finish();
7518
7519 settle().await;
7520
7521 announced.assert_next_wait();
7522 }
7523
7524 #[tokio::test]
7525 async fn test_coalesce_announce_unannounce_announce() {
7526 tokio::time::pause();
7529
7530 let origin = Origin::random().produce();
7531 let mut announced = origin.consume().announced();
7532
7533 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
7534 settle().await;
7535 broadcast1.finish();
7536 settle().await;
7537 let _broadcast2 = origin.create_broadcast("test", announce()).unwrap();
7538 settle().await;
7539
7540 announced.assert_next_some("test");
7541 announced.assert_next_wait();
7542 }
7543
7544 #[tokio::test]
7545 async fn test_coalesce_unannounce_announce_preserved() {
7546 tokio::time::pause();
7549
7550 let origin = Origin::random().produce();
7551 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
7552 settle().await;
7553
7554 let mut announced = origin.consume().announced();
7555 announced.assert_next_some("test");
7556
7557 broadcast1.finish();
7559 settle().await;
7560
7561 let _broadcast2 = origin.create_broadcast("test", announce()).unwrap();
7562 settle().await;
7563
7564 announced.assert_next_none("test");
7566 announced.assert_next_some("test");
7567 announced.assert_next_wait();
7568 }
7569
7570 #[tokio::test]
7571 async fn test_coalesce_unannounce_announce_unannounce() {
7572 tokio::time::pause();
7575
7576 let origin = Origin::random().produce();
7577 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
7578 settle().await;
7579
7580 let mut announced = origin.consume().announced();
7581 announced.assert_next_some("test");
7582
7583 broadcast1.finish();
7584 settle().await;
7585
7586 let mut broadcast2 = origin.create_broadcast("test", announce()).unwrap();
7587 settle().await;
7588 broadcast2.finish();
7589 settle().await;
7590
7591 announced.assert_next_none("test");
7592 announced.assert_next_wait();
7593 }
7594
7595 #[tokio::test]
7596 async fn test_coalesce_churn_bounded() {
7597 tokio::time::pause();
7602
7603 let origin = Origin::random().produce();
7604 let mut announced = origin.consume().announced();
7605
7606 for _ in 0..1000 {
7607 let mut broadcast = origin.create_broadcast("test", announce()).unwrap();
7608 settle().await;
7609 broadcast.finish();
7610 }
7611 settle().await;
7612
7613 let mut collected = Vec::new();
7614 while let Some(update) = announced.try_next() {
7615 collected.push(update);
7616 }
7617 assert!(
7618 collected.len() <= 1,
7619 "expected at most one pending update, got {}",
7620 collected.len()
7621 );
7622 assert!(
7623 collected.iter().all(|a| a.path == Path::new("test")),
7624 "unexpected path in pending updates",
7625 );
7626 }
7627
7628 #[tokio::test]
7632 async fn test_consumer_clone_is_side_effect_free() {
7633 let origin = Origin::random().produce();
7634
7635 let _broadcast1 = origin.create_broadcast("test1", announce()).unwrap();
7636 let _broadcast2 = origin.create_broadcast("test2", announce()).unwrap();
7637 settle().await;
7638
7639 let consumer = origin.consume();
7640 let mut announced = consumer.announced();
7641
7642 for _ in 0..16 {
7645 let cloned = consumer.clone();
7646 assert!(cloned.get_broadcast("test1").is_some());
7647 assert!(cloned.get_broadcast("test2").is_some());
7648 }
7649
7650 let a1 = announced.try_next().expect("first announcement");
7653 let a2 = announced.try_next().expect("second announcement");
7654 announced.assert_next_wait();
7655
7656 let mut paths: Vec<_> = [&a1, &a2].iter().map(|a| a.path.to_string()).collect();
7657 paths.sort();
7658 assert_eq!(paths, ["test1", "test2"]);
7659
7660 let mut fresh = consumer.announced();
7662 let b1 = fresh.try_next().expect("backlog: first");
7663 let b2 = fresh.try_next().expect("backlog: second");
7664 fresh.assert_next_wait();
7665
7666 let mut paths: Vec<_> = [&b1, &b2].iter().map(|a| a.path.to_string()).collect();
7667 paths.sort();
7668 assert_eq!(paths, ["test1", "test2"]);
7669 }
7670
7671 #[tokio::test]
7673 async fn dynamic_request_unroutable_without_handler() {
7674 let origin = Origin::random().produce();
7675 let consumer = origin.consume();
7676 assert!(matches!(
7677 consumer.request_broadcast("missing").await,
7678 Err(Error::Unroutable)
7679 ));
7680 }
7681
7682 #[tokio::test(start_paused = true)]
7685 async fn dynamic_request_served_not_announced() {
7686 let origin = Origin::random().produce();
7687 let mut dynamic = origin.dynamic();
7688 let consumer = origin.consume();
7689
7690 let mut announced = origin.consume().announced();
7692 announced.assert_next_wait();
7693
7694 let served = broadcast::Info::new().produce();
7695 let request_fut = consumer.request_broadcast("fallback");
7698
7699 let mut served_dynamic = served.dynamic();
7701
7702 let request = dynamic.requested_broadcast().await.unwrap();
7703 assert_eq!(request.path(), &Path::new("fallback"));
7704 request.accept(&served);
7705
7706 let broadcast = request_fut.await.unwrap();
7707 assert!(broadcast.is_clone(&served.consume()));
7708
7709 let track_fut = broadcast.track("video").unwrap().subscribe(None);
7711 let mut producer = served_dynamic.requested_track().await.unwrap().accept(None);
7712 let mut track = track_fut.await.unwrap();
7713 producer.append_group().unwrap();
7714 track.assert_group();
7715
7716 announced.assert_next_wait();
7718 }
7719
7720 #[tokio::test(start_paused = true)]
7722 async fn dynamic_request_coalesces() {
7723 let origin = Origin::random().produce();
7724 let mut dynamic = origin.dynamic();
7725 let consumer = origin.consume();
7726
7727 let f1 = consumer.request_broadcast("dup");
7729 let f2 = consumer.request_broadcast("dup");
7730
7731 let request = dynamic.requested_broadcast().await.unwrap();
7733 assert_eq!(request.path(), &Path::new("dup"));
7734 assert!(
7735 dynamic.requested_broadcast().now_or_never().is_none(),
7736 "a coalesced request must not be served twice"
7737 );
7738
7739 let served = broadcast::Info::new().produce();
7741 request.accept(&served);
7742 assert!(f1.await.unwrap().is_clone(&served.consume()));
7743 assert!(f2.await.unwrap().is_clone(&served.consume()));
7744 }
7745
7746 #[tokio::test(start_paused = true)]
7749 async fn dynamic_request_dedups_served() {
7750 let origin = Origin::random().produce();
7751 let mut dynamic = origin.dynamic();
7752 let consumer = origin.consume();
7753
7754 let request_fut = consumer.request_broadcast("fallback");
7755 let request = dynamic.requested_broadcast().await.unwrap();
7756 let served = broadcast::Info::new().produce();
7757 request.accept(&served);
7758 let first = request_fut.await.unwrap();
7759 assert!(first.is_clone(&served.consume()));
7760
7761 let second = consumer.request_broadcast("fallback").await.unwrap();
7763 assert!(second.is_clone(&served.consume()));
7764
7765 assert!(
7767 dynamic.requested_broadcast().now_or_never().is_none(),
7768 "a still-live served broadcast must not be re-requested from the handler"
7769 );
7770 }
7771
7772 #[tokio::test(start_paused = true)]
7774 async fn dynamic_request_reserves_after_close() {
7775 let origin = Origin::random().produce();
7776 let mut dynamic = origin.dynamic();
7777 let consumer = origin.consume();
7778
7779 let request_fut = consumer.request_broadcast("fallback");
7780 let request = dynamic.requested_broadcast().await.unwrap();
7781 let served = broadcast::Info::new().produce();
7782 request.accept(&served);
7783 request_fut.await.unwrap();
7784
7785 drop(served);
7787
7788 let request_fut = consumer.request_broadcast("fallback");
7790 let request = dynamic.requested_broadcast().await.unwrap();
7791 assert_eq!(request.path(), &Path::new("fallback"));
7792 let served = broadcast::Info::new().produce();
7793 request.accept(&served);
7794 assert!(request_fut.await.unwrap().is_clone(&served.consume()));
7795 }
7796
7797 #[tokio::test(start_paused = true)]
7800 async fn dynamic_request_served_cache_bounded() {
7801 let origin = Origin::random().produce();
7802 let mut dynamic = origin.dynamic();
7803 let consumer = origin.consume();
7804
7805 for i in 0..100 {
7806 let path = format!("one-shot/{i}");
7807 let request_fut = consumer.request_broadcast(&path);
7808 let request = dynamic.requested_broadcast().await.unwrap();
7809 let served = broadcast::Info::new().produce();
7810 request.accept(&served);
7811 request_fut.await.unwrap();
7812 drop(served);
7814 }
7815
7816 assert!(
7819 origin.dynamic.read().served.len() <= 4,
7820 "stale served entries must be reclaimed, not accumulate per distinct path: {}",
7821 origin.dynamic.read().served.len()
7822 );
7823 }
7824
7825 #[tokio::test(start_paused = true)]
7828 async fn dynamic_request_coalesces_after_handoff() {
7829 let origin = Origin::random().produce();
7830 let mut dynamic = origin.dynamic();
7831 let consumer = origin.consume();
7832
7833 let f1 = consumer.request_broadcast("fallback");
7834 let request = dynamic.requested_broadcast().await.unwrap();
7836
7837 let f2 = consumer.request_broadcast("fallback");
7839 assert!(
7840 dynamic.requested_broadcast().now_or_never().is_none(),
7841 "a repeat request during hand-off must coalesce, not re-queue"
7842 );
7843
7844 let served = broadcast::Info::new().produce();
7846 request.accept(&served);
7847 assert!(f1.await.unwrap().is_clone(&served.consume()));
7848 assert!(f2.await.unwrap().is_clone(&served.consume()));
7849 }
7850
7851 #[tokio::test(start_paused = true)]
7853 async fn dynamic_request_dropped_after_handoff() {
7854 let origin = Origin::random().produce();
7855 let mut dynamic = origin.dynamic();
7856 let consumer = origin.consume();
7857
7858 let f1 = consumer.request_broadcast("fallback");
7859 let request = dynamic.requested_broadcast().await.unwrap();
7860 let f2 = consumer.request_broadcast("fallback");
7861
7862 drop(request);
7864 assert!(matches!(f1.await, Err(Error::Unroutable)));
7865 assert!(matches!(f2.await, Err(Error::Unroutable)));
7866 }
7867
7868 #[tokio::test(start_paused = true)]
7870 async fn dynamic_request_rejected() {
7871 let origin = Origin::random().produce();
7872 let mut dynamic = origin.dynamic();
7873 let consumer = origin.consume();
7874
7875 let request_fut = consumer.request_broadcast("fallback");
7876
7877 let request = dynamic.requested_broadcast().await.unwrap();
7878 request.reject(Error::Cancel);
7879
7880 assert!(matches!(request_fut.await, Err(Error::Cancel)));
7881 }
7882
7883 #[tokio::test(start_paused = true)]
7887 async fn dynamic_request_rerequest_after_reject() {
7888 let origin = Origin::random().produce();
7889 let mut dynamic = origin.dynamic();
7890 let consumer = origin.consume();
7891
7892 let f1 = consumer.request_broadcast("fallback");
7893 dynamic.requested_broadcast().await.unwrap().reject(Error::Unroutable);
7894 assert!(matches!(f1.await, Err(Error::Unroutable)));
7895
7896 let served = broadcast::Info::new().produce();
7897 let f2 = consumer.request_broadcast("fallback");
7899 let request = dynamic.requested_broadcast().await.unwrap();
7900 assert_eq!(request.path(), &Path::new("fallback"));
7901 request.accept(&served);
7902 assert!(f2.await.unwrap().is_clone(&served.consume()));
7903 }
7904
7905 #[tokio::test(start_paused = true)]
7908 async fn dynamic_request_handler_dropped() {
7909 let origin = Origin::random().produce();
7910 let dynamic = origin.dynamic();
7911 let consumer = origin.consume();
7912
7913 let request_fut = consumer.request_broadcast("fallback");
7914 drop(dynamic);
7915 assert!(matches!(request_fut.await, Err(Error::Unroutable)));
7916
7917 assert!(matches!(
7919 consumer.request_broadcast("again").await,
7920 Err(Error::Unroutable)
7921 ));
7922 }
7923
7924 #[tokio::test(start_paused = true)]
7928 async fn dynamic_request_accept_after_handler_dropped() {
7929 let origin = Origin::random().produce();
7930 let mut dynamic = origin.dynamic();
7931 let consumer = origin.consume();
7932
7933 let request_fut = consumer.request_broadcast("fallback");
7934
7935 let request = dynamic.requested_broadcast().await.unwrap();
7937 drop(dynamic);
7938
7939 let served = broadcast::Info::new().produce();
7940 request.accept(&served);
7942 assert!(request_fut.await.unwrap().is_clone(&served.consume()));
7943 }
7944
7945 #[tokio::test(start_paused = true)]
7947 async fn dynamic_request_prefers_announced() {
7948 let origin = Origin::random().produce();
7949 let mut dynamic = origin.dynamic();
7950 let consumer = origin.consume();
7951
7952 let _broadcast = origin.create_broadcast("live", announce()).unwrap();
7953 settle().await;
7954
7955 let got = consumer.request_broadcast("live").await.unwrap();
7956 assert!(
7957 got.is_clone(&consumer.get_broadcast("live").unwrap()),
7958 "should return the published broadcast"
7959 );
7960 assert!(
7961 dynamic.requested_broadcast().now_or_never().is_none(),
7962 "a published path must not queue a fallback request"
7963 );
7964 }
7965
7966 #[tokio::test(start_paused = true)]
7968 async fn dynamic_clone_keeps_alive() {
7969 let origin = Origin::random().produce();
7970 let dynamic = origin.dynamic();
7971 let consumer = origin.consume();
7972
7973 drop(dynamic.clone());
7974
7975 let request_fut = consumer.request_broadcast("fallback");
7978 assert!(
7979 request_fut.now_or_never().is_none(),
7980 "request should stay pending until served"
7981 );
7982 }
7983
7984 fn wedge_watchdog<F>(name: &str, secs: u64, scenario: F)
7990 where
7991 F: std::future::Future<Output = ()> + Send + 'static,
7992 {
7993 let (done_tx, done_rx) = std::sync::mpsc::channel::<()>();
7994 let handle = std::thread::spawn(move || {
7995 let rt = ::tokio::runtime::Builder::new_current_thread()
7996 .enable_time()
7997 .build()
7998 .unwrap();
7999 rt.block_on(scenario);
8000 let _ = done_tx.send(());
8001 });
8002 match done_rx.recv_timeout(std::time::Duration::from_secs(secs)) {
8003 Ok(()) => {
8004 let _ = handle.join();
8005 }
8006 Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => {
8007 let err = handle.join().unwrap_err();
8009 std::panic::resume_unwind(err);
8010 }
8011 Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
8012 panic!("{name}: scenario wedged; a task is spinning inside a single poll")
8013 }
8014 }
8015 }
8016
8017 #[test]
8030 fn test_active_corpse_does_not_livelock_takeover() {
8031 wedge_watchdog("active-corpse", 20, async {
8032 let origin = Origin::random().produce();
8033 let consumer = origin.consume();
8034 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
8035
8036 let source_a = origin
8039 .create_broadcast("test", announce().with_hops(hops.clone()))
8040 .unwrap();
8041 let mut dynamic_a = source_a.dynamic();
8042 settle().await;
8043 settle().await;
8044
8045 let broadcast = consumer.request_broadcast("test").await.unwrap();
8046 let subscribing = broadcast.track("video").unwrap().subscribe(None);
8047 let mut producer_a = accept_track(&mut dynamic_a, "video").await;
8048 settle().await;
8049 let mut sub = subscribing.await.unwrap();
8050 producer_a.append_group().unwrap();
8051 sub.assert_group();
8052
8053 let source_b = origin
8057 .create_broadcast("test", announce().with_hops(hops.clone()))
8058 .unwrap();
8059 let dynamic_b = source_b.dynamic();
8060 settle().await;
8061
8062 drop(dynamic_b);
8070 source_b.abort(Error::Dropped).unwrap();
8071 settle().await;
8072
8073 producer_a.append_group().unwrap();
8076 sub.assert_group();
8077 sub.assert_not_closed();
8078 });
8079 }
8080
8081 #[test]
8089 fn test_route_churn_never_wedges() {
8090 for seed in 1..=8u64 {
8091 wedge_watchdog(&format!("churn seed {seed}"), 30, churn_scenario(seed));
8092 }
8093 }
8094
8095 async fn churn_scenario(seed: u64) {
8096 let mut rng = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
8098 let mut next = move || {
8099 rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
8100 rng >> 33
8101 };
8102
8103 let origin = Origin::random().produce();
8104 let consumer = origin.consume();
8105 let names: Vec<Arc<str>> = (0..8).map(|i| Arc::from(format!("t{i}"))).collect();
8106
8107 let mut subs: Vec<track::Subscriber> = Vec::new();
8108 let mut pending_subs: Vec<kio::Pending<track::Subscribing>> = Vec::new();
8109
8110 struct Source {
8111 producer: Option<broadcast::Producer>,
8112 server: ::tokio::task::JoinHandle<()>,
8113 }
8114 let mut sources: Vec<Source> = Vec::new();
8115
8116 for step in 0..400u64 {
8117 match next() % 10 {
8118 0 | 1 => {
8121 if sources.len() >= 3 {
8122 continue;
8123 }
8124 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
8125 let route = announce().with_hops(hops).with_cost(next() % 4);
8126 let Ok(source) = origin.create_broadcast("test", route) else {
8127 continue;
8128 };
8129 let mut dynamic = source.dynamic();
8130 let behavior = next();
8131 let server = ::tokio::spawn(async move {
8132 let mut round = 0u64;
8133 let mut kept: Vec<track::Producer> = Vec::new();
8134 while let Ok(request) = dynamic.requested_track().await {
8135 round += 1;
8136 match (behavior >> (round % 16)) % 4 {
8137 0 => drop(request), 1 => {
8139 let mut producer = request.accept(None);
8140 let _ = producer.create_group(group::Info { sequence: round });
8141 let _ = producer.abort(Error::Dropped);
8142 }
8143 2 => {
8144 let mut producer = request.accept(None);
8145 let _ = producer.create_group(group::Info { sequence: round });
8146 kept.push(producer);
8147 }
8148 _ => {
8149 let mut producer = request.accept(None);
8150 let _ = producer.finish();
8151 }
8152 }
8153 }
8154 });
8155 sources.push(Source {
8156 producer: Some(source),
8157 server,
8158 });
8159 }
8160 2 | 3 => {
8162 if sources.is_empty() {
8163 continue;
8164 }
8165 let i = (next() as usize) % sources.len();
8166 let mut source = sources.swap_remove(i);
8167 if next() % 2 == 0
8168 && let Some(producer) = source.producer.take()
8169 {
8170 let _ = producer.abort(Error::Dropped);
8171 }
8172 source.server.abort();
8173 }
8174 4..=6 => {
8176 if subs.len() + pending_subs.len() >= 24 {
8177 continue;
8178 }
8179 let Some(broadcast) = consumer.get_broadcast("test") else {
8180 continue;
8181 };
8182 let name = &names[(next() as usize) % names.len()];
8183 if let Ok(track) = broadcast.track(name.as_ref()) {
8184 pending_subs.push(track.subscribe(None));
8185 }
8186 }
8187 7 => {
8189 if subs.is_empty() {
8190 continue;
8191 }
8192 let i = (next() as usize) % subs.len();
8193 subs.swap_remove(i);
8194 }
8195 _ => {
8197 for sub in pending_subs.drain(..) {
8198 match ::tokio::time::timeout(std::time::Duration::from_millis(5), sub).await {
8199 Ok(Ok(sub)) => subs.push(sub),
8200 Ok(Err(_)) => {}
8201 Err(_) => {}
8203 }
8204 }
8205 for sub in subs.iter_mut() {
8206 while let Some(Ok(Some(_))) = sub.recv_group().now_or_never() {}
8207 }
8208 }
8209 }
8210 if step % 16 == 0 {
8211 settle().await;
8212 }
8213 for _ in 0..(next() % 3) {
8215 ::tokio::task::yield_now().await;
8216 }
8217 }
8218
8219 for source in sources {
8220 source.server.abort();
8221 }
8222 settle().await;
8223 }
8224}