1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-23 16:21:06 +01:00

Support asynchronous data factories #850

This commit is contained in:
Nikolay Kim 2019-06-28 10:43:52 +06:00
parent c0c71f82c0
commit af9fb5d190
3 changed files with 88 additions and 31 deletions

View File

@ -2,10 +2,15 @@
## [1.0.3] - unreleased
### Added
* Support asynchronous data factories #850
### Changed
* Use `encoding_rs` crate instead of unmaintained `encoding` crate
## [1.0.2] - 2019-06-17
### Changed
@ -17,7 +22,7 @@
## [1.0.1] - 2019-06-17
### Add
### Added
* Add support for PathConfig #903
@ -42,7 +47,7 @@
## [1.0.0] - 2019-06-05
### Add
### Added
* Add `Scope::configure()` method.

View File

@ -8,7 +8,7 @@ use actix_service::boxed::{self, BoxedNewService};
use actix_service::{
apply_transform, IntoNewService, IntoTransform, NewService, Transform,
};
use futures::IntoFuture;
use futures::{Future, IntoFuture};
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
use crate::config::{AppConfig, AppConfigInner, ServiceConfig};
@ -23,6 +23,7 @@ use crate::service::{
};
type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>;
type FnDataFactory = Box<Fn() -> Box<dyn Future<Item = Box<DataFactory>, Error = ()>>>;
/// Application builder - structure that follows the builder pattern
/// for building application instances.
@ -32,6 +33,7 @@ pub struct App<T, B> {
default: Option<Rc<HttpNewService>>,
factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>,
data: Vec<Box<DataFactory>>,
data_factories: Vec<FnDataFactory>,
config: AppConfigInner,
external: Vec<ResourceDef>,
_t: PhantomData<(B)>,
@ -44,6 +46,7 @@ impl App<AppEntry, Body> {
App {
endpoint: AppEntry::new(fref.clone()),
data: Vec::new(),
data_factories: Vec::new(),
services: Vec::new(),
default: None,
factory_ref: fref,
@ -100,6 +103,31 @@ where
self
}
/// Set application data factory. This function is
/// similar to `.data()` but it accepts data factory. Data object get
/// constructed asynchronously during application initialization.
pub fn data_factory<F, Out>(mut self, data: F) -> Self
where
F: Fn() -> Out + 'static,
Out: IntoFuture + 'static,
Out::Error: std::fmt::Debug,
{
self.data_factories.push(Box::new(move || {
Box::new(
data()
.into_future()
.map_err(|e| {
log::error!("Can not construct data instance: {:?}", e);
})
.map(|data| {
let data: Box<dyn DataFactory> = Box::new(Data::new(data));
data
}),
)
}));
self
}
/// Set application data. Application data could be accessed
/// by using `Data<T>` extractor where `T` is data type.
pub fn register_data<U: 'static>(mut self, data: Data<U>) -> Self {
@ -349,6 +377,7 @@ where
App {
endpoint,
data: self.data,
data_factories: self.data_factories,
services: self.services,
default: self.default,
factory_ref: self.factory_ref,
@ -423,6 +452,7 @@ where
fn into_new_service(self) -> AppInit<T, B> {
AppInit {
data: Rc::new(self.data),
data_factories: Rc::new(self.data_factories),
endpoint: self.endpoint,
services: Rc::new(RefCell::new(self.services)),
external: RefCell::new(self.external),
@ -490,24 +520,24 @@ mod tests {
assert_eq!(resp.status(), StatusCode::CREATED);
}
// #[test]
// fn test_data_factory() {
// let mut srv =
// init_service(App::new().data_factory(|| Ok::<_, ()>(10usize)).service(
// web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
// ));
// let req = TestRequest::default().to_request();
// let resp = block_on(srv.call(req)).unwrap();
// assert_eq!(resp.status(), StatusCode::OK);
#[test]
fn test_data_factory() {
let mut srv =
init_service(App::new().data_factory(|| Ok::<_, ()>(10usize)).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
// let mut srv =
// init_service(App::new().data_factory(|| Ok::<_, ()>(10u32)).service(
// web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
// ));
// let req = TestRequest::default().to_request();
// let resp = block_on(srv.call(req)).unwrap();
// assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
// }
let mut srv =
init_service(App::new().data_factory(|| Ok::<_, ()>(10u32)).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
fn md<S, B>(
req: ServiceRequest,

View File

@ -25,6 +25,7 @@ type BoxedResponse = Either<
FutureResult<ServiceResponse, Error>,
Box<Future<Item = ServiceResponse, Error = Error>>,
>;
type FnDataFactory = Box<Fn() -> Box<dyn Future<Item = Box<DataFactory>, Error = ()>>>;
/// Service factory to convert `Request` to a `ServiceRequest<S>`.
/// It also executes data factories.
@ -40,6 +41,7 @@ where
{
pub(crate) endpoint: T,
pub(crate) data: Rc<Vec<Box<DataFactory>>>,
pub(crate) data_factories: Rc<Vec<FnDataFactory>>,
pub(crate) config: RefCell<AppConfig>,
pub(crate) services: Rc<RefCell<Vec<Box<ServiceFactory>>>>,
pub(crate) default: Option<Rc<HttpNewService>>,
@ -119,16 +121,12 @@ where
let rmap = Rc::new(rmap);
rmap.finish(rmap.clone());
// create app data container
let mut data = Extensions::new();
for f in self.data.iter() {
f.create(&mut data);
}
AppInitResult {
endpoint: None,
endpoint_fut: self.endpoint.new_service(&()),
data: Rc::new(data),
data: self.data.clone(),
data_factories: Vec::new(),
data_factories_fut: self.data_factories.iter().map(|f| f()).collect(),
config,
rmap,
_t: PhantomData,
@ -144,7 +142,9 @@ where
endpoint_fut: T::Future,
rmap: Rc<ResourceMap>,
config: AppConfig,
data: Rc<Extensions>,
data: Rc<Vec<Box<DataFactory>>>,
data_factories: Vec<Box<DataFactory>>,
data_factories_fut: Vec<Box<dyn Future<Item = Box<DataFactory>, Error = ()>>>,
_t: PhantomData<B>,
}
@ -159,21 +159,43 @@ where
>,
{
type Item = AppInitService<T::Service, B>;
type Error = T::InitError;
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
// async data factories
let mut idx = 0;
while idx < self.data_factories_fut.len() {
match self.data_factories_fut[idx].poll()? {
Async::Ready(f) => {
self.data_factories.push(f);
self.data_factories_fut.remove(idx);
}
Async::NotReady => idx += 1,
}
}
if self.endpoint.is_none() {
if let Async::Ready(srv) = self.endpoint_fut.poll()? {
self.endpoint = Some(srv);
}
}
if self.endpoint.is_some() {
if self.endpoint.is_some() && self.data_factories_fut.is_empty() {
// create app data container
let mut data = Extensions::new();
for f in self.data.iter() {
f.create(&mut data);
}
for f in &self.data_factories {
f.create(&mut data);
}
Ok(Async::Ready(AppInitService {
service: self.endpoint.take().unwrap(),
rmap: self.rmap.clone(),
config: self.config.clone(),
data: self.data.clone(),
data: Rc::new(data),
pool: HttpRequestPool::create(),
}))
} else {