1use crate::{broadcast, cache, stats, track};
2use kio::Pollable;
3use std::{
4 collections::{BTreeMap, HashMap},
5 fmt,
6 sync::Arc,
7 sync::atomic::{AtomicU64, Ordering},
8 task::{Poll, ready},
9};
10
11use rand::RngExt;
12use web_async::Lock;
13
14use super::{Requests, WeakCache};
15use crate::{
16 AsPath, Error, Path, PathOwned, PathPrefixes,
17 coding::{BoundsExceeded, Decode, DecodeError, Encode, EncodeError},
18};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub struct Origin {
27 id: u64,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33#[non_exhaustive]
34pub struct InvalidOrigin;
35
36impl fmt::Display for InvalidOrigin {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(f, "local origin id must be non-zero and below 2^62")
39 }
40}
41
42impl std::error::Error for InvalidOrigin {}
43
44impl Origin {
45 pub(crate) const UNKNOWN: Self = Self { id: 0 };
48
49 pub fn new(id: u64) -> Result<Self, InvalidOrigin> {
55 if id == 0 || id >= 1u64 << 62 {
56 return Err(InvalidOrigin);
57 }
58 Ok(Self { id })
59 }
60
61 pub fn random() -> Self {
70 let mut rng = rand::rng();
71 let id = rng.random_range(1..(1u64 << 53));
72 Self { id }
73 }
74
75 pub fn id(self) -> u64 {
77 self.id
78 }
79
80 pub fn produce(self) -> Producer {
83 Info::new(self).produce()
84 }
85}
86
87#[derive(Clone, Debug)]
97#[non_exhaustive]
98pub struct Info {
99 pub id: Origin,
102
103 pub pool: cache::Pool,
109}
110
111impl Default for Info {
112 fn default() -> Self {
115 Self {
116 id: Origin::UNKNOWN,
117 pool: cache::Pool::default(),
118 }
119 }
120}
121
122impl Info {
123 pub fn new(id: Origin) -> Self {
125 Self {
126 id,
127 pool: cache::Pool::default(),
128 }
129 }
130
131 pub fn with_pool(mut self, pool: cache::Pool) -> Self {
133 self.pool = pool;
134 self
135 }
136
137 pub fn produce(self) -> Producer {
139 Producer::new(self)
140 }
141}
142
143impl TryFrom<u64> for Origin {
144 type Error = InvalidOrigin;
145
146 fn try_from(id: u64) -> Result<Self, Self::Error> {
147 Self::new(id)
148 }
149}
150
151impl fmt::Display for Origin {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 self.id.fmt(f)
154 }
155}
156
157impl<V: Copy> Encode<V> for Origin
158where
159 u64: Encode<V>,
160{
161 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
162 self.id.encode(w, version)
163 }
164}
165
166impl<V: Copy> Decode<V> for Origin
167where
168 u64: Decode<V>,
169{
170 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
171 let id = u64::decode(r, version)?;
172 if id >= 1u64 << 62 {
173 return Err(DecodeError::InvalidValue);
174 }
175 Ok(Self { id })
176 }
177}
178
179pub(crate) const MAX_HOPS: usize = 32;
185
186#[derive(Debug, Clone, Default, PartialEq, Eq)]
191pub struct OriginList(Vec<Origin>);
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195#[non_exhaustive]
196pub struct TooManyOrigins;
197
198impl fmt::Display for TooManyOrigins {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 write!(f, "too many origins (max {MAX_HOPS})")
201 }
202}
203
204impl std::error::Error for TooManyOrigins {}
205
206impl From<TooManyOrigins> for DecodeError {
207 fn from(_: TooManyOrigins) -> Self {
208 DecodeError::BoundsExceeded
209 }
210}
211
212impl OriginList {
213 pub fn new() -> Self {
215 Self(Vec::new())
216 }
217
218 pub fn push(&mut self, origin: Origin) -> Result<(), TooManyOrigins> {
220 if self.0.len() >= MAX_HOPS {
221 return Err(TooManyOrigins);
222 }
223 self.0.push(origin);
224 Ok(())
225 }
226
227 pub fn replace_first(&mut self, target: Origin, replacement: Origin) -> bool {
230 for entry in &mut self.0 {
231 if *entry == target {
232 *entry = replacement;
233 return true;
234 }
235 }
236 false
237 }
238
239 pub fn contains(&self, origin: &Origin) -> bool {
241 self.0.contains(origin)
242 }
243
244 pub fn len(&self) -> usize {
246 self.0.len()
247 }
248
249 pub fn is_empty(&self) -> bool {
251 self.0.is_empty()
252 }
253
254 pub fn iter(&self) -> std::slice::Iter<'_, Origin> {
256 self.0.iter()
257 }
258
259 pub fn as_slice(&self) -> &[Origin] {
261 &self.0
262 }
263}
264
265impl TryFrom<Vec<Origin>> for OriginList {
266 type Error = TooManyOrigins;
267
268 fn try_from(v: Vec<Origin>) -> Result<Self, Self::Error> {
269 if v.len() > MAX_HOPS {
270 return Err(TooManyOrigins);
271 }
272 Ok(Self(v))
273 }
274}
275
276impl<'a> IntoIterator for &'a OriginList {
277 type Item = &'a Origin;
278 type IntoIter = std::slice::Iter<'a, Origin>;
279
280 fn into_iter(self) -> Self::IntoIter {
281 self.iter()
282 }
283}
284
285impl<V: Copy> Encode<V> for OriginList
286where
287 u64: Encode<V>,
288 Origin: Encode<V>,
289{
290 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
291 (self.0.len() as u64).encode(w, version)?;
292 for origin in &self.0 {
293 origin.encode(w, version)?;
294 }
295 Ok(())
296 }
297}
298
299impl<V: Copy> Decode<V> for OriginList
300where
301 u64: Decode<V>,
302 Origin: Decode<V>,
303{
304 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
305 let count = u64::decode(r, version)? as usize;
306 if count > MAX_HOPS {
307 return Err(DecodeError::BoundsExceeded);
308 }
309 let mut list = Vec::with_capacity(count);
310 for _ in 0..count {
311 list.push(Origin::decode(r, version)?);
312 }
313 Ok(Self(list))
314 }
315}
316
317static NEXT_CONSUMER_ID: AtomicU64 = AtomicU64::new(0);
318
319#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
320struct ConsumerId(u64);
321
322impl ConsumerId {
323 fn new() -> Self {
324 Self(NEXT_CONSUMER_ID.fetch_add(1, Ordering::Relaxed))
325 }
326}
327
328struct OriginBroadcast {
334 path: PathOwned,
335 broadcast: broadcast::Producer,
337 state: kio::Producer<FrontState>,
340 announced: bool,
341}
342
343fn route_key(name: &Path, hops: &OriginList) -> (usize, u64) {
352 (hops.len(), fnv_key(name, hops.iter().copied()))
353}
354
355fn fnv_key(name: &Path, origins: impl IntoIterator<Item = Origin>) -> u64 {
368 const SEED: u64 = 0x420C0DECB00B; const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
370
371 let mut hash = SEED;
372 for &byte in name.as_str().as_bytes() {
373 hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
374 }
375 for origin in origins {
376 for &byte in &origin.id().to_le_bytes() {
377 hash = (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME);
378 }
379 }
380
381 hash
382}
383
384fn route_order(name: &Path, route: &broadcast::Route) -> (bool, u64, usize, u64) {
393 let (len, hash) = route_key(name, &route.hops);
394 (!route.announce, route.cost, len, hash)
395}
396
397enum PendingUpdate {
406 Announce(broadcast::Consumer),
407 Unannounce,
408 UnannounceAnnounce(broadcast::Consumer),
409}
410
411#[derive(Default)]
416struct OriginConsumerState {
417 pending: BTreeMap<PathOwned, PendingUpdate>,
418}
419
420impl OriginConsumerState {
421 fn apply_announce(&mut self, path: PathOwned, broadcast: broadcast::Consumer) {
422 let new = match self.pending.remove(&path) {
423 None | Some(PendingUpdate::Announce(_)) => PendingUpdate::Announce(broadcast),
425 Some(PendingUpdate::Unannounce | PendingUpdate::UnannounceAnnounce(_)) => {
427 PendingUpdate::UnannounceAnnounce(broadcast)
428 }
429 };
430 self.pending.insert(path, new);
431 }
432
433 fn apply_unannounce(&mut self, path: PathOwned) {
434 match self.pending.remove(&path) {
435 Some(PendingUpdate::Announce(_)) => {}
437 None | Some(PendingUpdate::Unannounce) => {
438 self.pending.insert(path, PendingUpdate::Unannounce);
439 }
440 Some(PendingUpdate::UnannounceAnnounce(_)) => {
443 self.pending.insert(path, PendingUpdate::Unannounce);
444 }
445 }
446 }
447
448 fn take(&mut self) -> Option<OriginAnnounce> {
450 let path = self.pending.keys().next()?.clone();
451 let broadcast = match self.pending.remove(&path).unwrap() {
452 PendingUpdate::Announce(broadcast) => Some(broadcast),
453 PendingUpdate::Unannounce => None,
454 PendingUpdate::UnannounceAnnounce(broadcast) => {
455 self.pending.insert(path.clone(), PendingUpdate::Announce(broadcast));
458 None
459 }
460 };
461 Some(OriginAnnounce { path, broadcast })
462 }
463}
464
465#[derive(Clone)]
466struct AnnounceConsumerNotify {
467 root: PathOwned,
468 state: kio::Producer<OriginConsumerState>,
469}
470
471impl AnnounceConsumerNotify {
472 fn announce(&self, path: impl AsPath, broadcast: broadcast::Consumer) {
473 let path = path.as_path().strip_prefix(&self.root).unwrap().to_owned();
474 self.state
475 .write()
476 .ok()
477 .expect("consumer closed")
478 .apply_announce(path, broadcast);
479 }
480
481 fn unannounce(&self, path: impl AsPath) {
482 let path = path.as_path().strip_prefix(&self.root).unwrap().to_owned();
483 self.state.write().ok().expect("consumer closed").apply_unannounce(path);
484 }
485}
486
487struct NotifyNode {
488 parent: Option<Lock<NotifyNode>>,
489
490 consumers: HashMap<ConsumerId, AnnounceConsumerNotify>,
493}
494
495impl NotifyNode {
496 fn new(parent: Option<Lock<NotifyNode>>) -> Self {
497 Self {
498 parent,
499 consumers: HashMap::new(),
500 }
501 }
502
503 fn announce(&mut self, path: impl AsPath, broadcast: &broadcast::Consumer) {
504 for consumer in self.consumers.values() {
505 consumer.announce(path.as_path(), broadcast.clone());
506 }
507
508 if let Some(parent) = &self.parent {
509 parent.lock().announce(path, broadcast);
510 }
511 }
512
513 fn unannounce(&mut self, path: impl AsPath) {
514 for consumer in self.consumers.values() {
515 consumer.unannounce(path.as_path());
516 }
517
518 if let Some(parent) = &self.parent {
519 parent.lock().unannounce(path);
520 }
521 }
522}
523
524struct OriginNode {
525 broadcast: Option<OriginBroadcast>,
528
529 nested: HashMap<String, Lock<OriginNode>>,
531
532 notify: Lock<NotifyNode>,
534}
535
536impl OriginNode {
537 fn new(parent: Option<Lock<NotifyNode>>) -> Self {
538 Self {
539 broadcast: None,
540 nested: HashMap::new(),
541 notify: Lock::new(NotifyNode::new(parent)),
542 }
543 }
544
545 fn leaf(&mut self, path: &Path) -> Lock<OriginNode> {
546 let (dir, rest) = path.next_part().expect("leaf called with empty path");
547
548 let next = self.entry(dir);
549 if rest.is_empty() { next } else { next.lock().leaf(&rest) }
550 }
551
552 fn entry(&mut self, dir: &str) -> Lock<OriginNode> {
553 match self.nested.get(dir) {
554 Some(next) => next.clone(),
555 None => {
556 let next = Lock::new(OriginNode::new(Some(self.notify.clone())));
557 self.nested.insert(dir.to_string(), next.clone());
558 next
559 }
560 }
561 }
562
563 fn set_announced(&mut self, expect: &kio::Producer<FrontState>, announce: bool) {
567 let Some(existing) = &mut self.broadcast else { return };
568 if !existing.state.same_channel(expect) || existing.announced == announce {
569 return;
570 }
571 existing.announced = announce;
572 let path = existing.path.clone();
573 let consumer = existing.broadcast.consume();
574 let mut notify = self.notify.lock();
575 if announce {
576 notify.announce(&path, &consumer);
577 } else {
578 notify.unannounce(&path);
579 }
580 }
581
582 fn consume(&mut self, id: ConsumerId, mut notify: AnnounceConsumerNotify) {
583 self.consume_initial(&mut notify);
584 self.notify.lock().consumers.insert(id, notify);
585 }
586
587 fn consume_initial(&mut self, notify: &mut AnnounceConsumerNotify) {
588 if let Some(broadcast) = &self.broadcast
591 && broadcast.announced
592 {
593 notify.announce(&broadcast.path, broadcast.broadcast.consume());
594 }
595
596 for nested in self.nested.values() {
598 nested.lock().consume_initial(notify);
599 }
600 }
601
602 fn consume_broadcast(&self, rest: impl AsPath) -> Option<broadcast::Consumer> {
603 let rest = rest.as_path();
604
605 if let Some((dir, rest)) = rest.next_part() {
606 let node = self.nested.get(dir)?.lock();
607 node.consume_broadcast(&rest)
608 } else {
609 self.broadcast.as_ref().map(|b| b.broadcast.consume())
610 }
611 }
612
613 fn unconsume(&mut self, id: ConsumerId) {
614 self.notify.lock().consumers.remove(&id).expect("consumer not found");
615 if self.is_empty() {
616 }
619 }
620
621 fn remove(&mut self, expect: &kio::Producer<FrontState>, relative: impl AsPath) {
625 let relative = relative.as_path();
626
627 if let Some((dir, relative)) = relative.next_part() {
628 let Some(nested) = self.nested.get(dir) else { return };
629 let nested = nested.clone();
630 let mut locked = nested.lock();
631 locked.remove(expect, &relative);
632
633 if locked.is_empty() {
634 drop(locked);
635 self.nested.remove(dir);
636 }
637 } else if let Some(existing) = &self.broadcast
638 && existing.state.same_channel(expect)
639 {
640 let existing = self.broadcast.take().expect("checked above");
641 if existing.announced {
642 self.notify.lock().unannounce(&existing.path);
643 }
644 }
645 }
646
647 fn is_empty(&self) -> bool {
648 self.broadcast.is_none() && self.nested.is_empty() && self.notify.lock().consumers.is_empty()
649 }
650}
651
652#[derive(Clone)]
653struct OriginNodes {
654 nodes: Vec<(PathOwned, Lock<OriginNode>)>,
655}
656
657impl OriginNodes {
658 pub fn select(&self, prefixes: &PathPrefixes) -> Option<Self> {
661 let mut roots = Vec::new();
662
663 for (root, state) in &self.nodes {
664 for prefix in prefixes {
665 if root.has_prefix(prefix) {
666 roots.push((root.to_owned(), state.clone()));
668 continue;
669 }
670
671 if let Some(suffix) = prefix.strip_prefix(root) {
672 let nested = state.lock().leaf(&suffix);
674 roots.push((prefix.to_owned(), nested));
675 }
676 }
677 }
678
679 if roots.is_empty() {
680 None
681 } else {
682 Some(Self { nodes: roots })
683 }
684 }
685
686 pub fn root(&self, new_root: impl AsPath) -> Option<Self> {
687 let new_root = new_root.as_path();
688 let mut roots = Vec::new();
689
690 if new_root.is_empty() {
691 return Some(self.clone());
692 }
693
694 for (root, state) in &self.nodes {
695 if let Some(suffix) = root.strip_prefix(&new_root) {
696 roots.push((suffix.to_owned(), state.clone()));
698 } else if let Some(suffix) = new_root.strip_prefix(root) {
699 let nested = state.lock().leaf(&suffix);
702 roots.push(("".into(), nested));
703 }
704 }
705
706 if roots.is_empty() {
707 None
708 } else {
709 Some(Self { nodes: roots })
710 }
711 }
712
713 pub fn get(&self, path: impl AsPath) -> Option<(Lock<OriginNode>, PathOwned)> {
715 let path = path.as_path();
716
717 for (root, state) in &self.nodes {
718 if let Some(suffix) = path.strip_prefix(root) {
719 return Some((state.clone(), suffix.to_owned()));
720 }
721 }
722
723 None
724 }
725}
726
727impl Default for OriginNodes {
728 fn default() -> Self {
729 Self {
730 nodes: vec![("".into(), Lock::new(OriginNode::new(None)))],
731 }
732 }
733}
734
735#[derive(Clone)]
737pub struct OriginAnnounce {
738 pub path: PathOwned,
740 pub broadcast: Option<broadcast::Consumer>,
746}
747
748#[derive(Clone)]
750pub struct Producer {
751 info: Origin,
755
756 nodes: OriginNodes,
759
760 root: PathOwned,
762
763 dynamic: kio::Shared<OriginDynamicState>,
767
768 pool: cache::Pool,
771
772 stats: stats::Session,
776}
777
778impl std::ops::Deref for Producer {
779 type Target = Origin;
780
781 fn deref(&self) -> &Self::Target {
782 &self.info
783 }
784}
785
786impl Producer {
787 pub fn new(info: Info) -> Self {
791 Self {
792 info: info.id,
793 nodes: OriginNodes::default(),
794 root: PathOwned::default(),
795 dynamic: kio::Shared::default(),
796 pool: info.pool,
797 stats: stats::Session::default(),
798 }
799 }
800
801 pub fn with_stats(mut self, session: stats::Session) -> Self {
805 self.stats = session;
806 self
807 }
808
809 pub fn info(&self) -> Info {
812 Info {
813 id: self.info,
814 pool: self.pool.clone(),
815 }
816 }
817
818 pub(crate) fn empty(info: Origin) -> Self {
823 Self {
824 info,
825 nodes: OriginNodes { nodes: Vec::new() },
826 root: PathOwned::default(),
827 dynamic: kio::Shared::default(),
828 pool: cache::Pool::default(),
829 stats: stats::Session::default(),
830 }
831 }
832
833 pub fn create_broadcast(&self, path: impl AsPath, route: broadcast::Route) -> Result<broadcast::Producer, Error> {
870 let path = path.as_path();
871
872 debug_assert!(
873 !route.hops.contains(&self.info),
874 "create_broadcast called with a looping hop chain",
875 );
876
877 let (node, rest) = self.nodes.get(&path).ok_or(Error::Unauthorized)?;
878 let full = self.root.join(&path).to_owned();
879
880 if full.parts().count() > Path::MAX_PARTS {
884 return Err(BoundsExceeded.into());
885 }
886
887 let ingress = self.stats.ingress(&full);
891
892 let mut source = broadcast::Info { origin: self.info() }
893 .produce()
894 .with_stats(ingress.clone());
895 source.set_route(route).expect("fresh producer");
896
897 web_async::spawn(run_source(self.info(), node, full, rest, source.consume(), ingress));
898
899 Ok(source)
900 }
901
902 pub fn scope(&self, prefixes: &[Path]) -> Option<Producer> {
908 let prefixes = PathPrefixes::new(prefixes);
909 Some(Producer {
910 info: self.info,
911 nodes: self.nodes.select(&prefixes)?,
912 root: self.root.clone(),
913 dynamic: self.dynamic.clone(),
914 pool: self.pool.clone(),
915 stats: self.stats.clone(),
916 })
917 }
918
919 pub fn dynamic(&self) -> Dynamic {
928 Dynamic::new(self.info, self.root.clone(), self.dynamic.clone())
929 }
930
931 pub fn consume(&self) -> Consumer {
936 Consumer::new(
939 self.info,
940 self.root.clone(),
941 self.nodes.clone(),
942 self.dynamic.clone(),
943 stats::Session::default(),
944 )
945 }
946
947 pub fn announces(&self) -> AnnounceProducer {
953 AnnounceProducer::new(self.root.clone(), self.nodes.clone())
954 }
955
956 pub fn with_root(&self, prefix: impl AsPath) -> Option<Self> {
961 let prefix = prefix.as_path();
962
963 Some(Self {
964 info: self.info,
965 root: self.root.join(&prefix).to_owned(),
966 nodes: self.nodes.root(&prefix)?,
967 dynamic: self.dynamic.clone(),
968 pool: self.pool.clone(),
969 stats: self.stats.clone(),
970 })
971 }
972
973 pub fn root(&self) -> &Path<'_> {
975 &self.root
976 }
977
978 pub fn allowed(&self) -> impl Iterator<Item = &Path<'_>> {
981 self.nodes.nodes.iter().map(|(root, _)| root)
982 }
983
984 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
986 self.root.join(path)
987 }
988}
989
990const MAX_TRACK_RETRIES: u32 = 3;
995
996struct FrontRoute {
998 id: u64,
999 route: broadcast::Route,
1002 source: broadcast::Consumer,
1004}
1005
1006struct FrontState {
1008 path: PathOwned,
1010 self_origin: Origin,
1012 next_route: u64,
1013 routes: Vec<FrontRoute>,
1014 active: Option<u64>,
1016 closed: bool,
1019}
1020
1021impl FrontState {
1022 fn best_route(&self) -> Option<u64> {
1025 self.routes
1026 .iter()
1027 .min_by_key(|r| route_order(&self.path.as_path(), &r.route))
1028 .map(|r| r.id)
1029 }
1030
1031 fn reselect(&mut self, carrying: bool) {
1049 let best = self.best_route();
1050 if carrying
1051 && let (Some(best_id), Some(cur_id)) = (best, self.active)
1052 && best_id != cur_id
1053 && let Some(candidate) = self.routes.iter().find(|r| r.id == best_id)
1054 && let Some(incumbent) = self.routes.iter().find(|r| r.id == cur_id)
1055 && incumbent.route.announce
1056 && candidate.route.cost < incumbent.route.cost
1057 && candidate.route.advertised == 0
1058 && candidate.route.hops.len() >= 2
1059 && !self.handover_allowed(&candidate.route)
1060 {
1061 return;
1063 }
1064 self.active = best;
1065 }
1066
1067 fn handover_allowed(&self, route: &broadcast::Route) -> bool {
1079 let name = self.path.as_path();
1080 match route.hops.iter().last() {
1081 Some(peer) => fnv_key(&name, [*peer]) < fnv_key(&name, [self.self_origin]),
1082 None => true,
1083 }
1084 }
1085
1086 fn active_route(&self) -> Option<broadcast::Route> {
1089 let id = self.active?;
1090 self.routes.iter().find(|r| r.id == id).map(|r| r.route.clone())
1091 }
1092}
1093
1094fn sync_front(state: &kio::Producer<FrontState>, broadcast: &broadcast::Producer, leaf: &Lock<OriginNode>) {
1104 let mut leaf_guard = leaf.lock();
1109 let advert = state.read().active_route();
1110 if let Some(advert) = advert {
1111 let announce = advert.announce;
1112 let _ = broadcast.clone().set_route(advert);
1113 leaf_guard.set_announced(state, announce);
1114 }
1115}
1116
1117fn detach_source(state: &kio::Producer<FrontState>, broadcast: &broadcast::Producer, leaf: &Lock<OriginNode>, id: u64) {
1124 let close = {
1125 let carrying = broadcast.demand().is_used();
1126 let Ok(mut s) = state.write() else { return };
1127 let Some(pos) = s.routes.iter().position(|r| r.id == id) else {
1128 return;
1129 };
1130 s.routes.remove(pos);
1131 s.reselect(carrying);
1132 if s.routes.is_empty() && !s.closed {
1133 s.closed = true;
1136 true
1137 } else {
1138 false
1139 }
1140 };
1141 if close {
1142 broadcast.abort_spliced(Error::Dropped);
1143 }
1144 sync_front(state, broadcast, leaf);
1145}
1146
1147async fn run_source(
1151 origin: Info,
1152 node: Lock<OriginNode>,
1153 full: PathOwned,
1154 rest: PathOwned,
1155 mut source: broadcast::Consumer,
1156 ingress: stats::Scope,
1157) {
1158 let Ok(route) = source.route_changed().await else {
1162 return;
1164 };
1165
1166 let mut announce = route.announce.then(|| ingress.announce());
1171
1172 let leaf = if rest.is_empty() {
1173 node.clone()
1174 } else {
1175 node.lock().leaf(&rest)
1176 };
1177
1178 let (state, broadcast, id) = attach_source(&origin, &node, &leaf, &full, &rest, &source, route);
1179
1180 loop {
1181 match source.route_changed().await {
1182 Ok(route) => {
1183 let announced = route.announce;
1184 {
1185 let carrying = broadcast.demand().is_used();
1186 let Ok(mut s) = state.write() else { return };
1187 let Some(entry) = s.routes.iter_mut().find(|r| r.id == id) else {
1188 return;
1189 };
1190 if entry.route == route {
1191 continue;
1192 }
1193 entry.route = route;
1194 s.reselect(carrying);
1195 }
1196 match (announced, announce.is_some()) {
1198 (true, false) => announce = Some(ingress.announce()),
1199 (false, true) => announce = None,
1200 _ => {}
1201 }
1202 sync_front(&state, &broadcast, &leaf);
1203 }
1204 Err(_) => {
1205 detach_source(&state, &broadcast, &leaf, id);
1206 return;
1207 }
1208 }
1209 }
1210}
1211
1212fn attach_source(
1217 origin: &Info,
1218 node: &Lock<OriginNode>,
1219 leaf: &Lock<OriginNode>,
1220 full: &PathOwned,
1221 rest: &PathOwned,
1222 source: &broadcast::Consumer,
1223 route: broadcast::Route,
1224) -> (kio::Producer<FrontState>, broadcast::Producer, u64) {
1225 let mut leaf_guard = leaf.lock();
1226
1227 if let Some(existing) = &leaf_guard.broadcast {
1230 let mut joined = None;
1231 let carrying = existing.broadcast.demand().is_used();
1232 if let Ok(mut s) = existing.state.write()
1233 && !s.closed
1234 {
1235 let id = s.next_route;
1236 s.next_route += 1;
1237 s.routes.push(FrontRoute {
1238 id,
1239 route: route.clone(),
1240 source: source.clone(),
1241 });
1242 s.reselect(carrying);
1243 joined = Some(id);
1244 }
1245 if let Some(id) = joined {
1246 let state = existing.state.clone();
1247 let broadcast = existing.broadcast.clone();
1248 drop(leaf_guard);
1249 sync_front(&state, &broadcast, leaf);
1250 return (state, broadcast, id);
1251 }
1252 }
1253
1254 let announce = route.announce;
1256 let broadcast = broadcast::Producer::new_spliced(broadcast::Info { origin: origin.clone() });
1257 let _ = broadcast.clone().set_route(route.clone());
1258 let state = kio::Producer::new(FrontState {
1259 path: full.clone(),
1260 self_origin: origin.id,
1261 next_route: 1,
1262 routes: vec![FrontRoute {
1263 id: 0,
1264 route,
1265 source: source.clone(),
1266 }],
1267 active: Some(0),
1268 closed: false,
1269 });
1270
1271 if let Some(stale) = leaf_guard.broadcast.take()
1275 && stale.announced
1276 {
1277 leaf_guard.notify.lock().unannounce(&stale.path);
1278 }
1279 let entry = OriginBroadcast {
1280 path: full.clone(),
1281 broadcast: broadcast.clone(),
1282 state: state.clone(),
1283 announced: announce,
1284 };
1285 if entry.announced {
1286 leaf_guard.notify.lock().announce(full, &broadcast.consume());
1287 }
1288 leaf_guard.broadcast = Some(entry);
1289 drop(leaf_guard);
1290
1291 web_async::spawn(run_front(state.clone(), broadcast.clone(), node.clone(), rest.clone()));
1292
1293 (state, broadcast, 0)
1294}
1295
1296async fn run_front(
1299 state: kio::Producer<FrontState>,
1300 mut broadcast: broadcast::Producer,
1301 node: Lock<OriginNode>,
1302 rest: PathOwned,
1303) {
1304 enum Step {
1305 Serve(Arc<str>, super::resume::Producer),
1306 Closed,
1307 }
1308
1309 loop {
1310 let step = kio::wait(|waiter| {
1311 if let Poll::Ready((name, resume)) = broadcast.poll_spliced_assigned(waiter) {
1312 return Poll::Ready(Step::Serve(name, resume));
1313 }
1314 match state.poll(waiter, |s| if s.closed { Poll::Ready(()) } else { Poll::Pending }) {
1317 Poll::Ready(_) => Poll::Ready(Step::Closed),
1318 Poll::Pending => Poll::Pending,
1319 }
1320 })
1321 .await;
1322
1323 match step {
1324 Step::Serve(name, resume) => {
1325 web_async::spawn(serve_track(state.clone(), name, resume));
1328 }
1329 Step::Closed => break,
1330 }
1331 }
1332
1333 broadcast.abort_spliced(Error::Dropped);
1335
1336 broadcast.finish();
1338
1339 node.lock().remove(&state, &rest);
1342}
1343
1344async fn serve_track(state: kio::Producer<FrontState>, name: Arc<str>, mut resume: super::resume::Producer) {
1352 enum Step {
1353 Closed,
1354 Splice(u64, broadcast::Consumer),
1355 Complete,
1356 Failed,
1357 }
1358
1359 let mut fails = 0u32;
1360 let mut serving: Option<(u64, track::Consumer)> = None;
1362 let mut dead: Option<u64> = None;
1366
1367 loop {
1368 let serving_id = serving.as_ref().map(|(id, _)| *id);
1369 let step = kio::wait(|waiter| {
1370 match state.poll(waiter, |s| {
1374 if s.closed || matches!(s.active, Some(active) if Some(active) != serving_id && Some(active) != dead) {
1375 Poll::Ready(())
1376 } else {
1377 Poll::Pending
1378 }
1379 }) {
1380 Poll::Ready(Ok(guard)) => {
1381 if guard.closed {
1382 return Poll::Ready(Step::Closed);
1383 }
1384 let active = guard.active.expect("predicate guaranteed an active source");
1385 let source = guard
1386 .routes
1387 .iter()
1388 .find(|r| r.id == active)
1389 .expect("active source in table")
1390 .source
1391 .clone();
1392 return Poll::Ready(Step::Splice(active, source));
1393 }
1394 Poll::Ready(Err(_)) => return Poll::Ready(Step::Closed),
1395 Poll::Pending => {}
1396 }
1397
1398 if let Some((_, track)) = &serving
1401 && let Poll::Ready(result) = track.poll_complete(waiter)
1402 {
1403 return Poll::Ready(match result {
1404 Ok(()) => Step::Complete,
1405 Err(_) => Step::Failed,
1406 });
1407 }
1408 Poll::Pending
1409 })
1410 .await;
1411
1412 match step {
1413 Step::Closed => return,
1415 Step::Complete => {
1416 let _ = resume.finish();
1417 return;
1418 }
1419 Step::Failed => {
1420 serving = None;
1423 }
1424 Step::Splice(id, source) => {
1425 let attempt = match source.track(&name) {
1429 Ok(track) => {
1430 let query = track.info().into_inner();
1433 let info = kio::wait(|waiter| {
1434 if let Poll::Ready(result) = query.poll(waiter) {
1435 return Poll::Ready(Some(result));
1436 }
1437 match state.poll(waiter, |s| {
1438 if s.closed || s.active != Some(id) {
1439 Poll::Ready(())
1440 } else {
1441 Poll::Pending
1442 }
1443 }) {
1444 Poll::Ready(_) => Poll::Ready(None),
1445 Poll::Pending => Poll::Pending,
1446 }
1447 })
1448 .await;
1449 match info {
1450 None => continue,
1453 Some(Ok(_)) => match track.poll_complete(&kio::Waiter::noop()) {
1457 Poll::Ready(Err(err)) => Err(err),
1458 _ => Ok(track),
1459 },
1460 Some(Err(err)) => Err(err),
1461 }
1462 }
1463 Err(err) => Err(err),
1464 };
1465
1466 match attempt {
1467 Ok(track) => {
1468 if resume.takeover(&track).is_err() {
1469 return;
1472 }
1473 fails = 0;
1476 dead = None;
1477 serving = Some((id, track));
1478 }
1479 Err(_) if source.is_closing() => {
1483 dead = Some(id);
1484 serving = None;
1485 }
1486 Err(err) => {
1487 fails += 1;
1488 if fails >= MAX_TRACK_RETRIES {
1489 tracing::debug!(name = %name, %err, "aborting unservable track");
1490 let _ = resume.abort(Error::Unroutable);
1491 return;
1492 }
1493 serving = None;
1494 }
1495 }
1496 }
1497 }
1498 }
1499}
1500
1501#[derive(Default)]
1507struct OriginDynamicState {
1508 requests: Requests<PathOwned, kio::Producer<PendingBroadcast>>,
1511
1512 served: WeakCache<PathOwned, broadcast::WeakConsumer>,
1518}
1519
1520#[derive(Default)]
1527struct PendingBroadcast {
1528 resolved: Option<Result<broadcast::Consumer, Error>>,
1529}
1530
1531pub struct Dynamic {
1542 info: Origin,
1543 root: PathOwned,
1544 state: kio::Shared<OriginDynamicState>,
1545}
1546
1547impl Clone for Dynamic {
1548 fn clone(&self) -> Self {
1549 self.state.lock().requests.add_handler();
1553
1554 Self {
1555 info: self.info,
1556 root: self.root.clone(),
1557 state: self.state.clone(),
1558 }
1559 }
1560}
1561
1562impl Dynamic {
1563 fn new(info: Origin, root: PathOwned, state: kio::Shared<OriginDynamicState>) -> Self {
1564 state.lock().requests.add_handler();
1565
1566 Self { info, root, state }
1567 }
1568
1569 pub fn info(&self) -> &Origin {
1571 &self.info
1572 }
1573
1574 pub fn poll_requested_broadcast(&mut self, waiter: &kio::Waiter) -> Poll<Result<Request, Error>> {
1576 let mut state = ready!(self.state.poll(waiter, |state| {
1577 if state.requests.has_queued() {
1578 Poll::Ready(())
1579 } else {
1580 Poll::Pending
1581 }
1582 }));
1583
1584 let path = state.requests.pop().expect("predicate guaranteed a request");
1585 let producer = state.requests.get(&path).expect("popped key must be pending").clone();
1591 Poll::Ready(Ok(Request {
1592 path,
1593 producer,
1594 state: self.state.clone(),
1595 }))
1596 }
1597
1598 pub async fn requested_broadcast(&mut self) -> Result<Request, Error> {
1601 kio::wait(|waiter| self.poll_requested_broadcast(waiter)).await
1602 }
1603
1604 pub fn root(&self) -> &Path<'_> {
1606 &self.root
1607 }
1608}
1609
1610impl Drop for Dynamic {
1611 fn drop(&mut self) {
1612 let mut state = self.state.lock();
1615 if state.requests.remove_handler() {
1616 state.requests.drain_queued();
1620 }
1621 }
1622}
1623
1624pub struct Request {
1631 path: PathOwned,
1633
1634 producer: kio::Producer<PendingBroadcast>,
1637
1638 state: kio::Shared<OriginDynamicState>,
1640}
1641
1642impl Request {
1643 pub fn path(&self) -> &Path<'_> {
1645 &self.path
1646 }
1647
1648 pub fn accept(self, broadcast: impl Consume<broadcast::Consumer>) {
1654 let broadcast = broadcast.consume();
1655
1656 let resolved = {
1662 let mut state = self.state.lock();
1663 let existing = state.served.insert(self.path.clone(), broadcast.weak());
1664 state
1665 .requests
1666 .remove_if(&self.path, |producer| producer.same_channel(&self.producer));
1667 existing.map(|weak| weak.consume()).unwrap_or(broadcast)
1668 };
1669
1670 if let Ok(mut pending) = self.producer.write() {
1671 pending.resolved = Some(Ok(resolved));
1672 }
1673 }
1675
1676 pub fn reject(self, err: Error) {
1678 self.state
1679 .lock()
1680 .requests
1681 .remove_if(&self.path, |producer| producer.same_channel(&self.producer));
1682 if let Ok(mut state) = self.producer.write() {
1683 state.resolved = Some(Err(err));
1684 }
1685 }
1686}
1687
1688impl Drop for Request {
1689 fn drop(&mut self) {
1690 self.state
1698 .lock()
1699 .requests
1700 .remove_if(&self.path, |producer| producer.same_channel(&self.producer));
1701 }
1702}
1703
1704pub struct Requesting {
1711 inner: RequestState,
1712 stats: stats::Scope,
1715}
1716
1717enum RequestState {
1718 Ready(broadcast::Consumer),
1720 Failed(Error),
1723 Pending(kio::Consumer<PendingBroadcast>),
1725}
1726
1727impl Requesting {
1728 fn ready(broadcast: broadcast::Consumer) -> Self {
1729 Self {
1730 inner: RequestState::Ready(broadcast),
1731 stats: stats::Scope::default(),
1732 }
1733 }
1734
1735 fn failed(error: Error) -> Self {
1736 Self {
1737 inner: RequestState::Failed(error),
1738 stats: stats::Scope::default(),
1739 }
1740 }
1741
1742 fn pending(consumer: kio::Consumer<PendingBroadcast>) -> Self {
1743 Self {
1744 inner: RequestState::Pending(consumer),
1745 stats: stats::Scope::default(),
1746 }
1747 }
1748
1749 fn with_stats(mut self, scope: stats::Scope) -> Self {
1750 self.stats = scope;
1751 self
1752 }
1753
1754 pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<broadcast::Consumer, Error>> {
1756 match &self.inner {
1757 RequestState::Ready(broadcast) => Poll::Ready(Ok(broadcast.clone().with_stats(self.stats.clone()))),
1758 RequestState::Failed(error) => Poll::Ready(Err(error.clone())),
1759 RequestState::Pending(consumer) => Poll::Ready(
1760 match ready!(consumer.poll(waiter, |state| match &state.resolved {
1761 Some(result) => Poll::Ready(result.clone()),
1762 None => Poll::Pending,
1763 })) {
1764 Ok(result) => result.map(|broadcast| broadcast.with_stats(self.stats.clone())),
1765 Err(_closed) => Err(Error::Unroutable),
1767 },
1768 ),
1769 }
1770 }
1771}
1772
1773impl kio::Pollable for Requesting {
1774 type Output = Result<broadcast::Consumer, Error>;
1775
1776 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
1777 self.poll_ok(waiter)
1778 }
1779}
1780
1781pub trait Consume<T> {
1789 fn consume(&self) -> T;
1791}
1792
1793impl<T, U: Consume<T>> Consume<T> for &U {
1794 fn consume(&self) -> T {
1795 (**self).consume()
1796 }
1797}
1798
1799impl Consume<Consumer> for Producer {
1800 fn consume(&self) -> Consumer {
1801 Consumer::new(
1805 self.info,
1806 self.root.clone(),
1807 self.nodes.clone(),
1808 self.dynamic.clone(),
1809 stats::Session::default(),
1810 )
1811 }
1812}
1813
1814impl Consume<Consumer> for Consumer {
1815 fn consume(&self) -> Consumer {
1816 self.clone()
1817 }
1818}
1819
1820impl Consume<broadcast::Consumer> for broadcast::Producer {
1821 fn consume(&self) -> broadcast::Consumer {
1822 self.consume()
1824 }
1825}
1826
1827impl Consume<broadcast::Consumer> for broadcast::Consumer {
1828 fn consume(&self) -> broadcast::Consumer {
1829 self.clone()
1830 }
1831}
1832
1833impl Consume<track::Consumer> for track::Producer {
1834 fn consume(&self) -> track::Consumer {
1835 self.consume()
1836 }
1837}
1838
1839impl Consume<track::Consumer> for track::Consumer {
1840 fn consume(&self) -> track::Consumer {
1841 self.clone()
1842 }
1843}
1844
1845#[derive(Clone)]
1851pub struct Consumer {
1852 info: Origin,
1854 nodes: OriginNodes,
1855
1856 root: PathOwned,
1858
1859 dynamic: kio::Shared<OriginDynamicState>,
1862
1863 stats: stats::Session,
1867}
1868
1869impl std::ops::Deref for Consumer {
1870 type Target = Origin;
1871
1872 fn deref(&self) -> &Self::Target {
1873 &self.info
1874 }
1875}
1876
1877impl Consumer {
1878 fn new(
1879 info: Origin,
1880 root: PathOwned,
1881 nodes: OriginNodes,
1882 dynamic: kio::Shared<OriginDynamicState>,
1883 stats: stats::Session,
1884 ) -> Self {
1885 Self {
1886 info,
1887 nodes,
1888 root,
1889 dynamic,
1890 stats,
1891 }
1892 }
1893
1894 pub fn with_stats(mut self, session: stats::Session) -> Self {
1898 self.stats = session;
1899 self
1900 }
1901
1902 fn untagged(&self) -> Self {
1906 Self {
1907 stats: stats::Session::default(),
1908 ..self.clone()
1909 }
1910 }
1911
1912 pub(crate) fn empty(&self) -> Self {
1917 Self {
1918 info: self.info,
1919 nodes: OriginNodes { nodes: Vec::new() },
1920 root: self.root.clone(),
1921 dynamic: self.dynamic.clone(),
1922 stats: self.stats.clone(),
1923 }
1924 }
1925
1926 pub fn announced(&self) -> AnnounceConsumer {
1933 AnnounceConsumer::new(self.root.clone(), self.nodes.clone(), self.stats.clone())
1934 }
1935
1936 pub fn consume(&self) -> Self {
1938 self.clone()
1939 }
1940
1941 fn get_broadcast(&self, path: impl AsPath) -> Option<broadcast::Consumer> {
1948 let path = path.as_path();
1949 let (root, rest) = self.nodes.get(&path)?;
1950 let state = root.lock();
1951 state.consume_broadcast(&rest)
1952 }
1953
1954 pub async fn announced_broadcast(&self, path: impl AsPath) -> Option<broadcast::Consumer> {
1966 let path = path.as_path();
1967
1968 let consumer = self.scope(std::slice::from_ref(&path))?;
1970
1971 if !consumer.allowed().any(|allowed| path.has_prefix(allowed)) {
1975 return None;
1976 }
1977
1978 let mut announced = consumer.untagged().announced();
1982 let scope = self.stats.egress(self.root.join(&path).to_owned());
1983 loop {
1984 let OriginAnnounce {
1985 path: announced_path,
1986 broadcast,
1987 } = announced.next().await?;
1988 if announced_path.as_path() == path
1990 && let Some(broadcast) = broadcast
1991 {
1992 return Some(broadcast.with_stats(scope));
1993 }
1994 }
1995 }
1996
1997 pub fn scope(&self, prefixes: &[Path]) -> Option<Consumer> {
2003 let prefixes = PathPrefixes::new(prefixes);
2004 Some(Consumer::new(
2005 self.info,
2006 self.root.clone(),
2007 self.nodes.select(&prefixes)?,
2008 self.dynamic.clone(),
2009 self.stats.clone(),
2010 ))
2011 }
2012
2013 pub fn request_broadcast(&self, path: impl AsPath) -> kio::Pending<Requesting> {
2032 let path = path.as_path();
2033
2034 let absolute = self.root.join(&path).to_owned();
2038 let scope = self.stats.egress(&absolute);
2039
2040 if let Some(broadcast) = self.get_broadcast(&path) {
2042 return kio::Pending::new(Requesting::ready(broadcast).with_stats(scope));
2043 }
2044
2045 let mut state = self.dynamic.lock();
2046
2047 if let Some(weak) = state.served.get(&absolute) {
2051 return kio::Pending::new(Requesting::ready(weak.consume()).with_stats(scope));
2052 }
2053
2054 let consumer = if let Some(producer) = state.requests.join(&absolute) {
2057 producer.consume()
2058 } else {
2059 let producer = kio::Producer::<PendingBroadcast>::default();
2060 let consumer = producer.consume();
2061 if state.requests.insert(absolute, producer).is_err() {
2062 return kio::Pending::new(Requesting::failed(Error::Unroutable));
2063 }
2064 consumer
2065 };
2066
2067 kio::Pending::new(Requesting::pending(consumer).with_stats(scope))
2068 }
2069
2070 pub fn with_root(&self, prefix: impl AsPath) -> Option<Self> {
2075 let prefix = prefix.as_path();
2076
2077 Some(Self::new(
2078 self.info,
2079 self.root.join(&prefix).to_owned(),
2080 self.nodes.root(&prefix)?,
2081 self.dynamic.clone(),
2082 self.stats.clone(),
2083 ))
2084 }
2085
2086 pub fn root(&self) -> &Path<'_> {
2088 &self.root
2089 }
2090
2091 pub fn allowed(&self) -> impl Iterator<Item = &Path<'_>> {
2094 self.nodes.nodes.iter().map(|(root, _)| root)
2095 }
2096
2097 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
2099 self.root.join(path)
2100 }
2101}
2102
2103#[derive(Clone)]
2108pub struct AnnounceProducer {
2109 nodes: OriginNodes,
2110 root: PathOwned,
2111}
2112
2113impl AnnounceProducer {
2114 fn new(root: PathOwned, nodes: OriginNodes) -> Self {
2115 Self { nodes, root }
2116 }
2117
2118 pub fn consume(&self) -> AnnounceConsumer {
2124 AnnounceConsumer::new(self.root.clone(), self.nodes.clone(), stats::Session::default())
2127 }
2128
2129 pub fn root(&self) -> &Path<'_> {
2131 &self.root
2132 }
2133}
2134
2135pub struct AnnounceConsumer {
2140 id: ConsumerId,
2141 nodes: OriginNodes,
2142 root: PathOwned,
2143
2144 state: kio::Producer<OriginConsumerState>,
2147
2148 stats: stats::Session,
2151
2152 guards: HashMap<PathOwned, stats::Announce>,
2156}
2157
2158impl AnnounceConsumer {
2159 fn new(root: PathOwned, nodes: OriginNodes, stats: stats::Session) -> Self {
2160 let state = kio::Producer::<OriginConsumerState>::default();
2161 let id = ConsumerId::new();
2162
2163 for (_, node) in &nodes.nodes {
2164 let notify = AnnounceConsumerNotify {
2165 root: root.clone(),
2166 state: state.clone(),
2167 };
2168 node.lock().consume(id, notify);
2169 }
2170
2171 Self {
2172 id,
2173 nodes,
2174 root,
2175 state,
2176 stats,
2177 guards: HashMap::new(),
2178 }
2179 }
2180
2181 fn attribute(&mut self, update: OriginAnnounce) -> OriginAnnounce {
2187 let OriginAnnounce { path, broadcast } = update;
2188 let absolute = self.root.join(&path).to_owned();
2189 match broadcast {
2190 Some(broadcast) => {
2191 let scope = self.stats.egress(&absolute);
2192 self.guards.entry(absolute).or_insert_with(|| scope.announce());
2193 OriginAnnounce {
2194 path,
2195 broadcast: Some(broadcast.with_stats(scope)),
2196 }
2197 }
2198 None => {
2199 self.guards.remove(&absolute);
2200 OriginAnnounce { path, broadcast: None }
2201 }
2202 }
2203 }
2204
2205 pub async fn next(&mut self) -> Option<OriginAnnounce> {
2212 kio::wait(|waiter| self.poll_next(waiter)).await
2213 }
2214
2215 pub fn poll_next(&mut self, waiter: &kio::Waiter) -> Poll<Option<OriginAnnounce>> {
2221 let update = {
2222 let mut state = match ready!(self.state.poll(waiter, |state| {
2223 if state.pending.is_empty() {
2224 Poll::Pending
2225 } else {
2226 Poll::Ready(())
2227 }
2228 })) {
2229 Ok(state) => state,
2230 Err(_) => return Poll::Ready(None),
2232 };
2233 state.take().expect("predicate guaranteed an update")
2234 };
2235 Poll::Ready(Some(self.attribute(update)))
2236 }
2237
2238 pub fn try_next(&mut self) -> Option<OriginAnnounce> {
2243 let update = self.state.write().ok()?.take()?;
2244 Some(self.attribute(update))
2245 }
2246
2247 pub fn is_closed(&self) -> bool {
2249 self.state.write().is_err()
2250 }
2251
2252 pub fn root(&self) -> &Path<'_> {
2254 &self.root
2255 }
2256
2257 pub fn absolute(&self, path: impl AsPath) -> Path<'_> {
2259 self.root.join(path)
2260 }
2261}
2262
2263impl Drop for AnnounceConsumer {
2264 fn drop(&mut self) {
2265 for (_, root) in &self.nodes.nodes {
2266 root.lock().unconsume(self.id);
2267 }
2268 }
2269}
2270
2271#[cfg(test)]
2272use futures::FutureExt;
2273
2274#[cfg(test)]
2275#[allow(missing_docs)] impl AnnounceConsumer {
2277 pub fn assert_next(&mut self, expected: impl AsPath, broadcast: &broadcast::Consumer) {
2278 let expected = expected.as_path();
2279 let announce = self.next().now_or_never().expect("next blocked").expect("no next");
2280 assert_eq!(announce.path, expected, "wrong path");
2281 let announced = announce.broadcast.expect("should be an active announce");
2282 assert!(announced.is_clone(broadcast), "should be the same broadcast");
2283 }
2284
2285 pub fn assert_next_some(&mut self, expected: impl AsPath) -> broadcast::Consumer {
2289 let expected = expected.as_path();
2290 let announce = self.next().now_or_never().expect("next blocked").expect("no next");
2291 assert_eq!(announce.path, expected, "wrong path");
2292 announce.broadcast.expect("should be an active announce")
2293 }
2294
2295 pub fn assert_try_next(&mut self, expected: impl AsPath, broadcast: &broadcast::Consumer) {
2296 let expected = expected.as_path();
2297 let announce = self.try_next().expect("no next");
2298 assert_eq!(announce.path, expected, "wrong path");
2299 let announced = announce.broadcast.expect("should be an active announce");
2300 assert!(announced.is_clone(broadcast), "should be the same broadcast");
2301 }
2302
2303 pub fn assert_try_next_some(&mut self, expected: impl AsPath) -> broadcast::Consumer {
2305 let expected = expected.as_path();
2306 let announce = self.try_next().expect("no next");
2307 assert_eq!(announce.path, expected, "wrong path");
2308 announce.broadcast.expect("should be an active announce")
2309 }
2310
2311 pub fn assert_next_none(&mut self, expected: impl AsPath) {
2312 let expected = expected.as_path();
2313 let announce = self.next().now_or_never().expect("next blocked").expect("no next");
2314 assert_eq!(announce.path, expected, "wrong path");
2315 assert!(announce.broadcast.is_none(), "should be unannounced");
2316 }
2317
2318 pub fn assert_next_wait(&mut self) {
2319 if let Some(res) = self.next().now_or_never() {
2320 panic!("next should block: got {:?}", res.map(|a| a.path));
2321 }
2322 }
2323
2324 }
2333
2334#[cfg(test)]
2335mod tests {
2336 use crate::coding::Decode;
2337 use crate::group;
2338
2339 use super::*;
2340
2341 fn announce() -> broadcast::Route {
2343 broadcast::Route::new().with_announce(true)
2344 }
2345
2346 fn origin_keyed(name: &str, peer: Origin, above: bool) -> Origin {
2352 let name = Path::new(name);
2353 let peer_key = fnv_key(&name, [peer]);
2354 (100u64..)
2355 .map(|id| Origin::new(id).unwrap())
2356 .find(|origin| (fnv_key(&name, [*origin]) > peer_key) == above)
2357 .unwrap()
2358 }
2359
2360 fn front_state(self_origin: Origin, routes: Vec<broadcast::Route>) -> FrontState {
2363 let source = broadcast::Info::new().produce().consume();
2364 FrontState {
2365 path: Path::new("test").to_owned(),
2366 self_origin,
2367 next_route: routes.len() as u64,
2368 routes: routes
2369 .into_iter()
2370 .enumerate()
2371 .map(|(id, route)| FrontRoute {
2372 id: id as u64,
2373 route,
2374 source: source.clone(),
2375 })
2376 .collect(),
2377 active: Some(0),
2378 closed: false,
2379 }
2380 }
2381
2382 fn sibling_route(peer: Origin) -> broadcast::Route {
2385 let hops = OriginList::try_from(vec![Origin::new(90).unwrap(), peer]).unwrap();
2386 announce().with_hops(hops)
2387 }
2388
2389 fn upstream_route(cost: u64) -> broadcast::Route {
2391 let hops = OriginList::try_from(vec![Origin::new(90).unwrap()]).unwrap();
2392 announce().with_hops(hops).with_cost(cost)
2393 }
2394
2395 #[test]
2399 fn test_carrying_gate_keys() {
2400 let peer = Origin::new(3).unwrap();
2401
2402 let mut lost = front_state(
2404 origin_keyed("test", peer, false),
2405 vec![upstream_route(10), sibling_route(peer)],
2406 );
2407 lost.reselect(true);
2408 assert_eq!(
2409 lost.active,
2410 Some(0),
2411 "carrying front re-parented onto a higher-keyed peer"
2412 );
2413 lost.reselect(false);
2414 assert_eq!(lost.active, Some(1), "idle front must take the cheaper route");
2415
2416 let mut won = front_state(
2418 origin_keyed("test", peer, true),
2419 vec![upstream_route(10), sibling_route(peer)],
2420 );
2421 won.reselect(true);
2422 assert_eq!(won.active, Some(1), "carrying front must follow a lower-keyed peer");
2423 }
2424
2425 #[test]
2430 fn test_carrying_gate_symmetric_race() {
2431 let a = Origin::new(1).unwrap();
2432 let b = Origin::new(2).unwrap();
2433
2434 let mut a_view = front_state(a, vec![upstream_route(10), sibling_route(b)]);
2435 let mut b_view = front_state(b, vec![upstream_route(10), sibling_route(a)]);
2436 a_view.reselect(true);
2437 b_view.reselect(true);
2438
2439 let a_moved = a_view.active == Some(1);
2440 let b_moved = b_view.active == Some(1);
2441 assert!(
2442 a_moved != b_moved,
2443 "exactly one side must re-parent (a: {a_moved}, b: {b_moved})"
2444 );
2445 }
2446
2447 #[test]
2452 fn test_carrying_switches_to_benign_routes() {
2453 let peer = Origin::new(3).unwrap();
2454 let lost = origin_keyed("test", peer, false);
2455
2456 let mut forwarder = sibling_route(peer).with_cost(4);
2458 forwarder.advertised = 4;
2459 let mut state = front_state(lost, vec![upstream_route(10), forwarder]);
2460 state.reselect(true);
2461 assert_eq!(
2462 state.active,
2463 Some(1),
2464 "a cheaper forwarder path must win while carrying"
2465 );
2466
2467 let direct = announce().with_hops(OriginList::try_from(vec![peer]).unwrap());
2469 let mut state = front_state(lost, vec![upstream_route(10), direct]);
2470 state.reselect(true);
2471 assert_eq!(
2472 state.active,
2473 Some(1),
2474 "a direct publisher route must win while carrying"
2475 );
2476 }
2477
2478 #[test]
2481 fn test_carrying_gate_ignores_unannounced_incumbent() {
2482 let peer = Origin::new(3).unwrap();
2483 let unannounced = upstream_route(10).with_announce(false);
2484 let mut state = front_state(
2485 origin_keyed("test", peer, false),
2486 vec![unannounced, sibling_route(peer)],
2487 );
2488 state.reselect(true);
2489 assert_eq!(
2490 state.active,
2491 Some(1),
2492 "an unannounced incumbent must always be displaced"
2493 );
2494 }
2495
2496 async fn settle() {
2499 tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
2500 }
2501
2502 async fn accept_track(dynamic: &mut broadcast::Dynamic, name: &str) -> track::Producer {
2505 let request = tokio::time::timeout(std::time::Duration::from_secs(1), dynamic.requested_track())
2506 .await
2507 .expect("timed out waiting for a track request")
2508 .expect("source closed");
2509 assert_eq!(request.name(), name, "unexpected track dispatched");
2510 request.accept(None)
2511 }
2512
2513 #[tokio::test]
2517 async fn test_stats_tagged_end_to_end() {
2518 use crate::Timestamp;
2519 use crate::stats::{Config, Registry, Tier};
2520 use bytes::Bytes;
2521
2522 tokio::time::pause();
2523
2524 let registry = Registry::new(Config::new());
2525 let ctx = registry.tier(Tier::default()).session("acme");
2526
2527 let origin = Origin::random().produce();
2528 let ingress = origin.clone().with_stats(ctx.clone());
2529 let egress = origin.consume().with_stats(ctx.clone());
2530
2531 let mut announced = egress.announced();
2534
2535 let source = ingress.create_broadcast("demo", announce()).unwrap();
2537 let mut dynamic = source.dynamic();
2538 settle().await;
2539 settle().await;
2540
2541 let update = announced.next().await.unwrap();
2543 assert_eq!(update.path.as_str(), "demo");
2544 let broadcast = update.broadcast.unwrap();
2545
2546 let subscribing = broadcast.track("video").unwrap().subscribe(None);
2548 let mut producer = accept_track(&mut dynamic, "video").await;
2549 settle().await;
2550 let mut sub = subscribing.await.unwrap();
2551
2552 let mut group = producer.append_group().unwrap();
2554 group
2555 .write_frame(Timestamp::ZERO, Bytes::from_static(b"hello"))
2556 .unwrap();
2557 group
2558 .write_frame(Timestamp::ZERO, Bytes::from_static(b"world"))
2559 .unwrap();
2560 group.finish().unwrap();
2561
2562 let mut group_c = sub.recv_group().await.unwrap().unwrap();
2564 let mut frames = 0;
2565 while let Some(frame) = group_c.read_frame().await.unwrap() {
2566 assert_eq!(frame.payload.len(), 5);
2567 frames += 1;
2568 }
2569 assert_eq!(frames, 2);
2570 settle().await;
2571
2572 let report = registry.report();
2573 let entry = report
2574 .traffic
2575 .iter()
2576 .find(|e| e.path.as_str() == "demo")
2577 .expect("demo tracked");
2578 let path_len = "demo".len() as u64;
2579
2580 let egress = &entry.publisher;
2582 assert_eq!(egress.announced, 1, "one egress announce");
2583 assert_eq!(egress.announced_bytes, path_len);
2584 assert_eq!(egress.subscriptions, 1, "one egress subscription");
2585 assert_eq!(egress.broadcasts, 1, "one viewer");
2586 assert_eq!(egress.groups, 1);
2587 assert_eq!(egress.frames, 2);
2588 assert_eq!(egress.bytes, 10);
2589 assert_eq!(egress.fetches, 0);
2590
2591 let ingress = &entry.subscriber;
2593 assert_eq!(ingress.announced, 1, "one ingress announce");
2594 assert_eq!(ingress.announced_bytes, path_len);
2595 assert_eq!(ingress.subscriptions, 1, "one ingress track");
2596 assert_eq!(ingress.broadcasts, 0, "ingress has no viewer refcount");
2597 assert_eq!(ingress.groups, 1);
2598 assert_eq!(ingress.frames, 2);
2599 assert_eq!(ingress.bytes, 10);
2600
2601 let fetched = broadcast.track("video").unwrap().fetch_group(0, None).await.unwrap();
2603 let _ = fetched;
2604 settle().await;
2605 let report = registry.report();
2606 let entry = report.traffic.iter().find(|e| e.path.as_str() == "demo").unwrap();
2607 assert_eq!(entry.publisher.fetches, 1, "one fetch");
2608 assert_eq!(entry.publisher.subscriptions, 1, "fetch does not bump subscriptions");
2609 assert_eq!(entry.publisher.broadcasts, 1, "fetch does not bump the viewer refcount");
2610 assert_eq!(entry.subscriber.fetches, 0, "ingress cannot fetch");
2614 }
2615
2616 #[tokio::test]
2621 async fn test_stats_read_frame_counts_once() {
2622 use crate::Timestamp;
2623 use crate::stats::{Config, Registry, Tier};
2624 use bytes::Bytes;
2625
2626 tokio::time::pause();
2627
2628 let registry = Registry::new(Config::new());
2629 let ctx = registry.tier(Tier::default()).session("acme");
2630
2631 let origin = Origin::random().produce();
2632 let ingress = origin.clone().with_stats(ctx.clone());
2633 let egress = origin.consume().with_stats(ctx.clone());
2634
2635 let mut announced = egress.announced();
2636 let source = ingress.create_broadcast("demo", announce()).unwrap();
2637 let mut dynamic = source.dynamic();
2638 settle().await;
2639 settle().await;
2640
2641 let broadcast = announced.next().await.unwrap().broadcast.unwrap();
2642 let subscribing = broadcast.track("video").unwrap().subscribe(None);
2643 let mut producer = accept_track(&mut dynamic, "video").await;
2644 settle().await;
2645 let mut sub = subscribing.await.unwrap();
2646
2647 producer
2649 .write_frame(Timestamp::ZERO, Bytes::from_static(b"hello"))
2650 .unwrap();
2651
2652 let frame = sub.read_frame().await.unwrap().expect("frame");
2653 assert_eq!(frame.payload.len(), 5);
2654 settle().await;
2655
2656 let report = registry.report();
2657 let entry = report
2658 .traffic
2659 .iter()
2660 .find(|e| e.path.as_str() == "demo")
2661 .expect("demo tracked");
2662 assert_eq!(entry.publisher.groups, 1, "one group, counted once");
2663 assert_eq!(entry.publisher.frames, 1, "one frame, counted once");
2664 assert_eq!(
2665 entry.publisher.bytes, 5,
2666 "payload counted once, not zero and not doubled"
2667 );
2668 }
2669
2670 #[tokio::test]
2674 async fn test_stats_datagrams_counted_both_sides() {
2675 use crate::Timestamp;
2676 use crate::stats::{Config, Registry, Tier};
2677
2678 tokio::time::pause();
2679
2680 let registry = Registry::new(Config::new());
2681 let ctx = registry.tier(Tier::default()).session("acme");
2682
2683 let origin = Origin::random().produce();
2684 let ingress = origin.clone().with_stats(ctx.clone());
2685 let egress = origin.consume().with_stats(ctx.clone());
2686
2687 let mut announced = egress.announced();
2688 let source = ingress.create_broadcast("demo", announce()).unwrap();
2689 let mut dynamic = source.dynamic();
2690 settle().await;
2691 settle().await;
2692
2693 let broadcast = announced.next().await.unwrap().broadcast.unwrap();
2694 let subscribing = broadcast.track("video").unwrap().subscribe(None);
2695 let mut producer = accept_track(&mut dynamic, "video").await;
2696 settle().await;
2697 let mut sub = subscribing.await.unwrap();
2698
2699 producer.append_datagram(Timestamp::ZERO, &b"hello"[..]).unwrap();
2700 let datagram = sub.recv_datagram().await.unwrap().expect("datagram");
2701 assert_eq!(&datagram.payload[..], b"hello");
2702 settle().await;
2703
2704 let report = registry.report();
2705 let entry = report
2706 .traffic
2707 .iter()
2708 .find(|e| e.path.as_str() == "demo")
2709 .expect("demo tracked");
2710
2711 for (side, traffic) in [("egress", &entry.publisher), ("ingress", &entry.subscriber)] {
2712 assert_eq!(traffic.datagrams, 1, "{side}: one datagram");
2713 assert_eq!(traffic.groups, 1, "{side}: counted as its single-frame group");
2714 assert_eq!(traffic.frames, 1, "{side}: one frame");
2715 assert_eq!(traffic.bytes, 5, "{side}: payload counted once");
2716 }
2717 }
2718
2719 #[test]
2720 fn origin_rejects_reserved_ids() {
2721 assert!(Origin::new(0).is_err());
2722 assert!(Origin::new(1u64 << 62).is_err());
2723 assert_eq!(Origin::new(1).unwrap().id(), 1);
2724
2725 let mut zero = [0u8].as_slice();
2726 assert_eq!(
2727 Origin::decode(&mut zero, crate::lite::Version::Lite05).unwrap(),
2728 Origin::UNKNOWN
2729 );
2730 }
2731
2732 #[test]
2733 fn origin_list_push_fails_at_limit() {
2734 let mut list = OriginList::new();
2735 for _ in 0..MAX_HOPS {
2736 list.push(Origin::random()).unwrap();
2737 }
2738 assert_eq!(list.len(), MAX_HOPS);
2739 assert_eq!(list.push(Origin::random()), Err(TooManyOrigins));
2740 }
2741
2742 #[test]
2743 fn origin_list_replace_first() {
2744 let mut list = OriginList::new();
2745 for _ in 0..3 {
2746 list.push(Origin::UNKNOWN).unwrap();
2747 }
2748
2749 assert!(list.replace_first(Origin::UNKNOWN, Origin::new(7).unwrap()));
2751 assert_eq!(
2752 list.as_slice(),
2753 &[Origin::new(7).unwrap(), Origin::UNKNOWN, Origin::UNKNOWN]
2754 );
2755
2756 assert!(!list.replace_first(Origin::new(99).unwrap(), Origin::new(8).unwrap()));
2758 assert_eq!(list.len(), 3);
2759 }
2760
2761 #[test]
2762 fn origin_list_try_from_vec_enforces_limit() {
2763 let under: Vec<Origin> = (0..MAX_HOPS).map(|_| Origin::random()).collect();
2764 assert!(OriginList::try_from(under).is_ok());
2765
2766 let over: Vec<Origin> = (0..MAX_HOPS + 1).map(|_| Origin::random()).collect();
2767 assert_eq!(OriginList::try_from(over), Err(TooManyOrigins));
2768 }
2769
2770 #[tokio::test]
2771 async fn test_announce() {
2772 tokio::time::pause();
2773
2774 let origin = Origin::random().produce();
2775
2776 let mut consumer1 = origin.consume().announced();
2777 consumer1.assert_next_wait();
2778
2779 let mut broadcast1 = origin.create_broadcast("test1", announce()).unwrap();
2781 settle().await;
2782
2783 consumer1.assert_next_some("test1");
2784 consumer1.assert_next_wait();
2785
2786 let mut consumer2 = origin.consume().announced();
2789
2790 let mut broadcast2 = origin.create_broadcast("test2", announce()).unwrap();
2792 settle().await;
2793
2794 consumer1.assert_next_some("test2");
2795 consumer1.assert_next_wait();
2796
2797 consumer2.assert_next_some("test1");
2798 consumer2.assert_next_some("test2");
2799 consumer2.assert_next_wait();
2800
2801 broadcast1.finish();
2803 settle().await;
2804
2805 consumer1.assert_next_none("test1");
2807 consumer2.assert_next_none("test1");
2808 consumer1.assert_next_wait();
2809 consumer2.assert_next_wait();
2810
2811 let mut consumer3 = origin.consume().announced();
2813 consumer3.assert_next_some("test2");
2814 consumer3.assert_next_wait();
2815
2816 broadcast2.finish();
2817 settle().await;
2818
2819 consumer1.assert_next_none("test2");
2820 consumer2.assert_next_none("test2");
2821 consumer3.assert_next_none("test2");
2822 }
2823
2824 #[tokio::test]
2828 async fn test_duplicate() {
2829 tokio::time::pause();
2830
2831 let origin = Origin::random().produce();
2832 let consumer = origin.consume();
2833 let mut announced = consumer.announced();
2834
2835 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
2836 let mut broadcast2 = origin.create_broadcast("test", announce()).unwrap();
2837 let mut broadcast3 = origin.create_broadcast("test", announce()).unwrap();
2838 settle().await;
2839 assert!(consumer.get_broadcast("test").is_some());
2840
2841 announced.assert_next_some("test");
2842 announced.assert_next_wait();
2843
2844 broadcast2.finish();
2846 settle().await;
2847 assert!(consumer.get_broadcast("test").is_some());
2848 announced.assert_next_wait();
2849
2850 broadcast1.finish();
2852 settle().await;
2853 assert!(consumer.get_broadcast("test").is_some());
2854 announced.assert_next_wait();
2855
2856 broadcast3.finish();
2858 settle().await;
2859 assert!(consumer.get_broadcast("test").is_none());
2860
2861 announced.assert_next_none("test");
2862 announced.assert_next_wait();
2863 }
2864
2865 #[tokio::test]
2868 async fn test_route_failover() {
2869 tokio::time::pause();
2870
2871 let origin = Origin::random().produce();
2872 let consumer = origin.consume();
2873 let mut announced = consumer.announced();
2874
2875 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
2876 let hops_b = OriginList::try_from(vec![Origin::new(2).unwrap(), Origin::new(3).unwrap()]).unwrap();
2877
2878 let source_a = origin.create_broadcast("test", announce().with_hops(hops_a)).unwrap();
2880 let mut dynamic_a = source_a.dynamic();
2881 settle().await;
2882 settle().await;
2883 let broadcast = consumer.request_broadcast("test").await.unwrap();
2884 announced.assert_next_some("test");
2885
2886 let source_b = origin.create_broadcast("test", announce().with_hops(hops_b)).unwrap();
2888 let mut dynamic_b = source_b.dynamic();
2889 settle().await;
2890 settle().await;
2891 announced.assert_next_wait();
2892
2893 let subscribing = broadcast.track("video").unwrap().subscribe(None);
2895 let mut producer = accept_track(&mut dynamic_a, "video").await;
2896 settle().await;
2897 dynamic_b.assert_no_request();
2898
2899 let mut sub = subscribing.await.unwrap();
2900 sub.assert_no_group();
2903 assert_eq!(producer.subscription().unwrap().group_start, None);
2904
2905 producer.append_group().unwrap();
2906 producer.append_group().unwrap();
2907 assert_eq!(sub.assert_group().sequence, 0);
2908 assert_eq!(sub.assert_group().sequence, 1);
2909
2910 producer.abort(Error::Dropped).unwrap();
2914 source_a.abort();
2915 drop(source_a);
2916 drop(dynamic_a);
2917 settle().await;
2918 announced.assert_next_wait();
2919
2920 let mut producer = accept_track(&mut dynamic_b, "video").await;
2923 settle().await;
2924 sub.assert_no_group();
2925 assert_eq!(producer.subscription().unwrap().group_start, Some(2));
2926 producer.create_group(group::Info { sequence: 1 }).unwrap();
2927 producer.create_group(group::Info { sequence: 2 }).unwrap();
2928 assert_eq!(sub.assert_group().sequence, 2, "groups below the boundary are filtered");
2929 sub.assert_not_closed();
2930 }
2931
2932 #[tokio::test]
2935 async fn test_broadcast_route_watch() {
2936 let mut producer = broadcast::Info::new().produce();
2937 let mut consumer = producer.consume();
2938
2939 assert_eq!(consumer.route_changed().await.unwrap(), broadcast::Route::default());
2941
2942 producer.set_route(broadcast::Route::default()).unwrap();
2944 assert!(consumer.route_changed().now_or_never().is_none());
2945
2946 let mut hops = OriginList::new();
2947 hops.push(Origin::new(7).unwrap()).unwrap();
2948 let route = broadcast::Route::new().with_hops(hops).with_cost(3);
2949 producer.set_route(route.clone()).unwrap();
2950 assert_eq!(consumer.route_changed().await.unwrap(), route);
2951
2952 let mut fresh = producer.consume();
2954 assert_eq!(fresh.route_changed().await.unwrap(), route);
2955
2956 drop(producer);
2957 assert!(matches!(consumer.route_changed().await.unwrap_err(), Error::Dropped));
2958 }
2959
2960 #[tokio::test]
2964 async fn test_route_cost_update() {
2965 tokio::time::pause();
2966
2967 let origin = Info::new(origin_keyed("test", Origin::new(3).unwrap(), true)).produce();
2971 let consumer = origin.consume();
2972 let mut announced = consumer.announced();
2973
2974 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
2975 let hops_b = OriginList::try_from(vec![Origin::new(2).unwrap(), Origin::new(3).unwrap()]).unwrap();
2976
2977 let mut source_a = origin
2979 .create_broadcast("test", announce().with_hops(hops_a.clone()))
2980 .unwrap();
2981 let mut dynamic_a = source_a.dynamic();
2982 settle().await;
2983 let broadcast = consumer.request_broadcast("test").await.unwrap();
2984 announced.assert_next_some("test");
2985
2986 let mut watch = broadcast.clone();
2987 assert_eq!(watch.route_changed().await.unwrap().hops, hops_a);
2988
2989 let mut source_b = origin
2990 .create_broadcast("test", announce().with_hops(hops_b.clone()))
2991 .unwrap();
2992 let mut dynamic_b = source_b.dynamic();
2993 settle().await;
2994 assert!(
2995 watch.route_changed().now_or_never().is_none(),
2996 "a losing standby must not change the advertised route"
2997 );
2998
2999 let subscribing = broadcast.track("video").unwrap().subscribe(None);
3001 let mut producer = accept_track(&mut dynamic_a, "video").await;
3002 settle().await;
3003 let mut sub = subscribing.await.unwrap();
3004 producer.append_group().unwrap();
3005 assert_eq!(sub.assert_group().sequence, 0);
3006
3007 source_a
3010 .set_route(announce().with_hops(hops_a.clone()).with_cost(10))
3011 .unwrap();
3012 settle().await;
3013 assert_eq!(watch.route_changed().await.unwrap().hops, hops_b);
3014 announced.assert_next_wait();
3015
3016 let mut producer_b = accept_track(&mut dynamic_b, "video").await;
3017 settle().await;
3018 sub.assert_no_group();
3021 assert_eq!(producer_b.subscription().unwrap().group_start, Some(1));
3022 producer_b.create_group(group::Info { sequence: 1 }).unwrap();
3023 assert_eq!(sub.assert_group().sequence, 1);
3024 sub.assert_not_closed();
3025
3026 source_b
3028 .set_route(announce().with_hops(hops_b.clone()).with_cost(5))
3029 .unwrap();
3030 settle().await;
3031 let advertised = watch.route_changed().await.unwrap();
3032 assert_eq!(advertised.hops, hops_b);
3033 assert_eq!(advertised.cost, 5);
3034 announced.assert_next_wait();
3035 }
3036
3037 #[tokio::test]
3040 async fn test_completed_track_survives_route_churn() {
3041 tokio::time::pause();
3042
3043 let origin = Origin::random().produce();
3044 let consumer = origin.consume();
3045
3046 let hops_a = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
3047 let hops_b = OriginList::try_from(vec![Origin::new(2).unwrap(), Origin::new(3).unwrap()]).unwrap();
3048
3049 let source_a = origin.create_broadcast("test", announce().with_hops(hops_a)).unwrap();
3050 let mut dynamic_a = source_a.dynamic();
3051 settle().await;
3052 let source_b = origin.create_broadcast("test", announce().with_hops(hops_b)).unwrap();
3053 let mut dynamic_b = source_b.dynamic();
3054 settle().await;
3055 settle().await;
3056 let broadcast = consumer.request_broadcast("test").await.unwrap();
3057
3058 let subscribing = broadcast.track("video").unwrap().subscribe(None);
3060 let mut producer = accept_track(&mut dynamic_a, "video").await;
3061 settle().await;
3062 let mut sub = subscribing.await.unwrap();
3063 producer.append_group().unwrap();
3064 assert_eq!(sub.assert_group().sequence, 0);
3065 producer.finish().unwrap();
3066 drop(producer);
3067 settle().await;
3068 sub.assert_closed();
3069
3070 source_a.abort();
3072 drop(source_a);
3073 drop(dynamic_a);
3074 settle().await;
3075 dynamic_b.assert_no_request();
3076
3077 let mut late = broadcast.track("video").unwrap().subscribe(None).await.unwrap();
3079 late.assert_closed();
3080 }
3081
3082 #[tokio::test]
3085 async fn test_serve_resets_retry_budget() {
3086 tokio::time::pause();
3087
3088 let origin = Origin::random().produce();
3089 let consumer = origin.consume();
3090
3091 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
3092 let source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
3093 let mut dynamic = source.dynamic();
3094 settle().await;
3095 settle().await;
3096 let broadcast = consumer.request_broadcast("test").await.unwrap();
3097
3098 let subscribing = broadcast.track("video").unwrap().subscribe(None);
3100
3101 for _ in 0..2 * MAX_TRACK_RETRIES {
3104 let request = tokio::time::timeout(std::time::Duration::from_secs(1), dynamic.requested_track())
3105 .await
3106 .expect("timed out waiting for a retry")
3107 .unwrap();
3108 request.reject(Error::NotFound);
3109 let producer = accept_track(&mut dynamic, "video").await;
3110 settle().await;
3111 drop(producer);
3112 }
3113
3114 let _producer = accept_track(&mut dynamic, "video").await;
3115 settle().await;
3116 let mut sub = subscribing.await.unwrap();
3117 sub.assert_not_closed();
3118 }
3119
3120 #[tokio::test]
3124 async fn test_route_handover() {
3125 tokio::time::pause();
3126
3127 let origin = Origin::random().produce();
3128 let consumer = origin.consume();
3129 let mut announced = consumer.announced();
3130
3131 let hops_long = OriginList::try_from(vec![Origin::new(2).unwrap(), Origin::new(3).unwrap()]).unwrap();
3132 let hops_short = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
3133
3134 let source_a = origin
3135 .create_broadcast("test", announce().with_hops(hops_long))
3136 .unwrap();
3137 let mut dynamic_a = source_a.dynamic();
3138 settle().await;
3139 settle().await;
3140 let broadcast = consumer.request_broadcast("test").await.unwrap();
3141 announced.assert_next_some("test");
3142
3143 let subscribing = broadcast.track("video").unwrap().subscribe(None);
3144 let mut producer_a = accept_track(&mut dynamic_a, "video").await;
3145 settle().await;
3146 let mut sub = subscribing.await.unwrap();
3147 producer_a.append_group().unwrap();
3148 producer_a.append_group().unwrap();
3149 assert_eq!(sub.assert_group().sequence, 0);
3150 assert_eq!(sub.assert_group().sequence, 1);
3151
3152 let source_b = origin
3155 .create_broadcast("test", announce().with_hops(hops_short))
3156 .unwrap();
3157 let mut dynamic_b = source_b.dynamic();
3158 settle().await;
3159 settle().await;
3160 announced.assert_next_wait();
3161
3162 let mut producer_b = accept_track(&mut dynamic_b, "video").await;
3163 settle().await;
3164
3165 sub.assert_no_group();
3168 assert_eq!(producer_a.subscription().unwrap().group_end, Some(1));
3169 assert_eq!(producer_b.subscription().unwrap().group_start, Some(2));
3170
3171 producer_a.create_group(group::Info { sequence: 2 }).unwrap();
3173 producer_b.create_group(group::Info { sequence: 2 }).unwrap();
3174 producer_b.create_group(group::Info { sequence: 3 }).unwrap();
3175 assert_eq!(sub.assert_group().sequence, 2);
3176 assert_eq!(sub.assert_group().sequence, 3);
3177 sub.assert_no_group();
3178 sub.assert_not_closed();
3179 }
3180
3181 #[tokio::test(start_paused = true)]
3184 async fn test_route_unannounce_immediate() {
3185 let origin = Origin::random().produce();
3186 let consumer = origin.consume();
3187 let mut announced = consumer.announced();
3188
3189 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
3190 let mut source = origin
3191 .create_broadcast("test", announce().with_hops(hops.clone()))
3192 .unwrap();
3193 settle().await;
3194 let broadcast = consumer.request_broadcast("test").await.unwrap();
3195 announced.assert_next_some("test");
3196
3197 source.finish();
3200 settle().await;
3201 announced.assert_next_none("test");
3202
3203 let _source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
3205 settle().await;
3206 let fresh = consumer.request_broadcast("test").await.unwrap();
3207 announced.assert_next_some("test");
3208 assert!(
3209 !fresh.is_clone(&broadcast),
3210 "re-create must not splice the old broadcast"
3211 );
3212 }
3213
3214 #[tokio::test(start_paused = true)]
3218 async fn test_route_detach_immediate() {
3219 let origin = Origin::random().produce();
3220 let consumer = origin.consume();
3221 let mut announced = consumer.announced();
3222
3223 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
3224 let source = origin
3225 .create_broadcast("test", announce().with_hops(hops.clone()))
3226 .unwrap();
3227 let mut dynamic = source.dynamic();
3228 settle().await;
3229 settle().await;
3230 let broadcast = consumer.request_broadcast("test").await.unwrap();
3231 announced.assert_next_some("test");
3232
3233 let subscribing = broadcast.track("video").unwrap().subscribe(None);
3234 let producer = accept_track(&mut dynamic, "video").await;
3235 settle().await;
3236 let mut sub = subscribing.await.unwrap();
3237
3238 drop(producer);
3240 source.abort();
3241 drop(source);
3242 drop(dynamic);
3243
3244 settle().await;
3245 announced.assert_next_none("test");
3246 sub.assert_error();
3247
3248 let _source = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
3251 settle().await;
3252 settle().await;
3253 let fresh = consumer.request_broadcast("test").await.unwrap();
3254 announced.assert_next_some("test");
3255 assert!(
3256 !fresh.is_clone(&broadcast),
3257 "re-create must not splice the old broadcast"
3258 );
3259 }
3260
3261 #[tokio::test]
3264 async fn test_announce_toggle() {
3265 tokio::time::pause();
3266
3267 let origin = Origin::random().produce();
3268 let consumer = origin.consume();
3269 let mut announced = consumer.announced();
3270
3271 let mut source = origin.create_broadcast("test", broadcast::Route::new()).unwrap();
3272 settle().await;
3273
3274 announced.assert_next_wait();
3276 let broadcast = consumer
3277 .get_broadcast("test")
3278 .expect("offline broadcast is still routable");
3279 assert!(!broadcast.route().announce);
3280
3281 let requested = consumer.request_broadcast("test").await.unwrap();
3283 assert!(requested.is_clone(&broadcast));
3284
3285 source.set_route(announce()).unwrap();
3287 settle().await;
3288 let face = announced.assert_next_some("test");
3289 assert!(face.is_clone(&broadcast));
3290
3291 let mut fresh = origin.consume().announced();
3293 fresh.assert_next_some("test");
3294 fresh.assert_next_wait();
3295
3296 source.set_route(broadcast::Route::new()).unwrap();
3298 settle().await;
3299 announced.assert_next_none("test");
3300 assert!(consumer.get_broadcast("test").is_some());
3301 let mut fresh = origin.consume().announced();
3302 fresh.assert_next_wait();
3303
3304 source.finish();
3305 settle().await;
3306 assert!(consumer.get_broadcast("test").is_none());
3307 }
3308
3309 #[tokio::test]
3312 async fn test_announce_beats_offline() {
3313 tokio::time::pause();
3314
3315 let origin = Origin::random().produce();
3316 let consumer = origin.consume();
3317 let mut announced = consumer.announced();
3318
3319 let _offline = origin.create_broadcast("test", broadcast::Route::new()).unwrap();
3321 settle().await;
3322 announced.assert_next_wait();
3323
3324 let mut announced_source = origin.create_broadcast("test", announce().with_cost(10)).unwrap();
3327 settle().await;
3328 announced.assert_next_some("test");
3329 let face = consumer.get_broadcast("test").unwrap();
3330 assert!(face.route().announce);
3331 assert_eq!(face.route().cost, 10);
3332
3333 announced_source.finish();
3336 settle().await;
3337 announced.assert_next_none("test");
3338 assert!(consumer.get_broadcast("test").is_some());
3339 }
3340
3341 #[tokio::test]
3344 async fn test_better_source_no_churn() {
3345 tokio::time::pause();
3346
3347 let origin = Origin::random().produce();
3348 let mut announced = origin.consume().announced();
3349
3350 let hops = OriginList::try_from(vec![Origin::new(1).unwrap()]).unwrap();
3352 let _a = origin.create_broadcast("test", announce().with_hops(hops)).unwrap();
3353 settle().await;
3354 let face = announced.assert_next_some("test");
3355
3356 let _b = origin.create_broadcast("test", announce()).unwrap();
3357 settle().await;
3358 announced.assert_next_wait();
3359 let current = origin.consume().get_broadcast("test").unwrap();
3360 assert!(current.is_clone(&face), "the broadcast identity must not change");
3361 assert!(current.route().hops.is_empty());
3363 }
3364
3365 #[tokio::test]
3366 async fn test_duplicate_reverse() {
3367 tokio::time::pause();
3368
3369 let origin = Origin::random().produce();
3370
3371 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
3372 let mut broadcast2 = origin.create_broadcast("test", announce()).unwrap();
3373 settle().await;
3374 assert!(origin.consume().get_broadcast("test").is_some());
3375
3376 broadcast2.finish();
3378 settle().await;
3379 assert!(origin.consume().get_broadcast("test").is_some());
3380
3381 broadcast1.finish();
3382 settle().await;
3383 assert!(origin.consume().get_broadcast("test").is_none());
3384 }
3385
3386 #[tokio::test]
3387 async fn test_deterministic_tiebreak() {
3388 tokio::time::pause();
3389
3390 fn hops(ids: &[u64]) -> OriginList {
3391 OriginList::try_from(
3392 ids.iter()
3393 .copied()
3394 .map(|id| Origin::new(id).unwrap())
3395 .collect::<Vec<_>>(),
3396 )
3397 .unwrap()
3398 }
3399
3400 async fn winner(first: &[u64], second: &[u64]) -> OriginList {
3403 let origin = Origin::random().produce();
3404 let _a = origin
3405 .create_broadcast("test", announce().with_hops(hops(first)))
3406 .unwrap();
3407 let _b = origin
3408 .create_broadcast("test", announce().with_hops(hops(second)))
3409 .unwrap();
3410 settle().await;
3411 origin.consume().get_broadcast("test").unwrap().route().hops
3412 }
3413
3414 let forward = winner(&[10, 20], &[30, 40]).await;
3417 let reverse = winner(&[30, 40], &[10, 20]).await;
3418 assert_eq!(forward, reverse, "tie-break must not depend on publish order");
3419
3420 assert_eq!(winner(&[10, 20], &[30]).await.len(), 1);
3422 assert_eq!(winner(&[30], &[10, 20]).await.len(), 1);
3423 }
3424
3425 #[tokio::test]
3430 async fn test_many_announces() {
3431 let origin = Origin::random().produce();
3432
3433 let mut consumer = origin.consume().announced();
3434 let mut broadcasts = Vec::new();
3436 for i in 0..256 {
3437 broadcasts.push(origin.create_broadcast(format!("test{i:03}"), announce()).unwrap());
3438 settle().await;
3439 }
3440
3441 for i in 0..256 {
3442 consumer.assert_next_some(format!("test{i:03}"));
3443 }
3444 consumer.assert_next_wait();
3445 }
3446
3447 #[tokio::test]
3448 async fn test_many_announces_try() {
3449 let origin = Origin::random().produce();
3450
3451 let mut consumer = origin.consume().announced();
3452 let mut broadcasts = Vec::new();
3454 for i in 0..256 {
3455 broadcasts.push(origin.create_broadcast(format!("test{i:03}"), announce()).unwrap());
3456 settle().await;
3457 }
3458
3459 for i in 0..256 {
3460 consumer.assert_try_next_some(format!("test{i:03}"));
3461 }
3462 }
3463
3464 #[tokio::test]
3465 async fn test_with_root_basic() {
3466 let origin = Origin::random().produce();
3467
3468 let foo_producer = origin.with_root("foo").expect("should create root");
3470 assert_eq!(foo_producer.root().as_str(), "foo");
3471
3472 let mut consumer = origin.consume().announced();
3473
3474 let _broadcast = foo_producer
3476 .create_broadcast("bar/baz", announce())
3477 .expect("publish allowed");
3478 settle().await;
3479 consumer.assert_next_some("foo/bar/baz");
3481
3482 let mut foo_consumer = foo_producer.consume().announced();
3484 foo_consumer.assert_next_some("bar/baz");
3485 }
3486
3487 #[tokio::test]
3488 async fn test_with_root_nested() {
3489 let origin = Origin::random().produce();
3490
3491 let foo_producer = origin.with_root("foo").expect("should create foo root");
3493 let foo_bar_producer = foo_producer.with_root("bar").expect("should create bar root");
3494 assert_eq!(foo_bar_producer.root().as_str(), "foo/bar");
3495
3496 let mut consumer = origin.consume().announced();
3497
3498 let _broadcast = foo_bar_producer
3500 .create_broadcast("baz", announce())
3501 .expect("publish allowed");
3502 settle().await;
3503 consumer.assert_next_some("foo/bar/baz");
3505
3506 let mut foo_bar_consumer = foo_bar_producer.consume().announced();
3508 foo_bar_consumer.assert_next_some("baz");
3509 }
3510
3511 #[tokio::test]
3512 async fn test_publish_scope_allows() {
3513 let origin = Origin::random().produce();
3514
3515 let limited_producer = origin
3517 .scope(&["allowed/path1".into(), "allowed/path2".into()])
3518 .expect("should create limited producer");
3519
3520 let _broadcast = limited_producer
3522 .create_broadcast("allowed/path1", announce())
3523 .expect("publish allowed");
3524 let _keep2 = limited_producer
3525 .create_broadcast("allowed/path1/nested", announce())
3526 .expect("publish allowed");
3527 let _keep3 = limited_producer
3528 .create_broadcast("allowed/path2", announce())
3529 .expect("publish allowed");
3530 settle().await;
3531
3532 assert!(limited_producer.create_broadcast("notallowed", announce()).is_err());
3534 assert!(limited_producer.create_broadcast("allowed", announce()).is_err()); assert!(limited_producer.create_broadcast("other/path", announce()).is_err());
3536 }
3537
3538 #[tokio::test]
3539 async fn test_publish_max_parts() {
3540 let origin = Origin::random().produce();
3541
3542 let at_limit = (0..Path::MAX_PARTS)
3543 .map(|i| i.to_string())
3544 .collect::<Vec<_>>()
3545 .join("/");
3546 let _broadcast = origin
3547 .create_broadcast(at_limit.as_str(), announce())
3548 .expect("publish allowed");
3549 settle().await;
3550
3551 let too_deep = format!("{at_limit}/extra");
3552 assert!(origin.create_broadcast(too_deep.as_str(), announce()).is_err());
3553
3554 let rooted = origin.with_root("root").expect("wildcard allows any root");
3556 assert!(rooted.create_broadcast(at_limit.as_str(), announce()).is_err());
3557 }
3558
3559 #[tokio::test]
3560 async fn test_publish_scope_empty() {
3561 let origin = Origin::random().produce();
3562
3563 assert!(origin.scope(&[]).is_none());
3565 }
3566
3567 #[tokio::test]
3568 async fn test_consume_scope_filters() {
3569 let origin = Origin::random().produce();
3570
3571 let mut consumer = origin.consume().announced();
3572
3573 let _broadcast1 = origin.create_broadcast("allowed", announce()).unwrap();
3575 let _broadcast2 = origin.create_broadcast("allowed/nested", announce()).unwrap();
3576 let _broadcast3 = origin.create_broadcast("notallowed", announce()).unwrap();
3577 settle().await;
3578
3579 let mut limited_consumer = origin
3581 .consume()
3582 .scope(&["allowed".into()])
3583 .expect("should create limited consumer")
3584 .announced();
3585
3586 limited_consumer.assert_next_some("allowed");
3588 limited_consumer.assert_next_some("allowed/nested");
3589 limited_consumer.assert_next_wait(); consumer.assert_next_some("allowed");
3593 consumer.assert_next_some("allowed/nested");
3594 consumer.assert_next_some("notallowed");
3595 }
3596
3597 #[tokio::test]
3598 async fn test_consume_scope_multiple_prefixes() {
3599 let origin = Origin::random().produce();
3600
3601 let _broadcast1 = origin.create_broadcast("foo/test", announce()).unwrap();
3602 let _broadcast2 = origin.create_broadcast("bar/test", announce()).unwrap();
3603 let _broadcast3 = origin.create_broadcast("baz/test", announce()).unwrap();
3604 settle().await;
3605
3606 let mut limited_consumer = origin
3608 .consume()
3609 .scope(&["foo".into(), "bar".into()])
3610 .expect("should create limited consumer")
3611 .announced();
3612
3613 limited_consumer.assert_next_some("bar/test");
3615 limited_consumer.assert_next_some("foo/test");
3616 limited_consumer.assert_next_wait(); }
3618
3619 #[tokio::test]
3620 async fn test_with_root_and_publish_scope() {
3621 let origin = Origin::random().produce();
3622
3623 let foo_producer = origin.with_root("foo").expect("should create foo root");
3625
3626 let limited_producer = foo_producer
3628 .scope(&["bar".into(), "goop/pee".into()])
3629 .expect("should create limited producer");
3630
3631 let mut consumer = origin.consume().announced();
3632
3633 let _broadcast = limited_producer
3635 .create_broadcast("bar", announce())
3636 .expect("publish allowed");
3637 let _keep2 = limited_producer
3638 .create_broadcast("bar/nested", announce())
3639 .expect("publish allowed");
3640 let _keep3 = limited_producer
3641 .create_broadcast("goop/pee", announce())
3642 .expect("publish allowed");
3643 let _keep4 = limited_producer
3644 .create_broadcast("goop/pee/nested", announce())
3645 .expect("publish allowed");
3646 settle().await;
3647
3648 assert!(limited_producer.create_broadcast("baz", announce()).is_err());
3650 assert!(limited_producer.create_broadcast("goop", announce()).is_err()); assert!(limited_producer.create_broadcast("goop/other", announce()).is_err());
3652
3653 consumer.assert_next_some("foo/bar");
3655 consumer.assert_next_some("foo/bar/nested");
3656 consumer.assert_next_some("foo/goop/pee");
3657 consumer.assert_next_some("foo/goop/pee/nested");
3658 }
3659
3660 #[tokio::test]
3661 async fn test_with_root_and_consume_scope() {
3662 let origin = Origin::random().produce();
3663
3664 let _broadcast1 = origin.create_broadcast("foo/bar/test", announce()).unwrap();
3666 let _broadcast2 = origin.create_broadcast("foo/goop/pee/test", announce()).unwrap();
3667 let _broadcast3 = origin.create_broadcast("foo/other/test", announce()).unwrap();
3668 settle().await;
3669
3670 let foo_producer = origin.with_root("foo").expect("should create foo root");
3672
3673 let mut limited_consumer = foo_producer
3675 .consume()
3676 .scope(&["bar".into(), "goop/pee".into()])
3677 .expect("should create limited consumer")
3678 .announced();
3679
3680 limited_consumer.assert_next_some("bar/test");
3682 limited_consumer.assert_next_some("goop/pee/test");
3683 limited_consumer.assert_next_wait(); }
3685
3686 #[tokio::test]
3687 async fn test_with_root_unauthorized() {
3688 let origin = Origin::random().produce();
3689
3690 let limited_producer = origin
3692 .scope(&["allowed".into()])
3693 .expect("should create limited producer");
3694
3695 assert!(limited_producer.with_root("notallowed").is_none());
3697
3698 let allowed_root = limited_producer
3700 .with_root("allowed")
3701 .expect("should create allowed root");
3702 assert_eq!(allowed_root.root().as_str(), "allowed");
3703 }
3704
3705 #[tokio::test]
3706 async fn test_wildcard_permission() {
3707 let origin = Origin::random().produce();
3708
3709 let root_producer = origin.clone();
3711
3712 let _broadcast = root_producer
3714 .create_broadcast("any/path", announce())
3715 .expect("publish allowed");
3716 let _keep2 = root_producer
3717 .create_broadcast("other/path", announce())
3718 .expect("publish allowed");
3719 settle().await;
3720
3721 let foo_producer = root_producer.with_root("foo").expect("should create any root");
3723 assert_eq!(foo_producer.root().as_str(), "foo");
3724 }
3725
3726 #[tokio::test]
3727 async fn test_consume_broadcast_with_permissions() {
3728 let origin = Origin::random().produce();
3729
3730 let _broadcast1 = origin.create_broadcast("allowed/test", announce()).unwrap();
3731 let _broadcast2 = origin.create_broadcast("notallowed/test", announce()).unwrap();
3732 settle().await;
3733
3734 let limited_consumer = origin
3736 .consume()
3737 .scope(&["allowed".into()])
3738 .expect("should create limited consumer");
3739
3740 let result = limited_consumer.get_broadcast("allowed/test");
3742 assert!(result.is_some());
3743 assert!(
3744 result
3745 .unwrap()
3746 .is_clone(&origin.consume().get_broadcast("allowed/test").unwrap())
3747 );
3748
3749 assert!(limited_consumer.get_broadcast("notallowed/test").is_none());
3751
3752 let consumer = origin.consume();
3754 assert!(consumer.get_broadcast("allowed/test").is_some());
3755 assert!(consumer.get_broadcast("notallowed/test").is_some());
3756 }
3757
3758 #[tokio::test]
3759 async fn test_nested_paths_with_permissions() {
3760 let origin = Origin::random().produce();
3761
3762 let limited_producer = origin.scope(&["a/b/c".into()]).expect("should create limited producer");
3764
3765 let _broadcast = limited_producer
3767 .create_broadcast("a/b/c", announce())
3768 .expect("publish allowed");
3769 let _keep2 = limited_producer
3770 .create_broadcast("a/b/c/d", announce())
3771 .expect("publish allowed");
3772 let _keep3 = limited_producer
3773 .create_broadcast("a/b/c/d/e", announce())
3774 .expect("publish allowed");
3775 settle().await;
3776
3777 assert!(limited_producer.create_broadcast("a", announce()).is_err());
3779 assert!(limited_producer.create_broadcast("a/b", announce()).is_err());
3780 assert!(limited_producer.create_broadcast("a/b/other", announce()).is_err());
3781 }
3782
3783 #[tokio::test]
3784 async fn test_multiple_consumers_with_different_permissions() {
3785 let origin = Origin::random().produce();
3786
3787 let _broadcast1 = origin.create_broadcast("foo/test", announce()).unwrap();
3789 let _broadcast2 = origin.create_broadcast("bar/test", announce()).unwrap();
3790 let _broadcast3 = origin.create_broadcast("baz/test", announce()).unwrap();
3791 settle().await;
3792
3793 let mut foo_consumer = origin
3795 .consume()
3796 .scope(&["foo".into()])
3797 .expect("should create foo consumer")
3798 .announced();
3799
3800 let mut bar_consumer = origin
3801 .consume()
3802 .scope(&["bar".into()])
3803 .expect("should create bar consumer")
3804 .announced();
3805
3806 let mut foobar_consumer = origin
3807 .consume()
3808 .scope(&["foo".into(), "bar".into()])
3809 .expect("should create foobar consumer")
3810 .announced();
3811
3812 foo_consumer.assert_next_some("foo/test");
3814 foo_consumer.assert_next_wait();
3815
3816 bar_consumer.assert_next_some("bar/test");
3817 bar_consumer.assert_next_wait();
3818
3819 foobar_consumer.assert_next_some("bar/test");
3820 foobar_consumer.assert_next_some("foo/test");
3821 foobar_consumer.assert_next_wait();
3822 }
3823
3824 #[tokio::test]
3825 async fn test_select_with_empty_prefix() {
3826 let origin = Origin::random().produce();
3827
3828 let demo_producer = origin.with_root("demo").expect("should create demo root");
3830 let limited_producer = demo_producer
3831 .scope(&["worm-node".into(), "foobar".into()])
3832 .expect("should create limited producer");
3833
3834 let _broadcast1 = limited_producer
3836 .create_broadcast("worm-node/test", announce())
3837 .expect("publish allowed");
3838 let _broadcast2 = limited_producer
3839 .create_broadcast("foobar/test", announce())
3840 .expect("publish allowed");
3841 settle().await;
3842
3843 let mut consumer = limited_producer
3845 .consume()
3846 .scope(&["".into()])
3847 .expect("should create consumer with empty prefix")
3848 .announced();
3849
3850 let a1 = consumer.try_next().expect("expected first announcement");
3852 let a2 = consumer.try_next().expect("expected second announcement");
3853 consumer.assert_next_wait();
3854
3855 let mut paths: Vec<_> = [&a1, &a2].iter().map(|a| a.path.to_string()).collect();
3856 paths.sort();
3857 assert_eq!(paths, ["foobar/test", "worm-node/test"]);
3858 }
3859
3860 #[tokio::test]
3861 async fn test_select_narrowing_scope() {
3862 let origin = Origin::random().produce();
3863
3864 let demo_producer = origin.with_root("demo").expect("should create demo root");
3866 let limited_producer = demo_producer
3867 .scope(&["worm-node".into(), "foobar".into()])
3868 .expect("should create limited producer");
3869
3870 let _broadcast1 = limited_producer
3872 .create_broadcast("worm-node", announce())
3873 .expect("publish allowed");
3874 let _broadcast2 = limited_producer
3875 .create_broadcast("worm-node/foo", announce())
3876 .expect("publish allowed");
3877 let _broadcast3 = limited_producer
3878 .create_broadcast("foobar/bar", announce())
3879 .expect("publish allowed");
3880 settle().await;
3881
3882 let mut worm_consumer = limited_producer
3884 .consume()
3885 .scope(&["worm-node".into()])
3886 .expect("should create worm-node consumer")
3887 .announced();
3888
3889 worm_consumer.assert_next_some("worm-node");
3891 worm_consumer.assert_next_some("worm-node/foo");
3892 worm_consumer.assert_next_wait(); let mut foo_consumer = limited_producer
3896 .consume()
3897 .scope(&["worm-node/foo".into()])
3898 .expect("should create worm-node/foo consumer")
3899 .announced();
3900
3901 foo_consumer.assert_next_some("worm-node/foo");
3902 foo_consumer.assert_next_wait(); }
3904
3905 #[tokio::test]
3906 async fn test_select_multiple_roots_with_empty_prefix() {
3907 let origin = Origin::random().produce();
3908
3909 let limited_producer = origin
3911 .scope(&["app1".into(), "app2".into(), "shared".into()])
3912 .expect("should create limited producer");
3913
3914 let _broadcast1 = limited_producer
3916 .create_broadcast("app1/data", announce())
3917 .expect("publish allowed");
3918 let _broadcast2 = limited_producer
3919 .create_broadcast("app2/config", announce())
3920 .expect("publish allowed");
3921 let _broadcast3 = limited_producer
3922 .create_broadcast("shared/resource", announce())
3923 .expect("publish allowed");
3924 settle().await;
3925
3926 let mut consumer = limited_producer
3928 .consume()
3929 .scope(&["".into()])
3930 .expect("should create consumer with empty prefix")
3931 .announced();
3932
3933 consumer.assert_next_some("app1/data");
3935 consumer.assert_next_some("app2/config");
3936 consumer.assert_next_some("shared/resource");
3937 consumer.assert_next_wait();
3938 }
3939
3940 #[tokio::test]
3941 async fn test_publish_scope_with_empty_prefix() {
3942 let origin = Origin::random().produce();
3943
3944 let limited_producer = origin
3946 .scope(&["services/api".into(), "services/web".into()])
3947 .expect("should create limited producer");
3948
3949 let same_producer = limited_producer
3951 .scope(&["".into()])
3952 .expect("should create producer with empty prefix");
3953
3954 let _broadcast = same_producer
3956 .create_broadcast("services/api", announce())
3957 .expect("publish allowed");
3958 let _keep2 = same_producer
3959 .create_broadcast("services/web", announce())
3960 .expect("publish allowed");
3961 assert!(same_producer.create_broadcast("services/db", announce()).is_err());
3962 assert!(same_producer.create_broadcast("other", announce()).is_err());
3963 }
3964
3965 #[tokio::test]
3966 async fn test_select_narrowing_to_deeper_path() {
3967 let origin = Origin::random().produce();
3968
3969 let limited_producer = origin.scope(&["org".into()]).expect("should create limited producer");
3971
3972 let _broadcast1 = limited_producer
3974 .create_broadcast("org/team1/project1", announce())
3975 .expect("publish allowed");
3976 let _broadcast2 = limited_producer
3977 .create_broadcast("org/team1/project2", announce())
3978 .expect("publish allowed");
3979 let _broadcast3 = limited_producer
3980 .create_broadcast("org/team2/project1", announce())
3981 .expect("publish allowed");
3982 settle().await;
3983
3984 let mut team2_consumer = limited_producer
3986 .consume()
3987 .scope(&["org/team2".into()])
3988 .expect("should create team2 consumer")
3989 .announced();
3990
3991 team2_consumer.assert_next_some("org/team2/project1");
3992 team2_consumer.assert_next_wait(); let mut project1_consumer = limited_producer
3996 .consume()
3997 .scope(&["org/team1/project1".into()])
3998 .expect("should create project1 consumer")
3999 .announced();
4000
4001 project1_consumer.assert_next_some("org/team1/project1");
4003 project1_consumer.assert_next_wait();
4004 }
4005
4006 #[tokio::test]
4007 async fn test_select_with_non_matching_prefix() {
4008 let origin = Origin::random().produce();
4009
4010 let limited_producer = origin
4012 .scope(&["allowed/path".into()])
4013 .expect("should create limited producer");
4014
4015 assert!(limited_producer.consume().scope(&["different/path".into()]).is_none());
4017
4018 assert!(limited_producer.scope(&["other/path".into()]).is_none());
4020 }
4021
4022 #[tokio::test]
4025 async fn test_with_root_trailing_slash_consumer() {
4026 let origin = Origin::random().produce();
4027
4028 let prefix = "some_prefix/".to_string();
4030 let mut consumer = origin.consume().with_root(prefix).unwrap().announced();
4031
4032 let _b = origin.create_broadcast("some_prefix/test", announce()).unwrap();
4033 settle().await;
4034 consumer.assert_next_some("test");
4035 }
4036
4037 #[tokio::test]
4039 async fn test_with_root_trailing_slash_producer() {
4040 let origin = Origin::random().produce();
4041
4042 let prefix = "some_prefix/".to_string();
4044 let rooted = origin.with_root(prefix).unwrap();
4045
4046 let _b = rooted.create_broadcast("test", announce()).unwrap();
4047 settle().await;
4048
4049 let mut consumer = rooted.consume().announced();
4050 consumer.assert_next_some("test");
4051 }
4052
4053 #[tokio::test]
4055 async fn test_with_root_trailing_slash_unannounce() {
4056 tokio::time::pause();
4057
4058 let origin = Origin::random().produce();
4059
4060 let prefix = "some_prefix/".to_string();
4061 let mut consumer = origin.consume().with_root(prefix).unwrap().announced();
4062
4063 let mut b = origin.create_broadcast("some_prefix/test", announce()).unwrap();
4064 settle().await;
4065 consumer.assert_next_some("test");
4066
4067 b.finish();
4069 settle().await;
4070
4071 consumer.assert_next_none("test");
4073 }
4074
4075 #[tokio::test]
4076 async fn test_select_maintains_access_with_wider_prefix() {
4077 let origin = Origin::random().produce();
4078
4079 let demo_producer = origin.with_root("demo").expect("should create demo root");
4081 let user_producer = demo_producer
4082 .scope(&["worm-node".into(), "foobar".into()])
4083 .expect("should create user producer");
4084
4085 let _broadcast1 = user_producer
4087 .create_broadcast("worm-node/data", announce())
4088 .expect("publish allowed");
4089 let _broadcast2 = user_producer
4090 .create_broadcast("foobar", announce())
4091 .expect("publish allowed");
4092 settle().await;
4093
4094 let mut consumer = user_producer
4096 .consume()
4097 .scope(&["".into()])
4098 .expect("scope with empty prefix should not fail when user has specific permissions")
4099 .announced();
4100
4101 let a1 = consumer.try_next().expect("expected first announcement");
4103 let a2 = consumer.try_next().expect("expected second announcement");
4104 consumer.assert_next_wait();
4105
4106 let mut paths: Vec<_> = [&a1, &a2].iter().map(|a| a.path.to_string()).collect();
4107 paths.sort();
4108 assert_eq!(paths, ["foobar", "worm-node/data"]);
4109
4110 let mut narrow_consumer = user_producer
4112 .consume()
4113 .scope(&["worm-node".into()])
4114 .expect("should be able to narrow scope to worm-node")
4115 .announced();
4116
4117 narrow_consumer.assert_next_some("worm-node/data");
4118 narrow_consumer.assert_next_wait(); }
4120
4121 #[tokio::test]
4122 async fn test_duplicate_prefixes_deduped() {
4123 let origin = Origin::random().produce();
4124
4125 let producer = origin
4127 .scope(&["demo".into(), "demo".into()])
4128 .expect("should create producer");
4129
4130 let _broadcast = producer
4131 .create_broadcast("demo/stream", announce())
4132 .expect("publish allowed");
4133 settle().await;
4134
4135 let mut consumer = producer.consume().announced();
4136 consumer.assert_next_some("demo/stream");
4137 consumer.assert_next_wait();
4138 }
4139
4140 #[tokio::test]
4141 async fn test_overlapping_prefixes_deduped() {
4142 let origin = Origin::random().produce();
4143
4144 let producer = origin
4146 .scope(&["demo".into(), "demo/foo".into()])
4147 .expect("should create producer");
4148
4149 let _broadcast = producer
4151 .create_broadcast("demo/bar/stream", announce())
4152 .expect("publish allowed");
4153 settle().await;
4154
4155 let mut consumer = producer.consume().announced();
4156 consumer.assert_next_some("demo/bar/stream");
4157 consumer.assert_next_wait();
4158 }
4159
4160 #[tokio::test]
4161 async fn test_overlapping_prefixes_no_duplicate_announcements() {
4162 let origin = Origin::random().produce();
4163
4164 let producer = origin
4166 .scope(&["demo".into(), "demo/foo".into()])
4167 .expect("should create producer");
4168
4169 let _broadcast = producer
4170 .create_broadcast("demo/foo/stream", announce())
4171 .expect("publish allowed");
4172 settle().await;
4173
4174 let mut consumer = producer.consume().announced();
4175 consumer.assert_next_some("demo/foo/stream");
4177 consumer.assert_next_wait();
4178 }
4179
4180 #[tokio::test]
4181 async fn test_allowed_returns_deduped_prefixes() {
4182 let origin = Origin::random().produce();
4183
4184 let producer = origin
4185 .scope(&["demo".into(), "demo/foo".into(), "anon".into()])
4186 .expect("should create producer");
4187
4188 let allowed: Vec<_> = producer.allowed().collect();
4189 assert_eq!(allowed.len(), 2, "demo/foo should be subsumed by demo");
4190 }
4191
4192 #[tokio::test]
4193 async fn test_announced_broadcast_already_announced() {
4194 let origin = Origin::random().produce();
4195
4196 let _broadcast = origin.create_broadcast("test", announce()).unwrap();
4197 settle().await;
4198
4199 let consumer = origin.consume();
4200 let result = consumer.announced_broadcast("test").await.expect("should find it");
4201 assert!(result.is_clone(&consumer.get_broadcast("test").unwrap()));
4202 }
4203
4204 #[tokio::test]
4205 async fn test_announced_broadcast_delayed() {
4206 tokio::time::pause();
4207
4208 let origin = Origin::random().produce();
4209
4210 let consumer = origin.consume();
4211
4212 let wait = tokio::spawn({
4214 let consumer = consumer.clone();
4215 async move { consumer.announced_broadcast("test").await }
4216 });
4217
4218 tokio::task::yield_now().await;
4220
4221 let _broadcast = origin.create_broadcast("test", announce()).unwrap();
4222 settle().await;
4223
4224 let result = wait.await.unwrap().expect("should find it");
4225 assert!(result.is_clone(&consumer.get_broadcast("test").unwrap()));
4226 }
4227
4228 #[tokio::test]
4229 async fn test_announced_broadcast_ignores_unrelated_paths() {
4230 tokio::time::pause();
4231
4232 let origin = Origin::random().produce();
4233
4234 let consumer = origin.consume();
4235
4236 let wait = tokio::spawn({
4237 let consumer = consumer.clone();
4238 async move { consumer.announced_broadcast("target").await }
4239 });
4240
4241 tokio::task::yield_now().await;
4242
4243 let _other = origin.create_broadcast("other", announce()).unwrap();
4245 settle().await;
4246 tokio::task::yield_now().await;
4247 assert!(!wait.is_finished(), "must not resolve on unrelated path");
4248
4249 let _target = origin.create_broadcast("target", announce()).unwrap();
4250 settle().await;
4251 let result = wait.await.unwrap().expect("should find target");
4252 assert!(result.is_clone(&consumer.get_broadcast("target").unwrap()));
4253 }
4254
4255 #[tokio::test]
4256 async fn test_announced_broadcast_skips_nested_paths() {
4257 tokio::time::pause();
4258
4259 let origin = Origin::random().produce();
4260
4261 let consumer = origin.consume();
4262
4263 let wait = tokio::spawn({
4264 let consumer = consumer.clone();
4265 async move { consumer.announced_broadcast("foo").await }
4266 });
4267
4268 tokio::task::yield_now().await;
4269
4270 let _nested = origin.create_broadcast("foo/bar", announce()).unwrap();
4272 settle().await;
4273 tokio::task::yield_now().await;
4274 assert!(!wait.is_finished(), "must not resolve on a nested path");
4275
4276 let _exact = origin.create_broadcast("foo", announce()).unwrap();
4277 settle().await;
4278 let result = wait.await.unwrap().expect("should find foo exactly");
4279 assert!(result.is_clone(&consumer.get_broadcast("foo").unwrap()));
4280 }
4281
4282 #[tokio::test]
4283 async fn test_announced_broadcast_disallowed() {
4284 let origin = Origin::random().produce();
4285 let limited = origin
4286 .consume()
4287 .scope(&["allowed".into()])
4288 .expect("should create limited");
4289
4290 assert!(limited.announced_broadcast("notallowed").await.is_none());
4292 }
4293
4294 #[tokio::test]
4295 async fn test_announced_broadcast_scope_too_narrow() {
4296 let origin = Origin::random().produce();
4299 let limited = origin
4300 .consume()
4301 .scope(&["foo/specific".into()])
4302 .expect("should create limited");
4303
4304 let result = limited
4306 .announced_broadcast("foo")
4307 .now_or_never()
4308 .expect("must not block");
4309 assert!(result.is_none());
4310 }
4311
4312 #[tokio::test]
4316 async fn test_coalesce_announce_then_unannounce() {
4317 tokio::time::pause();
4319
4320 let origin = Origin::random().produce();
4321 let mut announced = origin.consume().announced();
4322
4323 let mut broadcast = origin.create_broadcast("test", announce()).unwrap();
4324 settle().await;
4325 broadcast.finish();
4326
4327 settle().await;
4328
4329 announced.assert_next_wait();
4330 }
4331
4332 #[tokio::test]
4333 async fn test_coalesce_announce_unannounce_announce() {
4334 tokio::time::pause();
4337
4338 let origin = Origin::random().produce();
4339 let mut announced = origin.consume().announced();
4340
4341 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
4342 settle().await;
4343 broadcast1.finish();
4344 settle().await;
4345 let _broadcast2 = origin.create_broadcast("test", announce()).unwrap();
4346 settle().await;
4347
4348 announced.assert_next_some("test");
4349 announced.assert_next_wait();
4350 }
4351
4352 #[tokio::test]
4353 async fn test_coalesce_unannounce_announce_preserved() {
4354 tokio::time::pause();
4357
4358 let origin = Origin::random().produce();
4359 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
4360 settle().await;
4361
4362 let mut announced = origin.consume().announced();
4363 announced.assert_next_some("test");
4364
4365 broadcast1.finish();
4367 settle().await;
4368
4369 let _broadcast2 = origin.create_broadcast("test", announce()).unwrap();
4370 settle().await;
4371
4372 announced.assert_next_none("test");
4374 announced.assert_next_some("test");
4375 announced.assert_next_wait();
4376 }
4377
4378 #[tokio::test]
4379 async fn test_coalesce_unannounce_announce_unannounce() {
4380 tokio::time::pause();
4383
4384 let origin = Origin::random().produce();
4385 let mut broadcast1 = origin.create_broadcast("test", announce()).unwrap();
4386 settle().await;
4387
4388 let mut announced = origin.consume().announced();
4389 announced.assert_next_some("test");
4390
4391 broadcast1.finish();
4392 settle().await;
4393
4394 let mut broadcast2 = origin.create_broadcast("test", announce()).unwrap();
4395 settle().await;
4396 broadcast2.finish();
4397 settle().await;
4398
4399 announced.assert_next_none("test");
4400 announced.assert_next_wait();
4401 }
4402
4403 #[tokio::test]
4404 async fn test_coalesce_churn_bounded() {
4405 tokio::time::pause();
4410
4411 let origin = Origin::random().produce();
4412 let mut announced = origin.consume().announced();
4413
4414 for _ in 0..1000 {
4415 let mut broadcast = origin.create_broadcast("test", announce()).unwrap();
4416 settle().await;
4417 broadcast.finish();
4418 }
4419 settle().await;
4420
4421 let mut collected = Vec::new();
4422 while let Some(update) = announced.try_next() {
4423 collected.push(update);
4424 }
4425 assert!(
4426 collected.len() <= 1,
4427 "expected at most one pending update, got {}",
4428 collected.len()
4429 );
4430 assert!(
4431 collected.iter().all(|a| a.path == Path::new("test")),
4432 "unexpected path in pending updates",
4433 );
4434 }
4435
4436 #[tokio::test]
4440 async fn test_consumer_clone_is_side_effect_free() {
4441 let origin = Origin::random().produce();
4442
4443 let _broadcast1 = origin.create_broadcast("test1", announce()).unwrap();
4444 let _broadcast2 = origin.create_broadcast("test2", announce()).unwrap();
4445 settle().await;
4446
4447 let consumer = origin.consume();
4448 let mut announced = consumer.announced();
4449
4450 for _ in 0..16 {
4453 let cloned = consumer.clone();
4454 assert!(cloned.get_broadcast("test1").is_some());
4455 assert!(cloned.get_broadcast("test2").is_some());
4456 }
4457
4458 let a1 = announced.try_next().expect("first announcement");
4461 let a2 = announced.try_next().expect("second announcement");
4462 announced.assert_next_wait();
4463
4464 let mut paths: Vec<_> = [&a1, &a2].iter().map(|a| a.path.to_string()).collect();
4465 paths.sort();
4466 assert_eq!(paths, ["test1", "test2"]);
4467
4468 let mut fresh = consumer.announced();
4470 let b1 = fresh.try_next().expect("backlog: first");
4471 let b2 = fresh.try_next().expect("backlog: second");
4472 fresh.assert_next_wait();
4473
4474 let mut paths: Vec<_> = [&b1, &b2].iter().map(|a| a.path.to_string()).collect();
4475 paths.sort();
4476 assert_eq!(paths, ["test1", "test2"]);
4477 }
4478
4479 #[tokio::test]
4481 async fn dynamic_request_unroutable_without_handler() {
4482 let origin = Origin::random().produce();
4483 let consumer = origin.consume();
4484 assert!(matches!(
4485 consumer.request_broadcast("missing").await,
4486 Err(Error::Unroutable)
4487 ));
4488 }
4489
4490 #[tokio::test(start_paused = true)]
4493 async fn dynamic_request_served_not_announced() {
4494 let origin = Origin::random().produce();
4495 let mut dynamic = origin.dynamic();
4496 let consumer = origin.consume();
4497
4498 let mut announced = origin.consume().announced();
4500 announced.assert_next_wait();
4501
4502 let served = broadcast::Info::new().produce();
4503 let request_fut = consumer.request_broadcast("fallback");
4506
4507 let mut served_dynamic = served.dynamic();
4509
4510 let request = dynamic.requested_broadcast().await.unwrap();
4511 assert_eq!(request.path(), &Path::new("fallback"));
4512 request.accept(&served);
4513
4514 let broadcast = request_fut.await.unwrap();
4515 assert!(broadcast.is_clone(&served.consume()));
4516
4517 let track_fut = broadcast.track("video").unwrap().subscribe(None);
4519 let mut producer = served_dynamic.requested_track().await.unwrap().accept(None);
4520 let mut track = track_fut.await.unwrap();
4521 producer.append_group().unwrap();
4522 track.assert_group();
4523
4524 announced.assert_next_wait();
4526 }
4527
4528 #[tokio::test(start_paused = true)]
4530 async fn dynamic_request_coalesces() {
4531 let origin = Origin::random().produce();
4532 let mut dynamic = origin.dynamic();
4533 let consumer = origin.consume();
4534
4535 let f1 = consumer.request_broadcast("dup");
4537 let f2 = consumer.request_broadcast("dup");
4538
4539 let request = dynamic.requested_broadcast().await.unwrap();
4541 assert_eq!(request.path(), &Path::new("dup"));
4542 assert!(
4543 dynamic.requested_broadcast().now_or_never().is_none(),
4544 "a coalesced request must not be served twice"
4545 );
4546
4547 let served = broadcast::Info::new().produce();
4549 request.accept(&served);
4550 assert!(f1.await.unwrap().is_clone(&served.consume()));
4551 assert!(f2.await.unwrap().is_clone(&served.consume()));
4552 }
4553
4554 #[tokio::test(start_paused = true)]
4557 async fn dynamic_request_dedups_served() {
4558 let origin = Origin::random().produce();
4559 let mut dynamic = origin.dynamic();
4560 let consumer = origin.consume();
4561
4562 let request_fut = consumer.request_broadcast("fallback");
4563 let request = dynamic.requested_broadcast().await.unwrap();
4564 let served = broadcast::Info::new().produce();
4565 request.accept(&served);
4566 let first = request_fut.await.unwrap();
4567 assert!(first.is_clone(&served.consume()));
4568
4569 let second = consumer.request_broadcast("fallback").await.unwrap();
4571 assert!(second.is_clone(&served.consume()));
4572
4573 assert!(
4575 dynamic.requested_broadcast().now_or_never().is_none(),
4576 "a still-live served broadcast must not be re-requested from the handler"
4577 );
4578 }
4579
4580 #[tokio::test(start_paused = true)]
4582 async fn dynamic_request_reserves_after_close() {
4583 let origin = Origin::random().produce();
4584 let mut dynamic = origin.dynamic();
4585 let consumer = origin.consume();
4586
4587 let request_fut = consumer.request_broadcast("fallback");
4588 let request = dynamic.requested_broadcast().await.unwrap();
4589 let served = broadcast::Info::new().produce();
4590 request.accept(&served);
4591 request_fut.await.unwrap();
4592
4593 drop(served);
4595
4596 let request_fut = consumer.request_broadcast("fallback");
4598 let request = dynamic.requested_broadcast().await.unwrap();
4599 assert_eq!(request.path(), &Path::new("fallback"));
4600 let served = broadcast::Info::new().produce();
4601 request.accept(&served);
4602 assert!(request_fut.await.unwrap().is_clone(&served.consume()));
4603 }
4604
4605 #[tokio::test(start_paused = true)]
4608 async fn dynamic_request_served_cache_bounded() {
4609 let origin = Origin::random().produce();
4610 let mut dynamic = origin.dynamic();
4611 let consumer = origin.consume();
4612
4613 for i in 0..100 {
4614 let path = format!("one-shot/{i}");
4615 let request_fut = consumer.request_broadcast(&path);
4616 let request = dynamic.requested_broadcast().await.unwrap();
4617 let served = broadcast::Info::new().produce();
4618 request.accept(&served);
4619 request_fut.await.unwrap();
4620 drop(served);
4622 }
4623
4624 assert!(
4627 origin.dynamic.read().served.len() <= 4,
4628 "stale served entries must be reclaimed, not accumulate per distinct path: {}",
4629 origin.dynamic.read().served.len()
4630 );
4631 }
4632
4633 #[tokio::test(start_paused = true)]
4636 async fn dynamic_request_coalesces_after_handoff() {
4637 let origin = Origin::random().produce();
4638 let mut dynamic = origin.dynamic();
4639 let consumer = origin.consume();
4640
4641 let f1 = consumer.request_broadcast("fallback");
4642 let request = dynamic.requested_broadcast().await.unwrap();
4644
4645 let f2 = consumer.request_broadcast("fallback");
4647 assert!(
4648 dynamic.requested_broadcast().now_or_never().is_none(),
4649 "a repeat request during hand-off must coalesce, not re-queue"
4650 );
4651
4652 let served = broadcast::Info::new().produce();
4654 request.accept(&served);
4655 assert!(f1.await.unwrap().is_clone(&served.consume()));
4656 assert!(f2.await.unwrap().is_clone(&served.consume()));
4657 }
4658
4659 #[tokio::test(start_paused = true)]
4661 async fn dynamic_request_dropped_after_handoff() {
4662 let origin = Origin::random().produce();
4663 let mut dynamic = origin.dynamic();
4664 let consumer = origin.consume();
4665
4666 let f1 = consumer.request_broadcast("fallback");
4667 let request = dynamic.requested_broadcast().await.unwrap();
4668 let f2 = consumer.request_broadcast("fallback");
4669
4670 drop(request);
4672 assert!(matches!(f1.await, Err(Error::Unroutable)));
4673 assert!(matches!(f2.await, Err(Error::Unroutable)));
4674 }
4675
4676 #[tokio::test(start_paused = true)]
4678 async fn dynamic_request_rejected() {
4679 let origin = Origin::random().produce();
4680 let mut dynamic = origin.dynamic();
4681 let consumer = origin.consume();
4682
4683 let request_fut = consumer.request_broadcast("fallback");
4684
4685 let request = dynamic.requested_broadcast().await.unwrap();
4686 request.reject(Error::Cancel);
4687
4688 assert!(matches!(request_fut.await, Err(Error::Cancel)));
4689 }
4690
4691 #[tokio::test(start_paused = true)]
4695 async fn dynamic_request_rerequest_after_reject() {
4696 let origin = Origin::random().produce();
4697 let mut dynamic = origin.dynamic();
4698 let consumer = origin.consume();
4699
4700 let f1 = consumer.request_broadcast("fallback");
4701 dynamic.requested_broadcast().await.unwrap().reject(Error::Unroutable);
4702 assert!(matches!(f1.await, Err(Error::Unroutable)));
4703
4704 let served = broadcast::Info::new().produce();
4705 let f2 = consumer.request_broadcast("fallback");
4707 let request = dynamic.requested_broadcast().await.unwrap();
4708 assert_eq!(request.path(), &Path::new("fallback"));
4709 request.accept(&served);
4710 assert!(f2.await.unwrap().is_clone(&served.consume()));
4711 }
4712
4713 #[tokio::test(start_paused = true)]
4716 async fn dynamic_request_handler_dropped() {
4717 let origin = Origin::random().produce();
4718 let dynamic = origin.dynamic();
4719 let consumer = origin.consume();
4720
4721 let request_fut = consumer.request_broadcast("fallback");
4722 drop(dynamic);
4723 assert!(matches!(request_fut.await, Err(Error::Unroutable)));
4724
4725 assert!(matches!(
4727 consumer.request_broadcast("again").await,
4728 Err(Error::Unroutable)
4729 ));
4730 }
4731
4732 #[tokio::test(start_paused = true)]
4736 async fn dynamic_request_accept_after_handler_dropped() {
4737 let origin = Origin::random().produce();
4738 let mut dynamic = origin.dynamic();
4739 let consumer = origin.consume();
4740
4741 let request_fut = consumer.request_broadcast("fallback");
4742
4743 let request = dynamic.requested_broadcast().await.unwrap();
4745 drop(dynamic);
4746
4747 let served = broadcast::Info::new().produce();
4748 request.accept(&served);
4750 assert!(request_fut.await.unwrap().is_clone(&served.consume()));
4751 }
4752
4753 #[tokio::test(start_paused = true)]
4755 async fn dynamic_request_prefers_announced() {
4756 let origin = Origin::random().produce();
4757 let mut dynamic = origin.dynamic();
4758 let consumer = origin.consume();
4759
4760 let _broadcast = origin.create_broadcast("live", announce()).unwrap();
4761 settle().await;
4762
4763 let got = consumer.request_broadcast("live").await.unwrap();
4764 assert!(
4765 got.is_clone(&consumer.get_broadcast("live").unwrap()),
4766 "should return the published broadcast"
4767 );
4768 assert!(
4769 dynamic.requested_broadcast().now_or_never().is_none(),
4770 "a published path must not queue a fallback request"
4771 );
4772 }
4773
4774 #[tokio::test(start_paused = true)]
4776 async fn dynamic_clone_keeps_alive() {
4777 let origin = Origin::random().produce();
4778 let dynamic = origin.dynamic();
4779 let consumer = origin.consume();
4780
4781 drop(dynamic.clone());
4782
4783 let request_fut = consumer.request_broadcast("fallback");
4786 assert!(
4787 request_fut.now_or_never().is_none(),
4788 "request should stay pending until served"
4789 );
4790 }
4791}