diff --git a/src/args.rs b/src/args.rs index 867e3b4..9801e0c 100644 --- a/src/args.rs +++ b/src/args.rs @@ -120,6 +120,7 @@ pub struct Args { pub addrs: Vec<IpAddr>, pub port: u16, pub path: PathBuf, + pub path_is_file: bool, pub path_prefix: String, pub uri_prefix: String, pub auth: AccessControl, @@ -146,6 +147,7 @@ impl Args { .unwrap_or_else(|| vec!["0.0.0.0", "::"]); let addrs: Vec<IpAddr> = Args::parse_addrs(&addrs)?; let path = Args::parse_path(matches.value_of_os("path").unwrap_or_default())?; + let path_is_file = path.metadata()?.is_file(); let path_prefix = matches .value_of("path-prefix") .map(|v| v.trim_matches('/').to_owned()) @@ -180,6 +182,7 @@ impl Args { addrs, port, path, + path_is_file, path_prefix, uri_prefix, auth, diff --git a/src/server.rs b/src/server.rs index 81f01f7..1d3f014 100644 --- a/src/server.rs +++ b/src/server.rs @@ -90,19 +90,26 @@ impl Server { let headers = req.headers(); let method = req.method().clone(); - let authorization = headers.get(AUTHORIZATION); - let guard_type = self.args.auth.guard(req_path, &method, authorization); - if req_path == "/favicon.ico" && method == Method::GET { self.handle_send_favicon(headers, &mut res).await?; return Ok(res); } + let authorization = headers.get(AUTHORIZATION); + let guard_type = self.args.auth.guard(req_path, &method, authorization); if guard_type.is_reject() { self.auth_reject(&mut res); return Ok(res); } + let head_only = method == Method::HEAD; + + if self.args.path_is_file { + self.handle_send_file(&self.args.path, headers, head_only, &mut res) + .await?; + return Ok(res); + } + let path = match self.extract_path(req_path) { Some(v) => v, None => { @@ -133,7 +140,6 @@ impl Server { match method { Method::GET | Method::HEAD => { - let head_only = method == Method::HEAD; if is_dir { if render_try_index && query == "zip" { self.handle_zip_dir(path, head_only, &mut res).await?; @@ -343,7 +349,7 @@ impl Server { let filename = path .file_name() .and_then(|v| v.to_str()) - .ok_or_else(|| format!("Failed to get name of `{}`", path.display()))?; + .ok_or_else(|| format!("Failed to get file name of `{}`", path.display()))?; res.headers_mut().insert( CONTENT_DISPOSITION, HeaderValue::from_str(&format!( diff --git a/tests/path_prefix.rs b/tests/args.rs similarity index 56% rename from tests/path_prefix.rs rename to tests/args.rs index dd34acf..d68e58b 100644 --- a/tests/path_prefix.rs +++ b/tests/args.rs @@ -1,8 +1,11 @@ mod fixtures; mod utils; -use fixtures::{server, Error, TestServer}; +use assert_cmd::prelude::*; +use assert_fs::fixture::TempDir; +use fixtures::{port, server, tmpdir, Error, TestServer}; use rstest::rstest; +use std::process::{Command, Stdio}; #[rstest] fn path_prefix_index(#[with(&["--path-prefix", "xyz"])] server: TestServer) -> Result<(), Error> { @@ -28,3 +31,21 @@ fn path_prefix_propfind( assert!(text.contains("<D:href>/xyz/</D:href>")); Ok(()) } + +#[rstest] +#[case("index.html")] +fn serve_single_file(tmpdir: TempDir, port: u16, #[case] file: &str) -> Result<(), Error> { + let mut child = Command::cargo_bin("duf")? + .env("RUST_LOG", "false") + .arg(tmpdir.path().join(file)) + .arg("-p") + .arg(port.to_string()) + .stdout(Stdio::piped()) + .spawn()?; + + let resp = reqwest::blocking::get(format!("http://localhost:{}/index.html", port))?; + assert_eq!(resp.text()?, "This is index.html"); + + child.kill()?; + Ok(()) +}