Rocket/core/lib/src/rocket.rs

929 lines
31 KiB
Rust

use std::fmt;
use std::ops::{Deref, DerefMut};
use std::net::SocketAddr;
use yansi::Paint;
use either::Either;
use figment::{Figment, Provider};
use crate::{Catcher, Config, Route, Shutdown, sentinel, shield::Shield};
use crate::router::Router;
use crate::trip_wire::TripWire;
use crate::fairing::{Fairing, Fairings};
use crate::phase::{Phase, Build, Building, Ignite, Igniting, Orbit, Orbiting};
use crate::phase::{Stateful, StateRef, State};
use crate::http::uri::{self, Origin};
use crate::http::ext::IntoOwned;
use crate::error::{Error, ErrorKind};
use crate::log::PaintExt;
/// The application server itself.
///
/// # Phases
///
/// A `Rocket` instance represents a web server and its state. It progresses
/// through three statically-enforced phases: build, ignite, orbit.
///
/// * **Build**: _application and server configuration_
///
/// This phase enables:
///
/// * setting configuration options
/// * mounting/registering routes/catchers
/// * managing state
/// * attaching fairings
///
/// This is the _only_ phase in which an instance can be modified. To finalize
/// changes, an instance is ignited via [`Rocket::ignite()`], progressing it
/// into the _ignite_ phase, or directly launched into orbit with
/// [`Rocket::launch()`] which progress the instance through ignite into
/// orbit.
///
/// * **Ignite**: _verification and finalization of configuration_
///
/// An instance in the [`Ignite`] phase is in its final configuration,
/// available via [`Rocket::config()`]. Barring user-supplied interior
/// mutation, application state is guaranteed to remain unchanged beyond this
/// point. An instance in the ignite phase can be launched into orbit to serve
/// requests via [`Rocket::launch()`].
///
/// * **Orbit**: _a running web server_
///
/// An instance in the [`Orbit`] phase represents a _running_ application,
/// actively serving requests.
///
/// # Launching
///
/// To launch a `Rocket` application, the suggested approach is to return an
/// instance of `Rocket<Build>` from a function named `rocket` marked with the
/// [`#[launch]`](crate::launch) attribute:
///
/// ```rust,no_run
/// # use rocket::launch;
/// #[launch]
/// fn rocket() -> _ {
/// rocket::build()
/// }
/// ```
///
/// This generates a `main` function with an `async` runtime that runs the
/// returned `Rocket` instance.
///
/// * **Manual Launching**
///
/// To launch an instance of `Rocket`, it _must_ progress through all three
/// phases. To progress into the ignite or launch phases, a tokio `async`
/// runtime is required. The [`#[main]`](crate::main) attribute initializes a
/// Rocket-specific tokio runtime and runs the attributed `async fn` inside of
/// it:
///
/// ```rust,no_run
/// #[rocket::main]
/// async fn main() -> Result<(), rocket::Error> {
/// let _rocket = rocket::build()
/// .ignite().await?
/// .launch().await?;
///
/// Ok(())
/// }
/// ```
///
/// Note that [`Rocket::launch()`] automatically progresses an instance of
/// `Rocket` from any phase into orbit:
///
/// ```rust,no_run
/// #[rocket::main]
/// async fn main() -> Result<(), rocket::Error> {
/// let _rocket = rocket::build().launch().await?;
/// Ok(())
/// }
/// ```
///
/// For extreme and rare cases in which [`#[main]`](crate::main) imposes
/// obstinate restrictions, use [`rocket::execute()`](crate::execute()) to
/// execute Rocket's `launch()` future.
///
/// * **Automatic Launching**
///
/// Manually progressing an instance of Rocket though its phases is only
/// necessary when either an instance's finalized state is to be inspected (in
/// the _ignite_ phase) or the instance is expected to deorbit due to
/// [`Rocket::shutdown()`]. In the more common case when neither is required,
/// the [`#[launch]`](crate::launch) attribute can be used. When applied to a
/// function that returns a `Rocket<Build>`, it automatically initializes an
/// `async` runtime and launches the function's returned instance:
///
/// ```rust,no_run
/// # use rocket::launch;
/// use rocket::{Rocket, Build};
///
/// #[launch]
/// fn rocket() -> Rocket<Build> {
/// rocket::build()
/// }
/// ```
///
/// To avoid needing to import _any_ items in the common case, the `launch`
/// attribute will infer a return type written as `_` as `Rocket<Build>`:
///
/// ```rust,no_run
/// # use rocket::launch;
/// #[launch]
/// fn rocket() -> _ {
/// rocket::build()
/// }
/// ```
pub struct Rocket<P: Phase>(pub(crate) P::State);
impl Rocket<Build> {
/// Create a new `Rocket` application using the default configuration
/// provider, [`Config::figment()`].
///
/// This method is typically called through the
/// [`rocket::build()`](crate::build) alias.
///
/// # Examples
///
/// ```rust
/// # use rocket::launch;
/// #[launch]
/// fn rocket() -> _ {
/// rocket::build()
/// }
/// ```
#[must_use]
#[inline(always)]
pub fn build() -> Self {
Rocket::custom(Config::figment())
}
/// Creates a new `Rocket` application using the supplied configuration
/// provider.
///
/// This method is typically called through the
/// [`rocket::custom()`](crate::custom()) alias.
///
/// # Example
///
/// ```rust
/// # use rocket::launch;
/// use rocket::figment::{Figment, providers::{Toml, Env, Format}};
///
/// #[launch]
/// fn rocket() -> _ {
/// let figment = Figment::from(rocket::Config::default())
/// .merge(Toml::file("MyApp.toml").nested())
/// .merge(Env::prefixed("MY_APP_").global());
///
/// rocket::custom(figment)
/// }
/// ```
#[must_use]
pub fn custom<T: Provider>(provider: T) -> Self {
// We initialize the logger here so that logging from fairings and so on
// are visible; we use the final config to set a max log-level in ignite
crate::log::init_default();
let rocket: Rocket<Build> = Rocket(Building {
figment: Figment::from(provider),
..Default::default()
});
rocket.attach(Shield::default())
}
/// Sets the configuration provider in `self` to `provider`.
///
/// A [`Figment`] generated from the current `provider` can _always_ be
/// retrieved via [`Rocket::figment()`]. However, because the provider can
/// be changed at any point prior to ignition, a [`Config`] can only be
/// retrieved in the ignite or orbit phases, or by manually extracting one
/// from a particular figment.
///
/// # Example
///
/// ```rust
/// use rocket::Config;
/// # use std::net::Ipv4Addr;
/// # use std::path::{Path, PathBuf};
/// # type Result = std::result::Result<(), rocket::Error>;
///
/// let config = Config {
/// port: 7777,
/// address: Ipv4Addr::new(18, 127, 0, 1).into(),
/// temp_dir: "/tmp/config-example".into(),
/// ..Config::debug_default()
/// };
///
/// # let _: Result = rocket::async_test(async move {
/// let rocket = rocket::custom(&config).ignite().await?;
/// assert_eq!(rocket.config().port, 7777);
/// assert_eq!(rocket.config().address, Ipv4Addr::new(18, 127, 0, 1));
/// assert_eq!(rocket.config().temp_dir.relative(), Path::new("/tmp/config-example"));
///
/// // Create a new figment which modifies _some_ keys the existing figment:
/// let figment = rocket.figment().clone()
/// .merge((Config::PORT, 8888))
/// .merge((Config::ADDRESS, "171.64.200.10"));
///
/// let rocket = rocket::custom(&config)
/// .configure(figment)
/// .ignite().await?;
///
/// assert_eq!(rocket.config().port, 8888);
/// assert_eq!(rocket.config().address, Ipv4Addr::new(171, 64, 200, 10));
/// assert_eq!(rocket.config().temp_dir.relative(), Path::new("/tmp/config-example"));
/// # Ok(())
/// # });
/// ```
#[must_use]
pub fn configure<T: Provider>(mut self, provider: T) -> Self {
self.figment = Figment::from(provider);
self
}
#[track_caller]
fn load<'a, B, T, F, M>(mut self, kind: &str, base: B, items: Vec<T>, m: M, f: F) -> Self
where B: TryInto<Origin<'a>> + Clone + fmt::Display,
B::Error: fmt::Display,
M: Fn(&Origin<'a>, T) -> Result<T, uri::Error<'static>>,
F: Fn(&mut Self, T),
T: Clone + fmt::Display,
{
let mut base = match base.clone().try_into() {
Ok(origin) => origin.into_owned(),
Err(e) => {
error!("invalid {} base: {}", kind, Paint::white(&base));
error_!("{}", e);
info_!("{} {}", "in".primary(), std::panic::Location::caller());
panic!("aborting due to {} base error", kind);
}
};
if base.query().is_some() {
warn!("query in {} base '{}' is ignored", kind, Paint::white(&base));
base.clear_query();
}
for unmounted_item in items {
let item = match m(&base, unmounted_item.clone()) {
Ok(item) => item,
Err(e) => {
error!("malformed URI in {} {}", kind, unmounted_item);
error_!("{}", e);
info_!("{} {}", Paint::white("in"), std::panic::Location::caller());
panic!("aborting due to invalid {} URI", kind);
}
};
f(&mut self, item)
}
self
}
/// Mounts all of the routes in the supplied vector at the given `base`
/// path. Mounting a route with path `path` at path `base` makes the route
/// available at `base/path`.
///
/// # Panics
///
/// Panics if either:
/// * the `base` mount point is not a valid static path: a valid origin
/// URI without dynamic parameters.
///
/// * any route's URI is not a valid origin URI.
///
/// **Note:** _This kind of panic is guaranteed not to occur if the routes
/// were generated using Rocket's code generation._
///
/// # Examples
///
/// Use the `routes!` macro to mount routes created using the code
/// generation facilities. Requests to the `/hello/world` URI will be
/// dispatched to the `hi` route.
///
/// ```rust,no_run
/// # #[macro_use] extern crate rocket;
/// #
/// #[get("/world")]
/// fn hi() -> &'static str {
/// "Hello!"
/// }
///
/// #[launch]
/// fn rocket() -> _ {
/// rocket::build().mount("/hello", routes![hi])
/// }
/// ```
///
/// Manually create a route named `hi` at path `"/world"` mounted at base
/// `"/hello"`. Requests to the `/hello/world` URI will be dispatched to the
/// `hi` route.
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// use rocket::{Request, Route, Data, route};
/// use rocket::http::Method;
///
/// fn hi<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
/// route::Outcome::from(req, "Hello!").pin()
/// }
///
/// #[launch]
/// fn rocket() -> _ {
/// let hi_route = Route::new(Method::Get, "/world", hi);
/// rocket::build().mount("/hello", vec![hi_route])
/// }
/// ```
#[must_use]
#[track_caller]
pub fn mount<'a, B, R>(self, base: B, routes: R) -> Self
where B: TryInto<Origin<'a>> + Clone + fmt::Display,
B::Error: fmt::Display,
R: Into<Vec<Route>>
{
self.load("route", base, routes.into(),
|base, route| route.map_base(|old| format!("{}{}", base, old)),
|r, route| r.0.routes.push(route))
}
/// Registers all of the catchers in the supplied vector, scoped to `base`.
///
/// # Panics
///
/// Panics if `base` is not a valid static path: a valid origin URI without
/// dynamic parameters.
///
/// # Examples
///
/// ```rust,no_run
/// # #[macro_use] extern crate rocket;
/// use rocket::Request;
///
/// #[catch(500)]
/// fn internal_error() -> &'static str {
/// "Whoops! Looks like we messed up."
/// }
///
/// #[catch(404)]
/// fn not_found(req: &Request) -> String {
/// format!("I couldn't find '{}'. Try something else?", req.uri())
/// }
///
/// #[launch]
/// fn rocket() -> _ {
/// rocket::build().register("/", catchers![internal_error, not_found])
/// }
/// ```
#[must_use]
pub fn register<'a, B, C>(self, base: B, catchers: C) -> Self
where B: TryInto<Origin<'a>> + Clone + fmt::Display,
B::Error: fmt::Display,
C: Into<Vec<Catcher>>
{
self.load("catcher", base, catchers.into(),
|base, catcher| catcher.map_base(|old| format!("{}{}", base, old)),
|r, catcher| r.0.catchers.push(catcher))
}
/// Add `state` to the state managed by this instance of Rocket.
///
/// This method can be called any number of times as long as each call
/// refers to a different `T`.
///
/// Managed state can be retrieved by any request handler via the
/// [`State`](crate::State) request guard. In particular, if a value of type `T`
/// is managed by Rocket, adding `State<T>` to the list of arguments in a
/// request handler instructs Rocket to retrieve the managed value.
///
/// # Panics
///
/// Panics if state of type `T` is already being managed.
///
/// # Example
///
/// ```rust,no_run
/// # #[macro_use] extern crate rocket;
/// use rocket::State;
///
/// struct MyInt(isize);
/// struct MyString(String);
///
/// #[get("/int")]
/// fn int(state: &State<MyInt>) -> String {
/// format!("The stateful int is: {}", state.0)
/// }
///
/// #[get("/string")]
/// fn string(state: &State<MyString>) -> &str {
/// &state.0
/// }
///
/// #[launch]
/// fn rocket() -> _ {
/// rocket::build()
/// .manage(MyInt(10))
/// .manage(MyString("Hello, managed state!".to_string()))
/// .mount("/", routes![int, string])
/// }
/// ```
#[must_use]
pub fn manage<T>(self, state: T) -> Self
where T: Send + Sync + 'static
{
let type_name = std::any::type_name::<T>();
if !self.state.set(state) {
error!("state for type '{}' is already being managed", type_name);
panic!("aborting due to duplicated managed state");
}
self
}
/// Attaches a fairing to this instance of Rocket. No fairings are eagerly
/// executed; fairings are executed at their appropriate time.
///
/// If the attached fairing is _fungible_ and a fairing of the same name
/// already exists, this fairing replaces it.
///
/// # Example
///
/// ```rust,no_run
/// # #[macro_use] extern crate rocket;
/// use rocket::Rocket;
/// use rocket::fairing::AdHoc;
///
/// #[launch]
/// fn rocket() -> _ {
/// rocket::build()
/// .attach(AdHoc::on_liftoff("Liftoff Message", |_| Box::pin(async {
/// println!("We have liftoff!");
/// })))
/// }
/// ```
#[must_use]
pub fn attach<F: Fairing>(mut self, fairing: F) -> Self {
self.fairings.add(Box::new(fairing));
self
}
/// Returns a `Future` that transitions this instance of `Rocket` into the
/// _ignite_ phase.
///
/// When `await`ed, the future runs all _ignite_ fairings in serial,
/// [attach](Rocket::attach()) order, and verifies that `self` represents a
/// valid instance of `Rocket` ready for launch. This means that:
///
/// * All ignite fairings succeeded.
/// * A valid [`Config`] was extracted from [`Rocket::figment()`].
/// * If `secrets` are enabled, the extracted `Config` contains a safe
/// secret key.
/// * There are no [`Route#collisions`] or [`Catcher#collisions`]
/// collisions.
/// * No [`Sentinel`](crate::Sentinel) triggered an abort.
///
/// If any of these conditions fail to be met, a respective [`Error`] is
/// returned.
///
/// [configured]: Rocket::figment()
///
/// # Example
///
/// ```rust
/// use rocket::fairing::AdHoc;
///
/// #[rocket::main]
/// async fn main() -> Result<(), rocket::Error> {
/// let rocket = rocket::build()
/// # .configure(rocket::Config::debug_default())
/// .attach(AdHoc::on_ignite("Manage State", |rocket| async move {
/// rocket.manage(String::from("managed string"))
/// }));
///
/// // No fairings are run until ignition occurs.
/// assert!(rocket.state::<String>().is_none());
///
/// let rocket = rocket.ignite().await?;
/// assert_eq!(rocket.state::<String>().unwrap(), "managed string");
///
/// Ok(())
/// }
/// ```
pub async fn ignite(mut self) -> Result<Rocket<Ignite>, Error> {
self = Fairings::handle_ignite(self).await;
self.fairings.audit().map_err(|f| ErrorKind::FailedFairings(f.to_vec()))?;
// Extract the configuration; initialize the logger.
#[allow(unused_mut)]
let mut config = Config::try_from(&self.figment).map_err(ErrorKind::Config)?;
crate::log::init(&config);
// Check for safely configured secrets.
#[cfg(feature = "secrets")]
if !config.secret_key.is_provided() {
if config.profile != Config::DEBUG_PROFILE {
return Err(Error::new(ErrorKind::InsecureSecretKey(config.profile.clone())));
}
if config.secret_key.is_zero() {
config.secret_key = crate::config::SecretKey::generate()
.unwrap_or_else(crate::config::SecretKey::zero);
}
} else if config.known_secret_key_used() {
warn!("The configured `secret_key` is exposed and insecure.");
warn_!("The configured key is publicly published and thus insecure.");
warn_!("Try generating a new key with `head -c64 /dev/urandom | base64`.");
}
// Initialize the router; check for collisions.
let mut router = Router::new();
self.routes.clone().into_iter().for_each(|r| router.add_route(r));
self.catchers.clone().into_iter().for_each(|c| router.add_catcher(c));
router.finalize().map_err(ErrorKind::Collisions)?;
// Finally, freeze managed state.
self.state.freeze();
// Log everything we know: config, routes, catchers, fairings.
// TODO: Store/print managed state type names?
config.pretty_print(self.figment());
log_items("📬 ", "Routes", self.routes(), |r| &r.uri.base, |r| &r.uri);
log_items("🥅 ", "Catchers", self.catchers(), |c| &c.base, |c| &c.base);
self.fairings.pretty_print();
// Ignite the rocket.
let rocket: Rocket<Ignite> = Rocket(Igniting {
router, config,
shutdown: Shutdown(TripWire::new()),
figment: self.0.figment,
fairings: self.0.fairings,
state: self.0.state,
});
// Query the sentinels, abort if requested.
let sentinels = rocket.routes().flat_map(|r| r.sentinels.iter());
sentinel::query(sentinels, &rocket).map_err(ErrorKind::SentinelAborts)?;
Ok(rocket)
}
}
fn log_items<T, I, B, O>(e: &str, t: &str, items: I, base: B, origin: O)
where T: fmt::Display + Copy, I: Iterator<Item = T>,
B: Fn(&T) -> &Origin<'_>, O: Fn(&T) -> &Origin<'_>
{
let mut items: Vec<_> = items.collect();
if !items.is_empty() {
launch_meta!("{}{}:", e.emoji(), t.magenta());
}
items.sort_by_key(|i| origin(i).path().as_str().chars().count());
items.sort_by_key(|i| origin(i).path().segments().len());
items.sort_by_key(|i| base(i).path().as_str().chars().count());
items.sort_by_key(|i| base(i).path().segments().len());
items.iter().for_each(|i| launch_meta_!("{}", i));
}
impl Rocket<Ignite> {
/// Returns the finalized, active configuration. This is guaranteed to
/// remain stable through ignition and into orbit.
///
/// # Example
///
/// ```rust,no_run
/// #[rocket::main]
/// async fn main() -> Result<(), rocket::Error> {
/// let rocket = rocket::build().ignite().await?;
/// let config = rocket.config();
/// Ok(())
/// }
/// ```
pub fn config(&self) -> &Config {
&self.config
}
/// Returns a handle which can be used to trigger a shutdown and detect a
/// triggered shutdown.
///
/// A completed graceful shutdown resolves the future returned by
/// [`Rocket::launch()`]. If [`Shutdown::notify()`] is called _before_ an
/// instance is launched, it will be immediately shutdown after liftoff. See
/// [`Shutdown`] and [`config::Shutdown`](crate::config::Shutdown) for
/// details on graceful shutdown.
///
/// # Example
///
/// ```rust,no_run
/// # use std::time::Duration;
/// use rocket::tokio::{self, time};
///
/// #[rocket::main]
/// async fn main() -> Result<(), rocket::Error> {
/// let rocket = rocket::build().ignite().await?;
///
/// let shutdown = rocket.shutdown();
/// tokio::spawn(async move {
/// time::sleep(time::Duration::from_secs(5)).await;
/// shutdown.notify();
/// });
///
/// // The `launch()` future resolves after ~5 seconds.
/// let result = rocket.launch().await;
/// assert!(result.is_ok());
///
/// Ok(())
/// }
/// ```
pub fn shutdown(&self) -> Shutdown {
self.shutdown.clone()
}
fn into_orbit(self) -> Rocket<Orbit> {
Rocket(Orbiting {
router: self.0.router,
fairings: self.0.fairings,
figment: self.0.figment,
config: self.0.config,
state: self.0.state,
shutdown: self.0.shutdown,
})
}
async fn _local_launch(self) -> Rocket<Orbit> {
let rocket = self.into_orbit();
rocket.fairings.handle_liftoff(&rocket).await;
launch_info!("{}{}", "🚀 ".emoji(), "Rocket has launched locally".primary().bold());
rocket
}
async fn _launch(self) -> Result<Rocket<Ignite>, Error> {
self.into_orbit()
.default_tcp_http_server(|rkt| Box::pin(async move {
rkt.fairings.handle_liftoff(&rkt).await;
let proto = rkt.config.tls_enabled().then(|| "https").unwrap_or("http");
let socket_addr = SocketAddr::new(rkt.config.address, rkt.config.port);
let addr = format!("{}://{}", proto, socket_addr);
launch_info!("{}{} {}",
"🚀 ".emoji(),
"Rocket has launched from".bold().primary().linger(),
addr.underline());
}))
.await
.map(|rocket| rocket.into_ignite())
}
}
impl Rocket<Orbit> {
pub(crate) fn into_ignite(self) -> Rocket<Ignite> {
Rocket(Igniting {
router: self.0.router,
fairings: self.0.fairings,
figment: self.0.figment,
config: self.0.config,
state: self.0.state,
shutdown: self.0.shutdown,
})
}
/// Returns the finalized, active configuration. This is guaranteed to
/// remain stable after [`Rocket::ignite()`], through ignition and into
/// orbit.
///
/// # Example
///
/// ```rust,no_run
/// # #[macro_use] extern crate rocket;
/// use rocket::fairing::AdHoc;
///
/// #[launch]
/// fn rocket() -> _ {
/// rocket::build()
/// .attach(AdHoc::on_liftoff("Config", |rocket| Box::pin(async move {
/// println!("Rocket launch config: {:?}", rocket.config());
/// })))
/// }
/// ```
pub fn config(&self) -> &Config {
&self.config
}
/// Returns a handle which can be used to trigger a shutdown and detect a
/// triggered shutdown.
///
/// A completed graceful shutdown resolves the future returned by
/// [`Rocket::launch()`]. See [`Shutdown`] and
/// [`config::Shutdown`](crate::config::Shutdown) for details on graceful
/// shutdown.
///
/// # Example
///
/// ```rust,no_run
/// # #[macro_use] extern crate rocket;
/// use rocket::tokio::{self, time};
/// use rocket::fairing::AdHoc;
///
/// #[launch]
/// fn rocket() -> _ {
/// rocket::build()
/// .attach(AdHoc::on_liftoff("Shutdown", |rocket| Box::pin(async move {
/// let shutdown = rocket.shutdown();
/// tokio::spawn(async move {
/// time::sleep(time::Duration::from_secs(5)).await;
/// shutdown.notify();
/// });
/// })))
/// }
/// ```
pub fn shutdown(&self) -> Shutdown {
self.shutdown.clone()
}
}
impl<P: Phase> Rocket<P> {
/// Returns an iterator over all of the routes mounted on this instance of
/// Rocket. The order is unspecified.
///
/// # Example
///
/// ```rust
/// # use rocket::*;
/// use rocket::Rocket;
/// use rocket::fairing::AdHoc;
///
/// #[get("/hello")]
/// fn hello() -> &'static str {
/// "Hello, world!"
/// }
///
/// let rocket = rocket::build()
/// .mount("/", routes![hello])
/// .mount("/hi", routes![hello]);
///
/// assert_eq!(rocket.routes().count(), 2);
/// assert!(rocket.routes().any(|r| r.uri == "/hello"));
/// assert!(rocket.routes().any(|r| r.uri == "/hi/hello"));
/// ```
pub fn routes(&self) -> impl Iterator<Item = &Route> {
match self.0.as_state_ref() {
StateRef::Build(p) => Either::Left(p.routes.iter()),
StateRef::Ignite(p) => Either::Right(p.router.routes()),
StateRef::Orbit(p) => Either::Right(p.router.routes()),
}
}
/// Returns an iterator over all of the catchers registered on this instance
/// of Rocket. The order is unspecified.
///
/// # Example
///
/// ```rust
/// # use rocket::*;
/// use rocket::Rocket;
/// use rocket::fairing::AdHoc;
///
/// #[catch(404)] fn not_found() -> &'static str { "Nothing here, sorry!" }
/// #[catch(500)] fn just_500() -> &'static str { "Whoops!?" }
/// #[catch(default)] fn some_default() -> &'static str { "Everything else." }
///
/// let rocket = rocket::build()
/// .register("/foo", catchers![not_found])
/// .register("/", catchers![just_500, some_default]);
///
/// assert_eq!(rocket.catchers().count(), 3);
/// assert!(rocket.catchers().any(|c| c.code == Some(404) && c.base == "/foo"));
/// assert!(rocket.catchers().any(|c| c.code == Some(500) && c.base == "/"));
/// assert!(rocket.catchers().any(|c| c.code == None && c.base == "/"));
/// ```
pub fn catchers(&self) -> impl Iterator<Item = &Catcher> {
match self.0.as_state_ref() {
StateRef::Build(p) => Either::Left(p.catchers.iter()),
StateRef::Ignite(p) => Either::Right(p.router.catchers()),
StateRef::Orbit(p) => Either::Right(p.router.catchers()),
}
}
/// Returns `Some` of the managed state value for the type `T` if it is
/// being managed by `self`. Otherwise, returns `None`.
///
/// # Example
///
/// ```rust
/// #[derive(PartialEq, Debug)]
/// struct MyState(&'static str);
///
/// let rocket = rocket::build().manage(MyState("hello!"));
/// assert_eq!(rocket.state::<MyState>().unwrap(), &MyState("hello!"));
/// ```
pub fn state<T: Send + Sync + 'static>(&self) -> Option<&T> {
match self.0.as_state_ref() {
StateRef::Build(p) => p.state.try_get(),
StateRef::Ignite(p) => p.state.try_get(),
StateRef::Orbit(p) => p.state.try_get(),
}
}
/// Returns the figment derived from the configuration provider set for
/// `self`. To extract a typed config, prefer to use
/// [`AdHoc::config()`](crate::fairing::AdHoc::config()).
///
/// # Example
///
/// ```rust
/// let rocket = rocket::build();
/// let figment = rocket.figment();
/// ```
pub fn figment(&self) -> &Figment {
match self.0.as_state_ref() {
StateRef::Build(p) => &p.figment,
StateRef::Ignite(p) => &p.figment,
StateRef::Orbit(p) => &p.figment,
}
}
pub(crate) async fn local_launch(self) -> Result<Rocket<Orbit>, Error> {
let rocket = match self.0.into_state() {
State::Build(s) => Rocket::from(s).ignite().await?._local_launch().await,
State::Ignite(s) => Rocket::from(s)._local_launch().await,
State::Orbit(s) => Rocket::from(s)
};
Ok(rocket)
}
/// Returns a `Future` that transitions this instance of `Rocket` from any
/// phase into the _orbit_ phase. When `await`ed, the future drives the
/// server forward, listening for and dispatching requests to mounted routes
/// and catchers.
///
/// In addition to all of the processes that occur during
/// [ignition](Rocket::ignite()), a successful launch results in _liftoff_
/// fairings being executed _after_ binding to any respective network
/// interfaces but before serving the first request. Liftoff fairings are
/// run concurrently; resolution of all fairings is `await`ed before
/// resuming request serving.
///
/// The `Future` resolves as an `Err` if any of the following occur:
///
/// * there is an error igniting; see [`Rocket::ignite()`].
/// * there is an I/O error starting the server.
/// * an unrecoverable, system-level error occurs while running.
///
/// The `Future` resolves as an `Ok` if any of the following occur:
///
/// * graceful shutdown via [`Shutdown::notify()`] completes.
///
/// The returned value on `Ok(())` is previously running instance.
///
/// The `Future` does not resolve otherwise.
///
/// # Error
///
/// If there is a problem starting the application or the application fails
/// unexpectedly while running, an [`Error`] is returned. Note that a value
/// of type `Error` panics if dropped without first being inspected. See the
/// [`Error`] documentation for more information.
///
/// # Example
///
/// ```rust,no_run
/// #[rocket::main]
/// async fn main() {
/// let result = rocket::build().launch().await;
///
/// // this is reachable only after `Shutdown::notify()` or `Ctrl+C`.
/// println!("Rocket: deorbit.");
/// }
/// ```
pub async fn launch(self) -> Result<Rocket<Ignite>, Error> {
match self.0.into_state() {
State::Build(s) => Rocket::from(s).ignite().await?._launch().await,
State::Ignite(s) => Rocket::from(s)._launch().await,
State::Orbit(s) => Ok(Rocket::from(s).into_ignite())
}
}
}
#[doc(hidden)]
impl<P: Phase> Deref for Rocket<P> {
type Target = P::State;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[doc(hidden)]
impl<P: Phase> DerefMut for Rocket<P> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<P: Phase> fmt::Debug for Rocket<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}