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}