use rustls::{ internal::pemfile::{certs, pkcs8_private_keys}, AllowAnyAnonymousOrAuthenticatedClient, Certificate, PrivateKey, RootCertStore, ServerConfig, }; use std::{ fs::File, io::{self, BufReader}, path::{Path, PathBuf}, sync::Arc, thread, }; use structopt::StructOpt; mod http; mod server; #[derive(StructOpt, Debug)] struct Options { /// host to listen on #[structopt(short = "H", long, env = "HOST", default_value = "0.0.0.0")] host: String, /// port to listen on #[structopt(short = "p", long, env = "PORT", default_value = "1965")] port: u16, /// cert file #[structopt(short = "c", long = "cert", env = "CERT_FILE")] cert: PathBuf, /// key file #[structopt(short = "k", long = "key", env = "KEY_FILE")] key: PathBuf, /// static path #[structopt(short = "s", long, env = "STATIC_PATH", default_value = "./static")] static_path: PathBuf, /// CGI path #[structopt(short = "C", long, env = "CGI_PATH", default_value = "./cgi-bin")] cgi_path: PathBuf, /// server hostname #[structopt( long = "hostname", env = "SERVER_HOSTNAME", default_value = "cetacean.club" )] hostname: String, /// HTTP port #[structopt(long, default_value = "34587")] http_port: u16, } fn load_certs(path: &Path) -> io::Result> { certs(&mut BufReader::new(File::open(path)?)) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert")) } fn load_keys(path: &Path) -> io::Result> { pkcs8_private_keys(&mut BufReader::new(File::open(path)?)) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key")) } #[tokio::main] async fn main() -> Result<(), maj::server::Error> { env_logger::init(); let opts = Options::from_args(); let certs = load_certs(&opts.cert).unwrap(); let mut keys = load_keys(&opts.key).unwrap(); log::info!( "serving gemini://{} on {}:{}", opts.hostname, opts.host, opts.port ); let mut config = ServerConfig::new(AllowAnyAnonymousOrAuthenticatedClient::new( RootCertStore::empty(), )); config .set_single_cert(certs, keys.remove(0)) .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; let h = Arc::new(server::Handler { hostname: opts.hostname, files: maj::server::files::Handler::new(opts.static_path), cgi: maj::server::cgi::Handler::new(opts.cgi_path), }); { let port = opts.http_port.clone(); let h = h.clone(); thread::spawn(move || http::run(h.clone(), port)); } maj::server::serve(h.clone(), config, opts.host, opts.port).await?; Ok(()) } include!(concat!(env!("OUT_DIR"), "/templates.rs"));