tower-rs/tower-http

A means to conditionally provide a response to a request in lieu of calling the inner service

bassmanitram opened this issue · 3 comments

Feature Request

Conditionally provide a response instead of calling the inner service.

Motivation

This middleware provides a way to conditionally skip calling the inner service
if a response is already available for the request.

Probably the simplest visual for this is providing a cached response, though it
is unlikely that this middleware is suitable for a robust response cache interface
(or, more accurately, it's not the motivation for developing this so I haven't
looked into it adequately enough to provide a robust argument for it being so!).

The premise is simple - write a (non-async) function that assesses the current request
for the possibility of providing a response before invoking the inner service. Return
the "early" response if that is possible, otherwise return the request.

The differences between using this and returning an error from a pre-inner layer are.

  1. The response will still pass through any post-inner layer processing
  2. You aren't "hacking" the idea of an error when all you are trying to do is avoid
    calling the inner service when it isn't necessary.

Possible uses:

  • A pre-inner layer produces a successful response before the inner service is called
  • Caching (though see above - this could, however, be the layer that skips the inner
    call while a more robust pre-inner layer implements the actual caching)
  • Mocking
  • Debugging
  • ...

Proposal

The idea should be fairly straightforward to implement, being a merge of the capabilities of tower predicate layers
and simple pre-service layers. The slight complications of providing alternative responses from the function,
and providing different futures from the poll method can be overcome by judicious use of enums.

For simplicity, the first iteration should only allow sync functions. Maybe at a later date async functions can be
allowed (particularly after async methods in traits become generally available), which would then allow for a
more generic stack of multiple "end" services to be constructed in the vein of the chain of responsibility pattern!

Alternatives

Since the main purpose of this middleware is, effectively, to conditionally switch from pre-request processing to post-response processing - i.e. it's effectively a Request->Request mapper or a Request->Response generator - there currently exists no general way to do this in the provided filters of the crate.

If you are using axum, this kind of thing is easy to do with axum::middleware::from_fn.

I'm skeptical about having this middleware (as implemented in #427) in tower-http. But maybe we could have a simplified version of axum::middleware::from_fn without all of the axum-specific bits (wdyt @davidpdrsn?).