From 0bdb6b7bc762768afc1c0c1bdfd7ce93e0642f6c Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Thu, 1 Apr 2021 12:32:52 -0700 Subject: [PATCH] Remove 'attach' fairings. Add 'liftoff' fairings. Launch fairings are now fallible and take the place of attach fairings, but they are only run, as the name implies, at launch time. This is is a fundamental shift from eager execution of set-up routines, including the now defunct attach fairings, to lazy execution, precipitated by the transition to `async`. The previous functionality, while simple, caused grave issues: 1. A instance of 'Rocket' with async attach fairings requires an async runtime to be constructed. 2. The instance is accessible in non-async contexts. 3. The async attach fairings have no runtime in which to be run. Here's an example: ```rust let rocket = rocket::ignite() .attach(AttachFairing::from(|rocket| async { Ok(rocket.manage(load_from_network::().await)) })); let state = rocket.state::(); ``` This had no real meaning previously yet was accepted by running the attach fairing future in an isolated runtime. In isolation, this causes no issue, but when attach fairing futures share reactor state with other futures in Rocket, panics ensue. The new Rocket application lifecycle is this: * Build - A Rocket instance is constructed. No fairings are run. * Ignition - All launch fairings are run. * Liftoff - If all launch fairings succeeded, the server is started. New 'liftoff' fairings are run in this third phase. --- contrib/lib/src/databases/connection.rs | 2 +- contrib/lib/src/helmet/helmet.rs | 12 +- contrib/lib/src/templates/fairing.rs | 8 +- contrib/lib/src/templates/mod.rs | 4 +- contrib/lib/tests/databases.rs | 5 +- contrib/lib/tests/templates.rs | 10 +- core/http/src/listener.rs | 3 +- core/lib/src/config/config.rs | 6 +- core/lib/src/data/temp_file.rs | 5 +- core/lib/src/fairing/ad_hoc.rs | 219 ++++++++---------- core/lib/src/fairing/fairings.rs | 113 ++++----- core/lib/src/fairing/info_kind.rs | 22 +- core/lib/src/fairing/mod.rs | 110 ++++----- core/lib/src/local/asynchronous/client.rs | 4 +- core/lib/src/local/client.rs | 7 +- core/lib/src/rocket.rs | 139 ++++------- core/lib/src/server.rs | 95 +++++--- core/lib/tests/attach-inspect.rs | 44 ---- core/lib/tests/launch-inspect.rs | 79 +++++++ core/lib/tests/nested-fairing-attaches.rs | 2 +- .../on_launch_fairing_can_inspect_port.rs | 6 +- core/lib/tests/timer-on-attach.rs | 2 +- examples/config/src/main.rs | 5 +- examples/fairings/src/main.rs | 28 +-- examples/testing/src/async_required.rs | 2 +- examples/todo/Cargo.toml | 1 - examples/todo/src/main.rs | 46 ++-- examples/todo/src/tests.rs | 13 +- site/guide/7-fairings.md | 55 +++-- 29 files changed, 528 insertions(+), 519 deletions(-) delete mode 100644 core/lib/tests/attach-inspect.rs create mode 100644 core/lib/tests/launch-inspect.rs diff --git a/contrib/lib/src/databases/connection.rs b/contrib/lib/src/databases/connection.rs index 5471181b..8041cddf 100644 --- a/contrib/lib/src/databases/connection.rs +++ b/contrib/lib/src/databases/connection.rs @@ -69,7 +69,7 @@ macro_rules! dberr { impl ConnectionPool { pub fn fairing(fairing_name: &'static str, db: &'static str) -> impl Fairing { - AdHoc::on_attach(fairing_name, move |rocket| async move { + AdHoc::on_launch(fairing_name, move |rocket| async move { let config = match Config::from(db, &rocket) { Ok(config) => config, Err(e) => dberr!("config", db, "{}", e, rocket), diff --git a/contrib/lib/src/helmet/helmet.rs b/contrib/lib/src/helmet/helmet.rs index 91d036ae..221e69e4 100644 --- a/contrib/lib/src/helmet/helmet.rs +++ b/contrib/lib/src/helmet/helmet.rs @@ -193,15 +193,11 @@ impl Fairing for SpaceHelmet { fn info(&self) -> Info { Info { name: "Space Helmet", - kind: Kind::Response | Kind::Launch, + kind: Kind::Liftoff | Kind::Response, } } - async fn on_response<'r>(&self, _: &'r Request<'_>, res: &mut Response<'r>) { - self.apply(res); - } - - fn on_launch(&self, rocket: &Rocket) { + async fn on_liftoff(&self, rocket: &Rocket) { if rocket.config().tls_enabled() && rocket.figment().profile() != rocket::Config::DEBUG_PROFILE && !self.is_enabled::() @@ -212,4 +208,8 @@ impl Fairing for SpaceHelmet { self.force_hsts.store(true, Ordering::Relaxed); } } + + async fn on_response<'r>(&self, _: &'r Request<'_>, res: &mut Response<'r>) { + self.apply(res); + } } diff --git a/contrib/lib/src/templates/fairing.rs b/contrib/lib/src/templates/fairing.rs index 8bb20c63..d9cf3d32 100644 --- a/contrib/lib/src/templates/fairing.rs +++ b/contrib/lib/src/templates/fairing.rs @@ -129,8 +129,8 @@ pub struct TemplateFairing { impl Fairing for TemplateFairing { fn info(&self) -> Info { // on_request only applies in debug mode, so only enable it in debug. - #[cfg(debug_assertions)] let kind = Kind::Attach | Kind::Request; - #[cfg(not(debug_assertions))] let kind = Kind::Attach; + #[cfg(debug_assertions)] let kind = Kind::Launch | Kind::Request; + #[cfg(not(debug_assertions))] let kind = Kind::Launch; Info { kind, name: "Templates" } } @@ -140,7 +140,7 @@ impl Fairing for TemplateFairing { /// The user's callback, if any was supplied, is called to customize the /// template engines. In debug mode, the `ContextManager::new` method /// initializes a directory watcher for auto-reloading of templates. - async fn on_attach(&self, rocket: Rocket) -> Result { + async fn on_launch(&self, rocket: Rocket) -> Result { use rocket::figment::{Source, value::magic::RelativePathBuf}; let configured_dir = rocket.figment() @@ -186,7 +186,7 @@ impl Fairing for TemplateFairing { #[cfg(debug_assertions)] async fn on_request(&self, req: &mut rocket::Request<'_>, _data: &mut rocket::Data) { let cm = req.rocket().state::() - .expect("Template ContextManager registered in on_attach"); + .expect("Template ContextManager registered in on_launch"); cm.reload_if_needed(&self.callback); } diff --git a/contrib/lib/src/templates/mod.rs b/contrib/lib/src/templates/mod.rs index 3a4d3bab..7746a084 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/lib/src/templates/mod.rs @@ -395,8 +395,8 @@ impl Template { let info = ctxt.templates.get(name).ok_or_else(|| { let ts: Vec<_> = ctxt.templates.keys().map(|s| s.as_str()).collect(); error_!("Template '{}' does not exist.", name); - info_!("Known templates: {}", ts.join(",")); - info_!("Searched in '{:?}'.", ctxt.root); + info_!("Known templates: {}", ts.join(", ")); + info_!("Searched in {:?}.", ctxt.root); Status::InternalServerError })?; diff --git a/contrib/lib/tests/databases.rs b/contrib/lib/tests/databases.rs index aa7f872b..04e3613a 100644 --- a/contrib/lib/tests/databases.rs +++ b/contrib/lib/tests/databases.rs @@ -35,7 +35,10 @@ mod rusqlite_integration_test { let rocket = rocket::custom(config) .attach(SqliteDb::fairing()) - .attach(SqliteDb2::fairing()); + .attach(SqliteDb2::fairing()) + ._ignite() + .await + .unwrap(); let conn = SqliteDb::get_one(&rocket).await .expect("unable to get connection"); diff --git a/contrib/lib/tests/templates.rs b/contrib/lib/tests/templates.rs index 710ca9e1..b753ba80 100644 --- a/contrib/lib/tests/templates.rs +++ b/contrib/lib/tests/templates.rs @@ -61,17 +61,17 @@ mod templates_tests { #[test] fn test_tera_templates() { - let rocket = rocket(); + let client = Client::debug(rocket()).unwrap(); let mut map = HashMap::new(); map.insert("title", "_test_"); map.insert("content", "