Skip to content

Commit 9ab6a80

Browse files
committed
fix: use approach from @Benjamin-L
1 parent 6ccb946 commit 9ab6a80

File tree

5 files changed

+109
-3
lines changed

5 files changed

+109
-3
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ hyper = { version = "0.14.20", features = ["http1", "server", "stream", "tcp"] }
4242
hyper-rustls = { version = "0.23.0", features = ["webpki-roots"] }
4343
local-ip-address = "0.4.5"
4444
mime_guess = "2.0.4"
45+
percent-encoding = "2.1.0"
4546
rustls = "0.20.6"
4647
rustls-pemfile = "1.0.0"
4748
serde = { version = "1.0.139", features = ["derive"] }

src/addon/file_server/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::str::FromStr;
1717
use std::sync::Arc;
1818

1919
use crate::utils::fmt::{format_bytes, format_system_date};
20+
use crate::utils::url_encode::{decode_uri, encode_uri};
2021

2122
use self::directory_entry::{DirectoryEntry, DirectoryIndex};
2223
use self::http_utils::{make_http_file_response, CacheControlDirective};
@@ -64,9 +65,8 @@ impl<'a> FileServer {
6465

6566
if let Some(path_and_query) = uri_parts.path_and_query {
6667
let path = path_and_query.path();
67-
let path = path.replace("%20", " ");
6868

69-
return Ok(PathBuf::from_str(&path)?);
69+
return decode_uri(path);
7070
}
7171

7272
Ok(PathBuf::from_str("/")?)
@@ -199,8 +199,9 @@ impl<'a> FileServer {
199199
fn make_dir_entry_link(root_dir: &Path, entry_path: &Path) -> String {
200200
let root_dir = root_dir.to_str().unwrap();
201201
let entry_path = entry_path.to_str().unwrap();
202+
let path_buf = PathBuf::from_str(&entry_path[root_dir.len()..]).unwrap();
202203

203-
(&entry_path[root_dir.len()..]).replace(" ", "%20")
204+
encode_uri(&path_buf)
204205
}
205206
}
206207

src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod error;
22
pub mod fmt;
33
pub mod signal;
4+
pub mod url_encode;

src/utils/url_encode.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use anyhow::{Error, Result};
2+
use percent_encoding::{
3+
percent_decode, percent_encode, utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC,
4+
};
5+
use std::os::unix::ffi::OsStrExt;
6+
use std::path::PathBuf;
7+
use std::str::FromStr;
8+
9+
pub const PERCENT_ENCODE_SET: &AsciiSet = &NON_ALPHANUMERIC
10+
.remove(b'-')
11+
.remove(b'_')
12+
.remove(b'.')
13+
.remove(b'~');
14+
15+
pub fn encode_uri(file_path: &PathBuf) -> String {
16+
file_path
17+
.iter()
18+
.filter(|c| c.to_str().unwrap().ne("/"))
19+
.flat_map(|component| {
20+
let segment = match component.to_str() {
21+
Some(component) => utf8_percent_encode(component, PERCENT_ENCODE_SET),
22+
None => {
23+
let bytes = {
24+
#[cfg(windows)]
25+
{
26+
let mut bytes = Vec::with_capacity(component.len() * 2);
27+
28+
for wc in component.encode_wide() {
29+
let wc = wc.to_be_bytes();
30+
31+
bytes.push(wc[0]);
32+
bytes.push(wc[1]);
33+
}
34+
35+
bytes
36+
}
37+
38+
#[cfg(not(windows))]
39+
component.as_bytes()
40+
};
41+
42+
percent_encode(&bytes, PERCENT_ENCODE_SET)
43+
}
44+
};
45+
46+
std::iter::once("/").chain(segment)
47+
})
48+
.collect::<String>()
49+
}
50+
51+
pub fn decode_uri(file_path: &str) -> Result<PathBuf> {
52+
let path_string = file_path
53+
.split('/')
54+
.map(|encoded_part| {
55+
let decode = percent_decode(encoded_part.as_bytes());
56+
let decode = decode.decode_utf8_lossy();
57+
58+
decode.to_string()
59+
})
60+
.collect::<Vec<String>>()
61+
.join("/");
62+
63+
PathBuf::from_str(&path_string).map_err(|err| Error::msg(err.to_string()))
64+
}
65+
66+
#[cfg(test)]
67+
mod tests {
68+
use std::path::PathBuf;
69+
use std::str::FromStr;
70+
71+
use super::{decode_uri, encode_uri};
72+
73+
#[test]
74+
fn encodes_uri() {
75+
let file_path = "/these are important files/do_not_delete/file name.txt";
76+
let file_path = PathBuf::from_str(file_path).unwrap();
77+
let file_path = encode_uri(&file_path);
78+
79+
assert_eq!(
80+
file_path,
81+
"/these%20are%20important%20files/do_not_delete/file%20name.txt"
82+
);
83+
}
84+
85+
#[test]
86+
fn decodes_uri() {
87+
let file_path = "these%20are%20important%20files/do_not_delete/file%20name.txt";
88+
let file_path = decode_uri(&file_path).unwrap();
89+
let file_path = file_path.to_str().unwrap();
90+
91+
assert_eq!(
92+
file_path,
93+
"these are important files/do_not_delete/file name.txt"
94+
);
95+
}
96+
}

0 commit comments

Comments
 (0)