use std::{
fmt,
net::SocketAddr,
sync::{Arc, Mutex},
};
use crate::{connection::PathStats, high_level::WeakConnectionHandle};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PathId(u64);
impl PathId {
pub const PRIMARY: Self = Self(0);
pub const fn get(self) -> u64 {
self.0
}
}
impl From<u64> for PathId {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<PathId> for u64 {
fn from(value: PathId) -> Self {
value.0
}
}
impl fmt::Display for PathId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::connection::PathStats;
#[test]
fn path_id_primary_is_zero() {
assert_eq!(PathId::PRIMARY.get(), 0);
}
#[test]
fn path_id_from_u64() {
let id: PathId = 42u64.into();
assert_eq!(id.get(), 42);
}
#[test]
fn path_id_into_u64() {
let id = PathId::PRIMARY;
let val: u64 = id.into();
assert_eq!(val, 0);
}
#[test]
fn path_id_default_is_zero() {
let id = PathId::default();
assert_eq!(id.get(), 0);
assert_eq!(id, PathId::PRIMARY);
}
#[test]
fn path_id_equality() {
assert_eq!(PathId::from(1u64), PathId::from(1u64));
assert_ne!(PathId::from(1u64), PathId::from(2u64));
}
#[test]
fn path_id_ordering() {
assert!(PathId::from(0u64) < PathId::from(1u64));
assert!(PathId::from(1u64) > PathId::from(0u64));
}
#[test]
fn path_id_display() {
assert_eq!(format!("{}", PathId::PRIMARY), "0");
assert_eq!(format!("{}", PathId::from(42u64)), "42");
}
#[test]
fn path_id_debug() {
let debug = format!("{:?}", PathId::from(42u64));
assert!(debug.contains("42"));
}
#[test]
fn path_id_clone() {
let a = PathId::from(5u64);
let b = a;
assert_eq!(a, b);
}
#[test]
fn path_id_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let a = PathId::from(10u64);
let b = PathId::from(10u64);
let mut ha = DefaultHasher::new();
let mut hb = DefaultHasher::new();
a.hash(&mut ha);
b.hash(&mut hb);
assert_eq!(ha.finish(), hb.finish());
}
#[test]
fn path_snapshot_default() {
let stats = PathStats::default();
let snapshot = PathSnapshot {
stats,
remote_address: "127.0.0.1:9000".parse().unwrap(),
observed_external_addr: None,
};
assert_eq!(snapshot.remote_address.port(), 9000);
assert!(snapshot.observed_external_addr.is_none());
}
#[test]
fn path_snapshot_with_external_addr() {
let stats = PathStats::default();
let snapshot = PathSnapshot {
stats,
remote_address: "192.168.1.1:9000".parse().unwrap(),
observed_external_addr: Some("10.0.0.1:9001".parse().unwrap()),
};
assert!(snapshot.observed_external_addr.is_some());
assert_eq!(
snapshot.observed_external_addr.unwrap().to_string(),
"10.0.0.1:9001"
);
}
#[test]
fn path_snapshot_clone_copy() {
let stats = PathStats::default();
let s1 = PathSnapshot {
stats,
remote_address: "127.0.0.1:80".parse().unwrap(),
observed_external_addr: None,
};
let s2 = s1;
assert_eq!(s1.remote_address, s2.remote_address);
}
#[test]
fn retained_path_snapshot_store_and_load() {
let stats = PathStats::default();
let snapshot = PathSnapshot {
stats,
remote_address: "127.0.0.1:9000".parse().unwrap(),
observed_external_addr: None,
};
let retained = RetainedPathSnapshot::new(snapshot);
let loaded = retained.load();
assert_eq!(loaded.remote_address.port(), 9000);
}
#[test]
fn retained_path_snapshot_overwrite() {
let stats = PathStats::default();
let snapshot1 = PathSnapshot {
stats,
remote_address: "127.0.0.1:1".parse().unwrap(),
observed_external_addr: None,
};
let retained = RetainedPathSnapshot::new(snapshot1);
let snapshot2 = PathSnapshot {
stats: PathStats::default(),
remote_address: "127.0.0.1:2".parse().unwrap(),
observed_external_addr: None,
};
retained.store(snapshot2);
let loaded = retained.load();
assert_eq!(loaded.remote_address.port(), 2);
}
#[test]
fn retained_path_snapshot_clone() {
let stats = PathStats::default();
let snapshot = PathSnapshot {
stats,
remote_address: "10.0.0.1:8000".parse().unwrap(),
observed_external_addr: None,
};
let retained = RetainedPathSnapshot::new(snapshot);
let cloned = retained.clone();
assert_eq!(retained.load().remote_address, cloned.load().remote_address);
}
#[test]
fn retained_path_snapshot_concurrent_independence() {
let stats = PathStats::default();
let snapshot = PathSnapshot {
stats,
remote_address: "127.0.0.1:9000".parse().unwrap(),
observed_external_addr: None,
};
let retained = RetainedPathSnapshot::new(snapshot);
let cloned = retained.clone();
let new_snapshot = PathSnapshot {
stats: PathStats::default(),
remote_address: "127.0.0.1:9999".parse().unwrap(),
observed_external_addr: None,
};
retained.store(new_snapshot);
assert_eq!(retained.load().remote_address.port(), 9999);
assert_eq!(cloned.load().remote_address.port(), 9999); }
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct PathSnapshot {
pub(crate) stats: PathStats,
pub(crate) remote_address: SocketAddr,
pub(crate) observed_external_addr: Option<SocketAddr>,
}
#[derive(Debug, Clone)]
struct RetainedPathSnapshot(Arc<Mutex<PathSnapshot>>);
impl RetainedPathSnapshot {
fn new(snapshot: PathSnapshot) -> Self {
Self(Arc::new(Mutex::new(snapshot)))
}
fn load(&self) -> PathSnapshot {
match self.0.lock() {
Ok(snapshot) => *snapshot,
Err(poisoned) => *poisoned.into_inner(),
}
}
fn store(&self, snapshot: PathSnapshot) {
match self.0.lock() {
Ok(mut retained) => *retained = snapshot,
Err(poisoned) => *poisoned.into_inner() = snapshot,
}
}
}
#[derive(Debug, Clone)]
pub struct Path {
conn_handle: WeakConnectionHandle,
id: PathId,
retained: RetainedPathSnapshot,
}
impl Path {
pub(crate) fn new(
conn_handle: WeakConnectionHandle,
id: PathId,
snapshot: PathSnapshot,
) -> Self {
Self {
conn_handle,
id,
retained: RetainedPathSnapshot::new(snapshot),
}
}
fn live_snapshot(&self) -> Option<PathSnapshot> {
self.conn_handle
.upgrade()
.and_then(|conn| conn.path_snapshot(self.id))
}
fn snapshot(&self) -> PathSnapshot {
if let Some(snapshot) = self.live_snapshot() {
self.retained.store(snapshot);
snapshot
} else {
self.retained.load()
}
}
pub fn id(&self) -> PathId {
self.id
}
pub fn stats(&self) -> PathStats {
self.snapshot().stats
}
pub fn remote_address(&self) -> SocketAddr {
self.snapshot().remote_address
}
pub fn observed_external_addr(&self) -> Option<SocketAddr> {
self.snapshot().observed_external_addr
}
pub fn weak_handle(&self) -> WeakPathHandle {
WeakPathHandle {
conn_handle: self.conn_handle.clone(),
id: self.id,
retained: self.retained.clone(),
}
}
}
impl Drop for Path {
fn drop(&mut self) {
if let Some(snapshot) = self.live_snapshot() {
self.retained.store(snapshot);
}
}
}
#[derive(Debug, Clone)]
pub struct WeakPathHandle {
conn_handle: WeakConnectionHandle,
id: PathId,
retained: RetainedPathSnapshot,
}
impl WeakPathHandle {
fn live_snapshot(&self) -> Option<PathSnapshot> {
self.conn_handle
.upgrade()
.and_then(|conn| conn.path_snapshot(self.id))
}
fn snapshot(&self) -> PathSnapshot {
if let Some(snapshot) = self.live_snapshot() {
self.retained.store(snapshot);
snapshot
} else {
self.retained.load()
}
}
pub fn id(&self) -> PathId {
self.id
}
pub fn upgrade(&self) -> Option<Path> {
if !self.conn_handle.is_alive() {
return None;
}
let snapshot = self.live_snapshot()?;
Some(Path {
conn_handle: self.conn_handle.clone(),
id: self.id,
retained: RetainedPathSnapshot::new(snapshot),
})
}
pub fn is_alive(&self) -> bool {
self.conn_handle.is_alive() && self.live_snapshot().is_some()
}
pub fn stats(&self) -> PathStats {
self.snapshot().stats
}
pub fn remote_address(&self) -> SocketAddr {
self.snapshot().remote_address
}
pub fn observed_external_addr(&self) -> Option<SocketAddr> {
self.snapshot().observed_external_addr
}
}