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}