rocket/config/
config.rs

1use figment::{Figment, Profile, Provider, Metadata, error::Result};
2use figment::providers::{Serialized, Env, Toml, Format};
3use figment::value::{Map, Dict, magic::RelativePathBuf};
4use serde::{Deserialize, Serialize};
5
6#[cfg(feature = "secrets")]
7use crate::config::SecretKey;
8use crate::config::{ShutdownConfig, Level, TraceFormat, Ident, CliColors};
9use crate::request::{self, Request, FromRequest};
10use crate::http::uncased::Uncased;
11use crate::data::Limits;
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    /// use rocket::Config;
137    ///
138    /// let config = Config {
139    ///     keep_alive: 10,
140    ///     ..Default::default()
141    /// };
142    /// ```
143    #[doc(hidden)]
144    #[serde(skip)]
145    pub __non_exhaustive: (),
146}
147
148impl Default for Config {
149    /// Returns the default configuration based on the Rust compilation profile.
150    /// This is [`Config::debug_default()`] in `debug` and
151    /// [`Config::release_default()`] in `release`.
152    ///
153    /// # Example
154    ///
155    /// ```rust
156    /// use rocket::Config;
157    ///
158    /// let config = Config::default();
159    /// ```
160    fn default() -> Config {
161        #[cfg(debug_assertions)] { Config::debug_default() }
162        #[cfg(not(debug_assertions))] { Config::release_default() }
163    }
164}
165
166impl Config {
167    /// Returns the default configuration for the `debug` profile, _irrespective
168    /// of the Rust compilation profile_ and `ROCKET_PROFILE`.
169    ///
170    /// This may differ from the configuration used by default,
171    /// [`Config::default()`], which is selected based on the Rust compilation
172    /// profile. See [defaults](#defaults) and [provider
173    /// details](#provider-details) for specifics.
174    ///
175    /// # Example
176    ///
177    /// ```rust
178    /// use rocket::Config;
179    ///
180    /// let config = Config::debug_default();
181    /// ```
182    pub fn debug_default() -> Config {
183        Config {
184            profile: Self::DEBUG_PROFILE,
185            workers: num_cpus::get(),
186            max_blocking: 512,
187            ident: Ident::default(),
188            ip_header: Some(Uncased::from_borrowed("X-Real-IP")),
189            proxy_proto_header: None,
190            limits: Limits::default(),
191            temp_dir: std::env::temp_dir().into(),
192            keep_alive: 5,
193            #[cfg(feature = "secrets")]
194            secret_key: SecretKey::zero(),
195            shutdown: ShutdownConfig::default(),
196            log_level: Some(Level::INFO),
197            log_format: TraceFormat::Pretty,
198            cli_colors: CliColors::Auto,
199            __non_exhaustive: (),
200        }
201    }
202
203    /// Returns the default configuration for the `release` profile,
204    /// _irrespective of the Rust compilation profile_ and `ROCKET_PROFILE`.
205    ///
206    /// This may differ from the configuration used by default,
207    /// [`Config::default()`], which is selected based on the Rust compilation
208    /// profile. See [defaults](#defaults) and [provider
209    /// details](#provider-details) for specifics.
210    ///
211    /// # Example
212    ///
213    /// ```rust
214    /// use rocket::Config;
215    ///
216    /// let config = Config::release_default();
217    /// ```
218    pub fn release_default() -> Config {
219        Config {
220            profile: Self::RELEASE_PROFILE,
221            log_level: Some(Level::ERROR),
222            log_format: TraceFormat::Compact,
223            ..Config::debug_default()
224        }
225    }
226
227    /// Returns the default provider figment used by [`rocket::build()`].
228    ///
229    /// The default figment reads from the following sources, in ascending
230    /// priority order:
231    ///
232    ///   1. [`Config::default()`] (see [defaults](#defaults))
233    ///   2. `Rocket.toml` _or_ filename in `ROCKET_CONFIG` environment variable
234    ///   3. `ROCKET_` prefixed environment variables
235    ///
236    /// The profile selected is the value set in the `ROCKET_PROFILE`
237    /// environment variable. If it is not set, it defaults to `debug` when
238    /// compiled in debug mode and `release` when compiled in release mode.
239    ///
240    /// [`rocket::build()`]: crate::build()
241    ///
242    /// # Example
243    ///
244    /// ```rust
245    /// use rocket::Config;
246    /// use serde::Deserialize;
247    ///
248    /// #[derive(Deserialize)]
249    /// struct MyConfig {
250    ///     app_key: String,
251    /// }
252    ///
253    /// let my_config = Config::figment().extract::<MyConfig>();
254    /// ```
255    pub fn figment() -> Figment {
256        Figment::from(Config::default())
257            .merge(Toml::file(Env::var_or("ROCKET_CONFIG", "Rocket.toml")).nested())
258            .merge(Env::prefixed("ROCKET_").ignore(&["PROFILE"]).global())
259            .select(Profile::from_env_or("ROCKET_PROFILE", Self::DEFAULT_PROFILE))
260    }
261
262    /// Attempts to extract a `Config` from `provider`, returning the result.
263    ///
264    /// # Example
265    ///
266    /// ```rust
267    /// use rocket::Config;
268    /// use rocket::figment::providers::{Toml, Format, Env};
269    ///
270    /// // Use Rocket's default `Figment`, but allow values from `MyApp.toml`
271    /// // and `MY_APP_` prefixed environment variables to supersede its values.
272    /// let figment = Config::figment()
273    ///     .merge(("some-thing", 123))
274    ///     .merge(Env::prefixed("CONFIG_"));
275    ///
276    /// let config = Config::try_from(figment);
277    /// ```
278    pub fn try_from<T: Provider>(provider: T) -> Result<Self> {
279        let figment = Figment::from(provider);
280        let mut config = figment.extract::<Self>()?;
281        config.profile = figment.profile().clone();
282        Ok(config)
283    }
284
285    /// Extract a `Config` from `provider`, panicking if extraction fails.
286    ///
287    /// # Panics
288    ///
289    /// If extraction fails, logs an error message indicating the error and
290    /// panics. For a version that doesn't panic, use [`Config::try_from()`].
291    ///
292    /// # Example
293    ///
294    /// ```rust
295    /// use rocket::Config;
296    /// use rocket::figment::providers::{Toml, Format, Env};
297    ///
298    /// // Use Rocket's default `Figment`, but allow values from `MyApp.toml`
299    /// // and `MY_APP_` prefixed environment variables to supersede its values.
300    /// let figment = Config::figment()
301    ///     .merge(Toml::file("MyApp.toml").nested())
302    ///     .merge(Env::prefixed("MY_APP_"));
303    ///
304    /// let config = Config::from(figment);
305    /// ```
306    pub fn from<T: Provider>(provider: T) -> Self {
307        use crate::trace::Trace;
308
309        Self::try_from(provider).unwrap_or_else(|e| {
310            e.trace_error();
311            panic!("aborting due to configuration error(s)")
312        })
313    }
314}
315
316/// Associated constants for default profiles.
317impl Config {
318    /// The default debug profile: `debug`.
319    pub const DEBUG_PROFILE: Profile = Profile::const_new("debug");
320
321    /// The default release profile: `release`.
322    pub const RELEASE_PROFILE: Profile = Profile::const_new("release");
323
324    /// The default profile: "debug" on `debug`, "release" on `release`.
325    #[cfg(debug_assertions)]
326    pub const DEFAULT_PROFILE: Profile = Self::DEBUG_PROFILE;
327
328    /// The default profile: "debug" on `debug`, "release" on `release`.
329    #[cfg(not(debug_assertions))]
330    pub const DEFAULT_PROFILE: Profile = Self::RELEASE_PROFILE;
331}
332
333/// Associated constants for stringy versions of configuration parameters.
334impl Config {
335    /// The stringy parameter name for setting/extracting [`Config::workers`].
336    pub const WORKERS: &'static str = "workers";
337
338    /// The stringy parameter name for setting/extracting [`Config::max_blocking`].
339    pub const MAX_BLOCKING: &'static str = "max_blocking";
340
341    /// The stringy parameter name for setting/extracting [`Config::keep_alive`].
342    pub const KEEP_ALIVE: &'static str = "keep_alive";
343
344    /// The stringy parameter name for setting/extracting [`Config::ident`].
345    pub const IDENT: &'static str = "ident";
346
347    /// The stringy parameter name for setting/extracting [`Config::ip_header`].
348    pub const IP_HEADER: &'static str = "ip_header";
349
350    /// The stringy parameter name for setting/extracting [`Config::proxy_proto_header`].
351    pub const PROXY_PROTO_HEADER: &'static str = "proxy_proto_header";
352
353    /// The stringy parameter name for setting/extracting [`Config::limits`].
354    pub const LIMITS: &'static str = "limits";
355
356    /// The stringy parameter name for setting/extracting [`Config::secret_key`].
357    pub const SECRET_KEY: &'static str = "secret_key";
358
359    /// The stringy parameter name for setting/extracting [`Config::temp_dir`].
360    pub const TEMP_DIR: &'static str = "temp_dir";
361
362    /// The stringy parameter name for setting/extracting [`Config::log_level`].
363    pub const LOG_LEVEL: &'static str = "log_level";
364
365    /// The stringy parameter name for setting/extracting [`Config::log_format`].
366    pub const LOG_FORMAT: &'static str = "log_format";
367
368    /// The stringy parameter name for setting/extracting [`Config::shutdown`].
369    pub const SHUTDOWN: &'static str = "shutdown";
370
371    /// The stringy parameter name for setting/extracting [`Config::cli_colors`].
372    pub const CLI_COLORS: &'static str = "cli_colors";
373
374    /// An array of all of the stringy parameter names.
375    pub const PARAMETERS: &'static [&'static str] = &[
376        Self::WORKERS, Self::MAX_BLOCKING, Self::KEEP_ALIVE, Self::IDENT,
377        Self::IP_HEADER, Self::PROXY_PROTO_HEADER, Self::LIMITS,
378        Self::SECRET_KEY, Self::TEMP_DIR, Self::LOG_LEVEL, Self::LOG_FORMAT,
379        Self::SHUTDOWN, Self::CLI_COLORS,
380    ];
381
382    /// The stringy parameter name for setting/extracting [`Config::profile`].
383    ///
384    /// This isn't `pub` because setting it directly does nothing.
385    const PROFILE: &'static str = "profile";
386
387    /// An array of deprecated stringy parameter names.
388    pub(crate) const DEPRECATED_KEYS: &'static [(&'static str, Option<&'static str>)] = &[
389        ("env", Some(Self::PROFILE)), ("log", Some(Self::LOG_LEVEL)),
390        ("read_timeout", None), ("write_timeout", None),
391    ];
392
393    /// Secret keys that have been used in docs or leaked otherwise.
394    #[cfg(feature = "secrets")]
395    pub(crate) const KNOWN_SECRET_KEYS: &'static [&'static str] = &[
396        "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="
397    ];
398}
399
400impl Provider for Config {
401    #[track_caller]
402    fn metadata(&self) -> Metadata {
403        if self == &Config::default() {
404            Metadata::named("rocket::Config::default()")
405        } else {
406            Metadata::named("rocket::Config")
407        }
408    }
409
410    #[track_caller]
411    fn data(&self) -> Result<Map<Profile, Dict>> {
412        #[allow(unused_mut)]
413        let mut map: Map<Profile, Dict> = Serialized::defaults(self).data()?;
414
415        // We need to special-case `secret_key` since its serializer zeroes.
416        #[cfg(feature = "secrets")]
417        if !self.secret_key.is_zero() {
418            if let Some(map) = map.get_mut(&Profile::Default) {
419                map.insert("secret_key".into(), self.secret_key.key.master().into());
420            }
421        }
422
423        Ok(map)
424    }
425
426    fn profile(&self) -> Option<Profile> {
427        Some(self.profile.clone())
428    }
429}
430
431#[crate::async_trait]
432impl<'r> FromRequest<'r> for &'r Config {
433    type Error = std::convert::Infallible;
434
435    async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
436        request::Outcome::Success(req.rocket().config())
437    }
438}