1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2025-01-01 21:08:43 +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-http = { git = "https://github.com/actix/actix-http.git" }
actix-router = { git = "https://github.com/actix/actix-net.git" } actix-router = { git = "https://github.com/actix/actix-net.git" }
#actix-router = { path = "../actix-net/router" }
bytes = "0.4" bytes = "0.4"
derive_more = "0.14" derive_more = "0.14"

View file

@ -13,11 +13,13 @@ use actix_service::{
use futures::future::{ok, Either, FutureResult}; use futures::future::{ok, Either, FutureResult};
use futures::{Async, Future, IntoFuture, Poll}; use futures::{Async, Future, IntoFuture, Poll};
use crate::guard::Guard;
use crate::resource::Resource; use crate::resource::Resource;
use crate::scope::{insert_slash, Scope}; use crate::scope::{insert_slash, Scope};
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::state::{State, StateFactory, StateFactoryResult}; use crate::state::{State, StateFactory, StateFactoryResult};
type Guards = Vec<Box<Guard>>;
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>; type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>; type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>; type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
@ -141,14 +143,15 @@ where
where where
F: FnOnce(Scope<P>) -> Scope<P>, 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 rdef = scope.rdef().clone();
let default = scope.get_default(); let default = scope.get_default();
let guards = scope.take_guards();
let fref = Rc::new(RefCell::new(None)); let fref = Rc::new(RefCell::new(None));
AppRouter { AppRouter {
chain: self.chain, 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, default: None,
defaults: vec![default], defaults: vec![default],
endpoint: AppEntry::new(fref.clone()), endpoint: AppEntry::new(fref.clone()),
@ -201,13 +204,13 @@ where
> + 'static, > + 'static,
{ {
let rdef = ResourceDef::new(&insert_slash(path)); let rdef = ResourceDef::new(&insert_slash(path));
let resource = f(Resource::new()); let res = f(Resource::new());
let default = resource.get_default(); let default = res.get_default();
let fref = Rc::new(RefCell::new(None)); let fref = Rc::new(RefCell::new(None));
AppRouter { AppRouter {
chain: self.chain, 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, default: None,
defaults: vec![default], defaults: vec![default],
endpoint: AppEntry::new(fref.clone()), endpoint: AppEntry::new(fref.clone()),
@ -308,7 +311,7 @@ where
/// for building application instances. /// for building application instances.
pub struct AppRouter<C, P, B, T> { pub struct AppRouter<C, P, B, T> {
chain: C, chain: C,
services: Vec<(ResourceDef, HttpNewService<P>)>, services: Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)>,
default: Option<Rc<HttpNewService<P>>>, default: Option<Rc<HttpNewService<P>>>,
defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>, defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>,
endpoint: T, endpoint: T,
@ -357,11 +360,12 @@ where
where where
F: FnOnce(Scope<P>) -> Scope<P>, 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 rdef = scope.rdef().clone();
let guards = scope.take_guards();
self.defaults.push(scope.get_default()); self.defaults.push(scope.get_default());
self.services self.services
.push((rdef, boxed::new_service(scope.into_new_service()))); .push((rdef, boxed::new_service(scope.into_new_service()), guards));
self self
} }
@ -411,8 +415,11 @@ where
let rdef = ResourceDef::new(&insert_slash(path)); let rdef = ResourceDef::new(&insert_slash(path));
let resource = f(Resource::new()); let resource = f(Resource::new());
self.defaults.push(resource.get_default()); self.defaults.push(resource.get_default());
self.services self.services.push((
.push((rdef, boxed::new_service(resource.into_new_service()))); rdef,
boxed::new_service(resource.into_new_service()),
None,
));
self self
} }
@ -452,6 +459,7 @@ where
self.services.push(( self.services.push((
rdef.into(), rdef.into(),
boxed::new_service(factory.into_new_service().map_init_err(|_| ())), boxed::new_service(factory.into_new_service().map_init_err(|_| ())),
None,
)); ));
self self
} }
@ -562,7 +570,12 @@ where
// set factory // set factory
*self.factory_ref.borrow_mut() = Some(AppRoutingFactory { *self.factory_ref.borrow_mut() = Some(AppRoutingFactory {
default: self.default.clone(), 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 { AppInit {
@ -575,7 +588,7 @@ where
} }
pub struct AppRoutingFactory<P> { pub struct AppRoutingFactory<P> {
services: Rc<Vec<(ResourceDef, HttpNewService<P>)>>, services: Rc<Vec<(ResourceDef, HttpNewService<P>, RefCell<Option<Guards>>)>>,
default: Option<Rc<HttpNewService<P>>>, default: Option<Rc<HttpNewService<P>>>,
} }
@ -598,9 +611,10 @@ impl<P: 'static> NewService for AppRoutingFactory<P> {
fut: self fut: self
.services .services
.iter() .iter()
.map(|(path, service)| { .map(|(path, service, guards)| {
CreateAppRoutingItem::Future( CreateAppRoutingItem::Future(
Some(path.clone()), Some(path.clone()),
guards.borrow_mut().take(),
service.new_service(&()), service.new_service(&()),
) )
}) })
@ -622,8 +636,8 @@ pub struct AppRoutingFactoryResponse<P> {
} }
enum CreateAppRoutingItem<P> { enum CreateAppRoutingItem<P> {
Future(Option<ResourceDef>, HttpServiceFut<P>), Future(Option<ResourceDef>, Option<Guards>, HttpServiceFut<P>),
Service(ResourceDef, HttpService<P>), Service(ResourceDef, Option<Guards>, HttpService<P>),
} }
impl<P> Future for AppRoutingFactoryResponse<P> { impl<P> Future for AppRoutingFactoryResponse<P> {
@ -643,20 +657,24 @@ impl<P> Future for AppRoutingFactoryResponse<P> {
// poll http services // poll http services
for item in &mut self.fut { for item in &mut self.fut {
let res = match item { let res = match item {
CreateAppRoutingItem::Future(ref mut path, ref mut fut) => { CreateAppRoutingItem::Future(
match fut.poll()? { ref mut path,
Async::Ready(service) => Some((path.take().unwrap(), service)), ref mut guards,
Async::NotReady => { ref mut fut,
done = false; ) => match fut.poll()? {
None Async::Ready(service) => {
} Some((path.take().unwrap(), guards.take(), service))
} }
} Async::NotReady => {
CreateAppRoutingItem::Service(_, _) => continue, done = false;
None
}
},
CreateAppRoutingItem::Service(_, _, _) => continue,
}; };
if let Some((path, service)) = res { if let Some((path, guards, service)) = res {
*item = CreateAppRoutingItem::Service(path, service); *item = CreateAppRoutingItem::Service(path, guards, service);
} }
} }
@ -666,10 +684,11 @@ impl<P> Future for AppRoutingFactoryResponse<P> {
.drain(..) .drain(..)
.fold(Router::build(), |mut router, item| { .fold(Router::build(), |mut router, item| {
match item { match item {
CreateAppRoutingItem::Service(path, service) => { CreateAppRoutingItem::Service(path, guards, service) => {
router.rdef(path, service) router.rdef(path, service);
router.set_user_data(guards);
} }
CreateAppRoutingItem::Future(_, _) => unreachable!(), CreateAppRoutingItem::Future(_, _, _) => unreachable!(),
} }
router router
}); });
@ -685,7 +704,7 @@ impl<P> Future for AppRoutingFactoryResponse<P> {
} }
pub struct AppRouting<P> { pub struct AppRouting<P> {
router: Router<HttpService<P>>, router: Router<HttpService<P>, Guards>,
ready: Option<(ServiceRequest<P>, ResourceInfo)>, ready: Option<(ServiceRequest<P>, ResourceInfo)>,
default: Option<HttpService<P>>, default: Option<HttpService<P>>,
} }
@ -705,7 +724,18 @@ impl<P> Service for AppRouting<P> {
} }
fn call(&mut self, mut req: ServiceRequest<P>) -> Self::Future { 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)) Either::A(srv.call(req))
} else if let Some(ref mut default) = self.default { } else if let Some(ref mut default) = self.default {
Either::A(default.call(req)) Either::A(default.call(req))

View file

@ -15,6 +15,7 @@ use crate::resource::Resource;
use crate::route::Route; use crate::route::Route;
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
type Guards = Vec<Box<Guard>>;
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>; type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>; type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>; 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>> { pub struct Scope<P, T = ScopeEndpoint<P>> {
endpoint: T, endpoint: T,
rdef: ResourceDef, rdef: ResourceDef,
services: Vec<(ResourceDef, HttpNewService<P>)>, services: Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)>,
guards: Rc<Vec<Box<Guard>>>, guards: Vec<Box<Guard>>,
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>, default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>, defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>,
factory_ref: Rc<RefCell<Option<ScopeFactory<P>>>>, factory_ref: Rc<RefCell<Option<ScopeFactory<P>>>>,
@ -66,7 +67,7 @@ impl<P: 'static> Scope<P> {
Scope { Scope {
endpoint: ScopeEndpoint::new(fref.clone()), endpoint: ScopeEndpoint::new(fref.clone()),
rdef: rdef.clone(), rdef: rdef.clone(),
guards: Rc::new(Vec::new()), guards: Vec::new(),
services: Vec::new(), services: Vec::new(),
default: Rc::new(RefCell::new(None)), default: Rc::new(RefCell::new(None)),
defaults: Vec::new(), defaults: Vec::new(),
@ -110,7 +111,7 @@ where
/// } /// }
/// ``` /// ```
pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self { 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 self
} }
@ -135,11 +136,12 @@ where
where where
F: FnOnce(Scope<P>) -> Scope<P>, 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 rdef = scope.rdef().clone();
let guards = scope.take_guards();
self.defaults.push(scope.get_default()); self.defaults.push(scope.get_default());
self.services self.services
.push((rdef, boxed::new_service(scope.into_new_service()))); .push((rdef, boxed::new_service(scope.into_new_service()), guards));
self self
} }
@ -204,8 +206,11 @@ where
let rdef = ResourceDef::new(&insert_slash(path)); let rdef = ResourceDef::new(&insert_slash(path));
let resource = f(Resource::new()); let resource = f(Resource::new());
self.defaults.push(resource.get_default()); self.defaults.push(resource.get_default());
self.services self.services.push((
.push((rdef, boxed::new_service(resource.into_new_service()))); rdef,
boxed::new_service(resource.into_new_service()),
None,
));
self self
} }
@ -270,6 +275,14 @@ where
pub(crate) fn get_default(&self) -> Rc<RefCell<Option<Rc<HttpNewService<P>>>>> { pub(crate) fn get_default(&self) -> Rc<RefCell<Option<Rc<HttpNewService<P>>>>> {
self.default.clone() 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 { pub(crate) fn insert_slash(path: &str) -> String {
@ -301,7 +314,12 @@ where
*self.factory_ref.borrow_mut() = Some(ScopeFactory { *self.factory_ref.borrow_mut() = Some(ScopeFactory {
default: self.default.clone(), 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 self.endpoint
@ -309,7 +327,7 @@ where
} }
pub struct ScopeFactory<P> { 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>>>>>, default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
} }
@ -332,9 +350,10 @@ impl<P: 'static> NewService for ScopeFactory<P> {
fut: self fut: self
.services .services
.iter() .iter()
.map(|(path, service)| { .map(|(path, service, guards)| {
CreateScopeServiceItem::Future( CreateScopeServiceItem::Future(
Some(path.clone()), Some(path.clone()),
guards.borrow_mut().take(),
service.new_service(&()), service.new_service(&()),
) )
}) })
@ -356,8 +375,8 @@ pub struct ScopeFactoryResponse<P> {
type HttpServiceFut<P> = Box<Future<Item = HttpService<P>, Error = ()>>; type HttpServiceFut<P> = Box<Future<Item = HttpService<P>, Error = ()>>;
enum CreateScopeServiceItem<P> { enum CreateScopeServiceItem<P> {
Future(Option<ResourceDef>, HttpServiceFut<P>), Future(Option<ResourceDef>, Option<Guards>, HttpServiceFut<P>),
Service(ResourceDef, HttpService<P>), Service(ResourceDef, Option<Guards>, HttpService<P>),
} }
impl<P> Future for ScopeFactoryResponse<P> { impl<P> Future for ScopeFactoryResponse<P> {
@ -377,20 +396,24 @@ impl<P> Future for ScopeFactoryResponse<P> {
// poll http services // poll http services
for item in &mut self.fut { for item in &mut self.fut {
let res = match item { let res = match item {
CreateScopeServiceItem::Future(ref mut path, ref mut fut) => { CreateScopeServiceItem::Future(
match fut.poll()? { ref mut path,
Async::Ready(service) => Some((path.take().unwrap(), service)), ref mut guards,
Async::NotReady => { ref mut fut,
done = false; ) => match fut.poll()? {
None Async::Ready(service) => {
} Some((path.take().unwrap(), guards.take(), service))
} }
} Async::NotReady => {
CreateScopeServiceItem::Service(_, _) => continue, done = false;
None
}
},
CreateScopeServiceItem::Service(_, _, _) => continue,
}; };
if let Some((path, service)) = res { if let Some((path, guards, service)) = res {
*item = CreateScopeServiceItem::Service(path, service); *item = CreateScopeServiceItem::Service(path, guards, service);
} }
} }
@ -400,10 +423,11 @@ impl<P> Future for ScopeFactoryResponse<P> {
.drain(..) .drain(..)
.fold(Router::build(), |mut router, item| { .fold(Router::build(), |mut router, item| {
match item { match item {
CreateScopeServiceItem::Service(path, service) => { CreateScopeServiceItem::Service(path, guards, service) => {
router.rdef(path, service) router.rdef(path, service);
router.set_user_data(guards);
} }
CreateScopeServiceItem::Future(_, _) => unreachable!(), CreateScopeServiceItem::Future(_, _, _) => unreachable!(),
} }
router router
}); });
@ -419,7 +443,7 @@ impl<P> Future for ScopeFactoryResponse<P> {
} }
pub struct ScopeService<P> { pub struct ScopeService<P> {
router: Router<HttpService<P>>, router: Router<HttpService<P>, Vec<Box<Guard>>>,
default: Option<HttpService<P>>, default: Option<HttpService<P>>,
_ready: Option<(ServiceRequest<P>, ResourceInfo)>, _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 { 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)) Either::A(srv.call(req))
} else if let Some(ref mut default) = self.default { } else if let Some(ref mut default) = self.default {
Either::A(default.call(req)) Either::A(default.call(req))
@ -478,7 +513,7 @@ mod tests {
use bytes::Bytes; use bytes::Bytes;
use crate::test::TestRequest; use crate::test::TestRequest;
use crate::{web, App, HttpRequest, HttpResponse}; use crate::{guard, web, App, HttpRequest, HttpResponse};
#[test] #[test]
fn test_scope() { fn test_scope() {
@ -614,30 +649,30 @@ mod tests {
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
} }
// #[test] #[test]
// fn test_scope_guard() { fn test_scope_guard() {
// let mut rt = actix_rt::Runtime::new().unwrap(); let mut rt = actix_rt::Runtime::new().unwrap();
// let app = App::new() let app = App::new()
// .scope("/app", |scope| { .scope("/app", |scope| {
// scope scope
// .guard(guard::Get()) .guard(guard::Get())
// .resource("/path1", |r| r.to(|| HttpResponse::Ok())) .resource("/path1", |r| r.to(|| HttpResponse::Ok()))
// }) })
// .into_new_service(); .into_new_service();
// let mut srv = rt.block_on(app.new_service(&())).unwrap(); let mut srv = rt.block_on(app.new_service(&())).unwrap();
// let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
// .method(Method::POST) .method(Method::POST)
// .to_request(); .to_request();
// let resp = rt.block_on(srv.call(req)).unwrap(); let resp = rt.block_on(srv.call(req)).unwrap();
// assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
// let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
// .method(Method::GET) .method(Method::GET)
// .to_request(); .to_request();
// let resp = rt.block_on(srv.call(req)).unwrap(); let resp = rt.block_on(srv.call(req)).unwrap();
// assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
// } }
#[test] #[test]
fn test_scope_variable_segment() { fn test_scope_variable_segment() {
@ -728,30 +763,32 @@ mod tests {
assert_eq!(resp.status(), StatusCode::CREATED); assert_eq!(resp.status(), StatusCode::CREATED);
} }
// #[test] #[test]
// fn test_nested_scope_filter() { fn test_nested_scope_filter() {
// let app = App::new() let mut rt = actix_rt::Runtime::new().unwrap();
// .scope("/app", |scope| { let app = App::new()
// scope.nested("/t1", |scope| { .scope("/app", |scope| {
// scope scope.nested("/t1", |scope| {
// .filter(pred::Get()) scope
// .resource("/path1", |r| r.f(|_| HttpResponse::Ok())) .guard(guard::Get())
// }) .resource("/path1", |r| r.to(|| HttpResponse::Ok()))
// }) })
// .finish(); })
.into_new_service();
let mut srv = rt.block_on(app.new_service(&())).unwrap();
// let req = TestRequest::with_uri("/app/t1/path1") let req = TestRequest::with_uri("/app/t1/path1")
// .method(Method::POST) .method(Method::POST)
// .request(); .to_request();
// let resp = app.run(req); let resp = rt.block_on(srv.call(req)).unwrap();
// assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
// let req = TestRequest::with_uri("/app/t1/path1") let req = TestRequest::with_uri("/app/t1/path1")
// .method(Method::GET) .method(Method::GET)
// .request(); .to_request();
// let resp = app.run(req); let resp = rt.block_on(srv.call(req)).unwrap();
// assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
// } }
#[test] #[test]
fn test_nested_scope_with_variable_segment() { fn test_nested_scope_with_variable_segment() {

View file

@ -8,7 +8,7 @@ use actix_http::{
Error, Extensions, HttpMessage, Payload, Request, RequestHead, Response, Error, Extensions, HttpMessage, Payload, Request, RequestHead, Response,
ResponseHead, ResponseHead,
}; };
use actix_router::{Path, Url}; use actix_router::{Path, Resource, Url};
use futures::future::{ok, FutureResult, IntoFuture}; use futures::future::{ok, FutureResult, IntoFuture};
use crate::request::HttpRequest; 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> { impl<P> HttpMessage for ServiceRequest<P> {
type Stream = P; type Stream = P;