From a389db173b8b6f38c8330ddecf00023c72c8ee86 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 25 Mar 2021 21:06:08 -0700 Subject: Generate completions with `miniserve --print-completions ` This patch adds a `--print-completions` option to generate shell completion files at runtime. This ensures the completions are always up to date. Fixes #377. --- src/main.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 17ab204..747a705 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::thread; use std::time::Duration; use structopt::clap::crate_version; +use structopt::StructOpt; use yansi::{Color, Paint}; mod archive; @@ -101,20 +102,27 @@ pub struct MiniserveConfig { } fn main() { - match run() { + let args = args::CliArgs::from_args(); + + if let Some(shell) = args.print_completions { + args::CliArgs::clap().gen_completions_to("miniserve", shell, &mut std::io::stdout()); + return; + } + + let miniserve_config = args::parse_args(args); + + match run(miniserve_config) { Ok(()) => (), Err(e) => errors::log_error_chain(e.to_string()), } } #[actix_web::main(miniserve)] -async fn run() -> Result<(), ContextualError> { +async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { if cfg!(windows) && !Paint::enable_windows_ascii() { Paint::disable(); } - let miniserve_config = args::parse_args(); - let log_level = if miniserve_config.verbose { simplelog::LevelFilter::Info } else { -- cgit v1.2.3 From 26b0a519d226f555674db481f3a3cd2fc52a9961 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Sun, 28 Mar 2021 21:16:39 +0200 Subject: Refactor and separate out arg handling and config handling --- src/main.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 5 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 747a705..a6410a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,9 @@ +use std::io; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::thread; +use std::time::Duration; +use std::{io::Write, path::PathBuf}; + use actix_web::web; use actix_web::{ http::{header::ContentType, StatusCode}, @@ -6,10 +12,6 @@ use actix_web::{ use actix_web::{middleware, App, HttpRequest, HttpResponse}; use actix_web_httpauth::middleware::HttpAuthentication; use http::header::HeaderMap; -use std::io::{self, Write}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::thread; -use std::time::Duration; use structopt::clap::crate_version; use structopt::StructOpt; use yansi::{Color, Paint}; @@ -25,6 +27,11 @@ mod renderer; use crate::errors::ContextualError; +/// Possible characters for random routes +const ROUTE_ALPHABET: [char; 16] = [ + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', +]; + #[derive(Clone)] /// Configuration of the Miniserve application pub struct MiniserveConfig { @@ -101,6 +108,67 @@ pub struct MiniserveConfig { pub hide_version_footer: bool, } +impl MiniserveConfig { + /// Parses the command line arguments + fn from_args(args: args::CliArgs) -> Self { + let interfaces = if !args.interfaces.is_empty() { + args.interfaces + } else { + vec![ + IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + ] + }; + + let random_route = if args.random_route { + Some(nanoid::nanoid!(6, &ROUTE_ALPHABET)) + } else { + None + }; + + // Generate some random routes for the favicon and css so that they are very unlikely to conflict with + // real files. + let favicon_route = nanoid::nanoid!(10, &ROUTE_ALPHABET); + let css_route = nanoid::nanoid!(10, &ROUTE_ALPHABET); + + let default_color_scheme = args.color_scheme; + let default_color_scheme_dark = args.color_scheme_dark; + + let path_explicitly_chosen = args.path.is_some() || args.index.is_some(); + + let port = match args.port { + 0 => port_check::free_local_port().expect("no free ports available"), + _ => args.port, + }; + + crate::MiniserveConfig { + verbose: args.verbose, + path: args.path.unwrap_or_else(|| PathBuf::from(".")), + port, + interfaces, + auth: args.auth, + path_explicitly_chosen, + no_symlinks: args.no_symlinks, + show_hidden: args.hidden, + random_route, + favicon_route, + css_route, + default_color_scheme, + default_color_scheme_dark, + index: args.index, + overwrite_files: args.overwrite_files, + show_qrcode: args.qrcode, + file_upload: args.file_upload, + tar_enabled: args.enable_tar, + zip_enabled: args.enable_zip, + dirs_first: args.dirs_first, + title: args.title, + header: args.header, + hide_version_footer: args.hide_version_footer, + } + } +} + fn main() { let args = args::CliArgs::from_args(); @@ -109,7 +177,7 @@ fn main() { return; } - let miniserve_config = args::parse_args(args); + let miniserve_config = MiniserveConfig::from_args(args); match run(miniserve_config) { Ok(()) => (), -- cgit v1.2.3 From dfe2b2460c33794ca91cf614cd4ead1382a1199a Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Sun, 28 Mar 2021 21:28:20 +0200 Subject: Better message for when provided index file doesn't exist --- src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index a6410a6..8f29dc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use actix_web::{ use actix_web::{middleware, App, HttpRequest, HttpResponse}; use actix_web_httpauth::middleware::HttpAuthentication; use http::header::HeaderMap; +use log::error; use structopt::clap::crate_version; use structopt::StructOpt; use yansi::{Color, Paint}; @@ -251,9 +252,9 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { if let Some(index_path) = &miniserve_config.index { let has_index: std::path::PathBuf = [&canon_path, index_path].iter().collect(); if !has_index.exists() { - println!( - "{warning} The provided index file could not be found.", - warning = Color::RGB(255, 192, 0).paint("Notice:").bold() + error!( + "The file '{}' provided for option --index could not be found.", + index_path.to_string_lossy() ); } } -- cgit v1.2.3 From 31b5b9faf3cacb68d89fb2509368e472636a9cb4 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Sun, 28 Mar 2021 21:30:29 +0200 Subject: Change default log level to Warn --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 8f29dc6..1b896da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -195,7 +195,7 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { let log_level = if miniserve_config.verbose { simplelog::LevelFilter::Info } else { - simplelog::LevelFilter::Error + simplelog::LevelFilter::Warn }; if simplelog::TermLogger::init( -- cgit v1.2.3 From 3c5a2de4308975af2ba3a831b638705c424fd2f8 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Sun, 28 Mar 2021 21:46:06 +0200 Subject: Change start message without arguments to be a bit more clear --- src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 1b896da..5fadb07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use actix_web::{ use actix_web::{middleware, App, HttpRequest, HttpResponse}; use actix_web_httpauth::middleware::HttpAuthentication; use http::header::HeaderMap; -use log::error; +use log::{error, warn}; use structopt::clap::crate_version; use structopt::StructOpt; use yansi::{Color, Paint}; @@ -266,9 +266,9 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { version = crate_version!() ); if !miniserve_config.path_explicitly_chosen { - println!("{warning} miniserve has been invoked without an explicit path so it will serve the current directory.", warning=Color::RGB(255, 192, 0).paint("Notice:").bold()); - println!( - " Invoke with -h|--help to see options or invoke as `miniserve .` to hide this advice." + warn!("miniserve has been invoked without an explicit path so it will serve the current directory after a short delay."); + warn!( + "Invoke with -h|--help to see options or invoke as `miniserve .` to hide this advice." ); print!("Starting server in "); io::stdout() -- cgit v1.2.3 From 906af1587144dd4b3caecacdff5ea834012cffa4 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Sun, 28 Mar 2021 22:41:07 +0200 Subject: Refuse to start without explicit path if not attached to interactive terminal --- src/main.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 5fadb07..7cd4d3e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -220,8 +220,8 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .is_symlink(); if is_symlink { - return Err(ContextualError::from( - "The no-symlinks option cannot be used with a symlink path".to_string(), + return Err(ContextualError::NoSymlinksOptionWithSymlinkServePath( + miniserve_config.path.to_string_lossy().to_string(), )); } } @@ -266,6 +266,14 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { version = crate_version!() ); if !miniserve_config.path_explicitly_chosen { + // If the path to serve has NOT been explicitly chosen and if this is NOT an interactive + // terminal, we should refuse to start for security reasons. This would be the case when + // running miniserve as a service but forgetting to set the path. This could be pretty + // dangerous if given with an undesired context path (for instance /root or /). + if !atty::is(atty::Stream::Stdout) { + return Err(ContextualError::NoExplicitPathAndNoTerminal); + } + warn!("miniserve has been invoked without an explicit path so it will serve the current directory after a short delay."); warn!( "Invoke with -h|--help to see options or invoke as `miniserve .` to hide this advice." @@ -361,7 +369,9 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { addresses = addresses, ); - println!("\nQuit by pressing CTRL-C"); + if atty::is(atty::Stream::Stdout) { + println!("\nQuit by pressing CTRL-C"); + } srv.await .map_err(|e| ContextualError::IoError("".to_owned(), e)) -- cgit v1.2.3 From 9b278a471a7c9f0587ee421335a2f675afe90eb7 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Sun, 28 Mar 2021 22:46:32 +0200 Subject: Bump deps --- src/main.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 7cd4d3e..84a4cb8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -202,6 +202,7 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { log_level, simplelog::Config::default(), simplelog::TerminalMode::Mixed, + simplelog::ColorChoice::Auto, ) .is_err() { -- cgit v1.2.3 From 2bae301ed8efcf4239849a45b94cdc42e398b905 Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sun, 11 Apr 2021 11:00:16 +0800 Subject: Separate tar archive and tar flags It used to have one flag (-r) to enable both tar archive and tar. Now it has two flags [ -r: for tar, -g: for tar archive]. Related to #451 --- src/main.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 84a4cb8..a00ac3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,9 +90,12 @@ pub struct MiniserveConfig { /// Enable upload to override existing files pub overwrite_files: bool, - /// If false, creation of tar archives is disabled + /// If false, creation of tar is disabled pub tar_enabled: bool, + /// If false, creation of tar archives is disabled + pub tar_archive_enabled: bool, + /// If false, creation of zip archives is disabled pub zip_enabled: bool, @@ -161,6 +164,7 @@ impl MiniserveConfig { show_qrcode: args.qrcode, file_upload: args.file_upload, tar_enabled: args.enable_tar, + tar_archive_enabled: args.enable_tar_archive, zip_enabled: args.enable_zip, dirs_first: args.dirs_first, title: args.title, @@ -411,6 +415,7 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) { let show_qrcode = conf.show_qrcode; let file_upload = conf.file_upload; let tar_enabled = conf.tar_enabled; + let tar_archive_enabled = conf.tar_archive_enabled; let zip_enabled = conf.zip_enabled; let dirs_first = conf.dirs_first; let hide_version_footer = conf.hide_version_footer; @@ -453,6 +458,7 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) { show_qrcode, u_r.clone(), tar_enabled, + tar_archive_enabled, zip_enabled, dirs_first, hide_version_footer, -- cgit v1.2.3 From c9506c72ff368867982d138a686648fa302b116d Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sun, 18 Apr 2021 11:22:52 +0800 Subject: Change naming of uncompressed/compressed tarballs Use following terminology: uncompressed tarballs => `uncompressed tar archives` compressed ones => `gz-compressed tar archives` --- src/main.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index a00ac3c..f174d57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,11 +90,11 @@ pub struct MiniserveConfig { /// Enable upload to override existing files pub overwrite_files: bool, - /// If false, creation of tar is disabled + /// If false, creation of uncompressed tar archives is disabled pub tar_enabled: bool, - /// If false, creation of tar archives is disabled - pub tar_archive_enabled: bool, + /// If false, creation of gz-compressed tar archives is disabled + pub tar_gz_enabled: bool, /// If false, creation of zip archives is disabled pub zip_enabled: bool, @@ -164,7 +164,7 @@ impl MiniserveConfig { show_qrcode: args.qrcode, file_upload: args.file_upload, tar_enabled: args.enable_tar, - tar_archive_enabled: args.enable_tar_archive, + tar_gz_enabled: args.enable_tar_gz, zip_enabled: args.enable_zip, dirs_first: args.dirs_first, title: args.title, @@ -415,7 +415,7 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) { let show_qrcode = conf.show_qrcode; let file_upload = conf.file_upload; let tar_enabled = conf.tar_enabled; - let tar_archive_enabled = conf.tar_archive_enabled; + let tar_gz_enabled = conf.tar_gz_enabled; let zip_enabled = conf.zip_enabled; let dirs_first = conf.dirs_first; let hide_version_footer = conf.hide_version_footer; @@ -458,7 +458,7 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) { show_qrcode, u_r.clone(), tar_enabled, - tar_archive_enabled, + tar_gz_enabled, zip_enabled, dirs_first, hide_version_footer, -- cgit v1.2.3