use async_trait::async_trait;
use log::trace;
use pingora_core::{Error, ErrorType};
use pingora_proxy::Session;
use serde::de::DeserializeOwned;
use std::fmt::Debug;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
pub use module_utils_macros::{merge_conf, merge_opt, RequestFilter};
#[derive(Debug, Copy, Clone, PartialEq, Default)]
pub enum RequestFilterResult {
ResponseSent,
Handled,
#[default]
Unhandled,
}
#[async_trait]
pub trait RequestFilter {
type Conf;
fn new(conf: Self::Conf) -> Result<Self, Box<Error>>
where
Self: Sized,
Self::Conf: TryInto<Self, Error = Box<Error>>,
{
conf.try_into()
}
async fn handle(&self, session: &mut Session, ctx: &mut Self::CTX) -> Result<bool, Box<Error>>
where
Self::CTX: Send,
{
let result = self.request_filter(session, ctx).await?;
Ok(result == RequestFilterResult::ResponseSent)
}
type CTX;
fn new_ctx() -> Self::CTX;
async fn request_filter(
&self,
session: &mut Session,
ctx: &mut Self::CTX,
) -> Result<RequestFilterResult, Box<Error>>;
}
pub trait FromYaml {
fn load_from_yaml<P>(path: P) -> Result<Self, Box<Error>>
where
P: AsRef<Path>,
Self: Sized;
}
impl<D> FromYaml for D
where
D: DeserializeOwned + Debug + ?Sized,
{
fn load_from_yaml<P: AsRef<Path>>(path: P) -> Result<Self, Box<Error>> {
let file = File::open(path.as_ref()).map_err(|err| {
Error::because(
ErrorType::FileOpenError,
"failed opening configuration file",
err,
)
})?;
let reader = BufReader::new(file);
let conf = serde_yaml::from_reader(reader).map_err(|err| {
Error::because(
ErrorType::FileReadError,
"failed reading configuration file",
err,
)
})?;
trace!("Loaded configuration file: {conf:#?}");
Ok(conf)
}
}