pub trait Handler: Send + Sync + 'static {
// Required method
fn run<'life0, 'async_trait>(
&'life0 self,
conn: Conn
) -> Pin<Box<dyn Future<Output = Conn> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
// Provided methods
fn init<'life0, 'life1, 'async_trait>(
&'life0 mut self,
_info: &'life1 mut Info
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
fn before_send<'life0, 'async_trait>(
&'life0 self,
conn: Conn
) -> Pin<Box<dyn Future<Output = Conn> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn has_upgrade(&self, _upgrade: &Upgrade) -> bool { ... }
fn upgrade<'life0, 'async_trait>(
&'life0 self,
_upgrade: Upgrade
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn name(&self) -> Cow<'static, str> { ... }
}
Expand description
§The building block for Trillium applications.
§Concept
Many other frameworks have a notion of middleware
and endpoints
,
in which the model is that a request passes through a router and then
any number of middlewares, then a single endpoint that returns a
response, and then passes a response back through the middleware
stack.
Because a Trillium Conn represents both a request and response, there
is no distinction between middleware and endpoints, as all of these
can be modeled as Fn(Conn) -> Future<Output = Conn>
.
§Implementing Handler
The simplest handler is an async closure or async fn that receives a Conn and returns a Conn, and Handler has a blanket implementation for any such Fn.
// as a closure
let handler = |conn: trillium::Conn| async move { conn.ok("trillium!") };
use trillium_testing::prelude::*;
assert_ok!(get("/").on(&handler), "trillium!");
// as an async function
async fn handler(conn: trillium::Conn) -> trillium::Conn {
conn.ok("trillium!")
}
use trillium_testing::prelude::*;
assert_ok!(get("/").on(&handler), "trillium!");
The simplest implementation of Handler for a named type looks like this:
pub struct MyHandler;
#[trillium::async_trait]
impl trillium::Handler for MyHandler {
async fn run(&self, conn: trillium::Conn) -> trillium::Conn {
conn
}
}
use trillium_testing::prelude::*;
assert_not_handled!(get("/").on(&MyHandler)); // we did not halt or set a body status
Temporary Note: Until rust has true async traits, implementing
handler requires the use of the async_trait macro, which is reexported
as trillium::async_trait
.
§Full trait specification
Unfortunately, the async_trait macro results in the difficult-to-read documentation at the top of the page, so here is how the trait is actually defined in trillium code:
#[trillium::async_trait]
pub trait Handler: Send + Sync + 'static {
async fn run(&self, conn: Conn) -> Conn;
async fn init(&mut self, info: &mut Info); // optional
async fn before_send(&self, conn: Conn); // optional
fn has_upgrade(&self, _upgrade: &Upgrade) -> bool; // optional
async fn upgrade(&self, _upgrade: Upgrade); // mandatory only if has_upgrade returns true
fn name(&self) -> Cow<'static, str>; // optional
}
See each of the function definitions below for advanced implementation.
For most application code and even trillium-packaged framework code,
run
is the only trait function that needs to be implemented.
Required Methods§
Provided Methods§
sourcefn init<'life0, 'life1, 'async_trait>(
&'life0 mut self,
_info: &'life1 mut Info
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn init<'life0, 'life1, 'async_trait>(
&'life0 mut self,
_info: &'life1 mut Info
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Performs one-time async set up on a mutable borrow of the Handler before the server starts accepting requests. This allows a Handler to be defined in synchronous code but perform async setup such as establishing a database connection or fetching some state from an external source. This is optional, and chances are high that you do not need this.
It also receives a mutable borrow of the Info
that represents
the current connection.
stability note: This may go away at some point. Please open an **issue if you have a use case which requires it.
sourcefn before_send<'life0, 'async_trait>(
&'life0 self,
conn: Conn
) -> Pin<Box<dyn Future<Output = Conn> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn before_send<'life0, 'async_trait>(
&'life0 self,
conn: Conn
) -> Pin<Box<dyn Future<Output = Conn> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Performs any final modifications to this conn after all handlers have been run. Although this is a slight deviation from the simple conn->conn->conn chain represented by most Handlers, it provides an easy way for libraries to effectively inject a second handler into a response chain. This is useful for loggers that need to record information both before and after other handlers have run, as well as database transaction handlers and similar library code.
❗IMPORTANT NOTE FOR LIBRARY AUTHORS: Please note that this
will run whether or not the conn has was halted before
Handler::run
was called on a given conn. This means that if
you want to make your before_send
callback conditional on
whether run
was called, you need to put a unit type into the
conn’s state and check for that.
stability note: I don’t love this for the exact reason that it breaks the simplicity of the conn->conn->model, but it is currently the best compromise between that simplicity and convenience for the application author, who should not have to add two Handlers to achieve an “around” effect.
sourcefn has_upgrade(&self, _upgrade: &Upgrade) -> bool
fn has_upgrade(&self, _upgrade: &Upgrade) -> bool
predicate function answering the question of whether this Handler
would like to take ownership of the negotiated Upgrade. If this
returns true, you must implement Handler::upgrade
. The first
handler that responds true to this will receive ownership of the
trillium::Upgrade
in a subsequent call to Handler::upgrade
sourcefn upgrade<'life0, 'async_trait>(
&'life0 self,
_upgrade: Upgrade
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn upgrade<'life0, 'async_trait>(
&'life0 self,
_upgrade: Upgrade
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
This will only be called if the handler reponds true to
Handler::has_upgrade
and will only be called once for this
upgrade. There is no return value, and this function takes
exclusive ownership of the underlying transport once this is
called. You can downcast the transport to whatever the source
transport type is and perform any non-http protocol communication
that has been negotiated. You probably don’t want this unless
you’re implementing something like websockets. Please note that
for many transports such as TcpStreams, dropping the transport
(and therefore the Upgrade) will hang up / disconnect.
Trait Implementations§
source§impl Handler for Box<dyn Handler>
impl Handler for Box<dyn Handler>
source§fn run<'life0, 'async_trait>(
&'life0 self,
conn: Conn
) -> Pin<Box<dyn Future<Output = Conn> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn run<'life0, 'async_trait>(
&'life0 self,
conn: Conn
) -> Pin<Box<dyn Future<Output = Conn> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
source§fn init<'life0, 'life1, 'async_trait>(
&'life0 mut self,
info: &'life1 mut Info
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn init<'life0, 'life1, 'async_trait>(
&'life0 mut self,
info: &'life1 mut Info
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
source§fn before_send<'life0, 'async_trait>(
&'life0 self,
conn: Conn
) -> Pin<Box<dyn Future<Output = Conn> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn before_send<'life0, 'async_trait>(
&'life0 self,
conn: Conn
) -> Pin<Box<dyn Future<Output = Conn> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
source§fn name(&self) -> Cow<'static, str>
fn name(&self) -> Cow<'static, str>
source§fn has_upgrade(&self, upgrade: &Upgrade) -> bool
fn has_upgrade(&self, upgrade: &Upgrade) -> bool
Handler::upgrade
. The first
handler that responds true to this will receive ownership of the
trillium::Upgrade
in a subsequent call to Handler::upgrade
source§fn upgrade<'life0, 'async_trait>(
&'life0 self,
upgrade: Upgrade
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn upgrade<'life0, 'async_trait>(
&'life0 self,
upgrade: Upgrade
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Handler::has_upgrade
and will only be called once for this
upgrade. There is no return value, and this function takes
exclusive ownership of the underlying transport once this is
called. You can downcast the transport to whatever the source
transport type is and perform any non-http protocol communication
that has been negotiated. You probably don’t want this unless
you’re implementing something like websockets. Please note that
for many transports such as TcpStreams, dropping the transport
(and therefore the Upgrade) will hang up / disconnect.