slinger_mitm/
interceptor.rs

1//! Traffic interception and modification interfaces
2
3use crate::error::Result;
4use slinger::{Request, Response};
5use std::sync::Arc;
6
7/// Trait for intercepting and modifying HTTP requests
8#[async_trait::async_trait]
9pub trait RequestInterceptor: Send + Sync {
10  /// Intercept and optionally modify an HTTP request
11  ///
12  /// Return `None` to block the request, or return a modified request
13  async fn intercept_request(&self, request: Request) -> Result<Option<Request>>;
14}
15
16/// Trait for intercepting and modifying HTTP responses
17#[async_trait::async_trait]
18pub trait ResponseInterceptor: Send + Sync {
19  /// Intercept and optionally modify an HTTP response
20  ///
21  /// Return `None` to block the response, or return a modified response
22  async fn intercept_response(&self, response: Response) -> Result<Option<Response>>;
23}
24
25/// Combined interceptor handler
26pub struct InterceptorHandler {
27  request_interceptors: Vec<Arc<dyn RequestInterceptor>>,
28  response_interceptors: Vec<Arc<dyn ResponseInterceptor>>,
29}
30
31impl InterceptorHandler {
32  /// Create a new interceptor handler
33  pub fn new() -> Self {
34    Self {
35      request_interceptors: Vec::new(),
36      response_interceptors: Vec::new(),
37    }
38  }
39
40  /// Add a request interceptor
41  pub fn add_request_interceptor(&mut self, interceptor: Arc<dyn RequestInterceptor>) {
42    self.request_interceptors.push(interceptor);
43  }
44
45  /// Add a response interceptor
46  pub fn add_response_interceptor(&mut self, interceptor: Arc<dyn ResponseInterceptor>) {
47    self.response_interceptors.push(interceptor);
48  }
49
50  /// Process a request through all interceptors
51  pub async fn process_request(&self, mut request: Request) -> Result<Option<Request>> {
52    for interceptor in &self.request_interceptors {
53      match interceptor.intercept_request(request).await? {
54        Some(modified) => request = modified,
55        None => return Ok(None), // Request blocked
56      }
57    }
58    Ok(Some(request))
59  }
60
61  /// Process a response through all interceptors
62  pub async fn process_response(&self, mut response: Response) -> Result<Option<Response>> {
63    for interceptor in &self.response_interceptors {
64      match interceptor.intercept_response(response).await? {
65        Some(modified) => response = modified,
66        None => return Ok(None), // Response blocked
67      }
68    }
69    Ok(Some(response))
70  }
71}
72
73impl Default for InterceptorHandler {
74  fn default() -> Self {
75    Self::new()
76  }
77}
78
79/// Default pass-through interceptor
80pub struct Interceptor;
81
82impl Interceptor {
83  /// Create a logging interceptor that prints requests/responses
84  pub fn logging() -> LoggingInterceptor {
85    LoggingInterceptor
86  }
87}
88
89/// Logging interceptor implementation
90pub struct LoggingInterceptor;
91
92#[async_trait::async_trait]
93impl RequestInterceptor for LoggingInterceptor {
94  async fn intercept_request(&self, request: Request) -> Result<Option<Request>> {
95    tracing::info!("[MITM] Request: {} {}", request.method(), request.uri());
96    for (name, value) in request.headers() {
97      tracing::info!("  {}: {:?}", name, value);
98    }
99    Ok(Some(request))
100  }
101}
102
103#[async_trait::async_trait]
104impl ResponseInterceptor for LoggingInterceptor {
105  async fn intercept_response(&self, response: Response) -> Result<Option<Response>> {
106    tracing::info!("[MITM] Response: {}", response.status_code());
107    for (name, value) in response.headers() {
108      tracing::info!("  {}: {:?}", name, value);
109    }
110    // tracing::debug!("[MITM] Response Body: {:?}", response.body());
111    Ok(Some(response))
112  }
113}