Using Clone for a Hyper Response Body type

In the context of a HTTP/S proxy, I want to manipulate and change a Response type using the hyper library. I'm having trouble doing this because it seems no matter how I read it, it gets dropped because I'm unable to clone it.

error[E0599]: no method named `clone` found for struct `Response` in the current scope
  --> examples/log.rs:33:23
   |
33 |         let bod = res.clone().into_body();
   |                       ^^^^^ method not found in `Response<Body>`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `hudsucker` due to previous error

I'm using the hudsucker crate (which uses hyper). Here is my modified version of the example log in the github repo.

use hudsucker::{
    async_trait::async_trait,
    certificate_authority::RcgenAuthority,
    hyper::{Body, Request, Response},
    tokio_tungstenite::tungstenite::Message,
    *,
};
use rustls_pemfile as pemfile;
use std::net::SocketAddr;
use tracing::*;

async fn shutdown_signal() {
    tokio::signal::ctrl_c()
        .await
        .expect("Failed to install CTRL+C signal handler");
}

#[derive(Clone)]
struct LogHandler;

#[async_trait]
impl HttpHandler for LogHandler {
    async fn handle_request(
        &mut self,
        _ctx: &HttpContext,
        req: Request<Body>,
    ) -> RequestOrResponse {
        //println!("{:?}", req);
        req.into()
    }

    async fn handle_response(&mut self, _ctx: &HttpContext, mut res: Response<Body>) -> Response<Body> {
        let bod = res.clone().into_body();
        // println!("{:?}", hyper::body::to_bytes(bod).await);
        println!("{:?}", bod);
        res
    }
}

#[async_trait]
impl WebSocketHandler for LogHandler {
    async fn handle_message(&mut self, _ctx: &WebSocketContext, msg: Message) -> Option<Message> {
        //println!("{:?}", msg);
        Some(msg)
    }
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    let mut private_key_bytes: &[u8] = include_bytes!("ca/hudsucker.key");
    let mut ca_cert_bytes: &[u8] = include_bytes!("ca/hudsucker.cer");
    let private_key = rustls::PrivateKey(
        pemfile::pkcs8_private_keys(&mut private_key_bytes)
            .expect("Failed to parse private key")
            .remove(0),
    );
    let ca_cert = rustls::Certificate(
        pemfile::certs(&mut ca_cert_bytes)
            .expect("Failed to parse CA certificate")
            .remove(0),
    );

    let ca = RcgenAuthority::new(private_key, ca_cert, 1_000)
        .expect("Failed to create Certificate Authority");

    let proxy = Proxy::builder()
        .with_addr(SocketAddr::from(([127, 0, 0, 1], 3000)))
        .with_rustls_client()
        .with_ca(ca)
        .with_http_handler(LogHandler)
        .with_websocket_handler(LogHandler)
        .build();

    if let Err(e) = proxy.start(shutdown_signal()).await {
        error!("{}", e);
    }
}

I'm not to familiar with how the Body bytes work, would I be cloning that and sending a new body with modified bytes?

Response<T> does not implement Clone, so quite literally you cannot clone it.

But it does offer a few tools that will let you destructure and then recreate one. For instance:

async fn handle_response(
    &mut self,
    _ctx: &HttpContext,
    mut res: Response<Body>,
) -> Response<Body> {
    let (parts, bod) = res.into_parts();
    let bytes = hyper::body::to_bytes(bod).await.unwrap();
    println!("{:?}", bytes);

    Response::from_parts(parts, Body::from(bytes))
}
1 Like

Thank you! This is exactly what I was looking for :orange_heart:

1 Like