diff --git a/CHANGES.md b/CHANGES.md index 04e2ad8d8..4bbfe5bd2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -25,6 +25,8 @@ * `HttpRequest::url_for_static()` for a named route with no variables segments +* Propagation of the application's default resource to scopes that haven't set a default resource. + ### Changed diff --git a/src/application.rs b/src/application.rs index b28c1829f..9ce99e5b1 100644 --- a/src/application.rs +++ b/src/application.rs @@ -29,7 +29,7 @@ pub struct HttpApplication { #[doc(hidden)] pub struct Inner { prefix: usize, - default: ResourceHandler, + default: Rc>>, encoding: ContentEncoding, resources: Vec>, handlers: Vec>, @@ -51,7 +51,7 @@ impl PipelineHandler for Inner { match htype { HandlerType::Normal(idx) => match self.resources[idx].handle(req) { Ok(result) => result, - Err(req) => match self.default.handle(req) { + Err(req) => match self.default.borrow_mut().handle(req) { Ok(result) => result, Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)), }, @@ -60,7 +60,7 @@ impl PipelineHandler for Inner { PrefixHandlerType::Handler(_, ref mut hnd) => hnd.handle(req), PrefixHandlerType::Scope(_, ref mut hnd, _) => hnd.handle(req), }, - HandlerType::Default => match self.default.handle(req) { + HandlerType::Default => match self.default.borrow_mut().handle(req) { Ok(result) => result, Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)), }, @@ -172,7 +172,7 @@ struct ApplicationParts { state: S, prefix: String, settings: ServerSettings, - default: ResourceHandler, + default: Rc>>, resources: Vec<(Resource, Option>)>, handlers: Vec>, external: HashMap, @@ -223,7 +223,7 @@ where state, prefix: "/".to_owned(), settings: ServerSettings::default(), - default: ResourceHandler::default_not_found(), + default: Rc::new(RefCell::new(ResourceHandler::default_not_found())), resources: Vec::new(), handlers: Vec::new(), external: HashMap::new(), @@ -474,7 +474,7 @@ where { { let parts = self.parts.as_mut().expect("Use after finish"); - f(&mut parts.default); + f(&mut parts.default.borrow_mut()); } self } @@ -615,7 +615,7 @@ where /// Finish application configuration and create `HttpHandler` object. pub fn finish(&mut self) -> HttpApplication { - let parts = self.parts.take().expect("Use after finish"); + let mut 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) @@ -628,11 +628,19 @@ where resources.push((pattern, None)); } + for ref mut handler in parts.handlers.iter_mut() { + if let PrefixHandlerType::Scope(_, ref mut route_handler, _) = handler { + if !route_handler.has_default_resource() { + route_handler.default_resource(Rc::clone(&parts.default)); + } + }; + } + let (router, resources) = Router::new(&prefix, parts.settings, resources); let inner = Rc::new(RefCell::new(Inner { prefix: prefix_len, - default: parts.default, + default: Rc::clone(&parts.default), encoding: parts.encoding, handlers: parts.handlers, resources, diff --git a/src/handler.rs b/src/handler.rs index d330e0716..4428ce83f 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,5 +1,7 @@ +use std::cell::RefCell; use std::marker::PhantomData; use std::ops::Deref; +use std::rc::Rc; use futures::future::{err, ok, Future}; use futures::{Async, Poll}; @@ -8,6 +10,7 @@ use error::Error; use http::StatusCode; use httprequest::HttpRequest; use httpresponse::HttpResponse; +use resource::ResourceHandler; /// Trait defines object that could be registered as route handler #[allow(unused_variables)] @@ -403,6 +406,14 @@ where // /// Trait defines object that could be registered as resource route pub(crate) trait RouteHandler: 'static { fn handle(&mut self, req: HttpRequest) -> AsyncResult; + + fn has_default_resource(&self) -> bool { + false + } + + fn default_resource(&mut self, default: Rc>>) { + unimplemented!() + } } /// Route handler wrapper for Handler diff --git a/src/scope.rs b/src/scope.rs index 6651992db..70fb17287 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -405,6 +405,14 @@ impl RouteHandler for Scope { unimplemented!() } } + + fn has_default_resource(&self) -> bool { + self.default.is_some() + } + + fn default_resource(&mut self, default: ScopeResource) { + self.default = Some(default); + } } struct Wrapper { @@ -1188,4 +1196,27 @@ mod tests { let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } + + #[test] + fn test_default_resource_propagation() { + let mut app = App::new() + .scope("/app1", |scope| { + scope.default_resource(|r| r.f(|_| HttpResponse::BadRequest())) + }) + .scope("/app2", |scope| scope) + .default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed())) + .finish(); + + let req = TestRequest::with_uri("/non-exist").finish(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED); + + let req = TestRequest::with_uri("/app1/non-exist").finish(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST); + + let req = TestRequest::with_uri("/app2/non-exist").finish(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED); + } }