diff --git a/guide/src/qs_12.md b/guide/src/qs_12.md index 5900e4172..286709353 100644 --- a/guide/src/qs_12.md +++ b/guide/src/qs_12.md @@ -25,9 +25,8 @@ fn main() { ## Directory To serve files from specific directory and sub-directories `StaticFiles` could be used. -`StaticFiles` could be registered with `Application::resource` method. -`StaticFiles` requires tail named path expression for resource registration. -And this name has to be used in `StaticFile` constructor. +`StaticFiles` must be registered with `Application::handler()` method otherwise +it won't be able to server sub-paths. ```rust # extern crate actix_web; @@ -35,12 +34,11 @@ use actix_web::*; fn main() { Application::new() - .resource("/static/{tail:.*}", |r| r.h(fs::StaticFiles::new("tail", ".", true))) + .handler("/static", fs::StaticFiles::new(".", true)) .finish(); } ``` -First parameter is a name of path pattern. Second parameter is a base directory. -Third parameter is *show_index*, if it is set to *true* +First parameter is a base directory. Second parameter is *show_index*, if it is set to *true* directory listing would be returned for directories, if it is set to *false* then *404 Not Found* would be returned instead of directory listing. diff --git a/src/application.rs b/src/application.rs index 7ca1449c0..7eb178997 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,3 +1,4 @@ +use std::mem; use std::rc::Rc; use std::cell::RefCell; use std::collections::HashMap; @@ -22,6 +23,7 @@ pub struct HttpApplication { } pub(crate) struct Inner { + prefix: usize, default: Resource, router: Router, resources: Vec>, @@ -36,12 +38,18 @@ impl PipelineHandler for Inner { } else { for &mut (ref prefix, ref mut handler) in &mut self.handlers { let m = { - let path = req.path(); - path.starts_with(prefix) && ( - path.len() == prefix.len() || - path.split_at(prefix.len()).1.starts_with('/')) + let path = &req.path()[self.prefix..]; + path.starts_with(prefix) && (path.len() == prefix.len() || + path.split_at(prefix.len()).1.starts_with('/')) }; if m { + let path: &'static str = unsafe{ + mem::transmute(&req.path()[self.prefix+prefix.len()..])}; + if path.is_empty() { + req.match_info_mut().add("tail", ""); + } else { + req.match_info_mut().add("tail", path.trim_left_matches('/')); + } return handler.handle(req) } } @@ -335,6 +343,7 @@ impl Application where S: 'static { let inner = Rc::new(RefCell::new( Inner { + prefix: prefix.len(), default: parts.default, router: router.clone(), resources: resources, @@ -487,6 +496,39 @@ mod tests { let req = TestRequest::with_uri("/blah").finish(); let resp = app.run(req); assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND); + } + + #[test] + fn test_handler_prefix() { + let mut app = Application::new() + .prefix("/app") + .handler("/test", httpcodes::HTTPOk) + .finish(); + + let req = TestRequest::with_uri("/test").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND); + + let req = TestRequest::with_uri("/app/test").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/test/").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/test/app").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/testapp").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND); + + let req = TestRequest::with_uri("/app/blah").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND); } + } diff --git a/src/fs.rs b/src/fs.rs index c7dc68dc7..467e90192 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -191,8 +191,8 @@ impl Responder for FilesystemElement { /// Static files handling /// -/// Can be registered with `Application::resource()`. Resource path has to contain -/// tail named pattern and this name has to be used in `StaticFile` constructor. +/// `StaticFile` handler must be registered with `Application::handler()` method, +/// because `StaticFile` handler requires access sub-path information. /// /// ```rust /// # extern crate actix_web; @@ -200,12 +200,11 @@ impl Responder for FilesystemElement { /// /// fn main() { /// let app = Application::new() -/// .resource("/static/{tail:.*}", |r| r.h(fs::StaticFiles::new("tail", ".", true))) +/// .handler("/static", fs::StaticFiles::new(".", true)) /// .finish(); /// } /// ``` pub struct StaticFiles { - name: String, directory: PathBuf, accessible: bool, show_index: bool, @@ -219,7 +218,7 @@ impl StaticFiles { /// `dir` - base directory /// /// `index` - show index for directory - pub fn new>(name: &str, dir: D, index: bool) -> StaticFiles { + pub fn new>(dir: D, index: bool) -> StaticFiles { let dir = dir.into(); let (dir, access) = match dir.canonicalize() { @@ -238,7 +237,6 @@ impl StaticFiles { }; StaticFiles { - name: name.to_owned(), directory: dir, accessible: access, show_index: index, @@ -256,7 +254,7 @@ impl Handler for StaticFiles { if !self.accessible { Err(io::Error::new(io::ErrorKind::NotFound, "not found")) } else { - let path = if let Some(path) = req.match_info().get(&self.name) { + let path = if let Some(path) = req.match_info().get("tail") { path } else { return Err(io::Error::new(io::ErrorKind::NotFound, "not found")) @@ -300,7 +298,7 @@ mod tests { #[test] fn test_static_files() { - let mut st = StaticFiles::new("tail", ".", true); + let mut st = StaticFiles::new(".", true); st.accessible = false; assert!(st.handle(HttpRequest::default()).is_err()); diff --git a/src/pipeline.rs b/src/pipeline.rs index e8fbfbe8a..3ea1a3209 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -677,7 +677,14 @@ impl ProcessResponse { // response is completed match self.iostate { IOState::Done => { - io.write_eof(); + match io.write_eof() { + Ok(_) => (), + Err(err) => { + debug!("Error sending data: {}", err); + info.error = Some(err.into()); + return Ok(FinishingMiddlewares::init(info, self.resp)) + } + } self.resp.set_response_size(io.written()); Ok(FinishingMiddlewares::init(info, self.resp)) }