use crate::ctx::Ctx;
use crate::error::Result;
use crate::protocol::event_typed::Event;
pub trait Handler<E: Event, Marker>: Send + Sync + 'static {
fn call(&self, payload: &E::Payload, ctx: &mut Ctx<'_>) -> Result<()>;
}
pub struct PayloadOnly;
pub struct PayloadCtx;
pub struct CtxOnly;
impl<E, F> Handler<E, PayloadOnly> for F
where
E: Event,
F: Fn(&E::Payload) -> Result<()> + Send + Sync + 'static,
{
#[inline]
fn call(&self, p: &E::Payload, _ctx: &mut Ctx<'_>) -> Result<()> {
self(p)
}
}
impl<E, F> Handler<E, PayloadCtx> for F
where
E: Event,
F: for<'a> Fn(&'a E::Payload, &'a mut Ctx<'_>) -> Result<()> + Send + Sync + 'static,
{
#[inline]
fn call(&self, p: &E::Payload, ctx: &mut Ctx<'_>) -> Result<()> {
self(p, ctx)
}
}
impl<E, F> Handler<E, CtxOnly> for F
where
E: Event,
F: for<'a> Fn(&'a mut Ctx<'_>) -> Result<()> + Send + Sync + 'static,
{
#[inline]
fn call(&self, _p: &E::Payload, ctx: &mut Ctx<'_>) -> Result<()> {
self(ctx)
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use flowscope::Timestamp;
use super::*;
use crate::anomaly::sink::NoopSink;
use crate::correlate::TimeBucketedCounter;
use crate::ctx::{CounterRegistry, SourceIdx, StateMap};
use crate::protocol::builtin::Tcp;
use crate::protocol::event_typed::FlowStarted;
#[derive(Default)]
struct Counters {
flows: u64,
}
fn fresh_ctx<'a>(
state: &'a mut StateMap,
sink: &'a mut NoopSink,
counters: &'a mut CounterRegistry,
flow_states: &'a mut crate::ctx::FlowStateRegistry,
) -> Ctx<'a> {
Ctx {
flow: None,
ts: Timestamp::new(0, 0),
source: SourceIdx(0),
monitor_name: None,
state_map: state,
sink,
counters,
flow_states,
label_table: crate::ctx::default_label_table(),
tracker: None,
}
}
fn dummy_flow_started() -> FlowStarted<Tcp> {
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
let key = flowscope::extract::FiveTupleKey {
proto: flowscope::L4Proto::Tcp,
a: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 12345),
b: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 80),
};
FlowStarted::<Tcp>::new(key, Some(flowscope::L4Proto::Tcp), Timestamp::new(0, 0))
}
fn invoke<E, H, M>(h: H, p: &E::Payload, ctx: &mut Ctx<'_>) -> Result<()>
where
E: Event,
H: Handler<E, M>,
M: 'static,
{
h.call(p, ctx)
}
#[test]
fn payload_only_handler_runs() {
let mut s = StateMap::default();
let mut k = NoopSink;
let mut c = CounterRegistry::default();
let mut fs = crate::ctx::FlowStateRegistry::default();
let mut ctx = fresh_ctx(&mut s, &mut k, &mut c, &mut fs);
let evt = dummy_flow_started();
let h = |_p: &FlowStarted<Tcp>| Ok(());
invoke::<FlowStarted<Tcp>, _, PayloadOnly>(h, &evt, &mut ctx).unwrap();
}
#[test]
fn payload_ctx_handler_can_mutate_state() {
let mut s = StateMap::default();
let mut k = NoopSink;
let mut c = CounterRegistry::default();
let mut fs = crate::ctx::FlowStateRegistry::default();
let mut ctx = fresh_ctx(&mut s, &mut k, &mut c, &mut fs);
let evt = dummy_flow_started();
let h = |_p: &FlowStarted<Tcp>, ctx: &mut Ctx<'_>| {
ctx.state_mut::<Counters>().flows += 1;
Ok(())
};
invoke::<FlowStarted<Tcp>, _, PayloadCtx>(h, &evt, &mut ctx).unwrap();
invoke::<FlowStarted<Tcp>, _, PayloadCtx>(h, &evt, &mut ctx).unwrap();
assert_eq!(ctx.state_mut::<Counters>().flows, 2);
}
#[test]
fn payload_ctx_handler_multi_field_access() {
let mut s = StateMap::default();
let mut k = NoopSink;
let mut c = CounterRegistry::default();
c.register::<u16>(TimeBucketedCounter::<u16>::new_unbounded(
Duration::from_secs(60),
Duration::from_secs(1),
));
let mut fs = crate::ctx::FlowStateRegistry::default();
let mut ctx = fresh_ctx(&mut s, &mut k, &mut c, &mut fs);
ctx.ts = Timestamp::new(7, 0);
let evt = dummy_flow_started();
let h = |_p: &FlowStarted<Tcp>, ctx: &mut Ctx<'_>| {
ctx.state_mut::<Counters>().flows += 1;
let now = ctx.ts;
ctx.counter_mut::<u16>().bump(42u16, now);
Ok(())
};
invoke::<FlowStarted<Tcp>, _, PayloadCtx>(h, &evt, &mut ctx).unwrap();
assert_eq!(ctx.state_mut::<Counters>().flows, 1);
}
}