aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven-Hendrik Haase <svenstaro@gmail.com>2025-03-09 06:55:03 +0000
committerSven-Hendrik Haase <svenstaro@gmail.com>2025-03-09 06:55:03 +0000
commit3351805fffadcb9747f772ca9ea05e0d6d13619c (patch)
treed7c666d625d709354fcf03c31deaaf69d7e90fdb
parentFix lints on Windows (diff)
downloadminiserve-3351805fffadcb9747f772ca9ea05e0d6d13619c.tar.gz
miniserve-3351805fffadcb9747f772ca9ea05e0d6d13619c.zip
Fix dir size calculation for percent-encoded paths with spaces
-rw-r--r--src/listing.rs2
-rw-r--r--src/main.rs13
-rw-r--r--src/renderer.rs2
-rw-r--r--tests/api.rs12
-rw-r--r--tests/fixtures/mod.rs4
5 files changed, 23 insertions, 10 deletions
diff --git a/src/listing.rs b/src/listing.rs
index 6e50ba1..c98670b 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -23,7 +23,7 @@ use self::percent_encode_sets::COMPONENT;
/// "percent-encode sets" as defined by WHATWG specs:
/// https://url.spec.whatwg.org/#percent-encoded-bytes
-mod percent_encode_sets {
+pub mod percent_encode_sets {
use percent_encoding::{AsciiSet, CONTROLS};
pub const QUERY: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>');
pub const PATH: &AsciiSet = &QUERY.add(b'?').add(b'`').add(b'{').add(b'}');
diff --git a/src/main.rs b/src/main.rs
index adda3f7..a7166ef 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -23,6 +23,7 @@ use dav_server::{
};
use fast_qr::QRBuilder;
use log::{error, info, warn};
+use percent_encoding::percent_decode_str;
use serde::Deserialize;
mod archive;
@@ -455,10 +456,14 @@ async fn api(
config: web::Data<MiniserveConfig>,
) -> Result<impl Responder, RuntimeError> {
match command.into_inner() {
- ApiCommand::DirSize(dir) => {
- // Convert the relative dir to an absolute path on the system
- let sanitized_path =
- file_utils::sanitize_path(&dir, true).expect("Expected a path to directory");
+ ApiCommand::DirSize(path) => {
+ // The dir argument might be percent-encoded so let's decode it just in case.
+ let decoded_path = percent_decode_str(&path)
+ .decode_utf8()
+ .map_err(|e| RuntimeError::ParseError(path.clone(), e.to_string()))?;
+ // Convert the relative dir to an absolute path on the system.
+ let sanitized_path = file_utils::sanitize_path(&*decoded_path, true)
+ .expect("Expected a path to directory");
let full_path = config
.path
.canonicalize()
diff --git a/src/renderer.rs b/src/renderer.rs
index d6b01c8..6a8b808 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -684,7 +684,7 @@ fn page_header(
body: JSON.stringify({
DirSize: dir
})
- }).then(resp => resp.text())
+ }).then(resp => resp.ok ? resp.text() : "~")
}
// Initialize shimmer effects for .size-cell elements in .entry-type-directory rows
diff --git a/tests/api.rs b/tests/api.rs
index 32d6cef..fe313b0 100644
--- a/tests/api.rs
+++ b/tests/api.rs
@@ -1,5 +1,6 @@
use std::collections::HashMap;
+use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
use reqwest::{StatusCode, blocking::Client};
use rstest::rstest;
@@ -7,10 +8,17 @@ mod fixtures;
use crate::fixtures::{DIRECTORIES, Error, TestServer, server};
+/// Test that we can get dir size for plain paths as well as percent-encoded paths
#[rstest]
-fn api_dir_size(server: TestServer) -> Result<(), Error> {
+#[case(DIRECTORIES[0].to_string())]
+#[case(DIRECTORIES[1].to_string())]
+#[case(DIRECTORIES[2].to_string())]
+#[case(utf8_percent_encode(DIRECTORIES[0], NON_ALPHANUMERIC).to_string())]
+#[case(utf8_percent_encode(DIRECTORIES[1], NON_ALPHANUMERIC).to_string())]
+#[case(utf8_percent_encode(DIRECTORIES[2], NON_ALPHANUMERIC).to_string())]
+fn api_dir_size(#[case] dir: String, server: TestServer) -> Result<(), Error> {
let mut command = HashMap::new();
- command.insert("DirSize", DIRECTORIES[0]);
+ command.insert("DirSize", dir);
let resp = Client::new()
.post(server.url().join(&format!("__miniserve_internal/api"))?)
diff --git a/tests/fixtures/mod.rs b/tests/fixtures/mod.rs
index f2869b7..5b331e9 100644
--- a/tests/fixtures/mod.rs
+++ b/tests/fixtures/mod.rs
@@ -34,10 +34,10 @@ pub static FILES: &[&str] = &[
pub static HIDDEN_FILES: &[&str] = &[".hidden_file1", ".hidden_file2"];
/// Directory names for testing purpose
-pub static DIRECTORIES: &[&str] = &["dira/", "dirb/", "dirc/"];
+pub static DIRECTORIES: &[&str] = &["dira/", "dirb/", "dir space/"];
/// Hidden directories for testing purpose
-pub static HIDDEN_DIRECTORIES: &[&str] = &[".hidden_dir1/", ".hidden_dir2/"];
+pub static HIDDEN_DIRECTORIES: &[&str] = &[".hidden_dir1/", ".hidden space dir/"];
/// Name of a deeply nested file
pub static DEEPLY_NESTED_FILE: &str = "very/deeply/nested/test.rs";