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 rocket::{*, error::ErrorKind::SentinelAborts};
|
|
|
|
|
|
|
|
#[get("/two")]
|
2021-05-11 13:56:35 +00:00
|
|
|
fn two_states(_one: &State<u32>, _two: &State<String>) {}
|
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
|
|
|
|
2021-06-26 23:42:07 +00:00
|
|
|
#[post("/one", data = "<s>")]
|
|
|
|
fn one_state<'r>(_three: &'r State<u8>, s: &'r str) -> &'r str { 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
|
|
|
|
|
|
|
#[async_test]
|
|
|
|
async fn state_sentinel_works() {
|
|
|
|
let err = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![two_states])
|
|
|
|
.ignite().await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 2));
|
|
|
|
|
|
|
|
let err = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![two_states])
|
|
|
|
.manage(String::new())
|
|
|
|
.ignite().await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
let err = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![two_states])
|
|
|
|
.manage(1 as u32)
|
|
|
|
.ignite().await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
let result = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![two_states])
|
|
|
|
.manage(String::new())
|
|
|
|
.manage(1 as u32)
|
|
|
|
.ignite().await;
|
|
|
|
|
|
|
|
assert!(result.is_ok());
|
|
|
|
|
|
|
|
let err = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![one_state])
|
|
|
|
.ignite().await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
let result = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![one_state])
|
|
|
|
.manage(1 as u8)
|
|
|
|
.ignite().await;
|
|
|
|
|
|
|
|
assert!(result.is_ok());
|
|
|
|
|
|
|
|
let err = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![one_state, two_states])
|
|
|
|
.ignite().await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 3));
|
|
|
|
|
|
|
|
let err = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![one_state, two_states])
|
|
|
|
.manage(1 as u32)
|
|
|
|
.ignite().await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 2));
|
|
|
|
|
|
|
|
let err = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![one_state, two_states])
|
|
|
|
.manage(1 as u8)
|
|
|
|
.ignite().await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 2));
|
|
|
|
|
|
|
|
let err = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![one_state, two_states])
|
|
|
|
.manage(1 as u32)
|
|
|
|
.manage(1 as u8)
|
|
|
|
.ignite().await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
let result = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
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
|
|
|
.mount("/", routes![one_state, two_states])
|
|
|
|
.manage(1 as u32)
|
|
|
|
.manage(1 as u8)
|
|
|
|
.manage(String::new())
|
|
|
|
.ignite().await;
|
|
|
|
|
|
|
|
assert!(result.is_ok());
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:41:46 +00:00
|
|
|
struct Data;
|
|
|
|
|
|
|
|
#[crate::async_trait]
|
|
|
|
impl<'r> data::FromData<'r> for Data {
|
|
|
|
type Error = Error;
|
|
|
|
async fn from_data(_: &'r Request<'_>, _: data::Data<'r>) -> data::Outcome<'r, Self> {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sentinel for Data {
|
|
|
|
fn abort(rocket: &Rocket<Ignite>) -> bool {
|
|
|
|
rocket.state::<Data>().is_none()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/data", data = "<_data>")]
|
|
|
|
fn with_data(_data: Data) {}
|
|
|
|
|
|
|
|
#[async_test]
|
|
|
|
async fn data_sentinel_works() {
|
|
|
|
let err = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
2022-06-20 10:41:46 +00:00
|
|
|
.mount("/", routes![with_data])
|
|
|
|
.ignite().await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
let result = rocket::build()
|
2024-04-25 20:21:59 +00:00
|
|
|
.reconfigure(Config::debug_default())
|
2022-06-20 10:41:46 +00:00
|
|
|
.mount("/", routes![with_data])
|
|
|
|
.manage(Data)
|
|
|
|
.ignite().await;
|
|
|
|
|
|
|
|
assert!(result.is_ok());
|
|
|
|
}
|
|
|
|
|
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
|
|
|
#[test]
|
|
|
|
fn inner_sentinels_detected() {
|
|
|
|
use rocket::local::blocking::Client;
|
|
|
|
|
|
|
|
#[derive(Responder)]
|
|
|
|
struct MyThing<T>(T);
|
|
|
|
|
|
|
|
struct ResponderSentinel;
|
|
|
|
|
|
|
|
impl<'r, 'o: 'r> response::Responder<'r, 'o> for ResponderSentinel {
|
|
|
|
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
|
Update to hyper 1. Enable custom + unix listeners.
This commit completely rewrites Rocket's HTTP serving. In addition to
significant internal cleanup, this commit introduces the following major
features:
* Support for custom, external listeners in the `listener` module.
The new `listener` module contains new `Bindable`, `Listener`, and
`Connection` traits which enable composable, external
implementations of connection listeners. Rocket can launch on any
`Listener`, or anything that can be used to create a listener
(`Bindable`), via a new `launch_on()` method.
* Support for Unix domain socket listeners out of the box.
The default listener backwards compatibly supports listening on Unix
domain sockets. To do so, configure an `address` of
`unix:path/to/socket` and optional set `reuse` to `true` (the
default) or `false` which controls whether Rocket will handle
creating and deleting the unix domain socket.
In addition to these new features, this commit makes the following major
improvements:
* Rocket now depends on hyper 1.
* Rocket no longer depends on hyper to handle connections. This allows
us to handle more connection failure conditions which results in an
overall more robust server with fewer dependencies.
* Logic to work around hyper's inability to reference incoming request
data in the response results in a 15% performance improvement.
* `Client`s can be marked secure with `Client::{un}tracked_secure()`,
allowing Rocket to treat local connections as running under TLS.
* The `macros` feature of `tokio` is no longer used by Rocket itself.
Dependencies can take advantage of this reduction in compile-time
cost by disabling the new default feature `tokio-macros`.
* A new `TlsConfig::validate()` method allows checking a TLS config.
* New `TlsConfig::{certs,key}_reader()`,
`MtlsConfig::ca_certs_reader()` methods return `BufReader`s, which
allow reading the configured certs and key directly.
* A new `NamedFile::open_with()` constructor allows specifying
`OpenOptions`.
These improvements resulted in the following breaking changes:
* The MSRV is now 1.74.
* `hyper` is no longer exported from `rocket::http`.
* `IoHandler::io` takes `Box<Self>` instead of `Pin<Box<Self>>`.
- Use `Box::into_pin(self)` to recover the previous type.
* `Response::upgrade()` now returns an `&mut dyn IoHandler`, not
`Pin<& mut _>`.
* `Config::{address,port,tls,mtls}` methods have been removed.
- Use methods on `Rocket::endpoint()` instead.
* `TlsConfig` was moved to `tls::TlsConfig`.
* `MutualTls` was renamed and moved to `mtls::MtlsConfig`.
* `ErrorKind::TlsBind` was removed.
* The second field of `ErrorKind::Shutdown` was removed.
* `{Local}Request::{set_}remote()` methods take/return an `Endpoint`.
* `Client::new()` was removed; it was previously deprecated.
Internally, the following major changes were made:
* A new `async_bound` attribute macro was introduced to allow setting
bounds on futures returned by `async fn`s in traits while
maintaining good docs.
* All utility functionality was moved to a new `util` module.
Resolves #2671.
Resolves #1070.
2023-12-19 22:32:11 +00:00
|
|
|
unimplemented!()
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sentinel for ResponderSentinel {
|
|
|
|
fn abort(_: &Rocket<Ignite>) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn route() -> MyThing<ResponderSentinel> { todo!() }
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![route]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
#[derive(Responder)]
|
|
|
|
struct Inner<T>(T);
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn inner() -> MyThing<Inner<ResponderSentinel>> { todo!() }
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![inner]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn inner_either() -> Either<Inner<ResponderSentinel>, ResponderSentinel> { todo!() }
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![inner_either]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 2));
|
|
|
|
|
|
|
|
#[derive(Responder)]
|
|
|
|
struct Block<T>(T);
|
|
|
|
|
|
|
|
impl<T> Sentinel for Block<T> {
|
|
|
|
fn abort(_: &Rocket<Ignite>) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn blocked() -> Block<ResponderSentinel> { todo!() }
|
|
|
|
|
|
|
|
Client::debug_with(routes![blocked]).expect("no sentinel errors");
|
|
|
|
|
|
|
|
#[get("/a")]
|
|
|
|
fn inner_b() -> Either<Inner<Block<ResponderSentinel>>, Block<ResponderSentinel>> {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/b")]
|
|
|
|
fn inner_b2() -> Either<Block<Inner<ResponderSentinel>>, Block<ResponderSentinel>> {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
|
|
|
|
Client::debug_with(routes![inner_b, inner_b2]).expect("no sentinel errors");
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn half_b() -> Either<Inner<ResponderSentinel>, Block<ResponderSentinel>> {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![half_b]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
use rocket::response::Responder;
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn half_c<'r>() -> Either<
|
|
|
|
Inner<impl Responder<'r, 'static>>,
|
|
|
|
Result<ResponderSentinel, Inner<ResponderSentinel>>
|
|
|
|
> {
|
|
|
|
Either::Left(Inner(()))
|
|
|
|
}
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![half_c]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 2));
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn half_d<'r>() -> Either<
|
|
|
|
Inner<impl Responder<'r, 'static>>,
|
|
|
|
Result<Block<ResponderSentinel>, Inner<ResponderSentinel>>
|
|
|
|
> {
|
|
|
|
Either::Left(Inner(()))
|
|
|
|
}
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![half_d]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
// The special `Result` implementation.
|
|
|
|
type MyResult = Result<ResponderSentinel, ResponderSentinel>;
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn half_e<'r>() -> Either<Inner<impl Responder<'r, 'static>>, MyResult> {
|
|
|
|
Either::Left(Inner(()))
|
|
|
|
}
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![half_e]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
// Another specialized sentinel.
|
|
|
|
|
|
|
|
#[get("/")] fn either_route() -> Either<ResponderSentinel, ResponderSentinel> { todo!() }
|
|
|
|
let err = Client::debug_with(routes![either_route]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
#[get("/")] fn either_route2() -> Either<ResponderSentinel, ()> { todo!() }
|
|
|
|
let err = Client::debug_with(routes![either_route2]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
#[get("/")] fn either_route3() -> Either<(), ResponderSentinel> { todo!() }
|
|
|
|
let err = Client::debug_with(routes![either_route3]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
#[get("/")] fn either_route4() -> Either<(), ()> { todo!() }
|
|
|
|
Client::debug_with(routes![either_route4]).expect("no sentinel error");
|
|
|
|
}
|
2021-06-04 02:31:30 +00:00
|
|
|
|
|
|
|
#[async_test]
|
|
|
|
async fn known_macro_sentinel_works() {
|
|
|
|
use rocket::response::stream::{TextStream, ByteStream, ReaderStream};
|
|
|
|
use rocket::local::asynchronous::Client;
|
|
|
|
use rocket::tokio::io::AsyncRead;
|
|
|
|
|
|
|
|
#[derive(Responder)]
|
2021-06-26 23:42:07 +00:00
|
|
|
struct TextSentinel<'r>(&'r str);
|
2021-06-04 02:31:30 +00:00
|
|
|
|
2021-06-26 23:42:07 +00:00
|
|
|
impl Sentinel for TextSentinel<'_> {
|
2021-06-04 02:31:30 +00:00
|
|
|
fn abort(_: &Rocket<Ignite>) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-26 23:42:07 +00:00
|
|
|
impl AsRef<str> for TextSentinel<'_> {
|
2021-06-04 02:31:30 +00:00
|
|
|
fn as_ref(&self) -> &str {
|
|
|
|
self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-26 23:42:07 +00:00
|
|
|
impl AsRef<[u8]> for TextSentinel<'_> {
|
2021-06-04 02:31:30 +00:00
|
|
|
fn as_ref(&self) -> &[u8] {
|
|
|
|
self.0.as_bytes()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-26 23:42:07 +00:00
|
|
|
impl AsyncRead for TextSentinel<'_> {
|
2021-06-04 02:31:30 +00:00
|
|
|
fn poll_read(
|
|
|
|
self: std::pin::Pin<&mut Self>,
|
|
|
|
_: &mut futures::task::Context<'_>,
|
|
|
|
_: &mut tokio::io::ReadBuf<'_>,
|
|
|
|
) -> futures::task::Poll<std::io::Result<()>> {
|
|
|
|
futures::task::Poll::Ready(Ok(()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/text")]
|
2021-06-26 23:42:07 +00:00
|
|
|
fn text<'r>() -> TextStream![TextSentinel<'r>] {
|
2021-06-04 02:31:30 +00:00
|
|
|
TextStream!(yield TextSentinel("hi");)
|
|
|
|
}
|
|
|
|
|
2021-06-26 23:42:07 +00:00
|
|
|
#[get("/<a>")]
|
|
|
|
fn byte(a: &str) -> ByteStream![TextSentinel<'_>] {
|
|
|
|
ByteStream!(yield TextSentinel(a);)
|
2021-06-04 02:31:30 +00:00
|
|
|
}
|
|
|
|
|
2021-06-26 23:42:07 +00:00
|
|
|
#[get("/<_a>/<b>")]
|
2024-05-20 18:39:14 +00:00
|
|
|
fn reader<'b>(_a: &str, b: &'b str) -> ReaderStream![TextSentinel<'b>] {
|
2021-06-26 23:42:07 +00:00
|
|
|
ReaderStream!(yield TextSentinel(b);)
|
2021-06-04 02:31:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! UnknownStream {
|
|
|
|
($t:ty) => (ReaderStream![$t])
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/ignore")]
|
2021-06-26 23:42:07 +00:00
|
|
|
fn ignore() -> UnknownStream![TextSentinel<'static>] {
|
2021-06-04 02:31:30 +00:00
|
|
|
ReaderStream!(yield TextSentinel("hi");)
|
|
|
|
}
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![text, byte, reader, ignore]).await.unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 3));
|
|
|
|
}
|