From 5b11bb75dde7fc7ee25056f6b5c9f3e071f3b980 Mon Sep 17 00:00:00 2001 From: sigoden Date: Fri, 17 Jun 2022 08:41:01 +0800 Subject: [PATCH] feat: add option --render-try-index (#47) close #46 --- README.md | 13 +++++++------ src/args.rs | 46 +++++++++++++++++++++++++++------------------- src/server.rs | 8 +++++--- tests/fixtures.rs | 8 ++++++-- tests/render.rs | 31 ++++++++++++++++++++++++++----- 5 files changed, 71 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 481d853..23362dc 100644 --- a/README.md +++ b/README.md @@ -49,21 +49,22 @@ ARGS: Path to a root directory for serving files [default: .] OPTIONS: + -b, --bind ... Specify bind address + -p, --port Specify port to listen on [default: 5000] + --path-prefix Specify an url path prefix -a, --auth Use HTTP authentication --no-auth-access Not required auth when access static files -A, --allow-all Allow all operations + --allow-upload Allow upload files/folders --allow-delete Allow delete files/folders --allow-symlink Allow symlink to files/folders outside root directory - --allow-upload Allow upload files/folders - -b, --bind
... Specify bind address - --cors Enable CORS, sets `Access-Control-Allow-Origin: *` - -h, --help Print help information - -p, --port Specify port to listen on [default: 5000] - --path-prefix Specify an url path prefix --render-index Render index.html when requesting a directory + --render-try-index Try rendering index.html when requesting a directory --render-spa Render for single-page application + --cors Enable CORS, sets `Access-Control-Allow-Origin: *` --tls-cert Path to an SSL/TLS certificate to serve with HTTPS --tls-key Path to the SSL/TLS certificate's private key + -h, --help Print help information -V, --version Print version information ``` diff --git a/src/args.rs b/src/args.rs index 17059a8..2701540 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,4 +1,4 @@ -use clap::{Arg, ArgMatches, Command}; +use clap::{AppSettings, Arg, ArgMatches, Command}; use rustls::{Certificate, PrivateKey}; use std::env; use std::net::IpAddr; @@ -17,14 +17,15 @@ fn app() -> Command<'static> { " - ", env!("CARGO_PKG_REPOSITORY") )) + .global_setting(AppSettings::DeriveDisplayOrder) .arg( - Arg::new("address") + Arg::new("bind") .short('b') .long("bind") .help("Specify bind address") .multiple_values(true) .multiple_occurrences(true) - .value_name("address"), + .value_name("addr"), ) .arg( Arg::new("port") @@ -46,6 +47,18 @@ fn app() -> Command<'static> { .value_name("path") .help("Specify an url path prefix"), ) + .arg( + Arg::new("auth") + .short('a') + .long("auth") + .help("Use HTTP authentication") + .value_name("user:pass"), + ) + .arg( + Arg::new("no-auth-access") + .long("no-auth-access") + .help("Not required auth when access static files"), + ) .arg( Arg::new("allow-all") .short('A') @@ -72,25 +85,16 @@ fn app() -> Command<'static> { .long("render-index") .help("Render index.html when requesting a directory"), ) + .arg( + Arg::new("render-try-index") + .long("render-try-index") + .help("Try rendering index.html when requesting a directory"), + ) .arg( Arg::new("render-spa") .long("render-spa") .help("Render for single-page application"), ) - .arg( - Arg::new("auth") - .short('a') - .display_order(1) - .long("auth") - .help("Use HTTP authentication") - .value_name("user:pass"), - ) - .arg( - Arg::new("no-auth-access") - .display_order(1) - .long("no-auth-access") - .help("Not required auth when access static files"), - ) .arg( Arg::new("cors") .long("cors") @@ -128,6 +132,7 @@ pub struct Args { pub allow_symlink: bool, pub render_index: bool, pub render_spa: bool, + pub render_index_fallback: bool, pub cors: bool, pub tls: Option<(Vec, PrivateKey)>, } @@ -140,7 +145,7 @@ impl Args { pub fn parse(matches: ArgMatches) -> BoxResult { let port = matches.value_of_t::("port")?; let addrs = matches - .values_of("address") + .values_of("bind") .map(|v| v.collect()) .unwrap_or_else(|| vec!["0.0.0.0", "::"]); let addrs: Vec = Args::parse_addrs(&addrs)?; @@ -163,7 +168,9 @@ impl Args { let allow_upload = matches.is_present("allow-all") || matches.is_present("allow-upload"); let allow_delete = matches.is_present("allow-all") || matches.is_present("allow-delete"); let allow_symlink = matches.is_present("allow-all") || matches.is_present("allow-symlink"); - let render_index = matches.is_present("render-index"); + let render_index = + matches.is_present("render-index") || matches.is_present("render-try-index"); + let render_index_fallback = matches.is_present("render-try-index"); let render_spa = matches.is_present("render-spa"); let tls = match (matches.value_of("tls-cert"), matches.value_of("tls-key")) { (Some(certs_file), Some(key_file)) => { @@ -187,6 +194,7 @@ impl Args { allow_upload, allow_symlink, render_index, + render_index_fallback, render_spa, tls, }) diff --git a/src/server.rs b/src/server.rs index 23b899b..18503a8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -366,15 +366,17 @@ impl Server { head_only: bool, res: &mut Response, ) -> BoxResult<()> { - let path = path.join(INDEX_NAME); - if fs::metadata(&path) + let index_path = path.join(INDEX_NAME); + if fs::metadata(&index_path) .await .ok() .map(|v| v.is_file()) .unwrap_or_default() { - self.handle_send_file(&path, headers, head_only, res) + self.handle_send_file(&index_path, headers, head_only, res) .await?; + } else if self.args.render_index_fallback { + self.handle_ls_dir(path, true, head_only, res).await?; } else { status_not_found(res) } diff --git a/tests/fixtures.rs b/tests/fixtures.rs index 2641ddf..d581be3 100644 --- a/tests/fixtures.rs +++ b/tests/fixtures.rs @@ -29,7 +29,11 @@ pub static FILES: &[&str] = &[ "foo\\bar.test", ]; -/// Directory names for testing purpose +/// Directory names for testing diretory don't exist +#[allow(dead_code)] +pub static DIR_NO_FOUND: &str = "dir-no-found/"; + +/// Directory names for testing diretory don't have index.html #[allow(dead_code)] pub static DIR_NO_INDEX: &str = "dir-no-index/"; @@ -55,7 +59,7 @@ pub fn tmpdir() -> TempDir { } for directory in DIRECTORIES { for file in FILES { - if *directory == DIR_NO_INDEX { + if *directory == DIR_NO_INDEX && *file == "index.html" { continue; } tmpdir diff --git a/tests/render.rs b/tests/render.rs index e666265..70f041b 100644 --- a/tests/render.rs +++ b/tests/render.rs @@ -1,6 +1,7 @@ mod fixtures; +mod utils; -use fixtures::{server, Error, TestServer, DIR_NO_INDEX}; +use fixtures::{server, Error, TestServer, DIR_NO_FOUND, DIR_NO_INDEX}; use rstest::rstest; #[rstest] @@ -12,12 +13,32 @@ fn render_index(#[with(&["--render-index"])] server: TestServer) -> Result<(), E } #[rstest] -fn render_index_404(#[with(&["--render-index"])] server: TestServer) -> Result<(), Error> { - let resp = reqwest::blocking::get(format!("{}/{}", server.url(), DIR_NO_INDEX))?; +fn render_index2(#[with(&["--render-index"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}{}", server.url(), DIR_NO_INDEX))?; assert_eq!(resp.status(), 404); Ok(()) } +#[rstest] +fn render_try_index(#[with(&["--render-try-index"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(server.url())?; + let text = resp.text()?; + assert_eq!(text, "This is index.html"); + Ok(()) +} + +#[rstest] +fn render_try_index2(#[with(&["--render-try-index"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}{}", server.url(), DIR_NO_INDEX))?; + let files: Vec<&str> = self::fixtures::FILES + .iter() + .filter(|v| **v != "index.html") + .cloned() + .collect(); + assert_index_resp!(resp, files); + Ok(()) +} + #[rstest] fn render_spa(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error> { let resp = reqwest::blocking::get(server.url())?; @@ -27,8 +48,8 @@ fn render_spa(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error } #[rstest] -fn render_spa_no_404(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error> { - let resp = reqwest::blocking::get(format!("{}/{}", server.url(), DIR_NO_INDEX))?; +fn render_spa2(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}{}", server.url(), DIR_NO_FOUND))?; let text = resp.text()?; assert_eq!(text, "This is index.html"); Ok(())