From a48e616def6463826a47e20f84fdfaa43266d82d Mon Sep 17 00:00:00 2001 From: Roberto Huertas Date: Mon, 14 Oct 2019 17:23:15 +0200 Subject: [PATCH] feat(files): add possibility to redirect to slash-ended path (#1134) When accessing to a folder without a final slash, the index file will be loaded ok, but if it has references (like a css or an image in an html file) these resources won't be loaded correctly if they are using relative paths. In order to solve this, this PR adds the possibility to detect folders without a final slash and make a 302 redirect to mitigate this issue. The behavior is off by default. We're adding a new method called `redirect_to_slash_directory` which can be used to enable this behavior. --- actix-files/CHANGES.md | 9 +++---- actix-files/src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 5999f276..5eb4e9a6 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.1.6] - TBD + +* Add option to redirect to a slash-ended path `Files` #1132 + ## [0.1.5] - 2019-10-08 * Bump up `mime_guess` crate version to 2.0.1 @@ -7,18 +11,15 @@ * Bump up `percent-encoding` crate version to 2.1 * Allow user defined request guards for `Files` #1113 - - + ## [0.1.4] - 2019-07-20 * Allow to disable `Content-Disposition` header #686 - ## [0.1.3] - 2019-06-28 * Do not set `Content-Length` header, let actix-http set it #930 - ## [0.1.2] - 2019-06-13 * Content-Length is 0 for NamedFile HEAD request #914 diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 1cc26629..61674ca3 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -233,6 +233,7 @@ pub struct Files { directory: PathBuf, index: Option, show_index: bool, + redirect_to_slash: bool, default: Rc>>>, renderer: Rc, mime_override: Option>, @@ -246,6 +247,7 @@ impl Clone for Files { directory: self.directory.clone(), index: self.index.clone(), show_index: self.show_index, + redirect_to_slash: self.redirect_to_slash, default: self.default.clone(), renderer: self.renderer.clone(), file_flags: self.file_flags, @@ -273,6 +275,7 @@ impl Files { directory: dir, index: None, show_index: false, + redirect_to_slash: false, default: Rc::new(RefCell::new(None)), renderer: Rc::new(directory_listing), mime_override: None, @@ -289,6 +292,14 @@ impl Files { self } + /// Redirects to a slash-ended path when browsing a directory. + /// + /// By default never redirect. + pub fn redirect_to_slash_directory(mut self) -> Self { + self.redirect_to_slash = true; + self + } + /// Set custom directory renderer pub fn files_listing_renderer(mut self, f: F) -> Self where @@ -389,10 +400,10 @@ impl HttpServiceFactory for Files { } impl NewService for Files { - type Config = (); type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; + type Config = (); type Service = FilesService; type InitError = (); type Future = Box>; @@ -402,6 +413,7 @@ impl NewService for Files { directory: self.directory.clone(), index: self.index.clone(), show_index: self.show_index, + redirect_to_slash: self.redirect_to_slash, default: None, renderer: self.renderer.clone(), mime_override: self.mime_override.clone(), @@ -429,6 +441,7 @@ pub struct FilesService { directory: PathBuf, index: Option, show_index: bool, + redirect_to_slash: bool, default: Option, renderer: Rc, mime_override: Option>, @@ -502,6 +515,16 @@ impl Service for FilesService { if path.is_dir() { if let Some(ref redir_index) = self.index { + if self.redirect_to_slash && !req.path().ends_with('/') { + let redirect_to = format!("{}/", req.path()); + return Either::A(ok(req.into_response( + HttpResponse::Found() + .header(header::LOCATION, redirect_to) + .body("") + .into_body(), + ))); + } + let path = path.join(redir_index); match NamedFile::open(path) { @@ -1169,6 +1192,34 @@ mod tests { assert!(format!("{:?}", bytes).contains("/tests/test.png")); } + #[test] + fn test_redirect_to_slash_directory() { + // should not redirect if no index + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").redirect_to_slash_directory()), + ); + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + + // should redirect if index present + let mut srv = test::init_service( + App::new().service( + Files::new("/", ".") + .index_file("test.png") + .redirect_to_slash_directory(), + ), + ); + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req); + assert_eq!(resp.status(), StatusCode::FOUND); + + // should not redirect if the path is wrong + let req = TestRequest::with_uri("/not_existing").to_request(); + let resp = test::call_service(&mut srv, req); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + } + #[test] fn test_static_files_bad_directory() { let _st: Files = Files::new("/", "missing");