tower-rs / tower

async fn(Request) -> Result<Response, Error>
https://docs.rs/tower
MIT License
3.56k stars 281 forks source link

Returning a response from a tower layer #727

Closed ovidiu-ionescu closed 1 year ago

ovidiu-ionescu commented 1 year ago

I am trying to write a layer for hyper that would resolve the static requests for swagger objects. I tried using the log example but this part I can not get to compile:

    impl<S> Service<Request<Body>> for SwaggerService<S>
    where
        S: Service<Request<Body>>,
    {
        type Response = S::Response;
        type Error = S::Error;
        type Future = S::Future;

        fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
            self.service.poll_ready(cx)
        }

        fn call(&mut self, request: Request<Body>) -> Self::Future {
            // Insert log statement here or other functionality

            let path = request.uri().path();
            println!("request = {:?}, target = {:?}", path, self.target);
            if path == "/swagger/index.css" {
                let res = super::submodule::get_swagger_ui(request).unwrap(); // Response<Body>
                async move { res }
            } else {
                self.service.call(request)
            }
        }
    }

The compilation error is:

143 |         fn call(&mut self, request: Request<Body>) -> Self::Future {
    |                                                       ------------ expected `<S as Service<Request<Body>>>::Future` because of return type
...
150 |                 async move { res }
    |                 ^^^^^^^^^^^^^^^^^^ expected associated type, found `async` block
    |
    = note: expected associated type `<S as Service<Request<Body>>>::Future`
                 found `async` block `[async block@lib-hyper-organizator/src/swagger.rs:150:17: 150:35]`
    = help: consider constraining the associated type `<S as Service<Request<Body>>>::Future` to `[async block@lib-hyper-organizator/src/swagger.rs:150:17: 150:35]` or calling a method that returns `<S as Service<Request<Body>>>::Future`

I could not find any example for achieving this. Is there any solution?

olix0r commented 1 year ago

You can either use futures::future::Either to encode both return variants, or you can use Pin<Box<dyn Future<...>>> and box both response futures. Here's an example that boxes the response future: https://github.com/linkerd/linkerd2-proxy/blob/0814a154ba8c8cc7af394ac3fa6f940bd01755ae/linkerd/stack/src/fail_on_error.rs#LL30-L69C2

ovidiu-ionescu commented 1 year ago

I managed to get it working by taking inspiration from the linkerd source.

Thanks!