cloudpub_common/
routing.rs1use std::collections::HashMap;
2use std::net::SocketAddr;
3use std::time::{Duration, Instant};
4use tracing::{debug, info};
5
6#[derive(Debug, Clone)]
7pub struct UdpSession<T> {
8 pub external_addr: SocketAddr,
9 pub local_data: T,
10 pub last_activity: Instant,
11}
12
13pub struct UdpRoutingTable<T> {
14 external_to_session: HashMap<SocketAddr, UdpSession<T>>,
16 local_to_external: HashMap<SocketAddr, SocketAddr>,
18 session_timeout: Duration,
20}
21
22impl<T: Clone> UdpRoutingTable<T> {
23 pub fn new(session_timeout_secs: u64) -> Self {
24 Self {
25 external_to_session: HashMap::new(),
26 local_to_external: HashMap::new(),
27 session_timeout: Duration::from_secs(session_timeout_secs),
28 }
29 }
30
31 pub fn register_session(&mut self, external_addr: SocketAddr, local_data: T) {
32 let session = UdpSession {
33 external_addr,
34 local_data,
35 last_activity: Instant::now(),
36 };
37
38 self.external_to_session.insert(external_addr, session);
39 debug!("Registered UDP session for {}", external_addr);
40 }
41
42 pub fn register_client_session(
43 &mut self,
44 external_addr: SocketAddr,
45 local_addr: SocketAddr,
46 local_data: T,
47 ) {
48 let session = UdpSession {
49 external_addr,
50 local_data,
51 last_activity: Instant::now(),
52 };
53
54 self.external_to_session.insert(external_addr, session);
55 self.local_to_external.insert(local_addr, external_addr);
56
57 debug!(
58 "Registered UDP client session: {} <-> {}",
59 external_addr, local_addr
60 );
61 }
62
63 pub fn update_activity(&mut self, addr: &SocketAddr, is_external: bool) {
64 if is_external {
65 if let Some(session) = self.external_to_session.get_mut(addr) {
66 session.last_activity = Instant::now();
67 }
68 } else {
69 if let Some(external_addr) = self.local_to_external.get(addr) {
71 if let Some(session) = self.external_to_session.get_mut(external_addr) {
72 session.last_activity = Instant::now();
73 }
74 }
75 }
76 }
77
78 pub fn get_session(&self, external_addr: &SocketAddr) -> Option<&UdpSession<T>> {
79 self.external_to_session.get(external_addr)
80 }
81
82 pub fn get_local_data(&self, external_addr: &SocketAddr) -> Option<&T> {
83 self.external_to_session
84 .get(external_addr)
85 .map(|s| &s.local_data)
86 }
87
88 pub fn get_external_addr(&self, local_addr: &SocketAddr) -> Option<SocketAddr> {
89 self.local_to_external.get(local_addr).copied()
90 }
91
92 pub fn cleanup_expired_sessions(&mut self) -> usize {
93 let now = Instant::now();
94
95 let mut expired_external: Vec<SocketAddr> = Vec::new();
96 let mut expired_local: Vec<SocketAddr> = Vec::new();
97
98 for (external_addr, session) in &self.external_to_session {
99 if now.duration_since(session.last_activity) > self.session_timeout {
100 expired_external.push(*external_addr);
101 for (local_addr, ext_addr) in &self.local_to_external {
103 if *ext_addr == *external_addr {
104 expired_local.push(*local_addr);
105 }
106 }
107 }
108 }
109
110 for addr in &expired_external {
111 self.external_to_session.remove(addr);
112 debug!("Cleaned up expired UDP session for external {}", addr);
113 }
114
115 for addr in &expired_local {
116 self.local_to_external.remove(addr);
117 }
118
119 let cleaned = expired_external.len();
120 if cleaned > 0 {
121 info!("Cleaned up {} expired UDP sessions", cleaned);
122 }
123 cleaned
124 }
125
126 pub fn session_count(&self) -> usize {
127 self.external_to_session.len()
128 }
129}