use std::sync::Arc;
use crate::network::NetworkProvider;
use super::{DatagramSocket, DatagramTransport, TransportError};
#[derive(Debug, Clone)]
pub struct UdpConfig {
pub port: u16,
}
impl Default for UdpConfig {
fn default() -> Self {
Self { port: 0 }
}
}
pub struct UdpTransport<N: NetworkProvider> {
network: Arc<N>,
#[allow(dead_code)]
config: UdpConfig,
}
impl<N: NetworkProvider + 'static> UdpTransport<N> {
pub fn new(network: Arc<N>, config: UdpConfig) -> Self {
Self { network, config }
}
}
impl<N: NetworkProvider + 'static> DatagramTransport for UdpTransport<N> {
async fn bind(&self, port: u16) -> Result<DatagramSocket, TransportError> {
match self.network.bind_udp(port).await {
Ok(network_socket) => {
tracing::debug!(
port = port,
tsnet_port = network_socket.tsnet_port(),
"udp: bound via network provider"
);
return Ok(DatagramSocket::network(network_socket));
}
Err(crate::network::NetworkError::Internal(msg))
if msg.contains("not yet implemented") || msg.contains("not supported") =>
{
tracing::debug!(
port = port,
"udp: network provider does not support UDP, falling back to direct bind"
);
}
Err(crate::network::NetworkError::NotRunning) => {
tracing::debug!(
port = port,
"udp: network provider not running, falling back to direct bind"
);
}
Err(e) => {
return Err(TransportError::ListenFailed(format!(
"udp bind via network provider: {e}"
)));
}
}
let bind_addr = format!("0.0.0.0:{port}");
tracing::debug!(addr = %bind_addr, "udp: binding socket directly");
let socket = tokio::net::UdpSocket::bind(&bind_addr)
.await
.map_err(|e| TransportError::ListenFailed(format!("udp bind {bind_addr}: {e}")))?;
let local_addr = socket.local_addr().map_err(TransportError::Io)?;
tracing::debug!(local_addr = %local_addr, "udp: socket bound directly");
Ok(DatagramSocket::direct(socket))
}
}