1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-11 01:39:33 +00:00

allow scope level guards

This commit is contained in:
Nikolay Kim 2019-03-04 11:47:53 -08:00
parent 5c61321565
commit e442ddb167
4 changed files with 179 additions and 107 deletions

View file

@ -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"

View file

@ -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<Box<Guard>>;
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
@ -141,14 +143,15 @@ where
where
F: FnOnce(Scope<P>) -> Scope<P>,
{
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<C, P, B, T> {
chain: C,
services: Vec<(ResourceDef, HttpNewService<P>)>,
services: Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)>,
default: Option<Rc<HttpNewService<P>>>,
defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>,
endpoint: T,
@ -357,11 +360,12 @@ where
where
F: FnOnce(Scope<P>) -> Scope<P>,
{
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<P> {
services: Rc<Vec<(ResourceDef, HttpNewService<P>)>>,
services: Rc<Vec<(ResourceDef, HttpNewService<P>, RefCell<Option<Guards>>)>>,
default: Option<Rc<HttpNewService<P>>>,
}
@ -598,9 +611,10 @@ impl<P: 'static> NewService for AppRoutingFactory<P> {
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<P> {
}
enum CreateAppRoutingItem<P> {
Future(Option<ResourceDef>, HttpServiceFut<P>),
Service(ResourceDef, HttpService<P>),
Future(Option<ResourceDef>, Option<Guards>, HttpServiceFut<P>),
Service(ResourceDef, Option<Guards>, HttpService<P>),
}
impl<P> Future for AppRoutingFactoryResponse<P> {
@ -643,20 +657,24 @@ impl<P> Future for AppRoutingFactoryResponse<P> {
// 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<P> Future for AppRoutingFactoryResponse<P> {
.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<P> Future for AppRoutingFactoryResponse<P> {
}
pub struct AppRouting<P> {
router: Router<HttpService<P>>,
router: Router<HttpService<P>, Guards>,
ready: Option<(ServiceRequest<P>, ResourceInfo)>,
default: Option<HttpService<P>>,
}
@ -705,7 +724,18 @@ impl<P> Service for AppRouting<P> {
}
fn call(&mut self, mut req: ServiceRequest<P>) -> 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))

View file

@ -15,6 +15,7 @@ use crate::resource::Resource;
use crate::route::Route;
use crate::service::{ServiceRequest, ServiceResponse};
type Guards = Vec<Box<Guard>>;
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
@ -51,8 +52,8 @@ type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
pub struct Scope<P, T = ScopeEndpoint<P>> {
endpoint: T,
rdef: ResourceDef,
services: Vec<(ResourceDef, HttpNewService<P>)>,
guards: Rc<Vec<Box<Guard>>>,
services: Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)>,
guards: Vec<Box<Guard>>,
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>,
factory_ref: Rc<RefCell<Option<ScopeFactory<P>>>>,
@ -66,7 +67,7 @@ impl<P: 'static> Scope<P> {
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<G: Guard + 'static>(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<P>) -> Scope<P>,
{
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<RefCell<Option<Rc<HttpNewService<P>>>>> {
self.default.clone()
}
pub(crate) fn take_guards(&mut self) -> Option<Vec<Box<Guard>>> {
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<P> {
services: Rc<Vec<(ResourceDef, HttpNewService<P>)>>,
services: Rc<Vec<(ResourceDef, HttpNewService<P>, RefCell<Option<Guards>>)>>,
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
}
@ -332,9 +350,10 @@ impl<P: 'static> NewService for ScopeFactory<P> {
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<P> {
type HttpServiceFut<P> = Box<Future<Item = HttpService<P>, Error = ()>>;
enum CreateScopeServiceItem<P> {
Future(Option<ResourceDef>, HttpServiceFut<P>),
Service(ResourceDef, HttpService<P>),
Future(Option<ResourceDef>, Option<Guards>, HttpServiceFut<P>),
Service(ResourceDef, Option<Guards>, HttpService<P>),
}
impl<P> Future for ScopeFactoryResponse<P> {
@ -377,20 +396,24 @@ impl<P> Future for ScopeFactoryResponse<P> {
// 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<P> Future for ScopeFactoryResponse<P> {
.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<P> Future for ScopeFactoryResponse<P> {
}
pub struct ScopeService<P> {
router: Router<HttpService<P>>,
router: Router<HttpService<P>, Vec<Box<Guard>>>,
default: Option<HttpService<P>>,
_ready: Option<(ServiceRequest<P>, ResourceInfo)>,
}
@ -435,7 +459,18 @@ impl<P> Service for ScopeService<P> {
}
fn call(&mut self, mut req: ServiceRequest<P>) -> 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() {

View file

@ -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<P> ServiceRequest<P> {
}
}
impl<P> Resource<Url> for ServiceRequest<P> {
fn resource_path(&mut self) -> &mut Path<Url> {
self.match_info_mut()
}
}
impl<P> HttpMessage for ServiceRequest<P> {
type Stream = P;