gotham/middleware/mod.rs
1//! Defines types for `Middleware`, a reusable unit of logic that can apply to a group of requests
2//! by being added to the `Pipeline` in a `Router`.
3
4use std::panic::RefUnwindSafe;
5use std::pin::Pin;
6
7use crate::handler::HandlerFuture;
8use crate::state::State;
9
10pub mod chain;
11pub mod cookie;
12pub mod logger;
13pub mod security;
14#[cfg(feature = "session")]
15pub mod session;
16pub mod state;
17pub mod timer;
18
19#[cfg(feature = "derive")]
20pub use gotham_derive::NewMiddleware;
21
22/// `Middleware` has the opportunity to provide additional behaviour to the `Request` / `Response`
23/// interaction. For example:
24///
25/// * The request can be halted due to some unmet precondition;
26/// * Processing the request can be delayed until some other action has completed;
27/// * Middleware-specific state data can be recorded in the `State` struct for use elsewhere;
28/// * The returned future can be manipulated via continuations to provide additional behaviour
29/// after the request completes.
30///
31/// # Examples
32///
33/// Taking no action, and immediately passing the `Request` through to the rest of the application:
34///
35/// ```rust
36/// # #[macro_use]
37/// # extern crate gotham_derive;
38/// #
39/// # use std::pin::Pin;
40/// #
41/// # use hyper::{Body, Response, StatusCode};
42/// # use gotham::handler::HandlerFuture;
43/// # use gotham::middleware::Middleware;
44/// # use gotham::pipeline::*;
45/// # use gotham::router::builder::*;
46/// # use gotham::state::State;
47/// # use gotham::test::TestServer;
48/// #
49/// #[derive(NewMiddleware, Copy, Clone)]
50/// struct NoopMiddleware;
51///
52/// impl Middleware for NoopMiddleware {
53/// fn call<Chain>(self, state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
54/// where Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static
55/// {
56/// chain(state)
57/// }
58/// }
59/// #
60/// # fn main() {
61/// # let (chain, pipelines) = single_pipeline(
62/// # new_pipeline()
63/// # .add(NoopMiddleware)
64/// # .build()
65/// # );
66/// #
67/// # let router = build_router(chain, pipelines, |route| {
68/// # route
69/// # .get("/")
70/// # .to_new_handler(|| {
71/// # Ok(|state| (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap()))
72/// # });
73/// # });
74/// #
75/// # let test_server = TestServer::new(router).unwrap();
76/// # let response = test_server.client().get("https://example.com/").perform().unwrap();
77/// # assert_eq!(response.status(), StatusCode::ACCEPTED);
78/// # }
79/// ```
80///
81/// Recording a piece of state data before passing the request through:
82///
83/// ```rust
84/// # #[macro_use]
85/// # extern crate gotham_derive;
86/// #
87/// # use std::pin::Pin;
88/// #
89/// # use hyper::{Response, StatusCode};
90/// # use gotham::handler::HandlerFuture;
91/// # use gotham::middleware::Middleware;
92/// # use gotham::pipeline::*;
93/// # use gotham::router::builder::*;
94/// # use gotham::state::State;
95/// # use gotham::test::TestServer;
96/// #
97/// #[derive(NewMiddleware, Copy, Clone)]
98/// struct MiddlewareWithStateData;
99///
100/// #[derive(StateData)]
101/// struct MiddlewareStateData {
102/// i: i32,
103/// }
104///
105/// impl Middleware for MiddlewareWithStateData {
106/// fn call<Chain>(self, mut state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
107/// where Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static
108/// {
109/// state.put(MiddlewareStateData { i: 10 });
110/// chain(state)
111/// }
112/// }
113/// #
114/// # fn main() {
115/// # let (chain, pipelines) = single_pipeline(
116/// # new_pipeline()
117/// # .add(MiddlewareWithStateData)
118/// # .build()
119/// # );
120/// #
121/// # let router = build_router(chain, pipelines, |route| {
122/// # route
123/// # .get("/")
124/// # .to_new_handler(|| {
125/// # Ok(|mut state: State| {
126/// # let data = state.take::<MiddlewareStateData>();
127/// # let body = format!("{}", data.i).into_bytes();
128/// # (state, Response::builder().status(StatusCode::OK).body(body.into()).unwrap())
129/// # })
130/// # });
131/// # });
132/// #
133/// # let test_server = TestServer::new(router).unwrap();
134/// # let response = test_server.client().get("https://example.com/").perform().unwrap();
135/// # assert_eq!(response.status(), StatusCode::OK);
136/// # let body = response.read_utf8_body().unwrap();
137/// # assert_eq!(&body, "10");
138/// # }
139/// ```
140///
141/// Decorating the response after the request has completed:
142///
143/// ```rust
144/// # #[macro_use]
145/// # extern crate gotham_derive;
146/// #
147/// # use std::pin::Pin;
148/// #
149/// # use futures_util::{FutureExt, TryFutureExt};
150/// # use hyper::{Body, Response, StatusCode};
151/// # use hyper::header::WARNING;
152/// # use gotham::handler::HandlerFuture;
153/// # use gotham::middleware::Middleware;
154/// # use gotham::pipeline::*;
155/// # use gotham::router::builder::*;
156/// # use gotham::state::State;
157/// # use gotham::test::TestServer;
158/// #
159/// #[derive(NewMiddleware, Copy, Clone)]
160/// struct MiddlewareAddingResponseHeader;
161///
162/// impl Middleware for MiddlewareAddingResponseHeader {
163/// fn call<Chain>(self, state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
164/// where Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static
165/// {
166/// chain(state)
167/// .map_ok(|(state, mut response)| {
168/// response.headers_mut().insert(WARNING, "299 example.com Deprecated".parse().unwrap());
169/// (state, response)
170/// })
171/// .boxed()
172/// }
173/// }
174/// #
175/// # fn main() {
176/// # let (chain, pipelines) = single_pipeline(
177/// # new_pipeline()
178/// # .add(MiddlewareAddingResponseHeader)
179/// # .build()
180/// # );
181/// #
182/// # let router = build_router(chain, pipelines, |route| {
183/// # route
184/// # .get("/")
185/// # .to_new_handler(|| {
186/// # Ok(|state| (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap()))
187/// # });
188/// # });
189/// #
190/// # let test_server = TestServer::new(router).unwrap();
191/// # let response = test_server.client().get("https://example.com/").perform().unwrap();
192/// # assert_eq!(response.status(), StatusCode::ACCEPTED);
193/// #
194/// # {
195/// # let warning = response.headers().get(WARNING).unwrap();
196/// # assert_eq!(warning, "299 example.com Deprecated");
197/// # }
198/// # }
199/// ```
200///
201/// Terminating the request early based on some arbitrary condition:
202///
203/// ```rust
204/// # #[macro_use]
205/// # extern crate gotham_derive;
206/// #
207/// # use std::pin::Pin;
208/// #
209/// # use hyper::{Body, Response, Method, StatusCode};
210/// # use futures_util::future::{self, FutureExt};
211/// # use gotham::helpers::http::response::create_empty_response;
212/// # use gotham::handler::HandlerFuture;
213/// # use gotham::middleware::Middleware;
214/// # use gotham::pipeline::*;
215/// # use gotham::router::builder::*;
216/// # use gotham::state::{State, FromState};
217/// # use gotham::test::TestServer;
218/// #
219/// #[derive(NewMiddleware, Copy, Clone)]
220/// struct ConditionalMiddleware;
221///
222/// impl Middleware for ConditionalMiddleware {
223/// fn call<Chain>(self, state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
224/// where Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static
225/// {
226/// if *Method::borrow_from(&state) == Method::GET {
227/// chain(state)
228/// } else {
229/// let response = create_empty_response(&state, StatusCode::METHOD_NOT_ALLOWED);
230/// future::ok((state, response)).boxed()
231/// }
232/// }
233/// }
234/// #
235/// # fn main() {
236/// # let (chain, pipelines) = single_pipeline(
237/// # new_pipeline()
238/// # .add(ConditionalMiddleware)
239/// # .build()
240/// # );
241/// #
242/// # let router = build_router(chain, pipelines, |route| {
243/// # route
244/// # .get_or_head("/")
245/// # .to_new_handler(|| {
246/// # Ok(|state| (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap()))
247/// # });
248/// # });
249/// #
250/// # let test_server = TestServer::new(router).unwrap();
251/// #
252/// # let response = test_server.client().get("https://example.com/").perform().unwrap();
253/// # assert_eq!(response.status(), StatusCode::ACCEPTED);
254/// #
255/// # let response = test_server.client().head("https://example.com/").perform().unwrap();
256/// # assert_eq!(response.status(), StatusCode::METHOD_NOT_ALLOWED);
257/// # }
258/// ```
259///
260/// Asynchronous middleware, which continues the request after some action completes:
261///
262/// ```rust
263/// # #[macro_use]
264/// # extern crate gotham_derive;
265/// #
266/// # use std::pin::Pin;
267/// #
268/// # use futures_util::future::{self, FutureExt, TryFutureExt};
269/// # use hyper::{Body, Response, StatusCode};
270/// # use gotham::handler::HandlerFuture;
271/// # use gotham::middleware::Middleware;
272/// # use gotham::pipeline::*;
273/// # use gotham::router::builder::*;
274/// # use gotham::state::State;
275/// # use gotham::test::TestServer;
276/// #
277/// #[derive(NewMiddleware, Copy, Clone)]
278/// struct AsyncMiddleware;
279///
280/// impl Middleware for AsyncMiddleware {
281/// fn call<Chain>(self, state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
282/// where Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static
283/// {
284/// // This could be any asynchronous action. `future::lazy(_)` defers a function
285/// // until the next cycle of tokio's event loop.
286/// let f = future::lazy(|_| Ok(()));
287/// f.and_then(move |_| chain(state)).boxed()
288/// }
289/// }
290/// #
291/// # fn main() {
292/// # let (chain, pipelines) = single_pipeline(
293/// # new_pipeline()
294/// # .add(AsyncMiddleware)
295/// # .build()
296/// # );
297/// #
298/// # let router = build_router(chain, pipelines, |route| {
299/// # route
300/// # .get("/")
301/// # .to_new_handler(|| {
302/// # Ok(|state| (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap()))
303/// # });
304/// # });
305/// #
306/// # let test_server = TestServer::new(router).unwrap();
307/// # let response = test_server.client().get("https://example.com/").perform().unwrap();
308/// # assert_eq!(response.status(), StatusCode::ACCEPTED);
309/// # }
310/// ```
311pub trait Middleware {
312 /// Entry point to the middleware. To pass the request on to the application, the middleware
313 /// invokes the `chain` function with the provided `state`.
314 ///
315 /// By convention, the middleware should:
316 ///
317 /// * Not modify any request components added to `State` by Gotham.
318 /// * Avoid modifying parts of the `State` that don't strictly need to be modified to perform
319 /// its function.
320 fn call<Chain>(self, state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
321 where
322 Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static,
323 Self: Sized;
324}
325
326/// A type which is used to spawn new `Middleware` values. When implementing a `Middleware`, this
327/// defines how instances of the `Middleware` are created.
328///
329/// This can be derived by `Middleware` that implement `Clone`, and will result in the following
330/// implementation:
331///
332/// ```rust
333/// # extern crate gotham;
334/// #
335/// # use std::pin::Pin;
336/// #
337/// # use gotham::middleware::{NewMiddleware, Middleware};
338/// # use gotham::handler::HandlerFuture;
339/// # use gotham::pipeline::new_pipeline;
340/// # use gotham::state::State;
341/// #
342/// # #[allow(unused)]
343/// # #[derive(Copy, Clone)]
344/// struct MyMiddleware;
345///
346/// impl NewMiddleware for MyMiddleware {
347/// type Instance = Self;
348///
349/// fn new_middleware(&self) -> anyhow::Result<Self::Instance> {
350/// Ok(self.clone())
351/// }
352/// }
353/// #
354/// # impl Middleware for MyMiddleware {
355/// # fn call<Chain>(self, _state: State, _chain: Chain) -> Pin<Box<HandlerFuture>>
356/// # where Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + 'static
357/// # {
358/// # unimplemented!()
359/// # }
360/// # }
361/// #
362/// # fn main() {
363/// # // Just for the implied type assertion.
364/// # new_pipeline().add(MyMiddleware).build();
365/// # }
366pub trait NewMiddleware: Sync + RefUnwindSafe {
367 /// The type of `Middleware` created by the `NewMiddleware`.
368 type Instance: Middleware;
369
370 /// Create and return a new `Middleware` value.
371 fn new_middleware(&self) -> anyhow::Result<Self::Instance>;
372}