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
Awaiting a Conn runs handlers in three steps:
- 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 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) build a fresh
Conn from conn.client() in after_response, configure it (filtered headers, replayed body,
handler-internal state), and queue it via
ConnExt::set_followup. The
IntoFuture for &mut Conn loop picks the follow-up up after the
current cycle’s after_response has fully unwound: it recycles the current response body,
swaps the follow-up into place, and runs another full (run → network → after_response)
cycle on it.
§Handler-author affordances on Conn
Lifecycle-driving methods — queue a follow-up, stash or recover the transport-level error —
live on the ConnExt extension trait rather than directly on
Conn. Bring them into scope with use trillium_client::ConnExt;. The split is
intentional: those operations are meaningful only from inside a handler, and keeping them off
Conn’s inherent surface stops them from appearing in IDE completion for user code that holds
a Conn directly.
§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 the conn and runs
after_response anyway. A handler that recovers from an error should:
- Inspect
conn.error()to detect the failure. - Populate response state synthetically (
set_status,response_headers_mut,set_response_body) or enqueue a new followup conn. - Call
conn.take_error()to clear the error so the awaited conn returnsOk.
The error / take_error / set_error methods live on the
ConnExt extension trait — use trillium_client::ConnExt; to bring them into scope.
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".