How do you calculate the Future types of a Hyper Tokio service?


#1

Problem: when you start chaining together Futures in a Hyper service then defining the types becomes really hard for me as a human.
Example: I have a proxy server that returns its own HTTP responses in error case and otherwise passes through HTTP responses:

type DirectResponse = FutureResult<Response, hyper::Error>;
type UpstreamResponse = futures::Then<
    FutureResponse,
    FutureResult<Response, hyper::Error>,
    fn(Result<Response>) -> FutureResult<Response, hyper::Error>,
>;

impl Service for Proxy {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Either<DirectResponse, UpstreamResponse>;

    fn call(&self, mut request: Request) -> Self::Future {
        // ... no so relavant code here ...
        
        Either::B(self.client.request(request).then(|result| {
            let our_response = match result {
                Ok(response) => {
                    let mut headers = response.headers().clone();
                    headers.append_raw("Via", "rustnish-0.0.1");
                    response.with_headers(headers)
                }
                Err(_) => {
                    // For security reasons do not show the exact error to end users.
                    // @todo Log the error.
                    Response::new()
                        .with_status(StatusCode::BadGateway)
                        .with_body("Something went wrong, please try again later.")
                }
            };
            futures::future::ok(our_response)
        }))
    }
}    

(full code at https://github.com/klausi/rustnish/blob/goal-06/src/lib.rs )

Compiler error:

error[E0281]: type mismatch: `fn(std::result::Result<hyper::Response, errors::Error>) -> futures::FutureResult<hyper::Response, hyper::Error>` implements the trait `std::ops::FnOnce<(std::result::Result<hyper::Response, errors::Error>,)>`, but the trait `std::ops::FnOnce<(std::result::Result<hyper::Response, hyper::Error>,)>` is required
  --> src/lib.rs:39:6
   |
39 | impl Service for Proxy {
   |      ^^^^^^^ expected enum `hyper::Error`, found struct `errors::Error`
   |
   = note: required because of the requirements on the impl of `futures::Future` for `futures::Then<hyper::client::FutureResponse, futures::FutureResult<hyper::Response, hyper::Error>, fn(std::result::Result<hyper::Response, errors::Error>) -> futures::FutureResult<hyper::Response, hyper::Error>>`
   = note: required because of the requirements on the impl of `futures::Future` for `futures::future::Either<futures::FutureResult<hyper::Response, hyper::Error>, futures::Then<hyper::client::FutureResponse, futures::FutureResult<hyper::Response, hyper::Error>, fn(std::result::Result<hyper::Response, errors::Error>) -> futures::FutureResult<hyper::Response, hyper::Error>>>`

I think that Hyper’s FutureResponse type conflicts with fn(Result) which uses errors::Error. How can I calculate the types I need to define here? How can I convert between hyper::Error and errors::Error?

Any help appreciated, thanks!


How can I forward the stream of a Hyper client response to a Hyper sever response? (Proxy)
#2

Typically map_err adapter is used to convert from one error type to another.

You’re brave for using unboxed types in this circumstance - most people (I think) would work with boxed (type erased) return types.


What's everyone working on this week (35/2017)?
#3

You’re getting bit by the error chain import. If you look at the type mismatch, it’s asking for a fn(std::result::Result<hyper::Response, hyper::Error>,)> within the UpstreamResponse type.
You’re using the Result type that has been imported by error chain, which has a type of std::result::Result<hyper::Response, errors::Error>, so the function type doesn’t match. The following compiles:

type UpstreamResponse = futures::Then<
    FutureResponse,
    FutureResult<Response, hyper::Error>,
    fn(std::result::Result<Response, hyper::Error>) -> FutureResult<Response, hyper::Error>,
>;

#4

You are absolutely right, thank you!

I completely forgot about error_chain and that it also has its own Result type :frowning:

I even defined Result with the second error type, but Ms. Compiler told me to remove it. 2 lessons learned here:

  1. don’t blindly fix compiler complaints, they might be a hint to a type mismatch that you caused with competing use statements.
  2. Name your crates consistently and don’t make them look like std. Crate name “error-chain”, import name “error_chain”, namespace “error” …that feels wrong and should be “error_chain” everywhere.

#5

You can probably help yourself by not using use errors::* :slight_smile: Best to use module names in this case.