product-os-proxy 0.0.19

Product OS : Proxy builds on the work of hudsucker, taking it to the next level with a man-in-the-middle proxy server that can tunnel traffic through a VPN utilising Product OS : VPN.
//! Proxy middleware implementation.
//!
//! This module provides the middleware layer for the proxy, connecting
//! the Product OS middleware system with the MITM proxy handlers.

pub use product_os_request::ProductOSResponse;
pub use product_os_server::{
    BeforeResult, ProductOSMiddleware, ProductOSMiddlewareAsync, RequestData,
};
pub use product_os_server::{Request, Response};
use std::sync::Arc;

use async_trait::async_trait;
use hyper_tungstenite::tungstenite::Message;
use product_os_http_body::BodyBytes as Body;
use std::future::Future;

use crate::mitm;

/// Wrapper that adapts Product OS middleware to proxy HTTP handlers.
///
/// This struct bridges the Product OS middleware system with the proxy's
/// `HttpHandler` and `WebSocketHandler` traits, allowing you to use
/// standard Product OS middleware in the proxy.
///
/// # Examples
///
/// ```
/// use product_os_proxy::ProxyMiddleware;
/// use product_os_server::ProductOSMiddlewareAsync;
/// use std::sync::Arc;
///
/// // Create middleware with custom handler
/// // let my_handler: Arc<dyn ProductOSMiddlewareAsync> = Arc::new(MyHandler);
/// // let middleware = ProxyMiddleware::new(my_handler);
///
/// // Or use default
/// let middleware = ProxyMiddleware::default();
/// ```
#[derive(Clone)]
pub struct ProxyMiddleware {
    middleware_handler: Arc<dyn ProductOSMiddlewareAsync>,
}

impl ProxyMiddleware {
    /// Creates a new proxy middleware with the given handler.
    ///
    /// # Arguments
    ///
    /// * `middleware_handler` - Arc-wrapped middleware implementation
    ///
    /// # Examples
    ///
    /// ```
    /// use product_os_proxy::ProxyMiddleware;
    /// use product_os_server::ProductOSMiddlewareAsync;
    /// use std::sync::Arc;
    ///
    /// // let my_handler: Arc<dyn ProductOSMiddlewareAsync> = Arc::new(MyHandler);
    /// // let middleware = ProxyMiddleware::new(my_handler);
    /// ```
    pub fn new(middleware_handler: Arc<dyn ProductOSMiddlewareAsync>) -> Self {
        Self { middleware_handler }
    }

    /// Returns the inner async middleware handler.
    pub fn middleware_handler(&self) -> Arc<dyn ProductOSMiddlewareAsync> {
        Arc::clone(&self.middleware_handler)
    }
}

impl Default for ProxyMiddleware {
    /// Creates a default proxy middleware with pass-through behavior.
    ///
    /// # Returns
    ///
    /// A middleware instance using `DefaultAsyncMiddleware`
    fn default() -> Self {
        Self {
            middleware_handler: Arc::new(DefaultAsyncMiddleware::new()),
        }
    }
}

impl mitm::HttpHandler for ProxyMiddleware {
    fn handle_request(
        &mut self,
        _ctx: &mitm::HttpContext,
        request: Request<Body>,
    ) -> impl Future<Output = mitm::RequestOrResponse> + Send {
        let handler = self.middleware_handler.clone();
        async move {
            match handler.before(request).await {
                BeforeResult::Result(req) => mitm::RequestOrResponse::Request(req),
                BeforeResult::Bypass(res) => mitm::RequestOrResponse::Response(res),
            }
        }
    }

    fn handle_response(
        &mut self,
        _ctx: &mitm::HttpContext,
        request: Request<Body>,
        response: Response<Body>,
    ) -> impl Future<Output = Response<Body>> + Send {
        let handler = self.middleware_handler.clone();
        async move {
            let request_data = RequestData {
                method: product_os_request::Method::from_http_method(request.method()),
                headers: request.headers().clone(),
                uri: request.uri().clone(),
            };

            handler.after(response, request_data).await
        }
    }

    #[cfg(feature = "vpn")]
    fn handle_product_os_response(
        &mut self,
        _ctx: &mitm::HttpContext,
        request: Request<Body>,
        response: ProductOSResponse<Body>,
    ) -> impl Future<Output = Response<Body>> + Send {
        let handler = self.middleware_handler.clone();
        async move {
            let request_data = RequestData {
                method: product_os_request::Method::from_http_method(request.method()),
                headers: request.headers().clone(),
                uri: request.uri().clone(),
            };

            handler
                .after_product_os_response(response, request_data)
                .await
        }
    }
}

impl mitm::WebSocketHandler for ProxyMiddleware {
    async fn handle_message(
        &mut self,
        _ctx: &mitm::WebSocketContext,
        message: Message,
    ) -> Option<Message> {
        Some(message)
    }
}

#[derive(Default)]
pub struct DefaultAsyncMiddleware {}

impl DefaultAsyncMiddleware {
    pub fn new() -> Self {
        Self::default()
    }
}

impl ProductOSMiddleware for DefaultAsyncMiddleware {}

#[async_trait]
impl ProductOSMiddlewareAsync for DefaultAsyncMiddleware {
    async fn before(&self, request: Request<Body>) -> BeforeResult {
        // Pass through all requests unchanged
        BeforeResult::Result(request)
    }

    async fn after(&self, response: Response<Body>, _request_data: RequestData) -> Response<Body> {
        // Pass through all responses unchanged
        response
    }

    async fn success(
        &self,
        response: Response<Body>,
        _request_data: RequestData,
    ) -> Response<Body> {
        // Pass through successful responses unchanged
        response
    }

    async fn failure(
        &self,
        response: Response<Body>,
        _request_data: RequestData,
    ) -> Response<Body> {
        // Pass through failed responses unchanged
        response
    }

    async fn error(&self) -> Response<Body> {
        // Return empty 500 response for errors
        Response::builder()
            .status(500)
            .body(Body::empty())
            .unwrap_or_else(|_| Response::new(Body::empty()))
    }

    async fn before_product_os_response(&self, request: Request<Body>) -> BeforeResult {
        // Pass through all requests unchanged
        BeforeResult::Result(request)
    }

    async fn after_product_os_response(
        &self,
        response: ProductOSResponse<Body>,
        _request_data: RequestData,
    ) -> Response<Body> {
        let status = response
            .try_status()
            .unwrap_or(product_os_http::StatusCode::INTERNAL_SERVER_ERROR);
        let headers = response.try_get_headers().unwrap_or_default();

        let mut builder = Response::builder().status(status);
        for (key, value) in headers {
            builder = builder.header(key, value);
        }

        let Some((_, mut body)) = response.try_parts() else {
            return Response::builder()
                .status(500)
                .body(Body::empty())
                .unwrap_or_else(|_| Response::new(Body::empty()));
        };
        match body.as_bytes().await {
            Ok(bytes) => builder
                .body(Body::new(product_os_http_body::Bytes::from(bytes.to_vec())))
                .unwrap_or_else(|_| Response::new(Body::empty())),
            Err(_) => Response::builder()
                .status(500)
                .body(Body::empty())
                .unwrap_or_else(|_| Response::new(Body::empty())),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_proxy_middleware_new_creates_instance() {
        let handler: Arc<dyn ProductOSMiddlewareAsync> = Arc::new(DefaultAsyncMiddleware::new());
        let _middleware = ProxyMiddleware::new(handler);
    }

    #[test]
    fn test_proxy_middleware_default_creates_instance() {
        let _middleware = ProxyMiddleware::default();
    }

    #[test]
    fn test_proxy_middleware_clone() {
        let middleware = ProxyMiddleware::default();
        let _cloned = middleware.clone();
    }

    #[tokio::test]
    async fn test_default_async_middleware_before_passes_through() {
        let middleware = DefaultAsyncMiddleware::new();
        let request = Request::builder()
            .uri("http://example.com")
            .body(Body::empty())
            .unwrap();

        let result = middleware.before(request).await;
        match result {
            BeforeResult::Result(req) => {
                assert_eq!(req.uri(), "http://example.com");
            }
            BeforeResult::Bypass(_) => panic!("Expected BeforeResult::Result, got Bypass"),
        }
    }

    #[tokio::test]
    async fn test_default_async_middleware_after_passes_through() {
        let middleware = DefaultAsyncMiddleware::new();
        let response = Response::builder().status(200).body(Body::empty()).unwrap();
        let request_data = RequestData {
            method: product_os_request::Method::GET,
            headers: product_os_http::HeaderMap::new(),
            uri: "http://example.com".parse().unwrap(),
        };

        let res = middleware.after(response, request_data).await;
        assert_eq!(res.status(), 200);
    }

    #[tokio::test]
    async fn test_default_async_middleware_success_passes_through() {
        let middleware = DefaultAsyncMiddleware::new();
        let response = Response::builder().status(200).body(Body::empty()).unwrap();
        let request_data = RequestData {
            method: product_os_request::Method::GET,
            headers: product_os_http::HeaderMap::new(),
            uri: "http://example.com".parse().unwrap(),
        };

        let res = middleware.success(response, request_data).await;
        assert_eq!(res.status(), 200);
    }

    #[tokio::test]
    async fn test_default_async_middleware_failure_passes_through() {
        let middleware = DefaultAsyncMiddleware::new();
        let response = Response::builder().status(503).body(Body::empty()).unwrap();
        let request_data = RequestData {
            method: product_os_request::Method::GET,
            headers: product_os_http::HeaderMap::new(),
            uri: "http://example.com".parse().unwrap(),
        };

        let res = middleware.failure(response, request_data).await;
        assert_eq!(res.status(), 503);
    }

    #[tokio::test]
    async fn test_default_async_middleware_error_returns_500() {
        let middleware = DefaultAsyncMiddleware::new();
        let res = middleware.error().await;
        assert_eq!(res.status(), 500);
    }

    #[tokio::test]
    async fn test_default_async_middleware_before_product_os_response_passes_through() {
        let middleware = DefaultAsyncMiddleware::new();
        let request = Request::builder()
            .uri("http://example.com")
            .body(Body::empty())
            .unwrap();

        let result = middleware.before_product_os_response(request).await;
        match result {
            BeforeResult::Result(req) => {
                assert_eq!(req.uri(), "http://example.com");
            }
            BeforeResult::Bypass(_) => panic!("Expected BeforeResult::Result, got Bypass"),
        }
    }

    #[tokio::test]
    async fn test_proxy_middleware_handle_request_delegates_to_handler() {
        let mut middleware = ProxyMiddleware::default();
        let ctx = mitm::HttpContext {
            client_addr: "127.0.0.1:8080".parse().unwrap(),
        };
        let request = Request::builder()
            .uri("http://example.com/test")
            .body(Body::empty())
            .unwrap();

        let result = mitm::HttpHandler::handle_request(&mut middleware, &ctx, request).await;
        match result {
            mitm::RequestOrResponse::Request(req) => {
                assert_eq!(req.uri().path(), "/test");
            }
            mitm::RequestOrResponse::Response(_) => panic!("Expected Request, got Response"),
        }
    }

    #[tokio::test]
    async fn test_proxy_middleware_handle_response_delegates_to_handler() {
        let mut middleware = ProxyMiddleware::default();
        let ctx = mitm::HttpContext {
            client_addr: "127.0.0.1:8080".parse().unwrap(),
        };
        let request = Request::builder()
            .uri("http://example.com")
            .body(Body::empty())
            .unwrap();
        let response = Response::builder().status(200).body(Body::empty()).unwrap();

        let res =
            mitm::HttpHandler::handle_response(&mut middleware, &ctx, request, response).await;
        assert_eq!(res.status(), 200);
    }

    #[tokio::test]
    async fn test_proxy_middleware_websocket_handler_passes_through() {
        let mut middleware = ProxyMiddleware::default();
        let ctx = mitm::WebSocketContext::ClientToServer {
            src: "127.0.0.1:8080".parse().unwrap(),
            dst: "ws://example.com".parse().unwrap(),
        };
        let message = tokio_tungstenite::tungstenite::Message::Text("hello".to_string().into());

        let result = mitm::WebSocketHandler::handle_message(&mut middleware, &ctx, message).await;
        assert!(result.is_some());
        assert_eq!(
            result.unwrap(),
            tokio_tungstenite::tungstenite::Message::Text("hello".to_string().into())
        );
    }
}