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.
This commit is contained in:
Sergio Benitez 2021-04-13 19:26:45 -07:00
parent 887b2aed87
commit 4f3511786c
70 changed files with 1249 additions and 792 deletions

View File

@ -91,7 +91,9 @@ pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result<TokenStrea
/// Retrieves a connection of type `Self` from the `rocket` /// Retrieves a connection of type `Self` from the `rocket`
/// instance. Returns `Some` as long as `Self::fairing()` has been /// instance. Returns `Some` as long as `Self::fairing()` has been
/// attached. /// attached.
pub async fn get_one(__rocket: &::rocket::Rocket) -> Option<Self> { pub async fn get_one<P>(__rocket: &::rocket::Rocket<P>) -> Option<Self>
where P: ::rocket::Phase,
{
<#pool>::get_one(&__rocket).await.map(Self) <#pool>::get_one(&__rocket).await.map(Self)
} }

View File

@ -59,9 +59,10 @@ impl Config {
/// my_other_db = { url = "mysql://root:root@localhost/database" } /// my_other_db = { url = "mysql://root:root@localhost/database" }
/// # "#).nested(); /// # "#).nested();
/// ///
/// use rocket::{Rocket, Build};
/// use rocket_contrib::databases::Config; /// use rocket_contrib::databases::Config;
/// ///
/// fn pool(rocket: &rocket::Rocket) { /// fn pool(rocket: &Rocket<Build>) {
/// let config = Config::from("my_db", rocket).unwrap(); /// let config = Config::from("my_db", rocket).unwrap();
/// assert_eq!(config.url, "db/db.sqlite"); /// assert_eq!(config.url, "db/db.sqlite");
/// assert_eq!(config.pool_size, 25); /// assert_eq!(config.pool_size, 25);
@ -81,7 +82,7 @@ impl Config {
/// # pool(&rocket); /// # pool(&rocket);
/// # } /// # }
/// ``` /// ```
pub fn from(db_name: &str, rocket: &rocket::Rocket) -> Result<Config, figment::Error> { pub fn from(db_name: &str, rocket: &Rocket<Build>) -> Result<Config, figment::Error> {
Config::figment(db_name, rocket).extract::<Self>() Config::figment(db_name, rocket).extract::<Self>()
} }
@ -91,15 +92,15 @@ impl Config {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::Rocket; /// use rocket::{Rocket, Build};
/// use rocket_contrib::databases::Config; /// use rocket_contrib::databases::Config;
/// ///
/// fn pool(rocket: &Rocket) { /// fn pool(rocket: &Rocket<Build>) {
/// let my_db_figment = Config::figment("my_db", rocket); /// let my_db_figment = Config::figment("my_db", rocket);
/// let mysql_prod_figment = Config::figment("mysql_prod", rocket); /// let mysql_prod_figment = Config::figment("mysql_prod", rocket);
/// } /// }
/// ``` /// ```
pub fn figment(db_name: &str, rocket: &rocket::Rocket) -> Figment { pub fn figment(db_name: &str, rocket: &Rocket<Build>) -> Figment {
let db_key = format!("databases.{}", db_name); let db_key = format!("databases.{}", db_name);
let default_pool_size = rocket.figment() let default_pool_size = rocket.figment()
.extract_inner::<u32>(rocket::Config::WORKERS) .extract_inner::<u32>(rocket::Config::WORKERS)

View File

@ -1,6 +1,7 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use rocket::{Rocket, Phase};
use rocket::fairing::{AdHoc, Fairing}; use rocket::fairing::{AdHoc, Fairing};
use rocket::request::{Request, Outcome, FromRequest}; use rocket::request::{Request, Outcome, FromRequest};
use rocket::outcome::IntoOutcome; use rocket::outcome::IntoOutcome;
@ -69,7 +70,7 @@ macro_rules! dberr {
impl<K: 'static, C: Poolable> ConnectionPool<K, C> { impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
pub fn fairing(fairing_name: &'static str, db: &'static str) -> impl Fairing { pub fn fairing(fairing_name: &'static str, db: &'static str) -> impl Fairing {
AdHoc::try_on_launch(fairing_name, move |rocket| async move { AdHoc::try_on_ignite(fairing_name, move |rocket| async move {
let config = match Config::from(db, &rocket) { let config = match Config::from(db, &rocket) {
Ok(config) => config, Ok(config) => config,
Err(e) => dberr!("config", db, "{}", e, rocket), Err(e) => dberr!("config", db, "{}", e, rocket),
@ -117,7 +118,7 @@ impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
} }
#[inline] #[inline]
pub async fn get_one(rocket: &rocket::Rocket) -> Option<Connection<K, C>> { pub async fn get_one<P: Phase>(rocket: &Rocket<P>) -> Option<Connection<K, C>> {
match rocket.state::<Self>() { match rocket.state::<Self>() {
Some(pool) => match pool.get().await.ok() { Some(pool) => match pool.get().await.ok() {
Some(conn) => Some(conn), Some(conn) => Some(conn),
@ -134,7 +135,7 @@ impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
} }
#[inline] #[inline]
pub async fn get_pool(rocket: &rocket::Rocket) -> Option<Self> { pub async fn get_pool<P: Phase>(rocket: &Rocket<P>) -> Option<Self> {
rocket.state::<Self>().map(|pool| pool.clone()) rocket.state::<Self>().map(|pool| pool.clone())
} }
} }

View File

@ -198,7 +198,7 @@
//! Returns a fairing that initializes the associated database connection //! Returns a fairing that initializes the associated database connection
//! pool. //! pool.
//! //!
//! * `async fn get_one(&Rocket) -> Option<Self>` //! * `async fn get_one<P: Phase>(&Rocket<P>) -> Option<Self>`
//! //!
//! Retrieves a connection wrapper from the configured pool. Returns `Some` //! Retrieves a connection wrapper from the configured pool. Returns `Some`
//! as long as `Self::fairing()` has been attached. //! as long as `Self::fairing()` has been attached.

View File

@ -1,5 +1,6 @@
use r2d2::ManageConnection; use r2d2::ManageConnection;
use rocket::{Rocket, Build};
use crate::databases::{Config, Error}; use crate::databases::{Config, Error};
/// Trait implemented by `r2d2`-based database adapters. /// Trait implemented by `r2d2`-based database adapters.
@ -61,13 +62,14 @@ use crate::databases::{Config, Error};
/// # fn has_broken(&self, _: &mut Connection) -> bool { panic!() } /// # fn has_broken(&self, _: &mut Connection) -> bool { panic!() }
/// # } /// # }
/// # } /// # }
/// use rocket::{Rocket, Build};
/// use rocket_contrib::databases::{r2d2, Error, Config, Poolable, PoolResult}; /// use rocket_contrib::databases::{r2d2, Error, Config, Poolable, PoolResult};
/// ///
/// impl Poolable for foo::Connection { /// impl Poolable for foo::Connection {
/// type Manager = foo::ConnectionManager; /// type Manager = foo::ConnectionManager;
/// type Error = foo::Error; /// type Error = foo::Error;
/// ///
/// fn pool(db_name: &str, rocket: &rocket::Rocket) -> PoolResult<Self> { /// fn pool(db_name: &str, rocket: &Rocket<Build>) -> PoolResult<Self> {
/// let config = Config::from(db_name, rocket)?; /// let config = Config::from(db_name, rocket)?;
/// let manager = foo::ConnectionManager::new(&config.url).map_err(Error::Custom)?; /// let manager = foo::ConnectionManager::new(&config.url).map_err(Error::Custom)?;
/// Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?) /// Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
@ -97,7 +99,7 @@ pub trait Poolable: Send + Sized + 'static {
/// Creates an `r2d2` connection pool for `Manager::Connection`, returning /// Creates an `r2d2` connection pool for `Manager::Connection`, returning
/// the pool on success. /// the pool on success.
fn pool(db_name: &str, rocket: &rocket::Rocket) -> PoolResult<Self>; fn pool(db_name: &str, rocket: &Rocket<Build>) -> PoolResult<Self>;
} }
/// A type alias for the return type of [`Poolable::pool()`]. /// A type alias for the return type of [`Poolable::pool()`].
@ -109,7 +111,7 @@ impl Poolable for diesel::SqliteConnection {
type Manager = diesel::r2d2::ConnectionManager<diesel::SqliteConnection>; type Manager = diesel::r2d2::ConnectionManager<diesel::SqliteConnection>;
type Error = std::convert::Infallible; type Error = std::convert::Infallible;
fn pool(db_name: &str, rocket: &rocket::Rocket) -> PoolResult<Self> { fn pool(db_name: &str, rocket: &Rocket<Build>) -> PoolResult<Self> {
use diesel::{SqliteConnection, connection::SimpleConnection}; use diesel::{SqliteConnection, connection::SimpleConnection};
use diesel::r2d2::{CustomizeConnection, ConnectionManager, Error, Pool}; use diesel::r2d2::{CustomizeConnection, ConnectionManager, Error, Pool};
@ -144,7 +146,7 @@ impl Poolable for diesel::PgConnection {
type Manager = diesel::r2d2::ConnectionManager<diesel::PgConnection>; type Manager = diesel::r2d2::ConnectionManager<diesel::PgConnection>;
type Error = std::convert::Infallible; type Error = std::convert::Infallible;
fn pool(db_name: &str, rocket: &rocket::Rocket) -> PoolResult<Self> { fn pool(db_name: &str, rocket: &Rocket<Build>) -> PoolResult<Self> {
let config = Config::from(db_name, rocket)?; let config = Config::from(db_name, rocket)?;
let manager = diesel::r2d2::ConnectionManager::new(&config.url); let manager = diesel::r2d2::ConnectionManager::new(&config.url);
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?) Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
@ -156,7 +158,7 @@ impl Poolable for diesel::MysqlConnection {
type Manager = diesel::r2d2::ConnectionManager<diesel::MysqlConnection>; type Manager = diesel::r2d2::ConnectionManager<diesel::MysqlConnection>;
type Error = std::convert::Infallible; type Error = std::convert::Infallible;
fn pool(db_name: &str, rocket: &rocket::Rocket) -> PoolResult<Self> { fn pool(db_name: &str, rocket: &Rocket<Build>) -> PoolResult<Self> {
let config = Config::from(db_name, rocket)?; let config = Config::from(db_name, rocket)?;
let manager = diesel::r2d2::ConnectionManager::new(&config.url); let manager = diesel::r2d2::ConnectionManager::new(&config.url);
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?) Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
@ -169,7 +171,7 @@ impl Poolable for postgres::Client {
type Manager = r2d2_postgres::PostgresConnectionManager<postgres::tls::NoTls>; type Manager = r2d2_postgres::PostgresConnectionManager<postgres::tls::NoTls>;
type Error = postgres::Error; type Error = postgres::Error;
fn pool(db_name: &str, rocket: &rocket::Rocket) -> PoolResult<Self> { fn pool(db_name: &str, rocket: &Rocket<Build>) -> PoolResult<Self> {
let config = Config::from(db_name, rocket)?; let config = Config::from(db_name, rocket)?;
let url = config.url.parse().map_err(Error::Custom)?; let url = config.url.parse().map_err(Error::Custom)?;
let manager = r2d2_postgres::PostgresConnectionManager::new(url, postgres::tls::NoTls); let manager = r2d2_postgres::PostgresConnectionManager::new(url, postgres::tls::NoTls);
@ -182,7 +184,7 @@ impl Poolable for rusqlite::Connection {
type Manager = r2d2_sqlite::SqliteConnectionManager; type Manager = r2d2_sqlite::SqliteConnectionManager;
type Error = std::convert::Infallible; type Error = std::convert::Infallible;
fn pool(db_name: &str, rocket: &rocket::Rocket) -> PoolResult<Self> { fn pool(db_name: &str, rocket: &Rocket<Build>) -> PoolResult<Self> {
use rocket::figment::providers::Serialized; use rocket::figment::providers::Serialized;
#[derive(Debug, serde::Deserialize, serde::Serialize)] #[derive(Debug, serde::Deserialize, serde::Serialize)]
@ -237,7 +239,7 @@ impl Poolable for memcache::Client {
// Unused, but we might want it in the future without a breaking change. // Unused, but we might want it in the future without a breaking change.
type Error = memcache::MemcacheError; type Error = memcache::MemcacheError;
fn pool(db_name: &str, rocket: &rocket::Rocket) -> PoolResult<Self> { fn pool(db_name: &str, rocket: &Rocket<Build>) -> PoolResult<Self> {
let config = Config::from(db_name, rocket)?; let config = Config::from(db_name, rocket)?;
let manager = r2d2_memcache::MemcacheConnectionManager::new(&*config.url); let manager = r2d2_memcache::MemcacheConnectionManager::new(&*config.url);
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?) Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)

View File

@ -1,9 +1,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use rocket::{Rocket, Request, Response, Orbit};
use rocket::http::uncased::UncasedStr; use rocket::http::uncased::UncasedStr;
use rocket::fairing::{Fairing, Info, Kind}; use rocket::fairing::{Fairing, Info, Kind};
use rocket::{Rocket, Request, Response};
use crate::helmet::*; use crate::helmet::*;
@ -197,7 +197,7 @@ impl Fairing for SpaceHelmet {
} }
} }
async fn on_liftoff(&self, rocket: &Rocket) { async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
if rocket.config().tls_enabled() if rocket.config().tls_enabled()
&& rocket.figment().profile() != rocket::Config::DEBUG_PROFILE && rocket.figment().profile() != rocket::Config::DEBUG_PROFILE
&& !self.is_enabled::<Hsts>() && !self.is_enabled::<Hsts>()

View File

@ -2,8 +2,8 @@ use std::error::Error;
use crate::templates::{DEFAULT_TEMPLATE_DIR, Context, Engines}; use crate::templates::{DEFAULT_TEMPLATE_DIR, Context, Engines};
use rocket::Rocket; use rocket::{Rocket, Build, Orbit};
use rocket::fairing::{Fairing, Info, Kind}; use rocket::fairing::{self, Fairing, Info, Kind};
pub(crate) use self::context::ContextManager; pub(crate) use self::context::ContextManager;
@ -128,11 +128,10 @@ pub struct TemplateFairing {
#[rocket::async_trait] #[rocket::async_trait]
impl Fairing for TemplateFairing { impl Fairing for TemplateFairing {
fn info(&self) -> Info { fn info(&self) -> Info {
// on_request only applies in debug mode, so only enable it in debug. let kind = Kind::Ignite | Kind::Liftoff;
#[cfg(debug_assertions)] let kind = Kind::Launch | Kind::Request; #[cfg(debug_assertions)] let kind = kind | Kind::Request;
#[cfg(not(debug_assertions))] let kind = Kind::Launch;
Info { kind, name: "Templates" } Info { kind, name: "Templating" }
} }
/// Initializes the template context. Templates will be searched for in the /// Initializes the template context. Templates will be searched for in the
@ -140,8 +139,8 @@ impl Fairing for TemplateFairing {
/// The user's callback, if any was supplied, is called to customize the /// The user's callback, if any was supplied, is called to customize the
/// template engines. In debug mode, the `ContextManager::new` method /// template engines. In debug mode, the `ContextManager::new` method
/// initializes a directory watcher for auto-reloading of templates. /// initializes a directory watcher for auto-reloading of templates.
async fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> { async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
use rocket::figment::{Source, value::magic::RelativePathBuf}; use rocket::figment::value::magic::RelativePathBuf;
let configured_dir = rocket.figment() let configured_dir = rocket.figment()
.extract_inner::<RelativePathBuf>("template_dir") .extract_inner::<RelativePathBuf>("template_dir")
@ -158,17 +157,8 @@ impl Fairing for TemplateFairing {
match Context::initialize(&path) { match Context::initialize(&path) {
Some(mut ctxt) => { Some(mut ctxt) => {
use rocket::{logger::PaintExt, yansi::Paint};
use crate::templates::Engines;
info!("{}{}", Paint::emoji("📐 "), Paint::magenta("Templating:"));
match (self.callback)(&mut ctxt.engines) { match (self.callback)(&mut ctxt.engines) {
Ok(()) => { Ok(()) => Ok(rocket.manage(ContextManager::new(ctxt))),
info_!("directory: {}", Paint::white(Source::from(&*path)));
info_!("engines: {:?}", Paint::white(Engines::ENABLED_EXTENSIONS));
Ok(rocket.manage(ContextManager::new(ctxt)))
}
Err(e) => { Err(e) => {
error_!("The template customization callback returned an error:"); error_!("The template customization callback returned an error:");
error_!("{}", e); error_!("{}", e);
@ -183,11 +173,23 @@ impl Fairing for TemplateFairing {
} }
} }
async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
use rocket::{figment::Source, logger::PaintExt, yansi::Paint};
let cm = rocket.state::<ContextManager>()
.expect("Template ContextManager registered in on_ignite");
info!("{}{}:", Paint::emoji("📐 "), Paint::magenta("Templating"));
info_!("directory: {}", Paint::white(Source::from(&*cm.context().root)));
info_!("engines: {:?}", Paint::white(Engines::ENABLED_EXTENSIONS));
}
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
async fn on_request(&self, req: &mut rocket::Request<'_>, _data: &mut rocket::Data) { async fn on_request(&self, req: &mut rocket::Request<'_>, _data: &mut rocket::Data) {
let cm = req.rocket().state::<ContextManager>() let cm = req.rocket().state::<ContextManager>()
.expect("Template ContextManager registered in on_launch"); .expect("Template ContextManager registered in on_ignite");
cm.reload_if_needed(&self.callback); cm.reload_if_needed(&self.callback);
} }
} }

View File

@ -137,7 +137,7 @@ use std::borrow::Cow;
use std::path::PathBuf; use std::path::PathBuf;
use std::error::Error; use std::error::Error;
use rocket::Rocket; use rocket::{Rocket, Orbit};
use rocket::request::Request; use rocket::request::Request;
use rocket::fairing::Fairing; use rocket::fairing::Fairing;
use rocket::response::{self, Content, Responder}; use rocket::response::{self, Content, Responder};
@ -373,7 +373,7 @@ impl Template {
/// } /// }
/// ``` /// ```
#[inline] #[inline]
pub fn show<S, C>(rocket: &Rocket, name: S, context: C) -> Option<String> pub fn show<S, C>(rocket: &Rocket<Orbit>, name: S, context: C) -> Option<String>
where S: Into<Cow<'static, str>>, C: Serialize where S: Into<Cow<'static, str>>, C: Serialize
{ {
let ctxt = rocket.state::<ContextManager>().map(ContextManager::context).or_else(|| { let ctxt = rocket.state::<ContextManager>().map(ContextManager::context).or_else(|| {

View File

@ -36,7 +36,7 @@ mod rusqlite_integration_test {
let rocket = rocket::custom(config) let rocket = rocket::custom(config)
.attach(SqliteDb::fairing()) .attach(SqliteDb::fairing())
.attach(SqliteDb2::fairing()) .attach(SqliteDb2::fairing())
._ignite() .ignite()
.await .await
.unwrap(); .unwrap();
@ -59,6 +59,7 @@ mod rusqlite_integration_test {
#[cfg(feature = "databases")] #[cfg(feature = "databases")]
#[cfg(test)] #[cfg(test)]
mod drop_runtime_test { mod drop_runtime_test {
use rocket::{Rocket, Build};
use r2d2::{ManageConnection, Pool}; use r2d2::{ManageConnection, Pool};
use rocket_contrib::databases::{database, Poolable, PoolResult}; use rocket_contrib::databases::{database, Poolable, PoolResult};
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
@ -87,7 +88,7 @@ mod drop_runtime_test {
type Manager = ContainsRuntime; type Manager = ContainsRuntime;
type Error = (); type Error = ();
fn pool(_db_name: &str, _rocket: &rocket::Rocket) -> PoolResult<Self> { fn pool(_db_name: &str, _rocket: &Rocket<Build>) -> PoolResult<Self> {
let manager = ContainsRuntime(tokio::runtime::Runtime::new().unwrap()); let manager = ContainsRuntime(tokio::runtime::Runtime::new().unwrap());
Ok(Pool::builder().build(manager)?) Ok(Pool::builder().build(manager)?)
} }

View File

@ -3,16 +3,17 @@ mod static_tests {
use std::{io::Read, fs::File}; use std::{io::Read, fs::File};
use std::path::Path; use std::path::Path;
use rocket::{self, Rocket, Route}; use rocket::{Rocket, Route, Build};
use rocket_contrib::serve::{StaticFiles, Options, crate_relative};
use rocket::http::Status; use rocket::http::Status;
use rocket::local::blocking::Client; use rocket::local::blocking::Client;
use rocket_contrib::serve::{StaticFiles, Options, crate_relative};
fn static_root() -> &'static Path { fn static_root() -> &'static Path {
Path::new(crate_relative!("/tests/static")) Path::new(crate_relative!("/tests/static"))
} }
fn rocket() -> Rocket { fn rocket() -> Rocket<Build> {
let root = static_root(); let root = static_root();
rocket::build() rocket::build()
.mount("/default", StaticFiles::from(&root)) .mount("/default", StaticFiles::from(&root))

View File

@ -5,7 +5,7 @@
mod templates_tests { mod templates_tests {
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use rocket::Rocket; use rocket::{Rocket, Build};
use rocket::config::Config; use rocket::config::Config;
use rocket_contrib::templates::{Template, Metadata}; use rocket_contrib::templates::{Template, Metadata};
@ -26,7 +26,7 @@ mod templates_tests {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests").join("templates") Path::new(env!("CARGO_MANIFEST_DIR")).join("tests").join("templates")
} }
fn rocket() -> Rocket { fn rocket() -> Rocket<Build> {
rocket::custom(Config::figment().merge(("template_dir", template_root()))) rocket::custom(Config::figment().merge(("template_dir", template_root())))
.attach(Template::fairing()) .attach(Template::fairing())
.mount("/", routes![template_check, is_reloading]) .mount("/", routes![template_check, is_reloading])
@ -42,7 +42,7 @@ mod templates_tests {
let error = Client::debug(rocket).expect_err("client failure"); let error = Client::debug(rocket).expect_err("client failure");
match error.kind() { match error.kind() {
FailedFairings(failures) => assert_eq!(failures[0].name, "Templates"), FailedFairings(failures) => assert_eq!(failures[0].name, "Templating"),
_ => panic!("Wrong kind of launch error"), _ => panic!("Wrong kind of launch error"),
} }
} }

View File

@ -49,7 +49,7 @@ pub fn _catch(
let catcher_response = quote_spanned!(return_type_span => { let catcher_response = quote_spanned!(return_type_span => {
let ___responder = #user_catcher_fn_name(#(#inputs),*) #dot_await; let ___responder = #user_catcher_fn_name(#(#inputs),*) #dot_await;
::rocket::response::Responder::respond_to(___responder, #__req)? #_response::Responder::respond_to(___responder, #__req)?
}); });
// Generate the catcher, keeping the user's input around. // Generate the catcher, keeping the user's input around.

View File

@ -20,10 +20,10 @@ impl EntryAttr for Launch {
.span_note(f.sig.ident.span(), "this function cannot be `main`")); .span_note(f.sig.ident.span(), "this function cannot be `main`"));
} }
// Always infer the type as `::rocket::Rocket`. // Always infer the type as `Rocket<Build>`.
if let syn::ReturnType::Type(_, ref mut ty) = &mut f.sig.output { if let syn::ReturnType::Type(_, ref mut ty) = &mut f.sig.output {
if let syn::Type::Infer(_) = &mut **ty { if let syn::Type::Infer(_) = &mut **ty {
let new = quote_spanned!(ty.span() => ::rocket::Rocket); let new = quote_spanned!(ty.span() => ::rocket::Rocket<::rocket::Build>);
*ty = syn::parse2(new).expect("path is type"); *ty = syn::parse2(new).expect("path is type");
} }
} }
@ -38,7 +38,7 @@ impl EntryAttr for Launch {
let block = &f.block; let block = &f.block;
let rocket = quote_spanned!(ty.span().into() => { let rocket = quote_spanned!(ty.span().into() => {
let ___rocket: #ty = #block; let ___rocket: #ty = #block;
let ___rocket: ::rocket::Rocket = ___rocket; let ___rocket: ::rocket::Rocket<::rocket::Build> = ___rocket;
___rocket ___rocket
}); });

View File

@ -3,25 +3,25 @@
mod a { mod a {
// async launch that is async. // async launch that is async.
#[rocket::launch] #[rocket::launch]
async fn rocket() -> rocket::Rocket { async fn rocket() -> rocket::Rocket<rocket::Build> {
let _ = rocket::build().launch().await; let _ = rocket::build().launch().await;
rocket::build() rocket::build()
} }
async fn use_it() { async fn use_it() {
let rocket: rocket::Rocket = rocket().await; let rocket: rocket::Rocket<rocket::Build> = rocket().await;
} }
} }
mod b { mod b {
// async launch that isn't async. // async launch that isn't async.
#[rocket::launch] #[rocket::launch]
async fn main2() -> rocket::Rocket { async fn main2() -> _ {
rocket::build() rocket::build()
} }
async fn use_it() { async fn use_it() {
let rocket: rocket::Rocket = main2().await; let rocket: rocket::Rocket<_> = main2().await;
} }
} }
@ -30,7 +30,7 @@ mod b_inferred {
async fn main2() -> _ { rocket::build() } async fn main2() -> _ { rocket::build() }
async fn use_it() { async fn use_it() {
let rocket: rocket::Rocket = main2().await; let rocket: rocket::Rocket<_> = main2().await;
} }
} }
@ -42,7 +42,7 @@ mod c {
} }
fn use_it() { fn use_it() {
let rocket: rocket::Rocket = rocket(); let rocket: rocket::Rocket<_> = rocket();
} }
} }
@ -51,7 +51,7 @@ mod c_inferred {
fn rocket() -> _ { rocket::build() } fn rocket() -> _ { rocket::build() }
fn use_it() { fn use_it() {
let rocket: rocket::Rocket = rocket(); let rocket: rocket::Rocket<_> = rocket();
} }
} }

View File

@ -144,7 +144,7 @@ mod scopes {
use other::world; use other::world;
fn _rocket() -> rocket::Rocket { fn _rocket() -> rocket::Rocket<rocket::Build> {
rocket::build().mount("/", rocket::routes![hello, world]) rocket::build().mount("/", rocket::routes![hello, world])
} }
} }
@ -258,7 +258,7 @@ fn test_query_collection() {
(response.status(), response.into_string().unwrap()) (response.status(), response.into_string().unwrap())
} }
fn run_tests(rocket: rocket::Rocket) { fn run_tests(rocket: rocket::Rocket<rocket::Build>) {
let client = Client::debug(rocket).unwrap(); let client = Client::debug(rocket).unwrap();
let colors = &["blue", "green"]; let colors = &["blue", "green"];

View File

@ -1,156 +1,168 @@
error: attribute can only be applied to `async` functions error: attribute can only be applied to `async` functions
--> $DIR/async-entry.rs:6:5 --> $DIR/async-entry.rs:4:5
| |
6 | #[rocket::main] 4 | #[rocket::main]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
| |
note: this function must be `async` note: this function must be `async`
--> $DIR/async-entry.rs:7:5 --> $DIR/async-entry.rs:5:5
| |
7 | fn foo() { } 5 | fn foo() { }
| ^^^^^^^^ | ^^^^^^^^
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
warning: attribute is typically applied to `main` function warning: attribute is typically applied to `main` function
--> $DIR/async-entry.rs:12:5 --> $DIR/async-entry.rs:10:5
| |
12 | #[rocket::main] 10 | #[rocket::main]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
| |
note: this function is not `main` note: this function is not `main`
--> $DIR/async-entry.rs:13:14 --> $DIR/async-entry.rs:11:14
| |
13 | async fn foo() { } 11 | async fn foo() { }
| ^^^ | ^^^
= note: this warning originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this warning originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: attribute can only be applied to `async` functions error: attribute can only be applied to `async` functions
--> $DIR/async-entry.rs:18:5 --> $DIR/async-entry.rs:16:5
| |
18 | #[rocket::main] 16 | #[rocket::main]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
| |
note: this function must be `async` note: this function must be `async`
--> $DIR/async-entry.rs:19:5 --> $DIR/async-entry.rs:17:5
| |
19 | fn main() { 17 | fn main() {
| ^^^^^^^^^ | ^^^^^^^^^
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: attribute cannot be applied to `main` function error: attribute cannot be applied to `main` function
--> $DIR/async-entry.rs:51:5 --> $DIR/async-entry.rs:49:5
| |
51 | #[rocket::launch] 49 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= note: this attribute generates a `main` function = note: this attribute generates a `main` function
note: this function cannot be `main` note: this function cannot be `main`
--> $DIR/async-entry.rs:52:8 --> $DIR/async-entry.rs:50:8
| |
52 | fn main() -> rocket::Rocket { 50 | fn main() -> rocekt::Rocket<rocket::Build> {
| ^^^^ | ^^^^
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: attribute can only be applied to functions that return a value error: attribute can only be applied to functions that return a value
--> $DIR/async-entry.rs:58:5 --> $DIR/async-entry.rs:56:5
| |
58 | #[rocket::launch] 56 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
note: this function must return a value note: this function must return a value
--> $DIR/async-entry.rs:59:5 --> $DIR/async-entry.rs:57:5
| |
59 | async fn rocket() { 57 | async fn rocket() {
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: attribute can only be applied to functions that return a value error: attribute can only be applied to functions that return a value
--> $DIR/async-entry.rs:66:5 --> $DIR/async-entry.rs:64:5
| |
66 | #[rocket::launch] 64 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
note: this function must return a value note: this function must return a value
--> $DIR/async-entry.rs:67:5 --> $DIR/async-entry.rs:65:5
| |
67 | fn rocket() { 65 | fn rocket() {
| ^^^^^^^^^^^ | ^^^^^^^^^^^
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: attribute cannot be applied to `main` function error: attribute cannot be applied to `main` function
--> $DIR/async-entry.rs:81:5 --> $DIR/async-entry.rs:79:5
| |
81 | #[rocket::launch] 79 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= note: this attribute generates a `main` function = note: this attribute generates a `main` function
note: this function cannot be `main` note: this function cannot be `main`
--> $DIR/async-entry.rs:82:8 --> $DIR/async-entry.rs:80:8
| |
82 | fn main() -> &'static str { 80 | fn main() -> &'static str {
| ^^^^ | ^^^^
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: attribute cannot be applied to `main` function error: attribute cannot be applied to `main` function
--> $DIR/async-entry.rs:89:5 --> $DIR/async-entry.rs:87:5
| |
89 | #[rocket::launch] 87 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= note: this attribute generates a `main` function = note: this attribute generates a `main` function
note: this function cannot be `main` note: this function cannot be `main`
--> $DIR/async-entry.rs:90:14 --> $DIR/async-entry.rs:88:14
| |
90 | async fn main() -> rocket::Rocket { 88 | async fn main() -> _ {
| ^^^^ | ^^^^
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0728]: `await` is only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks
--> $DIR/async-entry.rs:75:17 --> $DIR/async-entry.rs:73:17
| |
72 | fn rocket() -> _ { 72 | fn rocket() -> _ {
| ------ this is not `async` | ------ this is not `async`
75 | let _ = rocket::build().launch().await; 73 | let _ = rocket::build().launch().await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/async-entry.rs:37:9 --> $DIR/async-entry.rs:35:9
| |
37 | rocket::build() 35 | rocket::build()
| ^^^^^^^^^^^^^^^ expected struct `std::string::String`, found struct `Rocket` | ^^^^^^^^^^^^^^^ expected struct `std::string::String`, found struct `Rocket`
|
= note: expected struct `std::string::String`
found struct `Rocket<Build>`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/async-entry.rs:46:9 --> $DIR/async-entry.rs:44:9
| |
46 | "hi".to_string() 44 | "hi".to_string()
| ^^^^^^^^^^^^^^^^ expected struct `Rocket`, found struct `std::string::String` | ^^^^^^^^^^^^^^^^ expected struct `Rocket`, found struct `std::string::String`
|
= note: expected struct `Rocket<Build>`
found struct `std::string::String`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/async-entry.rs:26:21 --> $DIR/async-entry.rs:24:21
| |
26 | async fn main() { 24 | async fn main() {
| ^ expected `()` because of default return type | ^ expected `()` because of default return type
| _____________________| | _____________________|
| | | |
27 | | rocket::build() 25 | | rocket::build()
28 | | } 26 | | }
| | ^- help: consider using a semicolon here: `;` | | ^- help: consider using a semicolon here: `;`
| |_____| | |_____|
| expected `()`, found struct `Rocket` | expected `()`, found struct `Rocket`
|
= note: expected unit type `()`
found struct `Rocket<Build>`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/async-entry.rs:35:26 --> $DIR/async-entry.rs:33:26
| |
35 | async fn rocket() -> String { 33 | async fn rocket() -> String {
| ^^^^^^ | ^^^^^^
| | | |
| expected struct `Rocket`, found struct `std::string::String` | expected struct `Rocket`, found struct `std::string::String`
| expected due to this | expected due to this
error[E0277]: `main` has invalid return type `Rocket`
--> $DIR/async-entry.rs:96:20
| |
96 | async fn main() -> rocket::Rocket { = note: expected struct `Rocket<Build>`
| ^^^^^^^^^^^^^^ `main` can only return types that implement `Termination` found struct `std::string::String`
error[E0277]: `main` has invalid return type `Rocket<Build>`
--> $DIR/async-entry.rs:94:20
|
94 | async fn main() -> rocket::Rocket<rocket::Build> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` can only return types that implement `Termination`
| |
= help: consider using `()`, or a `Result` = help: consider using `()`, or a `Result`

View File

@ -1,150 +1,162 @@
error: attribute can only be applied to `async` functions error: attribute can only be applied to `async` functions
--> $DIR/async-entry.rs:6:5 --> $DIR/async-entry.rs:4:5
| |
6 | #[rocket::main] 4 | #[rocket::main]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
| |
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: [note] this function must be `async` error: [note] this function must be `async`
--> $DIR/async-entry.rs:7:5 --> $DIR/async-entry.rs:5:5
| |
7 | fn foo() { } 5 | fn foo() { }
| ^^ | ^^
error: attribute can only be applied to `async` functions error: attribute can only be applied to `async` functions
--> $DIR/async-entry.rs:18:5 --> $DIR/async-entry.rs:16:5
| |
18 | #[rocket::main] 16 | #[rocket::main]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
| |
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: [note] this function must be `async` error: [note] this function must be `async`
--> $DIR/async-entry.rs:19:5 --> $DIR/async-entry.rs:17:5
| |
19 | fn main() { 17 | fn main() {
| ^^ | ^^
error: attribute cannot be applied to `main` function error: attribute cannot be applied to `main` function
--- note: this attribute generates a `main` function --- note: this attribute generates a `main` function
--> $DIR/async-entry.rs:51:5 --> $DIR/async-entry.rs:49:5
| |
51 | #[rocket::launch] 49 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: [note] this function cannot be `main` error: [note] this function cannot be `main`
--> $DIR/async-entry.rs:52:8 --> $DIR/async-entry.rs:50:8
| |
52 | fn main() -> rocket::Rocket { 50 | fn main() -> rocekt::Rocket<rocket::Build> {
| ^^^^ | ^^^^
error: attribute can only be applied to functions that return a value error: attribute can only be applied to functions that return a value
--> $DIR/async-entry.rs:58:5 --> $DIR/async-entry.rs:56:5
| |
58 | #[rocket::launch] 56 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: [note] this function must return a value error: [note] this function must return a value
--> $DIR/async-entry.rs:59:5 --> $DIR/async-entry.rs:57:5
| |
59 | async fn rocket() { 57 | async fn rocket() {
| ^^^^^ | ^^^^^
error: attribute can only be applied to functions that return a value error: attribute can only be applied to functions that return a value
--> $DIR/async-entry.rs:66:5 --> $DIR/async-entry.rs:64:5
| |
66 | #[rocket::launch] 64 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: [note] this function must return a value error: [note] this function must return a value
--> $DIR/async-entry.rs:67:5 --> $DIR/async-entry.rs:65:5
| |
67 | fn rocket() { 65 | fn rocket() {
| ^^ | ^^
error: attribute cannot be applied to `main` function error: attribute cannot be applied to `main` function
--- note: this attribute generates a `main` function --- note: this attribute generates a `main` function
--> $DIR/async-entry.rs:81:5 --> $DIR/async-entry.rs:79:5
| |
81 | #[rocket::launch] 79 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: [note] this function cannot be `main` error: [note] this function cannot be `main`
--> $DIR/async-entry.rs:82:8 --> $DIR/async-entry.rs:80:8
| |
82 | fn main() -> &'static str { 80 | fn main() -> &'static str {
| ^^^^ | ^^^^
error: attribute cannot be applied to `main` function error: attribute cannot be applied to `main` function
--- note: this attribute generates a `main` function --- note: this attribute generates a `main` function
--> $DIR/async-entry.rs:89:5 --> $DIR/async-entry.rs:87:5
| |
89 | #[rocket::launch] 87 | #[rocket::launch]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: [note] this function cannot be `main` error: [note] this function cannot be `main`
--> $DIR/async-entry.rs:90:14 --> $DIR/async-entry.rs:88:14
| |
90 | async fn main() -> rocket::Rocket { 88 | async fn main() -> _ {
| ^^^^ | ^^^^
error[E0728]: `await` is only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks
--> $DIR/async-entry.rs:75:17 --> $DIR/async-entry.rs:73:17
| |
72 | fn rocket() -> _ { 72 | fn rocket() -> _ {
| ------ this is not `async` | ------ this is not `async`
75 | let _ = rocket::build().launch().await; 73 | let _ = rocket::build().launch().await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/async-entry.rs:37:9 --> $DIR/async-entry.rs:35:9
| |
37 | rocket::build() 35 | rocket::build()
| ^^^^^^^^^^^^^^^ expected struct `std::string::String`, found struct `Rocket` | ^^^^^^^^^^^^^^^ expected struct `std::string::String`, found struct `Rocket`
|
= note: expected struct `std::string::String`
found struct `Rocket<Build>`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/async-entry.rs:46:9 --> $DIR/async-entry.rs:44:9
| |
46 | "hi".to_string() 44 | "hi".to_string()
| ^^^^^^^^^^^^^^^^ expected struct `Rocket`, found struct `std::string::String` | ^^^^^^^^^^^^^^^^ expected struct `Rocket`, found struct `std::string::String`
|
= note: expected struct `Rocket<Build>`
found struct `std::string::String`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/async-entry.rs:26:21 --> $DIR/async-entry.rs:24:21
| |
26 | async fn main() { 24 | async fn main() {
| ^ expected `()` because of default return type | ^ expected `()` because of default return type
| _____________________| | _____________________|
| | | |
27 | | rocket::build() 25 | | rocket::build()
28 | | } 26 | | }
| | ^- help: try adding a semicolon: `;` | | ^- help: try adding a semicolon: `;`
| |_____| | |_____|
| expected `()`, found struct `Rocket` | expected `()`, found struct `Rocket`
|
= note: expected unit type `()`
found struct `Rocket<Build>`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/async-entry.rs:35:26 --> $DIR/async-entry.rs:33:26
| |
35 | async fn rocket() -> String { 33 | async fn rocket() -> String {
| ^^^^^^ | ^^^^^^
| | | |
| expected struct `Rocket`, found struct `std::string::String` | expected struct `Rocket`, found struct `std::string::String`
| expected due to this | expected due to this
error[E0277]: `main` has invalid return type `Rocket`
--> $DIR/async-entry.rs:96:20
| |
96 | async fn main() -> rocket::Rocket { = note: expected struct `Rocket<Build>`
| ^^^^^^^^^^^^^^ `main` can only return types that implement `Termination` found struct `std::string::String`
error[E0277]: `main` has invalid return type `Rocket<Build>`
--> $DIR/async-entry.rs:94:20
|
94 | async fn main() -> rocket::Rocket<rocket::Build> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` can only return types that implement `Termination`
| |
= help: consider using `()`, or a `Result` = help: consider using `()`, or a `Result`

View File

@ -1,7 +1,5 @@
#![allow(dead_code)] #![allow(dead_code)]
// rocket::main
mod main_a { mod main_a {
#[rocket::main] #[rocket::main]
fn foo() { } fn foo() { }
@ -49,7 +47,7 @@ mod launch_b {
mod launch_c { mod launch_c {
#[rocket::launch] #[rocket::launch]
fn main() -> rocket::Rocket { fn main() -> rocekt::Rocket<rocket::Build> {
rocket::build() rocket::build()
} }
} }
@ -87,12 +85,12 @@ mod launch_g {
mod launch_h { mod launch_h {
#[rocket::launch] #[rocket::launch]
async fn main() -> rocket::Rocket { async fn main() -> _ {
rocket::build() rocket::build()
} }
} }
#[rocket::main] #[rocket::main]
async fn main() -> rocket::Rocket { async fn main() -> rocket::Rocket<rocket::Build> {
rocket::build() rocket::build()
} }

View File

@ -23,7 +23,6 @@ serde = ["uncased/with-serde-alloc", "_serde"]
[dependencies] [dependencies]
smallvec = "1.0" smallvec = "1.0"
percent-encoding = "2" percent-encoding = "2"
hyper = { version = "0.14", default-features = false, features = ["http1", "http2", "runtime", "server", "stream"] }
http = "0.2" http = "0.2"
mime = "0.3.13" mime = "0.3.13"
time = "0.2.11" time = "0.2.11"
@ -41,9 +40,14 @@ memchr = "2"
stable-pattern = "0.1" stable-pattern = "0.1"
cookie = { version = "0.15", features = ["percent-encode"] } cookie = { version = "0.15", features = ["percent-encode"] }
[dependencies.hyper]
version = "0.14.5"
default-features = false
features = ["http1", "http2", "runtime", "server", "stream"]
[dependencies.state] [dependencies.state]
git = "https://github.com/SergioBenitez/state.git" git = "https://github.com/SergioBenitez/state.git"
rev = "504ef71a" rev = "8f94dc"
[dependencies._serde] [dependencies._serde]
package = "serde" package = "serde"

View File

@ -306,8 +306,14 @@ impl Config {
false => launch_info_!("tls: {}", Paint::default("disabled").bold()), false => launch_info_!("tls: {}", Paint::default("disabled").bold()),
} }
#[cfg(feature = "secrets")] #[cfg(feature = "secrets")] {
launch_info_!("secret key: {:?}", Paint::default(&self.secret_key).bold()); launch_info_!("secret key: {:?}", Paint::default(&self.secret_key).bold());
if !self.secret_key.is_provided() {
warn!("secrets enabled without a stable `secret_key`");
launch_info_!("disable `secrets` feature or configure a `secret_key`");
launch_info_!("this becomes an {} in non-debug profiles", Paint::red("error"));
}
}
launch_info_!("temp dir: {}", Paint::default(&self.temp_dir.display()).bold()); launch_info_!("temp dir: {}", Paint::default(&self.temp_dir.display()).bold());
launch_info_!("log level: {}", Paint::default(self.log_level).bold()); launch_info_!("log level: {}", Paint::default(self.log_level).bold());
@ -318,11 +324,11 @@ impl Config {
if let Some(md) = figment.find_metadata(key) { if let Some(md) = figment.find_metadata(key) {
warn!("found value for deprecated config key `{}`", Paint::white(key)); warn!("found value for deprecated config key `{}`", Paint::white(key));
if let Some(ref source) = md.source { if let Some(ref source) = md.source {
info_!("in {} {}", Paint::white(source), md.name); launch_info_!("in {} {}", Paint::white(source), md.name);
} }
if let Some(new_key) = replacement { if let Some(new_key) = replacement {
info_!("key has been by replaced by `{}`", Paint::white(new_key)); launch_info_!("key has been by replaced by `{}`", Paint::white(new_key));
} }
} }
} }

View File

@ -77,6 +77,9 @@ pub enum ErrorKind {
Io(io::Error), Io(io::Error),
/// An I/O error occurred in the runtime. /// An I/O error occurred in the runtime.
Runtime(Box<dyn std::error::Error + Send + Sync>), Runtime(Box<dyn std::error::Error + Send + Sync>),
/// A valid [`Config`](crate::Config) could not be extracted from the
/// configured figment.
Config(figment::Error),
/// Route collisions were detected. /// Route collisions were detected.
Collisions(crate::router::Collisions), Collisions(crate::router::Collisions),
/// Launch fairing(s) failed. /// Launch fairing(s) failed.
@ -142,6 +145,7 @@ impl fmt::Display for ErrorKind {
ErrorKind::FailedFairings(_) => "a launch fairing failed".fmt(f), ErrorKind::FailedFairings(_) => "a launch fairing failed".fmt(f),
ErrorKind::Runtime(e) => write!(f, "runtime error: {}", e), ErrorKind::Runtime(e) => write!(f, "runtime error: {}", e),
ErrorKind::InsecureSecretKey(_) => "insecure secret key config".fmt(f), ErrorKind::InsecureSecretKey(_) => "insecure secret key config".fmt(f),
ErrorKind::Config(_) => "failed to extract configuration".fmt(f),
} }
} }
} }
@ -214,6 +218,10 @@ impl Drop for Error {
info_!("disable `secrets` feature or configure a `secret_key`"); info_!("disable `secrets` feature or configure a `secret_key`");
panic!("aborting due to insecure configuration") panic!("aborting due to insecure configuration")
} }
ErrorKind::Config(error) => {
crate::config::pretty_print_error(error.clone());
panic!("aborting due to invalid configuration")
}
} }
} }
} }

View File

@ -2,8 +2,8 @@ use std::sync::Mutex;
use futures::future::{Future, BoxFuture, FutureExt}; use futures::future::{Future, BoxFuture, FutureExt};
use crate::{Rocket, Request, Response, Data}; use crate::{Rocket, Request, Response, Data, Build, Orbit};
use crate::fairing::{Fairing, Kind, Info}; use crate::fairing::{Fairing, Kind, Info, Result};
/// A ad-hoc fairing that can be created from a function or closure. /// A ad-hoc fairing that can be created from a function or closure.
/// ///
@ -12,7 +12,7 @@ use crate::fairing::{Fairing, Kind, Info};
/// ///
/// # Usage /// # Usage
/// ///
/// Use [`AdHoc::on_launch`], [`AdHoc::on_liftoff`], [`AdHoc::on_request()`], or /// Use [`AdHoc::on_ignite`], [`AdHoc::on_liftoff`], [`AdHoc::on_request()`], or
/// [`AdHoc::on_response()`] to create an `AdHoc` structure from a function or /// [`AdHoc::on_response()`] to create an `AdHoc` structure from a function or
/// closure. Then, simply attach the structure to the `Rocket` instance. /// closure. Then, simply attach the structure to the `Rocket` instance.
/// ///
@ -51,14 +51,12 @@ impl<F: ?Sized> Once<F> {
} }
} }
type Result<T = Rocket, E = Rocket> = std::result::Result<T, E>;
enum AdHocKind { enum AdHocKind {
/// An ad-hoc **launch** fairing. Called just before Rocket launches. /// An ad-hoc **ignite** fairing. Called during ignition.
Launch(Once<dyn FnOnce(Rocket) -> BoxFuture<'static, Result> + Send + 'static>), Ignite(Once<dyn FnOnce(Rocket<Build>) -> BoxFuture<'static, Result> + Send + 'static>),
/// An ad-hoc **liftoff** fairing. Called just after Rocket launches. /// An ad-hoc **liftoff** fairing. Called just after Rocket launches.
Liftoff(Once<dyn for<'a> FnOnce(&'a Rocket) -> BoxFuture<'a, ()> + Send + 'static>), Liftoff(Once<dyn for<'a> FnOnce(&'a Rocket<Orbit>) -> BoxFuture<'a, ()> + Send + 'static>),
/// An ad-hoc **request** fairing. Called when a request is received. /// An ad-hoc **request** fairing. Called when a request is received.
Request(Box<dyn for<'a> Fn(&'a mut Request<'_>, &'a Data) Request(Box<dyn for<'a> Fn(&'a mut Request<'_>, &'a Data)
@ -71,52 +69,52 @@ enum AdHocKind {
} }
impl AdHoc { impl AdHoc {
/// Constructs an `AdHoc` launch fairing named `name`. The function `f` will /// Constructs an `AdHoc` ignite fairing named `name`. The function `f` will
/// be called by Rocket just prior to launch. /// be called by Rocket during the [`Rocket::ignite()`] phase.
/// ///
/// This version of an `AdHoc` launch fairing cannot abort launch. For a /// This version of an `AdHoc` ignite fairing cannot abort ignite. For a
/// fallible version that can, see [`AdHoc::try_on_launch()`]. /// fallible version that can, see [`AdHoc::try_on_ignite()`].
/// ///
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::fairing::AdHoc; /// use rocket::fairing::AdHoc;
/// ///
/// // The no-op launch fairing. /// // The no-op ignite fairing.
/// let fairing = AdHoc::on_launch("Boom!", |rocket| async move { /// let fairing = AdHoc::on_ignite("Boom!", |rocket| async move {
/// rocket /// rocket
/// }); /// });
/// ``` /// ```
pub fn on_launch<F, Fut>(name: &'static str, f: F) -> AdHoc pub fn on_ignite<F, Fut>(name: &'static str, f: F) -> AdHoc
where F: FnOnce(Rocket) -> Fut + Send + 'static, where F: FnOnce(Rocket<Build>) -> Fut + Send + 'static,
Fut: Future<Output = Rocket> + Send + 'static, Fut: Future<Output = Rocket<Build>> + Send + 'static,
{ {
AdHoc::try_on_launch(name, |rocket| f(rocket).map(Ok)) AdHoc::try_on_ignite(name, |rocket| f(rocket).map(Ok))
} }
/// Constructs an `AdHoc` launch fairing named `name`. The function `f` will /// Constructs an `AdHoc` ignite fairing named `name`. The function `f` will
/// be called by Rocket just prior to launch. Returning an `Err` aborts /// be called by Rocket during the [`Rocket::ignite()`] phase. Returning an
/// launch. /// `Err` aborts ignition and thus launch.
/// ///
/// For an infallible version, see [`AdHoc::on_launch()`]. /// For an infallible version, see [`AdHoc::on_ignite()`].
/// ///
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::fairing::AdHoc; /// use rocket::fairing::AdHoc;
/// ///
/// // The no-op try launch fairing. /// // The no-op try ignite fairing.
/// let fairing = AdHoc::try_on_launch("No-Op", |rocket| async { Ok(rocket) }); /// let fairing = AdHoc::try_on_ignite("No-Op", |rocket| async { Ok(rocket) });
/// ``` /// ```
pub fn try_on_launch<F, Fut>(name: &'static str, f: F) -> AdHoc pub fn try_on_ignite<F, Fut>(name: &'static str, f: F) -> AdHoc
where F: FnOnce(Rocket) -> Fut + Send + 'static, where F: FnOnce(Rocket<Build>) -> Fut + Send + 'static,
Fut: Future<Output = Result<Rocket, Rocket>> + Send + 'static, Fut: Future<Output = Result> + Send + 'static,
{ {
AdHoc { name, kind: AdHocKind::Launch(Once::new(Box::new(|r| f(r).boxed()))) } AdHoc { name, kind: AdHocKind::Ignite(Once::new(Box::new(|r| f(r).boxed()))) }
} }
/// Constructs an `AdHoc` launch fairing named `name`. The function `f` will /// Constructs an `AdHoc` liftoff fairing named `name`. The function `f`
/// be called by Rocket just after launching. /// will be called by Rocket just after [`Rocket::launch()`].
/// ///
/// # Example /// # Example
/// ///
@ -129,7 +127,7 @@ impl AdHoc {
/// })); /// }));
/// ``` /// ```
pub fn on_liftoff<F: Send + Sync + 'static>(name: &'static str, f: F) -> AdHoc pub fn on_liftoff<F: Send + Sync + 'static>(name: &'static str, f: F) -> AdHoc
where F: for<'a> FnOnce(&'a Rocket) -> BoxFuture<'a, ()> where F: for<'a> FnOnce(&'a Rocket<Orbit>) -> BoxFuture<'a, ()>
{ {
AdHoc { name, kind: AdHocKind::Liftoff(Once::new(Box::new(f))) } AdHoc { name, kind: AdHocKind::Liftoff(Once::new(Box::new(f))) }
} }
@ -190,6 +188,7 @@ impl AdHoc {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # use rocket::launch;
/// use serde::Deserialize; /// use serde::Deserialize;
/// use rocket::fairing::AdHoc; /// use rocket::fairing::AdHoc;
/// ///
@ -208,7 +207,7 @@ impl AdHoc {
pub fn config<'de, T>() -> AdHoc pub fn config<'de, T>() -> AdHoc
where T: serde::Deserialize<'de> + Send + Sync + 'static where T: serde::Deserialize<'de> + Send + Sync + 'static
{ {
AdHoc::try_on_launch(std::any::type_name::<T>(), |rocket| async { AdHoc::try_on_ignite(std::any::type_name::<T>(), |rocket| async {
let app_config = match rocket.figment().extract::<T>() { let app_config = match rocket.figment().extract::<T>() {
Ok(config) => config, Ok(config) => config,
Err(e) => { Err(e) => {
@ -226,7 +225,7 @@ impl AdHoc {
impl Fairing for AdHoc { impl Fairing for AdHoc {
fn info(&self) -> Info { fn info(&self) -> Info {
let kind = match self.kind { let kind = match self.kind {
AdHocKind::Launch(_) => Kind::Launch, AdHocKind::Ignite(_) => Kind::Ignite,
AdHocKind::Liftoff(_) => Kind::Liftoff, AdHocKind::Liftoff(_) => Kind::Liftoff,
AdHocKind::Request(_) => Kind::Request, AdHocKind::Request(_) => Kind::Request,
AdHocKind::Response(_) => Kind::Response, AdHocKind::Response(_) => Kind::Response,
@ -235,14 +234,14 @@ impl Fairing for AdHoc {
Info { name: self.name, kind } Info { name: self.name, kind }
} }
async fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> { async fn on_ignite(&self, rocket: Rocket<Build>) -> Result {
match self.kind { match self.kind {
AdHocKind::Launch(ref f) => (f.take())(rocket).await, AdHocKind::Ignite(ref f) => (f.take())(rocket).await,
_ => Ok(rocket) _ => Ok(rocket)
} }
} }
async fn on_liftoff(&self, rocket: &Rocket) { async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
if let AdHocKind::Liftoff(ref f) = self.kind { if let AdHocKind::Liftoff(ref f) = self.kind {
(f.take())(rocket).await (f.take())(rocket).await
} }

View File

@ -1,4 +1,4 @@
use crate::{Rocket, Request, Response, Data}; use crate::{Rocket, Request, Response, Data, Build, Orbit};
use crate::fairing::{Fairing, Info, Kind}; use crate::fairing::{Fairing, Info, Kind};
use crate::logger::PaintExt; use crate::logger::PaintExt;
@ -35,7 +35,7 @@ impl Fairings {
let index = self.all_fairings.len(); let index = self.all_fairings.len();
self.all_fairings.push(fairing); self.all_fairings.push(fairing);
if kind.is(Kind::Launch) { self.launch.push(index); } if kind.is(Kind::Ignite) { self.launch.push(index); }
if kind.is(Kind::Liftoff) { self.liftoff.push(index); } if kind.is(Kind::Liftoff) { self.liftoff.push(index); }
if kind.is(Kind::Request) { self.request.push(index); } if kind.is(Kind::Request) { self.request.push(index); }
if kind.is(Kind::Response) { self.response.push(index); } if kind.is(Kind::Response) { self.response.push(index); }
@ -43,19 +43,19 @@ impl Fairings {
&*self.all_fairings[index] &*self.all_fairings[index]
} }
pub fn append(&mut self, others: Fairings) { pub fn append(&mut self, others: &mut Fairings) {
for fairing in others.all_fairings { for fairing in others.all_fairings.drain(..) {
self.add(fairing); self.add(fairing);
} }
} }
pub async fn handle_launch(mut rocket: Rocket) -> Rocket { pub async fn handle_ignite(mut rocket: Rocket<Build>) -> Rocket<Build> {
while rocket.fairings.last_launch < rocket.fairings.launch.len() { while rocket.fairings.last_launch < rocket.fairings.launch.len() {
// We're going to move `rocket` while borrowing `fairings`... // We're going to move `rocket` while borrowing `fairings`...
let mut fairings = std::mem::replace(&mut rocket.fairings, Fairings::new()); let mut fairings = std::mem::replace(&mut rocket.fairings, Fairings::new());
for fairing in iter!(fairings.launch).skip(fairings.last_launch) { for fairing in iter!(fairings.launch).skip(fairings.last_launch) {
let info = fairing.info(); let info = fairing.info();
rocket = match fairing.on_launch(rocket).await { rocket = match fairing.on_ignite(rocket).await {
Ok(rocket) => rocket, Ok(rocket) => rocket,
Err(rocket) => { Err(rocket) => {
fairings.failures.push(info); fairings.failures.push(info);
@ -68,7 +68,7 @@ impl Fairings {
// Note that `rocket.fairings` may now be non-empty since launch // Note that `rocket.fairings` may now be non-empty since launch
// fairings could have added more fairings! Move them to the end. // fairings could have added more fairings! Move them to the end.
fairings.append(rocket.fairings); fairings.append(&mut rocket.fairings);
rocket.fairings = fairings; rocket.fairings = fairings;
} }
@ -76,7 +76,7 @@ impl Fairings {
} }
#[inline(always)] #[inline(always)]
pub async fn handle_liftoff(&self, rocket: &Rocket) { pub async fn handle_liftoff(&self, rocket: &Rocket<Orbit>) {
let liftoff_futures = iter!(self.liftoff).map(|f| f.on_liftoff(rocket)); let liftoff_futures = iter!(self.liftoff).map(|f| f.on_liftoff(rocket));
futures::future::join_all(liftoff_futures).await; futures::future::join_all(liftoff_futures).await;
} }
@ -95,30 +95,21 @@ impl Fairings {
} }
} }
pub fn failures(&self) -> Option<&[Info]> { pub fn audit(&self) -> Result<(), &[Info]> {
match self.failures.is_empty() { match self.failures.is_empty() {
true => None, true => Ok(()),
false => Some(&self.failures) false => Err(&self.failures)
} }
} }
pub fn pretty_print_counts(&self) { pub fn pretty_print(&self) {
fn pretty_print<'a>(prefix: &str, iter: impl Iterator<Item = &'a dyn Fairing>) { if !self.all_fairings.is_empty() {
let names: Vec<_> = iter.map(|f| f.info().name).collect(); launch_info!("{}{}:", Paint::emoji("📡 "), Paint::magenta("Fairings"));
if names.is_empty() {
return;
}
let (num, joined) = (names.len(), names.join(", "));
info_!("{} {}: {}", Paint::default(num).bold(), prefix, Paint::default(joined).bold());
} }
if !self.all_fairings.is_empty() { for fairing in &self.all_fairings {
info!("{}{}:", Paint::emoji("📡 "), Paint::magenta("Fairings")); launch_info_!("{} ({})", Paint::default(fairing.info().name).bold(),
pretty_print("launch", iter!(self.launch)); Paint::blue(fairing.info().kind).bold());
pretty_print("liftoff", iter!(self.liftoff));
pretty_print("request", iter!(self.request));
pretty_print("response", iter!(self.response));
} }
} }
} }

View File

@ -18,7 +18,7 @@ use std::ops::BitOr;
/// # let _unused_info = /// # let _unused_info =
/// Info { /// Info {
/// name: "Example Fairing", /// name: "Example Fairing",
/// kind: Kind::Launch | Kind::Liftoff | Kind::Request | Kind::Response /// kind: Kind::Ignite | Kind::Liftoff | Kind::Request | Kind::Response
/// } /// }
/// # ; /// # ;
/// ``` /// ```
@ -36,22 +36,22 @@ pub struct Info {
/// A fairing can request any combination of any of the following kinds of /// A fairing can request any combination of any of the following kinds of
/// callbacks: /// callbacks:
/// ///
/// * Launch /// * Ignite
/// * Liftoff /// * Liftoff
/// * Request /// * Request
/// * Response /// * Response
/// ///
/// Two `Kind` structures can be `or`d together to represent a combination. For /// Two `Kind` structures can be `or`d together to represent a combination. For
/// instance, to represent a fairing that is both a launch and request fairing, /// instance, to represent a fairing that is both an ignite and request fairing,
/// use `Kind::Launch | Kind::Request`. Similarly, to represent a fairing that /// use `Kind::Ignite | Kind::Request`. Similarly, to represent a fairing that
/// is only a launch fairing, use `Kind::Launch`. /// is only an ignite fairing, use `Kind::Ignite`.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Kind(usize); pub struct Kind(usize);
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
impl Kind { impl Kind {
/// `Kind` flag representing a request for a 'launch' callback. /// `Kind` flag representing a request for a 'ignite' callback.
pub const Launch: Kind = Kind(1 << 0); pub const Ignite: Kind = Kind(1 << 0);
/// `Kind` flag representing a request for a 'liftoff' callback. /// `Kind` flag representing a request for a 'liftoff' callback.
pub const Liftoff: Kind = Kind(1 << 1); pub const Liftoff: Kind = Kind(1 << 1);
@ -70,16 +70,16 @@ impl Kind {
/// ```rust /// ```rust
/// use rocket::fairing::Kind; /// use rocket::fairing::Kind;
/// ///
/// let launch_and_req = Kind::Launch | Kind::Request; /// let ignite_and_req = Kind::Ignite | Kind::Request;
/// assert!(launch_and_req.is(Kind::Launch | Kind::Request)); /// assert!(ignite_and_req.is(Kind::Ignite | Kind::Request));
/// ///
/// assert!(launch_and_req.is(Kind::Launch)); /// assert!(ignite_and_req.is(Kind::Ignite));
/// assert!(launch_and_req.is(Kind::Request)); /// assert!(ignite_and_req.is(Kind::Request));
/// ///
/// assert!(!launch_and_req.is(Kind::Liftoff)); /// assert!(!ignite_and_req.is(Kind::Liftoff));
/// assert!(!launch_and_req.is(Kind::Response)); /// assert!(!ignite_and_req.is(Kind::Response));
/// assert!(!launch_and_req.is(Kind::Launch | Kind::Response)); /// assert!(!ignite_and_req.is(Kind::Ignite | Kind::Response));
/// assert!(!launch_and_req.is(Kind::Launch | Kind::Request | Kind::Response)); /// assert!(!ignite_and_req.is(Kind::Ignite | Kind::Request | Kind::Response));
/// ``` /// ```
#[inline] #[inline]
pub fn is(self, other: Kind) -> bool { pub fn is(self, other: Kind) -> bool {
@ -93,13 +93,13 @@ impl Kind {
/// ```rust /// ```rust
/// use rocket::fairing::Kind; /// use rocket::fairing::Kind;
/// ///
/// let launch_and_req = Kind::Launch | Kind::Request; /// let ignite_and_req = Kind::Ignite | Kind::Request;
/// assert!(launch_and_req.is_exactly(Kind::Launch | Kind::Request)); /// assert!(ignite_and_req.is_exactly(Kind::Ignite | Kind::Request));
/// ///
/// assert!(!launch_and_req.is_exactly(Kind::Launch)); /// assert!(!ignite_and_req.is_exactly(Kind::Ignite));
/// assert!(!launch_and_req.is_exactly(Kind::Request)); /// assert!(!ignite_and_req.is_exactly(Kind::Request));
/// assert!(!launch_and_req.is_exactly(Kind::Response)); /// assert!(!ignite_and_req.is_exactly(Kind::Response));
/// assert!(!launch_and_req.is_exactly(Kind::Launch | Kind::Response)); /// assert!(!ignite_and_req.is_exactly(Kind::Ignite | Kind::Response));
/// ``` /// ```
#[inline] #[inline]
pub fn is_exactly(self, other: Kind) -> bool { pub fn is_exactly(self, other: Kind) -> bool {
@ -115,3 +115,23 @@ impl BitOr for Kind {
Kind(self.0 | rhs.0) Kind(self.0 | rhs.0)
} }
} }
impl std::fmt::Display for Kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut is_first = true;
let mut write = |string, kind| {
if self.is(kind) {
if !is_first { f.write_str(", ")?; }
f.write_str(string)?;
is_first = false;
}
Ok(())
};
write("ignite", Kind::Ignite)?;
write("liftoff", Kind::Liftoff)?;
write("request", Kind::Request)?;
write("response", Kind::Response)
}
}

View File

@ -47,7 +47,7 @@
//! of other `Fairings` are not jeopardized. For instance, unless it is made //! of other `Fairings` are not jeopardized. For instance, unless it is made
//! abundantly clear, a fairing should not rewrite every request. //! abundantly clear, a fairing should not rewrite every request.
use crate::{Rocket, Request, Response, Data}; use crate::{Rocket, Request, Response, Data, Build, Orbit};
mod fairings; mod fairings;
mod ad_hoc; mod ad_hoc;
@ -57,6 +57,9 @@ pub(crate) use self::fairings::Fairings;
pub use self::ad_hoc::AdHoc; pub use self::ad_hoc::AdHoc;
pub use self::info_kind::{Info, Kind}; pub use self::info_kind::{Info, Kind};
/// A type alias for the return `Result` type of [`Fairing::on_ignite()`].
pub type Result<T = Rocket<Build>, E = Rocket<Build>> = std::result::Result<T, E>;
// We might imagine that a request fairing returns an `Outcome`. If it returns // We might imagine that a request fairing returns an `Outcome`. If it returns
// `Success`, we don't do any routing and use that response directly. Same if it // `Success`, we don't do any routing and use that response directly. Same if it
// returns `Failure`. We only route if it returns `Forward`. I've chosen not to // returns `Failure`. We only route if it returns `Forward`. I've chosen not to
@ -101,24 +104,23 @@ pub use self::info_kind::{Info, Kind};
/// ///
/// The four callback kinds are as follows: /// The four callback kinds are as follows:
/// ///
/// * **Launch (`on_launch`)** /// * **Ignite (`on_ignite`)**
/// ///
/// A launch callback, represented by the [`Fairing::on_launch()`] method, /// An ignite callback, represented by the [`Fairing::on_ignite()`] method,
/// is called just prior to liftoff, while launching the application. The /// is called just prior to liftoff, during ignition. The state of the
/// state of the `Rocket` instance is, at this point, not finalized, as it /// `Rocket` instance is, at this point, not finalized, as it may be
/// may be modified at will by other launch fairings. As a result, it is /// modified at will by other ignite fairings.
/// unwise to depend on the state of the `Rocket` instance.
/// ///
/// All launch callbacks are executed in breadth-first `attach()` order. A /// All ignite callbacks are executed in breadth-first `attach()` order. A
/// callback `B` executing after a callback `A` can view changes made by `A` /// callback `B` executing after a callback `A` can view changes made by `A`
/// but not vice-versa. /// but not vice-versa.
/// ///
/// A launch callback can arbitrarily modify the `Rocket` instance being /// An ignite callback can arbitrarily modify the `Rocket` instance being
/// constructed. It should take care not to introduce infinite recursion by /// constructed. It should take care not to introduce infinite recursion by
/// recursively attaching launch fairings. It returns `Ok` if it would like /// recursively attaching ignite fairings. It returns `Ok` if it would like
/// launching to proceed nominally and `Err` otherwise. If a launch fairing /// ignition and launch to proceed nominally and `Err` otherwise. If an
/// returns `Err`, launch will be aborted. All launch fairings are executed /// ignite fairing returns `Err`, launch will be aborted. All ignite
/// even if one or more signal a failure. /// fairings are executed even if one or more signal a failure.
/// ///
/// * **Liftoff (`on_liftoff`)** /// * **Liftoff (`on_liftoff`)**
/// ///
@ -159,7 +161,7 @@ pub use self::info_kind::{Info, Kind};
/// # Implementing /// # Implementing
/// ///
/// A `Fairing` implementation has one required method: [`info`]. A `Fairing` /// A `Fairing` implementation has one required method: [`info`]. A `Fairing`
/// can also implement any of the available callbacks: `on_launch`, `on_liftoff`, /// can also implement any of the available callbacks: `on_ignite`, `on_liftoff`,
/// `on_request`, and `on_response`. A `Fairing` _must_ set the appropriate /// `on_request`, and `on_response`. A `Fairing` _must_ set the appropriate
/// callback kind in the `kind` field of the returned `Info` structure from /// callback kind in the `kind` field of the returned `Info` structure from
/// [`info`] for a callback to actually be called by Rocket. /// [`info`] for a callback to actually be called by Rocket.
@ -200,8 +202,8 @@ pub use self::info_kind::{Info, Kind};
/// decorated with an attribute of `#[rocket::async_trait]`: /// decorated with an attribute of `#[rocket::async_trait]`:
/// ///
/// ```rust /// ```rust
/// use rocket::{Rocket, Request, Data, Response}; /// use rocket::{Rocket, Request, Data, Response, Build, Orbit};
/// use rocket::fairing::{Fairing, Info, Kind}; /// use rocket::fairing::{self, Fairing, Info, Kind};
/// ///
/// # struct MyType; /// # struct MyType;
/// #[rocket::async_trait] /// #[rocket::async_trait]
@ -211,12 +213,12 @@ pub use self::info_kind::{Info, Kind};
/// # unimplemented!() /// # unimplemented!()
/// } /// }
/// ///
/// async fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> { /// async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
/// /* ... */ /// /* ... */
/// # unimplemented!() /// # unimplemented!()
/// } /// }
/// ///
/// async fn on_liftoff(&self, rocket: &Rocket) { /// async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
/// /* ... */ /// /* ... */
/// # unimplemented!() /// # unimplemented!()
/// } /// }
@ -381,15 +383,15 @@ pub trait Fairing: Send + Sync + 'static {
/// ///
/// Rocket will only dispatch callbacks to this fairing for the kinds in the /// Rocket will only dispatch callbacks to this fairing for the kinds in the
/// `kind` field of the returned `Info` structure. For instance, if /// `kind` field of the returned `Info` structure. For instance, if
/// `Kind::Launch | Kind::Request` is used, then Rocket will only call the /// `Kind::Ignite | Kind::Request` is used, then Rocket will only call the
/// `on_launch` and `on_request` methods of the fairing. Similarly, if /// `on_ignite` and `on_request` methods of the fairing. Similarly, if
/// `Kind::Response` is used, Rocket will only call the `on_response` method /// `Kind::Response` is used, Rocket will only call the `on_response` method
/// of this fairing. /// of this fairing.
/// ///
/// # Example /// # Example
/// ///
/// An `info` implementation for `MyFairing`: a fairing named "My Custom /// An `info` implementation for `MyFairing`: a fairing named "My Custom
/// Fairing" that is both a launch and response fairing. /// Fairing" that is both an ignite and response fairing.
/// ///
/// ```rust /// ```rust
/// use rocket::fairing::{Fairing, Info, Kind}; /// use rocket::fairing::{Fairing, Info, Kind};
@ -400,25 +402,25 @@ pub trait Fairing: Send + Sync + 'static {
/// fn info(&self) -> Info { /// fn info(&self) -> Info {
/// Info { /// Info {
/// name: "My Custom Fairing", /// name: "My Custom Fairing",
/// kind: Kind::Launch | Kind::Response /// kind: Kind::Ignite | Kind::Response
/// } /// }
/// } /// }
/// } /// }
/// ``` /// ```
fn info(&self) -> Info; fn info(&self) -> Info;
/// The launch callback. Returns `Ok` if launch should proceed and `Err` if /// The ignite callback. Returns `Ok` if ignition should proceed and `Err`
/// launch should be aborted. /// if ignition and launch should be aborted.
/// ///
/// This method is called when the application is being launched if /// This method is called during ignition and if `Kind::Ignite` is in the
/// `Kind::Launch` is in the `kind` field of the `Info` structure for this /// `kind` field of the `Info` structure for this fairing. The `rocket`
/// fairing. The `rocket` parameter is the `Rocket` instance that is /// parameter is the `Rocket` instance that is currently being built for
/// currently being built for this application. /// this application.
/// ///
/// ## Default Implementation /// ## Default Implementation
/// ///
/// The default implementation of this method simply returns `Ok(rocket)`. /// The default implementation of this method simply returns `Ok(rocket)`.
async fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> { Ok(rocket) } async fn on_ignite(&self, rocket: Rocket<Build>) -> Result { Ok(rocket) }
/// The liftoff callback. /// The liftoff callback.
/// ///
@ -429,7 +431,7 @@ pub trait Fairing: Send + Sync + 'static {
/// ## Default Implementation /// ## Default Implementation
/// ///
/// The default implementation of this method does nothing. /// The default implementation of this method does nothing.
async fn on_liftoff(&self, _rocket: &Rocket) { } async fn on_liftoff(&self, _rocket: &Rocket<Orbit>) { }
/// The request callback. /// The request callback.
/// ///
@ -464,12 +466,12 @@ impl<T: Fairing> Fairing for std::sync::Arc<T> {
} }
#[inline] #[inline]
async fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> { async fn on_ignite(&self, rocket: Rocket<Build>) -> Result {
(self as &T).on_launch(rocket).await (self as &T).on_ignite(rocket).await
} }
#[inline] #[inline]
async fn on_liftoff(&self, rocket: &Rocket) { async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
(self as &T).on_liftoff(rocket).await (self as &T).on_liftoff(rocket).await
} }

View File

@ -159,19 +159,22 @@ mod phase;
#[doc(inline)] pub use crate::catcher::Catcher; #[doc(inline)] pub use crate::catcher::Catcher;
#[doc(inline)] pub use crate::route::Route; #[doc(inline)] pub use crate::route::Route;
#[doc(hidden)] pub use either::Either; #[doc(hidden)] pub use either::Either;
pub use crate::request::Request; #[doc(inline)] pub use phase::{Phase, Build, Ignite, Orbit};
#[doc(inline)] pub use error::Error;
pub use crate::rocket::Rocket; pub use crate::rocket::Rocket;
pub use crate::request::Request;
pub use crate::shutdown::Shutdown; pub use crate::shutdown::Shutdown;
pub use crate::state::State; pub use crate::state::State;
/// Creates a new instance of `Rocket`: aliases [`Rocket::build()`]. /// Creates a [`Rocket`] instance with the default config provider: aliases
pub fn build() -> Rocket { /// [`Rocket::build()`].
pub fn build() -> Rocket<Build> {
Rocket::build() Rocket::build()
} }
/// Creates a new instance of `Rocket` with a custom configuration provider: /// Creates a [`Rocket`] instance with a custom config provider: aliases
/// aliases [`Rocket::custom()`]. /// [`Rocket::custom()`].
pub fn custom<T: figment::Provider>(provider: T) -> Rocket { pub fn custom<T: figment::Provider>(provider: T) -> Rocket<Build> {
Rocket::custom(provider) Rocket::custom(provider)
} }

View File

@ -3,10 +3,9 @@ use std::convert::TryInto;
use parking_lot::RwLock; use parking_lot::RwLock;
use crate::{Rocket, Phase, Orbit, Error};
use crate::local::asynchronous::{LocalRequest, LocalResponse}; use crate::local::asynchronous::{LocalRequest, LocalResponse};
use crate::rocket::Rocket;
use crate::http::{Method, uri::Origin, private::cookie}; use crate::http::{Method, uri::Origin, private::cookie};
use crate::error::Error;
/// An `async` client to construct and dispatch local requests. /// An `async` client to construct and dispatch local requests.
/// ///
@ -49,17 +48,17 @@ use crate::error::Error;
/// # }); /// # });
/// ``` /// ```
pub struct Client { pub struct Client {
rocket: Rocket, rocket: Rocket<Orbit>,
cookies: RwLock<cookie::CookieJar>, cookies: RwLock<cookie::CookieJar>,
pub(in super) tracked: bool, pub(in super) tracked: bool,
} }
impl Client { impl Client {
pub(crate) async fn _new( pub(crate) async fn _new<P: Phase>(
rocket: Rocket, rocket: Rocket<P>,
tracked: bool tracked: bool
) -> Result<Client, Error> { ) -> Result<Client, Error> {
let rocket = rocket._ignite().await?; let rocket = rocket.local_launch().await?;
let cookies = RwLock::new(cookie::CookieJar::new()); let cookies = RwLock::new(cookie::CookieJar::new());
Ok(Client { rocket, tracked, cookies }) Ok(Client { rocket, tracked, cookies })
} }
@ -78,7 +77,7 @@ impl Client {
} }
#[inline(always)] #[inline(always)]
pub(crate) fn _rocket(&self) -> &Rocket { pub(crate) fn _rocket(&self) -> &Rocket<Orbit> {
&self.rocket &self.rocket
} }

View File

@ -30,7 +30,7 @@ use crate::{Request, Response};
/// #[launch] /// #[launch]
/// fn rocket() -> _ { /// fn rocket() -> _ {
/// rocket::build().mount("/", routes![hello_world]) /// rocket::build().mount("/", routes![hello_world])
/// # .reconfigure(rocket::Config::debug_default()) /// # .configure(rocket::Config::debug_default())
/// } /// }
/// ///
/// # async fn read_body_manually() -> io::Result<()> { /// # async fn read_body_manually() -> io::Result<()> {

View File

@ -2,8 +2,7 @@ use std::fmt;
use std::cell::RefCell; use std::cell::RefCell;
use std::convert::TryInto; use std::convert::TryInto;
use crate::Rocket; use crate::{Rocket, Phase, Orbit, Error};
use crate::error::Error;
use crate::local::{asynchronous, blocking::{LocalRequest, LocalResponse}}; use crate::local::{asynchronous, blocking::{LocalRequest, LocalResponse}};
use crate::http::{Method, uri::Origin}; use crate::http::{Method, uri::Origin};
@ -32,7 +31,7 @@ pub struct Client {
} }
impl Client { impl Client {
fn _new(rocket: Rocket, tracked: bool) -> Result<Client, Error> { fn _new<P: Phase>(rocket: Rocket<P>, tracked: bool) -> Result<Client, Error> {
let runtime = tokio::runtime::Builder::new_multi_thread() let runtime = tokio::runtime::Builder::new_multi_thread()
.thread_name("rocket-local-client-worker-thread") .thread_name("rocket-local-client-worker-thread")
.worker_threads(1) .worker_threads(1)
@ -69,7 +68,7 @@ impl Client {
} }
#[inline(always)] #[inline(always)]
fn _rocket(&self) -> &Rocket { fn _rocket(&self) -> &Rocket<Orbit> {
self.inner()._rocket() self.inner()._rocket()
} }

View File

@ -27,7 +27,7 @@ use super::Client;
/// #[launch] /// #[launch]
/// fn rocket() -> _ { /// fn rocket() -> _ {
/// rocket::build().mount("/", routes![hello_world]) /// rocket::build().mount("/", routes![hello_world])
/// # .reconfigure(rocket::Config::debug_default()) /// # .configure(rocket::Config::debug_default())
/// } /// }
/// ///
/// # fn read_body_manually() -> io::Result<()> { /// # fn read_body_manually() -> io::Result<()> {

View File

@ -67,7 +67,7 @@ macro_rules! pub_client_impl {
/// let client = Client::tracked(rocket); /// let client = Client::tracked(rocket);
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub $($prefix)? fn tracked(rocket: Rocket) -> Result<Self, Error> { pub $($prefix)? fn tracked<P: Phase>(rocket: Rocket<P>) -> Result<Self, Error> {
Self::_new(rocket, true) $(.$suffix)? Self::_new(rocket, true) $(.$suffix)?
} }
@ -91,7 +91,7 @@ macro_rules! pub_client_impl {
/// let rocket = rocket::build(); /// let rocket = rocket::build();
/// let client = Client::untracked(rocket); /// let client = Client::untracked(rocket);
/// ``` /// ```
pub $($prefix)? fn untracked(rocket: Rocket) -> Result<Self, Error> { pub $($prefix)? fn untracked<P: Phase>(rocket: Rocket<P>) -> Result<Self, Error> {
Self::_new(rocket, false) $(.$suffix)? Self::_new(rocket, false) $(.$suffix)?
} }
@ -102,13 +102,14 @@ macro_rules! pub_client_impl {
} }
#[doc(hidden)] #[doc(hidden)]
pub $($prefix)? fn debug(rocket: Rocket) -> Result<Self, Error> { pub $($prefix)? fn debug(rocket: Rocket<crate::Build>) -> Result<Self, Error> {
let mut config = rocket.config.clone(); use crate::config;
config.log_level = crate::config::LogLevel::Debug;
config.profile = crate::Config::DEBUG_PROFILE;
let config = rocket.figment().clone().merge(config); let figment = rocket.figment().clone()
Self::tracked(rocket.reconfigure(config)) $(.$suffix)? .merge((config::Config::LOG_LEVEL, config::LogLevel::Debug))
.select(config::Config::DEBUG_PROFILE);
Self::tracked(rocket.configure(figment)) $(.$suffix)?
} }
/// Deprecated alias to [`Client::tracked()`]. /// Deprecated alias to [`Client::tracked()`].
@ -116,7 +117,7 @@ macro_rules! pub_client_impl {
since = "0.5", since = "0.5",
note = "choose between `Client::untracked()` and `Client::tracked()`" note = "choose between `Client::untracked()` and `Client::tracked()`"
)] )]
pub $($prefix)? fn new(rocket: Rocket) -> Result<Self, Error> { pub $($prefix)? fn new<P: Phase>(rocket: Rocket<P>) -> Result<Self, Error> {
Self::tracked(rocket) $(.$suffix)? Self::tracked(rocket) $(.$suffix)?
} }
@ -134,7 +135,7 @@ macro_rules! pub_client_impl {
/// # }); /// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn rocket(&self) -> &Rocket { pub fn rocket(&self) -> &Rocket<Orbit> {
&*self._rocket() &*self._rocket()
} }
@ -157,7 +158,7 @@ macro_rules! pub_client_impl {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn cookies(&self) -> crate::http::CookieJar<'_> { pub fn cookies(&self) -> crate::http::CookieJar<'_> {
let config = &self.rocket().config; let config = &self.rocket().config();
let jar = self._with_raw_cookies(|jar| jar.clone()); let jar = self._with_raw_cookies(|jar| jar.clone());
crate::http::CookieJar::from(jar, config) crate::http::CookieJar::from(jar, config)
} }

View File

@ -41,7 +41,6 @@
//! "Hello, world!" //! "Hello, world!"
//! } //! }
//! //!
//! # /*
//! #[launch] //! #[launch]
//! fn rocket() -> _ { //! fn rocket() -> _ {
//! rocket::build().mount("/", routes![hello]) //! rocket::build().mount("/", routes![hello])

View File

@ -8,7 +8,7 @@ use yansi::Paint;
use serde::{de, Serialize, Serializer, Deserialize, Deserializer}; use serde::{de, Serialize, Serializer, Deserialize, Deserializer};
#[derive(Debug)] #[derive(Debug)]
struct RocketLogger(LogLevel); struct RocketLogger;
/// Defines the maximum level of log messages to show. /// Defines the maximum level of log messages to show.
#[derive(PartialEq, Eq, Debug, Clone, Copy)] #[derive(PartialEq, Eq, Debug, Clone, Copy)]
@ -102,7 +102,7 @@ macro_rules! warn_ { ($($args:expr),+) => { log_!(warn: $($args),+); }; }
impl log::Log for RocketLogger { impl log::Log for RocketLogger {
#[inline(always)] #[inline(always)]
fn enabled(&self, record: &log::Metadata<'_>) -> bool { fn enabled(&self, record: &log::Metadata<'_>) -> bool {
match self.0.to_level_filter().to_level() { match log::max_level().to_level() {
Some(max) => record.level() <= max || record.target().starts_with("launch"), Some(max) => record.level() <= max || record.target().starts_with("launch"),
None => false None => false
} }
@ -114,36 +114,35 @@ impl log::Log for RocketLogger {
return; return;
} }
// Don't print Hyper or Rustls or r2d2 messages unless debug is enabled. // Don't print Hyper, Rustls or r2d2 messages unless debug is enabled.
let configged_level = self.0; let max = log::max_level();
let from = |path| record.module_path().map_or(false, |m| m.starts_with(path)); let from = |path| record.module_path().map_or(false, |m| m.starts_with(path));
let debug_only = from("hyper") || from("rustls") || from("r2d2"); let debug_only = from("hyper") || from("rustls") || from("r2d2");
if configged_level != LogLevel::Debug && debug_only { if LogLevel::Debug.to_level_filter() > max && debug_only {
return; return;
} }
// In Rocket, we abuse targets with suffix "_" to indicate indentation. // In Rocket, we abuse targets with suffix "_" to indicate indentation.
let is_launch = record.target().starts_with("launch"); let indented = record.target().ends_with('_');
if record.target().ends_with('_') { if indented {
if configged_level != LogLevel::Critical || is_launch { print!(" {} ", Paint::default(">>").bold());
print!(" {} ", Paint::default("=>").bold());
}
} }
match record.level() { match record.level() {
log::Level::Info => println!("{}", Paint::blue(record.args()).wrap()), log::Level::Error if !indented => {
log::Level::Trace => println!("{}", Paint::magenta(record.args()).wrap()),
log::Level::Error => {
println!("{} {}", println!("{} {}",
Paint::red("Error:").bold(), Paint::red("Error:").bold(),
Paint::red(record.args()).wrap()) Paint::red(record.args()).wrap())
} }
log::Level::Warn => { log::Level::Warn if !indented => {
println!("{} {}", println!("{} {}",
Paint::yellow("Warning:").bold(), Paint::yellow("Warning:").bold(),
Paint::yellow(record.args()).wrap()) Paint::yellow(record.args()).wrap())
} }
log::Level::Info => println!("{}", Paint::blue(record.args()).wrap()),
log::Level::Trace => println!("{}", Paint::magenta(record.args()).wrap()),
log::Level::Warn => println!("{}", Paint::yellow(record.args()).wrap()),
log::Level::Error => println!("{}", Paint::red(record.args()).wrap()),
log::Level::Debug => { log::Level::Debug => {
print!("\n{} ", Paint::blue("-->").bold()); print!("\n{} ", Paint::blue("-->").bold());
if let Some(file) = record.file() { if let Some(file) = record.file() {
@ -154,7 +153,7 @@ impl log::Log for RocketLogger {
println!(":{}", Paint::blue(line)); println!(":{}", Paint::blue(line));
} }
println!("{}", record.args()); println!("\t{}", record.args());
} }
} }
} }
@ -165,10 +164,6 @@ impl log::Log for RocketLogger {
} }
pub(crate) fn init(config: &crate::Config) -> bool { pub(crate) fn init(config: &crate::Config) -> bool {
if config.log_level == LogLevel::Off {
return false;
}
if !atty::is(atty::Stream::Stdout) if !atty::is(atty::Stream::Stdout)
|| (cfg!(windows) && !Paint::enable_windows_ascii()) || (cfg!(windows) && !Paint::enable_windows_ascii())
|| !config.cli_colors || !config.cli_colors
@ -176,7 +171,7 @@ pub(crate) fn init(config: &crate::Config) -> bool {
Paint::disable(); Paint::disable();
} }
if let Err(e) = log::set_boxed_logger(Box::new(RocketLogger(config.log_level))) { if let Err(e) = log::set_boxed_logger(Box::new(RocketLogger)) {
if config.log_level == LogLevel::Debug { if config.log_level == LogLevel::Debug {
eprintln!("Logger failed to initialize: {}", e); eprintln!("Logger failed to initialize: {}", e);
} }

118
core/lib/src/phase.rs Normal file
View File

@ -0,0 +1,118 @@
use std::sync::Arc;
use state::Container;
use figment::Figment;
use tokio::sync::Notify;
use crate::{Route, Catcher, Config, Rocket};
use crate::router::Router;
use crate::fairing::Fairings;
mod private {
pub trait Sealed { }
}
#[doc(hidden)]
pub trait Stateful: private::Sealed {
fn into_state(self) -> State;
fn as_state_ref(&self) -> StateRef<'_>;
}
/// A marker trait for Rocket's launch phases.
///
/// This treat is implemented by the three phase marker types: [`Build`],
/// [`Ignite`], and [`Orbit`], representing the three phases to launch an
/// instance of [`Rocket`]. This trait is _sealed_ and cannot be implemented
/// outside of Rocket.
///
/// For a description of the three phases, see [`Rocket#phases`].
pub trait Phase: private::Sealed {
#[doc(hidden)]
type State: std::fmt::Debug + Stateful + Sync + Send + Unpin;
}
macro_rules! phase {
($(#[$o:meta])* $P:ident ($(#[$i:meta])* $S:ident) { $($fields:tt)* }) => (
$(#[$o])*
pub enum $P { }
impl Phase for $P {
#[doc(hidden)]
type State = $S;
}
$(#[$i])*
#[doc(hidden)]
pub struct $S {
$($fields)*
}
impl Stateful for $S {
fn into_state(self) -> State { State::$P(self) }
fn as_state_ref(&self) -> StateRef<'_> { StateRef::$P(self) }
}
#[doc(hidden)]
impl From<$S> for Rocket<$P> {
fn from(s: $S) -> Self { Rocket(s) }
}
impl private::Sealed for $P {}
impl private::Sealed for $S {}
)
}
macro_rules! phases {
($($(#[$o:meta])* $P:ident ($(#[$i:meta])* $S:ident) { $($fields:tt)* })*) => (
#[doc(hidden)]
pub enum State { $($P($S)),* }
#[doc(hidden)]
pub enum StateRef<'a> { $($P(&'a $S)),* }
$(phase!($(#[$o])* $P ($(#[$i])* $S) { $($fields)* });)*
)
}
phases! {
/// The initial launch [`Phase`].
///
/// An instance of `Rocket` in this phase is typed as [`Rocket<Build>`] and
/// represents a transient, in-progress build. See [`Rocket#build`] for
/// details.
Build (#[derive(Default, Debug)] Building) {
pub(crate) routes: Vec<Route>,
pub(crate) catchers: Vec<Catcher>,
pub(crate) fairings: Fairings,
pub(crate) figment: Figment,
pub(crate) state: Container![Send + Sync],
}
/// The second launch [`Phase`]: post-build but pre-orbit.
///
/// An instance of `Rocket` in this phase is typed as [`Rocket<Ignite>`] and
/// represents a fully built and finalized application server ready for
/// launch into orbit. See [`Rocket#ignite`] for full details.
Ignite (#[derive(Debug)] Igniting) {
pub(crate) router: Router,
pub(crate) fairings: Fairings,
pub(crate) figment: Figment,
pub(crate) config: Config,
pub(crate) state: Container![Send + Sync],
pub(crate) shutdown: Arc<Notify>,
}
/// The final launch [`Phase`].
///
/// An instance of `Rocket` in this phase is typed as [`Rocket<Orbit>`] and
/// represents a running application. See [`Rocket#orbit`] for full details.
Orbit (#[derive(Debug)] Orbiting) {
pub(crate) router: Router,
pub(crate) fairings: Fairings,
pub(crate) figment: Figment,
pub(crate) config: Config,
pub(crate) state: Container![Send + Sync],
pub(crate) shutdown: Arc<Notify>,
}
}

View File

@ -13,7 +13,7 @@ use atomic::{Atomic, Ordering};
use crate::request::{FromParam, FromSegments, FromRequest, Outcome}; use crate::request::{FromParam, FromSegments, FromRequest, Outcome};
use crate::form::{self, ValueField, FromForm}; use crate::form::{self, ValueField, FromForm};
use crate::{Rocket, Config, Route}; use crate::{Rocket, Route, Orbit};
use crate::http::{hyper, uri::{Origin, Segments}, uncased::UncasedStr}; use crate::http::{hyper, uri::{Origin, Segments}, uncased::UncasedStr};
use crate::http::{Method, Header, HeaderMap}; use crate::http::{Method, Header, HeaderMap};
use crate::http::{ContentType, Accept, MediaType, CookieJar, Cookie}; use crate::http::{ContentType, Accept, MediaType, CookieJar, Cookie};
@ -34,7 +34,7 @@ pub struct Request<'r> {
} }
pub(crate) struct RequestState<'r> { pub(crate) struct RequestState<'r> {
pub rocket: &'r Rocket, pub rocket: &'r Rocket<Orbit>,
pub route: Atomic<Option<&'r Route>>, pub route: Atomic<Option<&'r Route>>,
pub cookies: CookieJar<'r>, pub cookies: CookieJar<'r>,
pub accept: Storage<Option<Accept>>, pub accept: Storage<Option<Accept>>,
@ -71,7 +71,7 @@ impl<'r> Request<'r> {
/// Create a new `Request` with the given `method` and `uri`. /// Create a new `Request` with the given `method` and `uri`.
#[inline(always)] #[inline(always)]
pub(crate) fn new<'s: 'r>( pub(crate) fn new<'s: 'r>(
rocket: &'r Rocket, rocket: &'r Rocket<Orbit>,
method: Method, method: Method,
uri: Origin<'s> uri: Origin<'s>
) -> Request<'r> { ) -> Request<'r> {
@ -83,7 +83,7 @@ impl<'r> Request<'r> {
state: RequestState { state: RequestState {
rocket, rocket,
route: Atomic::new(None), route: Atomic::new(None),
cookies: CookieJar::new(&rocket.config), cookies: CookieJar::new(rocket.config()),
accept: Storage::new(), accept: Storage::new(),
content_type: Storage::new(), content_type: Storage::new(),
cache: Arc::new(<Container![Send + Sync]>::new()), cache: Arc::new(<Container![Send + Sync]>::new()),
@ -109,19 +109,20 @@ impl<'r> Request<'r> {
self.method.load(Ordering::Acquire) self.method.load(Ordering::Acquire)
} }
/// Set the method of `self`. /// Set the method of `self` to `method`.
/// ///
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use rocket::http::Method; /// use rocket::http::Method;
/// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
/// # let mut req = c.get("/");
/// # let request = req.inner_mut();
/// ///
/// # rocket::Request::example(Method::Get, "/", |request| {
/// assert_eq!(request.method(), Method::Get); /// assert_eq!(request.method(), Method::Get);
/// ///
/// request.set_method(Method::Post); /// request.set_method(Method::Post);
/// assert_eq!(request.method(), Method::Post); /// assert_eq!(request.method(), Method::Post);
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_method(&mut self, method: Method) { pub fn set_method(&mut self, method: Method) {
@ -149,10 +150,10 @@ impl<'r> Request<'r> {
/// ///
/// ```rust /// ```rust
/// use rocket::http::uri::Origin; /// use rocket::http::uri::Origin;
/// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
/// # let mut req = c.get("/");
/// # let request = req.inner_mut();
/// ///
/// # use rocket::Request;
/// # use rocket::http::Method;
/// # Request::example(Method::Get, "/uri", |mut request| {
/// let uri = Origin::parse("/hello/Sergio?type=greeting").unwrap(); /// let uri = Origin::parse("/hello/Sergio?type=greeting").unwrap();
/// request.set_uri(uri); /// request.set_uri(uri);
/// assert_eq!(request.uri().path(), "/hello/Sergio"); /// assert_eq!(request.uri().path(), "/hello/Sergio");
@ -162,7 +163,6 @@ impl<'r> Request<'r> {
/// request.set_uri(new_uri); /// request.set_uri(new_uri);
/// assert_eq!(request.uri().path(), "/foo/hello/Sergio"); /// assert_eq!(request.uri().path(), "/foo/hello/Sergio");
/// assert_eq!(request.uri().query().unwrap(), "type=greeting"); /// assert_eq!(request.uri().query().unwrap(), "type=greeting");
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_uri(&mut self, uri: Origin<'r>) { pub fn set_uri(&mut self, uri: Origin<'r>) {
@ -186,14 +186,15 @@ impl<'r> Request<'r> {
/// ///
/// ```rust /// ```rust
/// use std::net::{SocketAddrV4, Ipv4Addr}; /// use std::net::{SocketAddrV4, Ipv4Addr};
/// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
/// # let mut req = c.get("/");
/// # let request = req.inner_mut();
/// ///
/// # rocket::Request::example(rocket::http::Method::Get, "/", |mut request| {
/// assert_eq!(request.remote(), None); /// assert_eq!(request.remote(), None);
/// ///
/// let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8000).into(); /// let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8000).into();
/// request.set_remote(localhost); /// request.set_remote(localhost);
/// assert_eq!(request.remote(), Some(localhost)); /// assert_eq!(request.remote(), Some(localhost));
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn remote(&self) -> Option<SocketAddr> { pub fn remote(&self) -> Option<SocketAddr> {
@ -208,14 +209,15 @@ impl<'r> Request<'r> {
/// ///
/// ```rust /// ```rust
/// use std::net::{SocketAddrV4, Ipv4Addr}; /// use std::net::{SocketAddrV4, Ipv4Addr};
/// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
/// # let mut req = c.get("/");
/// # let request = req.inner_mut();
/// ///
/// # rocket::Request::example(rocket::http::Method::Get, "/", |mut request| {
/// assert_eq!(request.remote(), None); /// assert_eq!(request.remote(), None);
/// ///
/// let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8000).into(); /// let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8000).into();
/// request.set_remote(localhost); /// request.set_remote(localhost);
/// assert_eq!(request.remote(), Some(localhost)); /// assert_eq!(request.remote(), Some(localhost));
/// # });
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_remote(&mut self, address: SocketAddr) { pub fn set_remote(&mut self, address: SocketAddr) {
@ -258,11 +260,12 @@ impl<'r> Request<'r> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # use rocket::Request; /// # use rocket::http::Header;
/// # use rocket::http::{Header, Method};
/// # use std::net::{SocketAddr, IpAddr, Ipv4Addr}; /// # use std::net::{SocketAddr, IpAddr, Ipv4Addr};
/// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
/// # let mut req = c.get("/");
/// # let request = req.inner_mut();
/// ///
/// # Request::example(Method::Get, "/uri", |mut request| {
/// // starting without an "X-Real-IP" header or remote addresss /// // starting without an "X-Real-IP" header or remote addresss
/// assert!(request.client_ip().is_none()); /// assert!(request.client_ip().is_none());
/// ///
@ -273,7 +276,6 @@ impl<'r> Request<'r> {
/// // now with an X-Real-IP header /// // now with an X-Real-IP header
/// request.add_header(Header::new("X-Real-IP", "8.8.8.8")); /// request.add_header(Header::new("X-Real-IP", "8.8.8.8"));
/// assert_eq!(request.client_ip(), Some("8.8.8.8".parse().unwrap())); /// assert_eq!(request.client_ip(), Some("8.8.8.8".parse().unwrap()));
/// # });
/// ``` /// ```
#[inline] #[inline]
pub fn client_ip(&self) -> Option<IpAddr> { pub fn client_ip(&self) -> Option<IpAddr> {
@ -333,14 +335,15 @@ impl<'r> Request<'r> {
/// ///
/// ```rust /// ```rust
/// use rocket::http::ContentType; /// use rocket::http::ContentType;
/// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
/// # let mut req = c.get("/");
/// # let request = req.inner_mut();
/// ///
/// # rocket::Request::example(rocket::http::Method::Get, "/uri", |mut request| {
/// assert!(request.headers().is_empty()); /// assert!(request.headers().is_empty());
/// ///
/// request.add_header(ContentType::HTML); /// request.add_header(ContentType::HTML);
/// assert!(request.headers().contains("Content-Type")); /// assert!(request.headers().contains("Content-Type"));
/// assert_eq!(request.headers().len(), 1); /// assert_eq!(request.headers().len(), 1);
/// # });
/// ``` /// ```
#[inline] #[inline]
pub fn add_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) { pub fn add_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) {
@ -357,8 +360,10 @@ impl<'r> Request<'r> {
/// ///
/// ```rust /// ```rust
/// use rocket::http::ContentType; /// use rocket::http::ContentType;
/// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
/// # let mut req = c.get("/");
/// # let request = req.inner_mut();
/// ///
/// # rocket::Request::example(rocket::http::Method::Get, "/uri", |mut request| {
/// assert!(request.headers().is_empty()); /// assert!(request.headers().is_empty());
/// ///
/// request.add_header(ContentType::Any); /// request.add_header(ContentType::Any);
@ -368,7 +373,6 @@ impl<'r> Request<'r> {
/// request.replace_header(ContentType::PNG); /// request.replace_header(ContentType::PNG);
/// assert_eq!(request.headers().get_one("Content-Type"), Some("image/png")); /// assert_eq!(request.headers().get_one("Content-Type"), Some("image/png"));
/// assert_eq!(request.content_type(), Some(&ContentType::PNG)); /// assert_eq!(request.content_type(), Some(&ContentType::PNG));
/// # });
/// ``` /// ```
#[inline] #[inline]
pub fn replace_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) { pub fn replace_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) {
@ -487,7 +491,7 @@ impl<'r> Request<'r> {
/// let catchers = request.rocket().catchers(); /// let catchers = request.rocket().catchers();
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn rocket(&self) -> &'r Rocket { pub fn rocket(&self) -> &'r Rocket<Orbit> {
&self.state.rocket &self.state.rocket
} }
@ -779,14 +783,6 @@ impl<'r> Request<'r> {
} }
} }
// Only used by doc-tests! Needs to be `pub` because doc-test are external.
pub fn example<F: Fn(&mut Request<'_>)>(method: Method, uri: &str, f: F) {
let rocket = Rocket::custom(Config::default());
let uri = Origin::parse(uri).expect("invalid URI in example");
let mut request = Request::new(&rocket, method, uri);
f(&mut request);
}
/// Get the `n`th path segment, 0-indexed, after the mount point for the /// Get the `n`th path segment, 0-indexed, after the mount point for the
/// currently matched route, as a string, if it exists. Used by codegen. /// currently matched route, as a string, if it exists. Used by codegen.
#[inline] #[inline]
@ -831,7 +827,7 @@ impl<'r> Request<'r> {
/// Convert from Hyper types into a Rocket Request. /// Convert from Hyper types into a Rocket Request.
pub(crate) fn from_hyp( pub(crate) fn from_hyp(
rocket: &'r Rocket, rocket: &'r Rocket<Orbit>,
h_method: hyper::Method, h_method: hyper::Method,
h_headers: hyper::HeaderMap<hyper::HeaderValue>, h_headers: hyper::HeaderMap<hyper::HeaderValue>,
h_uri: &'r hyper::Uri, h_uri: &'r hyper::Uri,
@ -839,7 +835,7 @@ impl<'r> Request<'r> {
) -> Result<Request<'r>, Error<'r>> { ) -> Result<Request<'r>, Error<'r>> {
// Get a copy of the URI (only supports path-and-query) for later use. // Get a copy of the URI (only supports path-and-query) for later use.
let uri = match (h_uri.scheme(), h_uri.authority(), h_uri.path_and_query()) { let uri = match (h_uri.scheme(), h_uri.authority(), h_uri.path_and_query()) {
(None, None, Some(paq)) => paq.as_str(), (None, None, Some(path_query)) => path_query.as_str(),
_ => return Err(Error::InvalidUri(h_uri)), _ => return Err(Error::InvalidUri(h_uri)),
}; };

View File

@ -1,7 +1,8 @@
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::collections::HashMap; use std::collections::HashMap;
use crate::{Rocket, Request, Config}; use crate::Request;
use crate::local::blocking::Client;
use crate::http::hyper; use crate::http::hyper;
macro_rules! assert_headers { macro_rules! assert_headers {
@ -20,8 +21,9 @@ macro_rules! assert_headers {
$(expected.entry($key).or_insert(vec![]).append(&mut vec![$($value),+]);)+ $(expected.entry($key).or_insert(vec![]).append(&mut vec![$($value),+]);)+
// Dispatch the request and check that the headers are what we expect. // Dispatch the request and check that the headers are what we expect.
let r = Rocket::custom(Config::default()); let client = Client::debug_with(vec![]).unwrap();
let req = Request::from_hyp(&r, h_method, h_headers, &h_uri, h_addr).unwrap(); let r = client.rocket();
let req = Request::from_hyp(r, h_method, h_headers, &h_uri, h_addr).unwrap();
let actual_headers = req.headers(); let actual_headers = req.headers();
for (key, values) in expected.iter() { for (key, values) in expected.iter() {
let actual: Vec<_> = actual_headers.get(key).collect(); let actual: Vec<_> = actual_headers.get(key).collect();

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ use futures::future::{self, FutureExt, Future, TryFutureExt, BoxFuture};
use tokio::sync::oneshot; use tokio::sync::oneshot;
use yansi::Paint; use yansi::Paint;
use crate::{Rocket, Request, Data, route}; use crate::{Rocket, Orbit, Request, Data, route};
use crate::form::Form; use crate::form::Form;
use crate::response::{Response, Body}; use crate::response::{Response, Body};
use crate::outcome::Outcome; use crate::outcome::Outcome;
@ -62,7 +62,7 @@ async fn handle<Fut, T, F>(name: Option<&str>, run: F) -> Option<T>
// which knows nothing about Hyper. Because responding depends on the // which knows nothing about Hyper. Because responding depends on the
// `HyperResponse` type, this function does the actual response processing. // `HyperResponse` type, this function does the actual response processing.
async fn hyper_service_fn( async fn hyper_service_fn(
rocket: Arc<Rocket>, rocket: Arc<Rocket<Orbit>>,
h_addr: std::net::SocketAddr, h_addr: std::net::SocketAddr,
hyp_req: hyper::Request<hyper::Body>, hyp_req: hyper::Request<hyper::Body>,
) -> Result<hyper::Response<hyper::Body>, io::Error> { ) -> Result<hyper::Response<hyper::Body>, io::Error> {
@ -107,7 +107,7 @@ async fn hyper_service_fn(
rx.await.map_err(|e| io::Error::new(io::ErrorKind::Other, e)) rx.await.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
} }
impl Rocket { impl Rocket<Orbit> {
/// Wrapper around `make_response` to log a success or failure. /// Wrapper around `make_response` to log a success or failure.
#[inline] #[inline]
async fn send_response( async fn send_response(
@ -370,7 +370,7 @@ impl Rocket {
crate::catcher::default_handler(Status::InternalServerError, req) crate::catcher::default_handler(Status::InternalServerError, req)
} }
pub async fn default_tcp_http_server<C>(mut self, ready: C) -> Result<(), Error> pub(crate) async fn default_tcp_http_server<C>(mut self, ready: C) -> Result<(), Error>
where C: for<'a> Fn(&'a Self) -> BoxFuture<'a, ()> where C: for<'a> Fn(&'a Self) -> BoxFuture<'a, ()>
{ {
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
@ -403,7 +403,7 @@ impl Rocket {
} }
// TODO.async: Solidify the Listener APIs and make this function public // TODO.async: Solidify the Listener APIs and make this function public
pub async fn http_server<L>(mut self, listener: L) -> Result<(), Error> pub(crate) async fn http_server<L>(self, listener: L) -> Result<(), Error>
where L: Listener + Send + Unpin + 'static, where L: Listener + Send + Unpin + 'static,
<L as Listener>::Connection: Send + Unpin + 'static <L as Listener>::Connection: Send + Unpin + 'static
{ {
@ -415,16 +415,12 @@ impl Rocket {
}; };
// Get the shutdown handle (to initiate) and signal (when initiated). // Get the shutdown handle (to initiate) and signal (when initiated).
let shutdown_handle = self.shutdown_handle.clone(); let shutdown_handle = self.shutdown.clone();
let shutdown_signal = match self.config.ctrlc { let shutdown_signal = match self.config.ctrlc {
true => tokio::signal::ctrl_c().boxed(), true => tokio::signal::ctrl_c().boxed(),
false => future::pending().boxed(), false => future::pending().boxed(),
}; };
// We need to get this before moving `self` into an `Arc`.
let mut shutdown_receiver = self.shutdown_receiver.take()
.expect("shutdown receiver has already been used");
let rocket = Arc::new(self); let rocket = Arc::new(self);
let service = hyper::make_service_fn(move |conn: &<L as Listener>::Connection| { let service = hyper::make_service_fn(move |conn: &<L as Listener>::Connection| {
let rocket = rocket.clone(); let rocket = rocket.clone();
@ -437,11 +433,12 @@ impl Rocket {
}); });
// NOTE: `hyper` uses `tokio::spawn()` as the default executor. // NOTE: `hyper` uses `tokio::spawn()` as the default executor.
let shutdown_receiver = shutdown_handle.clone();
let server = hyper::Server::builder(Incoming::from_listener(listener)) let server = hyper::Server::builder(Incoming::from_listener(listener))
.http1_keepalive(http1_keepalive) .http1_keepalive(http1_keepalive)
.http2_keep_alive_interval(http2_keep_alive) .http2_keep_alive_interval(http2_keep_alive)
.serve(service) .serve(service)
.with_graceful_shutdown(async move { shutdown_receiver.recv().await; }) .with_graceful_shutdown(async move { shutdown_receiver.notified().await; })
.map_err(|e| Error::new(ErrorKind::Runtime(Box::new(e)))); .map_err(|e| Error::new(ErrorKind::Runtime(Box::new(e))));
tokio::pin!(server); tokio::pin!(server);
@ -450,7 +447,7 @@ impl Rocket {
match selecter.await { match selecter.await {
future::Either::Left((Ok(()), server)) => { future::Either::Left((Ok(()), server)) => {
// Ctrl-was pressed. Signal shutdown, wait for the server. // Ctrl-was pressed. Signal shutdown, wait for the server.
shutdown_handle.shutdown(); shutdown_handle.notify_one();
server.await server.await
} }
future::Either::Left((Err(err), server)) => { future::Either::Left((Err(err), server)) => {

View File

@ -1,9 +1,12 @@
use std::sync::Arc;
use tokio::sync::Notify;
use crate::request::{FromRequest, Outcome, Request}; use crate::request::{FromRequest, Outcome, Request};
use tokio::sync::mpsc;
/// A request guard to gracefully shutdown a Rocket server. /// A request guard to gracefully shutdown a Rocket server.
/// ///
/// A server shutdown is manually requested by calling [`Shutdown::shutdown()`] /// A server shutdown is manually requested by calling [`Shutdown::notify()`]
/// or, if enabled, by pressing `Ctrl-C`. Rocket will finish handling any /// or, if enabled, by pressing `Ctrl-C`. Rocket will finish handling any
/// pending requests and return `Ok()` to the caller of [`Rocket::launch()`]. /// pending requests and return `Ok()` to the caller of [`Rocket::launch()`].
/// ///
@ -17,8 +20,8 @@ use tokio::sync::mpsc;
/// use rocket::Shutdown; /// use rocket::Shutdown;
/// ///
/// #[get("/shutdown")] /// #[get("/shutdown")]
/// fn shutdown(handle: Shutdown) -> &'static str { /// fn shutdown(shutdown: Shutdown) -> &'static str {
/// handle.shutdown(); /// shutdown.notify();
/// "Shutting down..." /// "Shutting down..."
/// } /// }
/// ///
@ -33,18 +36,18 @@ use tokio::sync::mpsc;
/// result.expect("server failed unexpectedly"); /// result.expect("server failed unexpectedly");
/// } /// }
/// ``` /// ```
#[must_use = "a shutdown request is only sent on `shutdown.notify()`"]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Shutdown(pub(crate) mpsc::Sender<()>); pub struct Shutdown(pub(crate) Arc<Notify>);
impl Shutdown { impl Shutdown {
/// Notify Rocket to shut down gracefully. This function returns /// Notify Rocket to shut down gracefully.
/// immediately; pending requests will continue to run until completion ///
/// before the actual shutdown occurs. /// This function returns immediately; pending requests will continue to run
/// until completion before the actual shutdown occurs.
#[inline] #[inline]
pub fn shutdown(self) { pub fn notify(self) {
// Intentionally ignore any error, as the only scenarios this can happen self.0.notify_one();
// is sending too many shutdown requests or we're already shut down.
let _ = self.0.try_send(());
info!("Server shutdown requested, waiting for all pending requests to finish."); info!("Server shutdown requested, waiting for all pending requests to finish.");
} }
} }
@ -55,6 +58,7 @@ impl<'r> FromRequest<'r> for Shutdown {
#[inline] #[inline]
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
Outcome::Success(request.state.rocket.shutdown()) let notifier = request.rocket().shutdown.clone();
Outcome::Success(Shutdown(notifier))
} }
} }

View File

@ -1,6 +1,6 @@
use std::ops::Deref; use std::ops::Deref;
use crate::rocket::Rocket; use crate::{Rocket, Phase};
use crate::request::{self, FromRequest, Request}; use crate::request::{self, FromRequest, Request};
use crate::outcome::Outcome; use crate::outcome::Outcome;
use crate::http::Status; use crate::http::Status;
@ -62,7 +62,7 @@ use crate::http::Status;
/// use rocket::request::{self, Request, FromRequest}; /// use rocket::request::{self, Request, FromRequest};
/// use rocket::outcome::IntoOutcome; /// use rocket::outcome::IntoOutcome;
/// ///
/// # struct MyConfig{ user_val: String }; /// # struct MyConfig { user_val: String };
/// struct Item<'r>(&'r str); /// struct Item<'r>(&'r str);
/// ///
/// #[rocket::async_trait] /// #[rocket::async_trait]
@ -163,7 +163,7 @@ impl<'r, T: Send + Sync + 'static> State<'r, T> {
/// assert_eq!(state, None); /// assert_eq!(state, None);
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn from(rocket: &'r Rocket) -> Option<Self> { pub fn from<P: Phase>(rocket: &'r Rocket<P>) -> Option<Self> {
rocket.state().map(State) rocket.state().map(State)
} }
} }

View File

@ -1,13 +1,12 @@
use rocket::error::Error;
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
#[rocket::async_test] #[rocket::async_test]
async fn test_inspectable_launch_state() -> Result<(), Error> { async fn test_inspectable_launch_state() -> Result<(), rocket::Error> {
let rocket = rocket::custom(rocket::Config::debug_default()) let rocket = rocket::custom(rocket::Config::debug_default())
.attach(AdHoc::on_launch("Add State", |rocket| async { .attach(AdHoc::on_ignite("Add State", |rocket| async {
rocket.manage("Hi!") rocket.manage("Hi!")
})) }))
._ignite() .ignite()
.await?; .await?;
let state = rocket.state::<&'static str>(); let state = rocket.state::<&'static str>();
@ -16,12 +15,12 @@ async fn test_inspectable_launch_state() -> Result<(), Error> {
} }
#[rocket::async_test] #[rocket::async_test]
async fn test_inspectable_launch_state_in_liftoff() -> Result<(), Error> { async fn test_inspectable_launch_state_in_liftoff() -> Result<(), rocket::Error> {
let rocket = rocket::custom(rocket::Config::debug_default()) let rocket = rocket::custom(rocket::Config::debug_default())
.attach(AdHoc::on_launch("Add State", |rocket| async { .attach(AdHoc::on_ignite("Add State", |rocket| async {
rocket.manage("Hi!") rocket.manage("Hi!")
})) }))
.attach(AdHoc::on_launch("Inspect State", |rocket| async { .attach(AdHoc::on_ignite("Inspect State", |rocket| async {
let state = rocket.state::<&'static str>(); let state = rocket.state::<&'static str>();
assert_eq!(state, Some(&"Hi!")); assert_eq!(state, Some(&"Hi!"));
rocket rocket
@ -30,7 +29,7 @@ async fn test_inspectable_launch_state_in_liftoff() -> Result<(), Error> {
let state = rocket.state::<&'static str>(); let state = rocket.state::<&'static str>();
assert_eq!(state, Some(&"Hi!")); assert_eq!(state, Some(&"Hi!"));
}))) })))
._ignite() .ignite()
.await?; .await?;
let state = rocket.state::<&'static str>(); let state = rocket.state::<&'static str>();
@ -39,22 +38,22 @@ async fn test_inspectable_launch_state_in_liftoff() -> Result<(), Error> {
} }
#[rocket::async_test] #[rocket::async_test]
async fn test_launch_state_is_well_ordered() -> Result<(), Error> { async fn test_launch_state_is_well_ordered() -> Result<(), rocket::Error> {
let rocket = rocket::custom(rocket::Config::debug_default()) let rocket = rocket::custom(rocket::Config::debug_default())
.attach(AdHoc::on_launch("Inspect State Pre", |rocket| async { .attach(AdHoc::on_ignite("Inspect State Pre", |rocket| async {
let state = rocket.state::<&'static str>(); let state = rocket.state::<&'static str>();
assert_eq!(state, None); assert_eq!(state, None);
rocket rocket
})) }))
.attach(AdHoc::on_launch("Add State", |rocket| async { .attach(AdHoc::on_ignite("Add State", |rocket| async {
rocket.manage("Hi!") rocket.manage("Hi!")
})) }))
.attach(AdHoc::on_launch("Inspect State", |rocket| async { .attach(AdHoc::on_ignite("Inspect State", |rocket| async {
let state = rocket.state::<&'static str>(); let state = rocket.state::<&'static str>();
assert_eq!(state, Some(&"Hi!")); assert_eq!(state, Some(&"Hi!"));
rocket rocket
})) }))
._ignite() .ignite()
.await?; .await?;
let state = rocket.state::<&'static str>(); let state = rocket.state::<&'static str>();
@ -66,14 +65,14 @@ async fn test_launch_state_is_well_ordered() -> Result<(), Error> {
#[rocket::async_test] #[rocket::async_test]
async fn negative_test_launch_state() { async fn negative_test_launch_state() {
let _ = rocket::custom(rocket::Config::debug_default()) let _ = rocket::custom(rocket::Config::debug_default())
.attach(AdHoc::on_launch("Add State", |rocket| async { .attach(AdHoc::on_ignite("Add State", |rocket| async {
rocket.manage("Hi!") rocket.manage("Hi!")
})) }))
.attach(AdHoc::on_launch("Inspect State", |rocket| async { .attach(AdHoc::on_ignite("Inspect State", |rocket| async {
let state = rocket.state::<&'static str>(); let state = rocket.state::<&'static str>();
assert_ne!(state, Some(&"Hi!")); assert_ne!(state, Some(&"Hi!"));
rocket rocket
})) }))
._ignite() .ignite()
.await; .await;
} }

View File

@ -8,12 +8,12 @@ fn index(form: Form<String>) -> String {
} }
mod limits_tests { mod limits_tests {
use rocket; use rocket::{Rocket, Build};
use rocket::local::blocking::Client; use rocket::local::blocking::Client;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
use rocket::data::Limits; use rocket::data::Limits;
fn rocket_with_forms_limit(limit: u64) -> rocket::Rocket { fn rocket_with_forms_limit(limit: u64) -> Rocket<Build> {
let mut config = rocket::Config::debug_default(); let mut config = rocket::Config::debug_default();
config.limits = Limits::default().limit("form", limit.into()); config.limits = Limits::default().limit("form", limit.into());
rocket::custom(config).mount("/", routes![super::index]) rocket::custom(config).mount("/", routes![super::index])

View File

@ -44,11 +44,11 @@ fn data_no_ct() -> &'static str {
mod local_request_content_type_tests { mod local_request_content_type_tests {
use super::*; use super::*;
use rocket::Rocket; use rocket::{Rocket, Build};
use rocket::local::blocking::Client; use rocket::local::blocking::Client;
use rocket::http::ContentType; use rocket::http::ContentType;
fn rocket() -> Rocket { fn rocket() -> Rocket<Build> {
rocket::build().mount("/", routes![rg_ct, data_has_ct, data_no_ct]) rocket::build().mount("/", routes![rg_ct, data_has_ct, data_no_ct])
} }

View File

@ -20,9 +20,10 @@ fn multi_get(jar_a: &CookieJar<'_>, jar_b: &CookieJar<'_>, jar_c: &CookieJar<'_>
#[cfg(test)] #[cfg(test)]
mod many_cookie_jars_tests { mod many_cookie_jars_tests {
use super::*; use super::*;
use rocket::{Rocket, Build};
use rocket::local::blocking::Client; use rocket::local::blocking::Client;
fn rocket() -> rocket::Rocket { fn rocket() -> Rocket<Build> {
rocket::build().mount("/", routes![multi_add, multi_get]) rocket::build().mount("/", routes![multi_add, multi_get])
} }

View File

@ -1,5 +1,5 @@
#[macro_use] extern crate rocket; #[macro_use] extern crate rocket;
use rocket::Route; use rocket::{Rocket, Route, Build};
pub fn prepend(prefix: &str, route: Route) -> Route { pub fn prepend(prefix: &str, route: Route) -> Route {
route.map_base(|base| format!("{}{}", prefix, base)).unwrap() route.map_base(|base| format!("{}{}", prefix, base)).unwrap()
@ -20,7 +20,7 @@ mod a {
} }
} }
fn rocket() -> rocket::Rocket { fn rocket() -> Rocket<Build> {
rocket::build().mount("/", a::routes()).mount("/foo", a::routes()) rocket::build().mount("/", a::routes()).mount("/foo", a::routes())
} }

View File

@ -2,7 +2,7 @@
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use rocket::State; use rocket::{Rocket, State, Build};
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use rocket::http::Method; use rocket::http::Method;
@ -19,10 +19,10 @@ fn index(counter: State<'_, Counter>) -> String {
format!("{}, {}", attaches, gets) format!("{}, {}", attaches, gets)
} }
fn rocket() -> rocket::Rocket { fn rocket() -> Rocket<Build> {
rocket::build() rocket::build()
.mount("/", routes![index]) .mount("/", routes![index])
.attach(AdHoc::on_launch("Outer", |rocket| async { .attach(AdHoc::on_ignite("Outer", |rocket| async {
let counter = Counter::default(); let counter = Counter::default();
counter.attach.fetch_add(1, Ordering::Relaxed); counter.attach.fetch_add(1, Ordering::Relaxed);
let rocket = rocket.manage(counter) let rocket = rocket.manage(counter)

View File

@ -3,7 +3,7 @@ use rocket::fairing::AdHoc;
use rocket::futures::channel::oneshot; use rocket::futures::channel::oneshot;
#[rocket::async_test] #[rocket::async_test]
async fn on_launch_fairing_can_inspect_port() { async fn on_ignite_fairing_can_inspect_port() {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let rocket = rocket::custom(Config { port: 0, ..Config::debug_default() }) let rocket = rocket::custom(Config { port: 0, ..Config::debug_default() })
.attach(AdHoc::on_liftoff("Send Port -> Channel", move |rocket| { .attach(AdHoc::on_liftoff("Send Port -> Channel", move |rocket| {

View File

@ -1,6 +1,6 @@
#[macro_use] extern crate rocket; #[macro_use] extern crate rocket;
use rocket::{Request, Rocket, Route, Catcher, route, catcher}; use rocket::{Request, Rocket, Route, Catcher, Build, route, catcher};
use rocket::data::Data; use rocket::data::Data;
use rocket::http::{Method, Status}; use rocket::http::{Method, Status};
use rocket::local::blocking::Client; use rocket::local::blocking::Client;
@ -24,7 +24,7 @@ fn pre_future_route<'r>(_: &'r Request<'_>, _: Data) -> route::BoxFuture<'r> {
panic!("hey now..."); panic!("hey now...");
} }
fn rocket() -> Rocket { fn rocket() -> Rocket<Build> {
rocket::build() rocket::build()
.mount("/", routes![panic_route]) .mount("/", routes![panic_route])
.mount("/", vec![Route::new(Method::Get, "/pre", pre_future_route)]) .mount("/", vec![Route::new(Method::Get, "/pre", pre_future_route)])

View File

@ -23,11 +23,11 @@ fn specified_html() -> &'static str {
mod tests { mod tests {
use super::*; use super::*;
use rocket::Rocket; use rocket::{Rocket, Build};
use rocket::local::blocking::Client; use rocket::local::blocking::Client;
use rocket::http::{Status, ContentType}; use rocket::http::{Status, ContentType};
fn rocket() -> Rocket { fn rocket() -> Rocket<Build> {
rocket::build() rocket::build()
.mount("/first", routes![specified, unspecified]) .mount("/first", routes![specified, unspecified])
.mount("/second", routes![specified_json, specified_html]) .mount("/second", routes![specified_json, specified_html])

View File

@ -1,3 +1,4 @@
use rocket::{Rocket, Build};
use rocket::{fairing::AdHoc, http::ContentType, local::blocking::Client}; use rocket::{fairing::AdHoc, http::ContentType, local::blocking::Client};
#[rocket::post("/", data = "<_data>", format = "json")] #[rocket::post("/", data = "<_data>", format = "json")]
@ -6,7 +7,7 @@ fn index(_data: rocket::Data) -> &'static str { "json" }
#[rocket::post("/", data = "<_data>", rank = 2)] #[rocket::post("/", data = "<_data>", rank = 2)]
fn other_index(_data: rocket::Data) -> &'static str { "other" } fn other_index(_data: rocket::Data) -> &'static str { "other" }
fn rocket() -> rocket::Rocket { fn rocket() -> Rocket<Build> {
rocket::build() rocket::build()
.mount("/", rocket::routes![index, other_index]) .mount("/", rocket::routes![index, other_index])
.attach(AdHoc::on_request("Change CT", |req, _| Box::pin(async move { .attach(AdHoc::on_request("Change CT", |req, _| Box::pin(async move {

View File

@ -1,4 +1,4 @@
use rocket::{get, routes}; use rocket::{Rocket, Build};
use rocket::local::blocking::Client; use rocket::local::blocking::Client;
mod inner { mod inner {
@ -10,14 +10,14 @@ mod inner {
} }
} }
#[get("/<name>")] #[rocket::get("/<name>")]
fn hello_name(name: String) -> String { fn hello_name(name: String) -> String {
format!("Hello, {}! This is {}.", name, rocket::uri!(hello_name: &name)) format!("Hello, {}! This is {}.", name, rocket::uri!(hello_name: &name))
} }
fn rocket() -> rocket::Rocket { fn rocket() -> Rocket<Build> {
rocket::build() rocket::build()
.mount("/", routes![hello_name]) .mount("/", rocket::routes![hello_name])
.mount("/", rocket::routes![inner::hello]) .mount("/", rocket::routes![inner::hello])
} }

View File

@ -8,7 +8,7 @@ async fn test_await_timer_inside_attach() {
} }
rocket::build() rocket::build()
.attach(rocket::fairing::AdHoc::on_launch("1", |rocket| async { .attach(rocket::fairing::AdHoc::on_ignite("1", |rocket| async {
do_async_setup().await; do_async_setup().await;
rocket rocket
})); }));

View File

@ -15,10 +15,10 @@ fn get<'a>(jar: &'a CookieJar<'_>) -> Option<&'a str> {
#[cfg(test)] #[cfg(test)]
mod many_cookie_jars_tests { mod many_cookie_jars_tests {
use super::*; use super::*;
use rocket::local::blocking::Client; use rocket::{Rocket, local::blocking::Client, Build};
use rocket::http::Status; use rocket::http::Status;
fn rocket() -> rocket::Rocket { fn rocket() -> Rocket<Build> {
rocket::custom(rocket::Config::debug_default()) rocket::custom(rocket::Config::debug_default())
.mount("/", routes![add, get]) .mount("/", routes![add, get])
} }

View File

@ -1,5 +1,6 @@
#[macro_use] extern crate rocket; #[macro_use] extern crate rocket;
use rocket::{Rocket, Build};
use rocket::response::Redirect; use rocket::response::Redirect;
use rocket::http::uri::Uri; use rocket::http::uri::Uri;
@ -20,11 +21,10 @@ fn uri_redirect() -> Redirect {
Redirect::to(uri!(hello: NAME)) Redirect::to(uri!(hello: NAME))
} }
fn rocket() -> rocket::Rocket { fn rocket() -> Rocket<Build> {
rocket::build().mount("/", routes![hello, uri_redirect, raw_redirect]) rocket::build().mount("/", routes![hello, uri_redirect, raw_redirect])
} }
mod tests { mod tests {
use super::*; use super::*;
use rocket::local::blocking::Client; use rocket::local::blocking::Client;

View File

@ -1,7 +1,8 @@
use rocket::config::{Config, LogLevel}; use rocket::config::{Config, LogLevel};
fn test_config(profile: &str) { async fn test_config(profile: &str) {
let rocket = rocket::custom(Config::figment().select(profile)); let provider = Config::figment().select(profile);
let rocket = rocket::custom(provider).ignite().await.unwrap();
let config = rocket.config(); let config = rocket.config();
match &*profile { match &*profile {
"debug" => { "debug" => {
@ -25,12 +26,12 @@ fn test_config(profile: &str) {
} }
} }
#[test] #[async_test]
fn test_debug_config() { async fn test_debug_config() {
test_config("debug") test_config("debug").await;
} }
#[test] #[async_test]
fn test_release_config() { async fn test_release_config() {
test_config("release") test_config("release").await;
} }

View File

@ -1,4 +1,4 @@
use rocket::Rocket; use rocket::{Rocket, Build};
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use rocket::response::{Debug, status::Created}; use rocket::response::{Debug, status::Created};
@ -83,7 +83,7 @@ async fn destroy(db: Db) -> Result<()> {
Ok(()) Ok(())
} }
async fn run_migrations(rocket: Rocket) -> Rocket { async fn run_migrations(rocket: Rocket<Build>) -> Rocket<Build> {
// This macro from `diesel_migrations` defines an `embedded_migrations` // This macro from `diesel_migrations` defines an `embedded_migrations`
// module containing a function named `run` that runs the migrations in the // module containing a function named `run` that runs the migrations in the
// specified directory, initializing the database. // specified directory, initializing the database.
@ -96,9 +96,9 @@ async fn run_migrations(rocket: Rocket) -> Rocket {
} }
pub fn stage() -> AdHoc { pub fn stage() -> AdHoc {
AdHoc::on_launch("Diesel SQLite Stage", |rocket| async { AdHoc::on_ignite("Diesel SQLite Stage", |rocket| async {
rocket.attach(Db::fairing()) rocket.attach(Db::fairing())
.attach(AdHoc::on_launch("Diesel Migrations", run_migrations)) .attach(AdHoc::on_ignite("Diesel Migrations", run_migrations))
.mount("/diesel", routes![list, read, create, delete, destroy]) .mount("/diesel", routes![list, read, create, delete, destroy])
}) })
} }

View File

@ -1,4 +1,4 @@
use rocket::Rocket; use rocket::{Rocket, Build};
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use rocket_contrib::databases::rusqlite; use rocket_contrib::databases::rusqlite;
use rocket::response::{Debug, status::Created}; use rocket::response::{Debug, status::Created};
@ -68,7 +68,7 @@ async fn destroy(db: Db) -> Result<()> {
Ok(()) Ok(())
} }
async fn init_db(rocket: Rocket) -> Rocket { async fn init_db(rocket: Rocket<Build>) -> Rocket<Build> {
Db::get_one(&rocket).await Db::get_one(&rocket).await
.expect("database mounted") .expect("database mounted")
.run(|conn| { .run(|conn| {
@ -86,9 +86,9 @@ async fn init_db(rocket: Rocket) -> Rocket {
} }
pub fn stage() -> AdHoc { pub fn stage() -> AdHoc {
AdHoc::on_launch("Rusqlite Stage", |rocket| async { AdHoc::on_ignite("Rusqlite Stage", |rocket| async {
rocket.attach(Db::fairing()) rocket.attach(Db::fairing())
.attach(AdHoc::on_launch("Rusqlite Init", init_db)) .attach(AdHoc::on_ignite("Rusqlite Init", init_db))
.mount("/rusqlite", routes![list, create, read, delete, destroy]) .mount("/rusqlite", routes![list, create, read, delete, destroy])
}) })
} }

View File

@ -1,5 +1,5 @@
use rocket::{Rocket, State, futures}; use rocket::{Rocket, State, Build, futures};
use rocket::fairing::AdHoc; use rocket::fairing::{self, AdHoc};
use rocket::response::status::Created; use rocket::response::status::Created;
use rocket_contrib::json::Json; use rocket_contrib::json::Json;
@ -66,7 +66,7 @@ async fn destroy(db: State<'_, Db>) -> Result<()> {
Ok(()) Ok(())
} }
async fn init_db(rocket: Rocket) -> Result<Rocket, Rocket> { async fn init_db(rocket: Rocket<Build>) -> fairing::Result {
use rocket_contrib::databases::Config; use rocket_contrib::databases::Config;
let config = match Config::from("sqlx", &rocket) { let config = match Config::from("sqlx", &rocket) {
@ -99,9 +99,9 @@ async fn init_db(rocket: Rocket) -> Result<Rocket, Rocket> {
} }
pub fn stage() -> AdHoc { pub fn stage() -> AdHoc {
AdHoc::on_launch("SQLx Stage", |rocket| async { AdHoc::on_ignite("SQLx Stage", |rocket| async {
rocket rocket
.attach(AdHoc::try_on_launch("SQLx Database", init_db)) .attach(AdHoc::try_on_ignite("SQLx Database", init_db))
.mount("/sqlx", routes![list, create, read, delete, destroy]) .mount("/sqlx", routes![list, create, read, delete, destroy])
}) })
} }

View File

@ -2,7 +2,7 @@
#[cfg(test)] mod tests; #[cfg(test)] mod tests;
use rocket::Request; use rocket::{Rocket, Request, Build};
use rocket::response::{content, status}; use rocket::response::{content, status};
use rocket::http::Status; use rocket::http::Status;
@ -43,7 +43,7 @@ fn default_catcher(status: Status, req: &Request<'_>) -> status::Custom<String>
status::Custom(status, msg) status::Custom(status, msg)
} }
fn rocket() -> rocket::Rocket { fn rocket() -> Rocket<Build> {
rocket::build() rocket::build()
// .mount("/", routes![hello, hello]) // uncoment this to get an error // .mount("/", routes![hello, hello]) // uncoment this to get an error
.mount("/", routes![hello, forced_error]) .mount("/", routes![hello, forced_error])

View File

@ -4,8 +4,8 @@ use std::io::Cursor;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use rocket::{Rocket, Request, State, Data}; use rocket::{Rocket, Request, State, Data, Build};
use rocket::fairing::{AdHoc, Fairing, Info, Kind}; use rocket::fairing::{self, AdHoc, Fairing, Info, Kind};
use rocket::http::Method; use rocket::http::Method;
struct Token(i64); struct Token(i64);
@ -23,11 +23,11 @@ impl Fairing for Counter {
fn info(&self) -> Info { fn info(&self) -> Info {
Info { Info {
name: "GET/POST Counter", name: "GET/POST Counter",
kind: Kind::Launch | Kind::Request kind: Kind::Ignite | Kind::Request
} }
} }
async fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> { async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
#[get("/counts")] #[get("/counts")]
fn counts(counts: State<'_, Counter>) -> String { fn counts(counts: State<'_, Counter>) -> String {
let get_count = counts.get.load(Ordering::Relaxed); let get_count = counts.get.load(Ordering::Relaxed);
@ -62,15 +62,15 @@ fn rocket() -> _ {
rocket::build() rocket::build()
.mount("/", routes![hello, token]) .mount("/", routes![hello, token])
.attach(Counter::default()) .attach(Counter::default())
.attach(AdHoc::try_on_launch("Token State", |rocket| async { .attach(AdHoc::try_on_ignite("Token State", |rocket| async {
println!("Adding token managed state..."); info!("Adding token managed state...");
match rocket.figment().extract_inner("token") { match rocket.figment().extract_inner("token") {
Ok(value) => Ok(rocket.manage(Token(value))), Ok(value) => Ok(rocket.manage(Token(value))),
Err(_) => Err(rocket) Err(_) => Err(rocket)
} }
})) }))
.attach(AdHoc::on_liftoff("Liftoff Message", |_| Box::pin(async move { .attach(AdHoc::on_liftoff("Liftoff Message", |_| Box::pin(async move {
println!("We have liftoff!"); info!("We have liftoff!");
}))) })))
.attach(AdHoc::on_request("PUT Rewriter", |req, _| { .attach(AdHoc::on_request("PUT Rewriter", |req, _| {
Box::pin(async move { Box::pin(async move {

View File

@ -57,7 +57,7 @@ fn not_found() -> JsonValue {
} }
pub fn stage() -> rocket::fairing::AdHoc { pub fn stage() -> rocket::fairing::AdHoc {
rocket::fairing::AdHoc::on_launch("JSON", |rocket| async { rocket::fairing::AdHoc::on_ignite("JSON", |rocket| async {
rocket.mount("/json", routes![new, update, get]) rocket.mount("/json", routes![new, update, get])
.register("/json", catchers![not_found]) .register("/json", catchers![not_found])
.manage(MessageList::new(vec![])) .manage(MessageList::new(vec![]))

View File

@ -19,7 +19,7 @@ fn echo<'r>(data: MsgPack<Message<'r>>) -> &'r str {
} }
pub fn stage() -> rocket::fairing::AdHoc { pub fn stage() -> rocket::fairing::AdHoc {
rocket::fairing::AdHoc::on_launch("MessagePack", |rocket| async { rocket::fairing::AdHoc::on_ignite("MessagePack", |rocket| async {
rocket.mount("/msgpack", routes![echo, get]) rocket.mount("/msgpack", routes![echo, get])
}) })
} }

View File

@ -13,7 +13,7 @@ fn index(hit_count: State<'_, HitCount>) -> content::Html<String> {
} }
pub fn stage() -> AdHoc { pub fn stage() -> AdHoc {
AdHoc::on_launch("Managed Hit Count", |rocket| async { AdHoc::on_ignite("Managed Hit Count", |rocket| async {
rocket.mount("/count", routes![index]) rocket.mount("/count", routes![index])
.manage(HitCount(AtomicUsize::new(0))) .manage(HitCount(AtomicUsize::new(0)))
}) })

View File

@ -16,7 +16,7 @@ fn pop(rx: State<'_, Rx>) -> Option<String> {
} }
pub fn stage() -> AdHoc { pub fn stage() -> AdHoc {
AdHoc::on_launch("Managed Queue", |rocket| async { AdHoc::on_ignite("Managed Queue", |rocket| async {
let (tx, rx) = flume::bounded(32); let (tx, rx) = flume::bounded(32);
rocket.mount("/queue", routes![push, pop]) rocket.mount("/queue", routes![push, pop])
.manage(Tx(tx)) .manage(Tx(tx))

View File

@ -98,7 +98,7 @@ fn all(
} }
pub fn stage() -> AdHoc { pub fn stage() -> AdHoc {
AdHoc::on_launch("Request Local State", |rocket| async { AdHoc::on_ignite("Request Local State", |rocket| async {
rocket.manage(Atomics::default()) rocket.manage(Atomics::default())
.mount("/req-local", routes![one_two, three_four, all]) .mount("/req-local", routes![one_two, three_four, all])
}) })

View File

@ -1,4 +1,4 @@
use rocket::State; use rocket::{Rocket, State, Build};
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use rocket::tokio::sync::Barrier; use rocket::tokio::sync::Barrier;
@ -9,10 +9,10 @@ async fn rendezvous(barrier: State<'_, Barrier>) -> &'static str {
"Rendezvous reached." "Rendezvous reached."
} }
pub fn rocket() -> rocket::Rocket { pub fn rocket() -> Rocket<Build> {
rocket::build() rocket::build()
.mount("/", routes![rendezvous]) .mount("/", routes![rendezvous])
.attach(AdHoc::on_launch("Add Channel", |rocket| async { .attach(AdHoc::on_ignite("Add Channel", |rocket| async {
rocket.manage(Barrier::new(2)) rocket.manage(Barrier::new(2))
})) }))
} }

View File

@ -7,7 +7,7 @@
mod tests; mod tests;
mod task; mod task;
use rocket::Rocket; use rocket::{Rocket, Build};
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use rocket::request::FlashMessage; use rocket::request::FlashMessage;
use rocket::response::{Flash, Redirect}; use rocket::response::{Flash, Redirect};
@ -90,7 +90,7 @@ async fn index(flash: Option<FlashMessage<'_>>, conn: DbConn) -> Template {
Template::render("index", Context::raw(&conn, flash).await) Template::render("index", Context::raw(&conn, flash).await)
} }
async fn run_migrations(rocket: Rocket) -> Rocket { async fn run_migrations(rocket: Rocket<Build>) -> Rocket<Build> {
// This macro from `diesel_migrations` defines an `embedded_migrations` // This macro from `diesel_migrations` defines an `embedded_migrations`
// module containing a function named `run`. This allows the example to be // module containing a function named `run`. This allows the example to be
// run and tested without any outside setup of the database. // run and tested without any outside setup of the database.
@ -107,7 +107,7 @@ fn rocket() -> _ {
rocket::build() rocket::build()
.attach(DbConn::fairing()) .attach(DbConn::fairing())
.attach(Template::fairing()) .attach(Template::fairing())
.attach(AdHoc::on_launch("Run Migrations", run_migrations)) .attach(AdHoc::on_ignite("Run Migrations", run_migrations))
.mount("/", StaticFiles::from(crate_relative!("static"))) .mount("/", StaticFiles::from(crate_relative!("static")))
.mount("/", routes![index]) .mount("/", routes![index])
.mount("/todo", routes![new, toggle, delete]) .mount("/todo", routes![new, toggle, delete])

View File

@ -165,21 +165,21 @@ Running the application, the console shows:
```sh ```sh
> cargo run > cargo run
🔧 Configured for debug. 🔧 Configured for debug.
=> address: 127.0.0.1 >> address: 127.0.0.1
=> port: 8000 >> port: 8000
=> workers: 64 >> workers: [..]
=> log level: normal >> keep-alive: 5s
=> secret key: [zero] >> limits: [..]
=> limits: forms = 32KiB >> tls: disabled
=> cli colors: true >> temp dir: /tmp
=> keep-alive: 5s >> log level: normal
=> tls: disabled >> cli colors: true
🛰 Mounting /hello: 🛰 Routes:
=> GET /hello/world (world) >> (world) GET /hello/world
🚀 Rocket has launched from http://127.0.0.1:8000 🚀 Rocket has launched from http://127.0.0.1:8000
``` ```
! tip: You can also return `_` from a `#[launch]` function! ! tip: `#[launch]` infers the return type!
Special to Rocket's `#[launch]` attribute, the return type of a function Special to Rocket's `#[launch]` attribute, the return type of a function
decorated with `#[launch]` is automatically inferred when the return type is decorated with `#[launch]` is automatically inferred when the return type is

View File

@ -74,15 +74,15 @@ be significant.
There are four events for which Rocket issues fairing callbacks. Each of these There are four events for which Rocket issues fairing callbacks. Each of these
events is described below: events is described below:
* **Launch (`on_launch`)** * **Ignite (`on_ignite`)**
A launch callback is called just prior to liftoff while launching the An ignite callback is called during [ignition] An ignite callback can
application. A launch callback can arbitrarily modify the `Rocket` instance arbitrarily modify the `Rocket` instance being build. They are are commonly
being constructed. They are are commonly used to parse and validate used to parse and validate configuration values, aborting on bad
configuration values, aborting on bad configurations, and inserting the configurations, and inserting the parsed value into managed state for later
parsed value into managed state for later retrieval. retrieval.
* **liftoff (`on_liftoff`)** * **Liftoff (`on_liftoff`)**
A liftoff callback is called immediately after a Rocket application has A liftoff callback is called immediately after a Rocket application has
launched. A liftoff callback can inspect the `Rocket` instance being launched. A liftoff callback can inspect the `Rocket` instance being
@ -105,19 +105,21 @@ events is described below:
example, response fairings can also be used to inject headers into all example, response fairings can also be used to inject headers into all
outgoing responses. outgoing responses.
[ignition]: @api/rocket/struct.Rocket.html#method.ignite
## Implementing ## Implementing
Recall that a fairing is any type that implements the [`Fairing`] trait. A Recall that a fairing is any type that implements the [`Fairing`] trait. A
`Fairing` implementation has one required method: [`info`], which returns an `Fairing` implementation has one required method: [`info`], which returns an
[`Info`] structure. This structure is used by Rocket to assign a name to the [`Info`] structure. This structure is used by Rocket to assign a name to the
fairing and determine the set of callbacks the fairing is registering for. A fairing and determine the set of callbacks the fairing is registering for. A
`Fairing` can implement any of the available callbacks: [`on_launch`], `Fairing` can implement any of the available callbacks: [`on_ignite`],
[`on_liftoff`], [`on_request`], and [`on_response`]. Each callback has a default [`on_liftoff`], [`on_request`], and [`on_response`]. Each callback has a default
implementation that does absolutely nothing. implementation that does absolutely nothing.
[`Info`]: @api/rocket/fairing/struct.Info.html [`Info`]: @api/rocket/fairing/struct.Info.html
[`info`]: @api/rocket/fairing/trait.Fairing.html#tymethod.info [`info`]: @api/rocket/fairing/trait.Fairing.html#tymethod.info
[`on_launch`]: @api/rocket/fairing/trait.Fairing.html#method.on_launch [`on_ignite`]: @api/rocket/fairing/trait.Fairing.html#method.on_ignite
[`on_liftoff`]: @api/rocket/fairing/trait.Fairing.html#method.on_liftoff [`on_liftoff`]: @api/rocket/fairing/trait.Fairing.html#method.on_liftoff
[`on_request`]: @api/rocket/fairing/trait.Fairing.html#method.on_request [`on_request`]: @api/rocket/fairing/trait.Fairing.html#method.on_request
[`on_response`]: @api/rocket/fairing/trait.Fairing.html#method.on_response [`on_response`]: @api/rocket/fairing/trait.Fairing.html#method.on_response
@ -204,7 +206,7 @@ documentation](@api/rocket/fairing/trait.Fairing.html#example).
For simple occasions, implementing the `Fairing` trait can be cumbersome. This For simple occasions, implementing the `Fairing` trait can be cumbersome. This
is why Rocket provides the [`AdHoc`] type, which creates a fairing from a simple is why Rocket provides the [`AdHoc`] type, which creates a fairing from a simple
function or closure. Using the `AdHoc` type is easy: simply call the function or closure. Using the `AdHoc` type is easy: simply call the
`on_launch`, `on_liftoff`, `on_request`, or `on_response` constructors on `on_ignite`, `on_liftoff`, `on_request`, or `on_response` constructors on
`AdHoc` to create an `AdHoc` structure from a function or closure. `AdHoc` to create an `AdHoc` structure from a function or closure.
As an example, the code below creates a `Rocket` instance with two attached As an example, the code below creates a `Rocket` instance with two attached

View File

@ -166,7 +166,7 @@ testing: we _want_ our tests to panic when something goes wrong.
```rust ```rust
# #[rocket::launch] # #[rocket::launch]
# fn rocket() -> _ { # fn rocket() -> _ {
# rocket::build().reconfigure(rocket::Config::debug_default()) # rocket::build().configure(rocket::Config::debug_default())
# } # }
# use rocket::local::blocking::Client; # use rocket::local::blocking::Client;
@ -179,7 +179,7 @@ application's response:
```rust ```rust
# #[rocket::launch] # #[rocket::launch]
# fn rocket() -> _ { # fn rocket() -> _ {
# rocket::build().reconfigure(rocket::Config::debug_default()) # rocket::build().configure(rocket::Config::debug_default())
# } # }
# use rocket::local::blocking::Client; # use rocket::local::blocking::Client;
# let client = Client::tracked(rocket()).expect("valid rocket instance"); # let client = Client::tracked(rocket()).expect("valid rocket instance");
@ -215,13 +215,18 @@ That's it! Altogether, this looks like:
```rust ```rust
# #[macro_use] extern crate rocket; # #[macro_use] extern crate rocket;
# use rocket::{Rocket, Build};
#[get("/")] #[get("/")]
fn hello() -> &'static str { fn hello() -> &'static str {
"Hello, world!" "Hello, world!"
} }
fn rocket() -> rocket::Rocket {
# /*
#[launch]
# */
fn rocket() -> Rocket<Build> {
rocket::build().mount("/", routes![hello]) rocket::build().mount("/", routes![hello])
} }
@ -243,7 +248,7 @@ mod test {
# let client = Client::debug(rocket()).expect("valid rocket instance"); # let client = Client::debug(rocket()).expect("valid rocket instance");
let mut response = client.get("/").dispatch(); let mut response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.into_string(), Some("Hello, world!".into())); assert_eq!(response.into_string().unwrap(), "Hello, world!");
} }
} }