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