use std::collections::HashMap;
use std::net::SocketAddr;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionState {
Connecting,
Active,
Degraded,
Timeout,
Closed,
}
#[derive(Debug, Clone)]
pub struct ConnectionInfo {
pub addr: SocketAddr,
pub state: ConnectionState,
pub established_at: Instant,
pub last_received: Instant,
pub last_sent: Instant,
pub packets_received: u64,
pub packets_sent: u64,
pub bytes_received: u64,
pub bytes_sent: u64,
pub loss_rate: f64,
pub rtt_us: u64,
}
impl ConnectionInfo {
#[must_use]
pub fn new(addr: SocketAddr) -> Self {
let now = Instant::now();
Self {
addr,
state: ConnectionState::Connecting,
established_at: now,
last_received: now,
last_sent: now,
packets_received: 0,
packets_sent: 0,
bytes_received: 0,
bytes_sent: 0,
loss_rate: 0.0,
rtt_us: 0,
}
}
pub fn update_state(&mut self, timeout: Duration) {
let now = Instant::now();
let idle_time = now.duration_since(self.last_received);
self.state = if idle_time > timeout {
ConnectionState::Timeout
} else if self.loss_rate > 0.1 {
ConnectionState::Degraded
} else if self.packets_received > 0 {
ConnectionState::Active
} else {
ConnectionState::Connecting
};
}
pub fn record_received(&mut self, bytes: usize) {
self.last_received = Instant::now();
self.packets_received += 1;
self.bytes_received += bytes as u64;
}
pub fn record_sent(&mut self, bytes: usize) {
self.last_sent = Instant::now();
self.packets_sent += 1;
self.bytes_sent += bytes as u64;
}
#[must_use]
pub fn uptime(&self) -> Duration {
Instant::now().duration_since(self.established_at)
}
#[must_use]
pub fn is_healthy(&self) -> bool {
self.state == ConnectionState::Active && self.loss_rate < 0.05
}
}
pub struct ConnectionManager {
connections: HashMap<SocketAddr, ConnectionInfo>,
timeout: Duration,
max_connections: usize,
}
impl ConnectionManager {
#[must_use]
pub fn new(timeout: Duration, max_connections: usize) -> Self {
Self {
connections: HashMap::new(),
timeout,
max_connections,
}
}
pub fn add_connection(&mut self, addr: SocketAddr) -> &mut ConnectionInfo {
if !self.connections.contains_key(&addr) && self.connections.len() >= self.max_connections {
if let Some((oldest_addr, _)) = self
.connections
.iter()
.min_by_key(|(_, info)| info.last_received)
{
let oldest_addr = *oldest_addr;
self.connections.remove(&oldest_addr);
}
}
self.connections
.entry(addr)
.or_insert_with(|| ConnectionInfo::new(addr))
}
#[must_use]
pub fn get_connection(&self, addr: &SocketAddr) -> Option<&ConnectionInfo> {
self.connections.get(addr)
}
pub fn get_connection_mut(&mut self, addr: &SocketAddr) -> Option<&mut ConnectionInfo> {
self.connections.get_mut(addr)
}
pub fn remove_connection(&mut self, addr: &SocketAddr) {
self.connections.remove(addr);
}
pub fn update_states(&mut self) {
for conn in self.connections.values_mut() {
conn.update_state(self.timeout);
}
}
pub fn cleanup_timeouts(&mut self) {
self.connections
.retain(|_, conn| conn.state != ConnectionState::Timeout);
}
#[must_use]
pub fn active_count(&self) -> usize {
self.connections
.values()
.filter(|conn| conn.state == ConnectionState::Active)
.count()
}
#[must_use]
pub fn connection_addrs(&self) -> Vec<SocketAddr> {
self.connections.keys().copied().collect()
}
#[must_use]
pub fn connections_by_state(&self, state: ConnectionState) -> Vec<&ConnectionInfo> {
self.connections
.values()
.filter(|conn| conn.state == state)
.collect()
}
#[must_use]
pub fn total_stats(&self) -> ConnectionStats {
let mut stats = ConnectionStats::default();
for conn in self.connections.values() {
stats.total_packets_received += conn.packets_received;
stats.total_packets_sent += conn.packets_sent;
stats.total_bytes_received += conn.bytes_received;
stats.total_bytes_sent += conn.bytes_sent;
}
stats.active_connections = self.active_count();
stats.total_connections = self.connections.len();
stats
}
}
#[derive(Debug, Clone, Default)]
pub struct ConnectionStats {
pub active_connections: usize,
pub total_connections: usize,
pub total_packets_received: u64,
pub total_packets_sent: u64,
pub total_bytes_received: u64,
pub total_bytes_sent: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_connection_info_creation() {
let addr = "127.0.0.1:5000".parse().expect("should succeed in test");
let info = ConnectionInfo::new(addr);
assert_eq!(info.addr, addr);
assert_eq!(info.state, ConnectionState::Connecting);
assert_eq!(info.packets_received, 0);
}
#[test]
fn test_connection_record() {
let addr = "127.0.0.1:5000".parse().expect("should succeed in test");
let mut info = ConnectionInfo::new(addr);
info.record_received(100);
info.record_sent(200);
assert_eq!(info.packets_received, 1);
assert_eq!(info.packets_sent, 1);
assert_eq!(info.bytes_received, 100);
assert_eq!(info.bytes_sent, 200);
}
#[test]
fn test_connection_state_update() {
let addr = "127.0.0.1:5000".parse().expect("should succeed in test");
let mut info = ConnectionInfo::new(addr);
info.record_received(100);
info.update_state(Duration::from_secs(1));
assert_eq!(info.state, ConnectionState::Active);
info.loss_rate = 0.2;
info.update_state(Duration::from_secs(1));
assert_eq!(info.state, ConnectionState::Degraded);
}
#[test]
fn test_connection_manager() {
let mut manager = ConnectionManager::new(Duration::from_secs(5), 10);
let addr1: SocketAddr = "127.0.0.1:5000".parse().expect("should succeed in test");
let addr2: SocketAddr = "127.0.0.1:5001".parse().expect("should succeed in test");
manager.add_connection(addr1);
manager.add_connection(addr2);
assert_eq!(manager.connections.len(), 2);
assert!(manager.get_connection(&addr1).is_some());
assert!(manager.get_connection(&addr2).is_some());
}
#[test]
fn test_connection_manager_max_connections() {
let mut manager = ConnectionManager::new(Duration::from_secs(5), 2);
let addr1: SocketAddr = "127.0.0.1:5000".parse().expect("should succeed in test");
let addr2: SocketAddr = "127.0.0.1:5001".parse().expect("should succeed in test");
let addr3: SocketAddr = "127.0.0.1:5002".parse().expect("should succeed in test");
manager.add_connection(addr1);
manager.add_connection(addr2);
manager.add_connection(addr3);
assert_eq!(manager.connections.len(), 2);
}
#[test]
fn test_connection_cleanup() {
let mut manager = ConnectionManager::new(Duration::from_millis(100), 10);
let addr: SocketAddr = "127.0.0.1:5000".parse().expect("should succeed in test");
manager.add_connection(addr);
std::thread::sleep(Duration::from_millis(200));
manager.update_states();
manager.cleanup_timeouts();
assert_eq!(manager.connections.len(), 0);
}
#[test]
fn test_connection_stats() {
let mut manager = ConnectionManager::new(Duration::from_secs(5), 10);
let addr1: SocketAddr = "127.0.0.1:5000".parse().expect("should succeed in test");
let addr2: SocketAddr = "127.0.0.1:5001".parse().expect("should succeed in test");
let conn1 = manager.add_connection(addr1);
conn1.record_received(100);
conn1.record_sent(200);
let conn2 = manager.add_connection(addr2);
conn2.record_received(150);
conn2.record_sent(250);
let stats = manager.total_stats();
assert_eq!(stats.total_connections, 2);
assert_eq!(stats.total_packets_received, 2);
assert_eq!(stats.total_packets_sent, 2);
assert_eq!(stats.total_bytes_received, 250);
assert_eq!(stats.total_bytes_sent, 450);
}
}