use std::{io, sync::Arc};
use sciparse::address::ip_socket_addr::ScionSocketIpAddr;
use tokio::sync::{Mutex, mpsc};
use tokio_util::sync::CancellationToken;
use crate::socket::{BoxedSocketError, GenericScionUdpSocket};
struct MockDatagram {
data: Vec<u8>,
src: ScionSocketIpAddr,
dst: ScionSocketIpAddr,
}
pub struct MockScionSocket {
recv_channel: Mutex<mpsc::Receiver<MockDatagram>>,
send_channel: mpsc::Sender<MockDatagram>,
local_addr: ScionSocketIpAddr,
}
impl MockScionSocket {
pub fn pair(
queue_size: usize,
sockaddr_a: ScionSocketIpAddr,
sockaddr_b: ScionSocketIpAddr,
) -> (MockScionSocket, MockScionSocket) {
let (a_to_b_tx, a_to_b_rx) = mpsc::channel(queue_size);
let (b_to_a_tx, b_to_a_rx) = mpsc::channel(queue_size);
let socket_a = MockScionSocket {
recv_channel: Mutex::new(a_to_b_rx),
send_channel: b_to_a_tx,
local_addr: sockaddr_a,
};
let socket_b = MockScionSocket {
recv_channel: Mutex::new(b_to_a_rx),
send_channel: a_to_b_tx,
local_addr: sockaddr_b,
};
(socket_a, socket_b)
}
}
#[async_trait::async_trait]
impl GenericScionUdpSocket for MockScionSocket {
async fn send_to(
&self,
payload: &[u8],
destination: ScionSocketIpAddr,
) -> Result<(), BoxedSocketError> {
let datagram = MockDatagram {
data: payload.to_vec(),
src: self.local_addr,
dst: destination,
};
self.send_channel
.send(datagram)
.await
.map_err(|e| Box::new(e) as BoxedSocketError)
}
async fn recv_from(
&self,
buf: &mut [u8],
) -> Result<(usize, ScionSocketIpAddr), BoxedSocketError> {
loop {
let datagram = self.recv_channel.lock().await.recv().await.ok_or_else(|| {
Box::new(io::Error::new(
io::ErrorKind::UnexpectedEof,
"Channel closed",
)) as BoxedSocketError
})?;
if datagram.dst.socket_addr() != self.local_addr.socket_addr() {
continue; }
let data = datagram.data;
let src = datagram.src;
if data.len() > buf.len() {
return Err(Box::new(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"receive buffer ({} bytes) too small for datagram ({} bytes)",
buf.len(),
data.len()
),
)) as BoxedSocketError);
}
buf[..data.len()].copy_from_slice(&data);
return Ok((data.len(), src));
}
}
fn local_addr(&self) -> ScionSocketIpAddr {
self.local_addr
}
}
pub struct BreakableScionSocket {
inner: Arc<dyn GenericScionUdpSocket>,
broken: CancellationToken,
}
impl BreakableScionSocket {
pub fn new(inner: Arc<dyn GenericScionUdpSocket>) -> Self {
Self {
inner,
broken: CancellationToken::new(),
}
}
pub fn break_socket(&self) {
self.broken.cancel();
}
fn failure() -> BoxedSocketError {
Box::new(io::Error::new(io::ErrorKind::BrokenPipe, "socket broken"))
}
}
#[async_trait::async_trait]
impl GenericScionUdpSocket for BreakableScionSocket {
async fn send_to(
&self,
payload: &[u8],
destination: ScionSocketIpAddr,
) -> Result<(), BoxedSocketError> {
if self.broken.is_cancelled() {
return Err(Self::failure());
}
self.inner.send_to(payload, destination).await
}
async fn recv_from(
&self,
buf: &mut [u8],
) -> Result<(usize, ScionSocketIpAddr), BoxedSocketError> {
tokio::select! {
biased;
() = self.broken.cancelled() => Err(Self::failure()),
res = self.inner.recv_from(buf) => res,
}
}
fn local_addr(&self) -> ScionSocketIpAddr {
self.inner.local_addr()
}
}