use std::collections::HashMap;
use std::io;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex, PoisonError};
use tokio::io::DuplexStream;
use tokio::sync::mpsc;
use crate::transport::Transport;
const QUEUE: usize = 65_536;
const STREAM_BUFFER: usize = 64 * 1024;
#[derive(Debug, Clone, Default)]
pub struct MemNetwork {
inner: Arc<Mutex<Hosts>>,
}
#[derive(Debug, Default)]
struct Hosts {
by_addr: HashMap<SocketAddr, Host>,
next_port: u16,
}
#[derive(Debug)]
struct Host {
datagrams: mpsc::Sender<(Vec<u8>, SocketAddr)>,
streams: mpsc::UnboundedSender<(DuplexStream, SocketAddr)>,
}
impl MemNetwork {
pub fn new() -> Self {
Self::default()
}
pub fn bind(&self, addr: SocketAddr) -> io::Result<MemTransport> {
let mut hosts = self.hosts();
let mut addr = addr;
if addr.port() == 0 {
loop {
hosts.next_port = hosts.next_port.wrapping_sub(1);
if hosts.next_port == 0 {
return Err(io::ErrorKind::AddrInUse.into());
}
addr.set_port(hosts.next_port);
if !hosts.by_addr.contains_key(&addr) {
break;
}
}
} else if hosts.by_addr.contains_key(&addr) {
return Err(io::ErrorKind::AddrInUse.into());
}
let (dtx, drx) = mpsc::channel(QUEUE);
let (stx, srx) = mpsc::unbounded_channel();
hosts.by_addr.insert(
addr,
Host {
datagrams: dtx,
streams: stx,
},
);
Ok(MemTransport {
net: self.clone(),
addr,
datagrams: tokio::sync::Mutex::new(drx),
streams: tokio::sync::Mutex::new(srx),
})
}
fn hosts(&self) -> std::sync::MutexGuard<'_, Hosts> {
self.inner.lock().unwrap_or_else(PoisonError::into_inner)
}
}
#[derive(Debug)]
pub struct MemTransport {
net: MemNetwork,
addr: SocketAddr,
datagrams: tokio::sync::Mutex<mpsc::Receiver<(Vec<u8>, SocketAddr)>>,
streams: tokio::sync::Mutex<mpsc::UnboundedReceiver<(DuplexStream, SocketAddr)>>,
}
impl Drop for MemTransport {
fn drop(&mut self) {
self.net.hosts().by_addr.remove(&self.addr);
}
}
fn copy_out(buf: &mut [u8], (data, from): (Vec<u8>, SocketAddr)) -> (usize, SocketAddr) {
let n = data.len().min(buf.len());
buf[..n].copy_from_slice(&data[..n]);
(n, from)
}
impl Transport for MemTransport {
type Stream = DuplexStream;
fn local_addr(&self) -> SocketAddr {
self.addr
}
async fn recv_datagram(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
let mut rx = self.datagrams.lock().await;
match rx.recv().await {
Some(d) => Ok(copy_out(buf, d)),
None => Err(io::ErrorKind::NotConnected.into()),
}
}
fn try_recv_datagram(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
let mut rx = self
.datagrams
.try_lock()
.map_err(|_| io::Error::from(io::ErrorKind::WouldBlock))?;
match rx.try_recv() {
Ok(d) => Ok(copy_out(buf, d)),
Err(_) => Err(io::ErrorKind::WouldBlock.into()),
}
}
fn send_datagram(&self, to: SocketAddr, payload: &[u8]) -> io::Result<()> {
let tx = self
.net
.hosts()
.by_addr
.get(&to)
.map(|h| h.datagrams.clone());
if let Some(tx) = tx {
let _ = tx.try_send((payload.to_vec(), self.addr));
}
Ok(())
}
async fn connect(&self, to: SocketAddr) -> io::Result<DuplexStream> {
let tx = self.net.hosts().by_addr.get(&to).map(|h| h.streams.clone());
let tx = tx.ok_or(io::ErrorKind::ConnectionRefused)?;
let (ours, theirs) = tokio::io::duplex(STREAM_BUFFER);
tx.send((theirs, self.addr))
.map_err(|_| io::Error::from(io::ErrorKind::ConnectionRefused))?;
Ok(ours)
}
async fn accept(&self) -> io::Result<(DuplexStream, SocketAddr)> {
let mut rx = self.streams.lock().await;
rx.recv()
.await
.ok_or_else(|| io::ErrorKind::NotConnected.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
fn addr(port: u16) -> SocketAddr {
SocketAddr::from(([10, 0, 0, 1], port))
}
#[tokio::test]
async fn datagrams_and_streams_reach_the_bound_address() {
let net = MemNetwork::new();
let a = net.bind(addr(1)).unwrap();
let b = net.bind(addr(0)).unwrap();
assert_ne!(b.local_addr().port(), 0);
assert!(net.bind(addr(1)).is_err());
b.send_datagram(addr(1), b"hi").unwrap();
let mut buf = [0; 8];
assert_eq!(a.try_recv_datagram(&mut buf).unwrap(), (2, b.local_addr()));
assert_eq!(
a.try_recv_datagram(&mut buf).unwrap_err().kind(),
io::ErrorKind::WouldBlock
);
let mut client = b.connect(addr(1)).await.unwrap();
let (mut server, from) = a.accept().await.unwrap();
assert_eq!(from, b.local_addr());
client.write_all(b"abc").await.unwrap();
let mut got = [0; 3];
server.read_exact(&mut got).await.unwrap();
assert_eq!(&got, b"abc");
drop(a);
let err = b.connect(addr(1)).await.unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::ConnectionRefused);
b.send_datagram(addr(1), b"lost").unwrap();
}
}