Middleware chain based on iterator

This commit is contained in:
Martin Algesten
2021-12-20 22:02:36 +01:00
parent a2d62368f3
commit 43680335dc
2 changed files with 17 additions and 31 deletions

View File

@@ -134,27 +134,18 @@ pub trait Middleware {
} }
/// Continuation of a [`Middleware`] chain. /// Continuation of a [`Middleware`] chain.
pub struct MiddlewareNext<'a>(Next<'a>); pub struct MiddlewareNext<'a> {
pub(crate) chain: Box<dyn Iterator<Item = Arc<dyn Middleware + Send + Sync + 'static>>>,
impl<'a> MiddlewareNext<'a> { pub(crate) request_fn: Box<dyn FnOnce(Request) -> Result<Response, Error> + 'a>,
pub(crate) fn new(n: Next<'a>) -> Self {
MiddlewareNext(n)
}
}
pub(crate) enum Next<'a> {
/// Chained middleware. The Box around the next MiddlewareNext is to break the recursive datatype.
Chain(Arc<dyn Middleware>, Box<MiddlewareNext<'a>>),
/// End of the middleware chain doing the actual request invocation.
End(Box<dyn FnOnce(Request) -> Result<Response, Error> + 'a>),
} }
impl<'a> MiddlewareNext<'a> { impl<'a> MiddlewareNext<'a> {
/// Continue the middleware chain by providing (a possibly amended) [`Request`]. /// Continue the middleware chain by providing (a possibly amended) [`Request`].
pub fn handle(self, request: Request) -> Result<Response, Error> { pub fn handle(mut self, request: Request) -> Result<Response, Error> {
match self.0 { if let Some(step) = self.chain.next() {
Next::Chain(mw, next) => mw.handle(request, *next), step.handle(request, self)
Next::End(request_fn) => request_fn(request), } else {
(self.request_fn)(request)
} }
} }
} }

View File

@@ -5,7 +5,7 @@ use url::{form_urlencoded, ParseError, Url};
use crate::body::Payload; use crate::body::Payload;
use crate::header::{self, Header}; use crate::header::{self, Header};
use crate::middleware::{MiddlewareNext, Next}; use crate::middleware::MiddlewareNext;
use crate::unit::{self, Unit}; use crate::unit::{self, Unit};
use crate::Response; use crate::Response;
use crate::{agent::Agent, error::Error}; use crate::{agent::Agent, error::Error};
@@ -118,8 +118,6 @@ impl Request {
#[cfg(any(feature = "gzip", feature = "brotli"))] #[cfg(any(feature = "gzip", feature = "brotli"))]
self.add_accept_encoding(); self.add_accept_encoding();
let agent = &self.agent;
let deadline = match self.timeout.or(self.agent.config.timeout) { let deadline = match self.timeout.or(self.agent.config.timeout) {
None => None, None => None,
Some(timeout) => { Some(timeout) => {
@@ -142,20 +140,17 @@ impl Request {
unit::connect(unit, true, reader).map_err(|e| e.url(url.clone())) unit::connect(unit, true, reader).map_err(|e| e.url(url.clone()))
}; };
let response = if !agent.state.middleware.is_empty() { let response = if !self.agent.state.middleware.is_empty() {
// This clone is quite cheap since either we are cloning a Vec<Arc<dyn Middleware>>. let middleware = self.agent.state.middleware.clone();
let middleware = agent.state.middleware.clone();
// The request_fn is the final target in the middleware chain doing the actual invocation. let chain = Box::new(middleware.into_iter());
let mut chain = MiddlewareNext::new(Next::End(Box::new(request_fn)));
// Build middleware in reverse order. let request_fn = Box::new(request_fn);
for mw in middleware.into_iter().rev() {
chain = MiddlewareNext::new(Next::Chain(mw, Box::new(chain)));
}
// Run middleware chain let next = MiddlewareNext { chain, request_fn };
chain.handle(self)?
// // Run middleware chain
next.handle(self)?
} else { } else {
// Run the request_fn without any further indirection. // Run the request_fn without any further indirection.
request_fn(self)? request_fn(self)?