use std::{borrow::Cow, sync::Arc, time::Instant};
use async_trait::async_trait;
use praxis_core::{health::HealthRegistry, kv::KvStoreRegistry};
use crate::{actions::FilterAction, filter::FilterError};
#[async_trait]
pub trait TcpFilter: Send + Sync {
fn name(&self) -> &'static str;
async fn on_connect(&self, ctx: &mut TcpFilterContext<'_>) -> Result<FilterAction, FilterError> {
let _ = ctx;
Ok(FilterAction::Continue)
}
async fn on_disconnect(&self, ctx: &mut TcpFilterContext<'_>) -> Result<(), FilterError> {
let _ = ctx;
Ok(())
}
}
pub struct TcpFilterContext<'a> {
pub remote_addr: &'a str,
pub local_addr: &'a str,
pub sni: Option<&'a str>,
pub upstream_addr: Option<Cow<'a, str>>,
pub cluster: Option<Arc<str>>,
pub health_registry: Option<&'a HealthRegistry>,
pub kv_stores: Option<&'a KvStoreRegistry>,
pub connect_time: Instant,
pub bytes_in: u64,
pub bytes_out: u64,
}
#[cfg(test)]
#[expect(clippy::allow_attributes, reason = "blanket test suppressions")]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::panic,
reason = "tests"
)]
mod tests {
use super::*;
#[tokio::test]
async fn default_on_connect_returns_continue() {
let filter = NoopTcpFilter;
let mut ctx = TcpFilterContext {
remote_addr: "127.0.0.1:12345",
local_addr: "0.0.0.0:5432",
sni: None,
upstream_addr: Some(Cow::Borrowed("10.0.0.1:5432")),
cluster: None,
health_registry: None,
kv_stores: None,
connect_time: Instant::now(),
bytes_in: 0,
bytes_out: 0,
};
let action = filter.on_connect(&mut ctx).await.unwrap();
assert!(matches!(action, FilterAction::Continue));
}
#[tokio::test]
async fn default_on_disconnect_succeeds() {
let filter = NoopTcpFilter;
let mut ctx = TcpFilterContext {
remote_addr: "127.0.0.1:12345",
local_addr: "0.0.0.0:5432",
sni: None,
upstream_addr: Some(Cow::Borrowed("10.0.0.1:5432")),
cluster: None,
health_registry: None,
kv_stores: None,
connect_time: Instant::now(),
bytes_in: 0,
bytes_out: 0,
};
filter.on_disconnect(&mut ctx).await.unwrap();
}
struct NoopTcpFilter;
#[async_trait]
impl TcpFilter for NoopTcpFilter {
fn name(&self) -> &'static str {
"noop_tcp"
}
}
}