Skip to content

Commit

Permalink
wip: refactor into traceable trait
Browse files Browse the repository at this point in the history
  • Loading branch information
SergioBenitez committed May 10, 2024
1 parent 543ef73 commit 5950018
Show file tree
Hide file tree
Showing 8 changed files with 390 additions and 234 deletions.
142 changes: 14 additions & 128 deletions core/lib/src/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::config::{ShutdownConfig, Ident, CliColors};
use crate::request::{self, Request, FromRequest};
use crate::http::uncased::Uncased;
use crate::data::Limits;
use crate::util::Formatter;
use crate::trace::traceable::Traceable;

/// Rocket server configuration.
///
Expand Down Expand Up @@ -303,98 +303,6 @@ impl Config {
pub fn from<T: Provider>(provider: T) -> Self {
Self::try_from(provider).unwrap_or_else(bail_with_config_error)
}

#[cfg(feature = "secrets")]
fn known_secret_key_used(&self) -> bool {
const KNOWN_SECRET_KEYS: &[&str] = &[
"hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="
];

KNOWN_SECRET_KEYS.iter().any(|&key_str| {
let value = figment::value::Value::from(key_str);
self.secret_key == value.deserialize().expect("known key is valid")
})
}

#[tracing::instrument(name = "config", skip_all, fields(profile = %figment.profile()))]
pub(crate) fn pretty_print(&self, figment: &Figment) {
info! {
name: "values",
http2 = cfg!(feature = "http2"),
log_level = self.log_level.map(|l| l.as_str()),
cli_colors = %self.cli_colors,
workers = self.workers,
max_blocking = self.max_blocking,
ident = %self.ident,
ip_header = self.ip_header.as_ref().map(|s| s.as_str()),
proxy_proto_header = self.proxy_proto_header.as_ref().map(|s| s.as_str()),
limits = %Formatter(|f| f.debug_map()
.entries(self.limits.limits.iter().map(|(k, v)| (k.as_str(), v.to_string())))
.finish()),
temp_dir = %self.temp_dir.relative().display(),
keep_alive = (self.keep_alive != 0).then_some(self.keep_alive),
shutdown.ctrlc = self.shutdown.ctrlc,
shutdown.signals = %{
#[cfg(not(unix))] {
"disabled (not unix)"
}

#[cfg(unix)] {
Formatter(|f| f.debug_set()
.entries(self.shutdown.signals.iter().map(|s| s.as_str()))
.finish())
}
},
shutdown.grace = self.shutdown.grace,
shutdown.mercy = self.shutdown.mercy,
shutdown.force = self.shutdown.force,
};

for param in Self::PARAMETERS {
if let Some(source) = figment.find_metadata(param) {
trace! {
param,
%source.name,
source.source = source.source.as_ref().map(|s| s.to_string()),
}
}
}

// Check for now deprecated config values.
for (key, replacement) in Self::DEPRECATED_KEYS {
if let Some(source) = figment.find_metadata(key) {
warn! {
name: "deprecated",
key,
replacement,
%source.name,
source.source = source.source.as_ref().map(|s| s.to_string()),
"config key `{key}` is deprecated and has no meaning"
}
}
}

#[cfg(feature = "secrets")] {
if !self.secret_key.is_provided() {
warn! {
name: "volatile_secret_key",
"secrets enabled without configuring a stable `secret_key`; \
private/signed cookies will become unreadable after restarting; \
disable the `secrets` feature or configure a `secret_key`; \
this becomes a hard error in non-debug profiles",
}
}

if self.known_secret_key_used() {
warn! {
name: "insecure_secret_key",
"The configured `secret_key` is exposed and insecure. \
The configured key is publicly published and thus insecure. \
Try generating a new key with `head -c64 /dev/urandom | base64`."
}
}
}
}
}

/// Associated constants for default profiles.
Expand All @@ -416,11 +324,6 @@ impl Config {

/// Associated constants for stringy versions of configuration parameters.
impl Config {
/// The stringy parameter name for setting/extracting [`Config::profile`].
///
/// This isn't `pub` because setting it directly does nothing.
const PROFILE: &'static str = "profile";

/// The stringy parameter name for setting/extracting [`Config::workers`].
pub const WORKERS: &'static str = "workers";

Expand Down Expand Up @@ -465,12 +368,22 @@ impl Config {
Self::CLI_COLORS,
];

/// The stringy parameter name for setting/extracting [`Config::profile`].
///
/// This isn't `pub` because setting it directly does nothing.
const PROFILE: &'static str = "profile";

/// An array of deprecated stringy parameter names.
const DEPRECATED_KEYS: &'static [(&'static str, Option<&'static str>)] = &[
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),
];

/// Secret keys that have been used in docs or leaked otherwise.
#[cfg(feature = "secrets")]
pub(crate) const KNOWN_SECRET_KEYS: &'static [&'static str] = &[
"hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="
];
}

impl Provider for Config {
Expand Down Expand Up @@ -520,34 +433,7 @@ pub fn bail_with_config_error<T>(error: figment::Error) -> T {
}

#[doc(hidden)]
// FIXME: Remove this funtion.
pub fn pretty_print_error(error: figment::Error) {
use figment::error::{OneOf as V, Kind::*};

for e in error {
let span = tracing::error_span! {
"error: configuration",
key = (!e.path.is_empty()).then_some(&e.path).and_then(|path| {
let (profile, metadata) = (e.profile.as_ref()?, e.metadata.as_ref()?);
Some(metadata.interpolate(profile, path))
}),
source.name = e.metadata.as_ref().map(|m| &*m.name),
source.source = e.metadata.as_ref().and_then(|m| Some(m.source.as_ref()?.to_string())),
};

let _scope = span.enter();
match e.kind {
Message(message) => error!(message),
InvalidType(actual, expected) => error!(name: "invalid type", %actual, expected),
InvalidValue(actual, expected) => error!(name: "invalid value", %actual, expected),
InvalidLength(actual, expected) => error!(name: "invalid length", %actual, expected),
UnknownVariant(actual, v) => error!(name: "unknown variant", actual, expected = %V(v)),
UnknownField(actual, v) => error!(name: "unknown field", actual, expected = %V(v)),
UnsupportedKey(actual, v) => error!(name: "unsupported key", %actual, expected = &*v),
MissingField(value) => error!(name: "missing field", value = &*value),
DuplicateField(value) => error!(name: "duplicate field", value),
ISizeOutOfRange(value) => error!(name: "out of range signed integer", value),
USizeOutOfRange(value) => error!(name: "out of range unsigned integer", value),
Unsupported(value) => error!(name: "unsupported type", %value),
}
}
error.trace()
}
28 changes: 16 additions & 12 deletions core/lib/src/fairing/fairings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ impl Fairings {
.chain(self.shutdown.iter())
}

pub fn unique_set(&self) -> Vec<&dyn Fairing> {
iter!(self, self.active().collect::<HashSet<_>>().into_iter())
.map(|v| v.1)
.collect()
// .into_iter()
// .map(|i| )
// if !active_fairings.is_empty() {
// tracing::info_span!("fairings").in_scope(|| {
// for (_, fairing) in iter!(self, active_fairings.into_iter()) {
// let (name, kind) = (fairing.info().name, fairing.info().kind);
// info!(name: "fairing", name, %kind)
// }
// });
// }
}

pub fn add(&mut self, fairing: Box<dyn Fairing>) {
let this = &fairing;
let this_info = this.info();
Expand Down Expand Up @@ -166,18 +182,6 @@ impl Fairings {
false => Err(&self.failures)
}
}

pub fn pretty_print(&self) {
let active_fairings = self.active().collect::<HashSet<_>>();
if !active_fairings.is_empty() {
tracing::info_span!("fairings").in_scope(|| {
for (_, fairing) in iter!(self, active_fairings.into_iter()) {
let (name, kind) = (fairing.info().name, fairing.info().kind);
info!(name: "fairing", name, %kind)
}
});
}
}
}

impl std::fmt::Debug for Fairings {
Expand Down
41 changes: 36 additions & 5 deletions core/lib/src/rocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use figment::{Figment, Provider};
use futures::TryFutureExt;

use crate::shutdown::{Stages, Shutdown};
use crate::trace::traceable::{Traceable, TraceableCollection};
use crate::{sentinel, shield::Shield, Catcher, Config, Route};
use crate::listener::{Bind, DefaultListener, Endpoint, Listener};
use crate::router::Router;
Expand Down Expand Up @@ -567,10 +568,40 @@ impl Rocket<Build> {

// Log everything we know: config, routes, catchers, fairings.
// TODO: Store/print managed state type names?
config.pretty_print(self.figment());
log_items("routes", self.routes(), |r| &r.uri.base, |r| &r.uri);
log_items("catchers", self.catchers(), |c| &c.base, |c| &c.base);
self.fairings.pretty_print();
let fairings = self.fairings.unique_set();
info_span!("config" [profile = %self.figment().profile()] => {
config.trace();
self.figment().trace();
});

info_span!("routes" [count = self.routes.len()] => self.routes().trace_all());
info_span!("catchers" [count = self.catchers.len()] => self.catchers().trace_all());
info_span!("fairings" [count = fairings.len()] => fairings.trace_all());

// trace::all("routes", self.routes());
// tracing::info_span!("routes").in_scope(|| self.routes().for_each(|r| r.trace()));
// tracing::info_span!("catchers").in_scope(|| self.catchers().for_each(|c| c.trace()));
// for header in self.policies.values() {
// info!(name: "header", name = header.name().as_str(), value = header.value());
// }
//
// warn!("Detected TLS-enabled liftoff without enabling HSTS.");
// warn!("Shield has enabled a default HSTS policy.");
// info!("To remove this warning, configure an HSTS policy.");
// });

// tracing::info_span!("routes")
// .in_scope(|| self.routes().for_each(|r| r.trace()));
// // self.polices.values().trace();
// // for header in self.policies.values() {
// // info!(name: "header", name = header.name().as_str(), value = header.value());
// // }

// TODO: Store/print managed state type names?
// trace::collection_info!("routes" => self.routes());
// trace::collection_info!("catchers" => self.catchers());
// trace::collection_info!("fairings" => self.fairings.active_set());
// trace::collection_info!("state" => self.active_set());

// Ignite the rocket.
let rocket: Rocket<Ignite> = Rocket(Igniting {
Expand Down Expand Up @@ -759,7 +790,7 @@ impl Rocket<Orbit> {
info_!("Forced shutdown is disabled. Runtime settings may be suboptimal.");
}

tracing::info!(target: "rocket::liftoff", endpoint = %rocket.endpoints[0]);
tracing::info!(name: "liftoff", endpoint = %rocket.endpoints[0]);
}

/// Returns the finalized, active configuration. This is guaranteed to
Expand Down
32 changes: 25 additions & 7 deletions core/lib/src/shield/shield.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{Rocket, Request, Response, Orbit, Config};
use crate::fairing::{Fairing, Info, Kind};
use crate::http::{Header, uncased::UncasedStr};
use crate::shield::*;
use crate::trace::traceable::*;

/// A [`Fairing`] that injects browser security and privacy headers into all
/// outgoing responses.
Expand Down Expand Up @@ -192,15 +193,32 @@ impl Fairing for Shield {
&& rocket.figment().profile() != Config::DEBUG_PROFILE
&& !self.is_enabled::<Hsts>();

tracing::info_span!("shield", force_hsts).in_scope(|| {
for header in self.policies.values() {
info!(name: "header", name = header.name().as_str(), value = header.value());
info_span!("shield" [policies = self.policies.len()] => {
self.policies.values().trace_all();

if force_hsts {
warn!("Detected TLS-enabled liftoff without enabling HSTS.");
warn!("Shield has enabled a default HSTS policy.");
info!("To remove this warning, configure an HSTS policy.");
}
})

// trace::collection_info!("shield", force_hsts => self.polices.values(), {
// warn!("Detected TLS-enabled liftoff without enabling HSTS.");
// warn!("Shield has enabled a default HSTS policy.");
// info!("To remove this warning, configure an HSTS policy.");
// });

warn!("Detected TLS-enabled liftoff without enabling HSTS.");
warn!("Shield has enabled a default HSTS policy.");
info!("To remove this warning, configure an HSTS policy.");
});
// // tracing::info_span!("shield", force_hsts).in_scope(|| {
// // self.polices.values().trace();
// // for header in self.policies.values() {
// // info!(name: "header", name = header.name().as_str(), value = header.value());
// // }
//
// warn!("Detected TLS-enabled liftoff without enabling HSTS.");
// warn!("Shield has enabled a default HSTS policy.");
// info!("To remove this warning, configure an HSTS policy.");
// });
}

async fn on_response<'r>(&self, _: &'r Request<'_>, response: &mut Response<'r>) {
Expand Down
55 changes: 55 additions & 0 deletions core/lib/src/trace/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
pub trait PaintExt: Sized {
fn emoji(self) -> yansi::Painted<Self>;
}

impl PaintExt for &str {
/// Paint::masked(), but hidden on Windows due to broken output. See #1122.
fn emoji(self) -> yansi::Painted<Self> {
#[cfg(windows)] { yansi::Paint::new("").mask() }
#[cfg(not(windows))] { yansi::Paint::new(self).mask() }
}
}

macro_rules! declare_macro {
($($name:ident $level:ident),* $(,)?) => (
$(declare_macro!([$] $name $level);)*
);

([$d:tt] $name:ident $level:ident) => (
#[macro_export]
macro_rules! $name {
($d ($t:tt)*) => ({
#[allow(unused_imports)]
use $crate::trace::macros::PaintExt as _;

$crate::tracing::$level!($d ($t)*);
})
}
);
}

declare_macro!(
// launch_meta INFO, launch_meta_ INFO,
error error, error_ error,
info info, info_ info,
trace trace, trace_ trace,
debug debug, debug_ debug,
warn warn, warn_ warn,
);

macro_rules! declare_span_macro {
($($name:ident $level:ident),* $(,)?) => (
$(declare_span_macro!([$] $name $level);)*
);

([$d:tt] $name:ident $level:ident) => (
#[macro_export]
macro_rules! $name {
($n:literal $d ([ $d ($f:tt)* ])? => $in_scope:expr) => ({
$crate::tracing::span!(tracing::Level::$level, $n $d (, $d ($f)* )?).in_scope(|| $in_scope);
})
}
);
}

declare_span_macro!(info_span INFO, trace_span TRACE);
Loading

0 comments on commit 5950018

Please sign in to comment.