use std::{fs, time::Duration}; use std::net::TcpStream; use serde_yml::{Number, Value}; use wildmatch::WildMatch; use super::tls::TlsCertificate; #[derive(Clone, Debug)] pub struct SiteConfig { pub domain: WildMatch, pub host: String, pub ssl: Option, pub enable_keep_alive: bool, pub support_keep_alive: bool, pub ip_forwarding: IpForwarding, pub replace_host: Option } impl SiteConfig { pub fn connect(&self) -> Option { TcpStream::connect(self.host.clone()).ok() } } #[derive(Clone, Debug)] pub enum IpForwarding { Simple, Header(String), Modern, None } impl IpForwarding { pub fn from_name(name: &str) -> Option { match name { "none" => Some(IpForwarding::None), "simple" => Some(IpForwarding::Simple), "modern" => Some(IpForwarding::Modern), "header" => Some(IpForwarding::Header(String::from("X-Real-IP"))), name => if name.starts_with("header:") { Some(IpForwarding::Header(name[7..].to_string())) } else { None } } } } #[derive(Clone, Debug)] pub struct Config { pub sites: Vec, pub http_host: Option, pub https_host: Option, pub connection_timeout: Duration, pub incoming_ip_forwarding: IpForwarding, pub threadpool_size: usize } impl Config { pub fn parse(filename: &str) -> Option { let file_content = fs::read_to_string(filename).ok()?; let doc = serde_yml::from_str::(file_content.as_str()).ok()?; let http_host = doc.get("http_host").and_then(|o| Some(o.as_str()?.to_string())); let https_host = doc.get("https_host").and_then(|o| Some(o.as_str()?.to_string())); let threadpool_size = doc.get("threadpool_size").and_then(|o| Some(o.as_u64()? as usize + 2)).unwrap_or(12); let connection_timeout = Duration::from_secs(doc.get("connection_timeout") .unwrap_or(&Value::Number(Number::from(10))).as_u64()?); let incoming_ip_forwarding = doc.get("incoming_ip_forwarding") .map(|o| o.as_str()).flatten() .map(|o| IpForwarding::from_name(o)).flatten() .unwrap_or(IpForwarding::None); let mut sites: Vec = Vec::new(); let sites_yaml = doc["sites"].as_sequence()?; for s in sites_yaml { let mut cert: Option = None; let s = s.as_mapping()?; if s.contains_key("ssl_cert") { cert = Some( TlsCertificate::new( s.get("ssl_cert")?.as_str()?, s.get("ssl_key")?.as_str()?, )?, ); } let site = SiteConfig { domain: WildMatch::new(&s.get("domain")?.as_str()?.to_string()), host: s.get("host")?.as_str()?.to_string(), ssl: cert, enable_keep_alive: s.get("enable_keep_alive") .map(|o| o.as_bool().unwrap()) .unwrap_or(true), support_keep_alive: s.get("support_keep_alive") .map(|o| o.as_bool().unwrap()) .unwrap_or(true), ip_forwarding: s.get("ip_forwarding") .map(|o| o.as_str()).flatten() .map(|o| IpForwarding::from_name(o)).flatten() .unwrap_or(IpForwarding::Header("X-Real-IP".to_string())), replace_host: s.get("replace_host") .map(|o| o.as_str()).flatten().map(|o| o.to_string()), }; sites.push(site); } Some(Config { sites, http_host, https_host, connection_timeout, incoming_ip_forwarding, threadpool_size }.clone()) } pub fn get_site(&self, domain: &str) -> Option<&SiteConfig> { for i in &self.sites { if i.domain.matches(domain) { return Some(i); } } return None; } }