use serve_net::{Envelope, HttpReply, HttpRequest, HttpService, Outcome};
use crate::{App, Gate, Open, Served};
pub struct Sites<G: Gate = Open> {
apps: Vec<App<G>>,
}
impl<G: Gate> Default for Sites<G> {
fn default() -> Self {
Self { apps: Vec::new() }
}
}
impl<G: Gate> Sites<G> {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn site(mut self, app: App<G>) -> Self {
self.apps.push(app);
self
}
#[must_use]
pub fn pick(&self, req: &HttpRequest<'_>) -> Option<&App<G>> {
let authority = req.head().authority();
let path = req.path();
self.apps
.iter()
.filter_map(|a| a.claim(authority, path).map(|score| (score, a)))
.max_by_key(|(score, _)| *score)
.map(|(_, a)| a)
.or_else(|| self.apps.first())
}
pub fn serve(
&self,
req: &HttpRequest<'_>,
env: &Envelope,
who: Option<&G::Identity>,
reply: &mut HttpReply,
) -> Served {
match self.pick(req) {
Some(app) => app.serve(req, env, who, reply),
None => {
reply.text(421, "no site is configured");
Served {
outcome: Outcome::Done,
route: None,
cache: crate::Cacheable::No,
}
}
}
}
}
impl<G: Gate> HttpService for Sites<G> {
fn call(&self, request: &HttpRequest<'_>, env: &Envelope, reply: &mut HttpReply) -> Outcome {
self.serve(request, env, None, reply).outcome
}
}
impl<G: Gate> std::fmt::Debug for Sites<G> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Sites").field("apps", &self.apps).finish()
}
}