pub trait Handler:
Send
+ Sync
+ 'static {
// Provided methods
fn run(&self, conn: Conn) -> impl Future<Output = Conn> + Send { ... }
fn init(&mut self, info: &mut Info) -> impl Future<Output = ()> + Send { ... }
fn before_send(&self, conn: Conn) -> impl Future<Output = Conn> + Send { ... }
fn has_upgrade(&self, upgrade: &Upgrade) -> bool { ... }
fn upgrade(&self, upgrade: Upgrade) -> impl Future<Output = ()> + Send { ... }
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!") };
let app = TestServer::new(handler).await;
app.get("/").await.assert_ok().assert_body("trillium!");// as an async function
async fn handler(conn: trillium::Conn) -> trillium::Conn {
conn.ok("trillium!")
}
let app = TestServer::new(handler).await;
app.get("/").await.assert_ok().assert_body("trillium!");The simplest implementation of Handler for a named type looks like this:
pub struct MyHandler;
impl trillium::Handler for MyHandler {
async fn run(&self, conn: trillium::Conn) -> trillium::Conn {
conn
}
}
let app = TestServer::new(MyHandler).await;
app.get("/").await.assert_status(404); // we did not halt or set a body status
//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.
Provided Methods§
Sourcefn run(&self, conn: Conn) -> impl Future<Output = Conn> + Send
fn run(&self, conn: Conn) -> impl Future<Output = Conn> + Send
Executes this handler, performing any modifications to the Conn that are desired.
Sourcefn init(&mut self, info: &mut Info) -> impl Future<Output = ()> + Send
fn init(&mut self, info: &mut Info) -> impl Future<Output = ()> + Send
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.
Sourcefn before_send(&self, conn: Conn) -> impl Future<Output = Conn> + Send
fn before_send(&self, conn: Conn) -> impl Future<Output = Conn> + Send
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 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(&self, upgrade: Upgrade) -> impl Future<Output = ()> + Send
fn upgrade(&self, upgrade: Upgrade) -> impl Future<Output = ()> + Send
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.
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.