1 use std::net::SocketAddr;
2 use std::rc::Rc;
3 
4 use actix_http::Extensions;
5 use actix_router::ResourceDef;
6 use actix_service::{boxed, IntoServiceFactory, ServiceFactory};
7 
8 use crate::data::Data;
9 use crate::error::Error;
10 use crate::guard::Guard;
11 use crate::resource::Resource;
12 use crate::rmap::ResourceMap;
13 use crate::route::Route;
14 use crate::service::{
15     AppServiceFactory, HttpServiceFactory, ServiceFactoryWrapper, ServiceRequest,
16     ServiceResponse,
17 };
18 
19 type Guards = Vec<Box<dyn Guard>>;
20 type HttpNewService = boxed::BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;
21 
22 /// Application configuration
23 pub struct AppService {
24     config: AppConfig,
25     root: bool,
26     default: Rc<HttpNewService>,
27     services: Vec<(
28         ResourceDef,
29         HttpNewService,
30         Option<Guards>,
31         Option<Rc<ResourceMap>>,
32     )>,
33 }
34 
35 impl AppService {
36     /// Crate server settings instance.
new(config: AppConfig, default: Rc<HttpNewService>) -> Self37     pub(crate) fn new(config: AppConfig, default: Rc<HttpNewService>) -> Self {
38         AppService {
39             config,
40             default,
41             root: true,
42             services: Vec::new(),
43         }
44     }
45 
46     /// Check if root is being configured
is_root(&self) -> bool47     pub fn is_root(&self) -> bool {
48         self.root
49     }
50 
into_services( self, ) -> ( AppConfig, Vec<( ResourceDef, HttpNewService, Option<Guards>, Option<Rc<ResourceMap>>, )>, )51     pub(crate) fn into_services(
52         self,
53     ) -> (
54         AppConfig,
55         Vec<(
56             ResourceDef,
57             HttpNewService,
58             Option<Guards>,
59             Option<Rc<ResourceMap>>,
60         )>,
61     ) {
62         (self.config, self.services)
63     }
64 
65     /// Clones inner config and default service, returning new `AppService` with empty service list
66     /// marked as non-root.
clone_config(&self) -> Self67     pub(crate) fn clone_config(&self) -> Self {
68         AppService {
69             config: self.config.clone(),
70             default: self.default.clone(),
71             services: Vec::new(),
72             root: false,
73         }
74     }
75 
76     /// Returns reference to configuration.
config(&self) -> &AppConfig77     pub fn config(&self) -> &AppConfig {
78         &self.config
79     }
80 
81     /// Returns default handler factory.
default_service(&self) -> Rc<HttpNewService>82     pub fn default_service(&self) -> Rc<HttpNewService> {
83         self.default.clone()
84     }
85 
86     /// Register HTTP service.
register_service<F, S>( &mut self, rdef: ResourceDef, guards: Option<Vec<Box<dyn Guard>>>, factory: F, nested: Option<Rc<ResourceMap>>, ) where F: IntoServiceFactory<S, ServiceRequest>, S: ServiceFactory< ServiceRequest, Response = ServiceResponse, Error = Error, Config = (), InitError = (), > + 'static,87     pub fn register_service<F, S>(
88         &mut self,
89         rdef: ResourceDef,
90         guards: Option<Vec<Box<dyn Guard>>>,
91         factory: F,
92         nested: Option<Rc<ResourceMap>>,
93     ) where
94         F: IntoServiceFactory<S, ServiceRequest>,
95         S: ServiceFactory<
96                 ServiceRequest,
97                 Response = ServiceResponse,
98                 Error = Error,
99                 Config = (),
100                 InitError = (),
101             > + 'static,
102     {
103         self.services
104             .push((rdef, boxed::factory(factory.into_factory()), guards, nested));
105     }
106 }
107 
108 /// Application connection config.
109 #[derive(Debug, Clone)]
110 pub struct AppConfig {
111     secure: bool,
112     host: String,
113     addr: SocketAddr,
114 }
115 
116 impl AppConfig {
new(secure: bool, host: String, addr: SocketAddr) -> Self117     pub(crate) fn new(secure: bool, host: String, addr: SocketAddr) -> Self {
118         AppConfig { secure, host, addr }
119     }
120 
121     /// Needed in actix-test crate. Semver exempt.
122     #[doc(hidden)]
__priv_test_new(secure: bool, host: String, addr: SocketAddr) -> Self123     pub fn __priv_test_new(secure: bool, host: String, addr: SocketAddr) -> Self {
124         AppConfig::new(secure, host, addr)
125     }
126 
127     /// Server host name.
128     ///
129     /// Host name is used by application router as a hostname for url generation.
130     /// Check [ConnectionInfo](super::dev::ConnectionInfo::host())
131     /// documentation for more information.
132     ///
133     /// By default host name is set to a "localhost" value.
host(&self) -> &str134     pub fn host(&self) -> &str {
135         &self.host
136     }
137 
138     /// Returns true if connection is secure(https)
secure(&self) -> bool139     pub fn secure(&self) -> bool {
140         self.secure
141     }
142 
143     /// Returns the socket address of the local half of this TCP connection
local_addr(&self) -> SocketAddr144     pub fn local_addr(&self) -> SocketAddr {
145         self.addr
146     }
147 
148     #[cfg(test)]
set_host(&mut self, host: &str)149     pub(crate) fn set_host(&mut self, host: &str) {
150         self.host = host.to_owned();
151     }
152 }
153 
154 impl Default for AppConfig {
default() -> Self155     fn default() -> Self {
156         AppConfig::new(
157             false,
158             "localhost:8080".to_owned(),
159             "127.0.0.1:8080".parse().unwrap(),
160         )
161     }
162 }
163 
164 /// Enables parts of app configuration to be declared separately from the app itself. Helpful for
165 /// modularizing large applications.
166 ///
167 /// Merge a `ServiceConfig` into an app using [`App::configure`](crate::App::configure). Scope and
168 /// resources services have similar methods.
169 ///
170 /// ```
171 /// use actix_web::{web, App, HttpResponse};
172 ///
173 /// // this function could be located in different module
174 /// fn config(cfg: &mut web::ServiceConfig) {
175 ///     cfg.service(web::resource("/test")
176 ///         .route(web::get().to(|| HttpResponse::Ok()))
177 ///         .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
178 ///     );
179 /// }
180 ///
181 /// // merge `/test` routes from config function to App
182 /// App::new().configure(config);
183 /// ```
184 pub struct ServiceConfig {
185     pub(crate) services: Vec<Box<dyn AppServiceFactory>>,
186     pub(crate) external: Vec<ResourceDef>,
187     pub(crate) app_data: Extensions,
188 }
189 
190 impl ServiceConfig {
new() -> Self191     pub(crate) fn new() -> Self {
192         Self {
193             services: Vec::new(),
194             external: Vec::new(),
195             app_data: Extensions::new(),
196         }
197     }
198 
199     /// Add shared app data item.
200     ///
201     /// Counterpart to [`App::data()`](crate::App::data).
202     #[deprecated(since = "4.0.0", note = "Use `.app_data(Data::new(val))` instead.")]
data<U: 'static>(&mut self, data: U) -> &mut Self203     pub fn data<U: 'static>(&mut self, data: U) -> &mut Self {
204         self.app_data(Data::new(data));
205         self
206     }
207 
208     /// Add arbitrary app data item.
209     ///
210     /// Counterpart to [`App::app_data()`](crate::App::app_data).
app_data<U: 'static>(&mut self, ext: U) -> &mut Self211     pub fn app_data<U: 'static>(&mut self, ext: U) -> &mut Self {
212         self.app_data.insert(ext);
213         self
214     }
215 
216     /// Configure route for a specific path.
217     ///
218     /// Counterpart to [`App::route()`](crate::App::route).
route(&mut self, path: &str, mut route: Route) -> &mut Self219     pub fn route(&mut self, path: &str, mut route: Route) -> &mut Self {
220         self.service(
221             Resource::new(path)
222                 .add_guards(route.take_guards())
223                 .route(route),
224         )
225     }
226 
227     /// Register HTTP service factory.
228     ///
229     /// Counterpart to [`App::service()`](crate::App::service).
service<F>(&mut self, factory: F) -> &mut Self where F: HttpServiceFactory + 'static,230     pub fn service<F>(&mut self, factory: F) -> &mut Self
231     where
232         F: HttpServiceFactory + 'static,
233     {
234         self.services
235             .push(Box::new(ServiceFactoryWrapper::new(factory)));
236         self
237     }
238 
239     /// Register an external resource.
240     ///
241     /// External resources are useful for URL generation purposes only and are never considered for
242     /// matching at request time. Calls to [`HttpRequest::url_for()`](crate::HttpRequest::url_for)
243     /// will work as expected.
244     ///
245     /// Counterpart to [`App::external_resource()`](crate::App::external_resource).
external_resource<N, U>(&mut self, name: N, url: U) -> &mut Self where N: AsRef<str>, U: AsRef<str>,246     pub fn external_resource<N, U>(&mut self, name: N, url: U) -> &mut Self
247     where
248         N: AsRef<str>,
249         U: AsRef<str>,
250     {
251         let mut rdef = ResourceDef::new(url.as_ref());
252         *rdef.name_mut() = name.as_ref().to_string();
253         self.external.push(rdef);
254         self
255     }
256 }
257 
258 #[cfg(test)]
259 mod tests {
260     use actix_service::Service;
261     use bytes::Bytes;
262 
263     use super::*;
264     use crate::http::{Method, StatusCode};
265     use crate::test::{call_service, init_service, read_body, TestRequest};
266     use crate::{web, App, HttpRequest, HttpResponse};
267 
268     // allow deprecated `ServiceConfig::data`
269     #[allow(deprecated)]
270     #[actix_rt::test]
test_data()271     async fn test_data() {
272         let cfg = |cfg: &mut ServiceConfig| {
273             cfg.data(10usize);
274             cfg.app_data(15u8);
275         };
276 
277         let srv = init_service(App::new().configure(cfg).service(web::resource("/").to(
278             |_: web::Data<usize>, req: HttpRequest| {
279                 assert_eq!(*req.app_data::<u8>().unwrap(), 15u8);
280                 HttpResponse::Ok()
281             },
282         )))
283         .await;
284         let req = TestRequest::default().to_request();
285         let resp = srv.call(req).await.unwrap();
286         assert_eq!(resp.status(), StatusCode::OK);
287     }
288 
289     // #[actix_rt::test]
290     // async fn test_data_factory() {
291     //     let cfg = |cfg: &mut ServiceConfig| {
292     //         cfg.data_factory(|| {
293     //             sleep(std::time::Duration::from_millis(50)).then(|_| {
294     //                 println!("READY");
295     //                 Ok::<_, ()>(10usize)
296     //             })
297     //         });
298     //     };
299 
300     //     let srv =
301     //         init_service(App::new().configure(cfg).service(
302     //             web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
303     //         ));
304     //     let req = TestRequest::default().to_request();
305     //     let resp = srv.call(req).await.unwrap();
306     //     assert_eq!(resp.status(), StatusCode::OK);
307 
308     //     let cfg2 = |cfg: &mut ServiceConfig| {
309     //         cfg.data_factory(|| Ok::<_, ()>(10u32));
310     //     };
311     //     let srv = init_service(
312     //         App::new()
313     //             .service(web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()))
314     //             .configure(cfg2),
315     //     );
316     //     let req = TestRequest::default().to_request();
317     //     let resp = srv.call(req).await.unwrap();
318     //     assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
319     // }
320 
321     #[actix_rt::test]
test_external_resource()322     async fn test_external_resource() {
323         let srv = init_service(
324             App::new()
325                 .configure(|cfg| {
326                     cfg.external_resource("youtube", "https://youtube.com/watch/{video_id}");
327                 })
328                 .route(
329                     "/test",
330                     web::get().to(|req: HttpRequest| {
331                         HttpResponse::Ok()
332                             .body(req.url_for("youtube", &["12345"]).unwrap().to_string())
333                     }),
334                 ),
335         )
336         .await;
337         let req = TestRequest::with_uri("/test").to_request();
338         let resp = call_service(&srv, req).await;
339         assert_eq!(resp.status(), StatusCode::OK);
340         let body = read_body(resp).await;
341         assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345"));
342     }
343 
344     #[actix_rt::test]
test_service()345     async fn test_service() {
346         let srv = init_service(App::new().configure(|cfg| {
347             cfg.service(web::resource("/test").route(web::get().to(HttpResponse::Created)))
348                 .route("/index.html", web::get().to(HttpResponse::Ok));
349         }))
350         .await;
351 
352         let req = TestRequest::with_uri("/test")
353             .method(Method::GET)
354             .to_request();
355         let resp = call_service(&srv, req).await;
356         assert_eq!(resp.status(), StatusCode::CREATED);
357 
358         let req = TestRequest::with_uri("/index.html")
359             .method(Method::GET)
360             .to_request();
361         let resp = call_service(&srv, req).await;
362         assert_eq!(resp.status(), StatusCode::OK);
363     }
364 }
365