How to return errors from tower layers?

Hi,

I've been experimenting with using the tower crate(s) to build an internal service because the Service and Layer abstraction seem like a good design. However, I'm struggling with how to return errors from layers because I can't get the returned future types to match the type alias on Service::Future. The following is a simple example using the HelloWorld and LogService examples from the documentation. On line 76 I've added a synthetic fault injection to demonstrate a layer returning an error.

use hyper::{Request, Response};
use std::fmt;
use tower::{Layer, Service};

use http::StatusCode;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use tower::ServiceBuilder;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let log_layer = LogLayer{target: "foobar"};
    let api_service = HelloWorld{};

    let mut svc = ServiceBuilder::new().layer(log_layer).service(api_service);

    let request = Request::builder()
        .uri("https:// www. rust-lang. org/")
        .header("User-Agent", "my-awesome-agent/ 1.0")
        .body(vec![0u8, 1u8, 2u8]).unwrap();

    let _ = svc.call(request).await;
    Ok(())
}

pub struct LogLayer {
    target: &'static str,
}

impl<S> Layer<S> for LogLayer {
    type Service = LogService<S>;

    fn layer(&self, service: S) -> Self::Service {
        LogService {
            target: self.target,
            service
        }
    }
}

// This service implements the Log behavior
pub struct LogService<S> {
    target: &'static str,
    service: S,
}

impl<S, Request> Service<Request> for LogService<S>
where
    S: Service<Request>,
    Request: fmt::Debug,
{
    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) -> Self::Future {
        // inject synthetic failure
        let current_time = chrono::offset::Local::now();
        if current_time.timestamp_millis() % 100 == 0 {
            // This line won't compile
            return Box::pin(std::future::ready(Err("oh noes".to_string())));
        }

        // Insert log statement here or other functionality
        println!("request = {:?}, target = {:?}", request, self.target);
        self.service.call(request)
    }
}


#[derive(Clone)]
struct HelloWorld;

impl Service<Request<Vec<u8>>> for HelloWorld {
    type Response = Response<Vec<u8>>;
    type Error = String;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, _req: Request<Vec<u8>>) -> Self::Future {
        // create the body
        let body: Vec<u8> = "hello, world!\n".as_bytes().to_owned();
        // Create the HTTP response
        let resp = Response::builder()
            .status(StatusCode::OK)
            .body(body)
            .expect("Unable to create `http::Response`");

        // create a response in a future.
        let fut = async { Ok(resp) };

        // Return the response as an immediate future
        Box::pin(fut)
    }
}

The error I receive from this is:

 note: expected associated type `<S as tower::Service<Request>>::Future`
                       found struct `Pin<Box<std::future::Ready<Result<_, String>>>>`

I've tried a few variants for how to return the error future, but it's not clear to me the right way to make this compatible with the future type. I've tried to find examples for how to get this to work, but nothing has popped up so far.

Any suggestions would be helpful, thanks!

this lines indicate your layer will return exactly the same types as the inner type, there's no room for you to inject any error, as the type S::Error is generic without any bounds, you cannot create a value for it.

if you want to be able to inject error to the inner service, you must also create wrappers for the Error and Future associated types.

for example, suppose the error you want to inject is String, something like this could work:

enum LogError<E> {
    Injected(String),
    Inner(E),
}

impl<S, Request> Service<Request> for LogService<S>
where
    S: Service<Request>,
    Request: fmt::Debug,
{
    type Response = S::Response;
    type Error = LogError<S::Error>;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>;
    fn call(&mut self, request: Request) -> Self::Future {
        // inject synthetic failure
        let current_time = chrono::offset::Local::now();
        if current_time.timestamp_millis() % 100 == 0 {
            // This line won't compile
            return Box::pin(async { Err(LogError::Injected("oh noes".to_string())) });
        }

        // Insert log statement here or other functionality
        println!("request = {:?}, target = {:?}", request, self.target);
        let inner_future = self.service.call(request);
        Box::pin(async move { inner_future.await.map_err(LogError::Inner) })
    }
}

Thanks, that definitely helps me understand this more. Appreciate it!