How to copy http::request::Request?

We have a reverse proxy like this:

let new_service = make_service_fn(move |conn: &AddrStream| {
    let remote_addr = conn.remote_addr().ip();
    async move {
        Ok::<_, GenericError>(service_fn(move |req| {
            let req_clone = req.clone();
            let remote_addr_clone = remote_addr.clone();
            handle(remote_addr, req);
            doStuffWith(req_clone);
        }))
    }
});

But apparently, clone is not a part of the http:request:Request object. How do we get the a values-only copy of the Request object without making it into a JSON string and serializing and parsing it?

You can't clone a request because it might have a Body object, which makes the body available using a streaming technique, making it impossible to clone. If you just want the headers, you can use into_parts to obtain the Parts struct that contains e.g. the Uri and stuff. You can clone the first four fields of Parts, but Parts is not Clone as the extensions field can't be cloned either.

There is Request::try_clone, see https://github.com/seanmonstar/reqwest/releases

Yea, I tried into_parts and clone and try_clone. This seems to work:

let new_service = make_service_fn(move |conn: &AddrStream| {
    let remote_addr = conn.remote_addr().ip();
    async move {
        Ok::<_, GenericError>(service_fn(move |req| {
            let r_clone = req.clone();
            handle(remote_addr, req)
        }))
    }
});

EDIT: I lied... it doesn't compile...

This is the message I get when I try to use try_clone there:

Request<Body>
no method named `try_clone` found for struct `http::request::Request<_>` in the current scope
method not found in `http::request::Request<_>`rustc(E0599)

https://docs.rs/http/0.2.1/http/request/struct.Request.html
It does not seem to have clone, try_clone, etc here...

Oh, I missed that you use request, I point out solution for reqwest.
But I suppose reqwest should use http crate under the hood.
So you can look at https://github.com/seanmonstar/reqwest/blob/d879d6f6c2f261524632b0d135a05b0bae14c7fd/src/async_impl/request.rs#L113 and see how it implemented

Yea, this looks hopeful, thanks. Let me try and get this working locally.

This yields another odd error:

async move {
    Ok::<_, GenericError>(service_fn(move |req| {
        let mut req_clone = Request::new(req.body().clone());
        *req_clone.headers_mut() = req.headers().clone();
        tokio::spawn(lib::q::do_stuff_with(req_clone));
        sidecar::hyper_reverse_proxy::handle(remote_addr, req)
    }))
}

this gives an error on |req| saying:

Request<Body>
type annotations needed for `http::request::Request<R>`
type must be known at this point rustc(E0282)

Okay, we got it working, but it is not pretty...

pub fn clone_request(parts:Parts, body:Body, new_url:String) -> (Request<Body>, Request<Body>) {

    let entire_body_as_vec = block_on(body
        .try_fold(Vec::new(), |mut data, chunk| async move {
            data.extend_from_slice(&chunk);
            Ok(data)
        }));

    let body_str = String::from_utf8(entire_body_as_vec.unwrap()).expect("response was not valid utf-8");
    let mut request_builder = Request::builder().uri(new_url.clone()).method(parts.method.as_str());
    let mut second_request_builder = Request::builder().uri(new_url).method(parts.method.as_str());

    for (header_name, header_value) in parts.headers.iter() {
        request_builder = request_builder.header(header_name.as_str(), header_value);
        second_request_builder = second_request_builder.header(header_name.as_str(), header_value);
    }

    let req1 = request_builder
        .body(Body::from(body_str.clone()))
        .unwrap();
    let req2 = second_request_builder
        .body(Body::from(body_str.clone()))
        .unwrap();

    (req1, req2)
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.