1 //! Application configuration and configuration parameter retrieval. 2 //! 3 //! This module implements configuration handling for Rocket. It implements the 4 //! parsing and interpretation of the `Rocket.toml` config file and 5 //! `ROCKET_{PARAM}` environment variables. It also allows libraries to access 6 //! user-configured values. 7 //! 8 //! ## Application Configuration 9 //! 10 //! ### Environments 11 //! 12 //! Rocket applications are always running in one of three environments: 13 //! 14 //! * development _or_ dev 15 //! * staging _or_ stage 16 //! * production _or_ prod 17 //! 18 //! Each environment can contain different configuration parameters. By default, 19 //! Rocket applications run in the **development** environment. The environment 20 //! can be changed via the `ROCKET_ENV` environment variable. For example, to 21 //! start a Rocket application in the **production** environment: 22 //! 23 //! ```sh 24 //! ROCKET_ENV=production ./target/release/rocket_app 25 //! ``` 26 //! 27 //! ### Configuration Parameters 28 //! 29 //! Each environments consists of several standard configuration parameters as 30 //! well as an arbitrary number of _extra_ configuration parameters, which are 31 //! not used by Rocket itself but can be used by external libraries. The 32 //! standard configuration parameters are: 33 //! 34 //! | name | type | description | examples | 35 //! |------------|----------------|-------------------------------------------------------------|----------------------------| 36 //! | address | string | ip address or host to listen on | `"localhost"`, `"1.2.3.4"` | 37 //! | port | integer | port number to listen on | `8000`, `80` | 38 //! | keep_alive | integer | keep-alive timeout in seconds | `0` (disable), `10` | 39 //! | workers | integer | number of concurrent thread workers | `36`, `512` | 40 //! | log | string | max log level: `"off"`, `"normal"`, `"debug"`, `"critical"` | `"off"`, `"normal"` | 41 //! | secret_key | 256-bit base64 | secret key for private cookies | `"8Xui8SI..."` (44 chars) | 42 //! | tls | table | tls config table with two keys (`certs`, `key`) | _see below_ | 43 //! | tls.certs | string | path to certificate chain in PEM format | `"private/cert.pem"` | 44 //! | tls.key | string | path to private key for `tls.certs` in PEM format | `"private/key.pem"` | 45 //! | limits | table | map from data type (string) to data limit (integer: bytes) | `{ forms = 65536 }` | 46 //! 47 //! ### Rocket.toml 48 //! 49 //! `Rocket.toml` is a Rocket application's configuration file. It can 50 //! optionally be used to specify the configuration parameters for each 51 //! environment. If it is not present, the default configuration parameters or 52 //! environment supplied parameters are used. 53 //! 54 //! The file must be a series of TOML tables, at most one for each environment, 55 //! and an optional "global" table, where each table contains key-value pairs 56 //! corresponding to configuration parameters for that environment. If a 57 //! configuration parameter is missing, the default value is used. The following 58 //! is a complete `Rocket.toml` file, where every standard configuration 59 //! parameter is specified with the default value: 60 //! 61 //! ```toml 62 //! [development] 63 //! address = "localhost" 64 //! port = 8000 65 //! workers = [number_of_cpus * 2] 66 //! keep_alive = 5 67 //! log = "normal" 68 //! secret_key = [randomly generated at launch] 69 //! limits = { forms = 32768 } 70 //! 71 //! [staging] 72 //! address = "0.0.0.0" 73 //! port = 8000 74 //! workers = [number_of_cpus * 2] 75 //! keep_alive = 5 76 //! log = "normal" 77 //! secret_key = [randomly generated at launch] 78 //! limits = { forms = 32768 } 79 //! 80 //! [production] 81 //! address = "0.0.0.0" 82 //! port = 8000 83 //! workers = [number_of_cpus * 2] 84 //! keep_alive = 5 85 //! log = "critical" 86 //! secret_key = [randomly generated at launch] 87 //! limits = { forms = 32768 } 88 //! ``` 89 //! 90 //! The `workers` and `secret_key` default parameters are computed by Rocket 91 //! automatically; the values above are not valid TOML syntax. When manually 92 //! specifying the number of workers, the value should be an integer: `workers = 93 //! 10`. When manually specifying the secret key, the value should a 256-bit 94 //! base64 encoded string. Such a string can be generated with the `openssl` 95 //! command line tool: `openssl rand -base64 32`. 96 //! 97 //! The "global" pseudo-environment can be used to set and/or override 98 //! configuration parameters globally. A parameter defined in a `[global]` table 99 //! sets, or overrides if already present, that parameter in every environment. 100 //! For example, given the following `Rocket.toml` file, the value of `address` 101 //! will be `"1.2.3.4"` in every environment: 102 //! 103 //! ```toml 104 //! [global] 105 //! address = "1.2.3.4" 106 //! 107 //! [development] 108 //! address = "localhost" 109 //! 110 //! [production] 111 //! address = "0.0.0.0" 112 //! ``` 113 //! 114 //! ### TLS Configuration 115 //! 116 //! TLS can be enabled by specifying the `tls.key` and `tls.certs` parameters. 117 //! Rocket must be compiled with the `tls` feature enabled for the parameters to 118 //! take effect. The recommended way to specify the parameters is via the 119 //! `global` environment: 120 //! 121 //! ```toml 122 //! [global.tls] 123 //! certs = "/path/to/certs.pem" 124 //! key = "/path/to/key.pem" 125 //! ``` 126 //! 127 //! ### Environment Variables 128 //! 129 //! All configuration parameters, including extras, can be overridden through 130 //! environment variables. To override the configuration parameter `{param}`, 131 //! use an environment variable named `ROCKET_{PARAM}`. For instance, to 132 //! override the "port" configuration parameter, you can run your application 133 //! with: 134 //! 135 //! ```sh 136 //! ROCKET_PORT=3721 ./your_application 137 //! ``` 138 //! 139 //! Environment variables take precedence over all other configuration methods: 140 //! if the variable is set, it will be used as the value for the parameter. 141 //! Variable values are parsed as if they were TOML syntax. As illustration, 142 //! consider the following examples: 143 //! 144 //! ```sh 145 //! ROCKET_INTEGER=1 146 //! ROCKET_FLOAT=3.14 147 //! ROCKET_STRING=Hello 148 //! ROCKET_STRING="Hello" 149 //! ROCKET_BOOL=true 150 //! ROCKET_ARRAY=[1,"b",3.14] 151 //! ROCKET_DICT={key="abc",val=123} 152 //! ``` 153 //! 154 //! ## Retrieving Configuration Parameters 155 //! 156 //! Configuration parameters for the currently active configuration environment 157 //! can be retrieved via the [`Rocket::config()`](crate::Rocket::config()) method 158 //! on `Rocket` and `get_` methods on [`Config`] structure. 159 //! 160 //! The retrivial of configuration parameters usually occurs at launch time via 161 //! a [launch fairing](crate::fairing::Fairing). If information about the 162 //! configuraiton is needed later in the program, an attach fairing can be used 163 //! to store the information as managed state. As an example of the latter, 164 //! consider the following short program which reads the `token` configuration 165 //! parameter and stores the value or a default in a `Token` managed state 166 //! value: 167 //! 168 //! ```rust 169 //! use rocket::fairing::AdHoc; 170 //! 171 //! struct Token(i64); 172 //! 173 //! fn main() { 174 //! rocket::ignite() 175 //! .attach(AdHoc::on_attach("Token Config", |rocket| { 176 //! println!("Adding token managed state from config..."); 177 //! let token_val = rocket.config().get_int("token").unwrap_or(-1); 178 //! Ok(rocket.manage(Token(token_val))) 179 //! })) 180 //! # ; 181 //! } 182 //! ``` 183 184 mod error; 185 mod environment; 186 mod config; 187 mod builder; 188 mod toml_ext; 189 mod custom_values; 190 191 use std::env; 192 use std::fs::File; 193 use std::collections::HashMap; 194 use std::io::Read; 195 use std::path::{Path, PathBuf}; 196 197 use toml; 198 199 pub use self::custom_values::Limits; 200 pub use toml::value::{Array, Table, Value, Datetime}; 201 pub use self::error::ConfigError; 202 pub use self::environment::Environment; 203 pub use self::config::Config; 204 pub use self::builder::ConfigBuilder; 205 pub use crate::logger::LoggingLevel; 206 pub(crate) use self::toml_ext::LoggedValue; 207 208 use self::Environment::*; 209 use self::environment::CONFIG_ENV; 210 use crate::logger::COLORS_ENV; 211 use self::toml_ext::parse_simple_toml_value; 212 use crate::http::uncased::uncased_eq; 213 214 const CONFIG_FILENAME: &str = "Rocket.toml"; 215 const GLOBAL_ENV_NAME: &str = "global"; 216 const ENV_VAR_PREFIX: &str = "ROCKET_"; 217 218 const CODEGEN_DEBUG_ENV: &str = "ROCKET_CODEGEN_DEBUG"; 219 const CONFIG_FILE_ENV: &str = "ROCKET_CONFIG_FILE"; 220 const PREHANDLED_VARS: [&str; 4] = [CODEGEN_DEBUG_ENV, CONFIG_FILE_ENV, CONFIG_ENV, COLORS_ENV]; 221 222 /// Wraps `std::result` with the error type of [`ConfigError`]. 223 pub type Result<T> = std::result::Result<T, ConfigError>; 224 225 /// Stores a "full" config, which is all `Config`s for every environment. 226 #[derive(Debug, PartialEq)] 227 pub(crate) struct FullConfig { 228 pub active_env: Environment, 229 config: HashMap<Environment, Config>, 230 } 231 232 impl FullConfig { 233 /// Read the configuration from the `Rocket.toml` file. The file is searched 234 /// for recursively up the tree, starting from the CWD. read_from(path: &Path) -> Result<FullConfig>235 pub fn read_from(path: &Path) -> Result<FullConfig> { 236 // Try to open the config file for reading. 237 let mut handle = File::open(path).map_err(|_| ConfigError::IoError)?; 238 239 // Read the configure file to a string for parsing. 240 let mut contents = String::new(); 241 handle.read_to_string(&mut contents).map_err(|_| ConfigError::IoError)?; 242 243 // Parse the config and return the result. 244 let mut config = FullConfig::parse(contents, path)?; 245 246 // Override any config values with those from the environment. 247 config.override_from_env()?; 248 249 Ok(config) 250 } 251 252 /// Return the default configuration for all environments and marks the 253 /// active environment (from `CONFIG_ENV`) as active. Overrides the defaults 254 /// with values from the `ROCKET_{PARAM}` environment variables. Doesn't 255 /// read any other sources. env_default() -> Result<FullConfig>256 pub fn env_default() -> Result<FullConfig> { 257 let mut config = Self::active_default_with_path(None)?; 258 config.override_from_env()?; 259 Ok(config) 260 } 261 262 /// Return the default configuration for all environments and marks the 263 /// active environment (from `CONFIG_ENV`) as active. This doesn't read 264 /// `filename`, nor any other config values from any source; it simply uses 265 /// `filename` to set up the config path property in the returned `Config`. active_default_with_path(path: Option<&Path>) -> Result<FullConfig>266 fn active_default_with_path(path: Option<&Path>) -> Result<FullConfig> { 267 let mut defaults = HashMap::new(); 268 if let Some(path) = path { 269 defaults.insert(Development, Config::default_from(Development, &path)?); 270 defaults.insert(Staging, Config::default_from(Staging, &path)?); 271 defaults.insert(Production, Config::default_from(Production, &path)?); 272 } else { 273 defaults.insert(Development, Config::default(Development)?); 274 defaults.insert(Staging, Config::default(Staging)?); 275 defaults.insert(Production, Config::default(Production)?); 276 } 277 278 Ok(FullConfig { 279 active_env: Environment::active()?, 280 config: defaults, 281 }) 282 } 283 284 /// Returns the path to the config file that should be parsed. 285 /// 286 /// If the environment variable `CONFIG_FILE_ENV` is set, that path is 287 /// assumed to be the config file. Assuming such a file exists, that path is 288 /// returned. If the file doesn't exist, an error is returned. 289 /// 290 /// If the variable isn't set, Iteratively search for `CONFIG_FILENAME` 291 /// starting at the current working directory and working up through its 292 /// parents. Returns the path to the discovered file. find_config_path() -> Result<PathBuf>293 fn find_config_path() -> Result<PathBuf> { 294 if let Some(path) = env::var_os(CONFIG_FILE_ENV) { 295 let config = Path::new(&path); 296 if config.metadata().map_or(false, |m| m.is_file()) { 297 return Ok(config.into()); 298 } else { 299 let msg = "The user-supplied config file does not exist."; 300 return Err(ConfigError::BadFilePath(config.into(), msg)); 301 } 302 } 303 304 let cwd = env::current_dir().map_err(|_| ConfigError::NotFound)?; 305 let mut current = cwd.as_path(); 306 307 loop { 308 let config = current.join(CONFIG_FILENAME); 309 if config.metadata().map_or(false, |m| m.is_file()) { 310 return Ok(config); 311 } 312 313 match current.parent() { 314 Some(p) => current = p, 315 None => break, 316 } 317 } 318 319 Err(ConfigError::NotFound) 320 } 321 322 #[inline] get_mut(&mut self, env: Environment) -> &mut Config323 fn get_mut(&mut self, env: Environment) -> &mut Config { 324 match self.config.get_mut(&env) { 325 Some(config) => config, 326 None => panic!("set(): {} config is missing.", env), 327 } 328 } 329 330 /// Set the configuration for the environment `env` to be the configuration 331 /// derived from the TOML table `kvs`. The environment must already exist in 332 /// `self`, otherwise this function panics. Any existing values are 333 /// overridden by those in `kvs`. set_from_table(&mut self, env: Environment, kvs: &Table) -> Result<()>334 fn set_from_table(&mut self, env: Environment, kvs: &Table) -> Result<()> { 335 for (key, value) in kvs { 336 self.get_mut(env).set_raw(key, value)?; 337 } 338 339 Ok(()) 340 } 341 342 /// Retrieves the `Config` for the environment `env`. 343 #[cfg(test)] get(&self, env: Environment) -> &Config344 pub fn get(&self, env: Environment) -> &Config { 345 match self.config.get(&env) { 346 Some(config) => config, 347 None => panic!("get(): {} config is missing.", env), 348 } 349 } 350 351 /// Retrieves the `Config` for the active environment. 352 #[cfg(test)] active(&self) -> &Config353 pub fn active(&self) -> &Config { 354 self.get(self.active_env) 355 } 356 357 /// Retrieves the `Config` for the active environment. take_active(mut self) -> Config358 pub fn take_active(mut self) -> Config { 359 self.config.remove(&self.active_env).expect("missing active config") 360 } 361 362 // Override all environments with values from env variables if present. override_from_env(&mut self) -> Result<()>363 fn override_from_env(&mut self) -> Result<()> { 364 for (key, val) in env::vars() { 365 if key.len() < ENV_VAR_PREFIX.len() { 366 continue 367 } else if !uncased_eq(&key[..ENV_VAR_PREFIX.len()], ENV_VAR_PREFIX) { 368 continue 369 } 370 371 // Skip environment variables that are handled elsewhere. 372 if PREHANDLED_VARS.iter().any(|var| uncased_eq(&key, var)) { 373 continue 374 } 375 376 // Parse the key and value and try to set the variable for all envs. 377 let key = key[ENV_VAR_PREFIX.len()..].to_lowercase(); 378 let toml_val = match parse_simple_toml_value(&val) { 379 Ok(val) => val, 380 Err(e) => return Err(ConfigError::BadEnvVal(key, val, e)) 381 }; 382 383 for env in &Environment::ALL { 384 match self.get_mut(*env).set_raw(&key, &toml_val) { 385 Err(ConfigError::BadType(_, exp, actual, _)) => { 386 let e = format!("expected {}, but found {}", exp, actual); 387 return Err(ConfigError::BadEnvVal(key, val, e)) 388 } 389 Err(e) => return Err(e), 390 Ok(_) => { /* move along */ } 391 } 392 } 393 } 394 395 Ok(()) 396 } 397 398 /// Parses the configuration from the Rocket.toml file. Also overrides any 399 /// values there with values from the environment. parse<S, P>(src: S, filename: P) -> Result<FullConfig> where S: Into<String>, P: AsRef<Path>400 fn parse<S, P>(src: S, filename: P) -> Result<FullConfig> 401 where S: Into<String>, P: AsRef<Path> 402 { 403 use self::ConfigError::ParseError; 404 405 // Parse the source as TOML, if possible. 406 let src = src.into(); 407 let path = filename.as_ref().to_path_buf(); 408 let table = match src.parse::<toml::Value>() { 409 Ok(toml::Value::Table(table)) => table, 410 Ok(value) => { 411 let err = format!("expected a table, found {}", value.type_str()); 412 return Err(ConfigError::ParseError(src, path, err, Some((1, 1)))); 413 } 414 Err(e) => return Err(ParseError(src, path, e.to_string(), e.line_col())) 415 }; 416 417 // Create a config with the defaults; set the env to the active one. 418 let mut config = FullConfig::active_default_with_path(Some(filename.as_ref()))?; 419 420 // Store all of the global overrides, if any, for later use. 421 let mut global = None; 422 423 // Parse the values from the TOML file. 424 for (entry, value) in table { 425 // Each environment must be a table. 426 let kv_pairs = match value.as_table() { 427 Some(table) => table, 428 None => return Err(ConfigError::BadType( 429 entry, "a table", value.type_str(), Some(path.clone()) 430 )) 431 }; 432 433 // Store the global table for later use and move on. 434 if entry.as_str() == GLOBAL_ENV_NAME { 435 global = Some(kv_pairs.clone()); 436 continue; 437 } 438 439 // This is not the global table. Parse the environment name from the 440 // table entry name and then set all of the key/values. 441 match entry.as_str().parse() { 442 Ok(env) => config.set_from_table(env, kv_pairs)?, 443 Err(_) => Err(ConfigError::BadEntry(entry.clone(), path.clone()))? 444 } 445 } 446 447 // Override all of the environments with the global values. 448 if let Some(ref global_kv_pairs) = global { 449 for env in &Environment::ALL { 450 config.set_from_table(*env, global_kv_pairs)?; 451 } 452 } 453 454 Ok(config) 455 } 456 } 457 458 #[cfg(test)] 459 mod test { 460 use std::env; 461 use std::sync::Mutex; 462 463 use super::{FullConfig, ConfigError, ConfigBuilder}; 464 use super::{Environment, GLOBAL_ENV_NAME}; 465 use super::environment::CONFIG_ENV; 466 use super::Environment::*; 467 use super::Result; 468 469 use crate::logger::LoggingLevel; 470 471 const TEST_CONFIG_FILENAME: &'static str = "/tmp/testing/Rocket.toml"; 472 473 // TODO: It's a shame we have to depend on lazy_static just for this. 474 lazy_static::lazy_static! { 475 static ref ENV_LOCK: Mutex<usize> = Mutex::new(0); 476 } 477 478 macro_rules! check_config { 479 ($rconfig:expr, $econfig:expr) => ( 480 let expected = $econfig.finalize().unwrap(); 481 match $rconfig { 482 Ok(config) => assert_eq!(config.active(), &expected), 483 Err(e) => panic!("Config {} failed: {:?}", stringify!($rconfig), e) 484 } 485 ); 486 487 ($env:expr, $rconfig:expr, $econfig:expr) => ( 488 let expected = $econfig.finalize().unwrap(); 489 match $rconfig { 490 Ok(ref config) => assert_eq!(config.get($env), &expected), 491 Err(ref e) => panic!("Config {} failed: {:?}", stringify!($rconfig), e) 492 } 493 ); 494 } 495 env_default() -> Result<FullConfig>496 fn env_default() -> Result<FullConfig> { 497 FullConfig::env_default() 498 } 499 default_config(env: Environment) -> ConfigBuilder500 fn default_config(env: Environment) -> ConfigBuilder { 501 ConfigBuilder::new(env) 502 } 503 504 #[test] test_defaults()505 fn test_defaults() { 506 // Take the lock so changing the environment doesn't cause races. 507 let _env_lock = ENV_LOCK.lock().unwrap(); 508 509 // First, without an environment. Should get development defaults on 510 // debug builds and productions defaults on non-debug builds. 511 env::remove_var(CONFIG_ENV); 512 #[cfg(debug_assertions)] check_config!(env_default(), default_config(Development)); 513 #[cfg(not(debug_assertions))] check_config!(env_default(), default_config(Production)); 514 515 // Now with an explicit dev environment. 516 for env in &["development", "dev"] { 517 env::set_var(CONFIG_ENV, env); 518 check_config!(env_default(), default_config(Development)); 519 } 520 521 // Now staging. 522 for env in &["stage", "staging"] { 523 env::set_var(CONFIG_ENV, env); 524 check_config!(env_default(), default_config(Staging)); 525 } 526 527 // Finally, production. 528 for env in &["prod", "production"] { 529 env::set_var(CONFIG_ENV, env); 530 check_config!(env_default(), default_config(Production)); 531 } 532 } 533 534 #[test] test_bad_environment_vars()535 fn test_bad_environment_vars() { 536 // Take the lock so changing the environment doesn't cause races. 537 let _env_lock = ENV_LOCK.lock().unwrap(); 538 539 for env in &["", "p", "pr", "pro", "prodo", " prod", "dev ", "!dev!", " "] { 540 env::set_var(CONFIG_ENV, env); 541 let err = ConfigError::BadEnv(env.to_string()); 542 assert!(env_default().err().map_or(false, |e| e == err)); 543 } 544 545 // Test that a bunch of invalid environment names give the right error. 546 env::remove_var(CONFIG_ENV); 547 for env in &["p", "pr", "pro", "prodo", "bad", "meow", "this", "that"] { 548 let toml_table = format!("[{}]\n", env); 549 let e_str = env.to_string(); 550 let err = ConfigError::BadEntry(e_str, TEST_CONFIG_FILENAME.into()); 551 assert!(FullConfig::parse(toml_table, TEST_CONFIG_FILENAME) 552 .err().map_or(false, |e| e == err)); 553 } 554 } 555 556 #[test] test_good_full_config_files()557 fn test_good_full_config_files() { 558 // Take the lock so changing the environment doesn't cause races. 559 let _env_lock = ENV_LOCK.lock().unwrap(); 560 env::remove_var(CONFIG_ENV); 561 562 let config_str = r#" 563 address = "1.2.3.4" 564 port = 7810 565 workers = 21 566 log = "critical" 567 keep_alive = 0 568 secret_key = "8Xui8SN4mI+7egV/9dlfYYLGQJeEx4+DwmSQLwDVXJg=" 569 template_dir = "mine" 570 json = true 571 pi = 3.14 572 "#; 573 574 let mut expected = default_config(Development) 575 .address("1.2.3.4") 576 .port(7810) 577 .workers(21) 578 .log_level(LoggingLevel::Critical) 579 .keep_alive(0) 580 .secret_key("8Xui8SN4mI+7egV/9dlfYYLGQJeEx4+DwmSQLwDVXJg=") 581 .extra("template_dir", "mine") 582 .extra("json", true) 583 .extra("pi", 3.14); 584 585 expected.environment = Development; 586 let dev_config = ["[dev]", config_str].join("\n"); 587 let parsed = FullConfig::parse(dev_config, TEST_CONFIG_FILENAME); 588 check_config!(Development, parsed, expected.clone()); 589 check_config!(Staging, parsed, default_config(Staging)); 590 check_config!(Production, parsed, default_config(Production)); 591 592 expected.environment = Staging; 593 let stage_config = ["[stage]", config_str].join("\n"); 594 let parsed = FullConfig::parse(stage_config, TEST_CONFIG_FILENAME); 595 check_config!(Staging, parsed, expected.clone()); 596 check_config!(Development, parsed, default_config(Development)); 597 check_config!(Production, parsed, default_config(Production)); 598 599 expected.environment = Production; 600 let prod_config = ["[prod]", config_str].join("\n"); 601 let parsed = FullConfig::parse(prod_config, TEST_CONFIG_FILENAME); 602 check_config!(Production, parsed, expected); 603 check_config!(Development, parsed, default_config(Development)); 604 check_config!(Staging, parsed, default_config(Staging)); 605 } 606 607 #[test] test_good_address_values()608 fn test_good_address_values() { 609 // Take the lock so changing the environment doesn't cause races. 610 let _env_lock = ENV_LOCK.lock().unwrap(); 611 env::set_var(CONFIG_ENV, "dev"); 612 613 check_config!(FullConfig::parse(r#" 614 [development] 615 address = "localhost" 616 "#.to_string(), TEST_CONFIG_FILENAME), { 617 default_config(Development).address("localhost") 618 }); 619 620 check_config!(FullConfig::parse(r#" 621 [development] 622 address = "127.0.0.1" 623 "#.to_string(), TEST_CONFIG_FILENAME), { 624 default_config(Development).address("127.0.0.1") 625 }); 626 627 check_config!(FullConfig::parse(r#" 628 [development] 629 address = "::" 630 "#.to_string(), TEST_CONFIG_FILENAME), { 631 default_config(Development).address("::") 632 }); 633 634 check_config!(FullConfig::parse(r#" 635 [dev] 636 address = "2001:db8::370:7334" 637 "#.to_string(), TEST_CONFIG_FILENAME), { 638 default_config(Development).address("2001:db8::370:7334") 639 }); 640 641 check_config!(FullConfig::parse(r#" 642 [dev] 643 address = "0.0.0.0" 644 "#.to_string(), TEST_CONFIG_FILENAME), { 645 default_config(Development).address("0.0.0.0") 646 }); 647 } 648 649 #[test] test_bad_address_values()650 fn test_bad_address_values() { 651 // Take the lock so changing the environment doesn't cause races. 652 let _env_lock = ENV_LOCK.lock().unwrap(); 653 env::remove_var(CONFIG_ENV); 654 655 assert!(FullConfig::parse(r#" 656 [development] 657 address = 0000 658 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 659 660 assert!(FullConfig::parse(r#" 661 [development] 662 address = true 663 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 664 665 assert!(FullConfig::parse(r#" 666 [development] 667 address = "........" 668 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 669 670 assert!(FullConfig::parse(r#" 671 [staging] 672 address = "1.2.3.4:100" 673 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 674 } 675 676 // Only do this test when the tls feature is disabled since the file paths 677 // we're supplying don't actually exist. 678 #[test] test_good_tls_values()679 fn test_good_tls_values() { 680 // Take the lock so changing the environment doesn't cause races. 681 let _env_lock = ENV_LOCK.lock().unwrap(); 682 env::set_var(CONFIG_ENV, "dev"); 683 684 assert!(FullConfig::parse(r#" 685 [staging] 686 tls = { certs = "some/path.pem", key = "some/key.pem" } 687 "#.to_string(), TEST_CONFIG_FILENAME).is_ok()); 688 689 assert!(FullConfig::parse(r#" 690 [staging.tls] 691 certs = "some/path.pem" 692 key = "some/key.pem" 693 "#.to_string(), TEST_CONFIG_FILENAME).is_ok()); 694 695 assert!(FullConfig::parse(r#" 696 [global.tls] 697 certs = "some/path.pem" 698 key = "some/key.pem" 699 "#.to_string(), TEST_CONFIG_FILENAME).is_ok()); 700 701 assert!(FullConfig::parse(r#" 702 [global] 703 tls = { certs = "some/path.pem", key = "some/key.pem" } 704 "#.to_string(), TEST_CONFIG_FILENAME).is_ok()); 705 } 706 707 #[test] test_bad_tls_config()708 fn test_bad_tls_config() { 709 // Take the lock so changing the environment doesn't cause races. 710 let _env_lock = ENV_LOCK.lock().unwrap(); 711 env::remove_var(CONFIG_ENV); 712 713 assert!(FullConfig::parse(r#" 714 [development] 715 tls = "hello" 716 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 717 718 assert!(FullConfig::parse(r#" 719 [development] 720 tls = { certs = "some/path.pem" } 721 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 722 723 assert!(FullConfig::parse(r#" 724 [development] 725 tls = { certs = "some/path.pem", key = "some/key.pem", extra = "bah" } 726 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 727 728 assert!(FullConfig::parse(r#" 729 [staging] 730 tls = { cert = "some/path.pem", key = "some/key.pem" } 731 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 732 } 733 734 #[test] test_good_port_values()735 fn test_good_port_values() { 736 // Take the lock so changing the environment doesn't cause races. 737 let _env_lock = ENV_LOCK.lock().unwrap(); 738 env::set_var(CONFIG_ENV, "stage"); 739 740 check_config!(FullConfig::parse(r#" 741 [stage] 742 port = 100 743 "#.to_string(), TEST_CONFIG_FILENAME), { 744 default_config(Staging).port(100) 745 }); 746 747 check_config!(FullConfig::parse(r#" 748 [stage] 749 port = 6000 750 "#.to_string(), TEST_CONFIG_FILENAME), { 751 default_config(Staging).port(6000) 752 }); 753 754 check_config!(FullConfig::parse(r#" 755 [stage] 756 port = 65535 757 "#.to_string(), TEST_CONFIG_FILENAME), { 758 default_config(Staging).port(65535) 759 }); 760 } 761 762 #[test] test_bad_port_values()763 fn test_bad_port_values() { 764 // Take the lock so changing the environment doesn't cause races. 765 let _env_lock = ENV_LOCK.lock().unwrap(); 766 env::remove_var(CONFIG_ENV); 767 768 assert!(FullConfig::parse(r#" 769 [development] 770 port = true 771 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 772 773 assert!(FullConfig::parse(r#" 774 [production] 775 port = "hello" 776 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 777 778 assert!(FullConfig::parse(r#" 779 [staging] 780 port = -1 781 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 782 783 assert!(FullConfig::parse(r#" 784 [staging] 785 port = 65536 786 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 787 788 assert!(FullConfig::parse(r#" 789 [staging] 790 port = 105836 791 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 792 } 793 794 #[test] test_good_workers_values()795 fn test_good_workers_values() { 796 // Take the lock so changing the environment doesn't cause races. 797 let _env_lock = ENV_LOCK.lock().unwrap(); 798 env::set_var(CONFIG_ENV, "stage"); 799 800 check_config!(FullConfig::parse(r#" 801 [stage] 802 workers = 1 803 "#.to_string(), TEST_CONFIG_FILENAME), { 804 default_config(Staging).workers(1) 805 }); 806 807 check_config!(FullConfig::parse(r#" 808 [stage] 809 workers = 300 810 "#.to_string(), TEST_CONFIG_FILENAME), { 811 default_config(Staging).workers(300) 812 }); 813 814 check_config!(FullConfig::parse(r#" 815 [stage] 816 workers = 65535 817 "#.to_string(), TEST_CONFIG_FILENAME), { 818 default_config(Staging).workers(65535) 819 }); 820 } 821 822 #[test] test_bad_workers_values()823 fn test_bad_workers_values() { 824 // Take the lock so changing the environment doesn't cause races. 825 let _env_lock = ENV_LOCK.lock().unwrap(); 826 env::remove_var(CONFIG_ENV); 827 828 assert!(FullConfig::parse(r#" 829 [development] 830 workers = true 831 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 832 833 assert!(FullConfig::parse(r#" 834 [production] 835 workers = "hello" 836 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 837 838 assert!(FullConfig::parse(r#" 839 [staging] 840 workers = -1 841 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 842 843 assert!(FullConfig::parse(r#" 844 [staging] 845 workers = 65536 846 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 847 848 assert!(FullConfig::parse(r#" 849 [staging] 850 workers = 105836 851 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 852 } 853 854 #[test] test_good_keep_alives()855 fn test_good_keep_alives() { 856 // Take the lock so changing the environment doesn't cause races. 857 let _env_lock = ENV_LOCK.lock().unwrap(); 858 env::set_var(CONFIG_ENV, "stage"); 859 860 check_config!(FullConfig::parse(r#" 861 [stage] 862 keep_alive = 10 863 "#.to_string(), TEST_CONFIG_FILENAME), { 864 default_config(Staging).keep_alive(10) 865 }); 866 867 check_config!(FullConfig::parse(r#" 868 [stage] 869 keep_alive = 0 870 "#.to_string(), TEST_CONFIG_FILENAME), { 871 default_config(Staging).keep_alive(0) 872 }); 873 874 check_config!(FullConfig::parse(r#" 875 [stage] 876 keep_alive = 348 877 "#.to_string(), TEST_CONFIG_FILENAME), { 878 default_config(Staging).keep_alive(348) 879 }); 880 881 check_config!(FullConfig::parse(r#" 882 [stage] 883 keep_alive = 0 884 "#.to_string(), TEST_CONFIG_FILENAME), { 885 default_config(Staging).keep_alive(0) 886 }); 887 } 888 889 #[test] test_bad_keep_alives()890 fn test_bad_keep_alives() { 891 // Take the lock so changing the environment doesn't cause races. 892 let _env_lock = ENV_LOCK.lock().unwrap(); 893 env::remove_var(CONFIG_ENV); 894 895 assert!(FullConfig::parse(r#" 896 [dev] 897 keep_alive = true 898 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 899 900 assert!(FullConfig::parse(r#" 901 [dev] 902 keep_alive = -10 903 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 904 905 assert!(FullConfig::parse(r#" 906 [dev] 907 keep_alive = "Some(10)" 908 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 909 910 assert!(FullConfig::parse(r#" 911 [dev] 912 keep_alive = 4294967296 913 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 914 } 915 916 #[test] test_good_log_levels()917 fn test_good_log_levels() { 918 // Take the lock so changing the environment doesn't cause races. 919 let _env_lock = ENV_LOCK.lock().unwrap(); 920 env::set_var(CONFIG_ENV, "stage"); 921 922 check_config!(FullConfig::parse(r#" 923 [stage] 924 log = "normal" 925 "#.to_string(), TEST_CONFIG_FILENAME), { 926 default_config(Staging).log_level(LoggingLevel::Normal) 927 }); 928 929 930 check_config!(FullConfig::parse(r#" 931 [stage] 932 log = "debug" 933 "#.to_string(), TEST_CONFIG_FILENAME), { 934 default_config(Staging).log_level(LoggingLevel::Debug) 935 }); 936 937 check_config!(FullConfig::parse(r#" 938 [stage] 939 log = "critical" 940 "#.to_string(), TEST_CONFIG_FILENAME), { 941 default_config(Staging).log_level(LoggingLevel::Critical) 942 }); 943 944 check_config!(FullConfig::parse(r#" 945 [stage] 946 log = "off" 947 "#.to_string(), TEST_CONFIG_FILENAME), { 948 default_config(Staging).log_level(LoggingLevel::Off) 949 }); 950 } 951 952 #[test] test_bad_log_level_values()953 fn test_bad_log_level_values() { 954 // Take the lock so changing the environment doesn't cause races. 955 let _env_lock = ENV_LOCK.lock().unwrap(); 956 env::remove_var(CONFIG_ENV); 957 958 assert!(FullConfig::parse(r#" 959 [dev] 960 log = false 961 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 962 963 assert!(FullConfig::parse(r#" 964 [development] 965 log = 0 966 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 967 968 assert!(FullConfig::parse(r#" 969 [prod] 970 log = "no" 971 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 972 } 973 974 #[test] test_good_secret_key()975 fn test_good_secret_key() { 976 // Take the lock so changing the environment doesn't cause races. 977 let _env_lock = ENV_LOCK.lock().unwrap(); 978 env::set_var(CONFIG_ENV, "stage"); 979 980 check_config!(FullConfig::parse(r#" 981 [stage] 982 secret_key = "TpUiXK2d/v5DFxJnWL12suJKPExKR8h9zd/o+E7SU+0=" 983 "#.to_string(), TEST_CONFIG_FILENAME), { 984 default_config(Staging).secret_key( 985 "TpUiXK2d/v5DFxJnWL12suJKPExKR8h9zd/o+E7SU+0=" 986 ) 987 }); 988 989 check_config!(FullConfig::parse(r#" 990 [stage] 991 secret_key = "jTyprDberFUiUFsJ3vcb1XKsYHWNBRvWAnXTlbTgGFU=" 992 "#.to_string(), TEST_CONFIG_FILENAME), { 993 default_config(Staging).secret_key( 994 "jTyprDberFUiUFsJ3vcb1XKsYHWNBRvWAnXTlbTgGFU=" 995 ) 996 }); 997 } 998 999 #[test] test_bad_secret_key()1000 fn test_bad_secret_key() { 1001 // Take the lock so changing the environment doesn't cause races. 1002 let _env_lock = ENV_LOCK.lock().unwrap(); 1003 env::remove_var(CONFIG_ENV); 1004 1005 assert!(FullConfig::parse(r#" 1006 [dev] 1007 secret_key = true 1008 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 1009 1010 assert!(FullConfig::parse(r#" 1011 [dev] 1012 secret_key = 1283724897238945234897 1013 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 1014 1015 assert!(FullConfig::parse(r#" 1016 [dev] 1017 secret_key = "abcv" 1018 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 1019 } 1020 1021 #[test] test_bad_toml()1022 fn test_bad_toml() { 1023 // Take the lock so changing the environment doesn't cause races. 1024 let _env_lock = ENV_LOCK.lock().unwrap(); 1025 env::remove_var(CONFIG_ENV); 1026 1027 assert!(FullConfig::parse(r#" 1028 [dev 1029 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 1030 1031 assert!(FullConfig::parse(r#" 1032 [dev] 1033 1. = 2 1034 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 1035 1036 assert!(FullConfig::parse(r#" 1037 [dev] 1038 secret_key = "abcv" = other 1039 "#.to_string(), TEST_CONFIG_FILENAME).is_err()); 1040 } 1041 1042 #[test] test_global_overrides()1043 fn test_global_overrides() { 1044 // Take the lock so changing the environment doesn't cause races. 1045 let _env_lock = ENV_LOCK.lock().unwrap(); 1046 1047 // Test first that we can override each environment. 1048 for env in &Environment::ALL { 1049 env::set_var(CONFIG_ENV, env.to_string()); 1050 1051 check_config!(FullConfig::parse(format!(r#" 1052 [{}] 1053 address = "::1" 1054 "#, GLOBAL_ENV_NAME), TEST_CONFIG_FILENAME), { 1055 default_config(*env).address("::1") 1056 }); 1057 1058 check_config!(FullConfig::parse(format!(r#" 1059 [{}] 1060 database = "mysql" 1061 "#, GLOBAL_ENV_NAME), TEST_CONFIG_FILENAME), { 1062 default_config(*env).extra("database", "mysql") 1063 }); 1064 1065 check_config!(FullConfig::parse(format!(r#" 1066 [{}] 1067 port = 3980 1068 "#, GLOBAL_ENV_NAME), TEST_CONFIG_FILENAME), { 1069 default_config(*env).port(3980) 1070 }); 1071 } 1072 } 1073 1074 macro_rules! check_value { 1075 ($key:expr, $val:expr, $config:expr) => ( 1076 match $key { 1077 "log" => assert_eq!($config.log_level, $val.parse().unwrap()), 1078 "port" => assert_eq!($config.port, $val.parse().unwrap()), 1079 "address" => assert_eq!($config.address, $val), 1080 "extra_extra" => assert_eq!($config.get_bool($key).unwrap(), true), 1081 "workers" => assert_eq!($config.workers, $val.parse().unwrap()), 1082 _ => panic!("Unexpected key: {}", $key) 1083 } 1084 ) 1085 } 1086 1087 #[test] test_env_override()1088 fn test_env_override() { 1089 // Take the lock so changing the environment doesn't cause races. 1090 let _env_lock = ENV_LOCK.lock().unwrap(); 1091 1092 let pairs = [ 1093 ("log", "critical"), ("LOG", "debug"), ("PORT", "8110"), 1094 ("address", "1.2.3.4"), ("EXTRA_EXTRA", "true"), ("workers", "3") 1095 ]; 1096 1097 // Check that setting the environment variable actually changes the 1098 // config for the default active and nonactive environments. 1099 for &(key, val) in &pairs { 1100 env::set_var(format!("ROCKET_{}", key), val); 1101 1102 // Check that it overrides the active config. 1103 for env in &Environment::ALL { 1104 env::set_var(CONFIG_ENV, env.to_string()); 1105 let rconfig = env_default().unwrap(); 1106 check_value!(&*key.to_lowercase(), val, rconfig.active()); 1107 } 1108 1109 // And non-active configs. 1110 let rconfig = env_default().unwrap(); 1111 for env in &Environment::ALL { 1112 check_value!(&*key.to_lowercase(), val, rconfig.get(*env)); 1113 } 1114 } 1115 1116 // Clear the variables so they don't override for the next test. 1117 for &(key, _) in &pairs { 1118 env::remove_var(format!("ROCKET_{}", key)) 1119 } 1120 1121 // Now we build a config file to test that the environment variables 1122 // override configurations from files as well. 1123 let toml = r#" 1124 [dev] 1125 address = "1.2.3.4" 1126 1127 [stage] 1128 address = "2.3.4.5" 1129 1130 [prod] 1131 address = "10.1.1.1" 1132 1133 [global] 1134 address = "1.2.3.4" 1135 port = 7810 1136 workers = 21 1137 log = "normal" 1138 "#; 1139 1140 // Check that setting the environment variable actually changes the 1141 // config for the default active environments. 1142 for &(key, val) in &pairs { 1143 env::set_var(format!("ROCKET_{}", key), val); 1144 1145 let mut r = FullConfig::parse(toml, TEST_CONFIG_FILENAME).unwrap(); 1146 r.override_from_env().unwrap(); 1147 check_value!(&*key.to_lowercase(), val, r.active()); 1148 1149 // And non-active configs. 1150 for env in &Environment::ALL { 1151 check_value!(&*key.to_lowercase(), val, r.get(*env)); 1152 } 1153 } 1154 1155 // Clear the variables so they don't override for the next test. 1156 for &(key, _) in &pairs { 1157 env::remove_var(format!("ROCKET_{}", key)) 1158 } 1159 } 1160 } 1161