rocket_community/config/
config.rs

1use figment::providers::{Env, Format, Serialized, Toml};
2use figment::value::{magic::RelativePathBuf, Dict, Map};
3use figment::{error::Result, Figment, Metadata, Profile, Provider};
4use serde::{Deserialize, Serialize};
5
6#[cfg(feature = "secrets")]
7use crate::config::SecretKey;
8use crate::config::{CliColors, Ident, Level, ShutdownConfig, TraceFormat};
9use crate::data::Limits;
10use crate::http::uncased::Uncased;
11use crate::request::{self, FromRequest, Request};
12
13/// Rocket server configuration.
14///
15/// See the [module level docs](crate::config) as well as the [configuration
16/// guide] for further details.
17///
18/// [configuration guide]: https://rocket.rs/master/guide/configuration/
19///
20/// # Defaults
21///
22/// All configuration values have a default, documented in the [fields](#fields)
23/// section below. [`Config::debug_default()`] returns the default values for
24/// the debug profile while [`Config::release_default()`] the default values for
25/// the release profile. The [`Config::default()`] method automatically selects
26/// the appropriate of the two based on the selected profile. With the exception
27/// of `log_level` and `log_format`, which are `info` / `pretty` in `debug` and
28/// `error` / `compact` in `release`, and `secret_key`, which is regenerated
29/// from a random value if not set in "debug" mode only, all default values are
30/// identical in all profiles.
31///
32/// # Provider Details
33///
34/// `Config` is a Figment [`Provider`] with the following characteristics:
35///
36///   * **Profile**
37///
38///     The profile is set to the value of the `profile` field.
39///
40///   * **Metadata**
41///
42///     This provider is named `Rocket Config`. It does not specify a
43///     [`Source`](figment::Source) and uses default interpolation.
44///
45///   * **Data**
46///
47///     The data emitted by this provider are the keys and values corresponding
48///     to the fields and values of the structure. The dictionary is emitted to
49///     the "default" meta-profile.
50///
51/// Note that these behaviors differ from those of [`Config::figment()`].
52#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
53pub struct Config {
54    /// The selected profile. **(default: _debug_ `debug` / _release_ `release`)**
55    ///
56    /// _**Note:** This field is never serialized nor deserialized. When a
57    /// `Config` is merged into a `Figment` as a `Provider`, this profile is
58    /// selected on the `Figment`. When a `Config` is extracted, this field is
59    /// set to the extracting Figment's selected `Profile`._
60    #[serde(skip)]
61    pub profile: Profile,
62    /// Number of threads to use for executing futures. **(default: `num_cores`)**
63    ///
64    /// _**Note:** Rocket only reads this value from sources in the [default
65    /// provider](Config::figment())._
66    pub workers: usize,
67    /// Limit on threads to start for blocking tasks. **(default: `512`)**
68    pub max_blocking: usize,
69    /// How, if at all, to identify the server via the `Server` header.
70    /// **(default: `"Rocket"`)**
71    pub ident: Ident,
72    /// The name of a header, whose value is typically set by an intermediary
73    /// server or proxy, which contains the real IP address of the connecting
74    /// client. Used internally and by [`Request::client_ip()`] and
75    /// [`Request::real_ip()`].
76    ///
77    /// To disable using any header for this purpose, set this value to `false`
78    /// or `None`. Deserialization semantics are identical to those of [`Ident`]
79    /// except that the value must syntactically be a valid HTTP header name.
80    ///
81    /// **(default: `"X-Real-IP"`)**
82    #[serde(deserialize_with = "crate::config::http_header::deserialize")]
83    pub ip_header: Option<Uncased<'static>>,
84    /// The name of a header, whose value is typically set by an intermediary
85    /// server or proxy, which contains the protocol ("http" or "https") used by
86    /// the connecting client. This is usually [`"X-Forwarded-Proto"`], as that
87    /// is the de-facto standard.
88    ///
89    /// The header value is parsed into a [`ProxyProto`], accessible via
90    /// [`Request::proxy_proto()`]. The value influences
91    /// [`Request::context_is_likely_secure()`] and the default value for the
92    /// `Secure` flag in cookies added to [`CookieJar`]s.
93    ///
94    /// To disable using any header for this purpose, set this value to `false`
95    /// or `None`. Deserialization semantics are identical to those of
96    /// [`Config::ip_header`] (the value must be a valid HTTP header name).
97    ///
98    /// **(default: `None`)**
99    ///
100    /// [`CookieJar`]: crate::http::CookieJar
101    /// [`ProxyProto`]: crate::http::ProxyProto
102    /// [`"X-Forwarded-Proto"`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
103    #[serde(deserialize_with = "crate::config::http_header::deserialize")]
104    pub proxy_proto_header: Option<Uncased<'static>>,
105    /// Streaming read size limits. **(default: [`Limits::default()`])**
106    pub limits: Limits,
107    /// Directory to store temporary files in. **(default:
108    /// [`std::env::temp_dir()`])**
109    #[serde(serialize_with = "RelativePathBuf::serialize_relative")]
110    pub temp_dir: RelativePathBuf,
111    /// Keep-alive timeout in seconds; disabled when `0`. **(default: `5`)**
112    pub keep_alive: u32,
113    /// The secret key for signing and encrypting. **(default: `0`)**
114    ///
115    /// _**Note:** This field _always_ serializes as a 256-bit array of `0`s to
116    /// aid in preventing leakage of the secret key._
117    #[cfg(feature = "secrets")]
118    #[cfg_attr(nightly, doc(cfg(feature = "secrets")))]
119    #[serde(serialize_with = "SecretKey::serialize_zero")]
120    pub secret_key: SecretKey,
121    /// Graceful shutdown configuration. **(default: [`ShutdownConfig::default()`])**
122    pub shutdown: ShutdownConfig,
123    /// Max level to log. **(default: _debug_ `info` / _release_ `error`)**
124    #[serde(with = "crate::trace::level")]
125    pub log_level: Option<Level>,
126    /// Format to use when logging. **(default: _debug_ `pretty` / _release_ `compact`)**
127    pub log_format: TraceFormat,
128    /// Whether to use colors and emoji when logging. **(default:
129    /// [`CliColors::Auto`])**
130    pub cli_colors: CliColors,
131    /// PRIVATE: This structure may grow (but never change otherwise) in a
132    /// non-breaking release. As such, constructing this structure should
133    /// _always_ be done using a public constructor or update syntax:
134    ///
135    /// ```rust
136    /// # extern crate rocket_community as rocket;
137    /// use rocket::Config;
138    ///
139    /// let config = Config {
140    ///     keep_alive: 10,
141    ///     ..Default::default()
142    /// };
143    /// ```
144    #[doc(hidden)]
145    #[serde(skip)]
146    pub __non_exhaustive: (),
147}
148
149impl Default for Config {
150    /// Returns the default configuration based on the Rust compilation profile.
151    /// This is [`Config::debug_default()`] in `debug` and
152    /// [`Config::release_default()`] in `release`.
153    ///
154    /// # Example
155    ///
156    /// ```rust
157    /// # extern crate rocket_community as rocket;
158    /// use rocket::Config;
159    ///
160    /// let config = Config::default();
161    /// ```
162    fn default() -> Config {
163        #[cfg(debug_assertions)]
164        {
165            Config::debug_default()
166        }
167        #[cfg(not(debug_assertions))]
168        {
169            Config::release_default()
170        }
171    }
172}
173
174impl Config {
175    /// Returns the default configuration for the `debug` profile, _irrespective
176    /// of the Rust compilation profile_ and `ROCKET_PROFILE`.
177    ///
178    /// This may differ from the configuration used by default,
179    /// [`Config::default()`], which is selected based on the Rust compilation
180    /// profile. See [defaults](#defaults) and [provider
181    /// details](#provider-details) for specifics.
182    ///
183    /// # Example
184    ///
185    /// ```rust
186    /// # extern crate rocket_community as rocket;
187    /// use rocket::Config;
188    ///
189    /// let config = Config::debug_default();
190    /// ```
191    pub fn debug_default() -> Config {
192        Config {
193            profile: Self::DEBUG_PROFILE,
194            workers: num_cpus::get(),
195            max_blocking: 512,
196            ident: Ident::default(),
197            ip_header: Some(Uncased::from_borrowed("X-Real-IP")),
198            proxy_proto_header: None,
199            limits: Limits::default(),
200            temp_dir: std::env::temp_dir().into(),
201            keep_alive: 5,
202            #[cfg(feature = "secrets")]
203            secret_key: SecretKey::zero(),
204            shutdown: ShutdownConfig::default(),
205            log_level: Some(Level::INFO),
206            log_format: TraceFormat::Pretty,
207            cli_colors: CliColors::Auto,
208            __non_exhaustive: (),
209        }
210    }
211
212    /// Returns the default configuration for the `release` profile,
213    /// _irrespective of the Rust compilation profile_ and `ROCKET_PROFILE`.
214    ///
215    /// This may differ from the configuration used by default,
216    /// [`Config::default()`], which is selected based on the Rust compilation
217    /// profile. See [defaults](#defaults) and [provider
218    /// details](#provider-details) for specifics.
219    ///
220    /// # Example
221    ///
222    /// ```rust
223    /// # extern crate rocket_community as rocket;
224    /// use rocket::Config;
225    ///
226    /// let config = Config::release_default();
227    /// ```
228    pub fn release_default() -> Config {
229        Config {
230            profile: Self::RELEASE_PROFILE,
231            log_level: Some(Level::ERROR),
232            log_format: TraceFormat::Compact,
233            ..Config::debug_default()
234        }
235    }
236
237    /// Returns the default provider figment used by [`rocket::build()`].
238    ///
239    /// The default figment reads from the following sources, in ascending
240    /// priority order:
241    ///
242    ///   1. [`Config::default()`] (see [defaults](#defaults))
243    ///   2. `Rocket.toml` _or_ filename in `ROCKET_CONFIG` environment variable
244    ///   3. `ROCKET_` prefixed environment variables
245    ///
246    /// The profile selected is the value set in the `ROCKET_PROFILE`
247    /// environment variable. If it is not set, it defaults to `debug` when
248    /// compiled in debug mode and `release` when compiled in release mode.
249    ///
250    /// [`rocket::build()`]: crate::build()
251    ///
252    /// # Example
253    ///
254    /// ```rust
255    /// # extern crate rocket_community as rocket;
256    /// use rocket::Config;
257    /// use serde::Deserialize;
258    ///
259    /// #[derive(Deserialize)]
260    /// struct MyConfig {
261    ///     app_key: String,
262    /// }
263    ///
264    /// let my_config = Config::figment().extract::<MyConfig>();
265    /// ```
266    pub fn figment() -> Figment {
267        Figment::from(Config::default())
268            .merge(Toml::file(Env::var_or("ROCKET_CONFIG", "Rocket.toml")).nested())
269            .merge(Env::prefixed("ROCKET_").ignore(&["PROFILE"]).global())
270            .select(Profile::from_env_or(
271                "ROCKET_PROFILE",
272                Self::DEFAULT_PROFILE,
273            ))
274    }
275
276    /// Attempts to extract a `Config` from `provider`, returning the result.
277    ///
278    /// # Example
279    ///
280    /// ```rust
281    /// # extern crate rocket_community as rocket;
282    /// use rocket::Config;
283    /// use rocket::figment::providers::{Toml, Format, Env};
284    ///
285    /// // Use Rocket's default `Figment`, but allow values from `MyApp.toml`
286    /// // and `MY_APP_` prefixed environment variables to supersede its values.
287    /// let figment = Config::figment()
288    ///     .merge(("some-thing", 123))
289    ///     .merge(Env::prefixed("CONFIG_"));
290    ///
291    /// let config = Config::try_from(figment);
292    /// ```
293    pub fn try_from<T: Provider>(provider: T) -> Result<Self> {
294        let figment = Figment::from(provider);
295        let mut config = figment.extract::<Self>()?;
296        config.profile = figment.profile().clone();
297        Ok(config)
298    }
299
300    /// Extract a `Config` from `provider`, panicking if extraction fails.
301    ///
302    /// # Panics
303    ///
304    /// If extraction fails, logs an error message indicating the error and
305    /// panics. For a version that doesn't panic, use [`Config::try_from()`].
306    ///
307    /// # Example
308    ///
309    /// ```rust
310    /// # extern crate rocket_community as rocket;
311    /// use rocket::Config;
312    /// use rocket::figment::providers::{Toml, Format, Env};
313    ///
314    /// // Use Rocket's default `Figment`, but allow values from `MyApp.toml`
315    /// // and `MY_APP_` prefixed environment variables to supersede its values.
316    /// let figment = Config::figment()
317    ///     .merge(Toml::file("MyApp.toml").nested())
318    ///     .merge(Env::prefixed("MY_APP_"));
319    ///
320    /// let config = Config::from(figment);
321    /// ```
322    pub fn from<T: Provider>(provider: T) -> Self {
323        use crate::trace::Trace;
324
325        Self::try_from(provider).unwrap_or_else(|e| {
326            e.trace_error();
327            panic!("aborting due to configuration error(s)")
328        })
329    }
330}
331
332/// Associated constants for default profiles.
333impl Config {
334    /// The default debug profile: `debug`.
335    pub const DEBUG_PROFILE: Profile = Profile::const_new("debug");
336
337    /// The default release profile: `release`.
338    pub const RELEASE_PROFILE: Profile = Profile::const_new("release");
339
340    /// The default profile: "debug" on `debug`, "release" on `release`.
341    #[cfg(debug_assertions)]
342    pub const DEFAULT_PROFILE: Profile = Self::DEBUG_PROFILE;
343
344    /// The default profile: "debug" on `debug`, "release" on `release`.
345    #[cfg(not(debug_assertions))]
346    pub const DEFAULT_PROFILE: Profile = Self::RELEASE_PROFILE;
347}
348
349/// Associated constants for stringy versions of configuration parameters.
350impl Config {
351    /// The stringy parameter name for setting/extracting [`Config::workers`].
352    pub const WORKERS: &'static str = "workers";
353
354    /// The stringy parameter name for setting/extracting [`Config::max_blocking`].
355    pub const MAX_BLOCKING: &'static str = "max_blocking";
356
357    /// The stringy parameter name for setting/extracting [`Config::keep_alive`].
358    pub const KEEP_ALIVE: &'static str = "keep_alive";
359
360    /// The stringy parameter name for setting/extracting [`Config::ident`].
361    pub const IDENT: &'static str = "ident";
362
363    /// The stringy parameter name for setting/extracting [`Config::ip_header`].
364    pub const IP_HEADER: &'static str = "ip_header";
365
366    /// The stringy parameter name for setting/extracting [`Config::proxy_proto_header`].
367    pub const PROXY_PROTO_HEADER: &'static str = "proxy_proto_header";
368
369    /// The stringy parameter name for setting/extracting [`Config::limits`].
370    pub const LIMITS: &'static str = "limits";
371
372    /// The stringy parameter name for setting/extracting [`Config::secret_key`].
373    pub const SECRET_KEY: &'static str = "secret_key";
374
375    /// The stringy parameter name for setting/extracting [`Config::temp_dir`].
376    pub const TEMP_DIR: &'static str = "temp_dir";
377
378    /// The stringy parameter name for setting/extracting [`Config::log_level`].
379    pub const LOG_LEVEL: &'static str = "log_level";
380
381    /// The stringy parameter name for setting/extracting [`Config::log_format`].
382    pub const LOG_FORMAT: &'static str = "log_format";
383
384    /// The stringy parameter name for setting/extracting [`Config::shutdown`].
385    pub const SHUTDOWN: &'static str = "shutdown";
386
387    /// The stringy parameter name for setting/extracting [`Config::cli_colors`].
388    pub const CLI_COLORS: &'static str = "cli_colors";
389
390    /// An array of all of the stringy parameter names.
391    pub const PARAMETERS: &'static [&'static str] = &[
392        Self::WORKERS,
393        Self::MAX_BLOCKING,
394        Self::KEEP_ALIVE,
395        Self::IDENT,
396        Self::IP_HEADER,
397        Self::PROXY_PROTO_HEADER,
398        Self::LIMITS,
399        Self::SECRET_KEY,
400        Self::TEMP_DIR,
401        Self::LOG_LEVEL,
402        Self::LOG_FORMAT,
403        Self::SHUTDOWN,
404        Self::CLI_COLORS,
405    ];
406
407    /// The stringy parameter name for setting/extracting [`Config::profile`].
408    ///
409    /// This isn't `pub` because setting it directly does nothing.
410    const PROFILE: &'static str = "profile";
411
412    /// An array of deprecated stringy parameter names.
413    pub(crate) const DEPRECATED_KEYS: &'static [(&'static str, Option<&'static str>)] = &[
414        ("env", Some(Self::PROFILE)),
415        ("log", Some(Self::LOG_LEVEL)),
416        ("read_timeout", None),
417        ("write_timeout", None),
418    ];
419
420    /// Secret keys that have been used in docs or leaked otherwise.
421    #[cfg(feature = "secrets")]
422    pub(crate) const KNOWN_SECRET_KEYS: &'static [&'static str] =
423        &["hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="];
424}
425
426impl Provider for Config {
427    #[track_caller]
428    fn metadata(&self) -> Metadata {
429        if self == &Config::default() {
430            Metadata::named("rocket::Config::default()")
431        } else {
432            Metadata::named("rocket::Config")
433        }
434    }
435
436    #[track_caller]
437    fn data(&self) -> Result<Map<Profile, Dict>> {
438        #[allow(unused_mut)]
439        let mut map: Map<Profile, Dict> = Serialized::defaults(self).data()?;
440
441        // We need to special-case `secret_key` since its serializer zeroes.
442        #[cfg(feature = "secrets")]
443        if !self.secret_key.is_zero() {
444            if let Some(map) = map.get_mut(&Profile::Default) {
445                map.insert("secret_key".into(), self.secret_key.key.master().into());
446            }
447        }
448
449        Ok(map)
450    }
451
452    fn profile(&self) -> Option<Profile> {
453        Some(self.profile.clone())
454    }
455}
456
457#[crate::async_trait]
458impl<'r> FromRequest<'r> for &'r Config {
459    type Error = std::convert::Infallible;
460
461    async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
462        request::Outcome::Success(req.rocket().config())
463    }
464}