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}