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