use std::future::Future;
use std::io;
use std::pin::Pin;
use http::StatusCode;
use super::ErrorPage;
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ListenerKind {
Local,
Share,
}
#[derive(Debug, Clone, Copy)]
pub struct RouteRequest<'a> {
pub host: &'a str,
pub tls: bool,
pub kind: ListenerKind,
pub method: &'a http::Method,
pub path: &'a str,
pub authorization: Option<&'a str>,
pub referrer: Option<&'a str>,
}
pub enum Route {
Upstream {
port: u16,
guard: Box<dyn ActivityGuard>,
},
File {
path: std::path::PathBuf,
guard: Box<dyn ActivityGuard>,
},
Error(ErrorPage),
}
pub trait ActivityGuard: Send + 'static {
fn long_lived(&mut self);
}
impl ActivityGuard for () {
fn long_lived(&mut self) {}
}
#[derive(Debug)]
pub enum UpstreamError {
Connect(io::Error),
Request(String),
NotHttp(String),
}
pub trait Router: Send + Sync + 'static {
fn route<'a>(&'a self, request: RouteRequest<'a>) -> BoxFuture<'a, Route>;
fn upstream_failed(
&self,
request: RouteRequest<'_>,
port: u16,
error: &UpstreamError,
) -> ErrorPage {
let _ = request;
match error {
UpstreamError::Connect(error) => ErrorPage::new(
StatusCode::BAD_GATEWAY,
"App not reachable",
format!("Nothing answered on port {port}: {error}"),
),
UpstreamError::Request(error) => ErrorPage::new(
StatusCode::BAD_GATEWAY,
"App failed to respond",
format!("The request to port {port} failed: {error}"),
),
UpstreamError::NotHttp(error) => ErrorPage::new(
StatusCode::BAD_GATEWAY,
"App sent an invalid response",
format!(
"The app on port {port} answered with something that isn't HTTP ({error}). \
Dev servers do this when they refuse a request, such as Next.js for a \
host that isn't in its allowedDevOrigins."
),
),
}
}
}