pub trait ClientHandler:
Send
+ Sync
+ 'static {
// Provided methods
fn run(&self, conn: &mut Conn) -> impl Future<Output = Result<()>> + Send { ... }
fn after_response(
&self,
conn: &mut Conn,
) -> impl Future<Output = Result<()>> + Send { ... }
fn name(&self) -> Cow<'static, str> { ... }
}Expand description
Client middleware extension point.
ClientHandler is the composition primitive for trillium-client middleware. It mirrors the
server-side trillium::Handler in spirit — handlers compose into tuples, halting on the conn
short-circuits the chain — but differs in shape because the client has different ownership
semantics: the conn is user-owned, so handlers take &mut Conn rather than owned Conn, and
they return Result<()> because client execution can fail outright (TLS handshake, refresh
token, signing, etc.).
§Lifecycle
Each Conn::exec call runs handlers in two passes:
- Forward pass —
run. Each handler runs in declared order. A handler may mutate the request, short-circuit by callingConn::halt+ populating synthetic response state (cache hit, mocked response), or fail. If any handler halts, subsequentrunmethods are skipped. - Network round-trip. Skipped if the conn is halted.
- Reverse pass —
after_response. Each handler’safter_responseruns in reverse order, regardless of halt status. This mirrorstrillium::Handler::before_sendand lets handlers that observe the response (loggers, metrics, status checkers) record cache hits and short-circuited responses, not just transport-backed ones.
§Re-execution
Handlers that need to re-issue a request (follow-redirects, retry, auth-refresh) construct a
fresh Conn from a Client they own — typically a separate one without recursion into the
same handler chain — execute it, and std::mem::swap it with the user’s conn. See the
follow_redirects and retry handlers for examples.
Pre-executing the same conn from inside run is undefined: the conn is mid-pipeline at that
point and re-entering IntoFuture recurses through the same handler chain. Don’t.
§Type erasure
Implementors write ClientHandler using native async fn syntax. The crate type-erases
handlers internally for storage on Client; Client::with_handler accepts any
impl ClientHandler, and Client::downcast_handler is the way to recover the concrete type
from a Client that has one installed.
Provided Methods§
Sourcefn run(&self, conn: &mut Conn) -> impl Future<Output = Result<()>> + Send
fn run(&self, conn: &mut Conn) -> impl Future<Output = Result<()>> + Send
Forward-pass hook, called before the network round-trip in declared order.
A handler can mutate the request, halt to short-circuit, or fail. The default implementation is a no-op.
Sourcefn after_response(
&self,
conn: &mut Conn,
) -> impl Future<Output = Result<()>> + Send
fn after_response( &self, conn: &mut Conn, ) -> impl Future<Output = Result<()>> + Send
Reverse-pass hook, called after the network round-trip (or after a halt-skipped network call) in reverse declared order. Always runs regardless of halt status or transport error.
A handler can observe the response, mutate it before passing it to upstream handlers, recover from a transport-level error, or fail.
Transport errors. If the network call failed (connect refused, TLS handshake error,
malformed HTTP frame, timeout), the framework stashes the error on
Conn::error and runs after_response anyway. A handler that
recovers (stale-if-error cache, retry-with-fallback, circuit breaker) should:
- Inspect
conn.error()to detect the failure. - Populate response state synthetically (
set_status,response_headers_mut,set_response_body). - Call
conn.take_error()to clear the error so the awaited conn returnsOk.
If no handler clears the error, it propagates as Err from the awaited conn.
The default implementation is a no-op.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.