use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use tokio::sync::mpsc;
use crate::intercept::{PacketData, PacketRecord};
use crate::wire_tap::WireTap;
pub trait RecordSink: Send + 'static {
fn on_packet(&mut self, record: PacketRecord);
}
pub struct ChannelRecorder {
tx: mpsc::UnboundedSender<PacketRecord>,
dropped: Arc<AtomicU64>,
}
impl ChannelRecorder {
pub fn spawn<S: RecordSink>(mut sink: S) -> (Self, RecorderHandle) {
let (tx, mut rx) = mpsc::unbounded_channel::<PacketRecord>();
let dropped = Arc::new(AtomicU64::new(0));
let handle = tokio::spawn(async move {
while let Some(record) = rx.recv().await {
sink.on_packet(record);
}
});
(Self { tx, dropped }, RecorderHandle { handle })
}
pub fn dropped(&self) -> u64 {
self.dropped.load(Ordering::Relaxed)
}
fn send(&self, timestamp_us: u64, data: PacketData) {
let record = PacketRecord { timestamp_us, data };
if self.tx.send(record).is_err() {
self.dropped.fetch_add(1, Ordering::Relaxed);
}
}
}
pub struct RecorderHandle {
handle: tokio::task::JoinHandle<()>,
}
impl Drop for RecorderHandle {
fn drop(&mut self) {
self.handle.abort();
}
}
impl WireTap for ChannelRecorder {
fn on_write(&self, bytes: &[u8], ts: u64) {
self.send(ts, PacketData::RawTx(bytes.to_vec()));
}
fn on_read(&self, bytes: &[u8], ts: u64) {
self.send(ts, PacketData::RawRx(bytes.to_vec()));
}
fn on_error(&self, bytes: &[u8], error: &str, ts: u64) {
self.send(ts, PacketData::RawError(bytes.to_vec(), error.to_string()));
}
}