Skip to content
This repository has been archived by the owner on Sep 15, 2022. It is now read-only.

Commit

Permalink
Percent decode path for static file handler
Browse files Browse the repository at this point in the history
  • Loading branch information
lawliet89 committed Sep 18, 2017
1 parent 5d41374 commit f1c03e2
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 18 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
52 changes: 34 additions & 18 deletions src/static_files_handler.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -20,7 +24,18 @@ impl<D> Middleware<D> for StaticFilesHandler {
fn invoke<'a>(&self, req: &mut Request<D>, 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()
}
}
Expand Down Expand Up @@ -56,28 +71,29 @@ impl StaticFilesHandler {
})
}

fn percent_decode<'a>(path: &'a str) -> Result<Cow<'a, str>, Utf8Error> {
percent_encoding::percent_decode(path.as_bytes()).decode_utf8()
}

fn with_file<'a, 'b, D, P>(&self,
relative_path: Option<P>,
path: P,
res: Response<'a, D>)
-> MiddlewareResult<'a, D> where P: AsRef<Path> {
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()
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions tests/examples/static_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}

0 comments on commit f1c03e2

Please sign in to comment.