Skip to content

Commit

Permalink
fix: percent decode path segments
Browse files Browse the repository at this point in the history
  • Loading branch information
ttys3 committed Aug 26, 2021
1 parent 2431e03 commit 7744278
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 0 deletions.
1 change: 1 addition & 0 deletions tower-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ http-body = "0.4.1"
pin-project = "1"
tower-layer = "0.3"
tower-service = "0.3"
percent-encoding = "2.1.0"

# optional dependencies
async-compression = { version = "0.3", optional = true, features = ["tokio"] }
Expand Down
29 changes: 29 additions & 0 deletions tower-http/src/services/fs/serve_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::{
};
use tokio::fs::File;
use tower_service::Service;
use percent_encoding::percent_decode;

/// Service that serves files from a given directory and all its sub directories.
///
Expand Down Expand Up @@ -66,6 +67,9 @@ impl<ReqBody> Service<Request<ReqBody>> for ServeDir {
// build and validate the path
let path = req.uri().path();
let path = path.trim_start_matches('/');
let path_decoded = percent_decode(path.as_ref()).decode_utf8_lossy().to_string();
let path = path_decoded.as_str();

let mut full_path = self.base.clone();
for seg in path.split('/') {
if seg.starts_with("..") || seg.contains('\\') {
Expand Down Expand Up @@ -327,4 +331,29 @@ mod tests {
let bytes = hyper::body::to_bytes(body).await.unwrap();
String::from_utf8(bytes.to_vec()).unwrap()
}

#[tokio::test]
async fn access_cjk_percent_encoded_uri_path() {
let cjk_filename = "你好世界.txt";
// percent encoding present of 你好世界.txt
let cjk_filename_encoded = "%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C.txt";

let tmp_dir = std::env::temp_dir();
let tmp_filename = std::path::Path::new(tmp_dir.as_path()).join(cjk_filename);
let file_created = tokio::fs::File::create(&tmp_filename).await;
assert_eq!(file_created.is_ok(), true);

let svc = ServeDir::new(tmp_dir.clone());

let req = Request::builder()
.uri("/".to_string() + cjk_filename_encoded)
.body(Body::empty())
.unwrap();
let res = svc.oneshot(req).await.unwrap();

assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers()["content-type"], "text/plain");
let file_removed = tokio::fs::remove_file(&tmp_filename).await;
assert_eq!(file_removed.is_ok(), true);
}
}

0 comments on commit 7744278

Please sign in to comment.