tower-rs / tower

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

Idea: Preventing Inappropriate Service Invocation #724

Closed byte-sourcerer closed 1 year ago

byte-sourcerer commented 1 year ago

The Service trait requires users to call poll_ready before invoking the call function in order to check if the service is ready or not. However, users are not obligated to do this and may forget to call poll_ready.

Therefore, it is worth considering whether it would be beneficial to use the type system to prevent users from invoking call if the service is not ready.

One suggestion is to introduce a ready token with type Service::Ready as follows:

pub trait Service<Request> {
    type Response;
    type Error;
    type Future: Future<Output = Result<Self::Response, Self::Error>>;
    type Ready;

    fn poll_ready(
        &mut self,
        cx: &mut Context<'_>
    ) -> Poll<Result<Self::Ready, Self::Error>>;

    fn call(&mut self, token: Self::Ready, req: Request) -> Self::Future;
}

With this approach, users must obtain a token: Self::Ready before invoking call, which can only be extracted from Poll::Ready(Ok(Self::Ready)). This follows the "Parse, not validate" paradigm.

Incorporating the ready token in the implementation of Service would help eliminate the need for checking the invalid state that may occur while invoking call:

https://github.com/tower-rs/tower/blob/664cb35abb8da07b6c46212375c481153d8abfb9/tower/src/limit/rate/service.rs#L116

davidpdrsn commented 1 year ago

This has been discussed before. See