use crate::http::middlewares::{Middleware, MiddlewareFlow};
use crate::http::response::anyhow::ResponseError;
use ntex::service::{Middleware as ServiceMiddleware, Service, ServiceCtx};
use ntex::web;
use ntex::web::{Error, WebRequest, WebResponse};
use std::rc::Rc;
use tracing::{debug, error, info};
#[derive(Clone)]
pub struct MiddlewareChain {
middlewares: Rc<Vec<Middleware>>,
}
impl MiddlewareChain {
pub fn new(middlewares: Vec<Middleware>) -> Self {
MiddlewareChain {
middlewares: Rc::new(middlewares),
}
}
pub fn single(middleware: Middleware) -> Self {
Self::new(vec![middleware])
}
}
impl<S, Cfg> ServiceMiddleware<S, Cfg> for MiddlewareChain {
type Service = MiddlewareChainInternal<S>;
fn create(&self, service: S, _cfg: Cfg) -> Self::Service {
MiddlewareChainInternal {
service,
middlewares: self.middlewares.clone(),
}
}
}
pub struct MiddlewareChainInternal<S> {
service: S,
middlewares: Rc<Vec<Middleware>>,
}
impl<S, Err> Service<web::WebRequest<Err>> for MiddlewareChainInternal<S>
where
S: Service<web::WebRequest<Err>, Response = web::WebResponse, Error = web::Error>,
Err: web::ErrorRenderer,
{
type Response = web::WebResponse;
type Error = web::Error;
ntex::forward_ready!(service);
async fn call(
&self,
request: web::WebRequest<Err>,
ctx: ServiceCtx<'_, Self>,
) -> Result<Self::Response, Self::Error> {
let (mut req, payload) = request.into_parts();
info!("{} {}", req.method(), req.path());
for middleware in self.middlewares.iter() {
if let Middleware::Before(mid) = middleware {
let flow = mid
.handle(req)
.await
.map_err(|err| Error::from(ResponseError::new(err)))?;
match flow {
MiddlewareFlow::Continue(modified_req) => {
req = modified_req;
}
MiddlewareFlow::Respond(request, response) => {
return Ok(WebResponse::new(response, request));
}
}
}
}
let request = WebRequest::from_parts(req, payload).unwrap();
debug!("calling http controller -> method...");
let mut response = ctx.call(&self.service, request).await?;
for middleware in self.middlewares.iter() {
if let Middleware::After(mid) = middleware {
match mid.handle(response).await {
Ok(modified_resp) => {
response = modified_resp;
}
Err(err) => {
error!("[middleware-chain-error][after] {err:?}");
return Err(Error::from(ResponseError::new(err)));
}
}
}
}
Ok(response)
}
}