product-os-proxy 0.0.17

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.
#[cfg(feature = "decoder")]
mod decoder;
mod proxy;
mod rewind;

pub mod certificate_authority;

use std::convert::Infallible;
use std::future::Future;
use std::io::Read;
use hyper::{Request, Response, Uri};

use std::net::SocketAddr;
use tokio_tungstenite::tungstenite::Message;

pub(crate) use rewind::Rewind;

pub use async_trait::async_trait;
use bytes::Bytes;
pub use hyper;
use hyper::header::{HeaderName, HeaderValue, InvalidHeaderName};
use product_os_http::HeaderMap;
use product_os_http_body::{BodyBytes as Body, BodyBytes, BodyError};
pub use product_os_http_body::BodyError as Error;

pub use tokio_rustls::rustls;
pub use tokio_tungstenite;

#[cfg(feature = "decoder")]
pub use decoder::{decode_request, decode_response};

#[cfg(feature = "vpn")]
use product_os_request::ProductOSResponse;

pub use proxy::*;


#[derive(Debug)]
pub enum RequestOrResponse {
    /// HTTP Request
    Request(Request<Body>),
    /// HTTP Response
    Response(Response<Body>),
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct HttpContext {
    pub client_addr: SocketAddr,
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum WebSocketContext {
    ClientToServer {
        src: SocketAddr,
        dst: Uri,
    },
    ServerToClient {
        src: Uri,
        dst: SocketAddr,
    },
}

#[async_trait]
pub trait HttpHandler: Clone + Send + Sync + 'static {
    async fn handle_request(
        &mut self,
        _ctx: &HttpContext,
        request: Request<Body>,
    ) -> RequestOrResponse {
        RequestOrResponse::Request(request)
    }

    async fn handle_response(
        &mut self,
        _ctx: &HttpContext,
        _: Request<Body>, // original_request
        response: Response<Body>,
    ) -> Response<Body> {
        response
    }

    #[cfg(feature = "vpn")]
    async fn handle_product_os_response(
        &mut self,
        _ctx: &HttpContext,
        _: Request<Body>, // original_request
        response: ProductOSResponse<Body>,
    ) -> Response<Body> {
        let status = response.status();

        let mut headers = HeaderMap::new();
        for (name, value) in &response.get_headers() {
            let header_name = match HeaderName::from_bytes(name.as_bytes()) {
                Ok(name) => name,
                Err(e) => {
                    tracing::error!("Error with header kind: {:?}", e);
                    continue
                }
            };
            let header_value = HeaderValue::from_str(value.as_str()).unwrap();
            headers.insert(header_name, header_value);
        }

        let (_, mut body) = response.parts();
        let body_bytes = match body.as_bytes().await {
            Ok(bytes) => bytes.to_vec(),
            Err(e) => {
                tracing::error!("Error converting body to bytes: {:?}", e);
                Vec::new()
            }
        };

        let response_body = Body::new(Bytes::from(body_bytes));

        let mut response_to_return = Response::builder();

        for (name, value) in headers {
            match name {
                None => {}
                Some(name) => { response_to_return = response_to_return.header(name, value); }
            }
        }

        response_to_return = response_to_return.status(status);
        response_to_return.body(response_body).unwrap()
    }

    fn should_intercept(
        &mut self,
        _ctx: &HttpContext,
        _req: &Request<Body>,
    ) -> impl Future<Output = bool> + Send {
        async { true }
    }
}

#[async_trait]
pub trait WebSocketHandler: Clone + Send + Sync + 'static {
    async fn handle_message(
        &mut self,
        _ctx: &WebSocketContext,
        message: Message,
    ) -> Option<Message> {
        Some(message)
    }
}