crux_http/
middleware.rs

1//! Middleware types
2//!
3//! # Examples
4//! ```no_run
5//! use crux_http::middleware::{Next, Middleware};
6//! use crux_http::{client::Client, Request, ResponseAsync, Result};
7//! use std::time;
8//! use std::sync::Arc;
9//!
10//! /// Log each request's duration
11//! #[derive(Debug)]
12//! pub struct Logger;
13//!
14//! #[async_trait::async_trait]
15//! impl Middleware for Logger {
16//!     async fn handle(
17//!         &self,
18//!         req: Request,
19//!         client: Client,
20//!         next: Next<'_>,
21//!     ) -> Result<ResponseAsync> {
22//!         println!("sending request to {}", req.url());
23//!         let now = time::Instant::now();
24//!         let res = next.run(req, client).await?;
25//!         println!("request completed ({:?})", now.elapsed());
26//!         Ok(res)
27//!     }
28//! }
29//! ```
30//! `Middleware` can also be instantiated using a free function thanks to some convenient trait
31//! implementations.
32//!
33//! ```no_run
34//! use futures_util::future::BoxFuture;
35//! use crux_http::middleware::{Next, Middleware};
36//! use crux_http::{client::Client, Request, ResponseAsync, Result};
37//! use std::time;
38//! use std::sync::Arc;
39//!
40//! fn logger<'a>(req: Request, client: Client, next: Next<'a>) -> BoxFuture<'a, Result<ResponseAsync>> {
41//!     Box::pin(async move {
42//!         println!("sending request to {}", req.url());
43//!         let now = time::Instant::now();
44//!         let res = next.run(req, client).await?;
45//!         println!("request completed ({:?})", now.elapsed());
46//!         Ok(res)
47//!     })
48//! }
49//! ```
50
51use std::sync::Arc;
52
53use crate::{Client, Request, ResponseAsync, Result};
54
55mod redirect;
56
57pub use redirect::Redirect;
58
59use async_trait::async_trait;
60use futures_util::future::BoxFuture;
61
62/// Middleware that wraps around remaining middleware chain.
63#[async_trait]
64pub trait Middleware: 'static + Send + Sync {
65    /// Asynchronously handle the request, and return a response.
66    async fn handle(&self, req: Request, client: Client, next: Next<'_>) -> Result<ResponseAsync>;
67}
68
69// This allows functions to work as middleware too.
70#[async_trait]
71impl<F> Middleware for F
72where
73    F: Send
74        + Sync
75        + 'static
76        + for<'a> Fn(Request, Client, Next<'a>) -> BoxFuture<'a, Result<ResponseAsync>>,
77{
78    async fn handle(&self, req: Request, client: Client, next: Next<'_>) -> Result<ResponseAsync> {
79        (self)(req, client, next).await
80    }
81}
82
83/// The remainder of a middleware chain, including the endpoint.
84#[allow(missing_debug_implementations)]
85#[derive(Copy, Clone)]
86pub struct Next<'a> {
87    next_middleware: &'a [Arc<dyn Middleware>],
88    endpoint: &'a (dyn (Fn(Request, Client) -> BoxFuture<'static, Result<ResponseAsync>>)
89             + Send
90             + Sync
91             + 'static),
92}
93
94impl<'a> Next<'a> {
95    /// Create a new instance
96    pub fn new(
97        next: &'a [Arc<dyn Middleware>],
98        endpoint: &'a (dyn (Fn(Request, Client) -> BoxFuture<'static, Result<ResponseAsync>>)
99                 + Send
100                 + Sync
101                 + 'static),
102    ) -> Self {
103        Self {
104            endpoint,
105            next_middleware: next,
106        }
107    }
108
109    /// Asynchronously execute the remaining middleware chain.
110    pub fn run(mut self, req: Request, client: Client) -> BoxFuture<'a, Result<ResponseAsync>> {
111        if let Some((current, next)) = self.next_middleware.split_first() {
112            self.next_middleware = next;
113            current.handle(req, client, self)
114        } else {
115            (self.endpoint)(req, client)
116        }
117    }
118}