use crate::error::Result;
use slinger::{Request, Response};
use std::sync::Arc;
#[async_trait::async_trait]
pub trait RequestInterceptor: Send + Sync {
async fn intercept_request(&self, request: Request) -> Result<Option<Request>>;
}
#[async_trait::async_trait]
pub trait ResponseInterceptor: Send + Sync {
async fn intercept_response(&self, response: Response) -> Result<Option<Response>>;
}
pub struct InterceptorHandler {
request_interceptors: Vec<Arc<dyn RequestInterceptor>>,
response_interceptors: Vec<Arc<dyn ResponseInterceptor>>,
}
impl InterceptorHandler {
pub fn new() -> Self {
Self {
request_interceptors: Vec::new(),
response_interceptors: Vec::new(),
}
}
pub fn add_request_interceptor(&mut self, interceptor: Arc<dyn RequestInterceptor>) {
self.request_interceptors.push(interceptor);
}
pub fn add_response_interceptor(&mut self, interceptor: Arc<dyn ResponseInterceptor>) {
self.response_interceptors.push(interceptor);
}
pub async fn process_request(&self, mut request: Request) -> Result<Option<Request>> {
for interceptor in &self.request_interceptors {
match interceptor.intercept_request(request).await? {
Some(modified) => request = modified,
None => return Ok(None), }
}
Ok(Some(request))
}
pub async fn process_response(&self, mut response: Response) -> Result<Option<Response>> {
for interceptor in &self.response_interceptors {
match interceptor.intercept_response(response).await? {
Some(modified) => response = modified,
None => return Ok(None), }
}
Ok(Some(response))
}
}
impl Default for InterceptorHandler {
fn default() -> Self {
Self::new()
}
}
pub struct Interceptor;
impl Interceptor {
pub fn logging() -> LoggingInterceptor {
LoggingInterceptor
}
}
pub struct LoggingInterceptor;
#[async_trait::async_trait]
impl RequestInterceptor for LoggingInterceptor {
async fn intercept_request(&self, request: Request) -> Result<Option<Request>> {
tracing::info!("[MITM] Request: {} {}", request.method(), request.uri());
for (name, value) in request.headers() {
tracing::info!(" {}: {:?}", name, value);
}
Ok(Some(request))
}
}
#[async_trait::async_trait]
impl ResponseInterceptor for LoggingInterceptor {
async fn intercept_response(&self, response: Response) -> Result<Option<Response>> {
tracing::info!("[MITM] Response: {}", response.status_code());
for (name, value) in response.headers() {
tracing::info!(" {}: {:?}", name, value);
}
Ok(Some(response))
}
}