2

I'm trying to implement a retry in a client built with Hyper v0.11, but I can't find a way to reuse a request for different attempts:

#[macro_use]
extern crate hyper;
extern crate futures;
extern crate tokio_core;

use futures::Future;

use hyper::{Client, Body, Uri, StatusCode};
use hyper::server::{Request, Response};
use hyper::client::HttpConnector;
use hyper::Get;

use tokio_core::reactor::Core;

fn main() {

    let mut core = Core::new().expect("Event Loop");
    let handle = core.handle();
    let client = Client::new(&handle.clone());

    // Request
    let json = r#"{"user":"Peter"}"#;
    let mut req: Request<Body> = Request::new(Post, "http://localhost:8080/create/user".parse().unwrap());
    req.headers_mut().set(ContentType::json());
    req.headers_mut().set(ContentLength(json.len() as u64));
    req.set_body(json);

    dispatch_request(&client, req, 2);
}

fn clone_req(req: &Request) -> Request {
    let mut new_req = Request::new(req.method().clone(), req.uri().clone());
    new_req.headers_mut().extend(req.headers().iter());
    new_req.set_body(req.body()); // <------- here the error occur!
    new_req
}

fn dispatch_request(
    client: &Client<HttpConnector, Body>,
    req: Request<Body>,
    n_retry: u32,
) -> Box<Future<Error = hyper::Error, Item = Response>> {
    println!("Attemp {}", n_retry);
    let max_retry = 3;

    let client_clone = client.clone();

    let clone_req = clone_req(&req);

    let resp = client.request(req).then(move |result| match result {
        Ok(client_resp) => {
            if client_resp.status() == hyper::StatusCode::Ok {
                Box::new(futures::future::ok(client_resp))
            } else if n_retry < max_retry {
                dispatch_request(&client_clone, clone_req, max_retry + 1)
            } else {
                Box::new(futures::future::ok(
                    Response::new().with_status(StatusCode::ServiceUnavailable),
                ))
            }
        }
        Err(e) => {
            println!("Connection error: {:?}", &e);
            Box::new(futures::future::ok(
                Response::new().with_status(StatusCode::ServiceUnavailable),
            ))
        }
    });
    Box::new(resp)
}

This is the compilation error:

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:28:22
   |
28 |     new_req.set_body(req.body());
   |                      ^^^ cannot move out of borrowed content

The error is clear, but I don't know how to fix it.

cspinetta
  • 443
  • 6
  • 17
  • I'm not sure if it's possible to copy body because body is a Stream. – Stargateur Jul 06 '17 at 08:32
  • In order to take the request's body with `body()`, you must own the request, yet in `clone_req` you only have a reference. Have you looked into the many [existing questions with the same error message](https://stackoverflow.com/search?q=%5Brust%5D+cannot+move+out+of+borrowed+content) which will likely give you an insight on what you are doing wrong? – E_net4 Jul 06 '17 at 10:23
  • @E_net4 yes, I spent much time searching for some workaround, seeing examples where trying to write the body (like a stream) in other variable, and so on, but I couldn't fix it. – cspinetta Jul 06 '17 at 16:21

2 Answers2

0

An option is to use the tokio-retry crate. I only tried with hyper v0.12 though.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
oddg
  • 43
  • 7
-1

Why not put retry in main loop? Note you also need to do core.run somewhere.

loop {
    let req = Request::new(Get, "http://www.google.com".parse().unwrap());
    let resp = dispatch_request(&client, req, );
    if let Ok(_) = resp.wait() {
        break
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Vishal Kumar
  • 762
  • 1
  • 7
  • 15
  • Thanks for your response! But, your proposal is not sufficient to cover the functionality I need. I need a retry scheme prepared for **all type of request**, also the request is given from outside, because it's a proxy. I need a way to propagate the incoming request to outcoming new request (similar to the incoming, but with other host), and the problem is how to propagate the body (ideally without using a `.concat()` in order to propagate a potentially stream of chunks in a properly way) – cspinetta Jul 15 '17 at 05:42