use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse}; use crate::{config::DynNode, error::ServiceError}; use futures::Future; use hyper::header::HOST; use serde::Deserialize; use serde_yml::Value; use std::{collections::HashMap, pin::Pin, sync::Arc}; #[derive(Deserialize)] #[serde(transparent)] struct Hosts(HashMap); pub struct HostsKind; impl NodeKind for HostsKind { fn name(&self) -> &'static str { "hosts" } fn instanciate(&self, config: Value) -> anyhow::Result> { Ok(Arc::new(serde_yml::from_value::(config)?)) } } impl Node for Hosts { fn handle<'a>( &'a self, context: &'a mut NodeContext, request: NodeRequest, ) -> Pin> + Send + Sync + 'a>> { Box::pin(async move { let host = request .headers() .get(HOST) .and_then(|e| e.to_str().ok()) .map(remove_port); let node = match host { Some(host) => self .0 .get(host) .or_else(|| self.0.get(":fallback")) .ok_or(ServiceError::UnknownHost)?, None => self.0.get(":none").ok_or(ServiceError::NoHost)?, }; node.handle(context, request).await }) } } pub fn remove_port(s: &str) -> &str { s.split_once(":").map(|(s, _)| s).unwrap_or(s) }