2021-05-11 13:56:35 +00:00
|
|
|
use std::fmt;
|
2017-01-21 03:31:46 +00:00
|
|
|
use std::ops::Deref;
|
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
|
|
|
use std::any::type_name;
|
2017-01-21 03:31:46 +00:00
|
|
|
|
2021-05-11 13:56:35 +00:00
|
|
|
use ref_cast::RefCast;
|
|
|
|
|
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
|
|
|
use crate::{Phase, Rocket, Ignite, Sentinel};
|
2019-06-13 01:48:02 +00:00
|
|
|
use crate::request::{self, FromRequest, Request};
|
|
|
|
use crate::outcome::Outcome;
|
|
|
|
use crate::http::Status;
|
2017-01-21 03:31:46 +00:00
|
|
|
|
2017-02-03 02:00:18 +00:00
|
|
|
/// Request guard to retrieve managed state.
|
|
|
|
///
|
2021-05-11 13:56:35 +00:00
|
|
|
/// A reference `&State<T>` type is a request guard which retrieves the managed
|
|
|
|
/// state managing for some type `T`. A value for the given type must previously
|
|
|
|
/// have been registered to be managed by Rocket via [`Rocket::manage()`]. The
|
|
|
|
/// type being managed must be thread safe and sendable across thread
|
|
|
|
/// boundaries as multiple handlers in multiple threads may be accessing the
|
|
|
|
/// value at once. In other words, it must implement [`Send`] + [`Sync`] +
|
|
|
|
/// `'static`.
|
2017-02-03 02:00:18 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// Imagine you have some configuration struct of the type `MyConfig` that you'd
|
|
|
|
/// like to initialize at start-up and later access it in several handlers. The
|
|
|
|
/// following example does just this:
|
|
|
|
///
|
2020-06-16 12:01:26 +00:00
|
|
|
/// ```rust,no_run
|
2018-06-28 15:55:15 +00:00
|
|
|
/// # #[macro_use] extern crate rocket;
|
2017-02-03 02:00:18 +00:00
|
|
|
/// use rocket::State;
|
|
|
|
///
|
|
|
|
/// // In a real application, this would likely be more complex.
|
2017-07-03 22:13:21 +00:00
|
|
|
/// struct MyConfig {
|
|
|
|
/// user_val: String
|
|
|
|
/// }
|
2017-02-03 02:00:18 +00:00
|
|
|
///
|
|
|
|
/// #[get("/")]
|
2021-05-11 13:56:35 +00:00
|
|
|
/// fn index(state: &State<MyConfig>) -> String {
|
2017-07-03 22:13:21 +00:00
|
|
|
/// format!("The config value is: {}", state.user_val)
|
2017-02-03 02:00:18 +00:00
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// #[get("/raw")]
|
2021-05-11 13:56:35 +00:00
|
|
|
/// fn raw_config_value(state: &State<MyConfig>) -> &str {
|
|
|
|
/// &state.user_val
|
2017-02-03 02:00:18 +00:00
|
|
|
/// }
|
|
|
|
///
|
2020-07-22 23:10:02 +00:00
|
|
|
/// #[launch]
|
2021-04-14 01:12:39 +00:00
|
|
|
/// fn rocket() -> _ {
|
2021-04-08 08:07:52 +00:00
|
|
|
/// rocket::build()
|
2017-02-03 02:00:18 +00:00
|
|
|
/// .mount("/", routes![index, raw_config_value])
|
2020-06-16 12:01:26 +00:00
|
|
|
/// .manage(MyConfig { user_val: "user input".to_string() })
|
2017-02-03 02:00:18 +00:00
|
|
|
/// }
|
|
|
|
/// ```
|
2017-07-03 22:13:21 +00:00
|
|
|
///
|
|
|
|
/// # Within Request Guards
|
|
|
|
///
|
|
|
|
/// Because `State` is itself a request guard, managed state can be retrieved
|
2021-03-15 02:57:59 +00:00
|
|
|
/// from another request guard's implementation using either
|
2021-03-26 01:33:15 +00:00
|
|
|
/// [`Request::guard()`] or [`Rocket::state()`]. In the following code example,
|
|
|
|
/// the `Item` request guard retrieves `MyConfig` from managed state:
|
2017-07-03 22:13:21 +00:00
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::State;
|
|
|
|
/// use rocket::request::{self, Request, FromRequest};
|
2021-03-15 02:57:59 +00:00
|
|
|
/// use rocket::outcome::IntoOutcome;
|
2017-07-03 22:13:21 +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
|
|
|
/// # struct MyConfig { user_val: String };
|
2021-03-15 02:57:59 +00:00
|
|
|
/// struct Item<'r>(&'r str);
|
2017-07-03 22:13:21 +00:00
|
|
|
///
|
2020-01-31 09:34:15 +00:00
|
|
|
/// #[rocket::async_trait]
|
2021-03-15 02:57:59 +00:00
|
|
|
/// impl<'r> FromRequest<'r> for Item<'r> {
|
2017-07-03 22:13:21 +00:00
|
|
|
/// type Error = ();
|
|
|
|
///
|
2021-03-15 02:57:59 +00:00
|
|
|
/// async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, ()> {
|
|
|
|
/// // Using `State` as a request guard. Use `inner()` to get an `'r`.
|
2021-05-11 13:56:35 +00:00
|
|
|
/// let outcome = request.guard::<&State<MyConfig>>().await
|
|
|
|
/// .map(|my_config| Item(&my_config.user_val));
|
2021-03-15 02:57:59 +00:00
|
|
|
///
|
2021-05-11 13:56:35 +00:00
|
|
|
/// // Or alternatively, using `Rocket::state()`:
|
2021-03-26 01:33:15 +00:00
|
|
|
/// let outcome = request.rocket().state::<MyConfig>()
|
2021-03-15 02:57:59 +00:00
|
|
|
/// .map(|my_config| Item(&my_config.user_val))
|
|
|
|
/// .or_forward(());
|
|
|
|
///
|
|
|
|
/// outcome
|
2017-07-03 22:13:21 +00:00
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2018-11-03 08:51:00 +00:00
|
|
|
///
|
|
|
|
/// # Testing with `State`
|
|
|
|
///
|
|
|
|
/// When unit testing your application, you may find it necessary to manually
|
|
|
|
/// construct a type of `State` to pass to your functions. To do so, use the
|
2021-05-11 13:56:35 +00:00
|
|
|
/// [`State::get()`] static method or the `From<&T>` implementation:
|
2018-11-03 08:51:00 +00:00
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # #[macro_use] extern crate rocket;
|
|
|
|
/// use rocket::State;
|
|
|
|
///
|
|
|
|
/// struct MyManagedState(usize);
|
|
|
|
///
|
|
|
|
/// #[get("/")]
|
2021-05-11 13:56:35 +00:00
|
|
|
/// fn handler(state: &State<MyManagedState>) -> String {
|
2018-11-03 08:51:00 +00:00
|
|
|
/// state.0.to_string()
|
|
|
|
/// }
|
|
|
|
///
|
2021-04-08 08:07:52 +00:00
|
|
|
/// let mut rocket = rocket::build().manage(MyManagedState(127));
|
2021-05-11 13:56:35 +00:00
|
|
|
/// let state = State::get(&rocket).expect("managed `MyManagedState`");
|
2018-11-03 08:51:00 +00:00
|
|
|
/// assert_eq!(handler(state), "127");
|
2021-05-11 13:56:35 +00:00
|
|
|
///
|
|
|
|
/// let managed = MyManagedState(77);
|
|
|
|
/// assert_eq!(handler(State::from(&managed)), "77");
|
2018-11-03 08:51:00 +00:00
|
|
|
/// ```
|
2021-05-11 13:56:35 +00:00
|
|
|
#[repr(transparent)]
|
|
|
|
#[derive(RefCast, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
pub struct State<T: Send + Sync + 'static>(T);
|
|
|
|
|
|
|
|
impl<T: Send + Sync + 'static> State<T> {
|
|
|
|
/// Returns the managed state value in `rocket` for the type `T` if it is
|
|
|
|
/// being managed by `rocket`. Otherwise, returns `None`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::State;
|
|
|
|
///
|
|
|
|
/// #[derive(Debug, PartialEq)]
|
|
|
|
/// struct Managed(usize);
|
|
|
|
///
|
|
|
|
/// #[derive(Debug, PartialEq)]
|
|
|
|
/// struct Unmanaged(usize);
|
|
|
|
///
|
|
|
|
/// let rocket = rocket::build().manage(Managed(7));
|
|
|
|
///
|
|
|
|
/// let state: Option<&State<Managed>> = State::get(&rocket);
|
|
|
|
/// assert_eq!(state.map(|s| s.inner()), Some(&Managed(7)));
|
|
|
|
///
|
|
|
|
/// let state: Option<&State<Unmanaged>> = State::get(&rocket);
|
|
|
|
/// assert_eq!(state, None);
|
|
|
|
/// ```
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn get<P: Phase>(rocket: &Rocket<P>) -> Option<&State<T>> {
|
|
|
|
rocket.state::<T>().map(State::ref_cast)
|
|
|
|
}
|
2017-01-21 03:31:46 +00:00
|
|
|
|
2021-05-11 13:56:35 +00:00
|
|
|
/// This exists because `State::from()` would otherwise be nothing. But we
|
|
|
|
/// want `State::from(&foo)` to give us `<&State>::from(&foo)`. Here it is.
|
|
|
|
#[doc(hidden)]
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn from(value: &T) -> &State<T> {
|
|
|
|
State::ref_cast(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Borrow the inner value.
|
2017-01-21 03:31:46 +00:00
|
|
|
///
|
2018-10-06 13:25:17 +00:00
|
|
|
/// Using this method is typically unnecessary as `State` implements
|
|
|
|
/// [`Deref`] with a [`Deref::Target`] of `T`. This means Rocket will
|
|
|
|
/// automatically coerce a `State<T>` to an `&T` as required. This method
|
|
|
|
/// should only be used when a longer lifetime is required.
|
2017-07-03 22:13:21 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::State;
|
|
|
|
///
|
2021-05-11 13:56:35 +00:00
|
|
|
/// #[derive(Clone)]
|
2017-07-03 22:13:21 +00:00
|
|
|
/// struct MyConfig {
|
|
|
|
/// user_val: String
|
|
|
|
/// }
|
|
|
|
///
|
2021-05-11 13:56:35 +00:00
|
|
|
/// fn handler1<'r>(config: &State<MyConfig>) -> String {
|
|
|
|
/// let config = config.inner().clone();
|
|
|
|
/// config.user_val
|
2017-07-03 22:13:21 +00:00
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// // Use the `Deref` implementation which coerces implicitly
|
2021-05-11 13:56:35 +00:00
|
|
|
/// fn handler2(config: &State<MyConfig>) -> String {
|
2017-07-03 22:13:21 +00:00
|
|
|
/// config.user_val.clone()
|
|
|
|
/// }
|
|
|
|
/// ```
|
2017-03-28 10:10:18 +00:00
|
|
|
#[inline(always)]
|
2021-05-11 13:56:35 +00:00
|
|
|
pub fn inner(&self) -> &T {
|
|
|
|
&self.0
|
2017-01-21 03:31:46 +00:00
|
|
|
}
|
2021-05-11 13:56:35 +00:00
|
|
|
}
|
2018-11-03 08:51:00 +00:00
|
|
|
|
2021-05-11 13:56:35 +00:00
|
|
|
impl<'r, T: Send + Sync + 'static> From<&'r T> for &'r State<T> {
|
2018-11-03 08:51:00 +00:00
|
|
|
#[inline(always)]
|
2021-05-11 13:56:35 +00:00
|
|
|
fn from(reference: &'r T) -> Self {
|
|
|
|
State::ref_cast(reference)
|
2018-11-03 08:51:00 +00:00
|
|
|
}
|
2017-01-21 03:31:46 +00:00
|
|
|
}
|
|
|
|
|
2020-01-31 09:34:15 +00:00
|
|
|
#[crate::async_trait]
|
2021-05-11 13:56:35 +00:00
|
|
|
impl<'r, T: Send + Sync + 'static> FromRequest<'r> for &'r State<T> {
|
2017-01-21 03:31:46 +00:00
|
|
|
type Error = ();
|
|
|
|
|
2017-03-28 10:10:18 +00:00
|
|
|
#[inline(always)]
|
2021-03-15 02:57:59 +00:00
|
|
|
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, ()> {
|
2021-05-11 13:56:35 +00:00
|
|
|
match State::get(req.rocket()) {
|
|
|
|
Some(state) => Outcome::Success(state),
|
2017-03-28 10:10:18 +00:00
|
|
|
None => {
|
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
|
|
|
error_!("Attempted to retrieve unmanaged state `{}`!", type_name::<T>());
|
2017-03-28 10:10:18 +00:00
|
|
|
Outcome::Failure((Status::InternalServerError, ()))
|
2017-01-21 03:31:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 13:56:35 +00:00
|
|
|
impl<T: Send + Sync + 'static> Sentinel for &State<T> {
|
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
|
|
|
fn abort(rocket: &Rocket<Ignite>) -> bool {
|
|
|
|
if rocket.state::<T>().is_none() {
|
|
|
|
let type_name = yansi::Paint::default(type_name::<T>()).bold();
|
|
|
|
error!("launching with unmanaged `{}` state.", type_name);
|
|
|
|
info_!("Using `State` requires managing it with `.manage()`.");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 13:56:35 +00:00
|
|
|
impl<T: Send + Sync + fmt::Display + 'static> fmt::Display for State<T> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
self.0.fmt(f)
|
2020-10-02 09:54:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 13:56:35 +00:00
|
|
|
impl<T: Send + Sync + 'static> Deref for State<T> {
|
|
|
|
type Target = T;
|
2020-10-02 09:54:11 +00:00
|
|
|
|
2021-05-11 13:56:35 +00:00
|
|
|
#[inline(always)]
|
|
|
|
fn deref(&self) -> &T {
|
|
|
|
&self.0
|
2020-10-02 09:54:11 +00:00
|
|
|
}
|
|
|
|
}
|