use crate::error::{Error, Result};
use crate::request::Request;
use crate::response::Response;
use async_trait::async_trait;
use std::sync::Arc;
#[async_trait]
pub trait Middleware: Send + Sync + 'static {
async fn handle(&self, req: Request, next: Next) -> Result<Response>;
}
#[derive(Clone)]
pub struct Next {
middlewares: Arc<Vec<Arc<dyn Middleware>>>,
index: usize,
}
impl Next {
pub fn new(middlewares: Arc<Vec<Arc<dyn Middleware>>>) -> Self {
Self {
middlewares,
index: 0,
}
}
pub async fn run(mut self, req: Request) -> Result<Response> {
if self.index < self.middlewares.len() {
let middleware = self.middlewares[self.index].clone();
self.index += 1;
middleware.handle(req, self).await
} else {
Err(Error::not_found("Route not found"))
}
}
}
#[async_trait]
impl<M: Middleware + ?Sized> Middleware for Arc<M> {
async fn handle(&self, req: Request, next: Next) -> Result<Response> {
(**self).handle(req, next).await
}
}
pub(crate) struct Layered {
middlewares: Vec<Arc<dyn Middleware>>,
inner: crate::handler::DynHandler,
}
impl Layered {
pub(crate) fn new(
middlewares: Vec<Arc<dyn Middleware>>,
inner: crate::handler::DynHandler,
) -> Self {
Self { middlewares, inner }
}
}
#[async_trait]
impl crate::handler::Handler for Layered {
async fn call(&self, req: Request) -> Result<Response> {
let inner = self.inner.clone();
let mut chain = self.middlewares.clone();
chain.push(Arc::new(move |req: Request, _next: Next| {
let inner = inner.clone();
async move { inner.call(req).await }
}));
Next::new(Arc::new(chain)).run(req).await
}
}
#[async_trait]
impl<F, Fut> Middleware for F
where
F: Fn(Request, Next) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<Response>> + Send + 'static,
{
async fn handle(&self, req: Request, next: Next) -> Result<Response> {
(self)(req, next).await
}
}
pub async fn logger(req: Request, next: Next) -> Result<Response> {
let method = req.method.clone();
let uri = req.uri.clone();
let start = std::time::Instant::now();
let response = next.run(req).await;
let duration = start.elapsed();
println!("{} {} - {:?}", method, uri, duration);
response
}
pub fn cors(
allowed_origins: Vec<String>,
) -> impl Fn(
Request,
Next,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Response>> + Send>>
+ Clone {
move |req: Request, next: Next| {
let origins = allowed_origins.clone();
Box::pin(async move {
if req.method == http::Method::OPTIONS {
let mut response = Response::new(http::StatusCode::NO_CONTENT);
if !origins.is_empty() {
let origin = if origins.contains(&"*".to_string()) {
"*".to_string()
} else {
origins[0].clone()
};
response = response.with_header("Access-Control-Allow-Origin", origin);
response = response.with_header(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, PATCH, OPTIONS",
);
response = response.with_header(
"Access-Control-Allow-Headers",
"Content-Type, Authorization",
);
response = response.with_header("Access-Control-Max-Age", "86400");
}
return Ok(response);
}
let mut response = next.run(req).await?;
if !origins.is_empty() {
let origin = if origins.contains(&"*".to_string()) {
"*".to_string()
} else {
origins[0].clone()
};
response = response.with_header("Access-Control-Allow-Origin", origin);
response = response.with_header("Access-Control-Allow-Credentials", "true");
}
Ok(response)
})
}
}