<path> Path to a root directory for serving files [default: .]
OPTIONS:
+ -b, --bind <addr>... Specify bind address
+ -p, --port <port> Specify port to listen on [default: 5000]
+ --path-prefix <path> Specify an url path prefix
-a, --auth <user:pass> 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 <address>... Specify bind address
- --cors Enable CORS, sets `Access-Control-Allow-Origin: *`
- -h, --help Print help information
- -p, --port <port> Specify port to listen on [default: 5000]
- --path-prefix <path> 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> Path to an SSL/TLS certificate to serve with HTTPS
--tls-key <path> Path to the SSL/TLS certificate's private key
+ -h, --help Print help information
-V, --version Print version information
```
-use clap::{Arg, ArgMatches, Command};
+use clap::{AppSettings, Arg, ArgMatches, Command};
use rustls::{Certificate, PrivateKey};
use std::env;
use std::net::IpAddr;
" - ",
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")
.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')
.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")
pub allow_symlink: bool,
pub render_index: bool,
pub render_spa: bool,
+ pub render_index_fallback: bool,
pub cors: bool,
pub tls: Option<(Vec<Certificate>, PrivateKey)>,
}
pub fn parse(matches: ArgMatches) -> BoxResult<Args> {
let port = matches.value_of_t::<u16>("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<IpAddr> = Args::parse_addrs(&addrs)?;
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)) => {
allow_upload,
allow_symlink,
render_index,
+ render_index_fallback,
render_spa,
tls,
})
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)
}
"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/";
}
for directory in DIRECTORIES {
for file in FILES {
- if *directory == DIR_NO_INDEX {
+ if *directory == DIR_NO_INDEX && *file == "index.html" {
continue;
}
tmpdir
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]
}
#[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())?;
}
#[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(())