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`
/// instance. Returns `Some` as long as `Self::fairing()` has been
/// 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)
}

View File

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

View File

@ -1,6 +1,7 @@
use std::marker::PhantomData;
use std::sync::Arc;
use rocket::{Rocket, Phase};
use rocket::fairing::{AdHoc, Fairing};
use rocket::request::{Request, Outcome, FromRequest};
use rocket::outcome::IntoOutcome;
@ -69,7 +70,7 @@ macro_rules! dberr {
impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
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) {
Ok(config) => config,
Err(e) => dberr!("config", db, "{}", e, rocket),
@ -117,7 +118,7 @@ impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
}
#[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>() {
Some(pool) => match pool.get().await.ok() {
Some(conn) => Some(conn),
@ -134,7 +135,7 @@ impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
}
#[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())
}
}

View File

@ -198,7 +198,7 @@
//! Returns a fairing that initializes the associated database connection
//! 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`
//! as long as `Self::fairing()` has been attached.

View File

@ -1,5 +1,6 @@
use r2d2::ManageConnection;
use rocket::{Rocket, Build};
use crate::databases::{Config, Error};
/// Trait implemented by `r2d2`-based database adapters.
@ -61,13 +62,14 @@ use crate::databases::{Config, Error};
/// # fn has_broken(&self, _: &mut Connection) -> bool { panic!() }
/// # }
/// # }
/// use rocket::{Rocket, Build};
/// use rocket_contrib::databases::{r2d2, Error, Config, Poolable, PoolResult};
///
/// impl Poolable for foo::Connection {
/// type Manager = foo::ConnectionManager;
/// 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 manager = foo::ConnectionManager::new(&config.url).map_err(Error::Custom)?;
/// 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
/// 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()`].
@ -109,7 +111,7 @@ impl Poolable for diesel::SqliteConnection {
type Manager = diesel::r2d2::ConnectionManager<diesel::SqliteConnection>;
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::r2d2::{CustomizeConnection, ConnectionManager, Error, Pool};
@ -144,7 +146,7 @@ impl Poolable for diesel::PgConnection {
type Manager = diesel::r2d2::ConnectionManager<diesel::PgConnection>;
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 manager = diesel::r2d2::ConnectionManager::new(&config.url);
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 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 manager = diesel::r2d2::ConnectionManager::new(&config.url);
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 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 url = config.url.parse().map_err(Error::Custom)?;
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 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;
#[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.
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 manager = r2d2_memcache::MemcacheConnectionManager::new(&*config.url);
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)

View File

@ -1,9 +1,9 @@
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use rocket::{Rocket, Request, Response, Orbit};
use rocket::http::uncased::UncasedStr;
use rocket::fairing::{Fairing, Info, Kind};
use rocket::{Rocket, Request, Response};
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()
&& rocket.figment().profile() != rocket::Config::DEBUG_PROFILE
&& !self.is_enabled::<Hsts>()

View File

@ -2,8 +2,8 @@ use std::error::Error;
use crate::templates::{DEFAULT_TEMPLATE_DIR, Context, Engines};
use rocket::Rocket;
use rocket::fairing::{Fairing, Info, Kind};
use rocket::{Rocket, Build, Orbit};
use rocket::fairing::{self, Fairing, Info, Kind};
pub(crate) use self::context::ContextManager;
@ -128,11 +128,10 @@ pub struct TemplateFairing {
#[rocket::async_trait]
impl Fairing for TemplateFairing {
fn info(&self) -> Info {
// on_request only applies in debug mode, so only enable it in debug.
#[cfg(debug_assertions)] let kind = Kind::Launch | Kind::Request;
#[cfg(not(debug_assertions))] let kind = Kind::Launch;
let kind = Kind::Ignite | Kind::Liftoff;
#[cfg(debug_assertions)] let kind = kind | Kind::Request;
Info { kind, name: "Templates" }
Info { kind, name: "Templating" }
}
/// 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
/// template engines. In debug mode, the `ContextManager::new` method
/// initializes a directory watcher for auto-reloading of templates.
async fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> {
use rocket::figment::{Source, value::magic::RelativePathBuf};
async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
use rocket::figment::value::magic::RelativePathBuf;
let configured_dir = rocket.figment()
.extract_inner::<RelativePathBuf>("template_dir")
@ -158,17 +157,8 @@ impl Fairing for TemplateFairing {
match Context::initialize(&path) {
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) {
Ok(()) => {
info_!("directory: {}", Paint::white(Source::from(&*path)));
info_!("engines: {:?}", Paint::white(Engines::ENABLED_EXTENSIONS));
Ok(rocket.manage(ContextManager::new(ctxt)))
}
Ok(()) => Ok(rocket.manage(ContextManager::new(ctxt))),
Err(e) => {
error_!("The template customization callback returned an error:");
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)]
async fn on_request(&self, req: &mut rocket::Request<'_>, _data: &mut rocket::Data) {
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);
}
}

View File

@ -137,7 +137,7 @@ use std::borrow::Cow;
use std::path::PathBuf;
use std::error::Error;
use rocket::Rocket;
use rocket::{Rocket, Orbit};
use rocket::request::Request;
use rocket::fairing::Fairing;
use rocket::response::{self, Content, Responder};
@ -373,7 +373,7 @@ impl Template {
/// }
/// ```
#[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
{
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)
.attach(SqliteDb::fairing())
.attach(SqliteDb2::fairing())
._ignite()
.ignite()
.await
.unwrap();
@ -59,6 +59,7 @@ mod rusqlite_integration_test {
#[cfg(feature = "databases")]
#[cfg(test)]
mod drop_runtime_test {
use rocket::{Rocket, Build};
use r2d2::{ManageConnection, Pool};
use rocket_contrib::databases::{database, Poolable, PoolResult};
use tokio::runtime::Runtime;
@ -87,7 +88,7 @@ mod drop_runtime_test {
type Manager = ContainsRuntime;
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());
Ok(Pool::builder().build(manager)?)
}

View File

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

View File

@ -5,7 +5,7 @@
mod templates_tests {
use std::path::{Path, PathBuf};
use rocket::Rocket;
use rocket::{Rocket, Build};
use rocket::config::Config;
use rocket_contrib::templates::{Template, Metadata};
@ -26,7 +26,7 @@ mod templates_tests {
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())))
.attach(Template::fairing())
.mount("/", routes![template_check, is_reloading])
@ -42,7 +42,7 @@ mod templates_tests {
let error = Client::debug(rocket).expect_err("client failure");
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"),
}
}

View File

@ -49,7 +49,7 @@ pub fn _catch(
let catcher_response = quote_spanned!(return_type_span => {
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.

View File

@ -20,10 +20,10 @@ impl EntryAttr for Launch {
.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::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");
}
}
@ -38,7 +38,7 @@ impl EntryAttr for Launch {
let block = &f.block;
let rocket = quote_spanned!(ty.span().into() => {
let ___rocket: #ty = #block;
let ___rocket: ::rocket::Rocket = ___rocket;
let ___rocket: ::rocket::Rocket<::rocket::Build> = ___rocket;
___rocket
});

View File

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

View File

@ -144,7 +144,7 @@ mod scopes {
use other::world;
fn _rocket() -> rocket::Rocket {
fn _rocket() -> rocket::Rocket<rocket::Build> {
rocket::build().mount("/", rocket::routes![hello, world])
}
}
@ -258,7 +258,7 @@ fn test_query_collection() {
(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 colors = &["blue", "green"];

View File

@ -1,156 +1,168 @@
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`
--> $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)
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`
--> $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)
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`
--> $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)
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 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)
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
--> $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)
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
--> $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)
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 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)
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 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)
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() -> _ {
| ------ this is not `async`
75 | let _ = rocket::build().launch().await;
73 | let _ = rocket::build().launch().await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks
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`
|
= note: expected struct `std::string::String`
found struct `Rocket<Build>`
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`
|
= note: expected struct `Rocket<Build>`
found struct `std::string::String`
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
| _____________________|
| |
27 | | rocket::build()
28 | | }
25 | | rocket::build()
26 | | }
| | ^- help: consider using a semicolon here: `;`
| |_____|
| expected `()`, found struct `Rocket`
|
= note: expected unit type `()`
found struct `Rocket<Build>`
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 due to this
error[E0277]: `main` has invalid return type `Rocket`
--> $DIR/async-entry.rs:96:20
|
96 | async fn main() -> rocket::Rocket {
| ^^^^^^^^^^^^^^ `main` can only return types that implement `Termination`
= note: expected struct `Rocket<Build>`
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`

View File

@ -1,150 +1,162 @@
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)
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
--> $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)
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
--- 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)
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
--> $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)
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
--> $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)
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
--- 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)
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
--- 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)
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
--> $DIR/async-entry.rs:75:17
--> $DIR/async-entry.rs:73:17
|
72 | fn rocket() -> _ {
| ------ this is not `async`
75 | let _ = rocket::build().launch().await;
73 | let _ = rocket::build().launch().await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks
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`
|
= note: expected struct `std::string::String`
found struct `Rocket<Build>`
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`
|
= note: expected struct `Rocket<Build>`
found struct `std::string::String`
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
| _____________________|
| |
27 | | rocket::build()
28 | | }
25 | | rocket::build()
26 | | }
| | ^- help: try adding a semicolon: `;`
| |_____|
| expected `()`, found struct `Rocket`
|
= note: expected unit type `()`
found struct `Rocket<Build>`
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 due to this
error[E0277]: `main` has invalid return type `Rocket`
--> $DIR/async-entry.rs:96:20
|
96 | async fn main() -> rocket::Rocket {
| ^^^^^^^^^^^^^^ `main` can only return types that implement `Termination`
= note: expected struct `Rocket<Build>`
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`

View File

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

View File

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

View File

@ -306,8 +306,14 @@ impl Config {
false => launch_info_!("tls: {}", Paint::default("disabled").bold()),
}
#[cfg(feature = "secrets")]
launch_info_!("secret key: {:?}", Paint::default(&self.secret_key).bold());
#[cfg(feature = "secrets")] {
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_!("log level: {}", Paint::default(self.log_level).bold());
@ -318,11 +324,11 @@ impl Config {
if let Some(md) = figment.find_metadata(key) {
warn!("found value for deprecated config key `{}`", Paint::white(key));
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 {
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),
/// An I/O error occurred in the runtime.
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.
Collisions(crate::router::Collisions),
/// Launch fairing(s) failed.
@ -142,6 +145,7 @@ impl fmt::Display for ErrorKind {
ErrorKind::FailedFairings(_) => "a launch fairing failed".fmt(f),
ErrorKind::Runtime(e) => write!(f, "runtime error: {}", e),
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`");
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 crate::{Rocket, Request, Response, Data};
use crate::fairing::{Fairing, Kind, Info};
use crate::{Rocket, Request, Response, Data, Build, Orbit};
use crate::fairing::{Fairing, Kind, Info, Result};
/// A ad-hoc fairing that can be created from a function or closure.
///
@ -12,7 +12,7 @@ use crate::fairing::{Fairing, Kind, Info};
///
/// # 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
/// 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 {
/// An ad-hoc **launch** fairing. Called just before Rocket launches.
Launch(Once<dyn FnOnce(Rocket) -> BoxFuture<'static, Result> + Send + 'static>),
/// An ad-hoc **ignite** fairing. Called during ignition.
Ignite(Once<dyn FnOnce(Rocket<Build>) -> BoxFuture<'static, Result> + Send + 'static>),
/// 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.
Request(Box<dyn for<'a> Fn(&'a mut Request<'_>, &'a Data)
@ -71,52 +69,52 @@ enum AdHocKind {
}
impl AdHoc {
/// Constructs an `AdHoc` launch fairing named `name`. The function `f` will
/// be called by Rocket just prior to launch.
/// Constructs an `AdHoc` ignite fairing named `name`. The function `f` will
/// be called by Rocket during the [`Rocket::ignite()`] phase.
///
/// This version of an `AdHoc` launch fairing cannot abort launch. For a
/// fallible version that can, see [`AdHoc::try_on_launch()`].
/// This version of an `AdHoc` ignite fairing cannot abort ignite. For a
/// fallible version that can, see [`AdHoc::try_on_ignite()`].
///
/// # Example
///
/// ```rust
/// use rocket::fairing::AdHoc;
///
/// // The no-op launch fairing.
/// let fairing = AdHoc::on_launch("Boom!", |rocket| async move {
/// // The no-op ignite fairing.
/// let fairing = AdHoc::on_ignite("Boom!", |rocket| async move {
/// rocket
/// });
/// ```
pub fn on_launch<F, Fut>(name: &'static str, f: F) -> AdHoc
where F: FnOnce(Rocket) -> Fut + Send + 'static,
Fut: Future<Output = Rocket> + Send + 'static,
pub fn on_ignite<F, Fut>(name: &'static str, f: F) -> AdHoc
where F: FnOnce(Rocket<Build>) -> Fut + 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
/// be called by Rocket just prior to launch. Returning an `Err` aborts
/// launch.
/// Constructs an `AdHoc` ignite fairing named `name`. The function `f` will
/// be called by Rocket during the [`Rocket::ignite()`] phase. Returning an
/// `Err` aborts ignition and thus launch.
///
/// For an infallible version, see [`AdHoc::on_launch()`].
/// For an infallible version, see [`AdHoc::on_ignite()`].
///
/// # Example
///
/// ```rust
/// use rocket::fairing::AdHoc;
///
/// // The no-op try launch fairing.
/// let fairing = AdHoc::try_on_launch("No-Op", |rocket| async { Ok(rocket) });
/// // The no-op try ignite fairing.
/// 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
where F: FnOnce(Rocket) -> Fut + Send + 'static,
Fut: Future<Output = Result<Rocket, Rocket>> + Send + 'static,
pub fn try_on_ignite<F, Fut>(name: &'static str, f: F) -> AdHoc
where F: FnOnce(Rocket<Build>) -> Fut + 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
/// be called by Rocket just after launching.
/// Constructs an `AdHoc` liftoff fairing named `name`. The function `f`
/// will be called by Rocket just after [`Rocket::launch()`].
///
/// # Example
///
@ -129,7 +127,7 @@ impl 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))) }
}
@ -190,6 +188,7 @@ impl AdHoc {
/// # Example
///
/// ```rust
/// # use rocket::launch;
/// use serde::Deserialize;
/// use rocket::fairing::AdHoc;
///
@ -208,7 +207,7 @@ impl AdHoc {
pub fn config<'de, T>() -> AdHoc
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>() {
Ok(config) => config,
Err(e) => {
@ -226,7 +225,7 @@ impl AdHoc {
impl Fairing for AdHoc {
fn info(&self) -> Info {
let kind = match self.kind {
AdHocKind::Launch(_) => Kind::Launch,
AdHocKind::Ignite(_) => Kind::Ignite,
AdHocKind::Liftoff(_) => Kind::Liftoff,
AdHocKind::Request(_) => Kind::Request,
AdHocKind::Response(_) => Kind::Response,
@ -235,14 +234,14 @@ impl Fairing for AdHoc {
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 {
AdHocKind::Launch(ref f) => (f.take())(rocket).await,
AdHocKind::Ignite(ref f) => (f.take())(rocket).await,
_ => 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 {
(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::logger::PaintExt;
@ -35,7 +35,7 @@ impl Fairings {
let index = self.all_fairings.len();
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::Request) { self.request.push(index); }
if kind.is(Kind::Response) { self.response.push(index); }
@ -43,19 +43,19 @@ impl Fairings {
&*self.all_fairings[index]
}
pub fn append(&mut self, others: Fairings) {
for fairing in others.all_fairings {
pub fn append(&mut self, others: &mut Fairings) {
for fairing in others.all_fairings.drain(..) {
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() {
// We're going to move `rocket` while borrowing `fairings`...
let mut fairings = std::mem::replace(&mut rocket.fairings, Fairings::new());
for fairing in iter!(fairings.launch).skip(fairings.last_launch) {
let info = fairing.info();
rocket = match fairing.on_launch(rocket).await {
rocket = match fairing.on_ignite(rocket).await {
Ok(rocket) => rocket,
Err(rocket) => {
fairings.failures.push(info);
@ -68,7 +68,7 @@ impl Fairings {
// Note that `rocket.fairings` may now be non-empty since launch
// fairings could have added more fairings! Move them to the end.
fairings.append(rocket.fairings);
fairings.append(&mut rocket.fairings);
rocket.fairings = fairings;
}
@ -76,7 +76,7 @@ impl Fairings {
}
#[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));
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() {
true => None,
false => Some(&self.failures)
true => Ok(()),
false => Err(&self.failures)
}
}
pub fn pretty_print_counts(&self) {
fn pretty_print<'a>(prefix: &str, iter: impl Iterator<Item = &'a dyn Fairing>) {
let names: Vec<_> = iter.map(|f| f.info().name).collect();
if names.is_empty() {
return;
}
let (num, joined) = (names.len(), names.join(", "));
info_!("{} {}: {}", Paint::default(num).bold(), prefix, Paint::default(joined).bold());
pub fn pretty_print(&self) {
if !self.all_fairings.is_empty() {
launch_info!("{}{}:", Paint::emoji("📡 "), Paint::magenta("Fairings"));
}
if !self.all_fairings.is_empty() {
info!("{}{}:", Paint::emoji("📡 "), Paint::magenta("Fairings"));
pretty_print("launch", iter!(self.launch));
pretty_print("liftoff", iter!(self.liftoff));
pretty_print("request", iter!(self.request));
pretty_print("response", iter!(self.response));
for fairing in &self.all_fairings {
launch_info_!("{} ({})", Paint::default(fairing.info().name).bold(),
Paint::blue(fairing.info().kind).bold());
}
}
}

View File

@ -18,7 +18,7 @@ use std::ops::BitOr;
/// # let _unused_info =
/// Info {
/// 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
/// callbacks:
///
/// * Launch
/// * Ignite
/// * Liftoff
/// * Request
/// * Response
///
/// 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,
/// use `Kind::Launch | Kind::Request`. Similarly, to represent a fairing that
/// is only a launch fairing, use `Kind::Launch`.
/// instance, to represent a fairing that is both an ignite and request fairing,
/// use `Kind::Ignite | Kind::Request`. Similarly, to represent a fairing that
/// is only an ignite fairing, use `Kind::Ignite`.
#[derive(Debug, Clone, Copy)]
pub struct Kind(usize);
#[allow(non_upper_case_globals)]
impl Kind {
/// `Kind` flag representing a request for a 'launch' callback.
pub const Launch: Kind = Kind(1 << 0);
/// `Kind` flag representing a request for a 'ignite' callback.
pub const Ignite: Kind = Kind(1 << 0);
/// `Kind` flag representing a request for a 'liftoff' callback.
pub const Liftoff: Kind = Kind(1 << 1);
@ -70,16 +70,16 @@ impl Kind {
/// ```rust
/// use rocket::fairing::Kind;
///
/// let launch_and_req = Kind::Launch | Kind::Request;
/// assert!(launch_and_req.is(Kind::Launch | Kind::Request));
/// let ignite_and_req = Kind::Ignite | Kind::Request;
/// assert!(ignite_and_req.is(Kind::Ignite | Kind::Request));
///
/// assert!(launch_and_req.is(Kind::Launch));
/// assert!(launch_and_req.is(Kind::Request));
/// assert!(ignite_and_req.is(Kind::Ignite));
/// assert!(ignite_and_req.is(Kind::Request));
///
/// assert!(!launch_and_req.is(Kind::Liftoff));
/// assert!(!launch_and_req.is(Kind::Response));
/// assert!(!launch_and_req.is(Kind::Launch | Kind::Response));
/// assert!(!launch_and_req.is(Kind::Launch | Kind::Request | Kind::Response));
/// assert!(!ignite_and_req.is(Kind::Liftoff));
/// assert!(!ignite_and_req.is(Kind::Response));
/// assert!(!ignite_and_req.is(Kind::Ignite | Kind::Response));
/// assert!(!ignite_and_req.is(Kind::Ignite | Kind::Request | Kind::Response));
/// ```
#[inline]
pub fn is(self, other: Kind) -> bool {
@ -93,13 +93,13 @@ impl Kind {
/// ```rust
/// use rocket::fairing::Kind;
///
/// let launch_and_req = Kind::Launch | Kind::Request;
/// assert!(launch_and_req.is_exactly(Kind::Launch | Kind::Request));
/// let ignite_and_req = Kind::Ignite | Kind::Request;
/// assert!(ignite_and_req.is_exactly(Kind::Ignite | Kind::Request));
///
/// assert!(!launch_and_req.is_exactly(Kind::Launch));
/// assert!(!launch_and_req.is_exactly(Kind::Request));
/// assert!(!launch_and_req.is_exactly(Kind::Response));
/// assert!(!launch_and_req.is_exactly(Kind::Launch | Kind::Response));
/// assert!(!ignite_and_req.is_exactly(Kind::Ignite));
/// assert!(!ignite_and_req.is_exactly(Kind::Request));
/// assert!(!ignite_and_req.is_exactly(Kind::Response));
/// assert!(!ignite_and_req.is_exactly(Kind::Ignite | Kind::Response));
/// ```
#[inline]
pub fn is_exactly(self, other: Kind) -> bool {
@ -115,3 +115,23 @@ impl BitOr for Kind {
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
//! 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 ad_hoc;
@ -57,6 +57,9 @@ pub(crate) use self::fairings::Fairings;
pub use self::ad_hoc::AdHoc;
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
// `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
@ -101,24 +104,23 @@ pub use self::info_kind::{Info, Kind};
///
/// The four callback kinds are as follows:
///
/// * **Launch (`on_launch`)**
/// * **Ignite (`on_ignite`)**
///
/// A launch callback, represented by the [`Fairing::on_launch()`] method,
/// is called just prior to liftoff, while launching the application. The
/// state of the `Rocket` instance is, at this point, not finalized, as it
/// may be modified at will by other launch fairings. As a result, it is
/// unwise to depend on the state of the `Rocket` instance.
/// An ignite callback, represented by the [`Fairing::on_ignite()`] method,
/// is called just prior to liftoff, during ignition. The state of the
/// `Rocket` instance is, at this point, not finalized, as it may be
/// modified at will by other ignite fairings.
///
/// 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`
/// 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
/// recursively attaching launch fairings. It returns `Ok` if it would like
/// launching to proceed nominally and `Err` otherwise. If a launch fairing
/// returns `Err`, launch will be aborted. All launch fairings are executed
/// even if one or more signal a failure.
/// recursively attaching ignite fairings. It returns `Ok` if it would like
/// ignition and launch to proceed nominally and `Err` otherwise. If an
/// ignite fairing returns `Err`, launch will be aborted. All ignite
/// fairings are executed even if one or more signal a failure.
///
/// * **Liftoff (`on_liftoff`)**
///
@ -159,7 +161,7 @@ pub use self::info_kind::{Info, Kind};
/// # Implementing
///
/// 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
/// callback kind in the `kind` field of the returned `Info` structure from
/// [`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]`:
///
/// ```rust
/// use rocket::{Rocket, Request, Data, Response};
/// use rocket::fairing::{Fairing, Info, Kind};
/// use rocket::{Rocket, Request, Data, Response, Build, Orbit};
/// use rocket::fairing::{self, Fairing, Info, Kind};
///
/// # struct MyType;
/// #[rocket::async_trait]
@ -211,12 +213,12 @@ pub use self::info_kind::{Info, Kind};
/// # unimplemented!()
/// }
///
/// async fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> {
/// async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
/// /* ... */
/// # unimplemented!()
/// }
///
/// async fn on_liftoff(&self, rocket: &Rocket) {
/// async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
/// /* ... */
/// # unimplemented!()
/// }
@ -381,15 +383,15 @@ pub trait Fairing: Send + Sync + 'static {
///
/// Rocket will only dispatch callbacks to this fairing for the kinds in the
/// `kind` field of the returned `Info` structure. For instance, if
/// `Kind::Launch | Kind::Request` is used, then Rocket will only call the
/// `on_launch` and `on_request` methods of the fairing. Similarly, if
/// `Kind::Ignite | Kind::Request` is used, then Rocket will only call the
/// `on_ignite` and `on_request` methods of the fairing. Similarly, if
/// `Kind::Response` is used, Rocket will only call the `on_response` method
/// of this fairing.
///
/// # Example
///
/// 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
/// use rocket::fairing::{Fairing, Info, Kind};
@ -400,25 +402,25 @@ pub trait Fairing: Send + Sync + 'static {
/// fn info(&self) -> Info {
/// Info {
/// name: "My Custom Fairing",
/// kind: Kind::Launch | Kind::Response
/// kind: Kind::Ignite | Kind::Response
/// }
/// }
/// }
/// ```
fn info(&self) -> Info;
/// The launch callback. Returns `Ok` if launch should proceed and `Err` if
/// launch should be aborted.
/// The ignite callback. Returns `Ok` if ignition should proceed and `Err`
/// if ignition and launch should be aborted.
///
/// This method is called when the application is being launched if
/// `Kind::Launch` is in the `kind` field of the `Info` structure for this
/// fairing. The `rocket` parameter is the `Rocket` instance that is
/// currently being built for this application.
/// This method is called during ignition and if `Kind::Ignite` is in the
/// `kind` field of the `Info` structure for this fairing. The `rocket`
/// parameter is the `Rocket` instance that is currently being built for
/// this application.
///
/// ## Default Implementation
///
/// 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.
///
@ -429,7 +431,7 @@ pub trait Fairing: Send + Sync + 'static {
/// ## Default Implementation
///
/// 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.
///
@ -464,12 +466,12 @@ impl<T: Fairing> Fairing for std::sync::Arc<T> {
}
#[inline]
async fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> {
(self as &T).on_launch(rocket).await
async fn on_ignite(&self, rocket: Rocket<Build>) -> Result {
(self as &T).on_ignite(rocket).await
}
#[inline]
async fn on_liftoff(&self, rocket: &Rocket) {
async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
(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::route::Route;
#[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::request::Request;
pub use crate::shutdown::Shutdown;
pub use crate::state::State;
/// Creates a new instance of `Rocket`: aliases [`Rocket::build()`].
pub fn build() -> Rocket {
/// Creates a [`Rocket`] instance with the default config provider: aliases
/// [`Rocket::build()`].
pub fn build() -> Rocket<Build> {
Rocket::build()
}
/// Creates a new instance of `Rocket` with a custom configuration provider:
/// aliases [`Rocket::custom()`].
pub fn custom<T: figment::Provider>(provider: T) -> Rocket {
/// Creates a [`Rocket`] instance with a custom config provider: aliases
/// [`Rocket::custom()`].
pub fn custom<T: figment::Provider>(provider: T) -> Rocket<Build> {
Rocket::custom(provider)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ use yansi::Paint;
use serde::{de, Serialize, Serializer, Deserialize, Deserializer};
#[derive(Debug)]
struct RocketLogger(LogLevel);
struct RocketLogger;
/// Defines the maximum level of log messages to show.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
@ -102,7 +102,7 @@ macro_rules! warn_ { ($($args:expr),+) => { log_!(warn: $($args),+); }; }
impl log::Log for RocketLogger {
#[inline(always)]
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"),
None => false
}
@ -114,36 +114,35 @@ impl log::Log for RocketLogger {
return;
}
// Don't print Hyper or Rustls or r2d2 messages unless debug is enabled.
let configged_level = self.0;
// Don't print Hyper, Rustls or r2d2 messages unless debug is enabled.
let max = log::max_level();
let from = |path| record.module_path().map_or(false, |m| m.starts_with(path));
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;
}
// In Rocket, we abuse targets with suffix "_" to indicate indentation.
let is_launch = record.target().starts_with("launch");
if record.target().ends_with('_') {
if configged_level != LogLevel::Critical || is_launch {
print!(" {} ", Paint::default("=>").bold());
}
let indented = record.target().ends_with('_');
if indented {
print!(" {} ", Paint::default(">>").bold());
}
match record.level() {
log::Level::Info => println!("{}", Paint::blue(record.args()).wrap()),
log::Level::Trace => println!("{}", Paint::magenta(record.args()).wrap()),
log::Level::Error => {
log::Level::Error if !indented => {
println!("{} {}",
Paint::red("Error:").bold(),
Paint::red(record.args()).wrap())
}
log::Level::Warn => {
log::Level::Warn if !indented => {
println!("{} {}",
Paint::yellow("Warning:").bold(),
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 => {
print!("\n{} ", Paint::blue("-->").bold());
if let Some(file) = record.file() {
@ -154,7 +153,7 @@ impl log::Log for RocketLogger {
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 {
if config.log_level == LogLevel::Off {
return false;
}
if !atty::is(atty::Stream::Stdout)
|| (cfg!(windows) && !Paint::enable_windows_ascii())
|| !config.cli_colors
@ -176,7 +171,7 @@ pub(crate) fn init(config: &crate::Config) -> bool {
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 {
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::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::{Method, Header, HeaderMap};
use crate::http::{ContentType, Accept, MediaType, CookieJar, Cookie};
@ -34,7 +34,7 @@ pub struct Request<'r> {
}
pub(crate) struct RequestState<'r> {
pub rocket: &'r Rocket,
pub rocket: &'r Rocket<Orbit>,
pub route: Atomic<Option<&'r Route>>,
pub cookies: CookieJar<'r>,
pub accept: Storage<Option<Accept>>,
@ -71,7 +71,7 @@ impl<'r> Request<'r> {
/// Create a new `Request` with the given `method` and `uri`.
#[inline(always)]
pub(crate) fn new<'s: 'r>(
rocket: &'r Rocket,
rocket: &'r Rocket<Orbit>,
method: Method,
uri: Origin<'s>
) -> Request<'r> {
@ -83,7 +83,7 @@ impl<'r> Request<'r> {
state: RequestState {
rocket,
route: Atomic::new(None),
cookies: CookieJar::new(&rocket.config),
cookies: CookieJar::new(rocket.config()),
accept: Storage::new(),
content_type: Storage::new(),
cache: Arc::new(<Container![Send + Sync]>::new()),
@ -109,19 +109,20 @@ impl<'r> Request<'r> {
self.method.load(Ordering::Acquire)
}
/// Set the method of `self`.
/// Set the method of `self` to `method`.
///
/// # Example
///
/// ```rust
/// 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);
///
/// request.set_method(Method::Post);
/// assert_eq!(request.method(), Method::Post);
/// # });
/// ```
#[inline(always)]
pub fn set_method(&mut self, method: Method) {
@ -149,10 +150,10 @@ impl<'r> Request<'r> {
///
/// ```rust
/// 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();
/// request.set_uri(uri);
/// assert_eq!(request.uri().path(), "/hello/Sergio");
@ -162,7 +163,6 @@ impl<'r> Request<'r> {
/// request.set_uri(new_uri);
/// assert_eq!(request.uri().path(), "/foo/hello/Sergio");
/// assert_eq!(request.uri().query().unwrap(), "type=greeting");
/// # });
/// ```
#[inline(always)]
pub fn set_uri(&mut self, uri: Origin<'r>) {
@ -186,14 +186,15 @@ impl<'r> Request<'r> {
///
/// ```rust
/// 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);
///
/// let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8000).into();
/// request.set_remote(localhost);
/// assert_eq!(request.remote(), Some(localhost));
/// # });
/// ```
#[inline(always)]
pub fn remote(&self) -> Option<SocketAddr> {
@ -208,14 +209,15 @@ impl<'r> Request<'r> {
///
/// ```rust
/// 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);
///
/// let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8000).into();
/// request.set_remote(localhost);
/// assert_eq!(request.remote(), Some(localhost));
/// # });
/// ```
#[inline(always)]
pub fn set_remote(&mut self, address: SocketAddr) {
@ -258,11 +260,12 @@ impl<'r> Request<'r> {
/// # Example
///
/// ```rust
/// # use rocket::Request;
/// # use rocket::http::{Header, Method};
/// # use rocket::http::Header;
/// # 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
/// assert!(request.client_ip().is_none());
///
@ -273,7 +276,6 @@ impl<'r> Request<'r> {
/// // now with an X-Real-IP header
/// request.add_header(Header::new("X-Real-IP", "8.8.8.8"));
/// assert_eq!(request.client_ip(), Some("8.8.8.8".parse().unwrap()));
/// # });
/// ```
#[inline]
pub fn client_ip(&self) -> Option<IpAddr> {
@ -333,14 +335,15 @@ impl<'r> Request<'r> {
///
/// ```rust
/// 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());
///
/// request.add_header(ContentType::HTML);
/// assert!(request.headers().contains("Content-Type"));
/// assert_eq!(request.headers().len(), 1);
/// # });
/// ```
#[inline]
pub fn add_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) {
@ -357,8 +360,10 @@ impl<'r> Request<'r> {
///
/// ```rust
/// 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());
///
/// request.add_header(ContentType::Any);
@ -368,7 +373,6 @@ impl<'r> Request<'r> {
/// request.replace_header(ContentType::PNG);
/// assert_eq!(request.headers().get_one("Content-Type"), Some("image/png"));
/// assert_eq!(request.content_type(), Some(&ContentType::PNG));
/// # });
/// ```
#[inline]
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();
/// ```
#[inline(always)]
pub fn rocket(&self) -> &'r Rocket {
pub fn rocket(&self) -> &'r Rocket<Orbit> {
&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
/// currently matched route, as a string, if it exists. Used by codegen.
#[inline]
@ -831,7 +827,7 @@ impl<'r> Request<'r> {
/// Convert from Hyper types into a Rocket Request.
pub(crate) fn from_hyp(
rocket: &'r Rocket,
rocket: &'r Rocket<Orbit>,
h_method: hyper::Method,
h_headers: hyper::HeaderMap<hyper::HeaderValue>,
h_uri: &'r hyper::Uri,
@ -839,7 +835,7 @@ impl<'r> Request<'r> {
) -> Result<Request<'r>, Error<'r>> {
// 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()) {
(None, None, Some(paq)) => paq.as_str(),
(None, None, Some(path_query)) => path_query.as_str(),
_ => return Err(Error::InvalidUri(h_uri)),
};

View File

@ -1,7 +1,8 @@
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::collections::HashMap;
use crate::{Rocket, Request, Config};
use crate::Request;
use crate::local::blocking::Client;
use crate::http::hyper;
macro_rules! assert_headers {
@ -20,8 +21,9 @@ macro_rules! assert_headers {
$(expected.entry($key).or_insert(vec![]).append(&mut vec![$($value),+]);)+
// Dispatch the request and check that the headers are what we expect.
let r = Rocket::custom(Config::default());
let req = Request::from_hyp(&r, h_method, h_headers, &h_uri, h_addr).unwrap();
let client = Client::debug_with(vec![]).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();
for (key, values) in expected.iter() {
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 yansi::Paint;
use crate::{Rocket, Request, Data, route};
use crate::{Rocket, Orbit, Request, Data, route};
use crate::form::Form;
use crate::response::{Response, Body};
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
// `HyperResponse` type, this function does the actual response processing.
async fn hyper_service_fn(
rocket: Arc<Rocket>,
rocket: Arc<Rocket<Orbit>>,
h_addr: std::net::SocketAddr,
hyp_req: hyper::Request<hyper::Body>,
) -> 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))
}
impl Rocket {
impl Rocket<Orbit> {
/// Wrapper around `make_response` to log a success or failure.
#[inline]
async fn send_response(
@ -370,7 +370,7 @@ impl Rocket {
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, ()>
{
use std::net::ToSocketAddrs;
@ -403,7 +403,7 @@ impl Rocket {
}
// 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,
<L as Listener>::Connection: Send + Unpin + 'static
{
@ -415,16 +415,12 @@ impl Rocket {
};
// 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 {
true => tokio::signal::ctrl_c().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 service = hyper::make_service_fn(move |conn: &<L as Listener>::Connection| {
let rocket = rocket.clone();
@ -437,11 +433,12 @@ impl Rocket {
});
// NOTE: `hyper` uses `tokio::spawn()` as the default executor.
let shutdown_receiver = shutdown_handle.clone();
let server = hyper::Server::builder(Incoming::from_listener(listener))
.http1_keepalive(http1_keepalive)
.http2_keep_alive_interval(http2_keep_alive)
.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))));
tokio::pin!(server);
@ -450,7 +447,7 @@ impl Rocket {
match selecter.await {
future::Either::Left((Ok(()), server)) => {
// Ctrl-was pressed. Signal shutdown, wait for the server.
shutdown_handle.shutdown();
shutdown_handle.notify_one();
server.await
}
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 tokio::sync::mpsc;
/// 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
/// pending requests and return `Ok()` to the caller of [`Rocket::launch()`].
///
@ -17,8 +20,8 @@ use tokio::sync::mpsc;
/// use rocket::Shutdown;
///
/// #[get("/shutdown")]
/// fn shutdown(handle: Shutdown) -> &'static str {
/// handle.shutdown();
/// fn shutdown(shutdown: Shutdown) -> &'static str {
/// shutdown.notify();
/// "Shutting down..."
/// }
///
@ -33,18 +36,18 @@ use tokio::sync::mpsc;
/// result.expect("server failed unexpectedly");
/// }
/// ```
#[must_use = "a shutdown request is only sent on `shutdown.notify()`"]
#[derive(Debug, Clone)]
pub struct Shutdown(pub(crate) mpsc::Sender<()>);
pub struct Shutdown(pub(crate) Arc<Notify>);
impl Shutdown {
/// Notify Rocket to shut down gracefully. This function returns
/// immediately; pending requests will continue to run until completion
/// before the actual shutdown occurs.
/// Notify Rocket to shut down gracefully.
///
/// This function returns immediately; pending requests will continue to run
/// until completion before the actual shutdown occurs.
#[inline]
pub fn shutdown(self) {
// Intentionally ignore any error, as the only scenarios this can happen
// is sending too many shutdown requests or we're already shut down.
let _ = self.0.try_send(());
pub fn notify(self) {
self.0.notify_one();
info!("Server shutdown requested, waiting for all pending requests to finish.");
}
}
@ -55,6 +58,7 @@ impl<'r> FromRequest<'r> for Shutdown {
#[inline]
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 crate::rocket::Rocket;
use crate::{Rocket, Phase};
use crate::request::{self, FromRequest, Request};
use crate::outcome::Outcome;
use crate::http::Status;
@ -62,7 +62,7 @@ use crate::http::Status;
/// use rocket::request::{self, Request, FromRequest};
/// use rocket::outcome::IntoOutcome;
///
/// # struct MyConfig{ user_val: String };
/// # struct MyConfig { user_val: String };
/// struct Item<'r>(&'r str);
///
/// #[rocket::async_trait]
@ -163,7 +163,7 @@ impl<'r, T: Send + Sync + 'static> State<'r, T> {
/// assert_eq!(state, None);
/// ```
#[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)
}
}

View File

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

View File

@ -8,12 +8,12 @@ fn index(form: Form<String>) -> String {
}
mod limits_tests {
use rocket;
use rocket::{Rocket, Build};
use rocket::local::blocking::Client;
use rocket::http::{Status, ContentType};
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();
config.limits = Limits::default().limit("form", limit.into());
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 {
use super::*;
use rocket::Rocket;
use rocket::{Rocket, Build};
use rocket::local::blocking::Client;
use rocket::http::ContentType;
fn rocket() -> Rocket {
fn rocket() -> Rocket<Build> {
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)]
mod many_cookie_jars_tests {
use super::*;
use rocket::{Rocket, Build};
use rocket::local::blocking::Client;
fn rocket() -> rocket::Rocket {
fn rocket() -> Rocket<Build> {
rocket::build().mount("/", routes![multi_add, multi_get])
}

View File

@ -1,5 +1,5 @@
#[macro_use] extern crate rocket;
use rocket::Route;
use rocket::{Rocket, Route, Build};
pub fn prepend(prefix: &str, route: Route) -> Route {
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())
}

View File

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

View File

@ -3,7 +3,7 @@ use rocket::fairing::AdHoc;
use rocket::futures::channel::oneshot;
#[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 rocket = rocket::custom(Config { port: 0, ..Config::debug_default() })
.attach(AdHoc::on_liftoff("Send Port -> Channel", move |rocket| {

View File

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

View File

@ -23,11 +23,11 @@ fn specified_html() -> &'static str {
mod tests {
use super::*;
use rocket::Rocket;
use rocket::{Rocket, Build};
use rocket::local::blocking::Client;
use rocket::http::{Status, ContentType};
fn rocket() -> Rocket {
fn rocket() -> Rocket<Build> {
rocket::build()
.mount("/first", routes![specified, unspecified])
.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};
#[rocket::post("/", data = "<_data>", format = "json")]
@ -6,7 +7,7 @@ fn index(_data: rocket::Data) -> &'static str { "json" }
#[rocket::post("/", data = "<_data>", rank = 2)]
fn other_index(_data: rocket::Data) -> &'static str { "other" }
fn rocket() -> rocket::Rocket {
fn rocket() -> Rocket<Build> {
rocket::build()
.mount("/", rocket::routes![index, other_index])
.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;
mod inner {
@ -10,14 +10,14 @@ mod inner {
}
}
#[get("/<name>")]
#[rocket::get("/<name>")]
fn hello_name(name: String) -> String {
format!("Hello, {}! This is {}.", name, rocket::uri!(hello_name: &name))
}
fn rocket() -> rocket::Rocket {
fn rocket() -> Rocket<Build> {
rocket::build()
.mount("/", routes![hello_name])
.mount("/", rocket::routes![hello_name])
.mount("/", rocket::routes![inner::hello])
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
use rocket::Rocket;
use rocket::{Rocket, Build};
use rocket::fairing::AdHoc;
use rocket::response::{Debug, status::Created};
@ -83,7 +83,7 @@ async fn destroy(db: Db) -> Result<()> {
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`
// module containing a function named `run` that runs the migrations in the
// specified directory, initializing the database.
@ -96,9 +96,9 @@ async fn run_migrations(rocket: Rocket) -> Rocket {
}
pub fn stage() -> AdHoc {
AdHoc::on_launch("Diesel SQLite Stage", |rocket| async {
AdHoc::on_ignite("Diesel SQLite Stage", |rocket| async {
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])
})
}

View File

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

View File

@ -1,5 +1,5 @@
use rocket::{Rocket, State, futures};
use rocket::fairing::AdHoc;
use rocket::{Rocket, State, Build, futures};
use rocket::fairing::{self, AdHoc};
use rocket::response::status::Created;
use rocket_contrib::json::Json;
@ -66,7 +66,7 @@ async fn destroy(db: State<'_, Db>) -> Result<()> {
Ok(())
}
async fn init_db(rocket: Rocket) -> Result<Rocket, Rocket> {
async fn init_db(rocket: Rocket<Build>) -> fairing::Result {
use rocket_contrib::databases::Config;
let config = match Config::from("sqlx", &rocket) {
@ -99,9 +99,9 @@ async fn init_db(rocket: Rocket) -> Result<Rocket, Rocket> {
}
pub fn stage() -> AdHoc {
AdHoc::on_launch("SQLx Stage", |rocket| async {
AdHoc::on_ignite("SQLx Stage", |rocket| async {
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])
})
}

View File

@ -2,7 +2,7 @@
#[cfg(test)] mod tests;
use rocket::Request;
use rocket::{Rocket, Request, Build};
use rocket::response::{content, status};
use rocket::http::Status;
@ -43,7 +43,7 @@ fn default_catcher(status: Status, req: &Request<'_>) -> status::Custom<String>
status::Custom(status, msg)
}
fn rocket() -> rocket::Rocket {
fn rocket() -> Rocket<Build> {
rocket::build()
// .mount("/", routes![hello, hello]) // uncoment this to get an 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::Arc;
use rocket::{Rocket, Request, State, Data};
use rocket::fairing::{AdHoc, Fairing, Info, Kind};
use rocket::{Rocket, Request, State, Data, Build};
use rocket::fairing::{self, AdHoc, Fairing, Info, Kind};
use rocket::http::Method;
struct Token(i64);
@ -23,11 +23,11 @@ impl Fairing for Counter {
fn info(&self) -> Info {
Info {
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")]
fn counts(counts: State<'_, Counter>) -> String {
let get_count = counts.get.load(Ordering::Relaxed);
@ -62,15 +62,15 @@ fn rocket() -> _ {
rocket::build()
.mount("/", routes![hello, token])
.attach(Counter::default())
.attach(AdHoc::try_on_launch("Token State", |rocket| async {
println!("Adding token managed state...");
.attach(AdHoc::try_on_ignite("Token State", |rocket| async {
info!("Adding token managed state...");
match rocket.figment().extract_inner("token") {
Ok(value) => Ok(rocket.manage(Token(value))),
Err(_) => Err(rocket)
}
}))
.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, _| {
Box::pin(async move {

View File

@ -57,7 +57,7 @@ fn not_found() -> JsonValue {
}
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])
.register("/json", catchers![not_found])
.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 {
rocket::fairing::AdHoc::on_launch("MessagePack", |rocket| async {
rocket::fairing::AdHoc::on_ignite("MessagePack", |rocket| async {
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 {
AdHoc::on_launch("Managed Hit Count", |rocket| async {
AdHoc::on_ignite("Managed Hit Count", |rocket| async {
rocket.mount("/count", routes![index])
.manage(HitCount(AtomicUsize::new(0)))
})

View File

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

View File

@ -98,7 +98,7 @@ fn all(
}
pub fn stage() -> AdHoc {
AdHoc::on_launch("Request Local State", |rocket| async {
AdHoc::on_ignite("Request Local State", |rocket| async {
rocket.manage(Atomics::default())
.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::tokio::sync::Barrier;
@ -9,10 +9,10 @@ async fn rendezvous(barrier: State<'_, Barrier>) -> &'static str {
"Rendezvous reached."
}
pub fn rocket() -> rocket::Rocket {
pub fn rocket() -> Rocket<Build> {
rocket::build()
.mount("/", routes![rendezvous])
.attach(AdHoc::on_launch("Add Channel", |rocket| async {
.attach(AdHoc::on_ignite("Add Channel", |rocket| async {
rocket.manage(Barrier::new(2))
}))
}

View File

@ -7,7 +7,7 @@
mod tests;
mod task;
use rocket::Rocket;
use rocket::{Rocket, Build};
use rocket::fairing::AdHoc;
use rocket::request::FlashMessage;
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)
}
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`
// module containing a function named `run`. This allows the example to be
// run and tested without any outside setup of the database.
@ -107,7 +107,7 @@ fn rocket() -> _ {
rocket::build()
.attach(DbConn::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("/", routes![index])
.mount("/todo", routes![new, toggle, delete])

View File

@ -165,21 +165,21 @@ Running the application, the console shows:
```sh
> cargo run
🔧 Configured for debug.
=> address: 127.0.0.1
=> port: 8000
=> workers: 64
=> log level: normal
=> secret key: [zero]
=> limits: forms = 32KiB
=> cli colors: true
=> keep-alive: 5s
=> tls: disabled
🛰 Mounting /hello:
=> GET /hello/world (world)
>> address: 127.0.0.1
>> port: 8000
>> workers: [..]
>> keep-alive: 5s
>> limits: [..]
>> tls: disabled
>> temp dir: /tmp
>> log level: normal
>> cli colors: true
🛰 Routes:
>> (world) GET /hello/world
🚀 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
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
events is described below:
* **Launch (`on_launch`)**
* **Ignite (`on_ignite`)**
A launch callback is called just prior to liftoff while launching the
application. A launch callback can arbitrarily modify the `Rocket` instance
being constructed. They are are commonly used to parse and validate
configuration values, aborting on bad configurations, and inserting the
parsed value into managed state for later retrieval.
An ignite callback is called during [ignition] An ignite callback can
arbitrarily modify the `Rocket` instance being build. They are are commonly
used to parse and validate configuration values, aborting on bad
configurations, and inserting the parsed value into managed state for later
retrieval.
* **liftoff (`on_liftoff`)**
* **Liftoff (`on_liftoff`)**
A liftoff callback is called immediately after a Rocket application has
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
outgoing responses.
[ignition]: @api/rocket/struct.Rocket.html#method.ignite
## Implementing
Recall that a fairing is any type that implements the [`Fairing`] trait. A
`Fairing` implementation has one required method: [`info`], which returns an
[`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` 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
implementation that does absolutely nothing.
[`Info`]: @api/rocket/fairing/struct.Info.html
[`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_request`]: @api/rocket/fairing/trait.Fairing.html#method.on_request
[`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
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
`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.
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
# #[rocket::launch]
# fn rocket() -> _ {
# rocket::build().reconfigure(rocket::Config::debug_default())
# rocket::build().configure(rocket::Config::debug_default())
# }
# use rocket::local::blocking::Client;
@ -179,7 +179,7 @@ application's response:
```rust
# #[rocket::launch]
# fn rocket() -> _ {
# rocket::build().reconfigure(rocket::Config::debug_default())
# rocket::build().configure(rocket::Config::debug_default())
# }
# use rocket::local::blocking::Client;
# let client = Client::tracked(rocket()).expect("valid rocket instance");
@ -215,13 +215,18 @@ That's it! Altogether, this looks like:
```rust
# #[macro_use] extern crate rocket;
# use rocket::{Rocket, Build};
#[get("/")]
fn hello() -> &'static str {
"Hello, world!"
}
fn rocket() -> rocket::Rocket {
# /*
#[launch]
# */
fn rocket() -> Rocket<Build> {
rocket::build().mount("/", routes![hello])
}
@ -243,7 +248,7 @@ mod test {
# let client = Client::debug(rocket()).expect("valid rocket instance");
let mut response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok);
assert_eq!(response.into_string(), Some("Hello, world!".into()));
assert_eq!(response.into_string().unwrap(), "Hello, world!");
}
}