Allow customizing and removing 'Server' header.

This commit is contained in:
Sergio Benitez 2021-06-08 23:00:36 -07:00
parent 6206a46222
commit 128234d9a8
10 changed files with 515 additions and 103 deletions

View File

@ -54,6 +54,74 @@ impl<'h> Header<'h> {
}
}
/// Returns `true` if `val` is a valid header value.
///
/// If `allow_empty` is `true`, this function returns `true` for empty
/// values. Otherwise, this function returns `false` for empty values.
///
/// This implements a simple (i.e, correct but not particularly performant)
/// header "field-content" checker as defined in RFC 7230 without support
/// for obsolete (`obs-`) syntax:
///
/// field-value = *(field-content)
/// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
/// field-vchar = VCHAR
/// VCHAR = %x21-7E ; visible (printing) characters
///
/// Note that this is a generic checker. Specific headers may have stricter
/// requirements. For example, the `Server` header does not allow delimiters
/// in its values.
///
/// # Example
///
/// ```rust
/// # extern crate rocket;
/// use rocket::http::Header;
///
/// assert!(!Header::is_valid_value("", false));
/// assert!(!Header::is_valid_value(" " , false));
/// assert!(!Header::is_valid_value(" hi", false));
/// assert!(!Header::is_valid_value("a\nbc", false));
/// assert!(!Header::is_valid_value("\nbc", false));
/// assert!(!Header::is_valid_value("\n", false));
/// assert!(!Header::is_valid_value("\t", false));
/// assert!(!Header::is_valid_value("\r", false));
/// assert!(!Header::is_valid_value("a\nb\nc", false));
/// assert!(!Header::is_valid_value("a\rb\rc", false));
///
/// assert!(Header::is_valid_value("", true));
/// assert!(Header::is_valid_value("a", false));
/// assert!(Header::is_valid_value("a", true));
/// assert!(Header::is_valid_value("abc", false));
/// assert!(Header::is_valid_value("abc", true));
/// assert!(Header::is_valid_value("a b c", false));
/// assert!(Header::is_valid_value("a b c", true));
/// ```
#[doc(hidden)]
pub const fn is_valid_value(val: &str, allow_empty: bool) -> bool {
const fn is_valid_start(b: &u8) -> bool {
b.is_ascii_graphic()
}
const fn is_valid_continue(b: &u8) -> bool {
is_valid_start(b) || *b == b' ' || *b == b'\t'
}
let mut i = 0;
let bytes = val.as_bytes();
while i < bytes.len() {
match i {
0 if !is_valid_start(&bytes[i]) => return false,
_ if i > 0 && !is_valid_continue(&bytes[i]) => return false,
_ => { /* ok */ }
};
i += 1;
}
allow_empty || i > 0
}
/// Returns the name of this header.
///
/// # Example

View File

@ -7,7 +7,7 @@ use figment::value::{Map, Dict};
use serde::{Deserialize, Serialize};
use yansi::Paint;
use crate::config::{TlsConfig, LogLevel, Shutdown};
use crate::config::{TlsConfig, LogLevel, Shutdown, Ident};
use crate::request::{self, Request, FromRequest};
use crate::data::Limits;
@ -74,6 +74,9 @@ pub struct Config {
pub limits: Limits,
/// The TLS configuration, if any. **(default: `None`)**
pub tls: Option<TlsConfig>,
/// How, if at all, to identify the server via the `Server` header.
/// **(default: `"Rocket"`)**
pub ident: Ident,
/// The secret key for signing and encrypting. **(default: `0`)**
///
/// **Note:** This field _always_ serializes as a 256-bit array of `0`s to
@ -129,7 +132,6 @@ impl Default for Config {
}
impl Config {
const DEPRECATED_KEYS: &'static [(&'static str, Option<&'static str>)] = &[
("env", Some(Self::PROFILE)), ("log", Some(Self::LOG_LEVEL)),
];
@ -162,6 +164,7 @@ impl Config {
keep_alive: 5,
limits: Limits::default(),
tls: None,
ident: Ident::default(),
#[cfg(feature = "secrets")]
secret_key: SecretKey::zero(),
temp_dir: std::env::temp_dir(),
@ -308,6 +311,7 @@ impl Config {
launch_info_!("address: {}", Paint::default(&self.address).bold());
launch_info_!("port: {}", Paint::default(&self.port).bold());
launch_info_!("workers: {}", Paint::default(self.workers).bold());
launch_info_!("ident: {}", Paint::default(&self.ident).bold());
let ka = self.keep_alive;
if ka > 0 {

View File

@ -0,0 +1,238 @@
use std::fmt;
use serde::{Deserialize, Serialize};
use serde::de::{self, Deserializer};
use crate::http::Header;
/// An identifier (or `None`) to send as the `Server` header.
///
/// # Deserialization
///
/// An `Ident` deserializes from any of the following:
///
/// * `string`
///
/// The string must be a valid `Ident`. See [`Ident::try_new()`] for details.
///
/// * `boolean`
///
/// The boolean must be `false`. The value will be [`Ident::none()`].
///
/// * `Option<string>`
///
/// If `Some`, this is the same as deserializing from the inner string. If
/// `None`, the value is [`Ident::none()`].
///
/// * `unit`
///
/// Always deserializes as [`Ident::none()`].
///
/// # Examples
///
/// As with all Rocket configuration options, when using the default
/// [`Config::figment()`](crate::Config::figment()), `Ident` can be configured
/// via a `Rocket.toml` file. When no value for `ident` is provided, the value
/// defaults to `"Rocket"`. Because a default is provided, configuration only
/// needs to provided to customize or remove the value.
///
/// ```rust
/// # use rocket::figment::{Figment, providers::{Format, Toml}};
/// use rocket::config::{Config, Ident};
///
/// // If these are the contents of `Rocket.toml`...
/// # let toml = Toml::string(r#"
/// [default]
/// ident = false
/// # "#).nested();
///
/// // The config parses as follows:
/// # let config = Config::from(Figment::from(Config::debug_default()).merge(toml));
/// assert_eq!(config.ident, Ident::none());
///
/// // If these are the contents of `Rocket.toml`...
/// # let toml = Toml::string(r#"
/// [default]
/// ident = "My Server"
/// # "#).nested();
///
/// // The config parses as follows:
/// # let config = Config::from(Figment::from(Config::debug_default()).merge(toml));
/// assert_eq!(config.ident, Ident::try_new("My Server").unwrap());
/// ```
///
/// The following example illustrates manual configuration:
///
/// ```rust
/// use rocket::config::{Config, Ident};
///
/// let figment = rocket::Config::figment().merge(("ident", false));
/// let config = rocket::Config::from(figment);
/// assert_eq!(config.ident, Ident::none());
///
/// let figment = rocket::Config::figment().merge(("ident", "Fancy/1.0"));
/// let config = rocket::Config::from(figment);
/// assert_eq!(config.ident, Ident::try_new("Fancy/1.0").unwrap());
/// ```
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Ident(Option<String>);
macro_rules! ident {
($value:expr) => {
{
#[allow(unknown_lints, eq_op)]
const _: [(); 0 - !{
const ASSERT: bool = $crate::http::Header::is_valid_value($value, false);
ASSERT
} as usize] = [];
$crate::config::Ident::try_new($value).unwrap()
}
}
}
impl Ident {
/// Returns a new `Ident` with the string `ident`.
///
/// When configured as the [`Config::ident`](crate::Config::ident), Rocket
/// will set a `Server` header with the `ident` value on all responses.
///
/// # Errors
///
/// The string `ident` must be non-empty and may only contain visible ASCII
/// characters. The first character cannot be whitespace. The only
/// whitespace characters allowed are ` ` (space) and `\t` (horizontal tab).
/// The string is returned wrapped in `Err` if it contains any invalid
/// characters.
///
/// # Example
///
/// ```rust
/// use rocket::config::Ident;
///
/// let ident = Ident::try_new("Rocket").unwrap();
/// assert_eq!(ident.as_str(), Some("Rocket"));
///
/// let ident = Ident::try_new("Rocket Run").unwrap();
/// assert_eq!(ident.as_str(), Some("Rocket Run"));
///
/// let ident = Ident::try_new(" Rocket");
/// assert!(ident.is_err());
///
/// let ident = Ident::try_new("Rocket\nRun");
/// assert!(ident.is_err());
///
/// let ident = Ident::try_new("\tShip");
/// assert!(ident.is_err());
/// ```
pub fn try_new<S: Into<String>>(ident: S) -> Result<Ident, String> {
// This is a little more lenient than reality.
let ident = ident.into();
if !Header::is_valid_value(&ident, false) {
return Err(ident);
}
Ok(Ident(Some(ident)))
}
/// Returns a new `Ident` which is `None`.
///
/// When configured as the [`Config::ident`](crate::Config::ident), Rocket
/// will not set a `Server` header on any response. Any `Server` header
/// emitted by the application will still be written out.
///
/// # Example
///
/// ```rust
/// use rocket::config::Ident;
///
/// let ident = Ident::none();
/// assert_eq!(ident.as_str(), None);
/// ```
pub const fn none() -> Ident {
Ident(None)
}
/// Returns `self` as an `Option<&str>`.
///
/// # Example
///
/// ```rust
/// use rocket::config::Ident;
///
/// let ident = Ident::try_new("Rocket").unwrap();
/// assert_eq!(ident.as_str(), Some("Rocket"));
///
/// let ident = Ident::try_new("Rocket/1 (Unix)").unwrap();
/// assert_eq!(ident.as_str(), Some("Rocket/1 (Unix)"));
///
/// let ident = Ident::none();
/// assert_eq!(ident.as_str(), None);
/// ```
pub fn as_str(&self) -> Option<&str> {
self.0.as_deref()
}
}
impl<'de> Deserialize<'de> for Ident {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Ident;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a server ident string or `false`")
}
fn visit_bool<E: de::Error>(self, v: bool) -> Result<Self::Value, E> {
if !v {
return Ok(Ident::none());
}
Err(E::invalid_value(de::Unexpected::Bool(v), &self))
}
fn visit_some<D>(self, de: D) -> Result<Self::Value, D::Error>
where D: Deserializer<'de>
{
de.deserialize_string(self)
}
fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(Ident::none())
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(Ident::none())
}
fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
Ident::try_new(v)
.map_err(|s| E::invalid_value(de::Unexpected::Str(&s), &self))
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
self.visit_string(v.into())
}
}
de.deserialize_string(Visitor)
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.as_str() {
Some(name) => name.fmt(f),
None => "disabled".fmt(f),
}
}
}
/// The default `Ident` is `"Rocket"`.
impl Default for Ident {
fn default() -> Self {
ident!("Rocket")
}
}

View File

@ -111,6 +111,8 @@
//! [`Toml`]: figment::providers::Toml
//! [`Env`]: figment::providers::Env
#[macro_use]
mod ident;
mod config;
mod tls;
mod shutdown;
@ -124,6 +126,7 @@ pub use config::Config;
pub use crate::log::LogLevel;
pub use shutdown::Shutdown;
pub use tls::TlsConfig;
pub use ident::Ident;
#[cfg(feature = "secrets")]
#[cfg_attr(nightly, doc(cfg(feature = "secrets")))]
@ -139,10 +142,20 @@ mod tests {
use figment::{Figment, Profile};
use pretty_assertions::assert_eq;
use crate::config::{Config, TlsConfig, Shutdown};
use crate::config::{Config, TlsConfig, Shutdown, Ident};
use crate::log::LogLevel;
use crate::data::{Limits, ToByteUnit};
#[test]
fn test_figment_is_default() {
figment::Jail::expect_with(|_| {
let mut default: Config = Config::figment().extract().unwrap();
default.profile = Config::default().profile;
assert_eq!(default, Config::default());
Ok(())
});
}
#[test]
fn test_default_round_trip() {
figment::Jail::expect_with(|_| {
@ -182,6 +195,7 @@ mod tests {
jail.create_file("Rocket.toml", r#"
[default]
address = "1.2.3.4"
ident = "Something Cool"
port = 1234
workers = 20
keep_alive = 10
@ -194,6 +208,7 @@ mod tests {
address: Ipv4Addr::new(1, 2, 3, 4).into(),
port: 1234,
workers: 20,
ident: ident!("Something Cool"),
keep_alive: 10,
log_level: LogLevel::Off,
cli_colors: false,
@ -203,6 +218,7 @@ mod tests {
jail.create_file("Rocket.toml", r#"
[global]
address = "1.2.3.4"
ident = "Something Else Cool"
port = 1234
workers = 20
keep_alive = 10
@ -215,6 +231,7 @@ mod tests {
address: Ipv4Addr::new(1, 2, 3, 4).into(),
port: 1234,
workers: 20,
ident: ident!("Something Else Cool"),
keep_alive: 10,
log_level: LogLevel::Off,
cli_colors: false,
@ -224,6 +241,7 @@ mod tests {
jail.create_file("Rocket.toml", r#"
[global]
shutdown.ctrlc = 0
ident = false
[global.tls]
certs = "/ssl/cert.pem"
@ -238,6 +256,7 @@ mod tests {
let config = Config::from(Config::figment());
assert_eq!(config, Config {
shutdown: Shutdown { ctrlc: false, ..Default::default() },
ident: Ident::none(),
tls: Some(TlsConfig::from_paths("/ssl/cert.pem", "/ssl/key.pem")),
limits: Limits::default()
.limit("forms", 1.mebibytes())
@ -363,6 +382,16 @@ mod tests {
..Config::default()
});
jail.set_env("ROCKET_IDENT", false);
let config = Config::from(Config::figment().join(&prev_figment));
assert_eq!(config, Config {
port: 9999,
tls: Some(TlsConfig::from_paths("new.pem", "key.pem")),
limits: Limits::default().limit("stream", 100.kibibytes()),
ident: Ident::none(),
..Config::default()
});
Ok(())
});
}

View File

@ -137,7 +137,7 @@ impl fmt::Display for Sig {
///
/// ```rust
/// # use rocket::figment::{Figment, providers::{Format, Toml}};
/// use rocket::{Rocket, Config};
/// use rocket::Config;
///
/// // If these are the contents of `Rocket.toml`...
/// # let toml = Toml::string(r#"
@ -169,8 +169,7 @@ impl fmt::Display for Sig {
///
/// ```rust
/// # use rocket::figment::{Figment, providers::{Format, Toml}};
/// use rocket::{Rocket, Config};
/// use rocket::config::Shutdown;
/// use rocket::config::{Config, Shutdown};
///
/// #[cfg(unix)]
/// use rocket::config::Sig;

View File

@ -5,7 +5,7 @@ use crate::outcome::{self, IntoOutcome, try_outcome, Outcome::*};
/// Type alias for the `Outcome` of [`FromData`].
///
/// [`FromData`]: crTte::data::FromData
/// [`FromData`]: crate::data::FromData
pub type Outcome<'r, T, E = <T as FromData<'r>>::Error>
= outcome::Outcome<T, (Status, E), Data<'r>>;

View File

@ -210,8 +210,10 @@ impl Rocket<Orbit> {
// Add a default 'Server' header if it isn't already there.
// TODO: If removing Hyper, write out `Date` header too.
if !response.headers().contains("Server") {
response.set_header(Header::new("Server", "Rocket"));
if let Some(ident) = request.rocket().config.ident.as_str() {
if !response.headers().contains("Server") {
response.set_header(Header::new("Server", ident));
}
}
// Run the response fairings.

View File

@ -28,5 +28,25 @@ mod conditionally_set_server_header {
let response = client.get("/use_default").dispatch();
let server = response.headers().get_one("Server");
assert_eq!(server, Some("Rocket"));
// Now with a special `Ident`.
let config = rocket::Config {
ident: rocket::config::Ident::try_new("My Special Server").unwrap(),
..rocket::Config::debug_default()
};
let rocket = rocket::custom(config)
.mount("/", routes![do_not_overwrite, use_default]);
let client = Client::debug(rocket).unwrap();
let response = client.get("/do_not_overwrite").dispatch();
let server = response.headers().get_one("Server");
assert_eq!(server, Some("Test"));
let response = client.get("/use_default").dispatch();
let server = response.headers().get_one("Server");
assert_eq!(server, Some("My Special Server"));
}
}

View File

@ -10,6 +10,7 @@ msgpack = "2 MiB"
[default]
key = "a default app-key"
extra = false
ident = "Rocket"
[debug]
address = "127.0.0.1"

View File

@ -17,21 +17,23 @@ is configured with. This means that no matter which configuration provider
Rocket is asked to use, it must be able to read the following configuration
values:
| key | kind | description | debug/release default |
|----------------|-----------------|-------------------------------------------------|-----------------------|
| `address` | `IpAddr` | IP address to serve on | `127.0.0.1` |
| `port` | `u16` | Port to serve on. | `8000` |
| `workers` | `usize` | Number of threads to use for executing futures. | cpu core count |
| `keep_alive` | `u32` | Keep-alive timeout seconds; disabled when `0`. | `5` |
| `log_level` | `LogLevel` | Max level to log. (off/normal/debug/critical) | `normal`/`critical` |
| `cli_colors` | `bool` | Whether to use colors and emoji when logging. | `true` |
| `secret_key` | `SecretKey` | Secret key for signing and encrypting values. | `None` |
| `tls` | `TlsConfig` | TLS configuration, if any. | `None` |
| `tls.key` | `&[u8]`/`&Path` | Path/bytes to DER-encoded ASN.1 PKCS#1/#8 key. | |
| `tls.certs` | `&[u8]`/`&Path` | Path/bytes to DER-encoded X.509 TLS cert chain. | |
| `limits` | `Limits` | Streaming read size limits. | [`Limits::default()`] |
| `limits.$name` | `&str`/`uint` | Read limit for `$name`. | forms = "32KiB" |
| `ctrlc` | `bool` | Whether `ctrl-c` initiates a server shutdown. | `true` |
| key | kind | description | debug/release default |
|----------------|-------------------|-------------------------------------------------|-------------------------|
| `address` | `IpAddr` | IP address to serve on | `127.0.0.1` |
| `port` | `u16` | Port to serve on. | `8000` |
| `workers` | `usize` | Number of threads to use for executing futures. | cpu core count |
| `ident` | `string`, `false` | If and how to identify via the `Server` header. | `"Rocket"` |
| `keep_alive` | `u32` | Keep-alive timeout seconds; disabled when `0`. | `5` |
| `log_level` | [`LogLevel`] | Max level to log. (off/normal/debug/critical) | `normal`/`critical` |
| `cli_colors` | `bool` | Whether to use colors and emoji when logging. | `true` |
| `secret_key` | [`SecretKey`] | Secret key for signing and encrypting values. | `None` |
| `tls` | [`TlsConfig`] | TLS configuration, if any. | `None` |
| `tls.key` | `&[u8]`/`&Path` | Path/bytes to DER-encoded ASN.1 PKCS#1/#8 key. | |
| `tls.certs` | `&[u8]`/`&Path` | Path/bytes to DER-encoded X.509 TLS cert chain. | |
| `limits` | [`Limits`] | Streaming read size limits. | [`Limits::default()`] |
| `limits.$name` | `&str`/`uint` | Read limit for `$name`. | forms = "32KiB" |
| `ctrlc` | `bool` | Whether `ctrl-c` initiates a server shutdown. | `true` |
| `shutdown` | [`Shutdown`] | Graceful shutdown configuration. | [`Shutdown::default()`] |
### Profiles
@ -58,7 +60,129 @@ selected profile doesn't contain a requested values, while values in the
[`Json`]: @figment/providers/struct.Json.html
[`Figment`]: @api/rocket/struct.Figment.html
[`Deserialize`]: @api/rocket/serde/trait.Deserialize.html
[`LogLevel`]: @api/rocket/config/enum.LogLevel.html
[`Limits`]: @api/rocket/data/struct.Limits.html
[`Limits::default()`]: @api/rocket/data/struct.Limits.html#impl-Default
[`SecretKey`]: @api/rocket/config/struct.SecretKey.html
[`TlsConfig`]: @api/rocket/config/struct.TlsConfig.html
[`Shutdown`]: @api/rocket/config/struct.Shutdown.html
[`Shutdown::default()`]: @api/rocket/config/struct.Shutdown.html#fields
## Default Provider
Rocket's default configuration provider is [`Config::figment()`]; this is the
provider that's used when calling [`rocket::build()`].
The default figment merges, at a per-key level, and reads from the following
sources, in ascending priority order:
1. [`Config::default()`] - which provides default values for all parameters.
2. `Rocket.toml` _or_ TOML file path in `ROCKET_CONFIG` environment variable.
3. `ROCKET_` prefixed environment variables.
The selected profile is the value of the `ROCKET_PROFILE` environment variable,
or if it is not set, "debug" when compiled in debug mode and "release" when
compiled in release mode.
As a result, without any effort, Rocket's server can be configured via a
`Rocket.toml` file and/or via environment variables, the latter of which take
precedence over the former. Note that neither the file nor any environment
variables need to be present as [`Config::default()`] is a complete
configuration source.
[`Config::default()`]: @api/rocket/struct.Config.html#method.default
### Rocket.toml
Rocket searches for `Rocket.toml` or the filename in a `ROCKET_CONFIG`
environment variable starting at the current working directory. If it is not
found, the parent directory, its parent, and so on, are searched until the file
is found or the root is reached. If the path set in `ROCKET_CONFIG` is absolute,
no such search occurs and the set path is used directly.
The file is assumed to be _nested_, so each top-level key declares a profile and
its values the value for the profile. The following is an example of what such a
file might look like:
```toml
## defaults for _all_ profiles
[default]
address = "0.0.0.0"
limits = { forms = "64 kB", json = "1 MiB" }
## set only when compiled in debug mode, i.e, `cargo build`
[debug]
port = 8000
## only the `json` key from `default` will be overridden; `forms` will remain
limits = { json = "10MiB" }
## set only when the `nyc` profile is selected
[nyc]
port = 9001
## set only when compiled in release mode, i.e, `cargo build --release`
## don't use this secret_key! generate your own and keep it private!
[release]
port = 9999
secret_key = "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="
```
The following is a `Rocket.toml` file with all configuration options set for
demonstratation purposes. You **do not** and _should not_ set a value for
configuration options needlessly, preferring to use the default value when
sensible.
```toml
[default]
address = "127.0.0.1"
port = 8000
workers = 16
keep_alive = 5
ident = "Rocket"
log_level = "normal"
temp_dir = "/tmp"
cli_colors = true
## NOTE: Don't (!) use this key! Generate your own!
secret_key = "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="
[default.limits]
forms = "64 kB"
json = "1 MiB"
msgpack = "2 MiB"
"file/jpg" = "5 MiB"
[default.tls]
certs = "path/to/cert-chain.pem"
key = "path/to/key.pem"
[default.shutdown]
ctrlc = true
signals = ["term", "hup"]
grace = 5
mercy = 5
```
### Environment Variables
Rocket reads all environment variable names prefixed with `ROCKET_` using the
string after the `_` as the name of a configuration value as the value of the
parameter as the value itself. Environment variables take precedence over values
in `Rocket.toml`. Values are parsed as loose form of TOML syntax. Consider the
following examples:
```sh
ROCKET_FLOAT=3.14
ROCKET_ARRAY=[1,"b",3.14]
ROCKET_STRING=Hello
ROCKET_STRING="Hello There"
ROCKET_KEEP_ALIVE=1
ROCKET_IDENT=Rocket
ROCKET_IDENT="Hello Rocket"
ROCKET_IDENT=false
ROCKET_TLS={certs="abc",key="foo/bar"}
ROCKET_LIMITS={forms="64 KiB"}
```
### Secret Key
@ -66,6 +190,10 @@ The `secret_key` parameter configures a cryptographic key to use when encrypting
application values. In particular, the key is used to encrypt [private cookies],
which are available only when the `secrets` crate feature is enabled.
Generating a string suitable for use as a `secret_key` configuration value is
usually done through tools like `openssl`. Using `openssl`, a 256-bit base64 key
can be generated with the command `openssl rand -base64 32`.
When compiled in debug mode, a fresh key is generated automatically. In release
mode, Rocket requires you to set a secret key if the `secrets` feature is
enabled. Failure to do so results in a hard error at launch time. The value of
@ -106,8 +234,8 @@ the expected value. When a path is configured in a file source, such as
`Rocket.toml`, relative paths are interpreted as being relative to the source
file's directory.
! warning: Rocket's built-in TLS implements only TLS 1.2 and 1.3. As such, it
may not be suitable for production use.
! warning: Rocket's built-in TLS implements only TLS 1.2 and 1.3. It may not be
suitable for production use.
### Workers
@ -120,83 +248,6 @@ only the values set by the `ROCKET_WORKERS` environment variable or in the
`workers` property of `Rocket.toml` will be considered - all other `workers`
values are ignored.
## Default Provider
Rocket's default configuration provider is [`Config::figment()`]; this is the
provider that's used when calling [`rocket::build()`].
The default figment merges, at a per-key level, and reads from the following
sources, in ascending priority order:
1. [`Config::default()`] - which provides default values for all parameters.
2. `Rocket.toml` _or_ TOML file path in `ROCKET_CONFIG` environment variable.
3. `ROCKET_` prefixed environment variables.
The selected profile is the value of the `ROCKET_PROFILE` environment variable,
or if it is not set, "debug" when compiled in debug mode and "release" when
compiled in release mode.
As a result, without any effort, Rocket's server can be configured via a
`Rocket.toml` file and/or via environment variables, the latter of which take
precedence over the former. Note that neither the file nor any environment
variables need to be present as [`Config::default()`] is a complete
configuration source.
[`Config::default()`]: @api/rocket/struct.Config.html#method.default
### Rocket.toml
Rocket searches for `Rocket.toml` or the filename in a `ROCKET_CONFIG`
environment variable starting at the current working directory. If it is not
found, the parent directory, its parent, and so on, are searched until the file
is found or the root is reached. If the path set in `ROCKET_CONFIG` is absolute,
no such search occurs, and the set path is used directly.
The file is assumed to be _nested_, so each top-level key declares a profile and
its values the value for the profile. The following is an example of what such a
file might look like:
```toml
## defaults for _all_ profiles
[default]
address = "0.0.0.0"
limits = { forms = "64 kB", json = "1 MiB" }
## set only when compiled in debug mode, i.e, `cargo build`
[debug]
port = 8000
## only the `json` key from `default` will be overridden; `forms` will remain
limits = { json = "10MiB" }
## set only when the `nyc` profile is selected
[nyc]
port = 9001
## set only when compiled in release mode, i.e, `cargo build --release`
## don't use this secret_key! generate your own and keep it private!
[release]
port = 9999
secret_key = "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="
```
### Environment Variables
Rocket reads all environment variable names prefixed with `ROCKET_` using the
string after the `_` as the name of a configuration value as the value of the
parameter as the value itself. Environment variables take precedence over values
in `Rocket.toml`. Values are parsed as loose form of TOML syntax. Consider the
following examples:
```sh
ROCKET_INTEGER=1
ROCKET_FLOAT=3.14
ROCKET_STRING=Hello
ROCKET_STRING="Hello"
ROCKET_BOOL=true
ROCKET_ARRAY=[1,"b",3.14]
ROCKET_DICT={key="abc",val=123}
```
## Extracting Values
Your application can extract any configuration that implements [`Deserialize`]