use figment::providers::{Env, Format, Serialized, Toml};
use figment::value::{magic::RelativePathBuf, Dict, Map};
use figment::{error::Result, Figment, Metadata, Profile, Provider};
use serde::{Deserialize, Serialize};
#[cfg(feature = "secrets")]
use crate::config::SecretKey;
use crate::config::{CliColors, Ident, Level, ShutdownConfig, TraceFormat};
use crate::data::Limits;
use crate::http::uncased::Uncased;
use crate::request::{self, FromRequest, Request};
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Config {
#[serde(skip)]
pub profile: Profile,
pub workers: usize,
pub max_blocking: usize,
pub ident: Ident,
#[serde(deserialize_with = "crate::config::http_header::deserialize")]
pub ip_header: Option<Uncased<'static>>,
#[serde(deserialize_with = "crate::config::http_header::deserialize")]
pub proxy_proto_header: Option<Uncased<'static>>,
pub limits: Limits,
#[serde(serialize_with = "RelativePathBuf::serialize_relative")]
pub temp_dir: RelativePathBuf,
pub keep_alive: u32,
#[cfg(feature = "secrets")]
#[cfg_attr(nightly, doc(cfg(feature = "secrets")))]
#[serde(serialize_with = "SecretKey::serialize_zero")]
pub secret_key: SecretKey,
pub shutdown: ShutdownConfig,
#[serde(with = "crate::trace::level")]
pub log_level: Option<Level>,
pub log_format: TraceFormat,
pub cli_colors: CliColors,
#[doc(hidden)]
#[serde(skip)]
pub __non_exhaustive: (),
}
impl Default for Config {
fn default() -> Config {
#[cfg(debug_assertions)]
{
Config::debug_default()
}
#[cfg(not(debug_assertions))]
{
Config::release_default()
}
}
}
impl Config {
pub fn debug_default() -> Config {
Config {
profile: Self::DEBUG_PROFILE,
workers: num_cpus::get(),
max_blocking: 512,
ident: Ident::default(),
ip_header: Some(Uncased::from_borrowed("X-Real-IP")),
proxy_proto_header: None,
limits: Limits::default(),
temp_dir: std::env::temp_dir().into(),
keep_alive: 5,
#[cfg(feature = "secrets")]
secret_key: SecretKey::zero(),
shutdown: ShutdownConfig::default(),
log_level: Some(Level::INFO),
log_format: TraceFormat::Pretty,
cli_colors: CliColors::Auto,
__non_exhaustive: (),
}
}
pub fn release_default() -> Config {
Config {
profile: Self::RELEASE_PROFILE,
log_level: Some(Level::ERROR),
log_format: TraceFormat::Compact,
..Config::debug_default()
}
}
pub fn figment() -> Figment {
Figment::from(Config::default())
.merge(Toml::file(Env::var_or("ROCKET_CONFIG", "Rocket.toml")).nested())
.merge(Env::prefixed("ROCKET_").ignore(&["PROFILE"]).global())
.select(Profile::from_env_or(
"ROCKET_PROFILE",
Self::DEFAULT_PROFILE,
))
}
pub fn try_from<T: Provider>(provider: T) -> Result<Self> {
let figment = Figment::from(provider);
let mut config = figment.extract::<Self>()?;
config.profile = figment.profile().clone();
Ok(config)
}
pub fn from<T: Provider>(provider: T) -> Self {
use crate::trace::Trace;
Self::try_from(provider).unwrap_or_else(|e| {
e.trace_error();
panic!("aborting due to configuration error(s)")
})
}
}
impl Config {
pub const DEBUG_PROFILE: Profile = Profile::const_new("debug");
pub const RELEASE_PROFILE: Profile = Profile::const_new("release");
#[cfg(debug_assertions)]
pub const DEFAULT_PROFILE: Profile = Self::DEBUG_PROFILE;
#[cfg(not(debug_assertions))]
pub const DEFAULT_PROFILE: Profile = Self::RELEASE_PROFILE;
}
impl Config {
pub const WORKERS: &'static str = "workers";
pub const MAX_BLOCKING: &'static str = "max_blocking";
pub const KEEP_ALIVE: &'static str = "keep_alive";
pub const IDENT: &'static str = "ident";
pub const IP_HEADER: &'static str = "ip_header";
pub const PROXY_PROTO_HEADER: &'static str = "proxy_proto_header";
pub const LIMITS: &'static str = "limits";
pub const SECRET_KEY: &'static str = "secret_key";
pub const TEMP_DIR: &'static str = "temp_dir";
pub const LOG_LEVEL: &'static str = "log_level";
pub const LOG_FORMAT: &'static str = "log_format";
pub const SHUTDOWN: &'static str = "shutdown";
pub const CLI_COLORS: &'static str = "cli_colors";
pub const PARAMETERS: &'static [&'static str] = &[
Self::WORKERS,
Self::MAX_BLOCKING,
Self::KEEP_ALIVE,
Self::IDENT,
Self::IP_HEADER,
Self::PROXY_PROTO_HEADER,
Self::LIMITS,
Self::SECRET_KEY,
Self::TEMP_DIR,
Self::LOG_LEVEL,
Self::LOG_FORMAT,
Self::SHUTDOWN,
Self::CLI_COLORS,
];
const PROFILE: &'static str = "profile";
pub(crate) const DEPRECATED_KEYS: &'static [(&'static str, Option<&'static str>)] = &[
("env", Some(Self::PROFILE)),
("log", Some(Self::LOG_LEVEL)),
("read_timeout", None),
("write_timeout", None),
];
#[cfg(feature = "secrets")]
pub(crate) const KNOWN_SECRET_KEYS: &'static [&'static str] =
&["hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="];
}
impl Provider for Config {
#[track_caller]
fn metadata(&self) -> Metadata {
if self == &Config::default() {
Metadata::named("rkt::Config::default()")
} else {
Metadata::named("rkt::Config")
}
}
#[track_caller]
fn data(&self) -> Result<Map<Profile, Dict>> {
#[allow(unused_mut)]
let mut map: Map<Profile, Dict> = Serialized::defaults(self).data()?;
#[cfg(feature = "secrets")]
if !self.secret_key.is_zero() {
if let Some(map) = map.get_mut(&Profile::Default) {
map.insert("secret_key".into(), self.secret_key.key.master().into());
}
}
Ok(map)
}
fn profile(&self) -> Option<Profile> {
Some(self.profile.clone())
}
}
#[crate::async_trait]
impl<'r> FromRequest<'r> for &'r Config {
type Error = std::convert::Infallible;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
request::Outcome::Success(req.rocket().config())
}
}