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 (
89 dyn (Fn(Request, Client) -> BoxFuture<'static, Result<ResponseAsync>>)
90 + Send
91 + Sync
92 + 'static
93 ),
94}
95
96impl<'a> Next<'a> {
97 /// Create a new instance
98 pub fn new(
99 next: &'a [Arc<dyn Middleware>],
100 endpoint: &'a (
101 dyn (Fn(Request, Client) -> BoxFuture<'static, Result<ResponseAsync>>)
102 + Send
103 + Sync
104 + 'static
105 ),
106 ) -> Self {
107 Self {
108 endpoint,
109 next_middleware: next,
110 }
111 }
112
113 /// Asynchronously execute the remaining middleware chain.
114 #[must_use]
115 pub fn run(mut self, req: Request, client: Client) -> BoxFuture<'a, Result<ResponseAsync>> {
116 if let Some((current, next)) = self.next_middleware.split_first() {
117 self.next_middleware = next;
118 current.handle(req, client, self)
119 } else {
120 (self.endpoint)(req, client)
121 }
122 }
123}