use super::Transport;
use super::capture::CaptureFile;
use super::capture::CapturedPacket;
use crate::error::Error;
use bytes::Bytes;
use futures_util::lock::Mutex;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
#[derive(Clone, Default)]
pub struct Recorder {
packets: Arc<Mutex<Vec<CapturedPacket>>>,
}
impl Recorder {
pub async fn flush(&self) -> CaptureFile {
let packets = std::mem::take(&mut *self.packets.lock().await);
CaptureFile {
description: String::new(),
packets,
}
}
}
pub struct RecordingTransport<T: Transport> {
inner: T,
recorder: Recorder,
seq: AtomicU32,
}
impl<T: Transport> RecordingTransport<T> {
pub fn new(inner: T) -> Self {
Self::with_recorder(inner, Recorder::default())
}
pub fn with_recorder(inner: T, recorder: Recorder) -> Self {
Self {
inner,
recorder,
seq: AtomicU32::new(0),
}
}
pub fn recorder(&self) -> Recorder {
self.recorder.clone()
}
}
impl<T: Transport> Transport for RecordingTransport<T> {
fn send(
&self,
payload: &[u8],
) -> Pin<Box<dyn std::future::Future<Output = Result<(), Error>> + Send + '_>> {
let payload = payload.to_vec();
Box::pin(async move { self.inner.send(&payload).await })
}
fn recv(&self) -> Pin<Box<dyn std::future::Future<Output = Result<Bytes, Error>> + Send + '_>> {
Box::pin(async move {
let data = self.inner.recv().await?;
let seq = self.seq.fetch_add(1, Ordering::Relaxed);
let packet = CapturedPacket::new(seq, &data);
self.recorder.packets.lock().await.push(packet);
Ok(data)
})
}
}
#[cfg(test)]
mod tests {
use super::super::replay::ReplayTransport;
use super::*;
struct FakeInner {
to_recv: Mutex<std::collections::VecDeque<Vec<u8>>>,
sent: Mutex<Vec<Vec<u8>>>,
}
impl Transport for FakeInner {
fn send(
&self,
payload: &[u8],
) -> Pin<Box<dyn std::future::Future<Output = Result<(), Error>> + Send + '_>> {
let payload = payload.to_vec();
Box::pin(async move {
self.sent.lock().await.push(payload);
Ok(())
})
}
fn recv(
&self,
) -> Pin<Box<dyn std::future::Future<Output = Result<Bytes, Error>> + Send + '_>> {
Box::pin(async move {
let next = self.to_recv.lock().await.pop_front();
match next {
Some(p) => Ok(Bytes::from(p)),
None => Err(crate::error::ConnectionError::Disconnected.into()),
}
})
}
}
#[tokio::test]
async fn forwards_payload_and_records_only_recv() {
let inner = FakeInner {
to_recv: Mutex::new(vec![vec![1, 2, 3], vec![4, 5]].into()),
sent: Mutex::new(Vec::new()),
};
let rt = RecordingTransport::new(inner);
let recorder = rt.recorder();
rt.send(b"hello").await.unwrap();
let a = rt.recv().await.unwrap();
let b = rt.recv().await.unwrap();
assert_eq!(&a[..], &[1, 2, 3]);
assert_eq!(&b[..], &[4, 5]);
assert_eq!(rt.inner.sent.lock().await.as_slice(), &[b"hello".to_vec()]);
let capture = recorder.flush().await;
assert_eq!(capture.packets.len(), 2);
assert_eq!(capture.packets[0].decode_payload().unwrap(), vec![1, 2, 3]);
assert_eq!(capture.packets[1].decode_payload().unwrap(), vec![4, 5]);
let replay = ReplayTransport::from_capture(capture);
assert_eq!(&replay.recv().await.unwrap()[..], &[1, 2, 3]);
assert_eq!(&replay.recv().await.unwrap()[..], &[4, 5]);
}
}