From 36e006f8df4714f41d1e51824d0264680c8bf40f Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Thu, 26 Apr 2018 19:32:25 +0200 Subject: Initial code checkin --- src/main.rs | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/main.rs (limited to 'src') diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..2e4a69f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,131 @@ +extern crate actix; +extern crate actix_web; +extern crate simplelog; + +#[macro_use] +extern crate clap; + +use actix_web::{server, App, fs, middleware, HttpRequest, HttpResponse, Result}; +use simplelog::{TermLogger, LevelFilter, Config}; +use std::path::PathBuf; +use std::net::IpAddr; + +#[derive(Clone)] +pub struct MiniserveConfig { + verbose: bool, + path: std::path::PathBuf, + port: u16, + interface: IpAddr, +} + +fn is_valid_path(path: String) -> Result<(), String> { + let path_to_check = PathBuf::from(path); + if path_to_check.is_file() || path_to_check.is_dir() { + return Ok(()); + } + Err(String::from("Path either doesn't exist or is not a regular file or a directory")) +} + +fn is_valid_port(port: String) -> Result<(), String> { + port.parse::().and(Ok(())).or_else(|e| Err(e.to_string())) +} + +fn is_valid_interface(interface: String) -> Result<(), String> { + interface.parse::().and(Ok(())).or_else(|e| Err(e.to_string())) +} + +pub fn parse_args() -> MiniserveConfig { + use clap::{App, Arg}; + + let matches = App::new(crate_name!()) + .version(crate_version!()) + .author(crate_authors!()) + .about(crate_description!()) + .arg( + Arg::with_name("verbose") + .short("v") + .long("verbose") + .help("Be verbose"), + ) + .arg( + Arg::with_name("PATH") + .required(true) + .validator(is_valid_path) + .help("Which path to serve"), + ) + .arg( + Arg::with_name("port") + .short("p") + .long("port") + .help("Port to use") + .validator(is_valid_port) + .required(false) + .default_value("8080") + .takes_value(true), + ) + .arg( + Arg::with_name("interface") + .short("i") + .long("if") + .help("Interface to listen on") + .validator(is_valid_interface) + .required(false) + .default_value("0.0.0.0") + .takes_value(true), + ) + .get_matches(); + + let verbose = matches.is_present("verbose"); + let path = matches.value_of("PATH").unwrap(); + let port = matches.value_of("port").unwrap().parse().unwrap(); + let interface = matches.value_of("interface").unwrap().parse().unwrap(); + + MiniserveConfig { + verbose, + path: PathBuf::from(path), + port, + interface, + } +} + +fn file_handler(req: HttpRequest) -> Result { + let path = &req.state().path; + Ok(fs::NamedFile::open(path)?) +} + +fn configure_app(app: App) -> App { + let s = { + let path = &app.state().path; + if path.is_file() { + None + } else { + Some(fs::StaticFiles::new(path).show_files_listing()) + } + }; + + if let Some(s) = s { + app.handler("/", s) + } else { + app.resource("/", |r| r.f(file_handler)) + } +} + +fn main() { + let miniserve_config = parse_args(); + + if miniserve_config.verbose { + let _ = TermLogger::init(LevelFilter::Info, Config::default()); + } + let sys = actix::System::new("miniserve"); + + let inside_config = miniserve_config.clone(); + server::new( + move || App::with_state(inside_config.clone()) + .middleware(middleware::Logger::default()) + .configure(configure_app)) + .bind(format!("{}:{}", &miniserve_config.interface, miniserve_config.port)).expect("Couldn't bind server") + .shutdown_timeout(1) + .start(); + + let _ = sys.run(); +} -- cgit v1.2.3