use super::async_proxy::ProxyRequest;
use super::rejection::{HANDLER, MIDDLEWARE, Rejected, RejectionScope};
use super::trie::{Handler, HandlerOutcome};
use super::{Request, Response};
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
pub type MiddlewareFuture = Pin<Box<dyn Future<Output = HandlerOutcome> + Send>>;
pub type ResponseFuture = Pin<Box<dyn Future<Output = Response> + Send>>;
pub type MiddlewareFn = Box<dyn Fn(&Request, Next) -> MiddlewareFuture + Send + Sync>;
pub(super) enum Terminal<'a> {
Handler(&'a Handler),
Rejected(Rejected),
Gate,
Proxy { backend: Arc<str>, prefix: Arc<str> },
}
impl Terminal<'_> {
fn run(self, req: &Request, scope: RejectionScope) -> ResponseFuture {
match self {
Self::Handler(handler) => {
let outcome = handler(req);
Box::pin(async move { scope.resolve(outcome.await, HANDLER) })
}
Self::Rejected(rejected) => Box::pin(async move { scope.map(rejected) }),
Self::Gate => Box::pin(async { Response::empty_raw(200).mark_gate() }),
Self::Proxy { backend, prefix } => Box::pin(forward_proxy(
ProxyRequest::from_request(req),
backend,
prefix,
scope,
)),
}
}
}
pub struct Next<'a> {
remaining: &'a [MiddlewareFn],
terminal: Terminal<'a>,
scope: RejectionScope,
}
impl<'a> Next<'a> {
pub(super) fn new(
remaining: &'a [MiddlewareFn],
terminal: Terminal<'a>,
scope: RejectionScope,
) -> Self {
Self {
remaining,
terminal,
scope,
}
}
pub fn call(self, req: &Request) -> ResponseFuture {
let Self {
remaining,
terminal,
scope,
} = self;
match remaining.split_first() {
Some((frame, rest)) => {
let next = Self {
remaining: rest,
terminal,
scope: scope.clone(),
};
let entered = frame(req, next);
Box::pin(async move { scope.resolve(entered.await, MIDDLEWARE) })
}
None => terminal.run(req, scope),
}
}
}
async fn forward_proxy(
proxy_req: ProxyRequest,
backend: Arc<str>,
prefix: Arc<str>,
scope: RejectionScope,
) -> Response {
match super::async_proxy::forward_request_buffered(proxy_req, &backend, &prefix).await {
Ok(resp) => resp,
Err(failure) => scope.map(Rejected::from_proxy_failure(failure)),
}
}