From f1c03e27c47366c974322e9db2904edd0ffcb9de Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Mon, 18 Sep 2017 15:00:56 +0800 Subject: [PATCH] Percent decode path for static file handler Fixes #405 --- Cargo.toml | 1 + src/lib.rs | 1 + src/static_files_handler.rs | 52 ++++++++++++++++++++++------------ tests/examples/static_files.rs | 7 +++++ 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 692e4c7a14..d5d156b67d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ groupable = "0.2" mustache = "0.8" lazy_static = "0.2" modifier = "0.1" +percent-encoding = "1.0.0" [dependencies.hyper] version = "0.10" diff --git a/src/lib.rs b/src/lib.rs index e6eb6fec04..ee3ef18831 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ extern crate url; extern crate mustache; extern crate groupable; extern crate modifier; +extern crate percent_encoding; #[macro_use] extern crate log; #[macro_use] extern crate lazy_static; diff --git a/src/static_files_handler.rs b/src/static_files_handler.rs index bebd4223ab..cfcc14114b 100644 --- a/src/static_files_handler.rs +++ b/src/static_files_handler.rs @@ -1,9 +1,13 @@ +use std::borrow::Cow; use std::path::{Path, PathBuf}; use std::io::ErrorKind::NotFound; use std::fs; +use std::str::Utf8Error; use hyper::method::Method::{Get, Head}; +use percent_encoding; +use NickelError; use status::StatusCode; use request::Request; use response::Response; @@ -20,7 +24,18 @@ impl Middleware for StaticFilesHandler { fn invoke<'a>(&self, req: &mut Request, res: Response<'a, D>) -> MiddlewareResult<'a, D> { match req.origin.method { - Get | Head => self.with_file(self.extract_path(req), res), + Get | Head => { + match self.extract_path(req) { + Some(path) => { + let path = Self::percent_decode(path); + match path { + Ok(path) => self.with_file(Path::new(path.as_ref()), res), + Err(e) => Err(NickelError::new(res, e.to_string(), StatusCode::BadRequest)) + } + } + None => res.next_middleware() + } + }, _ => res.next_middleware() } } @@ -56,28 +71,29 @@ impl StaticFilesHandler { }) } + fn percent_decode<'a>(path: &'a str) -> Result, Utf8Error> { + percent_encoding::percent_decode(path.as_bytes()).decode_utf8() + } + fn with_file<'a, 'b, D, P>(&self, - relative_path: Option

, + path: P, res: Response<'a, D>) -> MiddlewareResult<'a, D> where P: AsRef { - if let Some(path) = relative_path { - let path = path.as_ref(); - if !safe_path(path) { - let log_msg = format!("The path '{:?}' was denied access.", path); - return res.error(StatusCode::BadRequest, log_msg); - } + let path = path.as_ref(); + if !safe_path(path) { + let log_msg = format!("The path '{:?}' was denied access.", path); + return res.error(StatusCode::BadRequest, log_msg); + } - let path = self.root_path.join(path); - match fs::metadata(&path) { - Ok(ref attr) if attr.is_file() => return res.send_file(&path), - Err(ref e) if e.kind() != NotFound => debug!("Error getting metadata \ - for file '{:?}': {:?}", - path, e), - _ => {} + let path = self.root_path.join(path); + match fs::metadata(&path) { + Ok(ref attr) if attr.is_file() => res.send_file(&path), + Err(ref e) if e.kind() != NotFound => { + debug!("Error getting metadata for file '{:?}': {:?}", path, e); + res.next_middleware() } - }; - - res.next_middleware() + _ => res.next_middleware() + } } } diff --git a/tests/examples/static_files.rs b/tests/examples/static_files.rs index 3a4c439768..af8b83852a 100644 --- a/tests/examples/static_files.rs +++ b/tests/examples/static_files.rs @@ -56,3 +56,10 @@ fn fallthroughs_with_same_prefix() { assert_eq!(s, "Not Found"); }); } + +#[test] +fn percent_decode_path() { + with_path("/nested/file%20with%20space.js", |res| { + assert_eq!(res.status, StatusCode::Ok); + }); +}