use serde::Deserialize; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::{Path, PathBuf}; use structopt::StructOpt; use warp::Filter; #[macro_use] extern crate log; #[derive(Debug, Deserialize)] struct Config { webroot: PathBuf, listen: String, tls_key: PathBuf, tls_cert: PathBuf, } #[derive(Debug, StructOpt)] #[structopt(name = "ewww", about = "Web server for static sites")] struct Opt { #[structopt(parse(from_os_str))] config: PathBuf, } #[derive(Debug, thiserror::Error)] #[allow(clippy::enum_variant_names)] enum EwwwError { #[error("Web root {0} does not exist")] WebrootNotFound(PathBuf), #[error("TLS certificate {0} does not exist")] TlsCertNotFound(PathBuf), #[error("TLS key {0} does not exist")] TlsKeyNotFound(PathBuf), } #[tokio::main] async fn main() -> anyhow::Result<()> { pretty_env_logger::init(); info!("ewww starting up"); let opt = Opt::from_args(); let config = read_config(&opt.config).unwrap(); let webroot = config.webroot.canonicalize().unwrap(); let addr: SocketAddr = config.listen.to_socket_addrs().unwrap().next().unwrap(); info!("webroot: {:?}", webroot); info!("listen on {}", addr); let log = warp::log("ewww"); let webroot = warp::any().and(warp::fs::dir(webroot)).with(log); warp::serve(webroot) .tls() .key_path(config.tls_key) .cert_path(config.tls_cert) .run(addr) .await; Ok(()) } fn read_config(filename: &Path) -> anyhow::Result { debug!("reading config from {:?}", filename); let config = std::fs::read_to_string(filename)?; let config: Config = serde_yaml::from_str(&config)?; check_config(&config)?; info!("config: {:?}", config); Ok(config) } fn check_config(config: &Config) -> anyhow::Result<()> { if !config.webroot.exists() { return Err(EwwwError::WebrootNotFound(config.webroot.clone()).into()); } if !config.tls_cert.exists() { return Err(EwwwError::TlsCertNotFound(config.tls_cert.clone()).into()); } if !config.tls_key.exists() { return Err(EwwwError::TlsKeyNotFound(config.tls_key.clone()).into()); } Ok(()) }