1

I'm making a proxy server using tokio and hyper. Depending on the request, there can be multiple proxy servers(can send request). I have the addresses of the servers to proxy as a vector and try to connect in turn.

For example, when a GET /test HTTP/1.1 request is received, there are proxy server candidates named ["aaa.com", "bbb.com", "ccc.com"]. My server tries to connect in the order aaa -> bbb -> ccc and sends a proxy request to the server where the connection succeeds. If it cannot connect to all servers, a 500 error is returned.

The problem is that the same request must be sent until the connection to the server is successful, but if use hyper's method, the request is consumed and a problem occurs in the next loop. But I couldn't find a way to copy hyper::Request (clone, etc.) How can I copy a hyper::Request in Rust?

fn proxy(
    mut req: Request<hyper::Body>,
) -> BoxFuture<'static, Response<hyper::Body>> {
    let route = req
        .extensions_mut()
        .remove::<RoutingInfo>()
        .expect("route not found.");

    let size = route.api_config.target_servers.len();

    Box::pin(async move {
        let mut index = 1;
        let resp = loop {    
           let target_server = schedule_server(&route);// get "aaa.com" or "bbb.com" or "ccc.com"
           let https = HttpsConnectorBuilder::new()
               .with_native_roots()
               .https_or_http()
               .enable_http1()
               .build();
           let client = Client::builder().build::<_, RawBody>(https);

           let destination = format!("{}{}", target_server, route.target_uri);
           *req.uri_mut() = destination.parse::<Uri>().unwrap();

           match client.request(req).await {
                Ok(resp) => {
                    break resp;
                },
                Err(_e) => {//TODO: only connection error
                    if index == size {
                        break Response::builder()
                            .status(StatusCode::INTERNAL_SERVER_ERROR)
                            .body(hyper::Body::empty())
                            .unwrap();
                    }
                    index++;
                }
            };
        };
        resp
    })
}
error[E0382]: borrow of moved value: `req`
  --> src/service/proxy.rs:83:14
   |
83 |             *req.uri_mut() = destination.parse::<Uri>().unwrap();
   |              ^^^^^^^^^^^^^ value borrowed here after move
...
87 |            match client.request(req).await {
   |                                 --- value moved here, in previous iteration of loop
kmdreko
  • 42,554
  • 6
  • 57
  • 106
yjlee
  • 363
  • 1
  • 14
  • I don't think this is always possible as the request body could be a single-use value (a stream, for example) and so hyper doesn't implement such an API. Maybe consider using `reqwest`; its `Request` type has a [`try_clone`](https://docs.rs/reqwest/latest/reqwest/struct.Request.html#method.try_clone) method that will fail at runtime if the body is a single-use value. – cdhowie Aug 01 '22 at 14:48

0 Answers0