1#![forbid(unsafe_code)]
4
5use std::cmp::Reverse;
6use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
7use std::hash::Hash;
8use std::io;
9use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
10use std::sync::{Arc, Mutex};
11
12use sectorsync_core::prelude::{ClientId, StationId, Tick};
13
14const HASHED_BOUNDED_SET_MIN_CAPACITY: usize = 256;
15const HASHED_ENDPOINT_MAP_MIN_ENTRIES: usize = 2_048;
16const IN_MEMORY_BATCH_LOCK_PACKETS: usize = 64;
17const RETRY_DEADLINE_COMPACTION_MULTIPLE: usize = 4;
18
19#[derive(Clone, Debug)]
20enum AdaptiveEndpointMap<K, V> {
21 Ordered(BTreeMap<K, V>),
22 Hashed(HashMap<K, V>),
23}
24
25impl<K: Copy + Eq + Hash + Ord, V> AdaptiveEndpointMap<K, V> {
26 fn new() -> Self {
27 Self::Ordered(BTreeMap::new())
28 }
29
30 fn insert(&mut self, key: K, value: V) -> Option<V> {
31 let promote = match self {
32 Self::Ordered(entries) => {
33 entries.len() >= HASHED_ENDPOINT_MAP_MIN_ENTRIES.saturating_sub(1)
34 && !entries.contains_key(&key)
35 }
36 Self::Hashed(_) => false,
37 };
38 if promote {
39 let Self::Ordered(ordered) = std::mem::replace(self, Self::Hashed(HashMap::new()))
40 else {
41 unreachable!("promotion starts from ordered endpoint storage");
42 };
43 let mut hashed = HashMap::with_capacity(ordered.len().saturating_add(1));
44 hashed.extend(ordered);
45 *self = Self::Hashed(hashed);
46 }
47 match self {
48 Self::Ordered(entries) => entries.insert(key, value),
49 Self::Hashed(entries) => entries.insert(key, value),
50 }
51 }
52
53 fn get(&self, key: &K) -> Option<&V> {
54 match self {
55 Self::Ordered(entries) => entries.get(key),
56 Self::Hashed(entries) => entries.get(key),
57 }
58 }
59
60 fn get_mut(&mut self, key: &K) -> Option<&mut V> {
61 match self {
62 Self::Ordered(entries) => entries.get_mut(key),
63 Self::Hashed(entries) => entries.get_mut(key),
64 }
65 }
66
67 fn remove(&mut self, key: &K) -> Option<V> {
68 match self {
69 Self::Ordered(entries) => entries.remove(key),
70 Self::Hashed(entries) => entries.remove(key),
71 }
72 }
73
74 fn values(&self) -> AdaptiveEndpointValues<'_, K, V> {
75 match self {
76 Self::Ordered(entries) => AdaptiveEndpointValues::Ordered(entries.values()),
77 Self::Hashed(entries) => AdaptiveEndpointValues::Hashed(entries.values()),
78 }
79 }
80
81 #[cfg(test)]
82 fn is_hashed(&self) -> bool {
83 matches!(self, Self::Hashed(_))
84 }
85}
86
87enum AdaptiveEndpointValues<'a, K, V> {
88 Ordered(std::collections::btree_map::Values<'a, K, V>),
89 Hashed(std::collections::hash_map::Values<'a, K, V>),
90}
91
92impl<'a, K, V> Iterator for AdaptiveEndpointValues<'a, K, V> {
93 type Item = &'a V;
94
95 fn next(&mut self) -> Option<Self::Item> {
96 match self {
97 Self::Ordered(values) => values.next(),
98 Self::Hashed(values) => values.next(),
99 }
100 }
101
102 fn size_hint(&self) -> (usize, Option<usize>) {
103 match self {
104 Self::Ordered(values) => values.size_hint(),
105 Self::Hashed(values) => values.size_hint(),
106 }
107 }
108}
109
110impl<K, V> ExactSizeIterator for AdaptiveEndpointValues<'_, K, V> {}
111
112#[derive(Clone, Debug)]
113enum BoundedLookupSet<K> {
114 Ordered(BTreeSet<K>),
115 Hashed(HashSet<K>),
116}
117
118impl<K: Copy + Eq + Hash + Ord> BoundedLookupSet<K> {
119 fn new(max_entries: usize) -> Self {
120 if max_entries >= HASHED_BOUNDED_SET_MIN_CAPACITY {
121 Self::Hashed(HashSet::new())
122 } else {
123 Self::Ordered(BTreeSet::new())
124 }
125 }
126
127 fn len(&self) -> usize {
128 match self {
129 Self::Ordered(entries) => entries.len(),
130 Self::Hashed(entries) => entries.len(),
131 }
132 }
133
134 fn is_empty(&self) -> bool {
135 match self {
136 Self::Ordered(entries) => entries.is_empty(),
137 Self::Hashed(entries) => entries.is_empty(),
138 }
139 }
140
141 fn contains(&self, key: &K) -> bool {
142 match self {
143 Self::Ordered(entries) => entries.contains(key),
144 Self::Hashed(entries) => entries.contains(key),
145 }
146 }
147
148 fn insert(&mut self, key: K) {
149 match self {
150 Self::Ordered(entries) => {
151 entries.insert(key);
152 }
153 Self::Hashed(entries) => {
154 entries.insert(key);
155 }
156 }
157 }
158
159 fn remove(&mut self, key: &K) {
160 match self {
161 Self::Ordered(entries) => {
162 entries.remove(key);
163 }
164 Self::Hashed(entries) => {
165 entries.remove(key);
166 }
167 }
168 }
169
170 #[cfg(test)]
171 fn is_hashed(&self) -> bool {
172 matches!(self, Self::Hashed(_))
173 }
174}
175
176pub const DEFAULT_UDP_RECV_BUFFER_SIZE: usize = 16 * 1024;
178
179#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct OutboundPacket {
182 pub client_id: ClientId,
184 pub bytes: Vec<u8>,
186}
187
188#[derive(Clone, Debug, PartialEq, Eq)]
190pub struct InboundPacket {
191 pub client_id: Option<ClientId>,
193 pub remote_addr: SocketAddr,
195 pub bytes: Vec<u8>,
197}
198
199#[derive(Clone, Copy, Debug, PartialEq, Eq)]
201pub struct InboundPacketRef<'a> {
202 pub client_id: Option<ClientId>,
204 pub remote_addr: SocketAddr,
206 pub bytes: &'a [u8],
208}
209
210impl InboundPacketRef<'_> {
211 pub fn to_owned(self) -> InboundPacket {
213 InboundPacket {
214 client_id: self.client_id,
215 remote_addr: self.remote_addr,
216 bytes: self.bytes.to_vec(),
217 }
218 }
219}
220
221#[derive(Clone, Debug, PartialEq, Eq)]
223pub struct StationOutboundPacket {
224 pub source_station: StationId,
226 pub target_station: StationId,
228 pub bytes: Vec<u8>,
230}
231
232#[derive(Clone, Debug, PartialEq, Eq)]
234pub struct StationInboundPacket {
235 pub source_station: StationId,
237 pub target_station: StationId,
239 pub bytes: Vec<u8>,
241}
242
243#[derive(Clone, Copy, Debug, PartialEq, Eq)]
245pub struct StationInboundPacketRef<'a> {
246 pub source_station: StationId,
248 pub target_station: StationId,
250 pub bytes: &'a [u8],
252}
253
254impl StationInboundPacketRef<'_> {
255 pub fn to_owned(self) -> StationInboundPacket {
257 StationInboundPacket {
258 source_station: self.source_station,
259 target_station: self.target_station,
260 bytes: self.bytes.to_vec(),
261 }
262 }
263}
264
265#[derive(Clone, Debug, Default, PartialEq, Eq)]
267pub struct StationPacketBatch {
268 pub packets: Vec<StationOutboundPacket>,
270}
271
272impl StationPacketBatch {
273 pub const fn new() -> Self {
275 Self {
276 packets: Vec::new(),
277 }
278 }
279
280 pub fn push(&mut self, packet: StationOutboundPacket) {
282 self.packets.push(packet);
283 }
284
285 pub fn len(&self) -> usize {
287 self.packets.len()
288 }
289
290 pub fn is_empty(&self) -> bool {
292 self.packets.is_empty()
293 }
294
295 pub fn bytes_len(&self) -> usize {
297 self.packets.iter().map(|packet| packet.bytes.len()).sum()
298 }
299}
300
301#[derive(Clone, Debug, Default, PartialEq, Eq)]
303pub struct PacketBatch {
304 pub packets: Vec<OutboundPacket>,
306}
307
308impl PacketBatch {
309 pub const fn new() -> Self {
311 Self {
312 packets: Vec::new(),
313 }
314 }
315
316 pub fn push(&mut self, packet: OutboundPacket) {
318 self.packets.push(packet);
319 }
320
321 pub fn len(&self) -> usize {
323 self.packets.len()
324 }
325
326 pub fn is_empty(&self) -> bool {
328 self.packets.is_empty()
329 }
330
331 pub fn bytes_len(&self) -> usize {
333 self.packets.iter().map(|packet| packet.bytes.len()).sum()
334 }
335}
336
337pub trait TransportSink {
340 type Error;
342
343 fn send(&mut self, packet: OutboundPacket) -> Result<(), Self::Error>;
345
346 fn send_batch(&mut self, batch: PacketBatch) -> Result<(), Self::Error> {
348 for packet in batch.packets {
349 self.send(packet)?;
350 }
351 Ok(())
352 }
353}
354
355pub trait TransportReceiver {
357 type Error;
359
360 fn try_recv(&mut self) -> Result<Option<InboundPacket>, Self::Error>;
365}
366
367const PACKET_SECURITY_MAGIC: [u8; 4] = *b"SSEC";
368pub const PACKET_SECURITY_HEADER_BYTES: usize = 22;
370pub const DEFAULT_PACKET_SECURITY_MAX_PAYLOAD_BYTES: usize =
373 (16 * 1024) - PACKET_SECURITY_HEADER_BYTES;
374pub const DEFAULT_PACKET_SECURITY_MAX_TAG_BYTES: usize = 128;
376pub const DEFAULT_PACKET_SECURITY_REPLAY_HISTORY: usize = 4096;
378pub const DEFAULT_PACKET_KEY_RING_MAX_KEYS: usize = 32;
380
381#[derive(Clone, Copy, Debug, PartialEq, Eq)]
383pub struct PacketSecurityConfig {
384 pub max_payload_bytes: usize,
386 pub max_tag_bytes: usize,
388 pub max_replay_history: usize,
390}
391
392impl Default for PacketSecurityConfig {
393 fn default() -> Self {
394 Self {
395 max_payload_bytes: DEFAULT_PACKET_SECURITY_MAX_PAYLOAD_BYTES,
396 max_tag_bytes: DEFAULT_PACKET_SECURITY_MAX_TAG_BYTES,
397 max_replay_history: DEFAULT_PACKET_SECURITY_REPLAY_HISTORY,
398 }
399 }
400}
401
402#[derive(Clone, Copy, Debug, PartialEq, Eq)]
404pub enum PacketKeyState {
405 Active,
407 Retiring,
409 Revoked,
411}
412
413impl PacketKeyState {
414 fn can_send(self) -> bool {
415 self == Self::Active
416 }
417
418 fn can_accept(self) -> bool {
419 matches!(self, Self::Active | Self::Retiring)
420 }
421}
422
423#[derive(Clone, Copy, Debug, PartialEq, Eq)]
428pub struct PacketKeyDescriptor {
429 pub key_id: u32,
431 pub state: PacketKeyState,
433 pub created_at: Tick,
435 pub activated_at: Tick,
437 pub retires_at: Option<Tick>,
439 pub expires_at: Option<Tick>,
441 pub send_priority: u32,
443}
444
445impl PacketKeyDescriptor {
446 pub const fn active(key_id: u32, now: Tick, send_priority: u32) -> Self {
448 Self {
449 key_id,
450 state: PacketKeyState::Active,
451 created_at: now,
452 activated_at: now,
453 retires_at: None,
454 expires_at: None,
455 send_priority,
456 }
457 }
458
459 #[must_use]
461 pub const fn with_expiry(mut self, expires_at: Tick) -> Self {
462 self.expires_at = Some(expires_at);
463 self
464 }
465
466 pub fn is_send_eligible(self, now: Tick) -> bool {
468 self.state.can_send() && self.is_activated_at(now) && !self.is_expired_at(now)
469 }
470
471 pub fn is_accept_eligible(self, now: Tick) -> bool {
473 self.state.can_accept() && self.is_activated_at(now) && !self.is_expired_at(now)
474 }
475
476 fn is_activated_at(self, now: Tick) -> bool {
477 now >= self.activated_at
478 }
479
480 fn is_expired_at(self, now: Tick) -> bool {
481 self.expires_at.is_some_and(|expires_at| now >= expires_at)
482 }
483}
484
485#[derive(Clone, Copy, Debug, PartialEq, Eq)]
487pub struct PacketKeyRingConfig {
488 pub max_keys: usize,
490}
491
492impl Default for PacketKeyRingConfig {
493 fn default() -> Self {
494 Self {
495 max_keys: DEFAULT_PACKET_KEY_RING_MAX_KEYS,
496 }
497 }
498}
499
500#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
502pub struct PacketKeyRingStats {
503 pub keys_inserted: usize,
505 pub keys_activated: usize,
507 pub keys_retired: usize,
509 pub keys_revoked: usize,
511 pub keys_expired_removed: usize,
513}
514
515#[derive(Clone, Copy, Debug, PartialEq, Eq)]
517pub enum PacketKeyRingError {
518 CapacityFull {
520 capacity: usize,
522 },
523 DuplicateKey(u32),
525 MissingKey(u32),
527 NoSendKey,
529 KeyNotSendable {
531 key_id: u32,
533 state: PacketKeyState,
535 },
536 KeyNotAccepted {
538 key_id: u32,
540 state: PacketKeyState,
542 },
543}
544
545impl core::fmt::Display for PacketKeyRingError {
546 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
547 match self {
548 Self::CapacityFull { capacity } => {
549 write!(f, "packet key ring capacity full: capacity {capacity}")
550 }
551 Self::DuplicateKey(key_id) => write!(f, "packet key {key_id} already exists"),
552 Self::MissingKey(key_id) => write!(f, "packet key {key_id} is missing"),
553 Self::NoSendKey => f.write_str("no packet key is eligible for send"),
554 Self::KeyNotSendable { key_id, state } => {
555 write!(f, "packet key {key_id} is not sendable in state {state:?}")
556 }
557 Self::KeyNotAccepted { key_id, state } => {
558 write!(f, "packet key {key_id} is not accepted in state {state:?}")
559 }
560 }
561 }
562}
563
564impl std::error::Error for PacketKeyRingError {}
565
566#[derive(Clone, Debug)]
568pub struct PacketKeyRing {
569 config: PacketKeyRingConfig,
570 keys: BTreeMap<u32, PacketKeyDescriptor>,
571 stats: PacketKeyRingStats,
572}
573
574impl PacketKeyRing {
575 pub fn new(config: PacketKeyRingConfig) -> Self {
577 Self {
578 config,
579 keys: BTreeMap::new(),
580 stats: PacketKeyRingStats::default(),
581 }
582 }
583
584 pub fn with_defaults() -> Self {
586 Self::new(PacketKeyRingConfig::default())
587 }
588
589 pub const fn config(&self) -> PacketKeyRingConfig {
591 self.config
592 }
593
594 pub fn len(&self) -> usize {
596 self.keys.len()
597 }
598
599 pub fn is_empty(&self) -> bool {
601 self.keys.is_empty()
602 }
603
604 pub const fn stats(&self) -> PacketKeyRingStats {
606 self.stats
607 }
608
609 pub fn get(&self, key_id: u32) -> Option<PacketKeyDescriptor> {
611 self.keys.get(&key_id).copied()
612 }
613
614 pub fn iter(&self) -> impl Iterator<Item = &PacketKeyDescriptor> {
616 self.keys.values()
617 }
618
619 pub fn insert(&mut self, descriptor: PacketKeyDescriptor) -> Result<(), PacketKeyRingError> {
621 if self.keys.contains_key(&descriptor.key_id) {
622 return Err(PacketKeyRingError::DuplicateKey(descriptor.key_id));
623 }
624 if self.keys.len() >= self.config.max_keys {
625 return Err(PacketKeyRingError::CapacityFull {
626 capacity: self.config.max_keys,
627 });
628 }
629 self.keys.insert(descriptor.key_id, descriptor);
630 self.stats.keys_inserted = self.stats.keys_inserted.saturating_add(1);
631 Ok(())
632 }
633
634 pub fn insert_active(
636 &mut self,
637 key_id: u32,
638 now: Tick,
639 send_priority: u32,
640 ) -> Result<(), PacketKeyRingError> {
641 self.insert(PacketKeyDescriptor::active(key_id, now, send_priority))
642 }
643
644 pub fn activate(&mut self, key_id: u32, activated_at: Tick) -> Result<(), PacketKeyRingError> {
646 let descriptor = self
647 .keys
648 .get_mut(&key_id)
649 .ok_or(PacketKeyRingError::MissingKey(key_id))?;
650 descriptor.state = PacketKeyState::Active;
651 descriptor.activated_at = activated_at;
652 descriptor.retires_at = None;
653 self.stats.keys_activated = self.stats.keys_activated.saturating_add(1);
654 Ok(())
655 }
656
657 pub fn retire(&mut self, key_id: u32, retires_at: Tick) -> Result<(), PacketKeyRingError> {
659 let descriptor = self
660 .keys
661 .get_mut(&key_id)
662 .ok_or(PacketKeyRingError::MissingKey(key_id))?;
663 descriptor.state = PacketKeyState::Retiring;
664 descriptor.retires_at = Some(retires_at);
665 self.stats.keys_retired = self.stats.keys_retired.saturating_add(1);
666 Ok(())
667 }
668
669 pub fn revoke(&mut self, key_id: u32) -> Result<(), PacketKeyRingError> {
671 let descriptor = self
672 .keys
673 .get_mut(&key_id)
674 .ok_or(PacketKeyRingError::MissingKey(key_id))?;
675 descriptor.state = PacketKeyState::Revoked;
676 self.stats.keys_revoked = self.stats.keys_revoked.saturating_add(1);
677 Ok(())
678 }
679
680 pub fn set_expiry(
682 &mut self,
683 key_id: u32,
684 expires_at: Option<Tick>,
685 ) -> Result<(), PacketKeyRingError> {
686 let descriptor = self
687 .keys
688 .get_mut(&key_id)
689 .ok_or(PacketKeyRingError::MissingKey(key_id))?;
690 descriptor.expires_at = expires_at;
691 Ok(())
692 }
693
694 pub fn remove_expired(&mut self, now: Tick) -> usize {
696 let before = self.keys.len();
697 self.keys
698 .retain(|_, descriptor| !descriptor.is_expired_at(now));
699 let removed = before.saturating_sub(self.keys.len());
700 self.stats.keys_expired_removed = self.stats.keys_expired_removed.saturating_add(removed);
701 removed
702 }
703
704 pub fn select_send_key(&self, now: Tick) -> Result<PacketKeyDescriptor, PacketKeyRingError> {
706 self.keys
707 .values()
708 .copied()
709 .filter(|descriptor| descriptor.is_send_eligible(now))
710 .max_by_key(|descriptor| {
711 (
712 descriptor.send_priority,
713 descriptor.activated_at,
714 descriptor.key_id,
715 )
716 })
717 .ok_or(PacketKeyRingError::NoSendKey)
718 }
719
720 pub fn accept_key(
722 &self,
723 key_id: u32,
724 now: Tick,
725 ) -> Result<PacketKeyDescriptor, PacketKeyRingError> {
726 let descriptor = self
727 .keys
728 .get(&key_id)
729 .copied()
730 .ok_or(PacketKeyRingError::MissingKey(key_id))?;
731 if descriptor.is_accept_eligible(now) {
732 Ok(descriptor)
733 } else {
734 Err(PacketKeyRingError::KeyNotAccepted {
735 key_id,
736 state: descriptor.state,
737 })
738 }
739 }
740}
741
742impl Default for PacketKeyRing {
743 fn default() -> Self {
744 Self::with_defaults()
745 }
746}
747
748#[derive(Clone, Copy, Debug, PartialEq, Eq)]
750pub struct PacketSecurityEnvelopeRef<'a> {
751 pub key_id: u32,
753 pub nonce: u64,
755 pub payload: &'a [u8],
757 pub tag: &'a [u8],
759}
760
761impl<'a> PacketSecurityEnvelopeRef<'a> {
762 pub fn decode(
764 config: PacketSecurityConfig,
765 input: &'a [u8],
766 ) -> Result<Self, PacketSecurityDecodeError> {
767 let mut cursor = SecurityCursor::new(input);
768 let magic = cursor.read_array::<4>()?;
769 if magic != PACKET_SECURITY_MAGIC {
770 return Err(PacketSecurityDecodeError::BadMagic);
771 }
772 let key_id = cursor.read_u32()?;
773 let nonce = cursor.read_u64()?;
774 let payload_len = cursor.read_u32()? as usize;
775 let tag_len = cursor.read_u16()? as usize;
776 if payload_len > config.max_payload_bytes {
777 return Err(PacketSecurityDecodeError::PayloadTooLarge {
778 budget: config.max_payload_bytes,
779 actual: payload_len,
780 });
781 }
782 if tag_len > config.max_tag_bytes {
783 return Err(PacketSecurityDecodeError::TagTooLarge {
784 budget: config.max_tag_bytes,
785 actual: tag_len,
786 });
787 }
788 let payload = cursor.read_slice(payload_len)?;
789 let tag = cursor.read_slice(tag_len)?;
790 cursor.finish()?;
791 Ok(Self {
792 key_id,
793 nonce,
794 payload,
795 tag,
796 })
797 }
798}
799
800#[derive(Clone, Debug, PartialEq, Eq)]
802pub struct PacketSecurityEnvelope {
803 pub key_id: u32,
805 pub nonce: u64,
807 pub payload: Vec<u8>,
809 pub tag: Vec<u8>,
811}
812
813impl PacketSecurityEnvelope {
814 pub fn encode_parts(
816 config: PacketSecurityConfig,
817 key_id: u32,
818 nonce: u64,
819 payload: &[u8],
820 tag: &[u8],
821 out: &mut Vec<u8>,
822 ) -> Result<(), PacketSecurityEncodeError> {
823 if payload.len() > config.max_payload_bytes {
824 return Err(PacketSecurityEncodeError::PayloadTooLarge {
825 budget: config.max_payload_bytes,
826 actual: payload.len(),
827 });
828 }
829 if tag.len() > config.max_tag_bytes {
830 return Err(PacketSecurityEncodeError::TagTooLarge {
831 budget: config.max_tag_bytes,
832 actual: tag.len(),
833 });
834 }
835
836 let payload_len = u32::try_from(payload.len()).map_err(|_| {
837 PacketSecurityEncodeError::PayloadTooLarge {
838 budget: config.max_payload_bytes,
839 actual: payload.len(),
840 }
841 })?;
842 let tag_len =
843 u16::try_from(tag.len()).map_err(|_| PacketSecurityEncodeError::TagTooLarge {
844 budget: config.max_tag_bytes,
845 actual: tag.len(),
846 })?;
847 out.extend_from_slice(&PACKET_SECURITY_MAGIC);
848 out.extend_from_slice(&key_id.to_le_bytes());
849 out.extend_from_slice(&nonce.to_le_bytes());
850 out.extend_from_slice(&payload_len.to_le_bytes());
851 out.extend_from_slice(&tag_len.to_le_bytes());
852 out.extend_from_slice(payload);
853 out.extend_from_slice(tag);
854 Ok(())
855 }
856
857 pub fn encode(
859 &self,
860 config: PacketSecurityConfig,
861 out: &mut Vec<u8>,
862 ) -> Result<(), PacketSecurityEncodeError> {
863 Self::encode_parts(
864 config,
865 self.key_id,
866 self.nonce,
867 &self.payload,
868 &self.tag,
869 out,
870 )
871 }
872
873 pub fn decode(
875 config: PacketSecurityConfig,
876 input: &[u8],
877 ) -> Result<Self, PacketSecurityDecodeError> {
878 let envelope = PacketSecurityEnvelopeRef::decode(config, input)?;
879 Ok(Self {
880 key_id: envelope.key_id,
881 nonce: envelope.nonce,
882 payload: envelope.payload.to_vec(),
883 tag: envelope.tag.to_vec(),
884 })
885 }
886}
887
888#[derive(Clone, Copy, Debug, PartialEq, Eq)]
890pub enum PacketSecurityEncodeError {
891 PayloadTooLarge {
893 budget: usize,
895 actual: usize,
897 },
898 TagTooLarge {
900 budget: usize,
902 actual: usize,
904 },
905}
906
907impl core::fmt::Display for PacketSecurityEncodeError {
908 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
909 match self {
910 Self::PayloadTooLarge { budget, actual } => write!(
911 f,
912 "packet security payload exceeded byte budget: budget {budget}, actual {actual}"
913 ),
914 Self::TagTooLarge { budget, actual } => write!(
915 f,
916 "packet security tag exceeded byte budget: budget {budget}, actual {actual}"
917 ),
918 }
919 }
920}
921
922impl std::error::Error for PacketSecurityEncodeError {}
923
924#[derive(Clone, Debug, PartialEq, Eq)]
926pub enum PacketSecurityDecodeError {
927 BadMagic,
929 Truncated {
931 needed: usize,
933 available: usize,
935 },
936 PayloadTooLarge {
938 budget: usize,
940 actual: usize,
942 },
943 TagTooLarge {
945 budget: usize,
947 actual: usize,
949 },
950 TrailingBytes(usize),
952}
953
954impl core::fmt::Display for PacketSecurityDecodeError {
955 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
956 match self {
957 Self::BadMagic => f.write_str("bad packet security envelope magic"),
958 Self::Truncated { needed, available } => {
959 write!(
960 f,
961 "truncated packet security envelope: needed {needed}, available {available}"
962 )
963 }
964 Self::PayloadTooLarge { budget, actual } => write!(
965 f,
966 "packet security payload exceeded byte budget: budget {budget}, actual {actual}"
967 ),
968 Self::TagTooLarge { budget, actual } => write!(
969 f,
970 "packet security tag exceeded byte budget: budget {budget}, actual {actual}"
971 ),
972 Self::TrailingBytes(bytes) => {
973 write!(f, "packet security envelope has {bytes} trailing bytes")
974 }
975 }
976 }
977}
978
979impl std::error::Error for PacketSecurityDecodeError {}
980
981pub trait PacketAuthenticator {
984 type Error;
986
987 fn sign(
989 &mut self,
990 key_id: u32,
991 nonce: u64,
992 payload: &[u8],
993 out: &mut Vec<u8>,
994 ) -> Result<(), Self::Error>;
995
996 fn verify(
998 &mut self,
999 key_id: u32,
1000 nonce: u64,
1001 payload: &[u8],
1002 tag: &[u8],
1003 ) -> Result<bool, Self::Error>;
1004}
1005
1006pub trait PacketCipher {
1009 type Error;
1011
1012 fn seal(&mut self, key_id: u32, nonce: u64, payload: &mut Vec<u8>) -> Result<(), Self::Error>;
1014
1015 fn open(&mut self, key_id: u32, nonce: u64, payload: &mut Vec<u8>) -> Result<(), Self::Error>;
1017}
1018
1019#[derive(Clone, Copy, Debug, Default)]
1022pub struct PlaintextPacketCipher;
1023
1024impl PacketCipher for PlaintextPacketCipher {
1025 type Error = core::convert::Infallible;
1026
1027 fn seal(
1028 &mut self,
1029 _key_id: u32,
1030 _nonce: u64,
1031 _payload: &mut Vec<u8>,
1032 ) -> Result<(), Self::Error> {
1033 Ok(())
1034 }
1035
1036 fn open(
1037 &mut self,
1038 _key_id: u32,
1039 _nonce: u64,
1040 _payload: &mut Vec<u8>,
1041 ) -> Result<(), Self::Error> {
1042 Ok(())
1043 }
1044}
1045
1046#[derive(Clone, Debug)]
1048pub struct PacketReplayWindow {
1049 max_seen: usize,
1050 seen: BoundedLookupSet<(u32, u64)>,
1051 order: VecDeque<(u32, u64)>,
1052}
1053
1054impl PacketReplayWindow {
1055 pub fn new(max_seen: usize) -> Self {
1057 Self {
1058 max_seen,
1059 seen: BoundedLookupSet::new(max_seen),
1060 order: VecDeque::new(),
1061 }
1062 }
1063
1064 pub fn len(&self) -> usize {
1066 self.seen.len()
1067 }
1068
1069 pub fn is_empty(&self) -> bool {
1071 self.seen.is_empty()
1072 }
1073
1074 pub fn contains(&self, key_id: u32, nonce: u64) -> bool {
1076 self.seen.contains(&(key_id, nonce))
1077 }
1078
1079 pub fn accept(&mut self, key_id: u32, nonce: u64) -> bool {
1081 if self.max_seen == 0 {
1082 return true;
1083 }
1084
1085 let key = (key_id, nonce);
1086 if self.seen.contains(&key) {
1087 return false;
1088 }
1089 self.seen.insert(key);
1090 self.order.push_back(key);
1091 while self.order.len() > self.max_seen {
1092 if let Some(old) = self.order.pop_front() {
1093 self.seen.remove(&old);
1094 }
1095 }
1096 true
1097 }
1098}
1099
1100impl Default for PacketReplayWindow {
1101 fn default() -> Self {
1102 Self::new(DEFAULT_PACKET_SECURITY_REPLAY_HISTORY)
1103 }
1104}
1105
1106#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1108pub struct PacketSecurityStats {
1109 pub sealed: usize,
1111 pub opened: usize,
1113 pub key_rejected: usize,
1115 pub auth_failed: usize,
1117 pub replay_rejected: usize,
1119}
1120
1121#[derive(Clone, Debug, Default)]
1123pub struct PacketSecurityScratch {
1124 sealed_payload: Vec<u8>,
1125 tag: Vec<u8>,
1126}
1127
1128impl PacketSecurityScratch {
1129 pub fn new() -> Self {
1131 Self::default()
1132 }
1133
1134 pub fn with_capacity(payload_bytes: usize, tag_bytes: usize) -> Self {
1136 Self {
1137 sealed_payload: Vec::with_capacity(payload_bytes),
1138 tag: Vec::with_capacity(tag_bytes),
1139 }
1140 }
1141
1142 pub fn retained_payload_capacity(&self) -> usize {
1144 self.sealed_payload.capacity()
1145 }
1146
1147 pub fn retained_tag_capacity(&self) -> usize {
1149 self.tag.capacity()
1150 }
1151}
1152
1153#[derive(Clone, Debug, Default)]
1155pub struct PacketSecurityOpenScratch {
1156 payload: Vec<u8>,
1157}
1158
1159impl PacketSecurityOpenScratch {
1160 pub fn new() -> Self {
1162 Self::default()
1163 }
1164
1165 pub fn with_capacity(payload_bytes: usize) -> Self {
1167 Self {
1168 payload: Vec::with_capacity(payload_bytes),
1169 }
1170 }
1171
1172 pub fn retained_payload_capacity(&self) -> usize {
1174 self.payload.capacity()
1175 }
1176}
1177
1178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1180pub struct PacketSecurityOpenView<'a> {
1181 pub key_id: u32,
1183 pub nonce: u64,
1185 pub payload: &'a [u8],
1187}
1188
1189#[derive(Debug)]
1191pub enum PacketSecurityError<A, C> {
1192 Encode(PacketSecurityEncodeError),
1194 Decode(PacketSecurityDecodeError),
1196 Authenticator(A),
1198 Cipher(C),
1200 Key(PacketKeyRingError),
1202 AuthenticationFailed {
1204 key_id: u32,
1206 nonce: u64,
1208 },
1209 Replay {
1211 key_id: u32,
1213 nonce: u64,
1215 },
1216}
1217
1218impl<A: core::fmt::Display, C: core::fmt::Display> core::fmt::Display
1219 for PacketSecurityError<A, C>
1220{
1221 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1222 match self {
1223 Self::Encode(error) => write!(f, "{error}"),
1224 Self::Decode(error) => write!(f, "{error}"),
1225 Self::Authenticator(error) => write!(f, "{error}"),
1226 Self::Cipher(error) => write!(f, "{error}"),
1227 Self::Key(error) => write!(f, "{error}"),
1228 Self::AuthenticationFailed { key_id, nonce } => write!(
1229 f,
1230 "packet authentication failed for key {key_id} nonce {nonce}"
1231 ),
1232 Self::Replay { key_id, nonce } => {
1233 write!(f, "packet replay rejected for key {key_id} nonce {nonce}")
1234 }
1235 }
1236 }
1237}
1238
1239impl<A, C> std::error::Error for PacketSecurityError<A, C>
1240where
1241 A: std::error::Error + 'static,
1242 C: std::error::Error + 'static,
1243{
1244 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1245 match self {
1246 Self::Encode(error) => Some(error),
1247 Self::Decode(error) => Some(error),
1248 Self::Authenticator(error) => Some(error),
1249 Self::Cipher(error) => Some(error),
1250 Self::Key(error) => Some(error),
1251 Self::AuthenticationFailed { .. } | Self::Replay { .. } => None,
1252 }
1253 }
1254}
1255
1256#[derive(Clone, Debug)]
1259pub struct PacketSecurityBox<A, C> {
1260 config: PacketSecurityConfig,
1261 authenticator: A,
1262 cipher: C,
1263 replay: PacketReplayWindow,
1264 next_nonce: BTreeMap<u32, u64>,
1265 stats: PacketSecurityStats,
1266}
1267
1268impl<A, C> PacketSecurityBox<A, C> {
1269 pub fn new(config: PacketSecurityConfig, authenticator: A, cipher: C) -> Self {
1271 Self {
1272 config,
1273 authenticator,
1274 cipher,
1275 replay: PacketReplayWindow::new(config.max_replay_history),
1276 next_nonce: BTreeMap::new(),
1277 stats: PacketSecurityStats::default(),
1278 }
1279 }
1280
1281 pub const fn config(&self) -> PacketSecurityConfig {
1283 self.config
1284 }
1285
1286 pub const fn stats(&self) -> PacketSecurityStats {
1288 self.stats
1289 }
1290
1291 pub const fn replay(&self) -> &PacketReplayWindow {
1293 &self.replay
1294 }
1295
1296 pub fn into_inner(self) -> (A, C, PacketReplayWindow) {
1298 (self.authenticator, self.cipher, self.replay)
1299 }
1300}
1301
1302impl<A, C> PacketSecurityBox<A, C>
1303where
1304 A: PacketAuthenticator,
1305 C: PacketCipher,
1306{
1307 pub fn seal(
1309 &mut self,
1310 key_id: u32,
1311 payload: &[u8],
1312 ) -> Result<Vec<u8>, PacketSecurityError<A::Error, C::Error>> {
1313 let nonce = self.allocate_nonce(key_id);
1314 self.seal_with_nonce(key_id, nonce, payload)
1315 }
1316
1317 pub fn seal_with_key_ring(
1319 &mut self,
1320 key_ring: &PacketKeyRing,
1321 payload: &[u8],
1322 now: Tick,
1323 ) -> Result<Vec<u8>, PacketSecurityError<A::Error, C::Error>> {
1324 let descriptor = key_ring
1325 .select_send_key(now)
1326 .map_err(PacketSecurityError::Key)?;
1327 self.seal(descriptor.key_id, payload)
1328 }
1329
1330 pub fn seal_with_nonce(
1332 &mut self,
1333 key_id: u32,
1334 nonce: u64,
1335 payload: &[u8],
1336 ) -> Result<Vec<u8>, PacketSecurityError<A::Error, C::Error>> {
1337 let mut scratch = PacketSecurityScratch::with_capacity(
1338 payload.len().min(self.config.max_payload_bytes),
1339 0,
1340 );
1341 self.prepare_seal(key_id, nonce, payload, &mut scratch)?;
1342 let mut out = Vec::with_capacity(
1343 PACKET_SECURITY_HEADER_BYTES
1344 .saturating_add(scratch.sealed_payload.len())
1345 .saturating_add(scratch.tag.len()),
1346 );
1347 PacketSecurityEnvelope::encode_parts(
1348 self.config,
1349 key_id,
1350 nonce,
1351 &scratch.sealed_payload,
1352 &scratch.tag,
1353 &mut out,
1354 )
1355 .map_err(PacketSecurityError::Encode)?;
1356 self.stats.sealed = self.stats.sealed.saturating_add(1);
1357 Ok(out)
1358 }
1359
1360 pub fn seal_into(
1362 &mut self,
1363 key_id: u32,
1364 payload: &[u8],
1365 out: &mut Vec<u8>,
1366 scratch: &mut PacketSecurityScratch,
1367 ) -> Result<u64, PacketSecurityError<A::Error, C::Error>> {
1368 let nonce = self.allocate_nonce(key_id);
1369 self.seal_with_nonce_into(key_id, nonce, payload, out, scratch)?;
1370 Ok(nonce)
1371 }
1372
1373 pub fn seal_with_key_ring_into(
1375 &mut self,
1376 key_ring: &PacketKeyRing,
1377 payload: &[u8],
1378 now: Tick,
1379 out: &mut Vec<u8>,
1380 scratch: &mut PacketSecurityScratch,
1381 ) -> Result<(u32, u64), PacketSecurityError<A::Error, C::Error>> {
1382 let descriptor = key_ring
1383 .select_send_key(now)
1384 .map_err(PacketSecurityError::Key)?;
1385 let nonce = self.seal_into(descriptor.key_id, payload, out, scratch)?;
1386 Ok((descriptor.key_id, nonce))
1387 }
1388
1389 pub fn seal_with_nonce_into(
1391 &mut self,
1392 key_id: u32,
1393 nonce: u64,
1394 payload: &[u8],
1395 out: &mut Vec<u8>,
1396 scratch: &mut PacketSecurityScratch,
1397 ) -> Result<(), PacketSecurityError<A::Error, C::Error>> {
1398 self.prepare_seal(key_id, nonce, payload, scratch)?;
1399 PacketSecurityEnvelope::encode_parts(
1400 self.config,
1401 key_id,
1402 nonce,
1403 &scratch.sealed_payload,
1404 &scratch.tag,
1405 out,
1406 )
1407 .map_err(PacketSecurityError::Encode)?;
1408 self.stats.sealed = self.stats.sealed.saturating_add(1);
1409 Ok(())
1410 }
1411
1412 fn prepare_seal(
1413 &mut self,
1414 key_id: u32,
1415 nonce: u64,
1416 payload: &[u8],
1417 scratch: &mut PacketSecurityScratch,
1418 ) -> Result<(), PacketSecurityError<A::Error, C::Error>> {
1419 if payload.len() > self.config.max_payload_bytes {
1420 return Err(PacketSecurityError::Encode(
1421 PacketSecurityEncodeError::PayloadTooLarge {
1422 budget: self.config.max_payload_bytes,
1423 actual: payload.len(),
1424 },
1425 ));
1426 }
1427 scratch.sealed_payload.clear();
1428 scratch.sealed_payload.extend_from_slice(payload);
1429 self.cipher
1430 .seal(key_id, nonce, &mut scratch.sealed_payload)
1431 .map_err(PacketSecurityError::Cipher)?;
1432 scratch.tag.clear();
1433 self.authenticator
1434 .sign(key_id, nonce, &scratch.sealed_payload, &mut scratch.tag)
1435 .map_err(PacketSecurityError::Authenticator)?;
1436 Ok(())
1437 }
1438
1439 pub fn open(
1441 &mut self,
1442 input: &[u8],
1443 ) -> Result<Vec<u8>, PacketSecurityError<A::Error, C::Error>> {
1444 let envelope = PacketSecurityEnvelopeRef::decode(self.config, input)
1445 .map_err(PacketSecurityError::Decode)?;
1446 self.open_borrowed_owned(envelope)
1447 }
1448
1449 pub fn open_with_key_ring(
1451 &mut self,
1452 key_ring: &PacketKeyRing,
1453 input: &[u8],
1454 now: Tick,
1455 ) -> Result<Vec<u8>, PacketSecurityError<A::Error, C::Error>> {
1456 let envelope = PacketSecurityEnvelopeRef::decode(self.config, input)
1457 .map_err(PacketSecurityError::Decode)?;
1458 if let Err(error) = key_ring.accept_key(envelope.key_id, now) {
1459 self.stats.key_rejected = self.stats.key_rejected.saturating_add(1);
1460 return Err(PacketSecurityError::Key(error));
1461 }
1462 self.open_borrowed_owned(envelope)
1463 }
1464
1465 pub fn open_with_scratch<'a>(
1467 &mut self,
1468 input: &[u8],
1469 scratch: &'a mut PacketSecurityOpenScratch,
1470 ) -> Result<PacketSecurityOpenView<'a>, PacketSecurityError<A::Error, C::Error>> {
1471 let envelope = PacketSecurityEnvelopeRef::decode(self.config, input)
1472 .map_err(PacketSecurityError::Decode)?;
1473 self.open_borrowed_with_scratch(envelope, scratch)
1474 }
1475
1476 pub fn open_with_key_ring_and_scratch<'a>(
1478 &mut self,
1479 key_ring: &PacketKeyRing,
1480 input: &[u8],
1481 now: Tick,
1482 scratch: &'a mut PacketSecurityOpenScratch,
1483 ) -> Result<PacketSecurityOpenView<'a>, PacketSecurityError<A::Error, C::Error>> {
1484 let envelope = PacketSecurityEnvelopeRef::decode(self.config, input)
1485 .map_err(PacketSecurityError::Decode)?;
1486 if let Err(error) = key_ring.accept_key(envelope.key_id, now) {
1487 self.stats.key_rejected = self.stats.key_rejected.saturating_add(1);
1488 return Err(PacketSecurityError::Key(error));
1489 }
1490 self.open_borrowed_with_scratch(envelope, scratch)
1491 }
1492
1493 fn open_borrowed_owned(
1494 &mut self,
1495 envelope: PacketSecurityEnvelopeRef<'_>,
1496 ) -> Result<Vec<u8>, PacketSecurityError<A::Error, C::Error>> {
1497 self.verify_and_accept(envelope)?;
1498 let mut payload = envelope.payload.to_vec();
1499 self.cipher
1500 .open(envelope.key_id, envelope.nonce, &mut payload)
1501 .map_err(PacketSecurityError::Cipher)?;
1502 self.stats.opened = self.stats.opened.saturating_add(1);
1503 Ok(payload)
1504 }
1505
1506 fn open_borrowed_with_scratch<'a>(
1507 &mut self,
1508 envelope: PacketSecurityEnvelopeRef<'_>,
1509 scratch: &'a mut PacketSecurityOpenScratch,
1510 ) -> Result<PacketSecurityOpenView<'a>, PacketSecurityError<A::Error, C::Error>> {
1511 self.verify_and_accept(envelope)?;
1512 scratch.payload.clear();
1513 scratch.payload.extend_from_slice(envelope.payload);
1514 self.cipher
1515 .open(envelope.key_id, envelope.nonce, &mut scratch.payload)
1516 .map_err(PacketSecurityError::Cipher)?;
1517 self.stats.opened = self.stats.opened.saturating_add(1);
1518 Ok(PacketSecurityOpenView {
1519 key_id: envelope.key_id,
1520 nonce: envelope.nonce,
1521 payload: &scratch.payload,
1522 })
1523 }
1524
1525 fn verify_and_accept(
1526 &mut self,
1527 envelope: PacketSecurityEnvelopeRef<'_>,
1528 ) -> Result<(), PacketSecurityError<A::Error, C::Error>> {
1529 let verified = self
1530 .authenticator
1531 .verify(
1532 envelope.key_id,
1533 envelope.nonce,
1534 envelope.payload,
1535 envelope.tag,
1536 )
1537 .map_err(PacketSecurityError::Authenticator)?;
1538 if !verified {
1539 self.stats.auth_failed = self.stats.auth_failed.saturating_add(1);
1540 return Err(PacketSecurityError::AuthenticationFailed {
1541 key_id: envelope.key_id,
1542 nonce: envelope.nonce,
1543 });
1544 }
1545 if !self.replay.accept(envelope.key_id, envelope.nonce) {
1546 self.stats.replay_rejected = self.stats.replay_rejected.saturating_add(1);
1547 return Err(PacketSecurityError::Replay {
1548 key_id: envelope.key_id,
1549 nonce: envelope.nonce,
1550 });
1551 }
1552 Ok(())
1553 }
1554
1555 fn allocate_nonce(&mut self, key_id: u32) -> u64 {
1556 let next = self.next_nonce.entry(key_id).or_insert(1);
1557 let nonce = *next;
1558 *next = next.saturating_add(1);
1559 nonce
1560 }
1561}
1562
1563struct SecurityCursor<'a> {
1564 input: &'a [u8],
1565 offset: usize,
1566}
1567
1568impl<'a> SecurityCursor<'a> {
1569 const fn new(input: &'a [u8]) -> Self {
1570 Self { input, offset: 0 }
1571 }
1572
1573 fn read_u16(&mut self) -> Result<u16, PacketSecurityDecodeError> {
1574 let bytes = self.read_array::<2>()?;
1575 Ok(u16::from_le_bytes(bytes))
1576 }
1577
1578 fn read_u32(&mut self) -> Result<u32, PacketSecurityDecodeError> {
1579 let bytes = self.read_array::<4>()?;
1580 Ok(u32::from_le_bytes(bytes))
1581 }
1582
1583 fn read_u64(&mut self) -> Result<u64, PacketSecurityDecodeError> {
1584 let bytes = self.read_array::<8>()?;
1585 Ok(u64::from_le_bytes(bytes))
1586 }
1587
1588 fn read_array<const N: usize>(&mut self) -> Result<[u8; N], PacketSecurityDecodeError> {
1589 self.require(N)?;
1590 let mut out = [0_u8; N];
1591 out.copy_from_slice(&self.input[self.offset..self.offset + N]);
1592 self.offset += N;
1593 Ok(out)
1594 }
1595
1596 fn read_slice(&mut self, len: usize) -> Result<&'a [u8], PacketSecurityDecodeError> {
1597 self.require(len)?;
1598 let bytes = &self.input[self.offset..self.offset + len];
1599 self.offset += len;
1600 Ok(bytes)
1601 }
1602
1603 fn require(&self, count: usize) -> Result<(), PacketSecurityDecodeError> {
1604 let needed = self.offset.saturating_add(count);
1605 if needed > self.input.len() {
1606 Err(PacketSecurityDecodeError::Truncated {
1607 needed,
1608 available: self.input.len(),
1609 })
1610 } else {
1611 Ok(())
1612 }
1613 }
1614
1615 fn finish(&self) -> Result<(), PacketSecurityDecodeError> {
1616 if self.offset == self.input.len() {
1617 Ok(())
1618 } else {
1619 Err(PacketSecurityDecodeError::TrailingBytes(
1620 self.input.len().saturating_sub(self.offset),
1621 ))
1622 }
1623 }
1624}
1625
1626#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1628pub struct ClientTransportLimits {
1629 pub max_queued_packets_per_client: usize,
1631 pub max_packet_bytes: usize,
1633}
1634
1635impl Default for ClientTransportLimits {
1636 fn default() -> Self {
1637 Self {
1638 max_queued_packets_per_client: 4096,
1639 max_packet_bytes: 16 * 1024,
1640 }
1641 }
1642}
1643
1644#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1646pub struct InMemoryTransportStats {
1647 pub packets_sent: usize,
1649 pub packets_received: usize,
1651 pub bytes_sent: usize,
1653 pub bytes_received: usize,
1655 pub packets_rejected_full: usize,
1657 pub packets_rejected_bytes: usize,
1659}
1660
1661#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1663pub enum InMemoryTransportError {
1664 MissingLocal(ClientId),
1666 MissingTarget(ClientId),
1668 QueueFull {
1670 client_id: ClientId,
1672 capacity: usize,
1674 },
1675 PacketTooLarge {
1677 budget: usize,
1679 actual: usize,
1681 },
1682 Poisoned,
1684}
1685
1686impl core::fmt::Display for InMemoryTransportError {
1687 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1688 match self {
1689 Self::MissingLocal(client_id) => {
1690 write!(
1691 f,
1692 "in-memory transport local client {} is missing",
1693 client_id.get()
1694 )
1695 }
1696 Self::MissingTarget(client_id) => {
1697 write!(
1698 f,
1699 "in-memory transport target client {} is missing",
1700 client_id.get()
1701 )
1702 }
1703 Self::QueueFull {
1704 client_id,
1705 capacity,
1706 } => write!(
1707 f,
1708 "in-memory transport target client {} queue is full at capacity {capacity}",
1709 client_id.get()
1710 ),
1711 Self::PacketTooLarge { budget, actual } => {
1712 write!(
1713 f,
1714 "in-memory transport packet exceeded byte budget: budget {budget}, actual {actual}"
1715 )
1716 }
1717 Self::Poisoned => f.write_str("in-memory transport state is poisoned"),
1718 }
1719 }
1720}
1721
1722impl std::error::Error for InMemoryTransportError {}
1723
1724#[derive(Clone, Debug)]
1725struct InMemoryTransportClient {
1726 remote_addr: SocketAddr,
1727 registration_token: u64,
1728 queue: VecDeque<InboundPacket>,
1729}
1730
1731#[derive(Debug)]
1732struct InMemoryTransportInner {
1733 limits: ClientTransportLimits,
1734 clients: AdaptiveEndpointMap<ClientId, InMemoryTransportClient>,
1735 addr_to_client: HashMap<SocketAddr, ClientId>,
1736 stats: InMemoryTransportStats,
1737 next_registration_token: u64,
1738}
1739
1740#[derive(Clone, Debug)]
1742pub struct InMemoryTransportHub {
1743 inner: Arc<Mutex<InMemoryTransportInner>>,
1744}
1745
1746#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1748pub struct InMemoryClientUnregister {
1749 pub remote_addr: SocketAddr,
1751 pub queued_packets: usize,
1753 pub retained_queue_capacity: usize,
1755}
1756
1757impl InMemoryTransportHub {
1758 pub fn new(limits: ClientTransportLimits) -> Self {
1760 Self {
1761 inner: Arc::new(Mutex::new(InMemoryTransportInner {
1762 limits,
1763 clients: AdaptiveEndpointMap::new(),
1764 addr_to_client: HashMap::new(),
1765 stats: InMemoryTransportStats::default(),
1766 next_registration_token: 1,
1767 })),
1768 }
1769 }
1770
1771 pub fn register_client(
1773 &self,
1774 client_id: ClientId,
1775 remote_addr: SocketAddr,
1776 ) -> Result<Option<SocketAddr>, InMemoryTransportError> {
1777 let mut inner = self.lock_inner()?;
1778 let registration_token = inner.next_registration_token;
1779 inner.next_registration_token = inner.next_registration_token.wrapping_add(1).max(1);
1780 let old_addr = inner.clients.insert(
1781 client_id,
1782 InMemoryTransportClient {
1783 remote_addr,
1784 registration_token,
1785 queue: VecDeque::new(),
1786 },
1787 );
1788 let previous_addr = old_addr.map(|client| client.remote_addr);
1789 if let Some(previous_addr) = previous_addr {
1790 inner.addr_to_client.remove(&previous_addr);
1791 }
1792 if let Some(old_client) = inner.addr_to_client.insert(remote_addr, client_id)
1793 && old_client != client_id
1794 {
1795 inner.clients.remove(&old_client);
1796 }
1797 Ok(previous_addr)
1798 }
1799
1800 pub fn endpoint(
1802 &self,
1803 client_id: ClientId,
1804 remote_addr: SocketAddr,
1805 ) -> Result<InMemoryTransportEndpoint, InMemoryTransportError> {
1806 self.register_client(client_id, remote_addr)?;
1807 Ok(self.endpoint_for_registered(client_id))
1808 }
1809
1810 pub fn endpoint_for_registered(&self, client_id: ClientId) -> InMemoryTransportEndpoint {
1813 let registration_token = self.lock_inner().ok().and_then(|inner| {
1814 inner
1815 .clients
1816 .get(&client_id)
1817 .map(|client| client.registration_token)
1818 });
1819 InMemoryTransportEndpoint {
1820 local_client_id: client_id,
1821 registration_token,
1822 hub: self.clone(),
1823 }
1824 }
1825
1826 pub fn unregister_client(
1828 &self,
1829 client_id: ClientId,
1830 ) -> Result<Option<InMemoryClientUnregister>, InMemoryTransportError> {
1831 let mut inner = self.lock_inner()?;
1832 let Some(client) = inner.clients.remove(&client_id) else {
1833 return Ok(None);
1834 };
1835 inner.addr_to_client.remove(&client.remote_addr);
1836 Ok(Some(InMemoryClientUnregister {
1837 remote_addr: client.remote_addr,
1838 queued_packets: client.queue.len(),
1839 retained_queue_capacity: client.queue.capacity(),
1840 }))
1841 }
1842
1843 pub fn queued_len(&self, client_id: ClientId) -> Result<Option<usize>, InMemoryTransportError> {
1845 let inner = self.lock_inner()?;
1846 Ok(inner
1847 .clients
1848 .get(&client_id)
1849 .map(|client| client.queue.len()))
1850 }
1851
1852 pub fn queued_capacity(
1854 &self,
1855 client_id: ClientId,
1856 ) -> Result<Option<usize>, InMemoryTransportError> {
1857 let inner = self.lock_inner()?;
1858 Ok(inner
1859 .clients
1860 .get(&client_id)
1861 .map(|client| client.queue.capacity()))
1862 }
1863
1864 pub fn retained_queue_capacity(&self) -> Result<usize, InMemoryTransportError> {
1866 let inner = self.lock_inner()?;
1867 Ok(inner
1868 .clients
1869 .values()
1870 .map(|client| client.queue.capacity())
1871 .sum())
1872 }
1873
1874 pub fn limits(&self) -> Result<ClientTransportLimits, InMemoryTransportError> {
1876 let inner = self.lock_inner()?;
1877 Ok(inner.limits)
1878 }
1879
1880 pub fn stats(&self) -> Result<InMemoryTransportStats, InMemoryTransportError> {
1882 let inner = self.lock_inner()?;
1883 Ok(inner.stats)
1884 }
1885
1886 fn lock_inner(
1887 &self,
1888 ) -> Result<std::sync::MutexGuard<'_, InMemoryTransportInner>, InMemoryTransportError> {
1889 self.inner
1890 .lock()
1891 .map_err(|_| InMemoryTransportError::Poisoned)
1892 }
1893}
1894
1895impl Default for InMemoryTransportHub {
1896 fn default() -> Self {
1897 Self::new(ClientTransportLimits::default())
1898 }
1899}
1900
1901#[derive(Clone, Debug)]
1903pub struct InMemoryTransportEndpoint {
1904 local_client_id: ClientId,
1905 registration_token: Option<u64>,
1906 hub: InMemoryTransportHub,
1907}
1908
1909impl InMemoryTransportEndpoint {
1910 pub const fn local_client_id(&self) -> ClientId {
1912 self.local_client_id
1913 }
1914
1915 pub fn local_addr(&self) -> Result<Option<SocketAddr>, InMemoryTransportError> {
1917 let inner = self.hub.lock_inner()?;
1918 let client = inner.clients.get(&self.local_client_id);
1919 if client.map(|client| client.registration_token) != self.registration_token {
1920 return Err(InMemoryTransportError::MissingLocal(self.local_client_id));
1921 }
1922 Ok(client.map(|client| client.remote_addr))
1923 }
1924
1925 fn send_locked(
1926 inner: &mut InMemoryTransportInner,
1927 local_client_id: ClientId,
1928 registration_token: Option<u64>,
1929 packet: OutboundPacket,
1930 ) -> Result<(), InMemoryTransportError> {
1931 let actual = packet.bytes.len();
1932 let limits = inner.limits;
1933 if actual > limits.max_packet_bytes {
1934 inner.stats.packets_rejected_bytes =
1935 inner.stats.packets_rejected_bytes.saturating_add(1);
1936 return Err(InMemoryTransportError::PacketTooLarge {
1937 budget: limits.max_packet_bytes,
1938 actual,
1939 });
1940 }
1941
1942 let source_addr = inner
1943 .clients
1944 .get(&local_client_id)
1945 .filter(|client| Some(client.registration_token) == registration_token)
1946 .ok_or(InMemoryTransportError::MissingLocal(local_client_id))?
1947 .remote_addr;
1948 let target = inner
1949 .clients
1950 .get_mut(&packet.client_id)
1951 .ok_or(InMemoryTransportError::MissingTarget(packet.client_id))?;
1952 if target.queue.len() >= limits.max_queued_packets_per_client {
1953 inner.stats.packets_rejected_full = inner.stats.packets_rejected_full.saturating_add(1);
1954 return Err(InMemoryTransportError::QueueFull {
1955 client_id: packet.client_id,
1956 capacity: limits.max_queued_packets_per_client,
1957 });
1958 }
1959 target.queue.push_back(InboundPacket {
1960 client_id: Some(local_client_id),
1961 remote_addr: source_addr,
1962 bytes: packet.bytes,
1963 });
1964 inner.stats.packets_sent = inner.stats.packets_sent.saturating_add(1);
1965 inner.stats.bytes_sent = inner.stats.bytes_sent.saturating_add(actual);
1966 Ok(())
1967 }
1968}
1969
1970impl TransportSink for InMemoryTransportEndpoint {
1971 type Error = InMemoryTransportError;
1972
1973 fn send(&mut self, packet: OutboundPacket) -> Result<(), Self::Error> {
1974 let mut inner = self.hub.lock_inner()?;
1975 Self::send_locked(
1976 &mut inner,
1977 self.local_client_id,
1978 self.registration_token,
1979 packet,
1980 )
1981 }
1982
1983 fn send_batch(&mut self, batch: PacketBatch) -> Result<(), Self::Error> {
1984 let mut packets = batch.packets.into_iter().peekable();
1985 while packets.peek().is_some() {
1986 let mut inner = self.hub.lock_inner()?;
1987 for _ in 0..IN_MEMORY_BATCH_LOCK_PACKETS {
1988 let Some(packet) = packets.next() else {
1989 return Ok(());
1990 };
1991 Self::send_locked(
1992 &mut inner,
1993 self.local_client_id,
1994 self.registration_token,
1995 packet,
1996 )?;
1997 }
1998 }
1999 Ok(())
2000 }
2001}
2002
2003impl TransportReceiver for InMemoryTransportEndpoint {
2004 type Error = InMemoryTransportError;
2005
2006 fn try_recv(&mut self) -> Result<Option<InboundPacket>, Self::Error> {
2007 let mut inner = self.hub.lock_inner()?;
2008 let local = inner
2009 .clients
2010 .get_mut(&self.local_client_id)
2011 .filter(|client| Some(client.registration_token) == self.registration_token)
2012 .ok_or(InMemoryTransportError::MissingLocal(self.local_client_id))?;
2013 let Some(packet) = local.queue.pop_front() else {
2014 return Ok(None);
2015 };
2016 inner.stats.packets_received = inner.stats.packets_received.saturating_add(1);
2017 inner.stats.bytes_received = inner
2018 .stats
2019 .bytes_received
2020 .saturating_add(packet.bytes.len());
2021 Ok(Some(packet))
2022 }
2023}
2024
2025const RELIABLE_CLIENT_MAGIC: [u8; 4] = *b"SSCR";
2026const RELIABLE_CLIENT_KIND_DATA: u8 = 0;
2027const RELIABLE_CLIENT_KIND_ACK: u8 = 1;
2028pub const RELIABLE_CLIENT_DATA_HEADER_BYTES: usize = 17;
2030pub const RELIABLE_CLIENT_ACK_BYTES: usize = 13;
2032pub const DEFAULT_RELIABLE_CLIENT_MAX_PAYLOAD_BYTES: usize =
2035 (16 * 1024) - RELIABLE_CLIENT_DATA_HEADER_BYTES;
2036pub const DEFAULT_RELIABLE_CLIENT_DELIVERED_HISTORY: usize = 4096;
2038
2039#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2041pub struct ReliableClientConfig {
2042 pub max_in_flight_per_peer: usize,
2044 pub retry_after_ticks: u64,
2046 pub max_attempts: u8,
2048 pub max_payload_bytes: usize,
2050 pub max_delivered_history: usize,
2053}
2054
2055impl Default for ReliableClientConfig {
2056 fn default() -> Self {
2057 Self {
2058 max_in_flight_per_peer: 1024,
2059 retry_after_ticks: 2,
2060 max_attempts: 4,
2061 max_payload_bytes: DEFAULT_RELIABLE_CLIENT_MAX_PAYLOAD_BYTES,
2062 max_delivered_history: DEFAULT_RELIABLE_CLIENT_DELIVERED_HISTORY,
2063 }
2064 }
2065}
2066
2067#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
2069pub struct ReliableClientStats {
2070 pub data_sent: usize,
2072 pub retries_sent: usize,
2074 pub acks_sent: usize,
2076 pub acks_received: usize,
2078 pub data_delivered: usize,
2080 pub duplicates_suppressed: usize,
2082 pub timed_out: usize,
2084}
2085
2086#[derive(Clone, Debug, PartialEq, Eq)]
2088pub enum ReliableClientFrame {
2089 Data {
2091 sequence: u64,
2093 payload: Vec<u8>,
2095 },
2096 Ack {
2098 sequence: u64,
2100 },
2101}
2102
2103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2105pub enum ReliableClientFrameRef<'a> {
2106 Data {
2108 sequence: u64,
2110 payload: &'a [u8],
2112 },
2113 Ack {
2115 sequence: u64,
2117 },
2118}
2119
2120impl ReliableClientFrameRef<'_> {
2121 pub fn to_owned(self) -> ReliableClientFrame {
2123 match self {
2124 Self::Data { sequence, payload } => ReliableClientFrame::Data {
2125 sequence,
2126 payload: payload.to_vec(),
2127 },
2128 Self::Ack { sequence } => ReliableClientFrame::Ack { sequence },
2129 }
2130 }
2131}
2132
2133impl ReliableClientFrame {
2134 pub fn encode_data(
2136 sequence: u64,
2137 payload: &[u8],
2138 out: &mut Vec<u8>,
2139 ) -> Result<(), ReliableClientEncodeError> {
2140 out.extend_from_slice(&RELIABLE_CLIENT_MAGIC);
2141 out.push(RELIABLE_CLIENT_KIND_DATA);
2142 out.extend_from_slice(&sequence.to_le_bytes());
2143 let len = u32::try_from(payload.len()).map_err(|_| {
2144 ReliableClientEncodeError::PayloadTooLarge {
2145 actual: payload.len(),
2146 }
2147 })?;
2148 out.extend_from_slice(&len.to_le_bytes());
2149 out.extend_from_slice(payload);
2150 Ok(())
2151 }
2152
2153 pub fn encode(&self, out: &mut Vec<u8>) -> Result<(), ReliableClientEncodeError> {
2155 match self {
2156 Self::Data { sequence, payload } => Self::encode_data(*sequence, payload, out),
2157 Self::Ack { sequence } => {
2158 out.extend_from_slice(&RELIABLE_CLIENT_MAGIC);
2159 out.push(RELIABLE_CLIENT_KIND_ACK);
2160 out.extend_from_slice(&sequence.to_le_bytes());
2161 Ok(())
2162 }
2163 }
2164 }
2165
2166 pub fn decode(input: &[u8]) -> Result<Self, ReliableClientDecodeError> {
2168 Self::decode_ref(input).map(ReliableClientFrameRef::to_owned)
2169 }
2170
2171 pub fn decode_ref(
2173 input: &[u8],
2174 ) -> Result<ReliableClientFrameRef<'_>, ReliableClientDecodeError> {
2175 let mut cursor = ReliableCursor::new(input);
2176 let magic = cursor
2177 .read_array::<4>()
2178 .map_err(ReliableClientDecodeError::from_station_decode)?;
2179 if magic != RELIABLE_CLIENT_MAGIC {
2180 return Err(ReliableClientDecodeError::BadMagic);
2181 }
2182 let kind = cursor
2183 .read_u8()
2184 .map_err(ReliableClientDecodeError::from_station_decode)?;
2185 let sequence = cursor
2186 .read_u64()
2187 .map_err(ReliableClientDecodeError::from_station_decode)?;
2188 let frame = match kind {
2189 RELIABLE_CLIENT_KIND_DATA => {
2190 let len = cursor
2191 .read_u32()
2192 .map_err(ReliableClientDecodeError::from_station_decode)?
2193 as usize;
2194 let payload = cursor
2195 .read_slice(len)
2196 .map_err(ReliableClientDecodeError::from_station_decode)?;
2197 ReliableClientFrameRef::Data { sequence, payload }
2198 }
2199 RELIABLE_CLIENT_KIND_ACK => ReliableClientFrameRef::Ack { sequence },
2200 other => return Err(ReliableClientDecodeError::UnknownKind(other)),
2201 };
2202 cursor
2203 .finish()
2204 .map_err(ReliableClientDecodeError::from_station_decode)?;
2205 Ok(frame)
2206 }
2207}
2208
2209#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2211pub enum ReliableClientEncodeError {
2212 PayloadTooLarge {
2214 actual: usize,
2216 },
2217}
2218
2219impl core::fmt::Display for ReliableClientEncodeError {
2220 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2221 match self {
2222 Self::PayloadTooLarge { actual } => {
2223 write!(f, "reliable client payload too large: {actual} bytes")
2224 }
2225 }
2226 }
2227}
2228
2229impl std::error::Error for ReliableClientEncodeError {}
2230
2231#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2233pub enum ReliableClientDecodeError {
2234 BadMagic,
2236 UnknownKind(u8),
2238 Truncated {
2240 needed: usize,
2242 available: usize,
2244 },
2245 TrailingBytes(usize),
2247}
2248
2249impl ReliableClientDecodeError {
2250 fn from_station_decode(error: ReliableStationDecodeError) -> Self {
2251 match error {
2252 ReliableStationDecodeError::BadMagic => Self::BadMagic,
2253 ReliableStationDecodeError::UnknownKind(kind) => Self::UnknownKind(kind),
2254 ReliableStationDecodeError::Truncated { needed, available } => {
2255 Self::Truncated { needed, available }
2256 }
2257 ReliableStationDecodeError::TrailingBytes(bytes) => Self::TrailingBytes(bytes),
2258 }
2259 }
2260}
2261
2262impl core::fmt::Display for ReliableClientDecodeError {
2263 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2264 match self {
2265 Self::BadMagic => f.write_str("bad reliable client frame magic"),
2266 Self::UnknownKind(kind) => write!(f, "unknown reliable client frame kind {kind}"),
2267 Self::Truncated { needed, available } => {
2268 write!(
2269 f,
2270 "truncated reliable client frame: needed {needed}, available {available}"
2271 )
2272 }
2273 Self::TrailingBytes(bytes) => {
2274 write!(f, "reliable client frame has {bytes} trailing bytes")
2275 }
2276 }
2277 }
2278}
2279
2280impl std::error::Error for ReliableClientDecodeError {}
2281
2282#[derive(Debug)]
2284pub enum ReliableClientError<E> {
2285 Transport(E),
2287 MissingSourceClient,
2289 PayloadTooLarge {
2291 budget: usize,
2293 actual: usize,
2295 },
2296 WindowFull {
2298 peer_client: ClientId,
2300 capacity: usize,
2302 },
2303 Encode(ReliableClientEncodeError),
2305 Decode(ReliableClientDecodeError),
2307}
2308
2309impl<E: core::fmt::Display> core::fmt::Display for ReliableClientError<E> {
2310 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2311 match self {
2312 Self::Transport(error) => write!(f, "{error}"),
2313 Self::MissingSourceClient => f.write_str("reliable client packet source is unknown"),
2314 Self::PayloadTooLarge { budget, actual } => {
2315 write!(
2316 f,
2317 "reliable client payload exceeded byte budget: budget {budget}, actual {actual}"
2318 )
2319 }
2320 Self::WindowFull {
2321 peer_client,
2322 capacity,
2323 } => write!(
2324 f,
2325 "reliable client peer {} window is full at capacity {capacity}",
2326 peer_client.get()
2327 ),
2328 Self::Encode(error) => write!(f, "{error}"),
2329 Self::Decode(error) => write!(f, "{error}"),
2330 }
2331 }
2332}
2333
2334impl<E> std::error::Error for ReliableClientError<E>
2335where
2336 E: std::error::Error + 'static,
2337{
2338 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
2339 match self {
2340 Self::Transport(error) => Some(error),
2341 Self::Encode(error) => Some(error),
2342 Self::Decode(error) => Some(error),
2343 Self::MissingSourceClient | Self::PayloadTooLarge { .. } | Self::WindowFull { .. } => {
2344 None
2345 }
2346 }
2347 }
2348}
2349
2350#[derive(Clone, Debug, PartialEq, Eq)]
2351struct InFlightReliableClientPacket {
2352 peer_client: ClientId,
2353 sequence: u64,
2354 payload: Vec<u8>,
2355 first_sent_tick: u64,
2356 last_sent_tick: u64,
2357 attempts: u8,
2358}
2359
2360#[derive(Clone, Debug, Default)]
2362pub struct ReliableClientRetryScratch {
2363 due_entries: Vec<(u64, ClientId, u64)>,
2364}
2365
2366impl ReliableClientRetryScratch {
2367 pub fn new() -> Self {
2369 Self::default()
2370 }
2371
2372 pub fn retained_key_capacity(&self) -> usize {
2374 self.due_entries.capacity()
2375 }
2376}
2377
2378#[derive(Clone, Debug)]
2380pub struct ReliableClientSender {
2381 config: ReliableClientConfig,
2382 next_sequence: BTreeMap<ClientId, u64>,
2383 in_flight: BTreeMap<(ClientId, u64), InFlightReliableClientPacket>,
2384 in_flight_by_peer: BTreeMap<ClientId, usize>,
2385 retry_deadlines: BinaryHeap<Reverse<(u64, ClientId, u64)>>,
2386 stats: ReliableClientStats,
2387}
2388
2389impl ReliableClientSender {
2390 pub fn new(config: ReliableClientConfig) -> Self {
2392 Self {
2393 config,
2394 next_sequence: BTreeMap::new(),
2395 in_flight: BTreeMap::new(),
2396 in_flight_by_peer: BTreeMap::new(),
2397 retry_deadlines: BinaryHeap::new(),
2398 stats: ReliableClientStats::default(),
2399 }
2400 }
2401
2402 pub const fn config(&self) -> ReliableClientConfig {
2404 self.config
2405 }
2406
2407 pub const fn stats(&self) -> ReliableClientStats {
2409 self.stats
2410 }
2411
2412 pub fn in_flight_len(&self) -> usize {
2414 self.in_flight.len()
2415 }
2416
2417 pub fn in_flight_for(&self, peer_client: ClientId) -> usize {
2419 self.in_flight_by_peer
2420 .get(&peer_client)
2421 .copied()
2422 .unwrap_or(0)
2423 }
2424
2425 pub fn send<T: TransportSink>(
2428 &mut self,
2429 transport: &mut T,
2430 packet: OutboundPacket,
2431 now_tick: u64,
2432 ) -> Result<u64, ReliableClientError<T::Error>> {
2433 self.validate_payload(packet.bytes.len())?;
2434 if self.in_flight_for(packet.client_id) >= self.config.max_in_flight_per_peer {
2435 return Err(ReliableClientError::WindowFull {
2436 peer_client: packet.client_id,
2437 capacity: self.config.max_in_flight_per_peer,
2438 });
2439 }
2440
2441 let sequence = self.allocate_sequence(packet.client_id);
2442 Self::send_data_frame(transport, packet.client_id, sequence, &packet.bytes)?;
2443 self.insert_in_flight(
2444 (packet.client_id, sequence),
2445 InFlightReliableClientPacket {
2446 peer_client: packet.client_id,
2447 sequence,
2448 payload: packet.bytes,
2449 first_sent_tick: now_tick,
2450 last_sent_tick: now_tick,
2451 attempts: 1,
2452 },
2453 );
2454 self.schedule_retry(packet.client_id, sequence, now_tick);
2455 self.stats.data_sent = self.stats.data_sent.saturating_add(1);
2456 Ok(sequence)
2457 }
2458
2459 pub fn acknowledge(&mut self, ack_source_client: ClientId, sequence: u64) -> bool {
2461 let removed = self
2462 .remove_in_flight(&(ack_source_client, sequence))
2463 .is_some();
2464 if removed {
2465 self.stats.acks_received = self.stats.acks_received.saturating_add(1);
2466 self.compact_retry_deadlines_if_stale();
2467 }
2468 removed
2469 }
2470
2471 fn compact_retry_deadlines_if_stale(&mut self) {
2472 if self.retry_deadlines.len()
2473 > self
2474 .in_flight
2475 .len()
2476 .saturating_mul(RETRY_DEADLINE_COMPACTION_MULTIPLE)
2477 {
2478 self.retry_deadlines = self
2479 .in_flight
2480 .values()
2481 .map(|packet| {
2482 Reverse((
2483 packet
2484 .last_sent_tick
2485 .saturating_add(self.config.retry_after_ticks),
2486 packet.peer_client,
2487 packet.sequence,
2488 ))
2489 })
2490 .collect();
2491 }
2492 }
2493
2494 pub fn retry_due_into<T: TransportSink>(
2496 &mut self,
2497 transport: &mut T,
2498 now_tick: u64,
2499 scratch: &mut ReliableClientRetryScratch,
2500 ) -> Result<ReliableRetryReport, ReliableClientError<T::Error>> {
2501 scratch.due_entries.clear();
2502 while let Some(Reverse((deadline, peer, sequence))) = self.retry_deadlines.peek().copied() {
2503 if deadline > now_tick {
2504 break;
2505 }
2506 self.retry_deadlines.pop();
2507 scratch.due_entries.push((deadline, peer, sequence));
2508 }
2509 let mut report = ReliableRetryReport::default();
2510
2511 for (entry_index, (deadline, peer, sequence)) in scratch.due_entries.iter().enumerate() {
2512 let key = (*peer, *sequence);
2513 let Some(packet) = self.in_flight.get(&key) else {
2514 continue;
2515 };
2516 if packet
2517 .last_sent_tick
2518 .saturating_add(self.config.retry_after_ticks)
2519 != *deadline
2520 {
2521 continue;
2522 }
2523 report.examined = report.examined.saturating_add(1);
2524 if packet.attempts >= self.config.max_attempts {
2525 self.remove_in_flight(&key);
2526 self.stats.timed_out = self.stats.timed_out.saturating_add(1);
2527 report.timed_out = report.timed_out.saturating_add(1);
2528 continue;
2529 }
2530
2531 if let Err(error) = Self::send_data_frame(
2532 transport,
2533 packet.peer_client,
2534 packet.sequence,
2535 &packet.payload,
2536 ) {
2537 self.retry_deadlines.extend(
2538 scratch.due_entries[entry_index..]
2539 .iter()
2540 .copied()
2541 .map(Reverse),
2542 );
2543 return Err(error);
2544 }
2545 if let Some(stored) = self.in_flight.get_mut(&key) {
2546 stored.last_sent_tick = now_tick;
2547 stored.attempts = stored.attempts.saturating_add(1);
2548 }
2549 self.schedule_retry(*peer, *sequence, now_tick);
2550 self.stats.retries_sent = self.stats.retries_sent.saturating_add(1);
2551 report.retried = report.retried.saturating_add(1);
2552 }
2553
2554 Ok(report)
2555 }
2556
2557 fn validate_payload<E>(&self, bytes: usize) -> Result<(), ReliableClientError<E>> {
2558 if bytes > self.config.max_payload_bytes {
2559 Err(ReliableClientError::PayloadTooLarge {
2560 budget: self.config.max_payload_bytes,
2561 actual: bytes,
2562 })
2563 } else {
2564 Ok(())
2565 }
2566 }
2567
2568 fn allocate_sequence(&mut self, peer_client: ClientId) -> u64 {
2569 let next = self.next_sequence.entry(peer_client).or_insert(1);
2570 let sequence = *next;
2571 *next = next.saturating_add(1);
2572 sequence
2573 }
2574
2575 fn insert_in_flight(&mut self, key: (ClientId, u64), packet: InFlightReliableClientPacket) {
2576 if self.in_flight.insert(key, packet).is_none() {
2577 let count = self.in_flight_by_peer.entry(key.0).or_insert(0);
2578 *count = count.saturating_add(1);
2579 }
2580 }
2581
2582 fn schedule_retry(&mut self, peer: ClientId, sequence: u64, sent_tick: u64) {
2583 self.retry_deadlines.push(Reverse((
2584 sent_tick.saturating_add(self.config.retry_after_ticks),
2585 peer,
2586 sequence,
2587 )));
2588 }
2589
2590 fn remove_in_flight(&mut self, key: &(ClientId, u64)) -> Option<InFlightReliableClientPacket> {
2591 let removed = self.in_flight.remove(key)?;
2592 if let Some(count) = self.in_flight_by_peer.get_mut(&removed.peer_client) {
2593 *count = count.saturating_sub(1);
2594 if *count == 0 {
2595 self.in_flight_by_peer.remove(&removed.peer_client);
2596 }
2597 }
2598 Some(removed)
2599 }
2600
2601 fn send_data_frame<T: TransportSink>(
2602 transport: &mut T,
2603 peer_client: ClientId,
2604 sequence: u64,
2605 payload: &[u8],
2606 ) -> Result<(), ReliableClientError<T::Error>> {
2607 let mut bytes = Vec::with_capacity(
2608 payload
2609 .len()
2610 .saturating_add(RELIABLE_CLIENT_DATA_HEADER_BYTES),
2611 );
2612 ReliableClientFrame::encode_data(sequence, payload, &mut bytes)
2613 .map_err(ReliableClientError::Encode)?;
2614 transport
2615 .send(OutboundPacket {
2616 client_id: peer_client,
2617 bytes,
2618 })
2619 .map_err(ReliableClientError::Transport)
2620 }
2621}
2622
2623impl Default for ReliableClientSender {
2624 fn default() -> Self {
2625 Self::new(ReliableClientConfig::default())
2626 }
2627}
2628
2629#[derive(Clone, Debug)]
2631pub struct ReliableClientReceiver {
2632 config: ReliableClientConfig,
2633 delivered: BoundedLookupSet<(ClientId, u64)>,
2634 delivered_order: VecDeque<(ClientId, u64)>,
2635 stats: ReliableClientStats,
2636}
2637
2638impl ReliableClientReceiver {
2639 pub fn new(config: ReliableClientConfig) -> Self {
2641 Self {
2642 config,
2643 delivered: BoundedLookupSet::new(config.max_delivered_history),
2644 delivered_order: VecDeque::new(),
2645 stats: ReliableClientStats::default(),
2646 }
2647 }
2648
2649 pub const fn config(&self) -> ReliableClientConfig {
2651 self.config
2652 }
2653
2654 pub const fn stats(&self) -> ReliableClientStats {
2656 self.stats
2657 }
2658
2659 pub fn handle_data<T: TransportSink>(
2662 &mut self,
2663 transport: &mut T,
2664 packet: InboundPacket,
2665 source_client: ClientId,
2666 sequence: u64,
2667 payload: Vec<u8>,
2668 ) -> Result<Option<InboundPacket>, ReliableClientError<T::Error>> {
2669 let InboundPacket {
2670 remote_addr,
2671 bytes: wire_bytes,
2672 ..
2673 } = packet;
2674 drop(wire_bytes);
2675 self.send_ack(transport, source_client, sequence)?;
2676 if !self.record_unique(source_client, sequence) {
2677 self.stats.duplicates_suppressed = self.stats.duplicates_suppressed.saturating_add(1);
2678 return Ok(None);
2679 }
2680
2681 self.stats.data_delivered = self.stats.data_delivered.saturating_add(1);
2682 Ok(Some(InboundPacket {
2683 client_id: Some(source_client),
2684 remote_addr,
2685 bytes: payload,
2686 }))
2687 }
2688
2689 fn send_ack<T: TransportSink>(
2690 &mut self,
2691 transport: &mut T,
2692 target_client: ClientId,
2693 sequence: u64,
2694 ) -> Result<(), ReliableClientError<T::Error>> {
2695 let mut bytes = Vec::with_capacity(RELIABLE_CLIENT_ACK_BYTES);
2696 ReliableClientFrame::Ack { sequence }
2697 .encode(&mut bytes)
2698 .map_err(ReliableClientError::Encode)?;
2699 transport
2700 .send(OutboundPacket {
2701 client_id: target_client,
2702 bytes,
2703 })
2704 .map_err(ReliableClientError::Transport)?;
2705 self.stats.acks_sent = self.stats.acks_sent.saturating_add(1);
2706 Ok(())
2707 }
2708
2709 fn record_unique(&mut self, source_client: ClientId, sequence: u64) -> bool {
2710 if self.config.max_delivered_history == 0 {
2711 return true;
2712 }
2713
2714 let key = (source_client, sequence);
2715 if self.delivered.contains(&key) {
2716 return false;
2717 }
2718
2719 self.delivered.insert(key);
2720 self.delivered_order.push_back(key);
2721 while self.delivered_order.len() > self.config.max_delivered_history {
2722 if let Some(old) = self.delivered_order.pop_front() {
2723 self.delivered.remove(&old);
2724 }
2725 }
2726 true
2727 }
2728}
2729
2730impl Default for ReliableClientReceiver {
2731 fn default() -> Self {
2732 Self::new(ReliableClientConfig::default())
2733 }
2734}
2735
2736#[derive(Clone, Debug)]
2738pub struct ReliableClientEndpoint {
2739 pub sender: ReliableClientSender,
2741 pub receiver: ReliableClientReceiver,
2743 retry_scratch: ReliableClientRetryScratch,
2744}
2745
2746impl ReliableClientEndpoint {
2747 pub fn new(config: ReliableClientConfig) -> Self {
2749 Self {
2750 sender: ReliableClientSender::new(config),
2751 receiver: ReliableClientReceiver::new(config),
2752 retry_scratch: ReliableClientRetryScratch::default(),
2753 }
2754 }
2755
2756 pub fn send<T: TransportSink>(
2758 &mut self,
2759 transport: &mut T,
2760 packet: OutboundPacket,
2761 now_tick: u64,
2762 ) -> Result<u64, ReliableClientError<T::Error>> {
2763 self.sender.send(transport, packet, now_tick)
2764 }
2765
2766 pub fn retry_due<T: TransportSink>(
2768 &mut self,
2769 transport: &mut T,
2770 now_tick: u64,
2771 ) -> Result<ReliableRetryReport, ReliableClientError<T::Error>> {
2772 self.sender
2773 .retry_due_into(transport, now_tick, &mut self.retry_scratch)
2774 }
2775
2776 pub fn handle_inbound<T: TransportSink>(
2778 &mut self,
2779 transport: &mut T,
2780 packet: InboundPacket,
2781 ) -> Result<Option<InboundPacket>, ReliableClientError<T::Error>> {
2782 let source_client = packet
2783 .client_id
2784 .ok_or(ReliableClientError::MissingSourceClient)?;
2785 match ReliableClientFrame::decode_ref(&packet.bytes).map_err(ReliableClientError::Decode)? {
2786 ReliableClientFrameRef::Data { sequence, payload } => {
2787 let payload_len = payload.len();
2788 let payload_offset = packet.bytes.len().saturating_sub(payload_len);
2789 let InboundPacket {
2790 client_id,
2791 remote_addr,
2792 bytes,
2793 } = packet;
2794 let payload = reuse_reliable_payload(bytes, payload_offset, payload_len);
2795 self.receiver.handle_data(
2796 transport,
2797 InboundPacket {
2798 client_id,
2799 remote_addr,
2800 bytes: Vec::new(),
2801 },
2802 source_client,
2803 sequence,
2804 payload,
2805 )
2806 }
2807 ReliableClientFrameRef::Ack { sequence } => {
2808 self.sender.acknowledge(source_client, sequence);
2809 Ok(None)
2810 }
2811 }
2812 }
2813}
2814
2815impl Default for ReliableClientEndpoint {
2816 fn default() -> Self {
2817 Self::new(ReliableClientConfig::default())
2818 }
2819}
2820
2821pub trait StationTransportSink {
2823 type Error;
2825
2826 fn send_station(&mut self, packet: StationOutboundPacket) -> Result<(), Self::Error>;
2828
2829 fn send_station_batch(&mut self, batch: StationPacketBatch) -> Result<(), Self::Error> {
2831 for packet in batch.packets {
2832 self.send_station(packet)?;
2833 }
2834 Ok(())
2835 }
2836}
2837
2838pub trait StationTransportReceiver {
2840 type Error;
2842
2843 fn try_recv_station(
2845 &mut self,
2846 target_station: StationId,
2847 ) -> Result<Option<StationInboundPacket>, Self::Error>;
2848}
2849
2850const RELIABLE_STATION_MAGIC: [u8; 4] = *b"SSRP";
2851const RELIABLE_KIND_DATA: u8 = 0;
2852const RELIABLE_KIND_ACK: u8 = 1;
2853pub const RELIABLE_STATION_DATA_HEADER_BYTES: usize = 17;
2855pub const RELIABLE_STATION_ACK_BYTES: usize = 13;
2857pub const DEFAULT_RELIABLE_STATION_MAX_PAYLOAD_BYTES: usize =
2860 (16 * 1024) - RELIABLE_STATION_DATA_HEADER_BYTES;
2861pub const DEFAULT_RELIABLE_STATION_DELIVERED_HISTORY: usize = 4096;
2863
2864#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2866pub struct ReliableStationConfig {
2867 pub max_in_flight_per_target: usize,
2869 pub retry_after_ticks: u64,
2871 pub max_attempts: u8,
2873 pub max_payload_bytes: usize,
2875 pub max_delivered_history: usize,
2878}
2879
2880impl Default for ReliableStationConfig {
2881 fn default() -> Self {
2882 Self {
2883 max_in_flight_per_target: 1024,
2884 retry_after_ticks: 2,
2885 max_attempts: 4,
2886 max_payload_bytes: DEFAULT_RELIABLE_STATION_MAX_PAYLOAD_BYTES,
2887 max_delivered_history: DEFAULT_RELIABLE_STATION_DELIVERED_HISTORY,
2888 }
2889 }
2890}
2891
2892#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
2894pub struct ReliableStationStats {
2895 pub data_sent: usize,
2897 pub retries_sent: usize,
2899 pub acks_sent: usize,
2901 pub acks_received: usize,
2903 pub data_delivered: usize,
2905 pub duplicates_suppressed: usize,
2907 pub timed_out: usize,
2909}
2910
2911#[derive(Clone, Debug, PartialEq, Eq)]
2913pub enum ReliableStationFrame {
2914 Data {
2916 sequence: u64,
2918 payload: Vec<u8>,
2920 },
2921 Ack {
2923 sequence: u64,
2925 },
2926}
2927
2928#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2930pub enum ReliableStationFrameRef<'a> {
2931 Data {
2933 sequence: u64,
2935 payload: &'a [u8],
2937 },
2938 Ack {
2940 sequence: u64,
2942 },
2943}
2944
2945impl ReliableStationFrameRef<'_> {
2946 pub fn to_owned(self) -> ReliableStationFrame {
2948 match self {
2949 Self::Data { sequence, payload } => ReliableStationFrame::Data {
2950 sequence,
2951 payload: payload.to_vec(),
2952 },
2953 Self::Ack { sequence } => ReliableStationFrame::Ack { sequence },
2954 }
2955 }
2956}
2957
2958impl ReliableStationFrame {
2959 pub fn encode_data(
2961 sequence: u64,
2962 payload: &[u8],
2963 out: &mut Vec<u8>,
2964 ) -> Result<(), ReliableStationEncodeError> {
2965 out.extend_from_slice(&RELIABLE_STATION_MAGIC);
2966 out.push(RELIABLE_KIND_DATA);
2967 out.extend_from_slice(&sequence.to_le_bytes());
2968 let len = u32::try_from(payload.len()).map_err(|_| {
2969 ReliableStationEncodeError::PayloadTooLarge {
2970 actual: payload.len(),
2971 }
2972 })?;
2973 out.extend_from_slice(&len.to_le_bytes());
2974 out.extend_from_slice(payload);
2975 Ok(())
2976 }
2977
2978 pub fn encode(&self, out: &mut Vec<u8>) -> Result<(), ReliableStationEncodeError> {
2980 match self {
2981 Self::Data { sequence, payload } => Self::encode_data(*sequence, payload, out),
2982 Self::Ack { sequence } => {
2983 out.extend_from_slice(&RELIABLE_STATION_MAGIC);
2984 out.push(RELIABLE_KIND_ACK);
2985 out.extend_from_slice(&sequence.to_le_bytes());
2986 Ok(())
2987 }
2988 }
2989 }
2990
2991 pub fn decode(input: &[u8]) -> Result<Self, ReliableStationDecodeError> {
2993 Self::decode_ref(input).map(ReliableStationFrameRef::to_owned)
2994 }
2995
2996 pub fn decode_ref(
2998 input: &[u8],
2999 ) -> Result<ReliableStationFrameRef<'_>, ReliableStationDecodeError> {
3000 let mut cursor = ReliableCursor::new(input);
3001 let magic = cursor.read_array::<4>()?;
3002 if magic != RELIABLE_STATION_MAGIC {
3003 return Err(ReliableStationDecodeError::BadMagic);
3004 }
3005 let kind = cursor.read_u8()?;
3006 let sequence = cursor.read_u64()?;
3007 let frame = match kind {
3008 RELIABLE_KIND_DATA => {
3009 let len = cursor.read_u32()? as usize;
3010 let payload = cursor.read_slice(len)?;
3011 ReliableStationFrameRef::Data { sequence, payload }
3012 }
3013 RELIABLE_KIND_ACK => ReliableStationFrameRef::Ack { sequence },
3014 other => return Err(ReliableStationDecodeError::UnknownKind(other)),
3015 };
3016 cursor.finish()?;
3017 Ok(frame)
3018 }
3019}
3020
3021#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3023pub enum ReliableStationEncodeError {
3024 PayloadTooLarge {
3026 actual: usize,
3028 },
3029}
3030
3031impl core::fmt::Display for ReliableStationEncodeError {
3032 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3033 match self {
3034 Self::PayloadTooLarge { actual } => {
3035 write!(f, "reliable station payload too large: {actual} bytes")
3036 }
3037 }
3038 }
3039}
3040
3041impl std::error::Error for ReliableStationEncodeError {}
3042
3043#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3045pub enum ReliableStationDecodeError {
3046 BadMagic,
3048 UnknownKind(u8),
3050 Truncated {
3052 needed: usize,
3054 available: usize,
3056 },
3057 TrailingBytes(usize),
3059}
3060
3061impl core::fmt::Display for ReliableStationDecodeError {
3062 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3063 match self {
3064 Self::BadMagic => f.write_str("bad reliable station frame magic"),
3065 Self::UnknownKind(kind) => write!(f, "unknown reliable station frame kind {kind}"),
3066 Self::Truncated { needed, available } => {
3067 write!(
3068 f,
3069 "truncated reliable station frame: needed {needed}, available {available}"
3070 )
3071 }
3072 Self::TrailingBytes(bytes) => {
3073 write!(f, "reliable station frame has {bytes} trailing bytes")
3074 }
3075 }
3076 }
3077}
3078
3079impl std::error::Error for ReliableStationDecodeError {}
3080
3081#[derive(Debug)]
3083pub enum ReliableStationError<E> {
3084 Transport(E),
3086 PayloadTooLarge {
3088 budget: usize,
3090 actual: usize,
3092 },
3093 WindowFull {
3095 target_station: StationId,
3097 capacity: usize,
3099 },
3100 Encode(ReliableStationEncodeError),
3102 Decode(ReliableStationDecodeError),
3104}
3105
3106impl<E: core::fmt::Display> core::fmt::Display for ReliableStationError<E> {
3107 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3108 match self {
3109 Self::Transport(error) => write!(f, "{error}"),
3110 Self::PayloadTooLarge { budget, actual } => {
3111 write!(
3112 f,
3113 "reliable station payload exceeded byte budget: budget {budget}, actual {actual}"
3114 )
3115 }
3116 Self::WindowFull {
3117 target_station,
3118 capacity,
3119 } => write!(
3120 f,
3121 "reliable station target {} window is full at capacity {capacity}",
3122 target_station.get()
3123 ),
3124 Self::Encode(error) => write!(f, "{error}"),
3125 Self::Decode(error) => write!(f, "{error}"),
3126 }
3127 }
3128}
3129
3130impl<E> std::error::Error for ReliableStationError<E>
3131where
3132 E: std::error::Error + 'static,
3133{
3134 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
3135 match self {
3136 Self::Transport(error) => Some(error),
3137 Self::Encode(error) => Some(error),
3138 Self::Decode(error) => Some(error),
3139 Self::PayloadTooLarge { .. } | Self::WindowFull { .. } => None,
3140 }
3141 }
3142}
3143
3144#[derive(Clone, Debug, PartialEq, Eq)]
3145struct InFlightReliableStationPacket {
3146 source_station: StationId,
3147 target_station: StationId,
3148 sequence: u64,
3149 payload: Vec<u8>,
3150 first_sent_tick: u64,
3151 last_sent_tick: u64,
3152 attempts: u8,
3153}
3154
3155#[derive(Clone, Debug, Default)]
3157pub struct ReliableStationRetryScratch {
3158 due_entries: Vec<(u64, StationId, u64)>,
3159}
3160
3161impl ReliableStationRetryScratch {
3162 pub fn new() -> Self {
3164 Self::default()
3165 }
3166
3167 pub fn retained_key_capacity(&self) -> usize {
3169 self.due_entries.capacity()
3170 }
3171}
3172
3173#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
3175pub struct ReliableRetryReport {
3176 pub examined: usize,
3178 pub retried: usize,
3180 pub timed_out: usize,
3182}
3183
3184#[derive(Clone, Debug)]
3186pub struct ReliableStationSender {
3187 config: ReliableStationConfig,
3188 next_sequence: BTreeMap<StationId, u64>,
3189 in_flight: BTreeMap<(StationId, u64), InFlightReliableStationPacket>,
3190 in_flight_by_target: BTreeMap<StationId, usize>,
3191 retry_deadlines: BinaryHeap<Reverse<(u64, StationId, u64)>>,
3192 stats: ReliableStationStats,
3193}
3194
3195impl ReliableStationSender {
3196 pub fn new(config: ReliableStationConfig) -> Self {
3198 Self {
3199 config,
3200 next_sequence: BTreeMap::new(),
3201 in_flight: BTreeMap::new(),
3202 in_flight_by_target: BTreeMap::new(),
3203 retry_deadlines: BinaryHeap::new(),
3204 stats: ReliableStationStats::default(),
3205 }
3206 }
3207
3208 pub const fn config(&self) -> ReliableStationConfig {
3210 self.config
3211 }
3212
3213 pub const fn stats(&self) -> ReliableStationStats {
3215 self.stats
3216 }
3217
3218 pub fn in_flight_len(&self) -> usize {
3220 self.in_flight.len()
3221 }
3222
3223 pub fn in_flight_for(&self, target_station: StationId) -> usize {
3225 self.in_flight_by_target
3226 .get(&target_station)
3227 .copied()
3228 .unwrap_or(0)
3229 }
3230
3231 pub fn send<T: StationTransportSink>(
3233 &mut self,
3234 transport: &mut T,
3235 packet: StationOutboundPacket,
3236 now_tick: u64,
3237 ) -> Result<u64, ReliableStationError<T::Error>> {
3238 self.validate_payload(packet.bytes.len())?;
3239 if self.in_flight_for(packet.target_station) >= self.config.max_in_flight_per_target {
3240 return Err(ReliableStationError::WindowFull {
3241 target_station: packet.target_station,
3242 capacity: self.config.max_in_flight_per_target,
3243 });
3244 }
3245
3246 let sequence = self.allocate_sequence(packet.target_station);
3247 Self::send_data_frame(
3248 transport,
3249 packet.source_station,
3250 packet.target_station,
3251 sequence,
3252 &packet.bytes,
3253 )?;
3254 self.insert_in_flight(
3255 (packet.target_station, sequence),
3256 InFlightReliableStationPacket {
3257 source_station: packet.source_station,
3258 target_station: packet.target_station,
3259 sequence,
3260 payload: packet.bytes,
3261 first_sent_tick: now_tick,
3262 last_sent_tick: now_tick,
3263 attempts: 1,
3264 },
3265 );
3266 self.schedule_retry(packet.target_station, sequence, now_tick);
3267 self.stats.data_sent = self.stats.data_sent.saturating_add(1);
3268 Ok(sequence)
3269 }
3270
3271 pub fn acknowledge(&mut self, ack_source_station: StationId, sequence: u64) -> bool {
3273 let removed = self
3274 .remove_in_flight(&(ack_source_station, sequence))
3275 .is_some();
3276 if removed {
3277 self.stats.acks_received = self.stats.acks_received.saturating_add(1);
3278 self.compact_retry_deadlines_if_stale();
3279 }
3280 removed
3281 }
3282
3283 fn compact_retry_deadlines_if_stale(&mut self) {
3284 if self.retry_deadlines.len()
3285 > self
3286 .in_flight
3287 .len()
3288 .saturating_mul(RETRY_DEADLINE_COMPACTION_MULTIPLE)
3289 {
3290 self.retry_deadlines = self
3291 .in_flight
3292 .values()
3293 .map(|packet| {
3294 Reverse((
3295 packet
3296 .last_sent_tick
3297 .saturating_add(self.config.retry_after_ticks),
3298 packet.target_station,
3299 packet.sequence,
3300 ))
3301 })
3302 .collect();
3303 }
3304 }
3305
3306 pub fn retry_due_into<T: StationTransportSink>(
3308 &mut self,
3309 transport: &mut T,
3310 now_tick: u64,
3311 scratch: &mut ReliableStationRetryScratch,
3312 ) -> Result<ReliableRetryReport, ReliableStationError<T::Error>> {
3313 scratch.due_entries.clear();
3314 while let Some(Reverse((deadline, target, sequence))) = self.retry_deadlines.peek().copied()
3315 {
3316 if deadline > now_tick {
3317 break;
3318 }
3319 self.retry_deadlines.pop();
3320 scratch.due_entries.push((deadline, target, sequence));
3321 }
3322 let mut report = ReliableRetryReport::default();
3323
3324 for (entry_index, (deadline, target, sequence)) in scratch.due_entries.iter().enumerate() {
3325 let key = (*target, *sequence);
3326 let Some(packet) = self.in_flight.get(&key) else {
3327 continue;
3328 };
3329 if packet
3330 .last_sent_tick
3331 .saturating_add(self.config.retry_after_ticks)
3332 != *deadline
3333 {
3334 continue;
3335 }
3336 report.examined = report.examined.saturating_add(1);
3337 if packet.attempts >= self.config.max_attempts {
3338 self.remove_in_flight(&key);
3339 self.stats.timed_out = self.stats.timed_out.saturating_add(1);
3340 report.timed_out = report.timed_out.saturating_add(1);
3341 continue;
3342 }
3343
3344 if let Err(error) = Self::send_data_frame(
3345 transport,
3346 packet.source_station,
3347 packet.target_station,
3348 packet.sequence,
3349 &packet.payload,
3350 ) {
3351 self.retry_deadlines.extend(
3352 scratch.due_entries[entry_index..]
3353 .iter()
3354 .copied()
3355 .map(Reverse),
3356 );
3357 return Err(error);
3358 }
3359 if let Some(stored) = self.in_flight.get_mut(&key) {
3360 stored.last_sent_tick = now_tick;
3361 stored.attempts = stored.attempts.saturating_add(1);
3362 }
3363 self.schedule_retry(*target, *sequence, now_tick);
3364 self.stats.retries_sent = self.stats.retries_sent.saturating_add(1);
3365 report.retried = report.retried.saturating_add(1);
3366 }
3367
3368 Ok(report)
3369 }
3370
3371 fn validate_payload<E>(&self, bytes: usize) -> Result<(), ReliableStationError<E>> {
3372 if bytes > self.config.max_payload_bytes {
3373 Err(ReliableStationError::PayloadTooLarge {
3374 budget: self.config.max_payload_bytes,
3375 actual: bytes,
3376 })
3377 } else {
3378 Ok(())
3379 }
3380 }
3381
3382 fn allocate_sequence(&mut self, target_station: StationId) -> u64 {
3383 let next = self.next_sequence.entry(target_station).or_insert(1);
3384 let sequence = *next;
3385 *next = next.saturating_add(1);
3386 sequence
3387 }
3388
3389 fn insert_in_flight(&mut self, key: (StationId, u64), packet: InFlightReliableStationPacket) {
3390 if self.in_flight.insert(key, packet).is_none() {
3391 let count = self.in_flight_by_target.entry(key.0).or_insert(0);
3392 *count = count.saturating_add(1);
3393 }
3394 }
3395
3396 fn schedule_retry(&mut self, target: StationId, sequence: u64, sent_tick: u64) {
3397 self.retry_deadlines.push(Reverse((
3398 sent_tick.saturating_add(self.config.retry_after_ticks),
3399 target,
3400 sequence,
3401 )));
3402 }
3403
3404 fn remove_in_flight(
3405 &mut self,
3406 key: &(StationId, u64),
3407 ) -> Option<InFlightReliableStationPacket> {
3408 let removed = self.in_flight.remove(key)?;
3409 if let Some(count) = self.in_flight_by_target.get_mut(&removed.target_station) {
3410 *count = count.saturating_sub(1);
3411 if *count == 0 {
3412 self.in_flight_by_target.remove(&removed.target_station);
3413 }
3414 }
3415 Some(removed)
3416 }
3417
3418 fn send_data_frame<T: StationTransportSink>(
3419 transport: &mut T,
3420 source_station: StationId,
3421 target_station: StationId,
3422 sequence: u64,
3423 payload: &[u8],
3424 ) -> Result<(), ReliableStationError<T::Error>> {
3425 let mut bytes = Vec::with_capacity(
3426 payload
3427 .len()
3428 .saturating_add(RELIABLE_STATION_DATA_HEADER_BYTES),
3429 );
3430 ReliableStationFrame::encode_data(sequence, payload, &mut bytes)
3431 .map_err(ReliableStationError::Encode)?;
3432 transport
3433 .send_station(StationOutboundPacket {
3434 source_station,
3435 target_station,
3436 bytes,
3437 })
3438 .map_err(ReliableStationError::Transport)
3439 }
3440}
3441
3442impl Default for ReliableStationSender {
3443 fn default() -> Self {
3444 Self::new(ReliableStationConfig::default())
3445 }
3446}
3447
3448#[derive(Clone, Debug)]
3450pub struct ReliableStationReceiver {
3451 config: ReliableStationConfig,
3452 delivered: BoundedLookupSet<(StationId, u64)>,
3453 delivered_order: VecDeque<(StationId, u64)>,
3454 stats: ReliableStationStats,
3455}
3456
3457impl ReliableStationReceiver {
3458 pub fn new(config: ReliableStationConfig) -> Self {
3460 Self {
3461 config,
3462 delivered: BoundedLookupSet::new(config.max_delivered_history),
3463 delivered_order: VecDeque::new(),
3464 stats: ReliableStationStats::default(),
3465 }
3466 }
3467
3468 pub const fn config(&self) -> ReliableStationConfig {
3470 self.config
3471 }
3472
3473 pub const fn stats(&self) -> ReliableStationStats {
3475 self.stats
3476 }
3477
3478 pub fn handle_data<T: StationTransportSink>(
3481 &mut self,
3482 transport: &mut T,
3483 packet: StationInboundPacket,
3484 sequence: u64,
3485 payload: Vec<u8>,
3486 ) -> Result<Option<StationInboundPacket>, ReliableStationError<T::Error>> {
3487 let StationInboundPacket {
3488 source_station,
3489 target_station,
3490 bytes: wire_bytes,
3491 } = packet;
3492 drop(wire_bytes);
3493 self.send_ack(transport, target_station, source_station, sequence)?;
3494 if !self.record_unique(source_station, sequence) {
3495 self.stats.duplicates_suppressed = self.stats.duplicates_suppressed.saturating_add(1);
3496 return Ok(None);
3497 }
3498
3499 self.stats.data_delivered = self.stats.data_delivered.saturating_add(1);
3500 Ok(Some(StationInboundPacket {
3501 source_station,
3502 target_station,
3503 bytes: payload,
3504 }))
3505 }
3506
3507 fn send_ack<T: StationTransportSink>(
3508 &mut self,
3509 transport: &mut T,
3510 source_station: StationId,
3511 target_station: StationId,
3512 sequence: u64,
3513 ) -> Result<(), ReliableStationError<T::Error>> {
3514 let mut bytes = Vec::with_capacity(RELIABLE_STATION_ACK_BYTES);
3515 ReliableStationFrame::Ack { sequence }
3516 .encode(&mut bytes)
3517 .map_err(ReliableStationError::Encode)?;
3518 transport
3519 .send_station(StationOutboundPacket {
3520 source_station,
3521 target_station,
3522 bytes,
3523 })
3524 .map_err(ReliableStationError::Transport)?;
3525 self.stats.acks_sent = self.stats.acks_sent.saturating_add(1);
3526 Ok(())
3527 }
3528
3529 fn record_unique(&mut self, source_station: StationId, sequence: u64) -> bool {
3530 if self.config.max_delivered_history == 0 {
3531 return true;
3532 }
3533
3534 let key = (source_station, sequence);
3535 if self.delivered.contains(&key) {
3536 return false;
3537 }
3538
3539 self.delivered.insert(key);
3540 self.delivered_order.push_back(key);
3541 while self.delivered_order.len() > self.config.max_delivered_history {
3542 if let Some(old) = self.delivered_order.pop_front() {
3543 self.delivered.remove(&old);
3544 }
3545 }
3546 true
3547 }
3548}
3549
3550impl Default for ReliableStationReceiver {
3551 fn default() -> Self {
3552 Self::new(ReliableStationConfig::default())
3553 }
3554}
3555
3556#[derive(Clone, Debug)]
3558pub struct ReliableStationEndpoint {
3559 pub sender: ReliableStationSender,
3561 pub receiver: ReliableStationReceiver,
3563 retry_scratch: ReliableStationRetryScratch,
3564}
3565
3566impl ReliableStationEndpoint {
3567 pub fn new(config: ReliableStationConfig) -> Self {
3569 Self {
3570 sender: ReliableStationSender::new(config),
3571 receiver: ReliableStationReceiver::new(config),
3572 retry_scratch: ReliableStationRetryScratch::default(),
3573 }
3574 }
3575
3576 pub fn send<T: StationTransportSink>(
3578 &mut self,
3579 transport: &mut T,
3580 packet: StationOutboundPacket,
3581 now_tick: u64,
3582 ) -> Result<u64, ReliableStationError<T::Error>> {
3583 self.sender.send(transport, packet, now_tick)
3584 }
3585
3586 pub fn retry_due<T: StationTransportSink>(
3588 &mut self,
3589 transport: &mut T,
3590 now_tick: u64,
3591 ) -> Result<ReliableRetryReport, ReliableStationError<T::Error>> {
3592 self.sender
3593 .retry_due_into(transport, now_tick, &mut self.retry_scratch)
3594 }
3595
3596 pub fn handle_inbound<T: StationTransportSink>(
3598 &mut self,
3599 transport: &mut T,
3600 packet: StationInboundPacket,
3601 ) -> Result<Option<StationInboundPacket>, ReliableStationError<T::Error>> {
3602 match ReliableStationFrame::decode_ref(&packet.bytes)
3603 .map_err(ReliableStationError::Decode)?
3604 {
3605 ReliableStationFrameRef::Data { sequence, payload } => {
3606 let payload_len = payload.len();
3607 let payload_offset = packet.bytes.len().saturating_sub(payload_len);
3608 let StationInboundPacket {
3609 source_station,
3610 target_station,
3611 bytes,
3612 } = packet;
3613 let payload = reuse_reliable_payload(bytes, payload_offset, payload_len);
3614 self.receiver.handle_data(
3615 transport,
3616 StationInboundPacket {
3617 source_station,
3618 target_station,
3619 bytes: Vec::new(),
3620 },
3621 sequence,
3622 payload,
3623 )
3624 }
3625 ReliableStationFrameRef::Ack { sequence } => {
3626 self.sender.acknowledge(packet.source_station, sequence);
3627 Ok(None)
3628 }
3629 }
3630 }
3631}
3632
3633impl Default for ReliableStationEndpoint {
3634 fn default() -> Self {
3635 Self::new(ReliableStationConfig::default())
3636 }
3637}
3638
3639struct ReliableCursor<'a> {
3640 input: &'a [u8],
3641 offset: usize,
3642}
3643
3644impl<'a> ReliableCursor<'a> {
3645 const fn new(input: &'a [u8]) -> Self {
3646 Self { input, offset: 0 }
3647 }
3648
3649 fn read_u8(&mut self) -> Result<u8, ReliableStationDecodeError> {
3650 self.require(1)?;
3651 let value = self.input[self.offset];
3652 self.offset += 1;
3653 Ok(value)
3654 }
3655
3656 fn read_u32(&mut self) -> Result<u32, ReliableStationDecodeError> {
3657 let bytes = self.read_array::<4>()?;
3658 Ok(u32::from_le_bytes(bytes))
3659 }
3660
3661 fn read_u64(&mut self) -> Result<u64, ReliableStationDecodeError> {
3662 let bytes = self.read_array::<8>()?;
3663 Ok(u64::from_le_bytes(bytes))
3664 }
3665
3666 fn read_array<const N: usize>(&mut self) -> Result<[u8; N], ReliableStationDecodeError> {
3667 self.require(N)?;
3668 let mut out = [0_u8; N];
3669 out.copy_from_slice(&self.input[self.offset..self.offset + N]);
3670 self.offset += N;
3671 Ok(out)
3672 }
3673
3674 fn read_slice(&mut self, len: usize) -> Result<&'a [u8], ReliableStationDecodeError> {
3675 self.require(len)?;
3676 let bytes = &self.input[self.offset..self.offset + len];
3677 self.offset += len;
3678 Ok(bytes)
3679 }
3680
3681 fn require(&self, count: usize) -> Result<(), ReliableStationDecodeError> {
3682 let needed = self.offset.saturating_add(count);
3683 if needed > self.input.len() {
3684 Err(ReliableStationDecodeError::Truncated {
3685 needed,
3686 available: self.input.len(),
3687 })
3688 } else {
3689 Ok(())
3690 }
3691 }
3692
3693 fn finish(&self) -> Result<(), ReliableStationDecodeError> {
3694 if self.offset == self.input.len() {
3695 Ok(())
3696 } else {
3697 Err(ReliableStationDecodeError::TrailingBytes(
3698 self.input.len().saturating_sub(self.offset),
3699 ))
3700 }
3701 }
3702}
3703
3704fn reuse_reliable_payload(mut wire_bytes: Vec<u8>, offset: usize, len: usize) -> Vec<u8> {
3705 wire_bytes.copy_within(offset..offset.saturating_add(len), 0);
3706 wire_bytes.truncate(len);
3707 wire_bytes
3708}
3709
3710#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3712pub struct StationTransportLimits {
3713 pub max_queued_packets_per_station: usize,
3715 pub max_packet_bytes: usize,
3717}
3718
3719impl Default for StationTransportLimits {
3720 fn default() -> Self {
3721 Self {
3722 max_queued_packets_per_station: 4096,
3723 max_packet_bytes: 16 * 1024,
3724 }
3725 }
3726}
3727
3728#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
3730pub struct InMemoryStationTransportStats {
3731 pub packets_sent: usize,
3733 pub packets_received: usize,
3735 pub bytes_sent: usize,
3737 pub bytes_received: usize,
3739 pub packets_rejected_full: usize,
3741 pub packets_rejected_bytes: usize,
3743}
3744
3745#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
3747pub struct UdpStationTransportStats {
3748 pub packets_sent: usize,
3750 pub packets_received: usize,
3752 pub bytes_sent: usize,
3754 pub bytes_received: usize,
3756}
3757
3758#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3760pub enum StationTransportError {
3761 MissingTarget(StationId),
3763 QueueFull {
3765 station_id: StationId,
3767 capacity: usize,
3769 },
3770 PacketTooLarge {
3772 budget: usize,
3774 actual: usize,
3776 },
3777}
3778
3779impl core::fmt::Display for StationTransportError {
3780 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3781 match self {
3782 Self::MissingTarget(station_id) => {
3783 write!(
3784 f,
3785 "station transport target {} is missing",
3786 station_id.get()
3787 )
3788 }
3789 Self::QueueFull {
3790 station_id,
3791 capacity,
3792 } => write!(
3793 f,
3794 "station transport target {} queue is full at capacity {capacity}",
3795 station_id.get()
3796 ),
3797 Self::PacketTooLarge { budget, actual } => {
3798 write!(
3799 f,
3800 "station transport packet exceeded byte budget: budget {budget}, actual {actual}"
3801 )
3802 }
3803 }
3804 }
3805}
3806
3807impl std::error::Error for StationTransportError {}
3808
3809#[derive(Clone, Debug)]
3811pub struct InMemoryStationTransport {
3812 limits: StationTransportLimits,
3813 queues: AdaptiveEndpointMap<StationId, VecDeque<StationInboundPacket>>,
3814 stats: InMemoryStationTransportStats,
3815}
3816
3817impl InMemoryStationTransport {
3818 pub fn new(limits: StationTransportLimits) -> Self {
3820 Self {
3821 limits,
3822 queues: AdaptiveEndpointMap::new(),
3823 stats: InMemoryStationTransportStats::default(),
3824 }
3825 }
3826
3827 pub fn register_station(&mut self, station_id: StationId) {
3829 if self.queues.get(&station_id).is_none() {
3830 self.queues.insert(station_id, VecDeque::new());
3831 }
3832 }
3833
3834 pub fn unregister_station(&mut self, station_id: StationId) -> Option<usize> {
3836 self.queues.remove(&station_id).map(|queue| queue.len())
3837 }
3838
3839 pub fn queued_len(&self, station_id: StationId) -> Option<usize> {
3841 self.queues.get(&station_id).map(VecDeque::len)
3842 }
3843
3844 pub fn queued_capacity(&self, station_id: StationId) -> Option<usize> {
3846 self.queues.get(&station_id).map(VecDeque::capacity)
3847 }
3848
3849 pub fn retained_queue_capacity(&self) -> usize {
3851 self.queues.values().map(VecDeque::capacity).sum()
3852 }
3853
3854 pub const fn limits(&self) -> StationTransportLimits {
3856 self.limits
3857 }
3858
3859 pub const fn stats(&self) -> InMemoryStationTransportStats {
3861 self.stats
3862 }
3863}
3864
3865impl Default for InMemoryStationTransport {
3866 fn default() -> Self {
3867 Self::new(StationTransportLimits::default())
3868 }
3869}
3870
3871impl StationTransportSink for InMemoryStationTransport {
3872 type Error = StationTransportError;
3873
3874 fn send_station(&mut self, packet: StationOutboundPacket) -> Result<(), Self::Error> {
3875 let actual = packet.bytes.len();
3876 if actual > self.limits.max_packet_bytes {
3877 self.stats.packets_rejected_bytes = self.stats.packets_rejected_bytes.saturating_add(1);
3878 return Err(StationTransportError::PacketTooLarge {
3879 budget: self.limits.max_packet_bytes,
3880 actual,
3881 });
3882 }
3883
3884 let queue = self
3885 .queues
3886 .get_mut(&packet.target_station)
3887 .ok_or(StationTransportError::MissingTarget(packet.target_station))?;
3888 if queue.len() >= self.limits.max_queued_packets_per_station {
3889 self.stats.packets_rejected_full = self.stats.packets_rejected_full.saturating_add(1);
3890 return Err(StationTransportError::QueueFull {
3891 station_id: packet.target_station,
3892 capacity: self.limits.max_queued_packets_per_station,
3893 });
3894 }
3895
3896 self.stats.packets_sent = self.stats.packets_sent.saturating_add(1);
3897 self.stats.bytes_sent = self.stats.bytes_sent.saturating_add(actual);
3898 queue.push_back(StationInboundPacket {
3899 source_station: packet.source_station,
3900 target_station: packet.target_station,
3901 bytes: packet.bytes,
3902 });
3903 Ok(())
3904 }
3905}
3906
3907impl StationTransportReceiver for InMemoryStationTransport {
3908 type Error = StationTransportError;
3909
3910 fn try_recv_station(
3911 &mut self,
3912 target_station: StationId,
3913 ) -> Result<Option<StationInboundPacket>, Self::Error> {
3914 let queue = self
3915 .queues
3916 .get_mut(&target_station)
3917 .ok_or(StationTransportError::MissingTarget(target_station))?;
3918 let Some(packet) = queue.pop_front() else {
3919 return Ok(None);
3920 };
3921 self.stats.packets_received = self.stats.packets_received.saturating_add(1);
3922 self.stats.bytes_received = self.stats.bytes_received.saturating_add(packet.bytes.len());
3923 Ok(Some(packet))
3924 }
3925}
3926
3927#[derive(Debug)]
3929pub enum UdpStationTransportError {
3930 UnknownStation(StationId),
3932 UnknownRemote(SocketAddr),
3934 LocalStationMismatch {
3936 local_station: StationId,
3938 packet_source: StationId,
3940 },
3941 TargetStationMismatch {
3943 local_station: StationId,
3945 requested_target: StationId,
3947 },
3948 Io(io::Error),
3950}
3951
3952impl core::fmt::Display for UdpStationTransportError {
3953 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3954 match self {
3955 Self::UnknownStation(station_id) => {
3956 write!(
3957 f,
3958 "udp station target {} is not registered",
3959 station_id.get()
3960 )
3961 }
3962 Self::UnknownRemote(addr) => {
3963 write!(f, "udp station remote address {addr} is not registered")
3964 }
3965 Self::LocalStationMismatch {
3966 local_station,
3967 packet_source,
3968 } => write!(
3969 f,
3970 "udp station local source mismatch: local {}, packet source {}",
3971 local_station.get(),
3972 packet_source.get()
3973 ),
3974 Self::TargetStationMismatch {
3975 local_station,
3976 requested_target,
3977 } => write!(
3978 f,
3979 "udp station receive target mismatch: local {}, requested {}",
3980 local_station.get(),
3981 requested_target.get()
3982 ),
3983 Self::Io(error) => write!(f, "{error}"),
3984 }
3985 }
3986}
3987
3988impl std::error::Error for UdpStationTransportError {
3989 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
3990 match self {
3991 Self::Io(error) => Some(error),
3992 Self::UnknownStation(_)
3993 | Self::UnknownRemote(_)
3994 | Self::LocalStationMismatch { .. }
3995 | Self::TargetStationMismatch { .. } => None,
3996 }
3997 }
3998}
3999
4000impl From<io::Error> for UdpStationTransportError {
4001 fn from(error: io::Error) -> Self {
4002 Self::Io(error)
4003 }
4004}
4005
4006#[derive(Debug)]
4012pub struct UdpStationTransport {
4013 local_station: StationId,
4014 socket: UdpSocket,
4015 stations: HashMap<StationId, SocketAddr>,
4016 addr_to_station: HashMap<SocketAddr, StationId>,
4017 recv_buffer: Vec<u8>,
4018 stats: UdpStationTransportStats,
4019}
4020
4021impl UdpStationTransport {
4022 pub fn bind<A: ToSocketAddrs>(local_station: StationId, addr: A) -> io::Result<Self> {
4024 let socket = UdpSocket::bind(addr)?;
4025 Self::from_socket(local_station, socket)
4026 }
4027
4028 pub fn from_socket(local_station: StationId, socket: UdpSocket) -> io::Result<Self> {
4030 socket.set_nonblocking(true)?;
4031 Ok(Self {
4032 local_station,
4033 socket,
4034 stations: HashMap::new(),
4035 addr_to_station: HashMap::new(),
4036 recv_buffer: vec![0; DEFAULT_UDP_RECV_BUFFER_SIZE],
4037 stats: UdpStationTransportStats::default(),
4038 })
4039 }
4040
4041 pub const fn local_station(&self) -> StationId {
4043 self.local_station
4044 }
4045
4046 pub fn local_addr(&self) -> io::Result<SocketAddr> {
4048 self.socket.local_addr()
4049 }
4050
4051 pub const fn socket(&self) -> &UdpSocket {
4053 &self.socket
4054 }
4055
4056 pub fn socket_mut(&mut self) -> &mut UdpSocket {
4058 &mut self.socket
4059 }
4060
4061 pub fn register_station(
4063 &mut self,
4064 station_id: StationId,
4065 addr: SocketAddr,
4066 ) -> Option<SocketAddr> {
4067 let old_addr = self.stations.insert(station_id, addr);
4068 if let Some(old_addr) = old_addr {
4069 self.addr_to_station.remove(&old_addr);
4070 }
4071 if let Some(old_station) = self.addr_to_station.insert(addr, station_id)
4072 && old_station != station_id
4073 {
4074 self.stations.remove(&old_station);
4075 }
4076 old_addr
4077 }
4078
4079 pub fn unregister_station(&mut self, station_id: StationId) -> Option<SocketAddr> {
4081 let addr = self.stations.remove(&station_id)?;
4082 self.addr_to_station.remove(&addr);
4083 Some(addr)
4084 }
4085
4086 pub fn station_addr(&self, station_id: StationId) -> Option<SocketAddr> {
4088 self.stations.get(&station_id).copied()
4089 }
4090
4091 pub fn station_for_addr(&self, addr: SocketAddr) -> Option<StationId> {
4093 self.addr_to_station.get(&addr).copied()
4094 }
4095
4096 pub fn set_recv_buffer_size(&mut self, bytes: usize) {
4098 self.recv_buffer.resize(bytes.max(1), 0);
4099 }
4100
4101 pub fn recv_buffer_size(&self) -> usize {
4103 self.recv_buffer.len()
4104 }
4105
4106 pub const fn stats(&self) -> UdpStationTransportStats {
4108 self.stats
4109 }
4110
4111 pub fn try_recv_station_ref(
4117 &mut self,
4118 target_station: StationId,
4119 ) -> Result<Option<StationInboundPacketRef<'_>>, UdpStationTransportError> {
4120 if target_station != self.local_station {
4121 return Err(UdpStationTransportError::TargetStationMismatch {
4122 local_station: self.local_station,
4123 requested_target: target_station,
4124 });
4125 }
4126 match self.socket.recv_from(&mut self.recv_buffer) {
4127 Ok((len, remote_addr)) => {
4128 let source_station = self
4129 .addr_to_station
4130 .get(&remote_addr)
4131 .copied()
4132 .ok_or(UdpStationTransportError::UnknownRemote(remote_addr))?;
4133 self.stats.packets_received = self.stats.packets_received.saturating_add(1);
4134 self.stats.bytes_received = self.stats.bytes_received.saturating_add(len);
4135 Ok(Some(StationInboundPacketRef {
4136 source_station,
4137 target_station: self.local_station,
4138 bytes: &self.recv_buffer[..len],
4139 }))
4140 }
4141 Err(error) if error.kind() == io::ErrorKind::WouldBlock => Ok(None),
4142 Err(error) => Err(error.into()),
4143 }
4144 }
4145}
4146
4147impl StationTransportSink for UdpStationTransport {
4148 type Error = UdpStationTransportError;
4149
4150 fn send_station(&mut self, packet: StationOutboundPacket) -> Result<(), Self::Error> {
4151 if packet.source_station != self.local_station {
4152 return Err(UdpStationTransportError::LocalStationMismatch {
4153 local_station: self.local_station,
4154 packet_source: packet.source_station,
4155 });
4156 }
4157 let addr = self.stations.get(&packet.target_station).copied().ok_or(
4158 UdpStationTransportError::UnknownStation(packet.target_station),
4159 )?;
4160 let sent = self.socket.send_to(&packet.bytes, addr)?;
4161 if sent != packet.bytes.len() {
4162 return Err(io::Error::new(
4163 io::ErrorKind::WriteZero,
4164 "udp station socket reported a partial datagram send",
4165 )
4166 .into());
4167 }
4168 self.stats.packets_sent = self.stats.packets_sent.saturating_add(1);
4169 self.stats.bytes_sent = self.stats.bytes_sent.saturating_add(packet.bytes.len());
4170 Ok(())
4171 }
4172}
4173
4174impl StationTransportReceiver for UdpStationTransport {
4175 type Error = UdpStationTransportError;
4176
4177 fn try_recv_station(
4178 &mut self,
4179 target_station: StationId,
4180 ) -> Result<Option<StationInboundPacket>, Self::Error> {
4181 Ok(self
4182 .try_recv_station_ref(target_station)?
4183 .map(StationInboundPacketRef::to_owned))
4184 }
4185}
4186
4187#[derive(Debug)]
4189pub enum UdpTransportError {
4190 UnknownClient(ClientId),
4192 Io(io::Error),
4194}
4195
4196impl core::fmt::Display for UdpTransportError {
4197 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4198 match self {
4199 Self::UnknownClient(client_id) => {
4200 write!(f, "udp target client {} is not registered", client_id.get())
4201 }
4202 Self::Io(error) => write!(f, "{error}"),
4203 }
4204 }
4205}
4206
4207impl std::error::Error for UdpTransportError {
4208 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
4209 match self {
4210 Self::UnknownClient(_) => None,
4211 Self::Io(error) => Some(error),
4212 }
4213 }
4214}
4215
4216impl From<io::Error> for UdpTransportError {
4217 fn from(error: io::Error) -> Self {
4218 Self::Io(error)
4219 }
4220}
4221
4222#[derive(Debug)]
4228pub struct UdpTransport {
4229 socket: UdpSocket,
4230 clients: HashMap<ClientId, SocketAddr>,
4231 addr_to_client: HashMap<SocketAddr, ClientId>,
4232 recv_buffer: Vec<u8>,
4233}
4234
4235impl UdpTransport {
4236 pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<Self> {
4238 let socket = UdpSocket::bind(addr)?;
4239 Self::from_socket(socket)
4240 }
4241
4242 pub fn from_socket(socket: UdpSocket) -> io::Result<Self> {
4244 socket.set_nonblocking(true)?;
4245 Ok(Self {
4246 socket,
4247 clients: HashMap::new(),
4248 addr_to_client: HashMap::new(),
4249 recv_buffer: vec![0; DEFAULT_UDP_RECV_BUFFER_SIZE],
4250 })
4251 }
4252
4253 pub fn local_addr(&self) -> io::Result<SocketAddr> {
4255 self.socket.local_addr()
4256 }
4257
4258 pub const fn socket(&self) -> &UdpSocket {
4260 &self.socket
4261 }
4262
4263 pub fn socket_mut(&mut self) -> &mut UdpSocket {
4265 &mut self.socket
4266 }
4267
4268 pub fn register_client(&mut self, client_id: ClientId, addr: SocketAddr) -> Option<SocketAddr> {
4270 let old_addr = self.clients.insert(client_id, addr);
4271 if let Some(old_addr) = old_addr {
4272 self.addr_to_client.remove(&old_addr);
4273 }
4274 if let Some(old_client) = self.addr_to_client.insert(addr, client_id)
4275 && old_client != client_id
4276 {
4277 self.clients.remove(&old_client);
4278 }
4279 old_addr
4280 }
4281
4282 pub fn unregister_client(&mut self, client_id: ClientId) -> Option<SocketAddr> {
4284 let addr = self.clients.remove(&client_id)?;
4285 self.addr_to_client.remove(&addr);
4286 Some(addr)
4287 }
4288
4289 pub fn client_addr(&self, client_id: ClientId) -> Option<SocketAddr> {
4291 self.clients.get(&client_id).copied()
4292 }
4293
4294 pub fn client_for_addr(&self, addr: SocketAddr) -> Option<ClientId> {
4296 self.addr_to_client.get(&addr).copied()
4297 }
4298
4299 pub fn set_recv_buffer_size(&mut self, bytes: usize) {
4304 self.recv_buffer.resize(bytes.max(1), 0);
4305 }
4306
4307 pub fn recv_buffer_size(&self) -> usize {
4309 self.recv_buffer.len()
4310 }
4311
4312 pub fn try_recv_ref(&mut self) -> Result<Option<InboundPacketRef<'_>>, UdpTransportError> {
4318 match self.socket.recv_from(&mut self.recv_buffer) {
4319 Ok((len, remote_addr)) => Ok(Some(InboundPacketRef {
4320 client_id: self.addr_to_client.get(&remote_addr).copied(),
4321 remote_addr,
4322 bytes: &self.recv_buffer[..len],
4323 })),
4324 Err(error) if error.kind() == io::ErrorKind::WouldBlock => Ok(None),
4325 Err(error) => Err(error.into()),
4326 }
4327 }
4328}
4329
4330impl TransportSink for UdpTransport {
4331 type Error = UdpTransportError;
4332
4333 fn send(&mut self, packet: OutboundPacket) -> Result<(), Self::Error> {
4334 let addr = self
4335 .clients
4336 .get(&packet.client_id)
4337 .copied()
4338 .ok_or(UdpTransportError::UnknownClient(packet.client_id))?;
4339 let sent = self.socket.send_to(&packet.bytes, addr)?;
4340 if sent == packet.bytes.len() {
4341 Ok(())
4342 } else {
4343 Err(io::Error::new(
4344 io::ErrorKind::WriteZero,
4345 "udp socket reported a partial datagram send",
4346 )
4347 .into())
4348 }
4349 }
4350}
4351
4352impl TransportReceiver for UdpTransport {
4353 type Error = UdpTransportError;
4354
4355 fn try_recv(&mut self) -> Result<Option<InboundPacket>, Self::Error> {
4356 Ok(self.try_recv_ref()?.map(InboundPacketRef::to_owned))
4357 }
4358}
4359
4360#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4362pub enum TransportError<E> {
4363 Inner(E),
4365 ByteBudgetExceeded {
4367 budget: usize,
4369 actual: usize,
4371 },
4372}
4373
4374impl<E: core::fmt::Display> core::fmt::Display for TransportError<E> {
4375 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4376 match self {
4377 Self::Inner(error) => write!(f, "{error}"),
4378 Self::ByteBudgetExceeded { budget, actual } => {
4379 write!(
4380 f,
4381 "transport byte budget exceeded: budget {budget}, actual {actual}"
4382 )
4383 }
4384 }
4385 }
4386}
4387
4388impl<E> std::error::Error for TransportError<E> where E: std::error::Error {}
4389
4390#[derive(Clone, Debug)]
4392pub struct BudgetedTransport<T> {
4393 inner: T,
4394 max_packet_bytes: usize,
4395 max_batch_bytes: usize,
4396}
4397
4398impl<T> BudgetedTransport<T> {
4399 pub const fn new(inner: T, max_packet_bytes: usize, max_batch_bytes: usize) -> Self {
4401 Self {
4402 inner,
4403 max_packet_bytes,
4404 max_batch_bytes,
4405 }
4406 }
4407
4408 pub fn into_inner(self) -> T {
4410 self.inner
4411 }
4412
4413 pub const fn inner(&self) -> &T {
4415 &self.inner
4416 }
4417}
4418
4419impl<T: TransportSink> TransportSink for BudgetedTransport<T> {
4420 type Error = TransportError<T::Error>;
4421
4422 fn send(&mut self, packet: OutboundPacket) -> Result<(), Self::Error> {
4423 let bytes = packet.bytes.len();
4424 if bytes > self.max_packet_bytes {
4425 return Err(TransportError::ByteBudgetExceeded {
4426 budget: self.max_packet_bytes,
4427 actual: bytes,
4428 });
4429 }
4430 self.inner.send(packet).map_err(TransportError::Inner)
4431 }
4432
4433 fn send_batch(&mut self, batch: PacketBatch) -> Result<(), Self::Error> {
4434 let mut bytes = 0_usize;
4435 let mut first_oversized_packet = None;
4436 for packet in &batch.packets {
4437 let packet_bytes = packet.bytes.len();
4438 bytes = bytes.saturating_add(packet_bytes);
4439 if first_oversized_packet.is_none() && packet_bytes > self.max_packet_bytes {
4440 first_oversized_packet = Some(packet_bytes);
4441 }
4442 }
4443 if bytes > self.max_batch_bytes {
4444 return Err(TransportError::ByteBudgetExceeded {
4445 budget: self.max_batch_bytes,
4446 actual: bytes,
4447 });
4448 }
4449 if let Some(actual) = first_oversized_packet {
4450 return Err(TransportError::ByteBudgetExceeded {
4451 budget: self.max_packet_bytes,
4452 actual,
4453 });
4454 }
4455 self.inner.send_batch(batch).map_err(TransportError::Inner)
4456 }
4457}
4458
4459#[derive(Clone, Debug, Default, PartialEq, Eq)]
4461pub struct FakeTransport {
4462 packets: usize,
4463 bytes: usize,
4464 batches: usize,
4465}
4466
4467impl FakeTransport {
4468 pub const fn packets_sent(&self) -> usize {
4470 self.packets
4471 }
4472
4473 pub const fn bytes_sent(&self) -> usize {
4475 self.bytes
4476 }
4477
4478 pub const fn batches_sent(&self) -> usize {
4480 self.batches
4481 }
4482}
4483
4484impl TransportSink for FakeTransport {
4485 type Error = core::convert::Infallible;
4486
4487 fn send(&mut self, packet: OutboundPacket) -> Result<(), Self::Error> {
4488 self.packets += 1;
4489 self.bytes += packet.bytes.len();
4490 Ok(())
4491 }
4492
4493 fn send_batch(&mut self, batch: PacketBatch) -> Result<(), Self::Error> {
4494 self.batches += 1;
4495 self.packets += batch.packets.len();
4496 self.bytes += batch.bytes_len();
4497 Ok(())
4498 }
4499}
4500
4501#[cfg(test)]
4502mod tests {
4503 use super::*;
4504 use std::thread;
4505 use std::time::Duration;
4506
4507 fn packet(bytes: usize) -> OutboundPacket {
4508 OutboundPacket {
4509 client_id: ClientId::new(1),
4510 bytes: vec![0; bytes],
4511 }
4512 }
4513
4514 fn station_packet(bytes: usize) -> StationOutboundPacket {
4515 StationOutboundPacket {
4516 source_station: StationId::new(1),
4517 target_station: StationId::new(2),
4518 bytes: vec![1; bytes],
4519 }
4520 }
4521
4522 fn memory_addr(port: u16) -> SocketAddr {
4523 SocketAddr::from(([127, 0, 0, 1], port))
4524 }
4525
4526 fn recv_with_retry(transport: &mut UdpTransport) -> InboundPacket {
4527 for _ in 0..50 {
4528 if let Some(packet) = transport.try_recv().expect("udp receive should work") {
4529 return packet;
4530 }
4531 thread::sleep(Duration::from_millis(2));
4532 }
4533 panic!("udp packet was not received");
4534 }
4535
4536 fn recv_ref_with_retry(
4537 transport: &mut UdpTransport,
4538 ) -> (Option<ClientId>, SocketAddr, usize, u8, u8, usize) {
4539 for _ in 0..50 {
4540 if let Some(packet) = transport
4541 .try_recv_ref()
4542 .expect("borrowed udp receive should work")
4543 {
4544 return (
4545 packet.client_id,
4546 packet.remote_addr,
4547 packet.bytes.len(),
4548 packet.bytes.first().copied().unwrap_or(0),
4549 packet.bytes.last().copied().unwrap_or(0),
4550 packet.bytes.as_ptr() as usize,
4551 );
4552 }
4553 thread::sleep(Duration::from_millis(2));
4554 }
4555 panic!("borrowed udp packet was not received");
4556 }
4557
4558 fn recv_station_with_retry(
4559 transport: &mut UdpStationTransport,
4560 station_id: StationId,
4561 ) -> StationInboundPacket {
4562 for _ in 0..50 {
4563 if let Some(packet) = transport
4564 .try_recv_station(station_id)
4565 .expect("udp station receive should work")
4566 {
4567 return packet;
4568 }
4569 thread::sleep(Duration::from_millis(2));
4570 }
4571 panic!("udp station packet was not received");
4572 }
4573
4574 fn recv_station_ref_with_retry(
4575 transport: &mut UdpStationTransport,
4576 station_id: StationId,
4577 ) -> (StationId, StationId, usize, u8, u8, usize) {
4578 for _ in 0..50 {
4579 if let Some(packet) = transport
4580 .try_recv_station_ref(station_id)
4581 .expect("borrowed udp station receive should work")
4582 {
4583 return (
4584 packet.source_station,
4585 packet.target_station,
4586 packet.bytes.len(),
4587 packet.bytes.first().copied().unwrap_or(0),
4588 packet.bytes.last().copied().unwrap_or(0),
4589 packet.bytes.as_ptr() as usize,
4590 );
4591 }
4592 thread::sleep(Duration::from_millis(2));
4593 }
4594 panic!("borrowed udp station packet was not received");
4595 }
4596
4597 #[derive(Clone, Debug, Default)]
4598 struct TestAuthenticator;
4599
4600 impl PacketAuthenticator for TestAuthenticator {
4601 type Error = core::convert::Infallible;
4602
4603 fn sign(
4604 &mut self,
4605 key_id: u32,
4606 nonce: u64,
4607 payload: &[u8],
4608 out: &mut Vec<u8>,
4609 ) -> Result<(), Self::Error> {
4610 out.extend_from_slice(&test_tag(key_id, nonce, payload));
4611 Ok(())
4612 }
4613
4614 fn verify(
4615 &mut self,
4616 key_id: u32,
4617 nonce: u64,
4618 payload: &[u8],
4619 tag: &[u8],
4620 ) -> Result<bool, Self::Error> {
4621 Ok(tag == test_tag(key_id, nonce, payload))
4622 }
4623 }
4624
4625 fn test_tag(key_id: u32, nonce: u64, payload: &[u8]) -> [u8; 8] {
4626 let mut acc = u64::from(key_id)
4627 .wrapping_mul(0x9E37_79B9_7F4A_7C15)
4628 .wrapping_add(nonce.rotate_left(17));
4629 for (index, byte) in payload.iter().copied().enumerate() {
4630 acc = acc.rotate_left(5) ^ (u64::from(byte) << ((index % 8) * 8));
4631 acc = acc.wrapping_mul(0x1000_0000_01B3);
4632 }
4633 acc.to_le_bytes()
4634 }
4635
4636 #[test]
4637 fn fake_transport_counts_batches_without_storing_packets() {
4638 let mut batch = PacketBatch::new();
4639 batch.push(packet(3));
4640 batch.push(packet(5));
4641
4642 let mut transport = FakeTransport::default();
4643 transport
4644 .send_batch(batch)
4645 .expect("fake transport is infallible");
4646
4647 assert_eq!(transport.batches_sent(), 1);
4648 assert_eq!(transport.packets_sent(), 2);
4649 assert_eq!(transport.bytes_sent(), 8);
4650 }
4651
4652 #[test]
4653 fn budgeted_transport_rejects_large_batch() {
4654 let mut batch = PacketBatch::new();
4655 batch.push(packet(8));
4656 batch.push(packet(8));
4657
4658 let mut transport = BudgetedTransport::new(FakeTransport::default(), 16, 12);
4659 let error = transport
4660 .send_batch(batch)
4661 .expect_err("batch should exceed budget");
4662 assert_eq!(
4663 error,
4664 TransportError::ByteBudgetExceeded {
4665 budget: 12,
4666 actual: 16
4667 }
4668 );
4669 assert_eq!(transport.inner().packets_sent(), 0);
4670
4671 let mut both_limits = PacketBatch::new();
4672 both_limits.push(packet(20));
4673 let error = transport
4674 .send_batch(both_limits)
4675 .expect_err("aggregate budget should retain priority");
4676 assert_eq!(
4677 error,
4678 TransportError::ByteBudgetExceeded {
4679 budget: 12,
4680 actual: 20,
4681 }
4682 );
4683
4684 let mut packet_limit = PacketBatch::new();
4685 packet_limit.push(packet(8));
4686 packet_limit.push(packet(20));
4687 let mut packet_budget = BudgetedTransport::new(FakeTransport::default(), 16, 64);
4688 let error = packet_budget
4689 .send_batch(packet_limit)
4690 .expect_err("single packet should exceed budget");
4691 assert_eq!(
4692 error,
4693 TransportError::ByteBudgetExceeded {
4694 budget: 16,
4695 actual: 20,
4696 }
4697 );
4698 assert_eq!(packet_budget.inner().packets_sent(), 0);
4699
4700 let mut valid = PacketBatch::new();
4701 valid.push(packet(8));
4702 valid.push(packet(12));
4703 packet_budget
4704 .send_batch(valid)
4705 .expect("valid batch should forward once");
4706 assert_eq!(packet_budget.inner().batches_sent(), 1);
4707 assert_eq!(packet_budget.inner().packets_sent(), 2);
4708 assert_eq!(packet_budget.inner().bytes_sent(), 20);
4709 }
4710
4711 #[test]
4712 fn packet_security_envelope_roundtrips_and_enforces_limits() {
4713 let config = PacketSecurityConfig {
4714 max_payload_bytes: 4,
4715 max_tag_bytes: 8,
4716 max_replay_history: 4,
4717 };
4718 let envelope = PacketSecurityEnvelope {
4719 key_id: 9,
4720 nonce: 42,
4721 payload: b"move".to_vec(),
4722 tag: vec![1, 2, 3, 4],
4723 };
4724 let mut bytes = Vec::new();
4725 envelope
4726 .encode(config, &mut bytes)
4727 .expect("envelope should encode");
4728 let mut borrowed_bytes = Vec::new();
4729 PacketSecurityEnvelope::encode_parts(
4730 config,
4731 envelope.key_id,
4732 envelope.nonce,
4733 &envelope.payload,
4734 &envelope.tag,
4735 &mut borrowed_bytes,
4736 )
4737 .expect("borrowed envelope should encode");
4738 assert_eq!(borrowed_bytes, bytes);
4739 let borrowed = PacketSecurityEnvelopeRef::decode(config, &bytes)
4740 .expect("borrowed envelope should decode");
4741 assert_eq!(borrowed.key_id, envelope.key_id);
4742 assert_eq!(borrowed.nonce, envelope.nonce);
4743 assert_eq!(borrowed.payload, envelope.payload);
4744 assert_eq!(borrowed.tag, envelope.tag);
4745 assert!(borrowed.payload.as_ptr() >= bytes.as_ptr());
4746 assert!(borrowed.tag.as_ptr() >= borrowed.payload.as_ptr());
4747 assert_eq!(
4748 PacketSecurityEnvelope::decode(config, &bytes).expect("envelope should decode"),
4749 envelope
4750 );
4751
4752 let too_large = PacketSecurityEnvelope {
4753 key_id: 9,
4754 nonce: 43,
4755 payload: b"large".to_vec(),
4756 tag: Vec::new(),
4757 }
4758 .encode(config, &mut Vec::new())
4759 .expect_err("payload should exceed configured budget");
4760 assert_eq!(
4761 too_large,
4762 PacketSecurityEncodeError::PayloadTooLarge {
4763 budget: 4,
4764 actual: 5
4765 }
4766 );
4767
4768 let mut bad = bytes;
4769 bad[16..20].copy_from_slice(&5_u32.to_le_bytes());
4770 assert_eq!(
4771 PacketSecurityEnvelope::decode(config, &bad)
4772 .expect_err("decoded payload length should exceed budget"),
4773 PacketSecurityDecodeError::PayloadTooLarge {
4774 budget: 4,
4775 actual: 5
4776 }
4777 );
4778 }
4779
4780 #[test]
4781 fn packet_key_ring_selects_active_key_and_accepts_retiring_key() {
4782 let now = Tick::new(10);
4783 let mut ring = PacketKeyRing::new(PacketKeyRingConfig { max_keys: 4 });
4784 ring.insert_active(1, now, 1)
4785 .expect("first key should insert");
4786 ring.insert_active(2, Tick::new(11), 10)
4787 .expect("second key should insert");
4788
4789 assert_eq!(
4790 ring.select_send_key(now).expect("key 1 should send").key_id,
4791 1
4792 );
4793 assert_eq!(
4794 ring.select_send_key(Tick::new(11))
4795 .expect("key 2 should send")
4796 .key_id,
4797 2
4798 );
4799
4800 ring.retire(1, Tick::new(12)).expect("key 1 should retire");
4801 assert_eq!(
4802 ring.accept_key(1, Tick::new(12))
4803 .expect("retiring key should still receive")
4804 .state,
4805 PacketKeyState::Retiring
4806 );
4807 assert_eq!(
4808 ring.select_send_key(Tick::new(12))
4809 .expect("active key should win")
4810 .key_id,
4811 2
4812 );
4813 assert_eq!(ring.stats().keys_inserted, 2);
4814 assert_eq!(ring.stats().keys_retired, 1);
4815 }
4816
4817 #[test]
4818 fn packet_key_ring_rejects_revoked_expired_and_over_capacity_keys() {
4819 let mut ring = PacketKeyRing::new(PacketKeyRingConfig { max_keys: 2 });
4820 ring.insert(PacketKeyDescriptor::active(1, Tick::new(1), 1).with_expiry(Tick::new(5)))
4821 .expect("expiring key should insert");
4822 ring.insert_active(2, Tick::new(1), 2)
4823 .expect("second key should insert");
4824 assert_eq!(
4825 ring.insert_active(3, Tick::new(1), 3)
4826 .expect_err("ring should be full"),
4827 PacketKeyRingError::CapacityFull { capacity: 2 }
4828 );
4829 assert_eq!(
4830 ring.insert_active(2, Tick::new(1), 2)
4831 .expect_err("duplicate should reject"),
4832 PacketKeyRingError::DuplicateKey(2)
4833 );
4834
4835 assert!(ring.accept_key(1, Tick::new(4)).is_ok());
4836 assert_eq!(
4837 ring.accept_key(1, Tick::new(5))
4838 .expect_err("expired key should reject"),
4839 PacketKeyRingError::KeyNotAccepted {
4840 key_id: 1,
4841 state: PacketKeyState::Active
4842 }
4843 );
4844 ring.revoke(2).expect("key should revoke");
4845 assert_eq!(
4846 ring.accept_key(2, Tick::new(4))
4847 .expect_err("revoked key should reject"),
4848 PacketKeyRingError::KeyNotAccepted {
4849 key_id: 2,
4850 state: PacketKeyState::Revoked
4851 }
4852 );
4853 assert_eq!(ring.remove_expired(Tick::new(5)), 1);
4854 assert_eq!(ring.len(), 1);
4855 assert_eq!(ring.stats().keys_revoked, 1);
4856 assert_eq!(ring.stats().keys_expired_removed, 1);
4857 }
4858
4859 #[test]
4860 fn packet_security_box_seals_opens_and_rejects_replay() {
4861 let mut sender = PacketSecurityBox::new(
4862 PacketSecurityConfig::default(),
4863 TestAuthenticator,
4864 PlaintextPacketCipher,
4865 );
4866 let mut receiver = PacketSecurityBox::new(
4867 PacketSecurityConfig::default(),
4868 TestAuthenticator,
4869 PlaintextPacketCipher,
4870 );
4871 let sealed = sender.seal(7, b"command").expect("packet should seal");
4872 assert_eq!(sender.stats().sealed, 1);
4873 let opened = receiver.open(&sealed).expect("packet should open");
4874 assert_eq!(opened, b"command");
4875 assert_eq!(receiver.stats().opened, 1);
4876
4877 let replay = receiver.open(&sealed).expect_err("replay should reject");
4878 match replay {
4879 PacketSecurityError::Replay { key_id, nonce } => {
4880 assert_eq!(key_id, 7);
4881 assert_eq!(nonce, 1);
4882 }
4883 other => panic!("unexpected error: {other}"),
4884 }
4885 assert_eq!(receiver.stats().replay_rejected, 1);
4886 }
4887
4888 #[test]
4889 fn packet_security_seal_into_matches_owned_and_reuses_scratch_atomically() {
4890 let config = PacketSecurityConfig::default();
4891 let mut owned = PacketSecurityBox::new(config, TestAuthenticator, PlaintextPacketCipher);
4892 let mut reused = PacketSecurityBox::new(config, TestAuthenticator, PlaintextPacketCipher);
4893 let mut scratch = PacketSecurityScratch::with_capacity(32, 8);
4894 let mut out = Vec::with_capacity(64);
4895
4896 let expected = owned
4897 .seal_with_nonce(7, 10, b"command")
4898 .expect("owned packet should seal");
4899 reused
4900 .seal_with_nonce_into(7, 10, b"command", &mut out, &mut scratch)
4901 .expect("reused packet should seal");
4902 assert_eq!(out, expected);
4903 let payload_ptr = scratch.sealed_payload.as_ptr();
4904 let tag_ptr = scratch.tag.as_ptr();
4905 let payload_capacity = scratch.retained_payload_capacity();
4906 let tag_capacity = scratch.retained_tag_capacity();
4907
4908 out.clear();
4909 let nonce = reused
4910 .seal_into(7, b"ack", &mut out, &mut scratch)
4911 .expect("allocated nonce packet should seal");
4912 assert_eq!(nonce, 1);
4913 let envelope =
4914 PacketSecurityEnvelope::decode(config, &out).expect("reused packet should decode");
4915 assert_eq!(envelope.nonce, nonce);
4916 assert_eq!(scratch.sealed_payload.as_ptr(), payload_ptr);
4917 assert_eq!(scratch.tag.as_ptr(), tag_ptr);
4918 assert_eq!(scratch.retained_payload_capacity(), payload_capacity);
4919 assert_eq!(scratch.retained_tag_capacity(), tag_capacity);
4920 assert_eq!(reused.stats().sealed, 2);
4921
4922 let before_error = out.clone();
4923 let too_large = vec![0_u8; config.max_payload_bytes + 1];
4924 assert!(matches!(
4925 reused.seal_with_nonce_into(7, 11, &too_large, &mut out, &mut scratch),
4926 Err(PacketSecurityError::Encode(
4927 PacketSecurityEncodeError::PayloadTooLarge { .. }
4928 ))
4929 ));
4930 assert_eq!(out, before_error);
4931
4932 let small_tag_config = PacketSecurityConfig {
4933 max_tag_bytes: 4,
4934 ..config
4935 };
4936 let mut small_tag =
4937 PacketSecurityBox::new(small_tag_config, TestAuthenticator, PlaintextPacketCipher);
4938 assert!(matches!(
4939 small_tag.seal_with_nonce_into(7, 12, b"tag", &mut out, &mut scratch),
4940 Err(PacketSecurityError::Encode(
4941 PacketSecurityEncodeError::TagTooLarge { .. }
4942 ))
4943 ));
4944 assert_eq!(out, before_error);
4945 }
4946
4947 #[test]
4948 fn packet_security_open_scratch_matches_owned_reuses_and_preserves_failed_input() {
4949 let config = PacketSecurityConfig::default();
4950 let mut sender = PacketSecurityBox::new(config, TestAuthenticator, PlaintextPacketCipher);
4951 let first = sender
4952 .seal_with_nonce(7, 10, b"first-command")
4953 .expect("first packet should seal");
4954 let second = sender
4955 .seal_with_nonce(7, 11, b"second-command")
4956 .expect("second packet should seal");
4957 let mut owned_receiver =
4958 PacketSecurityBox::new(config, TestAuthenticator, PlaintextPacketCipher);
4959 let expected = owned_receiver
4960 .open(&first)
4961 .expect("owned packet should open");
4962
4963 let mut receiver = PacketSecurityBox::new(config, TestAuthenticator, PlaintextPacketCipher);
4964 let mut scratch = PacketSecurityOpenScratch::with_capacity(32);
4965 let first_ptr = {
4966 let opened = receiver
4967 .open_with_scratch(&first, &mut scratch)
4968 .expect("scratch packet should open");
4969 assert_eq!(opened.key_id, 7);
4970 assert_eq!(opened.nonce, 10);
4971 assert_eq!(opened.payload, expected);
4972 opened.payload.as_ptr()
4973 };
4974 let retained = scratch.retained_payload_capacity();
4975 let second_payload = {
4976 let opened = receiver
4977 .open_with_scratch(&second, &mut scratch)
4978 .expect("second scratch packet should open");
4979 assert_eq!(opened.payload.as_ptr(), first_ptr);
4980 opened.payload.to_vec()
4981 };
4982 assert_eq!(second_payload, b"second-command");
4983 assert_eq!(scratch.retained_payload_capacity(), retained);
4984 assert_eq!(receiver.stats().opened, 2);
4985
4986 let before_failure = scratch.payload.clone();
4987 let mut tampered = sender
4988 .seal_with_nonce(7, 12, b"tampered")
4989 .expect("tampered source should seal");
4990 tampered[PACKET_SECURITY_HEADER_BYTES] ^= 0x55;
4991 assert!(matches!(
4992 receiver.open_with_scratch(&tampered, &mut scratch),
4993 Err(PacketSecurityError::AuthenticationFailed { .. })
4994 ));
4995 assert_eq!(scratch.payload, before_failure);
4996 }
4997
4998 #[test]
4999 fn packet_security_box_uses_key_ring_for_rotation_policy() {
5000 let mut sender_ring = PacketKeyRing::default();
5001 let mut receiver_ring = PacketKeyRing::default();
5002 sender_ring
5003 .insert_active(7, Tick::new(10), 1)
5004 .expect("sender key should insert");
5005 receiver_ring
5006 .insert_active(7, Tick::new(10), 1)
5007 .expect("receiver key should insert");
5008
5009 let mut sender = PacketSecurityBox::new(
5010 PacketSecurityConfig::default(),
5011 TestAuthenticator,
5012 PlaintextPacketCipher,
5013 );
5014 let mut receiver = PacketSecurityBox::new(
5015 PacketSecurityConfig::default(),
5016 TestAuthenticator,
5017 PlaintextPacketCipher,
5018 );
5019 let mut open_scratch = PacketSecurityOpenScratch::with_capacity(16);
5020
5021 let sealed = sender
5022 .seal_with_key_ring(&sender_ring, b"command", Tick::new(10))
5023 .expect("packet should seal through selected key");
5024 let envelope = PacketSecurityEnvelope::decode(PacketSecurityConfig::default(), &sealed)
5025 .expect("packet should decode");
5026 assert_eq!(envelope.key_id, 7);
5027 assert_eq!(
5028 receiver
5029 .open_with_key_ring_and_scratch(
5030 &receiver_ring,
5031 &sealed,
5032 Tick::new(10),
5033 &mut open_scratch,
5034 )
5035 .expect("packet should open through accepted key")
5036 .payload,
5037 b"command"
5038 );
5039
5040 sender_ring
5041 .insert_active(8, Tick::new(11), 10)
5042 .expect("rotated sender key should insert");
5043 receiver_ring
5044 .insert_active(8, Tick::new(11), 10)
5045 .expect("rotated receiver key should insert");
5046 receiver_ring
5047 .retire(7, Tick::new(11))
5048 .expect("old key should retire");
5049 let rotated = sender
5050 .seal_with_key_ring(&sender_ring, b"ack", Tick::new(11))
5051 .expect("rotated packet should seal");
5052 let rotated_envelope =
5053 PacketSecurityEnvelope::decode(PacketSecurityConfig::default(), &rotated)
5054 .expect("rotated packet should decode");
5055 assert_eq!(rotated_envelope.key_id, 8);
5056 assert_eq!(
5057 receiver
5058 .open_with_key_ring_and_scratch(
5059 &receiver_ring,
5060 &rotated,
5061 Tick::new(11),
5062 &mut open_scratch,
5063 )
5064 .expect("rotated packet should open")
5065 .payload,
5066 b"ack"
5067 );
5068
5069 receiver_ring.revoke(7).expect("old key should revoke");
5070 let stale = sender
5071 .seal_with_nonce(7, 99, b"stale")
5072 .expect("explicit stale-key packet should seal");
5073 let error = receiver
5074 .open_with_key_ring(&receiver_ring, &stale, Tick::new(12))
5075 .expect_err("revoked key should reject before auth");
5076 match error {
5077 PacketSecurityError::Key(PacketKeyRingError::KeyNotAccepted { key_id, state }) => {
5078 assert_eq!(key_id, 7);
5079 assert_eq!(state, PacketKeyState::Revoked);
5080 }
5081 other => panic!("unexpected error: {other}"),
5082 }
5083 assert_eq!(receiver.stats().key_rejected, 1);
5084 }
5085
5086 #[test]
5087 fn packet_security_box_rejects_tampered_payload() {
5088 let mut sender = PacketSecurityBox::new(
5089 PacketSecurityConfig::default(),
5090 TestAuthenticator,
5091 PlaintextPacketCipher,
5092 );
5093 let mut receiver = PacketSecurityBox::new(
5094 PacketSecurityConfig::default(),
5095 TestAuthenticator,
5096 PlaintextPacketCipher,
5097 );
5098
5099 let mut sealed = sender
5100 .seal_with_nonce(7, 10, b"command")
5101 .expect("packet should seal");
5102 let payload_offset = PACKET_SECURITY_HEADER_BYTES;
5103 sealed[payload_offset] ^= 0x55;
5104 let error = receiver
5105 .open(&sealed)
5106 .expect_err("tampered payload should reject");
5107 match error {
5108 PacketSecurityError::AuthenticationFailed { key_id, nonce } => {
5109 assert_eq!(key_id, 7);
5110 assert_eq!(nonce, 10);
5111 }
5112 other => panic!("unexpected error: {other}"),
5113 }
5114 assert_eq!(receiver.stats().auth_failed, 1);
5115 assert_eq!(receiver.replay().len(), 0);
5116 }
5117
5118 #[test]
5119 fn packet_replay_window_bounds_history() {
5120 let mut replay = PacketReplayWindow::new(2);
5121 assert!(replay.accept(1, 1));
5122 assert!(replay.accept(1, 2));
5123 assert!(!replay.accept(1, 2));
5124 assert!(replay.accept(1, 3));
5125 assert_eq!(replay.len(), 2);
5126 assert!(!replay.contains(1, 1));
5127 assert!(replay.accept(1, 1));
5128 }
5129
5130 #[test]
5131 fn bounded_duplicate_indexes_adapt_to_configured_capacity() {
5132 let ordered = PacketReplayWindow::new(HASHED_BOUNDED_SET_MIN_CAPACITY - 1);
5133 let hashed = PacketReplayWindow::new(HASHED_BOUNDED_SET_MIN_CAPACITY);
5134 assert!(!ordered.seen.is_hashed());
5135 assert!(hashed.seen.is_hashed());
5136
5137 let client = ReliableClientReceiver::new(ReliableClientConfig {
5138 max_delivered_history: HASHED_BOUNDED_SET_MIN_CAPACITY,
5139 ..ReliableClientConfig::default()
5140 });
5141 let station = ReliableStationReceiver::new(ReliableStationConfig {
5142 max_delivered_history: HASHED_BOUNDED_SET_MIN_CAPACITY,
5143 ..ReliableStationConfig::default()
5144 });
5145 assert!(client.delivered.is_hashed());
5146 assert!(station.delivered.is_hashed());
5147 }
5148
5149 #[test]
5150 fn endpoint_map_promotes_once_without_changing_entries() {
5151 let mut endpoints = AdaptiveEndpointMap::new();
5152 for key in 0..HASHED_ENDPOINT_MAP_MIN_ENTRIES - 1 {
5153 assert_eq!(endpoints.insert(key, key), None);
5154 }
5155 assert!(!endpoints.is_hashed());
5156 assert_eq!(endpoints.insert(0, usize::MAX), Some(0));
5157 assert!(!endpoints.is_hashed());
5158
5159 let final_key = HASHED_ENDPOINT_MAP_MIN_ENTRIES - 1;
5160 assert_eq!(endpoints.insert(final_key, final_key), None);
5161 assert!(endpoints.is_hashed());
5162 assert_eq!(endpoints.get(&0), Some(&usize::MAX));
5163 *endpoints.get_mut(&1).expect("migrated entry should exist") = 17;
5164 assert_eq!(endpoints.remove(&2), Some(2));
5165 assert_eq!(
5166 endpoints.values().len(),
5167 HASHED_ENDPOINT_MAP_MIN_ENTRIES - 1
5168 );
5169 assert_eq!(endpoints.get(&1), Some(&17));
5170 assert_eq!(endpoints.get(&2), None);
5171 }
5172
5173 #[test]
5174 fn in_memory_transports_preserve_queued_packets_during_map_promotion() {
5175 let hub = InMemoryTransportHub::default();
5176 for key in 0..HASHED_ENDPOINT_MAP_MIN_ENTRIES - 1 {
5177 let client_id = ClientId::new(u64::try_from(key).expect("test id fits u64"));
5178 hub.register_client(
5179 client_id,
5180 memory_addr(10_000 + u16::try_from(key).expect("test port fits u16")),
5181 )
5182 .expect("client should register");
5183 }
5184 let mut source = hub.endpoint_for_registered(ClientId::new(0));
5185 source
5186 .send(OutboundPacket {
5187 client_id: ClientId::new(1),
5188 bytes: vec![7],
5189 })
5190 .expect("packet should queue before promotion");
5191 let final_client = HASHED_ENDPOINT_MAP_MIN_ENTRIES - 1;
5192 hub.register_client(
5193 ClientId::new(u64::try_from(final_client).expect("test id fits u64")),
5194 memory_addr(10_000 + u16::try_from(final_client).expect("test port fits u16")),
5195 )
5196 .expect("threshold client should register");
5197 assert!(
5198 hub.lock_inner()
5199 .expect("hub should lock")
5200 .clients
5201 .is_hashed()
5202 );
5203 let mut target = hub.endpoint_for_registered(ClientId::new(1));
5204 assert_eq!(
5205 target
5206 .try_recv()
5207 .expect("receive should work")
5208 .expect("queued packet should survive")
5209 .bytes,
5210 vec![7]
5211 );
5212
5213 let mut stations = InMemoryStationTransport::default();
5214 for key in 0..HASHED_ENDPOINT_MAP_MIN_ENTRIES - 1 {
5215 stations.register_station(StationId::new(
5216 u32::try_from(key).expect("test station id fits u32"),
5217 ));
5218 }
5219 stations
5220 .send_station(StationOutboundPacket {
5221 source_station: StationId::new(0),
5222 target_station: StationId::new(1),
5223 bytes: vec![9],
5224 })
5225 .expect("station packet should queue before promotion");
5226 stations.register_station(StationId::new(
5227 u32::try_from(HASHED_ENDPOINT_MAP_MIN_ENTRIES - 1).expect("test station id fits u32"),
5228 ));
5229 assert!(stations.queues.is_hashed());
5230 assert_eq!(
5231 stations
5232 .try_recv_station(StationId::new(1))
5233 .expect("receive should work")
5234 .expect("queued station packet should survive")
5235 .bytes,
5236 vec![9]
5237 );
5238 }
5239
5240 #[test]
5241 fn in_memory_transport_delivers_bounded_packets() {
5242 let client_id = ClientId::new(7);
5243 let server_id = ClientId::new(0);
5244 let hub = InMemoryTransportHub::new(ClientTransportLimits {
5245 max_queued_packets_per_client: 2,
5246 max_packet_bytes: 8,
5247 });
5248 let mut client = hub
5249 .endpoint(client_id, memory_addr(20007))
5250 .expect("client should register");
5251 let mut server = hub
5252 .endpoint(server_id, memory_addr(20000))
5253 .expect("server should register");
5254
5255 client
5256 .send(OutboundPacket {
5257 client_id: server_id,
5258 bytes: b"command".to_vec(),
5259 })
5260 .expect("client packet should send");
5261 assert_eq!(
5262 hub.queued_len(server_id).expect("queue should exist"),
5263 Some(1)
5264 );
5265
5266 let inbound = server
5267 .try_recv()
5268 .expect("server receive should work")
5269 .expect("packet should exist");
5270 assert_eq!(inbound.client_id, Some(client_id));
5271 assert_eq!(inbound.remote_addr, memory_addr(20007));
5272 assert_eq!(inbound.bytes, b"command");
5273
5274 let stats = hub.stats().expect("stats should read");
5275 assert_eq!(stats.packets_sent, 1);
5276 assert_eq!(stats.packets_received, 1);
5277 assert_eq!(stats.bytes_sent, 7);
5278 assert_eq!(stats.bytes_received, 7);
5279 }
5280
5281 #[test]
5282 fn in_memory_transport_queues_allocate_lazily_and_retain_peak_capacity() {
5283 let client_id = ClientId::new(7);
5284 let server_id = ClientId::new(0);
5285 let hub = InMemoryTransportHub::default();
5286 let mut client = hub
5287 .endpoint(client_id, memory_addr(20007))
5288 .expect("client should register");
5289 let mut server = hub
5290 .endpoint(server_id, memory_addr(20000))
5291 .expect("server should register");
5292 assert_eq!(
5293 hub.retained_queue_capacity().expect("capacity should read"),
5294 0
5295 );
5296
5297 for _ in 0..8 {
5298 client
5299 .send(OutboundPacket {
5300 client_id: server_id,
5301 bytes: vec![1],
5302 })
5303 .expect("burst packet should send");
5304 }
5305 let peak = hub
5306 .queued_capacity(server_id)
5307 .expect("capacity should read")
5308 .expect("server should remain registered");
5309 assert!(peak >= 8);
5310 for _ in 0..8 {
5311 assert!(server.try_recv().expect("receive should work").is_some());
5312 }
5313 assert_eq!(
5314 hub.queued_capacity(server_id)
5315 .expect("capacity should read"),
5316 Some(peak)
5317 );
5318
5319 client
5320 .send(OutboundPacket {
5321 client_id: server_id,
5322 bytes: vec![2],
5323 })
5324 .expect("packet should queue before teardown");
5325 let released = hub
5326 .unregister_client(server_id)
5327 .expect("unregister should lock")
5328 .expect("server should unregister");
5329 assert_eq!(released.remote_addr, memory_addr(20000));
5330 assert_eq!(released.queued_packets, 1);
5331 assert_eq!(released.retained_queue_capacity, peak);
5332 assert_eq!(hub.queued_len(server_id).expect("lookup should work"), None);
5333 assert_eq!(
5334 hub.retained_queue_capacity().expect("capacity should read"),
5335 0
5336 );
5337 assert!(
5338 hub.unregister_client(server_id)
5339 .expect("second unregister should lock")
5340 .is_none()
5341 );
5342 }
5343
5344 #[test]
5345 fn in_memory_transport_batch_preserves_ordered_partial_commit() {
5346 let source_id = ClientId::new(7);
5347 let target_id = ClientId::new(0);
5348 let hub = InMemoryTransportHub::new(ClientTransportLimits {
5349 max_queued_packets_per_client: 65,
5350 max_packet_bytes: 4,
5351 });
5352 let mut source = hub
5353 .endpoint(source_id, memory_addr(20007))
5354 .expect("source should register");
5355 hub.endpoint(target_id, memory_addr(20000))
5356 .expect("target should register");
5357 let mut batch = PacketBatch::new();
5358 for byte in 0..66_u8 {
5359 batch.push(OutboundPacket {
5360 client_id: target_id,
5361 bytes: vec![byte],
5362 });
5363 }
5364
5365 assert_eq!(
5366 source.send_batch(batch),
5367 Err(InMemoryTransportError::QueueFull {
5368 client_id: target_id,
5369 capacity: 65,
5370 })
5371 );
5372 let mut target = hub.endpoint_for_registered(target_id);
5373 for byte in 0..65_u8 {
5374 assert_eq!(
5375 target
5376 .try_recv()
5377 .expect("receive should work")
5378 .expect("successful prefix should commit")
5379 .bytes,
5380 vec![byte]
5381 );
5382 }
5383 assert!(target.try_recv().expect("receive should work").is_none());
5384 let stats = hub.stats().expect("stats should read");
5385 assert_eq!(stats.packets_sent, 65);
5386 assert_eq!(stats.bytes_sent, 65);
5387 assert_eq!(stats.packets_rejected_full, 1);
5388 }
5389
5390 #[test]
5391 fn in_memory_transport_rejects_full_queue_and_large_packet() {
5392 let client_id = ClientId::new(7);
5393 let server_id = ClientId::new(0);
5394 let hub = InMemoryTransportHub::new(ClientTransportLimits {
5395 max_queued_packets_per_client: 1,
5396 max_packet_bytes: 4,
5397 });
5398 let mut client = hub
5399 .endpoint(client_id, memory_addr(20007))
5400 .expect("client should register");
5401 hub.endpoint(server_id, memory_addr(20000))
5402 .expect("server should register");
5403
5404 client
5405 .send(OutboundPacket {
5406 client_id: server_id,
5407 bytes: vec![0; 4],
5408 })
5409 .expect("first packet should send");
5410
5411 let full = client
5412 .send(OutboundPacket {
5413 client_id: server_id,
5414 bytes: vec![0; 4],
5415 })
5416 .expect_err("queue should be full");
5417 assert_eq!(
5418 full,
5419 InMemoryTransportError::QueueFull {
5420 client_id: server_id,
5421 capacity: 1
5422 }
5423 );
5424
5425 let large = client
5426 .send(OutboundPacket {
5427 client_id: server_id,
5428 bytes: vec![0; 5],
5429 })
5430 .expect_err("packet should exceed budget");
5431 assert_eq!(
5432 large,
5433 InMemoryTransportError::PacketTooLarge {
5434 budget: 4,
5435 actual: 5
5436 }
5437 );
5438
5439 let missing_id = ClientId::new(99);
5440 let missing = client
5441 .send(OutboundPacket {
5442 client_id: missing_id,
5443 bytes: vec![0; 4],
5444 })
5445 .expect_err("missing target should reject");
5446 assert_eq!(missing, InMemoryTransportError::MissingTarget(missing_id));
5447
5448 let stats = hub.stats().expect("stats should read");
5449 assert_eq!(stats.packets_sent, 1);
5450 assert_eq!(stats.bytes_sent, 4);
5451 assert_eq!(stats.packets_rejected_full, 1);
5452 assert_eq!(stats.packets_rejected_bytes, 1);
5453 }
5454
5455 #[test]
5456 fn reliable_client_frame_roundtrips_data_and_ack() {
5457 let data = ReliableClientFrame::Data {
5458 sequence: 42,
5459 payload: b"command".to_vec(),
5460 };
5461 let mut bytes = Vec::new();
5462 data.encode(&mut bytes).expect("data frame should encode");
5463 let mut direct = Vec::new();
5464 ReliableClientFrame::encode_data(42, b"command", &mut direct)
5465 .expect("borrowed data frame should encode");
5466 assert_eq!(direct, bytes);
5467 assert_eq!(
5468 ReliableClientFrame::decode_ref(&bytes).expect("data frame view should decode"),
5469 ReliableClientFrameRef::Data {
5470 sequence: 42,
5471 payload: b"command"
5472 }
5473 );
5474 assert_eq!(
5475 ReliableClientFrame::decode(&bytes).expect("data frame should decode"),
5476 data
5477 );
5478 let truncated = &bytes[..bytes.len() - 1];
5479 assert_eq!(
5480 ReliableClientFrame::decode_ref(truncated).expect_err("view should reject truncation"),
5481 ReliableClientFrame::decode(truncated).expect_err("owned should reject truncation")
5482 );
5483 let mut trailing = bytes.clone();
5484 trailing.push(0);
5485 assert_eq!(
5486 ReliableClientFrame::decode_ref(&trailing).expect_err("view should reject trailing"),
5487 ReliableClientFrame::decode(&trailing).expect_err("owned should reject trailing")
5488 );
5489
5490 let ack = ReliableClientFrame::Ack { sequence: 42 };
5491 bytes.clear();
5492 ack.encode(&mut bytes).expect("ack frame should encode");
5493 assert_eq!(
5494 ReliableClientFrame::decode(&bytes).expect("ack frame should decode"),
5495 ack
5496 );
5497 }
5498
5499 #[test]
5500 fn reliable_client_endpoint_delivers_payload_and_acknowledges() {
5501 let client_id = ClientId::new(7);
5502 let server_id = ClientId::new(0);
5503 let hub = InMemoryTransportHub::default();
5504 let mut client_transport = hub
5505 .endpoint(client_id, memory_addr(20007))
5506 .expect("client should register");
5507 let mut server_transport = hub
5508 .endpoint(server_id, memory_addr(20000))
5509 .expect("server should register");
5510 let mut client = ReliableClientEndpoint::default();
5511 let mut server = ReliableClientEndpoint::default();
5512
5513 let sequence = client
5514 .send(
5515 &mut client_transport,
5516 OutboundPacket {
5517 client_id: server_id,
5518 bytes: b"command".to_vec(),
5519 },
5520 0,
5521 )
5522 .expect("reliable command should send");
5523 assert_eq!(sequence, 1);
5524 assert_eq!(client.sender.in_flight_len(), 1);
5525
5526 let raw = server_transport
5527 .try_recv()
5528 .expect("server receive should work")
5529 .expect("data packet should exist");
5530 let wire_pointer = raw.bytes.as_ptr();
5531 let delivered = server
5532 .handle_inbound(&mut server_transport, raw)
5533 .expect("data packet should handle")
5534 .expect("first data packet should deliver");
5535 assert_eq!(delivered.client_id, Some(client_id));
5536 assert_eq!(delivered.remote_addr, memory_addr(20007));
5537 assert_eq!(delivered.bytes, b"command");
5538 assert_eq!(delivered.bytes.as_ptr(), wire_pointer);
5539 assert_eq!(server.receiver.stats().data_delivered, 1);
5540 assert_eq!(server.receiver.stats().acks_sent, 1);
5541
5542 let ack = client_transport
5543 .try_recv()
5544 .expect("client ACK receive should work")
5545 .expect("ACK packet should exist");
5546 assert_eq!(
5547 client
5548 .handle_inbound(&mut client_transport, ack)
5549 .expect("ACK should handle"),
5550 None
5551 );
5552 assert_eq!(client.sender.in_flight_len(), 0);
5553 assert_eq!(client.sender.stats().acks_received, 1);
5554 }
5555
5556 #[test]
5557 fn reliable_client_endpoint_retries_and_suppresses_duplicate_delivery() {
5558 let client_id = ClientId::new(7);
5559 let server_id = ClientId::new(0);
5560 let hub = InMemoryTransportHub::default();
5561 let mut client_transport = hub
5562 .endpoint(client_id, memory_addr(20007))
5563 .expect("client should register");
5564 let mut server_transport = hub
5565 .endpoint(server_id, memory_addr(20000))
5566 .expect("server should register");
5567 let mut client = ReliableClientEndpoint::default();
5568 let mut server = ReliableClientEndpoint::default();
5569
5570 client
5571 .send(
5572 &mut client_transport,
5573 OutboundPacket {
5574 client_id: server_id,
5575 bytes: b"idempotent-command".to_vec(),
5576 },
5577 0,
5578 )
5579 .expect("reliable command should send");
5580 let retry = client
5581 .retry_due(&mut client_transport, 2)
5582 .expect("retry should send");
5583 assert_eq!(retry.examined, 1);
5584 assert_eq!(retry.retried, 1);
5585 assert_eq!(retry.timed_out, 0);
5586 assert_eq!(client.sender.stats().retries_sent, 1);
5587 let retained_keys = client.retry_scratch.retained_key_capacity();
5588 assert!(retained_keys >= 1);
5589 assert_eq!(
5590 client
5591 .retry_due(&mut client_transport, 2)
5592 .expect("non-due scan should succeed"),
5593 ReliableRetryReport::default()
5594 );
5595 assert_eq!(client.retry_scratch.retained_key_capacity(), retained_keys);
5596 assert_eq!(
5597 hub.queued_len(server_id).expect("queue should exist"),
5598 Some(2)
5599 );
5600
5601 let first_raw = server_transport
5602 .try_recv()
5603 .expect("server receive should work")
5604 .expect("first data packet should exist");
5605 let delivered = server
5606 .handle_inbound(&mut server_transport, first_raw)
5607 .expect("first data packet should handle")
5608 .expect("first data packet should deliver");
5609 assert_eq!(delivered.bytes, b"idempotent-command");
5610
5611 let duplicate_raw = server_transport
5612 .try_recv()
5613 .expect("server receive should work")
5614 .expect("duplicate data packet should exist");
5615 assert_eq!(
5616 server
5617 .handle_inbound(&mut server_transport, duplicate_raw)
5618 .expect("duplicate data packet should handle"),
5619 None
5620 );
5621 assert_eq!(server.receiver.stats().data_delivered, 1);
5622 assert_eq!(server.receiver.stats().duplicates_suppressed, 1);
5623 assert_eq!(server.receiver.stats().acks_sent, 2);
5624 }
5625
5626 #[test]
5627 fn reliable_client_failed_retry_preserves_attempt_before_timeout() {
5628 let client_id = ClientId::new(7);
5629 let server_id = ClientId::new(0);
5630 let hub = InMemoryTransportHub::new(ClientTransportLimits {
5631 max_queued_packets_per_client: 1,
5632 max_packet_bytes: 128,
5633 });
5634 let mut client_transport = hub
5635 .endpoint(client_id, memory_addr(20007))
5636 .expect("client should register");
5637 let mut server_transport = hub
5638 .endpoint(server_id, memory_addr(20000))
5639 .expect("server should register");
5640 let mut sender = ReliableClientSender::new(ReliableClientConfig {
5641 max_in_flight_per_peer: 1,
5642 retry_after_ticks: 1,
5643 max_attempts: 2,
5644 max_payload_bytes: 64,
5645 max_delivered_history: 0,
5646 });
5647 let mut scratch = ReliableClientRetryScratch::new();
5648 sender
5649 .send(
5650 &mut client_transport,
5651 OutboundPacket {
5652 client_id: server_id,
5653 bytes: b"retry".to_vec(),
5654 },
5655 0,
5656 )
5657 .expect("initial packet should fill queue");
5658
5659 assert!(matches!(
5660 sender.retry_due_into(&mut client_transport, 1, &mut scratch),
5661 Err(ReliableClientError::Transport(
5662 InMemoryTransportError::QueueFull { .. }
5663 ))
5664 ));
5665 server_transport
5666 .try_recv()
5667 .expect("queue should read")
5668 .expect("initial packet should remain");
5669 assert_eq!(
5670 sender
5671 .retry_due_into(&mut client_transport, 1, &mut scratch)
5672 .expect("failed attempt must remain retryable")
5673 .retried,
5674 1
5675 );
5676 let timeout = sender
5677 .retry_due_into(&mut client_transport, 2, &mut scratch)
5678 .expect("exhausted packet should time out");
5679 assert_eq!(timeout.retried, 0);
5680 assert_eq!(timeout.timed_out, 1);
5681 assert_eq!(sender.in_flight_len(), 0);
5682 assert_eq!(sender.in_flight_for(server_id), 0);
5683 assert!(!sender.in_flight_by_peer.contains_key(&server_id));
5684 }
5685
5686 #[test]
5687 fn reliable_client_receiver_bounds_duplicate_history() {
5688 let client_id = ClientId::new(7);
5689 let server_id = ClientId::new(0);
5690 let hub = InMemoryTransportHub::default();
5691 hub.endpoint(client_id, memory_addr(20007))
5692 .expect("client should register");
5693 let mut server_transport = hub
5694 .endpoint(server_id, memory_addr(20000))
5695 .expect("server should register");
5696 let config = ReliableClientConfig {
5697 max_in_flight_per_peer: 8,
5698 retry_after_ticks: 2,
5699 max_attempts: 4,
5700 max_payload_bytes: DEFAULT_RELIABLE_CLIENT_MAX_PAYLOAD_BYTES,
5701 max_delivered_history: 1,
5702 };
5703 let mut server = ReliableClientEndpoint::new(config);
5704
5705 let packet = |sequence: u64, payload: &[u8]| {
5706 let mut bytes = Vec::new();
5707 ReliableClientFrame::Data {
5708 sequence,
5709 payload: payload.to_vec(),
5710 }
5711 .encode(&mut bytes)
5712 .expect("data frame should encode");
5713 InboundPacket {
5714 client_id: Some(client_id),
5715 remote_addr: memory_addr(20007),
5716 bytes,
5717 }
5718 };
5719
5720 assert!(
5721 server
5722 .handle_inbound(&mut server_transport, packet(1, b"first"))
5723 .expect("first data packet should handle")
5724 .is_some()
5725 );
5726 assert_eq!(
5727 server
5728 .handle_inbound(&mut server_transport, packet(1, b"first-duplicate"))
5729 .expect("duplicate data packet should handle"),
5730 None
5731 );
5732 assert!(
5733 server
5734 .handle_inbound(&mut server_transport, packet(2, b"second"))
5735 .expect("second data packet should handle")
5736 .is_some()
5737 );
5738 assert!(
5739 server
5740 .handle_inbound(&mut server_transport, packet(1, b"first-after-eviction"))
5741 .expect("evicted data packet should handle")
5742 .is_some()
5743 );
5744 assert_eq!(server.receiver.stats().data_delivered, 3);
5745 assert_eq!(server.receiver.stats().duplicates_suppressed, 1);
5746 }
5747
5748 #[test]
5749 fn reliable_client_sender_enforces_payload_and_window_limits() {
5750 let client_id = ClientId::new(7);
5751 let server_id = ClientId::new(0);
5752 let hub = InMemoryTransportHub::default();
5753 let mut client_transport = hub
5754 .endpoint(client_id, memory_addr(20007))
5755 .expect("client should register");
5756 hub.endpoint(server_id, memory_addr(20000))
5757 .expect("server should register");
5758 let config = ReliableClientConfig {
5759 max_in_flight_per_peer: 1,
5760 retry_after_ticks: 2,
5761 max_attempts: 4,
5762 max_payload_bytes: 4,
5763 max_delivered_history: DEFAULT_RELIABLE_CLIENT_DELIVERED_HISTORY,
5764 };
5765 let mut client = ReliableClientEndpoint::new(config);
5766
5767 let too_large = client
5768 .send(
5769 &mut client_transport,
5770 OutboundPacket {
5771 client_id: server_id,
5772 bytes: vec![0; 5],
5773 },
5774 0,
5775 )
5776 .expect_err("payload should exceed configured budget");
5777 match too_large {
5778 ReliableClientError::PayloadTooLarge { budget, actual } => {
5779 assert_eq!(budget, 4);
5780 assert_eq!(actual, 5);
5781 }
5782 other => panic!("unexpected error: {other}"),
5783 }
5784
5785 client
5786 .send(
5787 &mut client_transport,
5788 OutboundPacket {
5789 client_id: server_id,
5790 bytes: vec![0; 4],
5791 },
5792 0,
5793 )
5794 .expect("first packet should fit");
5795 let full = client
5796 .send(
5797 &mut client_transport,
5798 OutboundPacket {
5799 client_id: server_id,
5800 bytes: vec![1; 4],
5801 },
5802 0,
5803 )
5804 .expect_err("in-flight window should be full");
5805 match full {
5806 ReliableClientError::WindowFull {
5807 peer_client,
5808 capacity,
5809 } => {
5810 assert_eq!(peer_client, server_id);
5811 assert_eq!(capacity, 1);
5812 }
5813 other => panic!("unexpected error: {other}"),
5814 }
5815 }
5816
5817 #[test]
5818 fn reliable_client_window_counts_track_peers_and_saturated_sequence_replacement() {
5819 let first_peer = ClientId::new(1);
5820 let second_peer = ClientId::new(2);
5821 let mut transport = FakeTransport::default();
5822 let mut sender = ReliableClientSender::new(ReliableClientConfig {
5823 max_in_flight_per_peer: 3,
5824 retry_after_ticks: 2,
5825 max_attempts: 4,
5826 max_payload_bytes: 16,
5827 max_delivered_history: 0,
5828 });
5829 let mut send = |sender: &mut ReliableClientSender, peer_client| {
5830 sender
5831 .send(
5832 &mut transport,
5833 OutboundPacket {
5834 client_id: peer_client,
5835 bytes: b"count".to_vec(),
5836 },
5837 0,
5838 )
5839 .expect("bounded packet should send")
5840 };
5841
5842 assert_eq!(send(&mut sender, first_peer), 1);
5843 assert_eq!(send(&mut sender, first_peer), 2);
5844 assert_eq!(send(&mut sender, second_peer), 1);
5845 assert_eq!(sender.in_flight_for(first_peer), 2);
5846 assert_eq!(sender.in_flight_for(second_peer), 1);
5847 assert!(sender.acknowledge(first_peer, 1));
5848 assert_eq!(sender.in_flight_for(first_peer), 1);
5849
5850 sender.next_sequence.insert(first_peer, u64::MAX);
5851 assert_eq!(send(&mut sender, first_peer), u64::MAX);
5852 assert_eq!(sender.in_flight_for(first_peer), 2);
5853 assert_eq!(send(&mut sender, first_peer), u64::MAX);
5854 assert_eq!(sender.in_flight_for(first_peer), 2);
5855
5856 assert!(sender.acknowledge(first_peer, u64::MAX));
5857 assert!(sender.acknowledge(first_peer, 2));
5858 assert_eq!(sender.in_flight_for(first_peer), 0);
5859 assert!(!sender.in_flight_by_peer.contains_key(&first_peer));
5860 assert_eq!(sender.in_flight_for(second_peer), 1);
5861 }
5862
5863 #[test]
5864 fn reliable_client_endpoint_rejects_unknown_packet_source() {
5865 let mut endpoint = ReliableClientEndpoint::default();
5866 let mut transport = FakeTransport::default();
5867 let mut bytes = Vec::new();
5868 ReliableClientFrame::Ack { sequence: 1 }
5869 .encode(&mut bytes)
5870 .expect("ACK should encode");
5871 let error = endpoint
5872 .handle_inbound(
5873 &mut transport,
5874 InboundPacket {
5875 client_id: None,
5876 remote_addr: memory_addr(20007),
5877 bytes,
5878 },
5879 )
5880 .expect_err("unknown source should be rejected");
5881 match error {
5882 ReliableClientError::MissingSourceClient => {}
5883 other => panic!("unexpected error: {other}"),
5884 }
5885 }
5886
5887 #[test]
5888 fn in_memory_station_transport_delivers_bounded_packets() {
5889 let mut transport = InMemoryStationTransport::new(StationTransportLimits {
5890 max_queued_packets_per_station: 2,
5891 max_packet_bytes: 8,
5892 });
5893 transport.register_station(StationId::new(2));
5894
5895 transport
5896 .send_station(station_packet(4))
5897 .expect("station packet should send");
5898 assert_eq!(transport.queued_len(StationId::new(2)), Some(1));
5899
5900 let packet = transport
5901 .try_recv_station(StationId::new(2))
5902 .expect("receive should work")
5903 .expect("packet should exist");
5904 assert_eq!(packet.source_station, StationId::new(1));
5905 assert_eq!(packet.target_station, StationId::new(2));
5906 assert_eq!(packet.bytes, vec![1; 4]);
5907 assert_eq!(transport.stats().packets_sent, 1);
5908 assert_eq!(transport.stats().packets_received, 1);
5909 }
5910
5911 #[test]
5912 fn in_memory_station_queues_allocate_lazily_and_retain_peak_capacity() {
5913 let target = StationId::new(2);
5914 let mut transport = InMemoryStationTransport::default();
5915 transport.register_station(target);
5916 assert_eq!(transport.retained_queue_capacity(), 0);
5917
5918 for _ in 0..8 {
5919 transport
5920 .send_station(station_packet(1))
5921 .expect("burst packet should send");
5922 }
5923 let peak = transport
5924 .queued_capacity(target)
5925 .expect("station should remain registered");
5926 assert!(peak >= 8);
5927 for _ in 0..8 {
5928 assert!(
5929 transport
5930 .try_recv_station(target)
5931 .expect("receive should work")
5932 .is_some()
5933 );
5934 }
5935 assert_eq!(transport.queued_capacity(target), Some(peak));
5936
5937 transport
5938 .send_station(station_packet(1))
5939 .expect("packet should queue before teardown");
5940 assert_eq!(transport.unregister_station(target), Some(1));
5941 assert_eq!(transport.queued_len(target), None);
5942 assert_eq!(transport.retained_queue_capacity(), 0);
5943 assert_eq!(transport.unregister_station(target), None);
5944 }
5945
5946 #[test]
5947 fn in_memory_station_transport_rejects_full_queue_and_large_packet() {
5948 let mut transport = InMemoryStationTransport::new(StationTransportLimits {
5949 max_queued_packets_per_station: 1,
5950 max_packet_bytes: 4,
5951 });
5952 transport.register_station(StationId::new(2));
5953 transport
5954 .send_station(station_packet(4))
5955 .expect("first packet should send");
5956
5957 let full = transport
5958 .send_station(station_packet(4))
5959 .expect_err("queue should be full");
5960 assert_eq!(
5961 full,
5962 StationTransportError::QueueFull {
5963 station_id: StationId::new(2),
5964 capacity: 1
5965 }
5966 );
5967
5968 let large = transport
5969 .send_station(station_packet(5))
5970 .expect_err("packet should exceed budget");
5971 assert_eq!(
5972 large,
5973 StationTransportError::PacketTooLarge {
5974 budget: 4,
5975 actual: 5
5976 }
5977 );
5978 assert_eq!(transport.stats().packets_rejected_full, 1);
5979 assert_eq!(transport.stats().packets_rejected_bytes, 1);
5980 }
5981
5982 #[test]
5983 fn reliable_station_frame_roundtrips_data_and_ack() {
5984 let data = ReliableStationFrame::Data {
5985 sequence: 42,
5986 payload: b"station-event".to_vec(),
5987 };
5988 let mut bytes = Vec::new();
5989 data.encode(&mut bytes).expect("data frame should encode");
5990 let mut direct = Vec::new();
5991 ReliableStationFrame::encode_data(42, b"station-event", &mut direct)
5992 .expect("borrowed data frame should encode");
5993 assert_eq!(direct, bytes);
5994 assert_eq!(
5995 ReliableStationFrame::decode_ref(&bytes).expect("data frame view should decode"),
5996 ReliableStationFrameRef::Data {
5997 sequence: 42,
5998 payload: b"station-event"
5999 }
6000 );
6001 assert_eq!(
6002 ReliableStationFrame::decode(&bytes).expect("data frame should decode"),
6003 data
6004 );
6005 let truncated = &bytes[..bytes.len() - 1];
6006 assert_eq!(
6007 ReliableStationFrame::decode_ref(truncated).expect_err("view should reject truncation"),
6008 ReliableStationFrame::decode(truncated).expect_err("owned should reject truncation")
6009 );
6010 let mut trailing = bytes.clone();
6011 trailing.push(0);
6012 assert_eq!(
6013 ReliableStationFrame::decode_ref(&trailing).expect_err("view should reject trailing"),
6014 ReliableStationFrame::decode(&trailing).expect_err("owned should reject trailing")
6015 );
6016
6017 let ack = ReliableStationFrame::Ack { sequence: 42 };
6018 bytes.clear();
6019 ack.encode(&mut bytes).expect("ack frame should encode");
6020 assert_eq!(
6021 ReliableStationFrame::decode(&bytes).expect("ack frame should decode"),
6022 ack
6023 );
6024 }
6025
6026 #[test]
6027 fn reliable_station_endpoint_delivers_payload_and_acknowledges() {
6028 let station_one = StationId::new(1);
6029 let station_two = StationId::new(2);
6030 let mut transport = InMemoryStationTransport::default();
6031 transport.register_station(station_one);
6032 transport.register_station(station_two);
6033 let mut first = ReliableStationEndpoint::default();
6034 let mut second = ReliableStationEndpoint::default();
6035
6036 let sequence = first
6037 .send(
6038 &mut transport,
6039 StationOutboundPacket {
6040 source_station: station_one,
6041 target_station: station_two,
6042 bytes: b"event".to_vec(),
6043 },
6044 0,
6045 )
6046 .expect("reliable packet should send");
6047 assert_eq!(sequence, 1);
6048 assert_eq!(first.sender.in_flight_len(), 1);
6049
6050 let raw = transport
6051 .try_recv_station(station_two)
6052 .expect("receive should work")
6053 .expect("data packet should exist");
6054 let wire_pointer = raw.bytes.as_ptr();
6055 let delivered = second
6056 .handle_inbound(&mut transport, raw)
6057 .expect("data packet should handle")
6058 .expect("first data packet should deliver");
6059 assert_eq!(delivered.source_station, station_one);
6060 assert_eq!(delivered.target_station, station_two);
6061 assert_eq!(delivered.bytes, b"event");
6062 assert_eq!(delivered.bytes.as_ptr(), wire_pointer);
6063 assert_eq!(second.receiver.stats().data_delivered, 1);
6064 assert_eq!(second.receiver.stats().acks_sent, 1);
6065
6066 let ack = transport
6067 .try_recv_station(station_one)
6068 .expect("ack receive should work")
6069 .expect("ack packet should exist");
6070 assert_eq!(
6071 first
6072 .handle_inbound(&mut transport, ack)
6073 .expect("ack should handle"),
6074 None
6075 );
6076 assert_eq!(first.sender.in_flight_len(), 0);
6077 assert_eq!(first.sender.stats().acks_received, 1);
6078 }
6079
6080 #[test]
6081 fn reliable_station_endpoint_retries_and_suppresses_duplicate_delivery() {
6082 let station_one = StationId::new(1);
6083 let station_two = StationId::new(2);
6084 let mut transport = InMemoryStationTransport::default();
6085 transport.register_station(station_one);
6086 transport.register_station(station_two);
6087 let mut first = ReliableStationEndpoint::default();
6088 let mut second = ReliableStationEndpoint::default();
6089
6090 first
6091 .send(
6092 &mut transport,
6093 StationOutboundPacket {
6094 source_station: station_one,
6095 target_station: station_two,
6096 bytes: b"idempotent-event".to_vec(),
6097 },
6098 0,
6099 )
6100 .expect("reliable packet should send");
6101 let retry = first
6102 .retry_due(&mut transport, 2)
6103 .expect("retry should send");
6104 assert_eq!(retry.examined, 1);
6105 assert_eq!(retry.retried, 1);
6106 assert_eq!(retry.timed_out, 0);
6107 assert_eq!(first.sender.stats().retries_sent, 1);
6108 let retained_keys = first.retry_scratch.retained_key_capacity();
6109 assert!(retained_keys >= 1);
6110 assert_eq!(
6111 first
6112 .retry_due(&mut transport, 2)
6113 .expect("non-due scan should succeed"),
6114 ReliableRetryReport::default()
6115 );
6116 assert_eq!(first.retry_scratch.retained_key_capacity(), retained_keys);
6117 assert_eq!(transport.queued_len(station_two), Some(2));
6118
6119 let first_raw = transport
6120 .try_recv_station(station_two)
6121 .expect("receive should work")
6122 .expect("first data packet should exist");
6123 let delivered = second
6124 .handle_inbound(&mut transport, first_raw)
6125 .expect("first data packet should handle")
6126 .expect("first data packet should deliver");
6127 assert_eq!(delivered.bytes, b"idempotent-event");
6128
6129 let duplicate_raw = transport
6130 .try_recv_station(station_two)
6131 .expect("receive should work")
6132 .expect("duplicate data packet should exist");
6133 assert_eq!(
6134 second
6135 .handle_inbound(&mut transport, duplicate_raw)
6136 .expect("duplicate data packet should handle"),
6137 None
6138 );
6139 assert_eq!(second.receiver.stats().data_delivered, 1);
6140 assert_eq!(second.receiver.stats().duplicates_suppressed, 1);
6141 assert_eq!(second.receiver.stats().acks_sent, 2);
6142 }
6143
6144 #[test]
6145 fn reliable_station_failed_retry_preserves_attempt_before_timeout() {
6146 let source = StationId::new(1);
6147 let target = StationId::new(2);
6148 let mut transport = InMemoryStationTransport::new(StationTransportLimits {
6149 max_queued_packets_per_station: 1,
6150 max_packet_bytes: 128,
6151 });
6152 transport.register_station(target);
6153 let mut sender = ReliableStationSender::new(ReliableStationConfig {
6154 max_in_flight_per_target: 1,
6155 retry_after_ticks: 1,
6156 max_attempts: 2,
6157 max_payload_bytes: 64,
6158 max_delivered_history: 0,
6159 });
6160 let mut scratch = ReliableStationRetryScratch::new();
6161 sender
6162 .send(
6163 &mut transport,
6164 StationOutboundPacket {
6165 source_station: source,
6166 target_station: target,
6167 bytes: b"retry".to_vec(),
6168 },
6169 0,
6170 )
6171 .expect("initial packet should fill queue");
6172
6173 assert!(matches!(
6174 sender.retry_due_into(&mut transport, 1, &mut scratch),
6175 Err(ReliableStationError::Transport(
6176 StationTransportError::QueueFull { .. }
6177 ))
6178 ));
6179 transport
6180 .try_recv_station(target)
6181 .expect("queue should read")
6182 .expect("initial packet should remain");
6183 assert_eq!(
6184 sender
6185 .retry_due_into(&mut transport, 1, &mut scratch)
6186 .expect("failed attempt must remain retryable")
6187 .retried,
6188 1
6189 );
6190 let timeout = sender
6191 .retry_due_into(&mut transport, 2, &mut scratch)
6192 .expect("exhausted packet should time out");
6193 assert_eq!(timeout.retried, 0);
6194 assert_eq!(timeout.timed_out, 1);
6195 assert_eq!(sender.in_flight_len(), 0);
6196 assert_eq!(sender.in_flight_for(target), 0);
6197 assert!(!sender.in_flight_by_target.contains_key(&target));
6198 }
6199
6200 #[test]
6201 fn reliable_station_receiver_bounds_duplicate_history() {
6202 let station_one = StationId::new(1);
6203 let station_two = StationId::new(2);
6204 let mut transport = InMemoryStationTransport::default();
6205 transport.register_station(station_one);
6206 transport.register_station(station_two);
6207 let config = ReliableStationConfig {
6208 max_in_flight_per_target: 8,
6209 retry_after_ticks: 2,
6210 max_attempts: 4,
6211 max_payload_bytes: DEFAULT_RELIABLE_STATION_MAX_PAYLOAD_BYTES,
6212 max_delivered_history: 1,
6213 };
6214 let mut endpoint = ReliableStationEndpoint::new(config);
6215
6216 let packet = |sequence: u64, payload: &[u8]| {
6217 let mut bytes = Vec::new();
6218 ReliableStationFrame::Data {
6219 sequence,
6220 payload: payload.to_vec(),
6221 }
6222 .encode(&mut bytes)
6223 .expect("data frame should encode");
6224 StationInboundPacket {
6225 source_station: station_one,
6226 target_station: station_two,
6227 bytes,
6228 }
6229 };
6230
6231 assert!(
6232 endpoint
6233 .handle_inbound(&mut transport, packet(1, b"first"))
6234 .expect("first data packet should handle")
6235 .is_some()
6236 );
6237 assert_eq!(
6238 endpoint
6239 .handle_inbound(&mut transport, packet(1, b"first-duplicate"))
6240 .expect("duplicate data packet should handle"),
6241 None
6242 );
6243 assert!(
6244 endpoint
6245 .handle_inbound(&mut transport, packet(2, b"second"))
6246 .expect("second data packet should handle")
6247 .is_some()
6248 );
6249 assert!(
6250 endpoint
6251 .handle_inbound(&mut transport, packet(1, b"first-after-eviction"))
6252 .expect("evicted data packet should handle")
6253 .is_some()
6254 );
6255 assert_eq!(endpoint.receiver.stats().data_delivered, 3);
6256 assert_eq!(endpoint.receiver.stats().duplicates_suppressed, 1);
6257 }
6258
6259 #[test]
6260 fn reliable_station_sender_enforces_payload_and_window_limits() {
6261 let station_one = StationId::new(1);
6262 let station_two = StationId::new(2);
6263 let mut transport = InMemoryStationTransport::default();
6264 transport.register_station(station_one);
6265 transport.register_station(station_two);
6266 let config = ReliableStationConfig {
6267 max_in_flight_per_target: 1,
6268 retry_after_ticks: 2,
6269 max_attempts: 4,
6270 max_payload_bytes: 4,
6271 max_delivered_history: DEFAULT_RELIABLE_STATION_DELIVERED_HISTORY,
6272 };
6273 let mut endpoint = ReliableStationEndpoint::new(config);
6274
6275 let too_large = endpoint
6276 .send(
6277 &mut transport,
6278 StationOutboundPacket {
6279 source_station: station_one,
6280 target_station: station_two,
6281 bytes: vec![0; 5],
6282 },
6283 0,
6284 )
6285 .expect_err("payload should exceed configured budget");
6286 match too_large {
6287 ReliableStationError::PayloadTooLarge { budget, actual } => {
6288 assert_eq!(budget, 4);
6289 assert_eq!(actual, 5);
6290 }
6291 other => panic!("unexpected error: {other}"),
6292 }
6293
6294 endpoint
6295 .send(
6296 &mut transport,
6297 StationOutboundPacket {
6298 source_station: station_one,
6299 target_station: station_two,
6300 bytes: vec![0; 4],
6301 },
6302 0,
6303 )
6304 .expect("first packet should fit");
6305 let full = endpoint
6306 .send(
6307 &mut transport,
6308 StationOutboundPacket {
6309 source_station: station_one,
6310 target_station: station_two,
6311 bytes: vec![1; 4],
6312 },
6313 0,
6314 )
6315 .expect_err("in-flight window should be full");
6316 match full {
6317 ReliableStationError::WindowFull {
6318 target_station,
6319 capacity,
6320 } => {
6321 assert_eq!(target_station, station_two);
6322 assert_eq!(capacity, 1);
6323 }
6324 other => panic!("unexpected error: {other}"),
6325 }
6326 }
6327
6328 #[test]
6329 fn reliable_station_window_counts_track_targets_and_saturated_sequence_replacement() {
6330 let source = StationId::new(1);
6331 let first_target = StationId::new(2);
6332 let second_target = StationId::new(3);
6333 let mut transport = InMemoryStationTransport::default();
6334 transport.register_station(first_target);
6335 transport.register_station(second_target);
6336 let mut sender = ReliableStationSender::new(ReliableStationConfig {
6337 max_in_flight_per_target: 3,
6338 retry_after_ticks: 2,
6339 max_attempts: 4,
6340 max_payload_bytes: 16,
6341 max_delivered_history: 0,
6342 });
6343 let mut send = |sender: &mut ReliableStationSender, target_station| {
6344 sender
6345 .send(
6346 &mut transport,
6347 StationOutboundPacket {
6348 source_station: source,
6349 target_station,
6350 bytes: b"count".to_vec(),
6351 },
6352 0,
6353 )
6354 .expect("bounded packet should send")
6355 };
6356
6357 assert_eq!(send(&mut sender, first_target), 1);
6358 assert_eq!(send(&mut sender, first_target), 2);
6359 assert_eq!(send(&mut sender, second_target), 1);
6360 assert_eq!(sender.in_flight_for(first_target), 2);
6361 assert_eq!(sender.in_flight_for(second_target), 1);
6362 assert!(sender.acknowledge(first_target, 1));
6363 assert_eq!(sender.in_flight_for(first_target), 1);
6364
6365 sender.next_sequence.insert(first_target, u64::MAX);
6366 assert_eq!(send(&mut sender, first_target), u64::MAX);
6367 assert_eq!(sender.in_flight_for(first_target), 2);
6368 assert_eq!(send(&mut sender, first_target), u64::MAX);
6369 assert_eq!(sender.in_flight_for(first_target), 2);
6370
6371 assert!(sender.acknowledge(first_target, u64::MAX));
6372 assert!(sender.acknowledge(first_target, 2));
6373 assert_eq!(sender.in_flight_for(first_target), 0);
6374 assert!(!sender.in_flight_by_target.contains_key(&first_target));
6375 assert_eq!(sender.in_flight_for(second_target), 1);
6376 }
6377
6378 #[test]
6379 fn udp_transport_sends_and_receives_registered_client() {
6380 let client_id = ClientId::new(7);
6381 let server_id = ClientId::new(0);
6382 let mut server = UdpTransport::bind("127.0.0.1:0").expect("server should bind");
6383 let mut client = UdpTransport::bind("127.0.0.1:0").expect("client should bind");
6384 let server_addr = server.local_addr().expect("server addr should exist");
6385 let client_addr = client.local_addr().expect("client addr should exist");
6386
6387 server.register_client(client_id, client_addr);
6388 client.register_client(server_id, server_addr);
6389
6390 client
6391 .send(OutboundPacket {
6392 client_id: server_id,
6393 bytes: b"command".to_vec(),
6394 })
6395 .expect("client should send");
6396 let first = recv_ref_with_retry(&mut server);
6397 assert_eq!(first.0, Some(client_id));
6398 assert_eq!(first.1, client_addr);
6399 assert_eq!((first.2, first.3, first.4), (7, b'c', b'd'));
6400
6401 client
6402 .send(OutboundPacket {
6403 client_id: server_id,
6404 bytes: b"next".to_vec(),
6405 })
6406 .expect("client should send another packet");
6407 let second = recv_ref_with_retry(&mut server);
6408 assert_eq!((second.2, second.3, second.4), (4, b'n', b't'));
6409 assert_eq!(second.5, first.5);
6410
6411 server
6412 .send(OutboundPacket {
6413 client_id,
6414 bytes: b"replication".to_vec(),
6415 })
6416 .expect("server should send");
6417 let inbound = recv_with_retry(&mut client);
6418 assert_eq!(inbound.client_id, Some(server_id));
6419 assert_eq!(inbound.remote_addr, server_addr);
6420 assert_eq!(inbound.bytes, b"replication");
6421 }
6422
6423 #[test]
6424 fn udp_transport_rejects_unknown_client() {
6425 let mut transport = UdpTransport::bind("127.0.0.1:0").expect("transport should bind");
6426 let error = transport
6427 .send(OutboundPacket {
6428 client_id: ClientId::new(99),
6429 bytes: Vec::new(),
6430 })
6431 .expect_err("unknown client should fail");
6432
6433 match error {
6434 UdpTransportError::UnknownClient(client_id) => {
6435 assert_eq!(client_id, ClientId::new(99));
6436 }
6437 UdpTransportError::Io(error) => panic!("unexpected io error: {error}"),
6438 }
6439 }
6440
6441 #[test]
6442 fn udp_station_transport_sends_and_receives_registered_stations() {
6443 let station_one = StationId::new(1);
6444 let station_two = StationId::new(2);
6445 let mut first =
6446 UdpStationTransport::bind(station_one, "127.0.0.1:0").expect("first should bind");
6447 let mut second =
6448 UdpStationTransport::bind(station_two, "127.0.0.1:0").expect("second should bind");
6449 let first_addr = first.local_addr().expect("first addr should exist");
6450 let second_addr = second.local_addr().expect("second addr should exist");
6451
6452 first.register_station(station_two, second_addr);
6453 second.register_station(station_one, first_addr);
6454
6455 first
6456 .send_station(StationOutboundPacket {
6457 source_station: station_one,
6458 target_station: station_two,
6459 bytes: b"handoff-prepare".to_vec(),
6460 })
6461 .expect("first station should send");
6462 let prepare = recv_station_ref_with_retry(&mut second, station_two);
6463 assert_eq!(prepare.0, station_one);
6464 assert_eq!(prepare.1, station_two);
6465 assert_eq!((prepare.2, prepare.3, prepare.4), (15, b'h', b'e'));
6466
6467 first
6468 .send_station(StationOutboundPacket {
6469 source_station: station_one,
6470 target_station: station_two,
6471 bytes: b"next".to_vec(),
6472 })
6473 .expect("first station should send another packet");
6474 let next = recv_station_ref_with_retry(&mut second, station_two);
6475 assert_eq!((next.2, next.3, next.4), (4, b'n', b't'));
6476 assert_eq!(next.5, prepare.5);
6477
6478 second
6479 .send_station(StationOutboundPacket {
6480 source_station: station_two,
6481 target_station: station_one,
6482 bytes: b"handoff-commit".to_vec(),
6483 })
6484 .expect("second station should send");
6485 let inbound = recv_station_with_retry(&mut first, station_one);
6486 assert_eq!(inbound.source_station, station_two);
6487 assert_eq!(inbound.target_station, station_one);
6488 assert_eq!(inbound.bytes, b"handoff-commit");
6489 assert_eq!(first.stats().packets_sent, 2);
6490 assert_eq!(first.stats().packets_received, 1);
6491 assert_eq!(second.stats().packets_sent, 1);
6492 assert_eq!(second.stats().packets_received, 2);
6493 }
6494
6495 #[test]
6496 fn udp_station_transport_rejects_invalid_station_endpoints() {
6497 let local = StationId::new(1);
6498 let mut transport =
6499 UdpStationTransport::bind(local, "127.0.0.1:0").expect("transport should bind");
6500
6501 let source_mismatch = transport
6502 .send_station(StationOutboundPacket {
6503 source_station: StationId::new(9),
6504 target_station: StationId::new(2),
6505 bytes: Vec::new(),
6506 })
6507 .expect_err("source should match local station");
6508 match source_mismatch {
6509 UdpStationTransportError::LocalStationMismatch {
6510 local_station,
6511 packet_source,
6512 } => {
6513 assert_eq!(local_station, local);
6514 assert_eq!(packet_source, StationId::new(9));
6515 }
6516 other => panic!("unexpected error: {other}"),
6517 }
6518
6519 let unknown = transport
6520 .send_station(StationOutboundPacket {
6521 source_station: local,
6522 target_station: StationId::new(2),
6523 bytes: Vec::new(),
6524 })
6525 .expect_err("target station should be registered");
6526 match unknown {
6527 UdpStationTransportError::UnknownStation(station_id) => {
6528 assert_eq!(station_id, StationId::new(2));
6529 }
6530 other => panic!("unexpected error: {other}"),
6531 }
6532
6533 let target_mismatch = transport
6534 .try_recv_station(StationId::new(99))
6535 .expect_err("receive target should match local station");
6536 match target_mismatch {
6537 UdpStationTransportError::TargetStationMismatch {
6538 local_station,
6539 requested_target,
6540 } => {
6541 assert_eq!(local_station, local);
6542 assert_eq!(requested_target, StationId::new(99));
6543 }
6544 other => panic!("unexpected error: {other}"),
6545 }
6546 }
6547
6548 #[test]
6549 fn reliable_ack_churn_compacts_client_and_station_deadlines() {
6550 let peer = ClientId::new(2);
6551 let mut client_transport = FakeTransport::default();
6552 let mut client = ReliableClientSender::default();
6553 for tick in 0..100 {
6554 let sequence = client
6555 .send(
6556 &mut client_transport,
6557 OutboundPacket {
6558 client_id: peer,
6559 bytes: vec![1],
6560 },
6561 tick,
6562 )
6563 .expect("send succeeds");
6564 assert!(client.acknowledge(peer, sequence));
6565 assert!(client.retry_deadlines.len() <= RETRY_DEADLINE_COMPACTION_MULTIPLE);
6566 }
6567
6568 let source = StationId::new(1);
6569 let target = StationId::new(2);
6570 let mut station_transport = InMemoryStationTransport::default();
6571 station_transport.register_station(target);
6572 let mut station = ReliableStationSender::default();
6573 for tick in 0..100 {
6574 let sequence = station
6575 .send(
6576 &mut station_transport,
6577 StationOutboundPacket {
6578 source_station: source,
6579 target_station: target,
6580 bytes: vec![1],
6581 },
6582 tick,
6583 )
6584 .expect("send succeeds");
6585 assert!(station.acknowledge(target, sequence));
6586 assert!(station.retry_deadlines.len() <= RETRY_DEADLINE_COMPACTION_MULTIPLE);
6587 }
6588 }
6589
6590 #[test]
6591 fn stale_in_memory_endpoint_does_not_revive_after_reregistration() {
6592 let hub = InMemoryTransportHub::default();
6593 let id = ClientId::new(1);
6594 let peer_id = ClientId::new(2);
6595 let mut stale = hub.endpoint(id, memory_addr(24001)).expect("register old");
6596 let mut peer = hub
6597 .endpoint(peer_id, memory_addr(24002))
6598 .expect("register peer");
6599 let stale_clone = stale.clone();
6600 hub.unregister_client(id).expect("unregister old");
6601 let mut current = hub.endpoint(id, memory_addr(24003)).expect("register new");
6602
6603 assert!(
6604 matches!(stale.local_addr(), Err(InMemoryTransportError::MissingLocal(found)) if found == id)
6605 );
6606 assert!(
6607 matches!(stale.send(OutboundPacket { client_id: peer_id, bytes: vec![1] }), Err(InMemoryTransportError::MissingLocal(found)) if found == id)
6608 );
6609 assert!(
6610 matches!(stale.try_recv(), Err(InMemoryTransportError::MissingLocal(found)) if found == id)
6611 );
6612 assert!(
6613 matches!(stale_clone.local_addr(), Err(InMemoryTransportError::MissingLocal(found)) if found == id)
6614 );
6615 peer.send(OutboundPacket {
6616 client_id: id,
6617 bytes: vec![7],
6618 })
6619 .expect("new endpoint receives");
6620 assert_eq!(
6621 current.local_addr().expect("current valid"),
6622 Some(memory_addr(24003))
6623 );
6624 assert_eq!(
6625 current
6626 .try_recv()
6627 .expect("receive current")
6628 .expect("packet")
6629 .bytes,
6630 vec![7]
6631 );
6632 }
6633}