diff options
Diffstat (limited to '')
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | .travis.yml | 62 | ||||
-rw-r--r-- | Cargo.toml | 29 | ||||
-rw-r--r-- | README.md | 10 | ||||
-rw-r--r-- | index.html | 1 | ||||
-rw-r--r-- | src/main.rs | 131 |
6 files changed, 234 insertions, 2 deletions
@@ -8,3 +8,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +/target +**/*.rs.bk diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6ac6c9b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,62 @@ +language: rust +cache: cargo + +rust: + - stable + - beta + - nightly + +addons: + apt: + packages: + - mingw-w64 + - upx + +matrix: + include: + - env: + - TARGET=x86_64-unknown-linux-musl + - BIN_NAME=miniserve + - PROPER_NAME=miniserve-linux + os: linux + - env: + - TARGET=x86_64-pc-windows-gnu + - BIN_NAME=miniserve.exe + - PROPER_NAME=miniserve-win.exe + - RUSTFLAGS="-C linker=x86_64-w64-mingw32-gcc" + os: linux + - env: + - TARGET=x86_64-apple-darwin + - BIN_NAME=miniserve + - PROPER_NAME=miniserve-osx + os: osx + +before_install: + - rustup self update + +install: + # On Apple, the default target is already the right one. + - if [[ -n $TARGET && $TARGET != "x86_64-apple-darwin" ]]; then rustup target add $TARGET; fi + +script: + # If this is a normal, non-deployment build... + - if [[ -z $TARGET ]]; then cargo build --verbose; fi + - if [[ -n $TARGET ]]; then cargo build --verbose --release --target $TARGET; fi + +before_deploy: + # If this is a binary deployment... + - if [[ -n $TARGET ]]; then cp -a target/$TARGET/release/$BIN_NAME $PROPER_NAME && strip $PROPER_NAME; fi + - # Run upx on the binary if this is a deployment for Linux or Windows + - if [[ $TARGET = "x86_64-pc-windows-gnu" ]]; then upx $PROPER_NAME; fi + - if [[ $TARGET = "x86_64-unknown-linux-musl" ]]; then upx $PROPER_NAME; fi + +deploy: + - provider: releases + api_key: + secure: "n1OCiCjupkVieqsepGBOk6oxGcU89n9z0lQWWMAtBKREIEL1fnqt7A4z9FC1LV9UiAwvcvMHI8As5TA/7sV6jiWFuNOqu5HurX/n7cXlAuVWx00gCtWZ7Fh14ipVIDr2W52Mj/5qw1LSW19g8+9nLF1xO2YLrNpiaQX+V6Ypf04=" + file: $PROPER_NAME + skip_cleanup: true + on: + branch: master + tags: true + condition: $TRAVIS_RUST_VERSION = stable && -n $TARGET diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d1ed422 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "miniserve" +version = "0.1.0" +description = "For when you really just want to serve some files over HTTP right now!" +authors = ["Sven-Hendrik Haase <svenstaro@gmail.com>"] +repository = "https://github.com/svenstaro/miniserve" +license = "MIT" +readme = "README.md" +keywords = ["serve", "http-server", "static-files"] +categories = ["command-line-utilities", "command-line-interface"] + +[badges] +travis-ci = { repository = "svenstaro/miniserve", branch = "master" } +maintenance = { status = "actively-developed" } + +[dependencies] +pbr = "1.0" +yansi = "0.4" +rand = "0.4" +chrono = "0.4" +url = "1.6" +lazy_static = "1.0" +humantime = "1.1" +regex = "0.2.5" +clap = "2.29" +ctrlc = "3.1" +actix = "0.5" +actix-web = { git = "https://github.com/actix/actix-web.git" } +simplelog = "0.5" @@ -1,2 +1,8 @@ -# servethis -:star2: For when you really just want to serve some files over HTTP right now! +# miniserve - For when you really just want to serve some files over HTTP right now! + +[](https://travis-ci.org/svenstaro/miniserve) +[](https://aur.archlinux.org/packages/miniserve/) +[](https://crates.io/crates/miniserve) +[](https://deps.rs/repo/github/svenstaro/miniserve) +[](https://github.com/svenstaro/miniserve/blob/master/LICENSE) + diff --git a/index.html b/index.html new file mode 100644 index 0000000..ced54c9 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ +rofl 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::<u16>().and(Ok(())).or_else(|e| Err(e.to_string())) +} + +fn is_valid_interface(interface: String) -> Result<(), String> { + interface.parse::<IpAddr>().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<MiniserveConfig>) -> Result<fs::NamedFile> { + let path = &req.state().path; + Ok(fs::NamedFile::open(path)?) +} + +fn configure_app(app: App<MiniserveConfig>) -> App<MiniserveConfig> { + 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(); +} |