use std::collections::HashMap; use std::convert::Infallible; use std::fs::read_to_string; use std::net::SocketAddr; use std::path::PathBuf; use std::sync::Arc; use hyper::service::{make_service_fn, service_fn}; use hyper::{header, Body, Request, Response, Server, StatusCode}; use serde::Deserialize; use structopt::StructOpt; mod utils; use utils::HttpHandler; #[derive(Debug, StructOpt)] struct Opt { #[structopt(long, parse(from_os_str), default_value = "narchttpd.toml")] config_file: PathBuf, } #[derive(Deserialize)] #[serde(tag = "mode", rename_all = "kebab-case")] enum DomainConfig { Static { root: PathBuf, }, ProxyChild { command: String, in_dir: Option, port: u16, }, } impl DomainConfig { fn handler(self) -> Box { match self { Self::Static { root } => Box::new(utils::serve_static::Params::new(root)), Self::ProxyChild { command, in_dir, port, } => Box::new(utils::proxy_child::ProxyChild::new(command, in_dir, port)), } } } #[derive(Deserialize)] struct Config { http_ports: Vec, https_ports: Vec, #[serde(flatten)] domains: HashMap, } #[tokio::main] async fn main() { let opt = Opt::from_args(); let config_data = read_to_string(&opt.config_file).expect("Config file not found"); let Config { http_ports, https_ports, domains, } = toml::from_str(&config_data).expect("Config file not valid"); assert!(https_ports.is_empty(), "HTTPS is complicated oops"); let domains: HashMap<_, _> = domains .into_iter() .map(|(domain, config)| (domain, Arc::new(config.handler()))) .collect(); let do_response_domains = domains.clone(); // TODO learn hyper let do_response = move |req: Request| async move { for (domain, handler) in do_response_domains.clone() { let req_domain = req.headers().get(header::HOST).unwrap(); if &domain == req_domain { eprintln!("request {:?} matched domain {}", req, domain); // matched! return handler.handle(req).await; } } Response::builder() .status(StatusCode::NOT_FOUND) .body(Body::from("Not Found")) .unwrap() }; let addr = http_ports.into_iter().flat_map(|port| { ["127.0.0.1", "::1"] .iter() .map(move |ip| SocketAddr::new(ip.parse().unwrap(), port)) }); let make_svc = make_service_fn(move |_conn| async move { Ok::<_, Infallible>(service_fn(move |req| async move { let response = do_response(req).await; Ok::<_, Infallible>(response) })) }); // TODO uhh let mut addr = addr; let addr = addr.nth(0).unwrap(); let server = Server::bind(&addr).serve(make_svc); if let Err(e) = server.await { eprintln!("server error: {}", e); } }