diff --git a/Cargo.toml b/Cargo.toml index 370089e7e..03b1794ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,6 @@ actix-utils = "0.3.0" actix-http = { git = "https://github.com/actix/actix-http.git" } actix-router = { git = "https://github.com/actix/actix-net.git" } -#actix-router = { path = "../actix-net/router" } bytes = "0.4" derive_more = "0.14" diff --git a/src/app.rs b/src/app.rs index 3940b9fc5..119f1a21e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -13,11 +13,13 @@ use actix_service::{ use futures::future::{ok, Either, FutureResult}; use futures::{Async, Future, IntoFuture, Poll}; +use crate::guard::Guard; use crate::resource::Resource; use crate::scope::{insert_slash, Scope}; use crate::service::{ServiceRequest, ServiceResponse}; use crate::state::{State, StateFactory, StateFactoryResult}; +type Guards = Vec>; type HttpService

= BoxedService, ServiceResponse, ()>; type HttpNewService

= BoxedNewService<(), ServiceRequest

, ServiceResponse, (), ()>; type BoxedResponse = Box>; @@ -141,14 +143,15 @@ where where F: FnOnce(Scope

) -> Scope

, { - let scope = f(Scope::new(path)); + let mut scope = f(Scope::new(path)); let rdef = scope.rdef().clone(); let default = scope.get_default(); + let guards = scope.take_guards(); let fref = Rc::new(RefCell::new(None)); AppRouter { chain: self.chain, - services: vec![(rdef, boxed::new_service(scope.into_new_service()))], + services: vec![(rdef, boxed::new_service(scope.into_new_service()), guards)], default: None, defaults: vec![default], endpoint: AppEntry::new(fref.clone()), @@ -201,13 +204,13 @@ where > + 'static, { let rdef = ResourceDef::new(&insert_slash(path)); - let resource = f(Resource::new()); - let default = resource.get_default(); + let res = f(Resource::new()); + let default = res.get_default(); let fref = Rc::new(RefCell::new(None)); AppRouter { chain: self.chain, - services: vec![(rdef, boxed::new_service(resource.into_new_service()))], + services: vec![(rdef, boxed::new_service(res.into_new_service()), None)], default: None, defaults: vec![default], endpoint: AppEntry::new(fref.clone()), @@ -308,7 +311,7 @@ where /// for building application instances. pub struct AppRouter { chain: C, - services: Vec<(ResourceDef, HttpNewService

)>, + services: Vec<(ResourceDef, HttpNewService

, Option)>, default: Option>>, defaults: Vec>>>>>, endpoint: T, @@ -357,11 +360,12 @@ where where F: FnOnce(Scope

) -> Scope

, { - let scope = f(Scope::new(path)); + let mut scope = f(Scope::new(path)); let rdef = scope.rdef().clone(); + let guards = scope.take_guards(); self.defaults.push(scope.get_default()); self.services - .push((rdef, boxed::new_service(scope.into_new_service()))); + .push((rdef, boxed::new_service(scope.into_new_service()), guards)); self } @@ -411,8 +415,11 @@ where let rdef = ResourceDef::new(&insert_slash(path)); let resource = f(Resource::new()); self.defaults.push(resource.get_default()); - self.services - .push((rdef, boxed::new_service(resource.into_new_service()))); + self.services.push(( + rdef, + boxed::new_service(resource.into_new_service()), + None, + )); self } @@ -452,6 +459,7 @@ where self.services.push(( rdef.into(), boxed::new_service(factory.into_new_service().map_init_err(|_| ())), + None, )); self } @@ -562,7 +570,12 @@ where // set factory *self.factory_ref.borrow_mut() = Some(AppRoutingFactory { default: self.default.clone(), - services: Rc::new(self.services), + services: Rc::new( + self.services + .into_iter() + .map(|(rdef, srv, guards)| (rdef, srv, RefCell::new(guards))) + .collect(), + ), }); AppInit { @@ -575,7 +588,7 @@ where } pub struct AppRoutingFactory

{ - services: Rc)>>, + services: Rc, RefCell>)>>, default: Option>>, } @@ -598,9 +611,10 @@ impl NewService for AppRoutingFactory

{ fut: self .services .iter() - .map(|(path, service)| { + .map(|(path, service, guards)| { CreateAppRoutingItem::Future( Some(path.clone()), + guards.borrow_mut().take(), service.new_service(&()), ) }) @@ -622,8 +636,8 @@ pub struct AppRoutingFactoryResponse

{ } enum CreateAppRoutingItem

{ - Future(Option, HttpServiceFut

), - Service(ResourceDef, HttpService

), + Future(Option, Option, HttpServiceFut

), + Service(ResourceDef, Option, HttpService

), } impl

Future for AppRoutingFactoryResponse

{ @@ -643,20 +657,24 @@ impl

Future for AppRoutingFactoryResponse

{ // poll http services for item in &mut self.fut { let res = match item { - CreateAppRoutingItem::Future(ref mut path, ref mut fut) => { - match fut.poll()? { - Async::Ready(service) => Some((path.take().unwrap(), service)), - Async::NotReady => { - done = false; - None - } + CreateAppRoutingItem::Future( + ref mut path, + ref mut guards, + ref mut fut, + ) => match fut.poll()? { + Async::Ready(service) => { + Some((path.take().unwrap(), guards.take(), service)) } - } - CreateAppRoutingItem::Service(_, _) => continue, + Async::NotReady => { + done = false; + None + } + }, + CreateAppRoutingItem::Service(_, _, _) => continue, }; - if let Some((path, service)) = res { - *item = CreateAppRoutingItem::Service(path, service); + if let Some((path, guards, service)) = res { + *item = CreateAppRoutingItem::Service(path, guards, service); } } @@ -666,10 +684,11 @@ impl

Future for AppRoutingFactoryResponse

{ .drain(..) .fold(Router::build(), |mut router, item| { match item { - CreateAppRoutingItem::Service(path, service) => { - router.rdef(path, service) + CreateAppRoutingItem::Service(path, guards, service) => { + router.rdef(path, service); + router.set_user_data(guards); } - CreateAppRoutingItem::Future(_, _) => unreachable!(), + CreateAppRoutingItem::Future(_, _, _) => unreachable!(), } router }); @@ -685,7 +704,7 @@ impl

Future for AppRoutingFactoryResponse

{ } pub struct AppRouting

{ - router: Router>, + router: Router, Guards>, ready: Option<(ServiceRequest

, ResourceInfo)>, default: Option>, } @@ -705,7 +724,18 @@ impl

Service for AppRouting

{ } fn call(&mut self, mut req: ServiceRequest

) -> Self::Future { - if let Some((srv, _info)) = self.router.recognize_mut(req.match_info_mut()) { + let res = self.router.recognize_mut_checked(&mut req, |req, guards| { + if let Some(ref guards) = guards { + for f in guards { + if !f.check(req.head()) { + return false; + } + } + } + true + }); + + if let Some((srv, _info)) = res { Either::A(srv.call(req)) } else if let Some(ref mut default) = self.default { Either::A(default.call(req)) diff --git a/src/scope.rs b/src/scope.rs index ec6bc0354..2ed18a423 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -15,6 +15,7 @@ use crate::resource::Resource; use crate::route::Route; use crate::service::{ServiceRequest, ServiceResponse}; +type Guards = Vec>; type HttpService

= BoxedService, ServiceResponse, ()>; type HttpNewService

= BoxedNewService<(), ServiceRequest

, ServiceResponse, (), ()>; type BoxedResponse = Box>; @@ -51,8 +52,8 @@ type BoxedResponse = Box>; pub struct Scope> { endpoint: T, rdef: ResourceDef, - services: Vec<(ResourceDef, HttpNewService

)>, - guards: Rc>>, + services: Vec<(ResourceDef, HttpNewService

, Option)>, + guards: Vec>, default: Rc>>>>, defaults: Vec>>>>>, factory_ref: Rc>>>, @@ -66,7 +67,7 @@ impl Scope

{ Scope { endpoint: ScopeEndpoint::new(fref.clone()), rdef: rdef.clone(), - guards: Rc::new(Vec::new()), + guards: Vec::new(), services: Vec::new(), default: Rc::new(RefCell::new(None)), defaults: Vec::new(), @@ -110,7 +111,7 @@ where /// } /// ``` pub fn guard(mut self, guard: G) -> Self { - Rc::get_mut(&mut self.guards).unwrap().push(Box::new(guard)); + self.guards.push(Box::new(guard)); self } @@ -135,11 +136,12 @@ where where F: FnOnce(Scope

) -> Scope

, { - let scope = f(Scope::new(path)); + let mut scope = f(Scope::new(path)); let rdef = scope.rdef().clone(); + let guards = scope.take_guards(); self.defaults.push(scope.get_default()); self.services - .push((rdef, boxed::new_service(scope.into_new_service()))); + .push((rdef, boxed::new_service(scope.into_new_service()), guards)); self } @@ -204,8 +206,11 @@ where let rdef = ResourceDef::new(&insert_slash(path)); let resource = f(Resource::new()); self.defaults.push(resource.get_default()); - self.services - .push((rdef, boxed::new_service(resource.into_new_service()))); + self.services.push(( + rdef, + boxed::new_service(resource.into_new_service()), + None, + )); self } @@ -270,6 +275,14 @@ where pub(crate) fn get_default(&self) -> Rc>>>> { self.default.clone() } + + pub(crate) fn take_guards(&mut self) -> Option>> { + if self.guards.is_empty() { + None + } else { + Some(std::mem::replace(&mut self.guards, Vec::new())) + } + } } pub(crate) fn insert_slash(path: &str) -> String { @@ -301,7 +314,12 @@ where *self.factory_ref.borrow_mut() = Some(ScopeFactory { default: self.default.clone(), - services: Rc::new(self.services), + services: Rc::new( + self.services + .into_iter() + .map(|(rdef, srv, guards)| (rdef, srv, RefCell::new(guards))) + .collect(), + ), }); self.endpoint @@ -309,7 +327,7 @@ where } pub struct ScopeFactory

{ - services: Rc)>>, + services: Rc, RefCell>)>>, default: Rc>>>>, } @@ -332,9 +350,10 @@ impl NewService for ScopeFactory

{ fut: self .services .iter() - .map(|(path, service)| { + .map(|(path, service, guards)| { CreateScopeServiceItem::Future( Some(path.clone()), + guards.borrow_mut().take(), service.new_service(&()), ) }) @@ -356,8 +375,8 @@ pub struct ScopeFactoryResponse

{ type HttpServiceFut

= Box, Error = ()>>; enum CreateScopeServiceItem

{ - Future(Option, HttpServiceFut

), - Service(ResourceDef, HttpService

), + Future(Option, Option, HttpServiceFut

), + Service(ResourceDef, Option, HttpService

), } impl

Future for ScopeFactoryResponse

{ @@ -377,20 +396,24 @@ impl

Future for ScopeFactoryResponse

{ // poll http services for item in &mut self.fut { let res = match item { - CreateScopeServiceItem::Future(ref mut path, ref mut fut) => { - match fut.poll()? { - Async::Ready(service) => Some((path.take().unwrap(), service)), - Async::NotReady => { - done = false; - None - } + CreateScopeServiceItem::Future( + ref mut path, + ref mut guards, + ref mut fut, + ) => match fut.poll()? { + Async::Ready(service) => { + Some((path.take().unwrap(), guards.take(), service)) } - } - CreateScopeServiceItem::Service(_, _) => continue, + Async::NotReady => { + done = false; + None + } + }, + CreateScopeServiceItem::Service(_, _, _) => continue, }; - if let Some((path, service)) = res { - *item = CreateScopeServiceItem::Service(path, service); + if let Some((path, guards, service)) = res { + *item = CreateScopeServiceItem::Service(path, guards, service); } } @@ -400,10 +423,11 @@ impl

Future for ScopeFactoryResponse

{ .drain(..) .fold(Router::build(), |mut router, item| { match item { - CreateScopeServiceItem::Service(path, service) => { - router.rdef(path, service) + CreateScopeServiceItem::Service(path, guards, service) => { + router.rdef(path, service); + router.set_user_data(guards); } - CreateScopeServiceItem::Future(_, _) => unreachable!(), + CreateScopeServiceItem::Future(_, _, _) => unreachable!(), } router }); @@ -419,7 +443,7 @@ impl

Future for ScopeFactoryResponse

{ } pub struct ScopeService

{ - router: Router>, + router: Router, Vec>>, default: Option>, _ready: Option<(ServiceRequest

, ResourceInfo)>, } @@ -435,7 +459,18 @@ impl

Service for ScopeService

{ } fn call(&mut self, mut req: ServiceRequest

) -> Self::Future { - if let Some((srv, _info)) = self.router.recognize_mut(req.match_info_mut()) { + let res = self.router.recognize_mut_checked(&mut req, |req, guards| { + if let Some(ref guards) = guards { + for f in guards { + if !f.check(req.head()) { + return false; + } + } + } + true + }); + + if let Some((srv, _info)) = res { Either::A(srv.call(req)) } else if let Some(ref mut default) = self.default { Either::A(default.call(req)) @@ -478,7 +513,7 @@ mod tests { use bytes::Bytes; use crate::test::TestRequest; - use crate::{web, App, HttpRequest, HttpResponse}; + use crate::{guard, web, App, HttpRequest, HttpResponse}; #[test] fn test_scope() { @@ -614,30 +649,30 @@ mod tests { assert_eq!(resp.status(), StatusCode::NOT_FOUND); } - // #[test] - // fn test_scope_guard() { - // let mut rt = actix_rt::Runtime::new().unwrap(); - // let app = App::new() - // .scope("/app", |scope| { - // scope - // .guard(guard::Get()) - // .resource("/path1", |r| r.to(|| HttpResponse::Ok())) - // }) - // .into_new_service(); - // let mut srv = rt.block_on(app.new_service(&())).unwrap(); + #[test] + fn test_scope_guard() { + let mut rt = actix_rt::Runtime::new().unwrap(); + let app = App::new() + .scope("/app", |scope| { + scope + .guard(guard::Get()) + .resource("/path1", |r| r.to(|| HttpResponse::Ok())) + }) + .into_new_service(); + let mut srv = rt.block_on(app.new_service(&())).unwrap(); - // let req = TestRequest::with_uri("/app/path1") - // .method(Method::POST) - // .to_request(); - // let resp = rt.block_on(srv.call(req)).unwrap(); - // assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app/path1") + .method(Method::POST) + .to_request(); + let resp = rt.block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - // let req = TestRequest::with_uri("/app/path1") - // .method(Method::GET) - // .to_request(); - // let resp = rt.block_on(srv.call(req)).unwrap(); - // assert_eq!(resp.status(), StatusCode::OK); - // } + let req = TestRequest::with_uri("/app/path1") + .method(Method::GET) + .to_request(); + let resp = rt.block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } #[test] fn test_scope_variable_segment() { @@ -728,30 +763,32 @@ mod tests { assert_eq!(resp.status(), StatusCode::CREATED); } - // #[test] - // fn test_nested_scope_filter() { - // let app = App::new() - // .scope("/app", |scope| { - // scope.nested("/t1", |scope| { - // scope - // .filter(pred::Get()) - // .resource("/path1", |r| r.f(|_| HttpResponse::Ok())) - // }) - // }) - // .finish(); + #[test] + fn test_nested_scope_filter() { + let mut rt = actix_rt::Runtime::new().unwrap(); + let app = App::new() + .scope("/app", |scope| { + scope.nested("/t1", |scope| { + scope + .guard(guard::Get()) + .resource("/path1", |r| r.to(|| HttpResponse::Ok())) + }) + }) + .into_new_service(); + let mut srv = rt.block_on(app.new_service(&())).unwrap(); - // let req = TestRequest::with_uri("/app/t1/path1") - // .method(Method::POST) - // .request(); - // let resp = app.run(req); - // assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app/t1/path1") + .method(Method::POST) + .to_request(); + let resp = rt.block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - // let req = TestRequest::with_uri("/app/t1/path1") - // .method(Method::GET) - // .request(); - // let resp = app.run(req); - // assert_eq!(resp.as_msg().status(), StatusCode::OK); - // } + let req = TestRequest::with_uri("/app/t1/path1") + .method(Method::GET) + .to_request(); + let resp = rt.block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } #[test] fn test_nested_scope_with_variable_segment() { diff --git a/src/service.rs b/src/service.rs index a515300a4..637a8668a 100644 --- a/src/service.rs +++ b/src/service.rs @@ -8,7 +8,7 @@ use actix_http::{ Error, Extensions, HttpMessage, Payload, Request, RequestHead, Response, ResponseHead, }; -use actix_router::{Path, Url}; +use actix_router::{Path, Resource, Url}; use futures::future::{ok, FutureResult, IntoFuture}; use crate::request::HttpRequest; @@ -137,6 +137,12 @@ impl

ServiceRequest

{ } } +impl

Resource for ServiceRequest

{ + fn resource_path(&mut self) -> &mut Path { + self.match_info_mut() + } +} + impl

HttpMessage for ServiceRequest

{ type Stream = P;