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,
|
||||
Transform,
|
||||
};
|
||||
use futures_util::future::FutureExt;
|
||||
|
||||
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
|
||||
use crate::config::ServiceConfig;
|
||||
use crate::data::{Data, DataFactory, FnDataFactory};
|
||||
use crate::data::{Data, FnDataFactory};
|
||||
use crate::dev::ResourceDef;
|
||||
use crate::error::Error;
|
||||
use crate::resource::Resource;
|
||||
|
@ -114,22 +113,19 @@ where
|
|||
E: std::fmt::Debug,
|
||||
{
|
||||
self.data_factories.push(Box::new(move || {
|
||||
{
|
||||
let fut = data();
|
||||
async move {
|
||||
match fut.await {
|
||||
Err(e) => {
|
||||
log::error!("Can not construct data instance: {:?}", e);
|
||||
Err(())
|
||||
}
|
||||
Ok(data) => {
|
||||
let data: Box<dyn DataFactory> = Box::new(Data::new(data));
|
||||
Ok(data)
|
||||
}
|
||||
let fut = data();
|
||||
Box::pin(async move {
|
||||
match fut.await {
|
||||
Err(e) => {
|
||||
log::error!("Can not construct data instance: {:?}", e);
|
||||
Err(())
|
||||
}
|
||||
Ok(data) => {
|
||||
let data = Box::new(Data::new(data)) as _;
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
.boxed_local()
|
||||
})
|
||||
}));
|
||||
self
|
||||
}
|
||||
|
@ -453,7 +449,7 @@ where
|
|||
fn into_factory(self) -> AppInit<T, B> {
|
||||
AppInit {
|
||||
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)),
|
||||
external: RefCell::new(self.external),
|
||||
default: self.default,
|
||||
|
@ -473,9 +469,7 @@ mod tests {
|
|||
use crate::http::{header, HeaderValue, Method, StatusCode};
|
||||
use crate::middleware::DefaultHeaders;
|
||||
use crate::service::ServiceRequest;
|
||||
use crate::test::{
|
||||
call_service, init_service, read_body, try_init_service, TestRequest,
|
||||
};
|
||||
use crate::test::{call_service, init_service, read_body, TestRequest};
|
||||
use crate::{web, HttpRequest, HttpResponse};
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@ -546,13 +540,21 @@ mod tests {
|
|||
|
||||
#[actix_rt::test]
|
||||
async fn test_data_factory_errors() {
|
||||
let srv =
|
||||
try_init_service(App::new().data_factory(|| err::<u32, _>(())).service(
|
||||
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
||||
))
|
||||
.await;
|
||||
let mut srv = init_service(
|
||||
App::new().data_factory(|| err::<u32, _>(())).service(
|
||||
web::resource("/")
|
||||
.route(web::get().to(|_: web::Data<usize>| HttpResponse::Ok())),
|
||||
),
|
||||
)
|
||||
.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]
|
||||
|
|
|
@ -26,14 +26,14 @@ type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Err
|
|||
pub struct AppInit<T, B>
|
||||
where
|
||||
T: ServiceFactory<
|
||||
ServiceRequest,
|
||||
Config = (),
|
||||
Response = ServiceResponse<B>,
|
||||
Error = Error,
|
||||
InitError = (),
|
||||
>,
|
||||
ServiceRequest,
|
||||
Config = (),
|
||||
Response = ServiceResponse<B>,
|
||||
Error = Error,
|
||||
InitError = (),
|
||||
> + 'static,
|
||||
{
|
||||
pub(crate) endpoint: T,
|
||||
pub(crate) endpoint: Rc<T>,
|
||||
pub(crate) extensions: RefCell<Option<Extensions>>,
|
||||
pub(crate) async_data_factories: Rc<[FnDataFactory]>,
|
||||
pub(crate) services: Rc<RefCell<Vec<Box<dyn AppServiceFactory>>>>,
|
||||
|
@ -45,12 +45,12 @@ where
|
|||
impl<T, B> ServiceFactory<Request> for AppInit<T, B>
|
||||
where
|
||||
T: ServiceFactory<
|
||||
ServiceRequest,
|
||||
Config = (),
|
||||
Response = ServiceResponse<B>,
|
||||
Error = Error,
|
||||
InitError = (),
|
||||
>,
|
||||
ServiceRequest,
|
||||
Config = (),
|
||||
Response = ServiceResponse<B>,
|
||||
Error = Error,
|
||||
InitError = (),
|
||||
> + 'static,
|
||||
T::Future: 'static,
|
||||
{
|
||||
type Response = ServiceResponse<B>;
|
||||
|
@ -77,38 +77,21 @@ where
|
|||
.into_iter()
|
||||
.for_each(|mut srv| srv.register(&mut config));
|
||||
|
||||
let mut rmap = ResourceMap::new(ResourceDef::new(""));
|
||||
|
||||
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());
|
||||
let (config, mut services) = config.into_services();
|
||||
|
||||
// construct all async data factory futures
|
||||
let factory_futs = join_all(self.async_data_factories.iter().map(|f| f()));
|
||||
|
||||
// construct app service and middleware service factory future.
|
||||
let endpoint_fut = self.endpoint.new_service(());
|
||||
// clone factory_ref and endpoint to use them in async block.
|
||||
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.
|
||||
let mut app_data = self
|
||||
|
@ -118,15 +101,56 @@ where
|
|||
.unwrap_or_else(Extensions::new);
|
||||
|
||||
Box::pin(async move {
|
||||
// async data factories
|
||||
let async_data_factories = factory_futs
|
||||
// construct async data factories.
|
||||
let res = factory_futs
|
||||
.await
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| ())?;
|
||||
.collect::<Result<Vec<_>, _>>();
|
||||
|
||||
// app service and middleware
|
||||
let service = endpoint_fut.await?;
|
||||
let async_data_factories = match res {
|
||||
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.
|
||||
async_data_factories.iter().for_each(|factory| {
|
||||
|
|
Loading…
Reference in New Issue