omjadas / hudsucker

Intercepting HTTP/S proxy
https://crates.io/crates/hudsucker
Apache License 2.0
206 stars 35 forks source link

[feature request] Use anonymous function as handler parameter #27

Closed axxop closed 2 years ago

axxop commented 2 years ago

Thanks for your crate

My use case is that when I usewith_http_handler/with_incoming_message_handler/with_outgoing_message_handler, I want to store the request & response. In the current case, it looks like the only way to store it is to create global mutables using something like lazy_static. Switching to anonymous functions we can easily create Arc<Mutex<>> in any context to store the data. what do you think about this?

omjadas commented 2 years ago

Hi,

This is possible now using the existing handlers

use hudsucker::{
    async_trait::async_trait,
    hyper::{body::Bytes, Body, Request, Response},
    tungstenite::Message,
    HttpContext, HttpHandler, MessageContext, MessageHandler, RequestOrResponse,
};
use std::sync::{Arc, Mutex};

#[derive(Clone)]
struct MyHttpHandler {
    pub requests: Arc<Mutex<Vec<Request<Bytes>>>>,
    pub responses: Arc<Mutex<Vec<Response<Bytes>>>>,
}

#[async_trait]
impl HttpHandler for MyHttpHandler {
    async fn handle_request(
        &mut self,
        _ctx: &HttpContext,
        req: Request<Body>,
    ) -> RequestOrResponse {
        let (stored_req, req) = {
            let (parts, body) = req.into_parts();

            let mut builder = Request::builder()
                .method(&parts.method)
                .uri(&parts.uri)
                .version(parts.version);

            if let Some(h) = builder.headers_mut() {
                *h = parts.headers.clone();
            }

            let bytes = hyper::body::to_bytes(body).await.unwrap();

            (
                builder.body(bytes.clone()).unwrap(),
                Request::from_parts(parts, Body::from(bytes)),
            )
        };

        self.requests.lock().unwrap().push(stored_req);

        RequestOrResponse::Request(req)
    }

    async fn handle_response(&mut self, _ctx: &HttpContext, res: Response<Body>) -> Response<Body> {
        let (stored_res, res) = {
            let (parts, body) = res.into_parts();

            let mut builder = Response::builder()
                .status(parts.status)
                .version(parts.version);

            if let Some(h) = builder.headers_mut() {
                *h = parts.headers.clone();
            }

            let bytes = hyper::body::to_bytes(body).await.unwrap();

            (
                builder.body(bytes.clone()).unwrap(),
                Response::from_parts(parts, Body::from(bytes)),
            )
        };

        self.responses.lock().unwrap().push(stored_res);

        res
    }
}

#[derive(Clone)]
struct MyMessageHandler {
    pub messages: Arc<Mutex<Vec<Message>>>,
}

#[async_trait]
impl MessageHandler for MyMessageHandler {
    async fn handle_message(&mut self, _ctx: &MessageContext, msg: Message) -> Option<Message> {
        self.messages.lock().unwrap().push(msg.clone());
        Some(msg)
    }
}
axxop commented 2 years ago

Thanks for your reply, this answered my question.