mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-05-11 08:27:06 +00:00
3.1 KiB
3.1 KiB
warp
Warp has a pretty unique approach in terms of routing. It does not have a central router, rather a chain of filters composed together.
It encourages writing reusable filters to handle stuff like authentication, extracting user sessions, starting database transactions, etc.
Everything related to warp currently lives in the mas-core crate:
crates/core/src/handlers/: The actual handlers for each routeoauth2/: Everything related to OAuth 2.0/OIDC endpointsviews/: HTML views (login, registration, account management, etc.)
filters/: Reusable, composable filtersreply/: Composable replies
Defining a new endpoint
We usually keep one endpoint per file and use module roots to combine the filters of endpoints.
This is how it looks like in the current hierarchy at time of writing:
mod.rs: combines the filters fromoauth2,viewsandhealthoauth2/mod.rs: combines filters fromauthorization,discovery, etc.authorization.rs: handlesGET /oauth2/authorizeandGET /oauth2/authorize/stepdiscovery.rs: handlesGET /.well-known/openid-configuration- ...
views/mod.rs: combines the filters fromindex,login,logout, etc.index.rs: handlesGET /login.rs: handlesGET /loginandPOST /loginlogout.rs: handlesPOST /logout- ...
health.rs: handlesGET /health
All filters are functions that take their dependencies (the database connection pool, the template engine, etc.) as parameters and return an impl warp::Filter<Extract = (impl warp::Reply,)>.
// crates/core/src/handlers/hello.rs
// Don't be scared by the type at the end, just copy-paste it
pub(super) fn filter(
pool: &PgPool,
templates: &Templates,
cookies_config: &CookiesConfig,
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
// Handles `GET /hello/:param`
warp::path!("hello" / String)
.and(warp::get())
// Pass the template engine
.and(with_templates(templates))
// Extract the current user session
.and(optional_session(pool, cookies_config))
.and_then(get)
}
async fn get(
// Parameter from the route
parameter: String,
// Template engine
templates: Templates,
// The current user session
session: Option<SessionInfo>,
) -> Result<impl Reply, Rejection> {
let ctx = SomeTemplateContext::new(parameter)
.maybe_with_session(session);
let content = templates.render_something(&ctx)?;
let reply = html(content);
Ok(reply)
}
And then, it can be attached to the root handler:
// crates/core/src/handlers/mod.rs
use self::{health::filter as health, oauth2::filter as oauth2, hello::filter as hello};
pub fn root(
pool: &PgPool,
templates: &Templates,
config: &RootConfig,
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
health(pool)
.or(oauth2(pool, templates, &config.oauth2, &config.cookies))
// Attach it here, passing the right dependencies
.or(hello(pool, templates, &config.cookies))
}