1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-01-19 06:04:40 +01:00

refactor Resource (#1883)

This commit is contained in:
fakeshadow 2021-01-09 11:36:58 +08:00 committed by GitHub
parent d40ae8c8ca
commit 530d03791d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,18 +1,18 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt; use std::fmt;
use std::future::Future; use std::future::Future;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::Poll;
use actix_http::{Error, Extensions, Response}; use actix_http::{Error, Extensions, Response};
use actix_router::IntoPattern; use actix_router::IntoPattern;
use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::boxed::{self, BoxService, BoxServiceFactory};
use actix_service::{ use actix_service::{
apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, apply, apply_fn_factory, fn_service, IntoServiceFactory, Service, ServiceFactory,
ServiceFactoryExt, Transform, ServiceFactoryExt, Transform,
}; };
use futures_core::future::LocalBoxFuture; use futures_core::future::LocalBoxFuture;
use futures_util::future::join_all;
use crate::data::Data; use crate::data::Data;
use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef}; use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef};
@ -20,7 +20,7 @@ use crate::extract::FromRequest;
use crate::guard::Guard; use crate::guard::Guard;
use crate::handler::Handler; use crate::handler::Handler;
use crate::responder::Responder; use crate::responder::Responder;
use crate::route::{CreateRouteService, Route, RouteService}; use crate::route::{Route, RouteService};
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>; type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>;
@ -53,9 +53,9 @@ pub struct Resource<T = ResourceEndpoint> {
rdef: Vec<String>, rdef: Vec<String>,
name: Option<String>, name: Option<String>,
routes: Vec<Route>, routes: Vec<Route>,
data: Option<Extensions>, app_data: Option<Extensions>,
guards: Vec<Box<dyn Guard>>, guards: Vec<Box<dyn Guard>>,
default: Rc<RefCell<Option<Rc<HttpNewService>>>>, default: HttpNewService,
factory_ref: Rc<RefCell<Option<ResourceFactory>>>, factory_ref: Rc<RefCell<Option<ResourceFactory>>>,
} }
@ -70,8 +70,10 @@ impl Resource {
endpoint: ResourceEndpoint::new(fref.clone()), endpoint: ResourceEndpoint::new(fref.clone()),
factory_ref: fref, factory_ref: fref,
guards: Vec::new(), guards: Vec::new(),
data: None, app_data: None,
default: Rc::new(RefCell::new(None)), default: boxed::factory(fn_service(|req: ServiceRequest| async {
Ok(req.into_response(Response::MethodNotAllowed().finish()))
})),
} }
} }
} }
@ -201,10 +203,10 @@ where
/// ///
/// Data of different types from parent contexts will still be accessible. /// Data of different types from parent contexts will still be accessible.
pub fn app_data<U: 'static>(mut self, data: U) -> Self { pub fn app_data<U: 'static>(mut self, data: U) -> Self {
if self.data.is_none() { if self.app_data.is_none() {
self.data = Some(Extensions::new()); self.app_data = Some(Extensions::new());
} }
self.data.as_mut().unwrap().insert(data); self.app_data.as_mut().unwrap().insert(data);
self self
} }
@ -274,7 +276,7 @@ where
guards: self.guards, guards: self.guards,
routes: self.routes, routes: self.routes,
default: self.default, default: self.default,
data: self.data, app_data: self.app_data,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
} }
} }
@ -336,7 +338,7 @@ where
guards: self.guards, guards: self.guards,
routes: self.routes, routes: self.routes,
default: self.default, default: self.default,
data: self.data, app_data: self.app_data,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
} }
} }
@ -356,11 +358,9 @@ where
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
// create and configure default resource // create and configure default resource
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory( self.default = boxed::factory(f.into_factory().map_init_err(|e| {
f.into_factory().map_init_err(|e| { log::error!("Can not construct default service: {:?}", e)
log::error!("Can not construct default service: {:?}", e) }));
}),
)))));
self self
} }
@ -391,7 +391,7 @@ where
*rdef.name_mut() = name.clone(); *rdef.name_mut() = name.clone();
} }
// custom app data storage // custom app data storage
if let Some(ref mut ext) = self.data { if let Some(ref mut ext) = self.app_data {
config.set_service_data(ext); config.set_service_data(ext);
} }
@ -412,7 +412,7 @@ where
fn into_factory(self) -> T { fn into_factory(self) -> T {
*self.factory_ref.borrow_mut() = Some(ResourceFactory { *self.factory_ref.borrow_mut() = Some(ResourceFactory {
routes: self.routes, routes: self.routes,
data: self.data.map(Rc::new), app_data: self.app_data.map(Rc::new),
default: self.default, default: self.default,
}); });
@ -422,8 +422,8 @@ where
pub struct ResourceFactory { pub struct ResourceFactory {
routes: Vec<Route>, routes: Vec<Route>,
data: Option<Rc<Extensions>>, app_data: Option<Rc<Extensions>>,
default: Rc<RefCell<Option<Rc<HttpNewService>>>>, default: HttpNewService,
} }
impl ServiceFactory<ServiceRequest> for ResourceFactory { impl ServiceFactory<ServiceRequest> for ResourceFactory {
@ -432,126 +432,60 @@ impl ServiceFactory<ServiceRequest> for ResourceFactory {
type Config = (); type Config = ();
type Service = ResourceService; type Service = ResourceService;
type InitError = (); type InitError = ();
type Future = CreateResourceService; type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
let default_fut = if let Some(ref default) = *self.default.borrow() { // construct default service factory future.
Some(default.new_service(())) let default_fut = self.default.new_service(());
} else {
None
};
CreateResourceService { // construct route service factory futures
fut: self let factory_fut =
.routes join_all(self.routes.iter().map(|route| route.new_service(())));
.iter()
.map(|route| CreateRouteServiceItem::Future(route.new_service(())))
.collect(),
data: self.data.clone(),
default: None,
default_fut,
}
}
}
enum CreateRouteServiceItem { let app_data = self.app_data.clone();
Future(CreateRouteService),
Service(RouteService),
}
pub struct CreateResourceService { Box::pin(async move {
fut: Vec<CreateRouteServiceItem>, let default = default_fut.await?;
data: Option<Rc<Extensions>>, let routes = factory_fut
default: Option<HttpService>, .await
default_fut: Option<LocalBoxFuture<'static, Result<HttpService, ()>>>, .into_iter()
} .collect::<Result<Vec<_>, _>>()?;
impl Future for CreateResourceService { Ok(ResourceService {
type Output = Result<ResourceService, ()>; app_data,
default,
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut done = true;
if let Some(ref mut fut) = self.default_fut {
match Pin::new(fut).poll(cx)? {
Poll::Ready(default) => self.default = Some(default),
Poll::Pending => done = false,
}
}
// poll http services
for item in &mut self.fut {
match item {
CreateRouteServiceItem::Future(ref mut fut) => match Pin::new(fut)
.poll(cx)?
{
Poll::Ready(route) => *item = CreateRouteServiceItem::Service(route),
Poll::Pending => {
done = false;
}
},
CreateRouteServiceItem::Service(_) => continue,
};
}
if done {
let routes = self
.fut
.drain(..)
.map(|item| match item {
CreateRouteServiceItem::Service(service) => service,
CreateRouteServiceItem::Future(_) => unreachable!(),
})
.collect();
Poll::Ready(Ok(ResourceService {
routes, routes,
data: self.data.clone(), })
default: self.default.take(), })
}))
} else {
Poll::Pending
}
} }
} }
pub struct ResourceService { pub struct ResourceService {
routes: Vec<RouteService>, routes: Vec<RouteService>,
data: Option<Rc<Extensions>>, app_data: Option<Rc<Extensions>>,
default: Option<HttpService>, default: HttpService,
} }
impl Service<ServiceRequest> for ResourceService { impl Service<ServiceRequest> for ResourceService {
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = LocalBoxFuture<'static, Result<ServiceResponse, Error>>; type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, mut req: ServiceRequest) -> Self::Future { fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
for route in self.routes.iter_mut() { for route in self.routes.iter_mut() {
if route.check(&mut req) { if route.check(&mut req) {
if let Some(ref data) = self.data { if let Some(ref app_data) = self.app_data {
req.add_data_container(data.clone()); req.add_data_container(app_data.clone());
} }
return route.call(req); return route.call(req);
} }
} }
if let Some(ref mut default) = self.default { if let Some(ref app_data) = self.app_data {
if let Some(ref data) = self.data { req.add_data_container(app_data.clone());
req.add_data_container(data.clone());
}
default.call(req)
} else {
let req = req.into_parts().0;
Box::pin(async {
Ok(ServiceResponse::new(
req,
Response::MethodNotAllowed().finish(),
))
})
} }
self.default.call(req)
} }
} }
@ -567,15 +501,15 @@ impl ResourceEndpoint {
} }
impl ServiceFactory<ServiceRequest> for ResourceEndpoint { impl ServiceFactory<ServiceRequest> for ResourceEndpoint {
type Config = ();
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type InitError = (); type Config = ();
type Service = ResourceService; type Service = ResourceService;
type Future = CreateResourceService; type InitError = ();
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
self.factory.borrow_mut().as_mut().unwrap().new_service(()) self.factory.borrow().as_ref().unwrap().new_service(())
} }
} }