2017-03-16 05:10:09 +00:00
|
|
|
//! Types representing various errors that can occur in a Rocket application.
|
|
|
|
|
|
|
|
use std::{io, fmt};
|
2022-05-06 22:14:44 +00:00
|
|
|
use std::sync::{Arc, atomic::{Ordering, AtomicBool}};
|
|
|
|
use std::error::Error as StdError;
|
2017-03-16 05:10:09 +00:00
|
|
|
|
2017-08-19 01:37:25 +00:00
|
|
|
use yansi::Paint;
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
use figment::Profile;
|
2017-08-19 01:37:25 +00:00
|
|
|
|
2022-05-06 22:14:44 +00:00
|
|
|
use crate::{Rocket, Orbit};
|
|
|
|
|
2017-06-12 22:08:34 +00:00
|
|
|
/// An error that occurs during launch.
|
2017-03-16 05:10:09 +00:00
|
|
|
///
|
2022-05-06 22:14:44 +00:00
|
|
|
/// An `Error` is returned by [`launch()`](Rocket::launch()) when launching an
|
2022-09-07 12:42:21 +00:00
|
|
|
/// application fails or, more rarely, when the runtime fails after launching.
|
2017-03-16 05:10:09 +00:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// A value of this type panics if it is dropped without first being inspected.
|
|
|
|
/// An _inspection_ occurs when any method is called. For instance, if
|
2020-09-03 05:41:31 +00:00
|
|
|
/// `println!("Error: {}", e)` is called, where `e: Error`, the `Display::fmt`
|
|
|
|
/// method being called by `println!` results in `e` being marked as inspected;
|
|
|
|
/// a subsequent `drop` of the value will _not_ result in a panic. The following
|
|
|
|
/// snippet illustrates this:
|
2017-03-16 05:10:09 +00:00
|
|
|
///
|
2019-09-08 20:53:53 +00:00
|
|
|
/// ```rust
|
2020-06-14 15:57:55 +00:00
|
|
|
/// # let _ = async {
|
2021-04-08 08:07:52 +00:00
|
|
|
/// if let Err(error) = rocket::build().launch().await {
|
2020-09-03 05:41:31 +00:00
|
|
|
/// // This println "inspects" the error.
|
|
|
|
/// println!("Launch failed! Error: {}", error);
|
2017-03-16 05:10:09 +00:00
|
|
|
///
|
2020-09-03 05:41:31 +00:00
|
|
|
/// // This call to drop (explicit here for demonstration) will do nothing.
|
|
|
|
/// drop(error);
|
2019-09-08 20:53:53 +00:00
|
|
|
/// }
|
2020-06-14 15:57:55 +00:00
|
|
|
/// # };
|
2017-03-16 05:10:09 +00:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// When a value of this type panics, the corresponding error message is pretty
|
2017-06-12 22:08:34 +00:00
|
|
|
/// printed to the console. The following illustrates this:
|
2017-03-16 05:10:09 +00:00
|
|
|
///
|
|
|
|
/// ```rust
|
2020-06-14 15:57:55 +00:00
|
|
|
/// # let _ = async {
|
2021-04-08 08:07:52 +00:00
|
|
|
/// let error = rocket::build().launch().await;
|
2017-03-16 05:10:09 +00:00
|
|
|
///
|
|
|
|
/// // This call to drop (explicit here for demonstration) will result in
|
|
|
|
/// // `error` being pretty-printed to the console along with a `panic!`.
|
|
|
|
/// drop(error);
|
2020-06-14 15:57:55 +00:00
|
|
|
/// # };
|
2017-03-16 05:10:09 +00:00
|
|
|
/// ```
|
2017-06-12 22:08:34 +00:00
|
|
|
///
|
|
|
|
/// # Usage
|
|
|
|
///
|
2020-09-03 05:41:31 +00:00
|
|
|
/// An `Error` value should usually be allowed to `drop` without inspection.
|
|
|
|
/// There are at least two exceptions:
|
2017-06-12 22:08:34 +00:00
|
|
|
///
|
|
|
|
/// 1. If you are writing a library or high-level application on-top of
|
|
|
|
/// Rocket, you likely want to inspect the value before it drops to avoid a
|
|
|
|
/// Rocket-specific `panic!`. This typically means simply printing the
|
|
|
|
/// value.
|
|
|
|
///
|
|
|
|
/// 2. You want to display your own error messages.
|
2020-09-03 05:41:31 +00:00
|
|
|
pub struct Error {
|
2017-03-16 05:10:09 +00:00
|
|
|
handled: AtomicBool,
|
2020-09-03 05:41:31 +00:00
|
|
|
kind: ErrorKind
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The kind error that occurred.
|
|
|
|
///
|
|
|
|
/// In almost every instance, a launch error occurs because of an I/O error;
|
|
|
|
/// this is represented by the `Io` variant. A launch error may also occur
|
|
|
|
/// because of ill-defined routes that lead to collisions or because a fairing
|
|
|
|
/// encountered an error; these are represented by the `Collision` and
|
|
|
|
/// `FailedFairing` variants, respectively.
|
|
|
|
#[derive(Debug)]
|
Introduce sentinels: auto-discovered launch abort.
Sentinels resolve a long-standing usability and functional correctness
issue in Rocket: starting an application with guards and/or responders
that depend on state that isn't available. The canonical example is the
'State' guard. Prior to this commit, an application with routes that
queried unmanaged state via 'State' would fail at runtime. With this
commit, the application refuses to launch with a detailed error message.
The 'Sentinel' docs explains it as:
A sentinel, automatically run on ignition, can trigger a launch
abort should an instance fail to meet arbitrary conditions. Every
type that appears in a mounted route's type signature is eligible to
be a sentinel. Of these, those that implement 'Sentinel' have their
'abort()' method invoked automatically, immediately after ignition,
once for each unique type. Sentinels inspect the finalized instance
of 'Rocket' and can trigger a launch abort by returning 'true'.
The following types are now sentinels:
* 'contrib::databases::Connection' (any '#[database]' type)
* 'contrib::templates::Metadata'
* 'contrib::templates::Template'
* 'core::State'
The following are "specialized" sentinels, which allow sentinel
discovery even through type aliases:
* 'Option<T>', 'Debug<T>' if 'T: Sentinel'
* 'Result<T, E>', 'Either<T, E>' if 'T: Sentinel', 'E: Sentinel'
Closes #464.
2021-04-16 08:23:15 +00:00
|
|
|
#[non_exhaustive]
|
2020-09-03 05:41:31 +00:00
|
|
|
pub enum ErrorKind {
|
|
|
|
/// Binding to the provided address/port failed.
|
|
|
|
Bind(io::Error),
|
|
|
|
/// An I/O error occurred during launch.
|
|
|
|
Io(io::Error),
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
/// A valid [`Config`](crate::Config) could not be extracted from the
|
|
|
|
/// configured figment.
|
|
|
|
Config(figment::Error),
|
2020-09-03 05:41:31 +00:00
|
|
|
/// Route collisions were detected.
|
2021-03-26 04:36:00 +00:00
|
|
|
Collisions(crate::router::Collisions),
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
/// Launch fairing(s) failed.
|
|
|
|
FailedFairings(Vec<crate::fairing::Info>),
|
Introduce sentinels: auto-discovered launch abort.
Sentinels resolve a long-standing usability and functional correctness
issue in Rocket: starting an application with guards and/or responders
that depend on state that isn't available. The canonical example is the
'State' guard. Prior to this commit, an application with routes that
queried unmanaged state via 'State' would fail at runtime. With this
commit, the application refuses to launch with a detailed error message.
The 'Sentinel' docs explains it as:
A sentinel, automatically run on ignition, can trigger a launch
abort should an instance fail to meet arbitrary conditions. Every
type that appears in a mounted route's type signature is eligible to
be a sentinel. Of these, those that implement 'Sentinel' have their
'abort()' method invoked automatically, immediately after ignition,
once for each unique type. Sentinels inspect the finalized instance
of 'Rocket' and can trigger a launch abort by returning 'true'.
The following types are now sentinels:
* 'contrib::databases::Connection' (any '#[database]' type)
* 'contrib::templates::Metadata'
* 'contrib::templates::Template'
* 'core::State'
The following are "specialized" sentinels, which allow sentinel
discovery even through type aliases:
* 'Option<T>', 'Debug<T>' if 'T: Sentinel'
* 'Result<T, E>', 'Either<T, E>' if 'T: Sentinel', 'E: Sentinel'
Closes #464.
2021-04-16 08:23:15 +00:00
|
|
|
/// Sentinels requested abort.
|
|
|
|
SentinelAborts(Vec<crate::sentinel::Sentry>),
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
/// The configuration profile is not debug but not secret key is configured.
|
|
|
|
InsecureSecretKey(Profile),
|
2022-05-06 22:14:44 +00:00
|
|
|
/// Shutdown failed.
|
|
|
|
Shutdown(
|
|
|
|
/// The instance of Rocket that failed to shutdown.
|
|
|
|
Arc<Rocket<Orbit>>,
|
|
|
|
/// The error that occurred during shutdown, if any.
|
|
|
|
Option<Box<dyn StdError + Send + Sync>>
|
|
|
|
),
|
2017-03-16 05:10:09 +00:00
|
|
|
}
|
|
|
|
|
2020-09-03 05:41:31 +00:00
|
|
|
impl From<ErrorKind> for Error {
|
|
|
|
fn from(kind: ErrorKind) -> Self {
|
|
|
|
Error::new(kind)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Error {
|
2017-03-16 05:10:09 +00:00
|
|
|
#[inline(always)]
|
2020-09-03 05:41:31 +00:00
|
|
|
pub(crate) fn new(kind: ErrorKind) -> Error {
|
|
|
|
Error { handled: AtomicBool::new(false), kind }
|
2017-03-16 05:10:09 +00:00
|
|
|
}
|
|
|
|
|
2022-05-06 22:14:44 +00:00
|
|
|
#[inline(always)]
|
|
|
|
pub(crate) fn shutdown<E>(rocket: Arc<Rocket<Orbit>>, error: E) -> Error
|
|
|
|
where E: Into<Option<crate::http::hyper::Error>>
|
|
|
|
{
|
|
|
|
let error = error.into().map(|e| Box::new(e) as Box<dyn StdError + Sync + Send>);
|
|
|
|
Error::new(ErrorKind::Shutdown(rocket, error))
|
|
|
|
}
|
|
|
|
|
2017-03-16 05:10:09 +00:00
|
|
|
#[inline(always)]
|
|
|
|
fn was_handled(&self) -> bool {
|
|
|
|
self.handled.load(Ordering::Acquire)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn mark_handled(&self) {
|
|
|
|
self.handled.store(true, Ordering::Release)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve the `kind` of the launch error.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
2020-09-03 05:41:31 +00:00
|
|
|
/// use rocket::error::ErrorKind;
|
|
|
|
///
|
2020-06-14 15:57:55 +00:00
|
|
|
/// # let _ = async {
|
2021-04-08 08:07:52 +00:00
|
|
|
/// if let Err(error) = rocket::build().launch().await {
|
2020-09-03 05:41:31 +00:00
|
|
|
/// match error.kind() {
|
|
|
|
/// ErrorKind::Io(e) => println!("found an i/o launch error: {}", e),
|
|
|
|
/// e => println!("something else happened: {}", e)
|
2019-08-25 02:19:11 +00:00
|
|
|
/// }
|
|
|
|
/// }
|
2020-06-14 15:57:55 +00:00
|
|
|
/// # };
|
2017-03-16 05:10:09 +00:00
|
|
|
/// ```
|
|
|
|
#[inline]
|
2020-09-03 05:41:31 +00:00
|
|
|
pub fn kind(&self) -> &ErrorKind {
|
2017-03-16 05:10:09 +00:00
|
|
|
self.mark_handled();
|
|
|
|
&self.kind
|
|
|
|
}
|
|
|
|
|
2023-05-05 18:41:10 +00:00
|
|
|
/// Prints the error with color (if enabled) and detail. Returns a string
|
|
|
|
/// that indicates the abort condition such as "aborting due to i/o error".
|
|
|
|
///
|
|
|
|
/// This function is called on `Drop` to display the error message. By
|
|
|
|
/// contrast, the `Display` implementation prints a succinct version of the
|
|
|
|
/// error, without detail.
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # let _ = async {
|
|
|
|
/// if let Err(error) = rocket::build().launch().await {
|
|
|
|
/// let abort = error.pretty_print();
|
|
|
|
/// panic!("{}", abort);
|
|
|
|
/// }
|
|
|
|
/// # };
|
|
|
|
/// ```
|
|
|
|
pub fn pretty_print(&self) -> &'static str {
|
2017-03-16 05:10:09 +00:00
|
|
|
self.mark_handled();
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
match self.kind() {
|
2020-09-03 05:41:31 +00:00
|
|
|
ErrorKind::Bind(ref e) => {
|
2018-04-09 00:38:25 +00:00
|
|
|
error!("Rocket failed to bind network socket to given address/port.");
|
2020-09-03 05:41:31 +00:00
|
|
|
info_!("{}", e);
|
2023-05-05 18:41:10 +00:00
|
|
|
"aborting due to socket bind error"
|
2018-02-01 20:26:02 +00:00
|
|
|
}
|
2020-09-03 05:41:31 +00:00
|
|
|
ErrorKind::Io(ref e) => {
|
2017-03-16 05:10:09 +00:00
|
|
|
error!("Rocket failed to launch due to an I/O error.");
|
2020-09-03 05:41:31 +00:00
|
|
|
info_!("{}", e);
|
2023-05-05 18:41:10 +00:00
|
|
|
"aborting due to i/o error"
|
2017-03-16 05:10:09 +00:00
|
|
|
}
|
2021-03-26 04:36:00 +00:00
|
|
|
ErrorKind::Collisions(ref collisions) => {
|
|
|
|
fn log_collisions<T: fmt::Display>(kind: &str, collisions: &[(T, T)]) {
|
|
|
|
if collisions.is_empty() { return }
|
|
|
|
|
|
|
|
error!("Rocket failed to launch due to the following {} collisions:", kind);
|
|
|
|
for &(ref a, ref b) in collisions {
|
|
|
|
info_!("{} {} {}", a, Paint::red("collides with").italic(), b)
|
|
|
|
}
|
2017-08-19 01:37:25 +00:00
|
|
|
}
|
|
|
|
|
2021-03-26 04:36:00 +00:00
|
|
|
log_collisions("route", &collisions.routes);
|
|
|
|
log_collisions("catcher", &collisions.catchers);
|
|
|
|
|
|
|
|
info_!("Note: Route collisions can usually be resolved by ranking routes.");
|
2023-05-05 18:41:10 +00:00
|
|
|
"aborting due to detected routing collisions"
|
2017-04-19 00:42:44 +00:00
|
|
|
}
|
2020-09-03 05:41:31 +00:00
|
|
|
ErrorKind::FailedFairings(ref failures) => {
|
2018-02-21 11:08:54 +00:00
|
|
|
error!("Rocket failed to launch due to failing fairings:");
|
|
|
|
for fairing in failures {
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
info_!("{}", fairing.name);
|
2018-02-21 11:08:54 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 18:41:10 +00:00
|
|
|
"aborting due to fairing failure(s)"
|
2020-09-03 05:41:31 +00:00
|
|
|
}
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
ErrorKind::InsecureSecretKey(profile) => {
|
|
|
|
error!("secrets enabled in non-debug without `secret_key`");
|
Introduce sentinels: auto-discovered launch abort.
Sentinels resolve a long-standing usability and functional correctness
issue in Rocket: starting an application with guards and/or responders
that depend on state that isn't available. The canonical example is the
'State' guard. Prior to this commit, an application with routes that
queried unmanaged state via 'State' would fail at runtime. With this
commit, the application refuses to launch with a detailed error message.
The 'Sentinel' docs explains it as:
A sentinel, automatically run on ignition, can trigger a launch
abort should an instance fail to meet arbitrary conditions. Every
type that appears in a mounted route's type signature is eligible to
be a sentinel. Of these, those that implement 'Sentinel' have their
'abort()' method invoked automatically, immediately after ignition,
once for each unique type. Sentinels inspect the finalized instance
of 'Rocket' and can trigger a launch abort by returning 'true'.
The following types are now sentinels:
* 'contrib::databases::Connection' (any '#[database]' type)
* 'contrib::templates::Metadata'
* 'contrib::templates::Template'
* 'core::State'
The following are "specialized" sentinels, which allow sentinel
discovery even through type aliases:
* 'Option<T>', 'Debug<T>' if 'T: Sentinel'
* 'Result<T, E>', 'Either<T, E>' if 'T: Sentinel', 'E: Sentinel'
Closes #464.
2021-04-16 08:23:15 +00:00
|
|
|
info_!("selected profile: {}", Paint::default(profile).bold());
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
info_!("disable `secrets` feature or configure a `secret_key`");
|
2023-05-05 18:41:10 +00:00
|
|
|
"aborting due to insecure configuration"
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
}
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
ErrorKind::Config(error) => {
|
|
|
|
crate::config::pretty_print_error(error.clone());
|
2023-05-05 18:41:10 +00:00
|
|
|
"aborting due to invalid configuration"
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
}
|
Introduce sentinels: auto-discovered launch abort.
Sentinels resolve a long-standing usability and functional correctness
issue in Rocket: starting an application with guards and/or responders
that depend on state that isn't available. The canonical example is the
'State' guard. Prior to this commit, an application with routes that
queried unmanaged state via 'State' would fail at runtime. With this
commit, the application refuses to launch with a detailed error message.
The 'Sentinel' docs explains it as:
A sentinel, automatically run on ignition, can trigger a launch
abort should an instance fail to meet arbitrary conditions. Every
type that appears in a mounted route's type signature is eligible to
be a sentinel. Of these, those that implement 'Sentinel' have their
'abort()' method invoked automatically, immediately after ignition,
once for each unique type. Sentinels inspect the finalized instance
of 'Rocket' and can trigger a launch abort by returning 'true'.
The following types are now sentinels:
* 'contrib::databases::Connection' (any '#[database]' type)
* 'contrib::templates::Metadata'
* 'contrib::templates::Template'
* 'core::State'
The following are "specialized" sentinels, which allow sentinel
discovery even through type aliases:
* 'Option<T>', 'Debug<T>' if 'T: Sentinel'
* 'Result<T, E>', 'Either<T, E>' if 'T: Sentinel', 'E: Sentinel'
Closes #464.
2021-04-16 08:23:15 +00:00
|
|
|
ErrorKind::SentinelAborts(ref failures) => {
|
|
|
|
error!("Rocket failed to launch due to aborting sentinels:");
|
|
|
|
for sentry in failures {
|
|
|
|
let name = Paint::default(sentry.type_name).bold();
|
|
|
|
let (file, line, col) = sentry.location;
|
|
|
|
info_!("{} ({}:{}:{})", name, file, line, col);
|
|
|
|
}
|
|
|
|
|
2023-05-05 18:41:10 +00:00
|
|
|
"aborting due to sentinel-triggered abort(s)"
|
Introduce sentinels: auto-discovered launch abort.
Sentinels resolve a long-standing usability and functional correctness
issue in Rocket: starting an application with guards and/or responders
that depend on state that isn't available. The canonical example is the
'State' guard. Prior to this commit, an application with routes that
queried unmanaged state via 'State' would fail at runtime. With this
commit, the application refuses to launch with a detailed error message.
The 'Sentinel' docs explains it as:
A sentinel, automatically run on ignition, can trigger a launch
abort should an instance fail to meet arbitrary conditions. Every
type that appears in a mounted route's type signature is eligible to
be a sentinel. Of these, those that implement 'Sentinel' have their
'abort()' method invoked automatically, immediately after ignition,
once for each unique type. Sentinels inspect the finalized instance
of 'Rocket' and can trigger a launch abort by returning 'true'.
The following types are now sentinels:
* 'contrib::databases::Connection' (any '#[database]' type)
* 'contrib::templates::Metadata'
* 'contrib::templates::Template'
* 'core::State'
The following are "specialized" sentinels, which allow sentinel
discovery even through type aliases:
* 'Option<T>', 'Debug<T>' if 'T: Sentinel'
* 'Result<T, E>', 'Either<T, E>' if 'T: Sentinel', 'E: Sentinel'
Closes #464.
2021-04-16 08:23:15 +00:00
|
|
|
}
|
2022-05-06 22:14:44 +00:00
|
|
|
ErrorKind::Shutdown(_, error) => {
|
|
|
|
error!("Rocket failed to shutdown gracefully.");
|
|
|
|
if let Some(e) = error {
|
|
|
|
info_!("{}", e);
|
|
|
|
}
|
|
|
|
|
2023-05-05 18:41:10 +00:00
|
|
|
"aborting due to failed shutdown"
|
2022-05-06 22:14:44 +00:00
|
|
|
}
|
2017-03-16 05:10:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-05 18:41:10 +00:00
|
|
|
|
|
|
|
impl std::error::Error for Error { }
|
|
|
|
|
|
|
|
impl fmt::Display for ErrorKind {
|
|
|
|
#[inline]
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
ErrorKind::Bind(e) => write!(f, "binding failed: {}", e),
|
|
|
|
ErrorKind::Io(e) => write!(f, "I/O error: {}", e),
|
|
|
|
ErrorKind::Collisions(_) => "collisions detected".fmt(f),
|
|
|
|
ErrorKind::FailedFairings(_) => "launch fairing(s) failed".fmt(f),
|
|
|
|
ErrorKind::InsecureSecretKey(_) => "insecure secret key config".fmt(f),
|
|
|
|
ErrorKind::Config(_) => "failed to extract configuration".fmt(f),
|
|
|
|
ErrorKind::SentinelAborts(_) => "sentinel(s) aborted".fmt(f),
|
|
|
|
ErrorKind::Shutdown(_, Some(e)) => write!(f, "shutdown failed: {}", e),
|
|
|
|
ErrorKind::Shutdown(_, None) => "shutdown failed".fmt(f),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for Error {
|
|
|
|
#[inline]
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
self.mark_handled();
|
|
|
|
self.kind().fmt(f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Error {
|
|
|
|
#[inline]
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
self.mark_handled();
|
|
|
|
write!(f, "{}", self.kind())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Error {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
// Don't panic if the message has been seen. Don't double-panic.
|
|
|
|
if self.was_handled() || std::thread::panicking() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
panic!("{}", self.pretty_print());
|
|
|
|
}
|
|
|
|
}
|