use std::net::SocketAddr;
use std::sync::{Arc, OnceLock};
use std::time::Duration;
use dashmap::DashMap;
use futures::SinkExt;
use futures::stream::StreamExt;
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::Mutex;
use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::task::{AbortHandle, JoinHandle};
use tokio_util::codec::{FramedRead, FramedWrite};
use tracing::{debug, error, warn};
use crate::core::message_codec::MAX_FRAME_SIZE;
use crate::{Error, Message, MessageCodec, Peer, PeerInfo, RateLimiter, Result};
const WRITE_CHANNEL_CAPACITY: usize = 1024;
const RECV_CHANNEL_CAPACITY: usize = 1024;
const SHUTDOWN_DRAIN_GRACE_MS: u64 = 500;
struct ConnectionTask {
supervisor: JoinHandle<()>,
read_abort: AbortHandle,
write_abort: AbortHandle,
}
pub struct Tcp {
local_addr: OnceLock<SocketAddr>,
peers: Arc<DashMap<SocketAddr, Peer>>,
connections: Arc<DashMap<SocketAddr, ConnectionTask>>,
message_rx: Arc<Mutex<Receiver<(SocketAddr, Message)>>>,
message_tx: Sender<(SocketAddr, Message)>,
rate_limiter: Option<Arc<Mutex<RateLimiter>>>,
max_message_size: usize,
max_peers: usize,
accept_handle: Mutex<Option<JoinHandle<()>>>,
}
impl Tcp {
pub fn new() -> Self {
Self::with_max_message_size(MAX_FRAME_SIZE) }
pub fn with_max_message_size(max_message_size: usize) -> Self {
let (message_tx, message_rx) = mpsc::channel(RECV_CHANNEL_CAPACITY);
Self {
local_addr: OnceLock::new(),
peers: Arc::new(DashMap::new()),
connections: Arc::new(DashMap::new()),
message_rx: Arc::new(Mutex::new(message_rx)),
message_tx,
rate_limiter: None,
max_message_size,
max_peers: usize::MAX,
accept_handle: Mutex::new(None),
}
}
pub fn set_rate_limit(mut self, capacity: u32, refill_rate: u32) -> Result<Self> {
self.rate_limiter = Some(Arc::new(Mutex::new(RateLimiter::try_with_params(
capacity,
refill_rate,
)?)));
Ok(self)
}
pub fn set_max_peers(mut self, max_peers: usize) -> Self {
self.max_peers = max_peers;
self
}
pub async fn listen(&self, addr: SocketAddr) -> Result<()> {
let listener = TcpListener::bind(addr)
.await
.map_err(|e| Error::Connection { addr, source: e })?;
let local_addr = listener.local_addr().map_err(Error::Io)?;
let _ = self.local_addr.set(local_addr);
debug!("TCP transport listening on {local_addr}");
let peers = Arc::clone(&self.peers);
let connections = Arc::clone(&self.connections);
let message_tx = self.message_tx.clone();
let rate_limiter = self.rate_limiter.clone();
let max_message_size = self.max_message_size;
let max_peers = self.max_peers;
let handle = tokio::spawn(async move {
loop {
match listener.accept().await {
Ok((stream, peer_addr)) => {
if peers.len() >= max_peers {
debug!("At max_peers ({max_peers}), refusing inbound from {peer_addr}");
continue;
}
debug!("Accepted connection from {peer_addr}");
Self::handle_connection(
stream,
peer_addr,
Arc::clone(&peers),
Arc::clone(&connections),
message_tx.clone(),
rate_limiter.clone(),
max_message_size,
);
}
Err(e) => {
error!("Failed to accept connection: {e}");
}
}
}
});
*self.accept_handle.lock().await = Some(handle);
Ok(())
}
pub async fn connect(&self, addr: SocketAddr) -> Result<()> {
if self.local_addr() == Some(addr) {
return Err(Error::network(format!(
"refusing self-connection to {addr}"
)));
}
if self.peers.contains_key(&addr) {
debug!("Already connected to {addr}");
return Ok(());
}
if self.peers.len() >= self.max_peers {
return Err(Error::network(format!(
"at max_peers ({}), refusing connection to {addr}",
self.max_peers
)));
}
let stream = TcpStream::connect(addr)
.await
.map_err(|e| Error::Connection { addr, source: e })?;
debug!("TCP connection established to {addr}");
Self::handle_connection(
stream,
addr,
Arc::clone(&self.peers),
Arc::clone(&self.connections),
self.message_tx.clone(),
self.rate_limiter.clone(),
self.max_message_size,
);
Ok(())
}
pub async fn send(&self, peer: SocketAddr, message: Message) -> Result<()> {
if let Some(mut conn) = self.peers.get_mut(&peer) {
conn.send(message)
} else {
Err(Error::PeerNotFound(peer))
}
}
pub async fn recv(&self) -> Result<(SocketAddr, Message)> {
self.message_rx
.lock()
.await
.recv()
.await
.ok_or(Error::Channel("Channel recv error".to_string()))
}
pub fn local_addr(&self) -> Option<SocketAddr> {
self.local_addr.get().copied()
}
pub fn max_message_size(&self) -> usize {
self.max_message_size
}
pub fn peers(&self) -> Vec<SocketAddr> {
self.peers.iter().map(|entry| *entry.key()).collect()
}
pub fn peer_infos(&self) -> Vec<(SocketAddr, PeerInfo)> {
self.peers
.iter()
.map(|entry| (*entry.key(), entry.value().info.clone()))
.collect()
}
pub fn mark_stale(&self, addr: SocketAddr) {
if let Some(mut peer) = self.peers.get_mut(&addr) {
peer.info.mark_stale();
}
}
pub fn disconnect(&self, addr: SocketAddr) -> bool {
let removed = self.peers.remove(&addr).is_some();
if let Some((_, conn)) = self.connections.remove(&addr) {
conn.read_abort.abort();
conn.write_abort.abort();
}
removed
}
pub async fn shutdown(&self) {
if let Some(handle) = self.accept_handle.lock().await.take() {
handle.abort();
let _ = handle.await;
}
for entry in self.connections.iter() {
entry.value().read_abort.abort();
}
self.peers.clear();
let drained: Vec<ConnectionTask> = {
let addrs: Vec<SocketAddr> = self.connections.iter().map(|e| *e.key()).collect();
addrs
.into_iter()
.filter_map(|addr| self.connections.remove(&addr).map(|(_, conn)| conn))
.collect()
};
let (supervisors, write_aborts): (Vec<_>, Vec<_>) = drained
.into_iter()
.map(|conn| (conn.supervisor, conn.write_abort))
.unzip();
let reap = futures::future::join_all(supervisors);
if tokio::time::timeout(Duration::from_millis(SHUTDOWN_DRAIN_GRACE_MS), reap)
.await
.is_err()
{
for abort in &write_aborts {
abort.abort();
}
}
}
fn handle_connection(
stream: TcpStream,
peer_addr: SocketAddr,
peers: Arc<DashMap<SocketAddr, Peer>>,
connections: Arc<DashMap<SocketAddr, ConnectionTask>>,
message_tx: Sender<(SocketAddr, Message)>,
rate_limiter: Option<Arc<Mutex<RateLimiter>>>,
max_message_size: usize,
) {
let (reader, writer) = stream.into_split();
let (tx, mut rx) = mpsc::channel::<Message>(WRITE_CHANNEL_CAPACITY);
peers.insert(peer_addr, Peer::new(peer_addr, tx));
let codec = MessageCodec::with_max_frame_size(max_message_size);
let write_task = {
let mut sink = FramedWrite::new(writer, codec.clone());
tokio::spawn(async move {
while let Some(message) = rx.recv().await {
if let Err(e) = sink.send(message).await {
debug!("Failed to send to {peer_addr}: {e}");
break;
}
}
let _ = sink.flush().await;
})
};
let read_task = {
let read_peers = Arc::clone(&peers);
let mut stream = FramedRead::new(reader, codec);
tokio::spawn(async move {
while let Some(result) = stream.next().await {
match result {
Ok(message) => {
if let Some(mut peer) = read_peers.get_mut(&peer_addr) {
peer.info.increment_received();
}
if let Some(ref limiter) = rate_limiter
&& !limiter.lock().await.allow_request(peer_addr)
{
warn!("Rate limit exceeded for peer {peer_addr}, dropping message");
continue;
}
if message_tx.send((peer_addr, message)).await.is_err() {
warn!("Inbound channel closed");
break;
}
}
Err(e) => {
debug!("Connection from {peer_addr} closed: {e}");
break;
}
}
}
})
};
let read_abort = read_task.abort_handle();
let write_abort = write_task.abort_handle();
let supervisor = {
let peers = Arc::clone(&peers);
let connections = Arc::clone(&connections);
tokio::spawn(async move {
let _ = read_task.await;
peers.remove(&peer_addr);
let _ = write_task.await;
connections.remove(&peer_addr);
debug!("Connection closed: {peer_addr}");
})
};
connections.insert(
peer_addr,
ConnectionTask {
supervisor,
read_abort,
write_abort,
},
);
}
}
impl Default for Tcp {
fn default() -> Self {
Self::new()
}
}