actix_web/
app_service.rs

1use std::{cell::RefCell, mem, rc::Rc};
2
3use actix_http::Request;
4use actix_router::{Path, ResourceDef, Router, Url};
5use actix_service::{boxed, fn_service, Service, ServiceFactory};
6use futures_core::future::LocalBoxFuture;
7use futures_util::future::join_all;
8
9use crate::{
10    body::BoxBody,
11    config::{AppConfig, AppService},
12    data::FnDataFactory,
13    dev::Extensions,
14    guard::Guard,
15    request::{HttpRequest, HttpRequestPool},
16    rmap::ResourceMap,
17    service::{
18        AppServiceFactory, BoxedHttpService, BoxedHttpServiceFactory, ServiceRequest,
19        ServiceResponse,
20    },
21    Error, HttpResponse,
22};
23
24/// Service factory to convert [`Request`] to a [`ServiceRequest<S>`].
25///
26/// It also executes data factories.
27pub struct AppInit<T, B>
28where
29    T: ServiceFactory<
30        ServiceRequest,
31        Config = (),
32        Response = ServiceResponse<B>,
33        Error = Error,
34        InitError = (),
35    >,
36{
37    pub(crate) endpoint: T,
38    pub(crate) extensions: RefCell<Option<Extensions>>,
39    pub(crate) async_data_factories: Rc<[FnDataFactory]>,
40    pub(crate) services: Rc<RefCell<Vec<Box<dyn AppServiceFactory>>>>,
41    pub(crate) default: Option<Rc<BoxedHttpServiceFactory>>,
42    pub(crate) factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>,
43    pub(crate) external: RefCell<Vec<ResourceDef>>,
44}
45
46impl<T, B> ServiceFactory<Request> for AppInit<T, B>
47where
48    T: ServiceFactory<
49        ServiceRequest,
50        Config = (),
51        Response = ServiceResponse<B>,
52        Error = Error,
53        InitError = (),
54    >,
55    T::Future: 'static,
56{
57    type Response = ServiceResponse<B>;
58    type Error = T::Error;
59    type Config = AppConfig;
60    type Service = AppInitService<T::Service, B>;
61    type InitError = T::InitError;
62    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
63
64    fn new_service(&self, config: AppConfig) -> Self::Future {
65        // set AppService's default service to 404 NotFound
66        // if no user defined default service exists.
67        let default = self.default.clone().unwrap_or_else(|| {
68            Rc::new(boxed::factory(fn_service(|req: ServiceRequest| async {
69                Ok(req.into_response(HttpResponse::NotFound()))
70            })))
71        });
72
73        // create App config to pass to child services
74        let mut config = AppService::new(config, Rc::clone(&default));
75
76        // register services
77        mem::take(&mut *self.services.borrow_mut())
78            .into_iter()
79            .for_each(|mut srv| srv.register(&mut config));
80
81        let mut rmap = ResourceMap::new(ResourceDef::prefix(""));
82
83        let (config, services) = config.into_services();
84
85        // complete pipeline creation.
86        *self.factory_ref.borrow_mut() = Some(AppRoutingFactory {
87            default,
88            services: services
89                .into_iter()
90                .map(|(mut rdef, srv, guards, nested)| {
91                    rmap.add(&mut rdef, nested);
92                    (rdef, srv, RefCell::new(guards))
93                })
94                .collect::<Vec<_>>()
95                .into_boxed_slice()
96                .into(),
97        });
98
99        // external resources
100        for mut rdef in mem::take(&mut *self.external.borrow_mut()) {
101            rmap.add(&mut rdef, None);
102        }
103
104        // complete ResourceMap tree creation
105        let rmap = Rc::new(rmap);
106        ResourceMap::finish(&rmap);
107
108        // construct all async data factory futures
109        let factory_futs = join_all(self.async_data_factories.iter().map(|f| f()));
110
111        // construct app service and middleware service factory future.
112        let endpoint_fut = self.endpoint.new_service(());
113
114        // take extensions or create new one as app data container.
115        let mut app_data = self.extensions.borrow_mut().take().unwrap_or_default();
116
117        Box::pin(async move {
118            // async data factories
119            let async_data_factories = factory_futs
120                .await
121                .into_iter()
122                .collect::<Result<Vec<_>, _>>()
123                .map_err(|_| ())?;
124
125            // app service and middleware
126            let service = endpoint_fut.await?;
127
128            // populate app data container from (async) data factories.
129            for factory in &async_data_factories {
130                factory.create(&mut app_data);
131            }
132
133            Ok(AppInitService {
134                service,
135                app_data: Rc::new(app_data),
136                app_state: AppInitServiceState::new(rmap, config),
137            })
138        })
139    }
140}
141
142/// The [`Service`] that is passed to `actix-http`'s server builder.
143///
144/// Wraps a service receiving a [`ServiceRequest`] into one receiving a [`Request`].
145pub struct AppInitService<T, B>
146where
147    T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
148{
149    service: T,
150    app_data: Rc<Extensions>,
151    app_state: Rc<AppInitServiceState>,
152}
153
154/// A collection of state for [`AppInitService`] that is shared across [`HttpRequest`]s.
155pub(crate) struct AppInitServiceState {
156    rmap: Rc<ResourceMap>,
157    config: AppConfig,
158    pool: HttpRequestPool,
159}
160
161impl AppInitServiceState {
162    /// Constructs state collection from resource map and app config.
163    pub(crate) fn new(rmap: Rc<ResourceMap>, config: AppConfig) -> Rc<Self> {
164        Rc::new(AppInitServiceState {
165            rmap,
166            config,
167            pool: HttpRequestPool::default(),
168        })
169    }
170
171    /// Returns a reference to the application's resource map.
172    #[inline]
173    pub(crate) fn rmap(&self) -> &ResourceMap {
174        &self.rmap
175    }
176
177    /// Returns a reference to the application's configuration.
178    #[inline]
179    pub(crate) fn config(&self) -> &AppConfig {
180        &self.config
181    }
182
183    /// Returns a reference to the application's request pool.
184    #[inline]
185    pub(crate) fn pool(&self) -> &HttpRequestPool {
186        &self.pool
187    }
188}
189
190impl<T, B> Service<Request> for AppInitService<T, B>
191where
192    T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
193{
194    type Response = ServiceResponse<B>;
195    type Error = T::Error;
196    type Future = T::Future;
197
198    actix_service::forward_ready!(service);
199
200    fn call(&self, mut req: Request) -> Self::Future {
201        let extensions = Rc::new(RefCell::new(req.take_req_data()));
202        let conn_data = req.take_conn_data();
203        let (head, payload) = req.into_parts();
204
205        let req = match self.app_state.pool().pop() {
206            Some(mut req) => {
207                let inner = Rc::get_mut(&mut req.inner).unwrap();
208                inner.path.get_mut().update(&head.uri);
209                inner.path.reset();
210                inner.head = head;
211                inner.conn_data = conn_data;
212                inner.extensions = extensions;
213                req
214            }
215
216            None => HttpRequest::new(
217                Path::new(Url::new(head.uri.clone())),
218                head,
219                Rc::clone(&self.app_state),
220                Rc::clone(&self.app_data),
221                conn_data,
222                extensions,
223            ),
224        };
225
226        self.service.call(ServiceRequest::new(req, payload))
227    }
228}
229
230impl<T, B> Drop for AppInitService<T, B>
231where
232    T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
233{
234    fn drop(&mut self) {
235        self.app_state.pool().clear();
236    }
237}
238
239pub struct AppRoutingFactory {
240    #[allow(clippy::type_complexity)]
241    services: Rc<
242        [(
243            ResourceDef,
244            BoxedHttpServiceFactory,
245            RefCell<Option<Vec<Box<dyn Guard>>>>,
246        )],
247    >,
248    default: Rc<BoxedHttpServiceFactory>,
249}
250
251impl ServiceFactory<ServiceRequest> for AppRoutingFactory {
252    type Response = ServiceResponse;
253    type Error = Error;
254    type Config = ();
255    type Service = AppRouting;
256    type InitError = ();
257    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
258
259    fn new_service(&self, _: ()) -> Self::Future {
260        // construct all services factory future with its resource def and guards.
261        let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| {
262            let path = path.clone();
263            let guards = guards.borrow_mut().take().unwrap_or_default();
264            let factory_fut = factory.new_service(());
265            async move {
266                factory_fut
267                    .await
268                    .map(move |service| (path, guards, service))
269            }
270        }));
271
272        // construct default service factory future
273        let default_fut = self.default.new_service(());
274
275        Box::pin(async move {
276            let default = default_fut.await?;
277
278            // build router from the factory future result.
279            let router = factory_fut
280                .await
281                .into_iter()
282                .collect::<Result<Vec<_>, _>>()?
283                .drain(..)
284                .fold(Router::build(), |mut router, (path, guards, service)| {
285                    router.push(path, service, guards);
286                    router
287                })
288                .finish();
289
290            Ok(AppRouting { router, default })
291        })
292    }
293}
294
295/// The Actix Web router default entry point.
296pub struct AppRouting {
297    router: Router<BoxedHttpService, Vec<Box<dyn Guard>>>,
298    default: BoxedHttpService,
299}
300
301impl Service<ServiceRequest> for AppRouting {
302    type Response = ServiceResponse<BoxBody>;
303    type Error = Error;
304    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
305
306    actix_service::always_ready!();
307
308    fn call(&self, mut req: ServiceRequest) -> Self::Future {
309        let res = self.router.recognize_fn(&mut req, |req, guards| {
310            let guard_ctx = req.guard_ctx();
311            guards.iter().all(|guard| guard.check(&guard_ctx))
312        });
313
314        if let Some((srv, _info)) = res {
315            srv.call(req)
316        } else {
317            self.default.call(req)
318        }
319    }
320}
321
322/// Wrapper service for routing
323pub struct AppEntry {
324    factory: Rc<RefCell<Option<AppRoutingFactory>>>,
325}
326
327impl AppEntry {
328    pub fn new(factory: Rc<RefCell<Option<AppRoutingFactory>>>) -> Self {
329        AppEntry { factory }
330    }
331}
332
333impl ServiceFactory<ServiceRequest> for AppEntry {
334    type Response = ServiceResponse;
335    type Error = Error;
336    type Config = ();
337    type Service = AppRouting;
338    type InitError = ();
339    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
340
341    fn new_service(&self, _: ()) -> Self::Future {
342        self.factory.borrow_mut().as_mut().unwrap().new_service(())
343    }
344}
345
346#[cfg(test)]
347mod tests {
348    use std::sync::{
349        atomic::{AtomicBool, Ordering},
350        Arc,
351    };
352
353    use actix_service::Service;
354
355    use crate::{
356        test::{init_service, TestRequest},
357        web, App, HttpResponse,
358    };
359
360    struct DropData(Arc<AtomicBool>);
361
362    impl Drop for DropData {
363        fn drop(&mut self) {
364            self.0.store(true, Ordering::Relaxed);
365        }
366    }
367
368    // allow deprecated App::data
369    #[allow(deprecated)]
370    #[actix_rt::test]
371    async fn test_drop_data() {
372        let data = Arc::new(AtomicBool::new(false));
373
374        {
375            let app = init_service(
376                App::new()
377                    .data(DropData(data.clone()))
378                    .service(web::resource("/test").to(HttpResponse::Ok)),
379            )
380            .await;
381            let req = TestRequest::with_uri("/test").to_request();
382            let _ = app.call(req).await.unwrap();
383        }
384        assert!(data.load(Ordering::Relaxed));
385    }
386}