use crate::{
types::*,
error::Result,
circuit_breaker::CircuitBreaker,
transport::{Transport, TransportMetrics},
};
use async_trait::async_trait;
use std::{
net::SocketAddr,
sync::Arc,
time::{Duration, Instant},
collections::HashMap,
};
use tokio::{
net::UdpSocket as TokioUdpSocket,
sync::{RwLock, mpsc},
time::timeout,
};
use tracing::{info, debug, warn, error};
pub struct UdpTransport {
local_addr: SocketAddr,
socket: Arc<TokioUdpSocket>,
circuit_breaker: Arc<CircuitBreaker>,
metrics: Arc<RwLock<TransportMetrics>>,
known_peers: Arc<RwLock<HashMap<String, SocketAddr>>>,
#[allow(dead_code)]
message_sender: Option<mpsc::UnboundedSender<(SocketAddr, SecureMessage)>>,
}
impl UdpTransport {
pub async fn new(local_addr: SocketAddr) -> Result<Self> {
let socket = TokioUdpSocket::bind(local_addr).await?;
let actual_addr = socket.local_addr()?;
info!("UDP Transport bound to {}", actual_addr);
Ok(Self {
local_addr: actual_addr,
socket: Arc::new(socket),
circuit_breaker: Arc::new(CircuitBreaker::new(
crate::circuit_breaker::CircuitBreakerConfig {
failure_threshold: 3,
minimum_requests: 5,
failure_window: Duration::from_secs(60),
recovery_timeout: Duration::from_secs(30),
half_open_max_calls: 2,
success_threshold: 0.5,
}
)),
metrics: Arc::new(RwLock::new(TransportMetrics::default())),
known_peers: Arc::new(RwLock::new(HashMap::new())),
message_sender: None,
})
}
pub async fn register_peer(&self, peer_id: &str, addr: SocketAddr) {
let mut peers = self.known_peers.write().await;
peers.insert(peer_id.to_string(), addr);
info!("Registered UDP peer {} at {}", peer_id, addr);
}
pub async fn start_server(&self, mut message_handler: mpsc::UnboundedReceiver<(String, SecureMessage)>) -> Result<()> {
let socket = self.socket.clone();
let metrics = self.metrics.clone();
let socket_clone = socket.clone();
tokio::spawn(async move {
let mut buffer = [0u8; 65536];
loop {
match socket_clone.recv_from(&mut buffer).await {
Ok((size, peer_addr)) => {
debug!("Received UDP packet from {} ({} bytes)", peer_addr, size);
{
let mut _m = metrics.write().await;
}
match bincode::decode_from_slice::<SecureMessage, _>(&buffer[..size], bincode::config::standard()) {
Ok(_message) => {
debug!("Successfully parsed UDP message from {}", peer_addr);
}
Err(e) => {
warn!("Failed to deserialize UDP message from {}: {}", peer_addr, e);
}
}
}
Err(e) => {
error!("UDP receive error: {}", e);
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
}
});
tokio::spawn(async move {
while let Some((target, _message)) = message_handler.recv().await {
debug!("Would send UDP message to {}", target);
}
});
info!("UDP server started on {}", self.local_addr);
Ok(())
}
pub async fn discover_local_peers(&self) -> Result<Vec<SocketAddr>> {
let broadcast_addr: SocketAddr = "255.255.255.255:8765".parse()?;
let discovery_message = b"SYNAPSE_DISCOVERY";
self.socket.send_to(discovery_message, broadcast_addr).await?;
let mut discovered_peers = Vec::new();
let mut buffer = [0u8; 1024];
match timeout(Duration::from_secs(1), self.socket.recv_from(&mut buffer)).await {
Ok(Ok((_, peer_addr))) => {
if peer_addr != self.local_addr {
discovered_peers.push(peer_addr);
info!("Discovered UDP peer at {}", peer_addr);
}
}
_ => {
debug!("No UDP discovery responses received");
}
}
Ok(discovered_peers)
}
}
#[async_trait]
impl Transport for UdpTransport {
async fn send_message(&self, target: &str, message: &SecureMessage) -> Result<String> {
if !self.circuit_breaker.can_proceed().await {
return Err("UDP circuit breaker is open".into());
}
let start_time = Instant::now();
let target_addr = {
let peers = self.known_peers.read().await;
peers.get(target).copied()
};
let addr = match target_addr {
Some(addr) => addr,
None => {
match target.parse::<SocketAddr>() {
Ok(addr) => addr,
Err(_) => {
self.circuit_breaker.record_outcome(crate::circuit_breaker::RequestOutcome::Failure("Unknown UDP target".to_string())).await;
return Err(format!("Unknown UDP target: {}", target).into());
}
}
}
};
let serialized = bincode::encode_to_vec(message, bincode::config::standard())?;
match self.socket.send_to(&serialized, addr).await {
Ok(bytes_sent) => {
let duration = start_time.elapsed();
self.circuit_breaker.record_outcome(crate::circuit_breaker::RequestOutcome::Success).await;
{
let mut _metrics = self.metrics.write().await;
}
debug!("Sent UDP message to {} ({} bytes, {:?})", addr, bytes_sent, duration);
Ok(message.message_id.0.to_string()) }
Err(e) => {
self.circuit_breaker.record_outcome(crate::circuit_breaker::RequestOutcome::Failure(e.to_string())).await;
error!("Failed to send UDP message to {}: {}", addr, e);
Err(e.into())
}
}
}
async fn receive_messages(&self) -> Result<Vec<SecureMessage>> {
Ok(vec![])
}
async fn test_connectivity(&self, target: &str) -> Result<TransportMetrics> {
let start_time = Instant::now();
let addr: SocketAddr = match target.parse() {
Ok(addr) => addr,
Err(_) => {
let peers = self.known_peers.read().await;
*peers.get(target).ok_or_else(|| {
crate::error::SynapseError::TransportError(format!("Unknown UDP target: {}", target))
})?
}
};
let ping_data = b"PING";
let success = self.socket.send_to(&ping_data[..], addr).await.is_ok();
let latency = start_time.elapsed();
Ok(crate::transport::TransportMetrics {
latency,
throughput_bps: if success { 1000000 } else { 0 }, packet_loss: if success { 0.0 } else { 1.0 },
jitter_ms: 5, reliability_score: if success { 0.8 } else { 0.0 }, last_updated: Instant::now(),
})
}
async fn can_reach(&self, target: &str) -> bool {
if target.parse::<SocketAddr>().is_ok() {
return true;
}
let peers = self.known_peers.read().await;
peers.contains_key(target)
}
fn get_capabilities(&self) -> Vec<String> {
vec![
"low_latency".to_string(),
"connectionless".to_string(),
"broadcast".to_string(),
"real_time".to_string(),
"local_network".to_string(),
]
}
fn estimated_latency(&self) -> Duration {
Duration::from_millis(5) }
fn reliability_score(&self) -> f32 {
0.8 }
}
#[derive(Debug, Clone)]
pub struct UdpConfig {
pub bind_address: SocketAddr,
pub discovery_enabled: bool,
pub max_packet_size: usize,
pub timeout: Duration,
}
impl Default for UdpConfig {
fn default() -> Self {
Self {
bind_address: "0.0.0.0:8765".parse().unwrap(),
discovery_enabled: true,
max_packet_size: 65536,
timeout: Duration::from_secs(5),
}
}
}