use std::collections::VecDeque;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use flowscope::tracker::FlowEvents;
use flowscope::{FlowEvent, FlowExtractor, FlowTracker, FlowTrackerConfig, PacketView, Timestamp};
use futures_core::Stream;
use crate::error::Error;
use crate::pcap_source::AsyncPcapSource;
pub struct PcapFlowStream<E>
where
E: FlowExtractor,
{
source: AsyncPcapSource,
tracker: FlowTracker<E, ()>,
pending: VecDeque<FlowEvent<E::Key>>,
eof: bool,
}
impl<E> PcapFlowStream<E>
where
E: FlowExtractor,
E::Key: Clone + Send + 'static,
{
pub(crate) fn new(source: AsyncPcapSource, extractor: E) -> Self {
Self {
source,
tracker: FlowTracker::new(extractor),
pending: VecDeque::new(),
eof: false,
}
}
pub fn with_config(mut self, config: FlowTrackerConfig) -> Self {
self.tracker.set_config(config);
self
}
pub fn with_idle_timeout_fn<G>(mut self, f: G) -> Self
where
G: Fn(&E::Key, Option<flowscope::L4Proto>) -> Option<Duration> + Send + 'static,
{
self.tracker.set_idle_timeout_fn(f);
self
}
pub fn tracker(&self) -> &FlowTracker<E, ()> {
&self.tracker
}
pub fn packets_read(&self) -> u64 {
self.source.packets_yielded()
}
}
impl<E> Stream for PcapFlowStream<E>
where
E: FlowExtractor + Unpin,
E::Key: Clone + Unpin,
{
type Item = Result<FlowEvent<E::Key>, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(evt) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(evt)));
}
if this.eof {
let now = current_timestamp();
for ev in this.tracker.sweep(now) {
this.pending.push_back(ev);
}
if let Some(evt) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(evt)));
}
return Poll::Ready(None);
}
match Pin::new(&mut this.source).poll_next(cx) {
Poll::Ready(Some(Ok(owned))) => {
let view = PacketView::new(&owned.data, owned.timestamp);
let evts: FlowEvents<E::Key> = this.tracker.track(view);
for ev in evts {
this.pending.push_back(ev);
}
}
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
Poll::Ready(None) => {
this.eof = true;
}
Poll::Pending => return Poll::Pending,
}
}
}
}
impl AsyncPcapSource {
pub fn flow_events<E>(self, extractor: E) -> PcapFlowStream<E>
where
E: FlowExtractor,
E::Key: Clone + Send + 'static,
{
PcapFlowStream::new(self, extractor)
}
}
fn current_timestamp() -> Timestamp {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(Duration::ZERO);
Timestamp::new(now.as_secs() as u32, now.subsec_nanos())
}