diff --git a/CHANGES.md b/CHANGES.md index fb4943be1..cdac5fa05 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,16 +13,18 @@ * Added `ErrorHandlers` middleware -* Router cannot parse Non-ASCII characters in URL #137 +* Fix router cannot parse Non-ASCII characters in URL #137 + +* Fix client connection pooling * Fix long client urls #129 * Fix panic on invalid URL characters #130 -* Fix client connection pooling - * Fix logger request duration calculation #152 +* Fix prefix and static file serving #168 + * Add `signed` and `private` `CookieSessionBackend`s diff --git a/src/application.rs b/src/application.rs index 146150929..f6c126105 100644 --- a/src/application.rs +++ b/src/application.rs @@ -21,6 +21,7 @@ pub type Application = App; pub struct HttpApplication { state: Rc, prefix: String, + prefix_len: usize, router: Router, inner: Rc>>, middlewares: Rc>>>, @@ -73,6 +74,7 @@ impl HttpApplication { path.len() == prefix.len() || path.split_at(prefix.len()).1.starts_with('/')) }; + if m { let path: &'static str = unsafe { mem::transmute(&req.path()[inner.prefix+prefix.len()..]) }; @@ -106,8 +108,8 @@ impl HttpHandler for HttpApplication { let m = { let path = req.path(); path.starts_with(&self.prefix) && ( - path.len() == self.prefix.len() || - path.split_at(self.prefix.len()).1.starts_with('/')) + path.len() == self.prefix_len || + path.split_at(self.prefix_len).1.starts_with('/')) }; if m { let mut req = req.with_state(Rc::clone(&self.state), self.router.clone()); @@ -420,8 +422,12 @@ impl App where S: 'static { pub fn handler>(mut self, path: &str, handler: H) -> App { { - let path = path.trim().trim_right_matches('/').to_owned(); + let mut path = path.trim().trim_right_matches('/').to_owned(); + if !path.is_empty() && !path.starts_with('/') { + path.insert(0, '/') + } let parts = self.parts.as_mut().expect("Use after finish"); + parts.handlers.push((path, Box::new(WrapHandler::new(handler)))); } self @@ -471,17 +477,22 @@ impl App where S: 'static { pub fn finish(&mut self) -> HttpApplication { let parts = self.parts.take().expect("Use after finish"); let prefix = parts.prefix.trim().trim_right_matches('/'); + let (prefix, prefix_len) = if prefix.is_empty() { + ("/".to_owned(), 0) + } else { + (prefix.to_owned(), prefix.len()) + }; let mut resources = parts.resources; for (_, pattern) in parts.external { resources.push((pattern, None)); } - let (router, resources) = Router::new(prefix, parts.settings, resources); + let (router, resources) = Router::new(&prefix, parts.settings, resources); let inner = Rc::new(UnsafeCell::new( Inner { - prefix: prefix.len(), + prefix: prefix_len, default: parts.default, encoding: parts.encoding, handlers: parts.handlers, @@ -491,9 +502,10 @@ impl App where S: 'static { HttpApplication { state: Rc::new(parts.state), - prefix: prefix.to_owned(), router: router.clone(), middlewares: Rc::new(parts.middlewares), + prefix, + prefix_len, inner, } } @@ -670,6 +682,61 @@ mod tests { assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND); } + #[test] + fn test_handler2() { + let mut app = App::new() + .handler("test", |_| HttpResponse::Ok()) + .finish(); + + let req = TestRequest::with_uri("/test").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/test/").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/test/app").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/testapp").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND); + + 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_with_prefix() { + let mut app = App::new() + .prefix("prefix") + .handler("/test", |_| HttpResponse::Ok()) + .finish(); + + let req = TestRequest::with_uri("/prefix/test").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/prefix/test/").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/prefix/test/app").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/prefix/testapp").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND); + + let req = TestRequest::with_uri("/prefix/blah").finish(); + let resp = app.run(req); + assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND); + } + #[test] fn test_route() { let mut app = App::new() diff --git a/src/fs.rs b/src/fs.rs index 2f8f4b4e6..8c6402621 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -476,6 +476,9 @@ impl Handler for StaticFiles { new_path.push_str(&el.to_string_lossy()); new_path.push('/'); } + if !new_path.ends_with('/') { + new_path.push('/'); + } new_path.push_str(redir_index); HttpResponse::Found() .header(header::LOCATION, new_path.as_str()) @@ -500,7 +503,8 @@ impl Handler for StaticFiles { #[cfg(test)] mod tests { use super::*; - use test::TestRequest; + use application::App; + use test::{self, TestRequest}; use http::{header, Method, StatusCode}; #[test] @@ -603,4 +607,43 @@ mod tests { assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!(resp.headers().get(header::LOCATION).unwrap(), "/examples/basics/Cargo.toml"); } + + #[test] + fn integration_redirect_to_index_with_prefix() { + let mut srv = test::TestServer::with_factory( + || App::new() + .prefix("public") + .handler("/", StaticFiles::new(".").index_file("Cargo.toml"))); + + let request = srv.get().uri(srv.url("/public")).finish().unwrap(); + let response = srv.execute(request.send()).unwrap(); + assert_eq!(response.status(), StatusCode::FOUND); + let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap(); + assert_eq!(loc, "/public/Cargo.toml"); + + let request = srv.get().uri(srv.url("/public/")).finish().unwrap(); + let response = srv.execute(request.send()).unwrap(); + assert_eq!(response.status(), StatusCode::FOUND); + let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap(); + assert_eq!(loc, "/public/Cargo.toml"); + } + + #[test] + fn integration_redirect_to_index() { + let mut srv = test::TestServer::with_factory( + || App::new() + .handler("test", StaticFiles::new(".").index_file("Cargo.toml"))); + + let request = srv.get().uri(srv.url("/test")).finish().unwrap(); + let response = srv.execute(request.send()).unwrap(); + assert_eq!(response.status(), StatusCode::FOUND); + let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap(); + assert_eq!(loc, "/test/Cargo.toml"); + + let request = srv.get().uri(srv.url("/test/")).finish().unwrap(); + let response = srv.execute(request.send()).unwrap(); + assert_eq!(response.status(), StatusCode::FOUND); + let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap(); + assert_eq!(loc, "/test/Cargo.toml"); + } }