mirror of https://github.com/fafhrd91/actix-web
replace services with 500 error when data_factory construction failed
This commit is contained in:
parent
c201c15f8c
commit
bb01b691b9
54
src/app.rs
54
src/app.rs
|
@ -11,11 +11,10 @@ use actix_service::{
|
||||||
apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, ServiceFactoryExt,
|
apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, ServiceFactoryExt,
|
||||||
Transform,
|
Transform,
|
||||||
};
|
};
|
||||||
use futures_util::future::FutureExt;
|
|
||||||
|
|
||||||
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
|
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
|
||||||
use crate::config::ServiceConfig;
|
use crate::config::ServiceConfig;
|
||||||
use crate::data::{Data, DataFactory, FnDataFactory};
|
use crate::data::{Data, FnDataFactory};
|
||||||
use crate::dev::ResourceDef;
|
use crate::dev::ResourceDef;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::resource::Resource;
|
use crate::resource::Resource;
|
||||||
|
@ -114,22 +113,19 @@ where
|
||||||
E: std::fmt::Debug,
|
E: std::fmt::Debug,
|
||||||
{
|
{
|
||||||
self.data_factories.push(Box::new(move || {
|
self.data_factories.push(Box::new(move || {
|
||||||
{
|
let fut = data();
|
||||||
let fut = data();
|
Box::pin(async move {
|
||||||
async move {
|
match fut.await {
|
||||||
match fut.await {
|
Err(e) => {
|
||||||
Err(e) => {
|
log::error!("Can not construct data instance: {:?}", e);
|
||||||
log::error!("Can not construct data instance: {:?}", e);
|
Err(())
|
||||||
Err(())
|
}
|
||||||
}
|
Ok(data) => {
|
||||||
Ok(data) => {
|
let data = Box::new(Data::new(data)) as _;
|
||||||
let data: Box<dyn DataFactory> = Box::new(Data::new(data));
|
Ok(data)
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
.boxed_local()
|
|
||||||
}));
|
}));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -453,7 +449,7 @@ where
|
||||||
fn into_factory(self) -> AppInit<T, B> {
|
fn into_factory(self) -> AppInit<T, B> {
|
||||||
AppInit {
|
AppInit {
|
||||||
async_data_factories: self.data_factories.into_boxed_slice().into(),
|
async_data_factories: self.data_factories.into_boxed_slice().into(),
|
||||||
endpoint: self.endpoint,
|
endpoint: Rc::new(self.endpoint),
|
||||||
services: Rc::new(RefCell::new(self.services)),
|
services: Rc::new(RefCell::new(self.services)),
|
||||||
external: RefCell::new(self.external),
|
external: RefCell::new(self.external),
|
||||||
default: self.default,
|
default: self.default,
|
||||||
|
@ -473,9 +469,7 @@ mod tests {
|
||||||
use crate::http::{header, HeaderValue, Method, StatusCode};
|
use crate::http::{header, HeaderValue, Method, StatusCode};
|
||||||
use crate::middleware::DefaultHeaders;
|
use crate::middleware::DefaultHeaders;
|
||||||
use crate::service::ServiceRequest;
|
use crate::service::ServiceRequest;
|
||||||
use crate::test::{
|
use crate::test::{call_service, init_service, read_body, TestRequest};
|
||||||
call_service, init_service, read_body, try_init_service, TestRequest,
|
|
||||||
};
|
|
||||||
use crate::{web, HttpRequest, HttpResponse};
|
use crate::{web, HttpRequest, HttpResponse};
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@ -546,13 +540,21 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_data_factory_errors() {
|
async fn test_data_factory_errors() {
|
||||||
let srv =
|
let mut srv = init_service(
|
||||||
try_init_service(App::new().data_factory(|| err::<u32, _>(())).service(
|
App::new().data_factory(|| err::<u32, _>(())).service(
|
||||||
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
web::resource("/")
|
||||||
))
|
.route(web::get().to(|_: web::Data<usize>| HttpResponse::Ok())),
|
||||||
.await;
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
assert!(srv.is_err());
|
let req = TestRequest::get().uri("/").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
|
||||||
|
let req = TestRequest::post().uri("/test").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
|
|
@ -26,14 +26,14 @@ type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Err
|
||||||
pub struct AppInit<T, B>
|
pub struct AppInit<T, B>
|
||||||
where
|
where
|
||||||
T: ServiceFactory<
|
T: ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config = (),
|
||||||
Response = ServiceResponse<B>,
|
Response = ServiceResponse<B>,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
> + 'static,
|
||||||
{
|
{
|
||||||
pub(crate) endpoint: T,
|
pub(crate) endpoint: Rc<T>,
|
||||||
pub(crate) extensions: RefCell<Option<Extensions>>,
|
pub(crate) extensions: RefCell<Option<Extensions>>,
|
||||||
pub(crate) async_data_factories: Rc<[FnDataFactory]>,
|
pub(crate) async_data_factories: Rc<[FnDataFactory]>,
|
||||||
pub(crate) services: Rc<RefCell<Vec<Box<dyn AppServiceFactory>>>>,
|
pub(crate) services: Rc<RefCell<Vec<Box<dyn AppServiceFactory>>>>,
|
||||||
|
@ -45,12 +45,12 @@ where
|
||||||
impl<T, B> ServiceFactory<Request> for AppInit<T, B>
|
impl<T, B> ServiceFactory<Request> for AppInit<T, B>
|
||||||
where
|
where
|
||||||
T: ServiceFactory<
|
T: ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config = (),
|
||||||
Response = ServiceResponse<B>,
|
Response = ServiceResponse<B>,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
> + 'static,
|
||||||
T::Future: 'static,
|
T::Future: 'static,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse<B>;
|
type Response = ServiceResponse<B>;
|
||||||
|
@ -77,38 +77,21 @@ where
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.for_each(|mut srv| srv.register(&mut config));
|
.for_each(|mut srv| srv.register(&mut config));
|
||||||
|
|
||||||
let mut rmap = ResourceMap::new(ResourceDef::new(""));
|
let (config, mut services) = config.into_services();
|
||||||
|
|
||||||
let (config, services) = config.into_services();
|
|
||||||
|
|
||||||
// complete pipeline creation.
|
|
||||||
*self.factory_ref.borrow_mut() = Some(AppRoutingFactory {
|
|
||||||
default,
|
|
||||||
services: services
|
|
||||||
.into_iter()
|
|
||||||
.map(|(mut rdef, srv, guards, nested)| {
|
|
||||||
rmap.add(&mut rdef, nested);
|
|
||||||
(rdef, srv, RefCell::new(guards))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_boxed_slice()
|
|
||||||
.into(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// external resources
|
|
||||||
for mut rdef in std::mem::take(&mut *self.external.borrow_mut()) {
|
|
||||||
rmap.add(&mut rdef, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
// complete ResourceMap tree creation
|
|
||||||
let rmap = Rc::new(rmap);
|
|
||||||
rmap.finish(rmap.clone());
|
|
||||||
|
|
||||||
// construct all async data factory futures
|
// construct all async data factory futures
|
||||||
let factory_futs = join_all(self.async_data_factories.iter().map(|f| f()));
|
let factory_futs = join_all(self.async_data_factories.iter().map(|f| f()));
|
||||||
|
|
||||||
// construct app service and middleware service factory future.
|
// clone factory_ref and endpoint to use them in async block.
|
||||||
let endpoint_fut = self.endpoint.new_service(());
|
let factory_ref = self.factory_ref.clone();
|
||||||
|
let endpoint = self.endpoint.clone();
|
||||||
|
|
||||||
|
// construct resource map. it would move to async block afterwards.
|
||||||
|
let mut rmap = ResourceMap::new(ResourceDef::new(""));
|
||||||
|
// add external resources
|
||||||
|
for mut rdef in std::mem::take(&mut *self.external.borrow_mut()) {
|
||||||
|
rmap.add(&mut rdef, None);
|
||||||
|
}
|
||||||
|
|
||||||
// take extensions or create new one as app data container.
|
// take extensions or create new one as app data container.
|
||||||
let mut app_data = self
|
let mut app_data = self
|
||||||
|
@ -118,15 +101,56 @@ where
|
||||||
.unwrap_or_else(Extensions::new);
|
.unwrap_or_else(Extensions::new);
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
// async data factories
|
// construct async data factories.
|
||||||
let async_data_factories = factory_futs
|
let res = factory_futs
|
||||||
.await
|
.await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>();
|
||||||
.map_err(|_| ())?;
|
|
||||||
|
|
||||||
// app service and middleware
|
let async_data_factories = match res {
|
||||||
let service = endpoint_fut.await?;
|
Ok(async_data_factories) => async_data_factories,
|
||||||
|
Err(_) => {
|
||||||
|
// error occur producing app_data.
|
||||||
|
// replace services with 500 error response
|
||||||
|
services = services
|
||||||
|
.into_iter()
|
||||||
|
.map(|(rdef, _, guards, nested)| {
|
||||||
|
let srv = boxed::factory(fn_service(
|
||||||
|
|req: ServiceRequest| async {
|
||||||
|
Ok(req.into_response(
|
||||||
|
Response::InternalServerError().finish(),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
));
|
||||||
|
(rdef, srv, guards, nested)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// throw away async_data.
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// complete pipeline creation.
|
||||||
|
*factory_ref.borrow_mut() = Some(AppRoutingFactory {
|
||||||
|
default,
|
||||||
|
services: services
|
||||||
|
.into_iter()
|
||||||
|
.map(|(mut rdef, srv, guards, nested)| {
|
||||||
|
rmap.add(&mut rdef, nested);
|
||||||
|
(rdef, srv, RefCell::new(guards))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_boxed_slice()
|
||||||
|
.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// complete ResourceMap tree creation
|
||||||
|
let rmap = Rc::new(rmap);
|
||||||
|
rmap.finish(rmap.clone());
|
||||||
|
|
||||||
|
// construct app service and middleware service.
|
||||||
|
let service = endpoint.new_service(()).await?;
|
||||||
|
|
||||||
// populate app data container from (async) data factories.
|
// populate app data container from (async) data factories.
|
||||||
async_data_factories.iter().for_each(|factory| {
|
async_data_factories.iter().for_each(|factory| {
|
||||||
|
|
Loading…
Reference in New Issue