rainier_middleware/lib.rs
1//! # rainier-middleware
2//!
3//! Middleware as values: the [`Middleware`] contract, the [`Pipeline`] that
4//! runs a chain of it, and the [`MiddlewareStack`] a route or a group attaches.
5//!
6//! ```
7//! use rainier_http::{Request, Response, StatusCode};
8//! use rainier_middleware::{Middleware, Next, Pipeline};
9//!
10//! struct RequireApiKey;
11//!
12//! #[async_trait::async_trait]
13//! impl Middleware for RequireApiKey {
14//! async fn handle(&self, request: Request, next: Next) -> Response {
15//! match request.header("x-api-key") {
16//! Some("secret") => next.run(request).await,
17//! _ => Response::new(StatusCode::UNAUTHORIZED),
18//! }
19//! }
20//! }
21//!
22//! # #[tokio::main] async fn main() {
23//! let pipeline = Pipeline::new()
24//! .through(RequireApiKey)
25//! .then(|_: Request| async { Response::text("secrets") });
26//!
27//! let denied = pipeline.run(Request::builder().build()).await;
28//! assert_eq!(denied.status(), StatusCode::UNAUTHORIZED);
29//!
30//! let allowed = pipeline.run(Request::builder().header("x-api-key", "secret").build()).await;
31//! assert_eq!(allowed.status(), StatusCode::OK);
32//! # }
33//! ```
34//!
35//! ## No names anywhere
36//!
37//! A route attaches `.middleware(Authenticate::new(auth))`, not
38//! `.middleware("auth")`. A name-keyed kernel needs the string because a
39//! dynamic language has nowhere to put the type; Rust does not, and the
40//! indirection costs more than it saves —
41//! a misspelled alias is a route that runs unguarded, and only an integration
42//! test notices.
43//!
44//! The router still does not depend on `rainier-auth` or `rainier-session`. It
45//! only ever sees `dyn Middleware`, which is defined here; the **application**
46//! names the concrete type, because the application is the crate that knows it.
47//!
48//! | The name-based equivalent | Rainier |
49//! |---|---|
50//! | a route-middleware alias map | nothing — attach the value |
51//! | a named group in a map | a function returning a [`MiddlewareStack`] |
52//! | the global list | [`MiddlewareRegistry::global`] |
53//! | a throttle spelled as a string | `ThrottleRequests::per_minute(60)` |
54//! | excluding middleware by name | `.without_middleware::<Authenticate<User>>()` |
55//!
56//! The one thing a name genuinely bought — middleware that cannot be built
57//! until the container is populated — is [`MiddlewareStack::deferred`], which
58//! runs a closure when the router compiles.
59
60#![deny(missing_docs)]
61#![deny(rustdoc::broken_intra_doc_links)]
62
63pub mod builtin;
64pub mod compression;
65pub mod method_override;
66pub mod pipeline;
67pub mod proxy;
68pub mod rate_limit;
69pub mod registry;
70pub mod stack;
71pub mod timeout;
72
73pub use builtin::{
74 AddHeaders, AllowedOrigins, ConvertEmptyStringsToNull, HandleCors, SharedThrottle,
75 ThrottleRequests, TrimStrings,
76};
77pub use compression::Compress;
78pub use method_override::MethodOverride;
79pub use pipeline::{ConcreteMiddleware, Destination, Middleware, Next, Pipeline, ReadyPipeline};
80pub use proxy::{Cidr, TrustProxies, Trusted};
81pub use rate_limit::{Hit, MemoryRateLimitStore, RateLimitStore};
82pub use registry::MiddlewareRegistry;
83pub use stack::{IntoMiddlewareStack, MiddlewareStack};
84pub use timeout::Timeout;
85
86// Re-exported so middleware implementors get the attribute macro without
87// adding the dependency themselves.
88pub use async_trait::async_trait;