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}