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