Replace 'Manifest' with 'Cargo'.

This is largely an internal change. Prior to this commit, the 'Manifest'
type, now replaced with the 'Cargo' type, robbed responsibility from the
core 'Rocket' type. This new construction restores the previous
responsibility and makes it clear that 'Cargo' is _only_ for freezing,
and representing the stability of, Rocket's internal state.
This commit is contained in:
Sergio Benitez 2020-06-27 22:59:40 -07:00
parent dd5b518cc2
commit d89c7024ed
28 changed files with 534 additions and 404 deletions

View File

@ -118,8 +118,8 @@ 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 and there is at least one connection in the pool.
pub fn get_one(manifest: &::rocket::Manifest) -> Option<Self> {
manifest.state::<#pool_type>()
pub fn get_one(cargo: &::rocket::Cargo) -> Option<Self> {
cargo.state::<#pool_type>()
.and_then(|pool| pool.0.get().ok())
.map(#guard_type)
}

View File

@ -239,7 +239,7 @@
//! Returns a fairing that initializes the associated database connection
//! pool.
//!
//! * `fn get_one(&Manifest) -> Option<Self>`
//! * `fn get_one(&Cargo) -> Option<Self>`
//!
//! Retrieves a connection from the configured pool. Returns `Some` as long
//! as `Self::fairing()` has been attached and there is at least one

View File

@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use rocket::http::uncased::UncasedStr;
use rocket::fairing::{Fairing, Info, Kind};
use rocket::{Manifest, Request, Response};
use rocket::{Cargo, Request, Response};
use crate::helmet::*;
@ -201,9 +201,9 @@ impl Fairing for SpaceHelmet {
self.apply(res);
}
fn on_launch(&self, manifest: &Manifest) {
if manifest.config().tls_enabled()
&& !manifest.config().environment.is_dev()
fn on_launch(&self, cargo: &Cargo) {
if cargo.config().tls_enabled()
&& !cargo.config().environment.is_dev()
&& !self.is_enabled::<Hsts>()
{
warn_!("Space Helmet: deploying with TLS without enabling HSTS.");

View File

@ -141,7 +141,7 @@ use serde_json::{Value, to_value};
use std::borrow::Cow;
use std::path::PathBuf;
use rocket::Manifest;
use rocket::Cargo;
use rocket::request::Request;
use rocket::fairing::Fairing;
use rocket::response::{self, Content, Responder};
@ -340,15 +340,15 @@ impl Template {
///
/// # context.insert("test", "test");
/// # #[allow(unused_variables)]
/// let template = Template::show(client.manifest(), "index", context);
/// let template = Template::show(client.cargo(), "index", context);
/// # });
/// }
/// ```
#[inline]
pub fn show<S, C>(manifest: &Manifest, name: S, context: C) -> Option<String>
pub fn show<S, C>(cargo: &Cargo, name: S, context: C) -> Option<String>
where S: Into<Cow<'static, str>>, C: Serialize
{
let ctxt = manifest.state::<ContextManager>().map(ContextManager::context).or_else(|| {
let ctxt = cargo.state::<ContextManager>().map(ContextManager::context).or_else(|| {
warn!("Uninitialized template context: missing fairing.");
info!("To use templates, you must attach `Template::fairing()`.");
info!("See the `Template` documentation for more information.");

View File

@ -52,17 +52,17 @@ mod templates_tests {
#[rocket::async_test]
async fn test_tera_templates() {
let mut rocket = rocket();
let manifest = rocket.inspect().await;
let cargo = rocket.inspect().await;
let mut map = HashMap::new();
map.insert("title", "_test_");
map.insert("content", "<script />");
// Test with a txt file, which shouldn't escape.
let template = Template::show(manifest, "tera/txt_test", &map);
let template = Template::show(cargo, "tera/txt_test", &map);
assert_eq!(template, Some(UNESCAPED_EXPECTED.into()));
// Now with an HTML file, which should.
let template = Template::show(manifest, "tera/html_test", &map);
let template = Template::show(cargo, "tera/html_test", &map);
assert_eq!(template, Some(ESCAPED_EXPECTED.into()));
}
@ -97,13 +97,13 @@ mod templates_tests {
#[rocket::async_test]
async fn test_handlebars_templates() {
let mut rocket = rocket();
let manifest = rocket.inspect().await;
let cargo = rocket.inspect().await;
let mut map = HashMap::new();
map.insert("title", "_test_");
map.insert("content", "<script /> hi");
// Test with a txt file, which shouldn't escape.
let template = Template::show(manifest, "hbs/test", &map);
let template = Template::show(cargo, "hbs/test", &map);
assert_eq!(template, Some(EXPECTED.into()));
}
@ -153,7 +153,7 @@ mod templates_tests {
}
// verify that the initial content is correct
let initial_rendered = Template::show(client.manifest(), RELOAD_TEMPLATE, ());
let initial_rendered = Template::show(client.cargo(), RELOAD_TEMPLATE, ());
assert_eq!(initial_rendered, Some(INITIAL_TEXT.into()));
// write a change to the file
@ -164,7 +164,7 @@ mod templates_tests {
client.get("/").dispatch().await;
// if the new content is correct, we are done
let new_rendered = Template::show(client.manifest(), RELOAD_TEMPLATE, ());
let new_rendered = Template::show(client.cargo(), RELOAD_TEMPLATE, ());
if new_rendered == Some(NEW_TEXT.into()) {
write_file(&reload_path, INITIAL_TEXT);
return;

View File

@ -73,6 +73,7 @@ impl EntryAttr for Launch {
___rocket
});
// FIXME: Don't duplicate the `#block` here!
let (vis, mut sig) = (&f.vis, f.sig.clone());
sig.ident = syn::Ident::new("main", sig.ident.span());
sig.output = syn::ReturnType::Default;

View File

@ -114,7 +114,7 @@ mod benches {
// Hold all of the requests we're going to make during the benchmark.
let mut requests = vec![];
for route in client.manifest().routes() {
for route in client.cargo().routes() {
let request = client.req(route.method, route.uri.path());
requests.push(request);
}

View File

@ -123,6 +123,15 @@ impl fmt::Display for Catcher {
}
}
impl fmt::Debug for Catcher {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Catcher")
.field("code", &self.code)
.field("default", &self.is_default)
.finish()
}
}
macro_rules! error_page_template {
($code:expr, $name:expr, $description:expr) => (
concat!(r#"

View File

@ -2,7 +2,7 @@ use std::io::{self, Cursor};
use std::pin::Pin;
use std::task::{Poll, Context};
use futures::{ready, future::BoxFuture, stream::Stream};
use futures::{ready, Future, future::BoxFuture, stream::Stream};
use tokio::io::{AsyncRead, AsyncReadExt as _};
use crate::http::hyper;
@ -111,3 +111,53 @@ impl AsyncRead for AsyncReadBody {
}
}
}
// The code below was adapted from the `replace_with` crate and reproduced here
// under the rights granted by the MIT license. The code is copyright the
// `replace_with` developers. See LICENSE-MIT for the full text.
struct OnUnwind<F: FnOnce()>(std::mem::ManuallyDrop<F>);
impl<F: FnOnce()> Drop for OnUnwind<F> {
#[inline(always)]
fn drop(&mut self) {
(unsafe { std::ptr::read(&*self.0) })();
}
}
#[inline(always)]
pub async fn async_on_unwind<F, Fut, T, P: FnOnce()>(f: F, p: P) -> T
where F: FnOnce() -> Fut, Fut: Future<Output = T>,
{
let x = OnUnwind(std::mem::ManuallyDrop::new(p));
let t = f().await;
let _ = unsafe { std::ptr::read(&*x.0) };
std::mem::forget(x);
t
}
#[inline]
pub async fn async_replace_with_or_else<T, Fut, F, D>(dest: &mut T, d: D, f: F)
where Fut: Future<Output = T>,
F: FnOnce(T) -> Fut,
D: FnOnce() -> T,
{
unsafe {
let old = std::ptr::read(dest);
let new = async_on_unwind(
|| async move { f(old).await },
|| std::ptr::write(dest, d()),
).await;
std::ptr::write(dest, new);
}
}
#[inline]
pub async fn async_replace_with<T, Fut, F>(dest: &mut T, f: F)
where Fut: Future<Output = T>,
F: FnOnce(T) -> Fut,
{
async_replace_with_or_else(dest, || std::process::abort(), f).await
}

View File

@ -2,7 +2,7 @@ use std::sync::Mutex;
use futures::future::{Future, BoxFuture};
use crate::{Manifest, Rocket, Request, Response, Data};
use crate::{Cargo, Rocket, Request, Response, Data};
use crate::fairing::{Fairing, Kind, Info};
/// A ad-hoc fairing that can be created from a function or closure.
@ -67,7 +67,7 @@ enum AdHocKind {
-> BoxFuture<'static, Result<Rocket, Rocket>> + Send + 'static>>>),
/// An ad-hoc **launch** fairing. Called just before Rocket launches.
Launch(Mutex<Option<Box<dyn FnOnce(&Manifest) + Send + 'static>>>),
Launch(Mutex<Option<Box<dyn FnOnce(&Cargo) + Send + 'static>>>),
/// An ad-hoc **request** fairing. Called when a request is received.
Request(Box<dyn for<'a> Fn(&'a mut Request<'_>, &'a Data)
@ -116,7 +116,7 @@ impl AdHoc {
/// });
/// ```
pub fn on_launch<F: Send + 'static>(name: &'static str, f: F) -> AdHoc
where F: FnOnce(&Manifest)
where F: FnOnce(&Cargo)
{
AdHoc { name, kind: AdHocKind::Launch(Mutex::new(Some(Box::new(f)))) }
}
@ -204,11 +204,11 @@ impl Fairing for AdHoc {
}
}
fn on_launch(&self, manifest: &Manifest) {
fn on_launch(&self, state: &Cargo) {
if let AdHocKind::Launch(ref mutex) = self.kind {
let mut opt = mutex.lock().expect("AdHoc::Launch lock");
let f = opt.take().expect("internal error: `on_launch` one-call invariant broken");
f(manifest)
f(state)
}
}

View File

@ -1,4 +1,4 @@
use crate::{Manifest, Rocket, Request, Response, Data};
use crate::{Cargo, Rocket, Request, Response, Data};
use crate::fairing::{Fairing, Kind};
use crate::logger::PaintExt;
@ -52,9 +52,9 @@ impl Fairings {
}
#[inline(always)]
pub fn handle_launch(&self, manifest: &Manifest) {
pub fn handle_launch(&self, cargo: &Cargo) {
for &i in &self.launch {
self.all_fairings[i].on_launch(manifest);
self.all_fairings[i].on_launch(cargo);
}
}

View File

@ -22,6 +22,7 @@ use std::ops::BitOr;
/// }
/// # ;
/// ```
#[derive(Debug)]
pub struct Info {
/// The name of the fairing.
pub name: &'static str,

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::{Manifest, Rocket, Request, Response, Data};
use crate::{Cargo, Rocket, Request, Response, Data};
mod fairings;
mod ad_hoc;
@ -196,7 +196,7 @@ pub use self::info_kind::{Info, Kind};
/// decorated with an attribute of `#[rocket::async_trait]`:
///
/// ```rust
/// use rocket::{Manifest, Rocket, Request, Data, Response};
/// use rocket::{Cargo, Rocket, Request, Data, Response};
/// use rocket::fairing::{Fairing, Info, Kind};
///
/// # struct MyType;
@ -212,7 +212,7 @@ pub use self::info_kind::{Info, Kind};
/// # unimplemented!()
/// }
///
/// fn on_launch(&self, manifest: &Manifest) {
/// fn on_launch(&self, cargo: &Cargo) {
/// /* ... */
/// # unimplemented!()
/// }
@ -421,14 +421,14 @@ pub trait Fairing: Send + Sync + 'static {
///
/// This method is called just prior to launching the application if
/// `Kind::Launch` is in the `kind` field of the `Info` structure for this
/// fairing. The `Manifest` parameter corresponds to the application that
/// fairing. The `Cargo` parameter corresponds to the application that
/// will be launched.
///
/// ## Default Implementation
///
/// The default implementation of this method does nothing.
#[allow(unused_variables)]
fn on_launch(&self, manifest: &Manifest) {}
fn on_launch(&self, cargo: &Cargo) {}
/// The request callback.
///
@ -470,8 +470,8 @@ impl<T: Fairing> Fairing for std::sync::Arc<T> {
}
#[inline]
fn on_launch(&self, manifest: &Manifest) {
(self as &T).on_launch(manifest)
fn on_launch(&self, cargo: &Cargo) {
(self as &T).on_launch(cargo)
}
#[inline]

View File

@ -139,7 +139,7 @@ mod ext;
pub use crate::router::Route;
pub use crate::request::{Request, State};
pub use crate::catcher::Catcher;
pub use crate::rocket::{Manifest, Rocket};
pub use crate::rocket::{Cargo, Rocket};
/// Alias to [`Rocket::ignite()`] Creates a new instance of `Rocket`.
pub fn ignite() -> Rocket {

View File

@ -1,7 +1,7 @@
use std::sync::RwLock;
use std::borrow::Cow;
use crate::rocket::{Rocket, Manifest};
use crate::rocket::{Rocket, Cargo};
use crate::local::LocalRequest;
use crate::http::{Method, private::CookieJar};
use crate::error::LaunchError;
@ -70,7 +70,7 @@ use crate::error::LaunchError;
/// [`put()`]: #method.put
/// [`post()`]: #method.post
pub struct Client {
manifest: Manifest,
cargo: Cargo,
pub(crate) cookies: Option<RwLock<CookieJar>>,
}
@ -78,16 +78,14 @@ impl Client {
/// Constructs a new `Client`. If `tracked` is `true`, an empty `CookieJar`
/// is created for cookie tracking. Otherwise, the internal `CookieJar` is
/// set to `None`.
async fn _new(rocket: Rocket, tracked: bool) -> Result<Client, LaunchError> {
let mut manifest = rocket.actualize_and_take_manifest().await;
manifest.prelaunch_check()?;
async fn _new(mut rocket: Rocket, tracked: bool) -> Result<Client, LaunchError> {
rocket.prelaunch_check().await?;
let cookies = match tracked {
true => Some(RwLock::new(CookieJar::new())),
false => None
};
Ok(Client { manifest, cookies })
Ok(Client { cargo: rocket.into_cargo().await, cookies })
}
/// Construct a new `Client` from an instance of `Rocket` with cookie
@ -153,7 +151,7 @@ impl Client {
Client::_new(rocket, false).await
}
/// Returns a reference to the `Manifest` of the `Rocket` this client is
/// Returns a reference to the `Rocket` of the `Rocket` this client is
/// creating requests for.
///
/// # Example
@ -165,13 +163,32 @@ impl Client {
/// let my_rocket = rocket::ignite();
/// let client = Client::new(my_rocket).await.expect("valid rocket");
///
/// // get access to the manifest within `client`
/// let manifest = client.manifest();
/// let rocket = client.rocket();
/// # });
/// ```
#[inline(always)]
pub fn manifest(&self) -> &Manifest {
&self.manifest
pub fn rocket(&self) -> &Rocket {
&*self.cargo
}
/// Returns a reference to the `Rocket` of the `Rocket` this client is
/// creating requests for.
///
/// # Example
///
/// ```rust
/// use rocket::local::Client;
///
/// # rocket::async_test(async {
/// let my_rocket = rocket::ignite();
/// let client = Client::new(my_rocket).await.expect("valid rocket");
///
/// let cargo = client.cargo();
/// # });
/// ```
#[inline(always)]
pub fn cargo(&self) -> &Cargo {
&self.cargo
}
/// Create a local `GET` request to the URI `uri`.

View File

@ -105,7 +105,7 @@ impl<'c> LocalRequest<'c> {
uri: Cow<'c, str>
) -> LocalRequest<'c> {
// We set a dummy string for now and check the user's URI on dispatch.
let request = Request::new(client.manifest(), method, Origin::dummy());
let request = Request::new(client.rocket(), method, Origin::dummy());
// Set up any cookies we know about.
if let Some(ref jar) = client.cookies {
@ -428,12 +428,12 @@ impl<'c> LocalRequest<'c> {
request.set_uri(uri.into_owned());
} else {
error!("Malformed request URI: {}", uri);
let res = client.manifest().handle_error(Status::BadRequest, request).await;
let res = client.rocket().handle_error(Status::BadRequest, request).await;
return LocalResponse { _request: owned_request, response: res };
}
// Actually dispatch the request.
let response = client.manifest().dispatch(request, Data::local(data)).await;
let response = client.rocket().dispatch(request, Data::local(data)).await;
// If the client is tracking cookies, updates the internal cookie jar
// with the changes reflected by `response`.

View File

@ -11,7 +11,7 @@ use futures::future::BoxFuture;
use crate::request::{FromParam, FromSegments, FromRequest, Outcome};
use crate::request::{FromFormValue, FormItems, FormItem};
use crate::rocket::{Rocket, Manifest};
use crate::rocket::Rocket;
use crate::router::Route;
use crate::config::{Config, Limits};
use crate::http::{hyper, uri::{Origin, Segments}};
@ -60,7 +60,7 @@ impl<'r> Request<'r> {
/// Create a new `Request` with the given `method` and `uri`.
#[inline(always)]
pub(crate) fn new<'s: 'r>(
manifest: &'r Manifest,
rocket: &'r Rocket,
method: Method,
uri: Origin<'s>
) -> Request<'r> {
@ -72,8 +72,8 @@ impl<'r> Request<'r> {
state: RequestState {
path_segments: SmallVec::new(),
query_items: None,
config: &manifest.config,
managed: &manifest.state,
config: &rocket.config,
managed: &rocket.managed_state,
route: RwLock::new(None),
cookies: Mutex::new(Some(CookieJar::new())),
accept: Storage::new(),
@ -749,7 +749,7 @@ impl<'r> Request<'r> {
pub fn example<F: Fn(&mut Request<'_>)>(method: Method, uri: &str, f: F) {
let rocket = Rocket::custom(Config::development());
let uri = Origin::parse(uri).expect("invalid URI in example");
let mut request = Request::new(rocket._manifest(), method, uri);
let mut request = Request::new(&rocket, method, uri);
f(&mut request);
}
@ -832,7 +832,7 @@ impl<'r> Request<'r> {
/// Convert from Hyper types into a Rocket Request.
pub(crate) fn from_hyp(
manifest: &'r Manifest,
rocket: &'r Rocket,
h_method: hyper::Method,
h_headers: hyper::HeaderMap<hyper::HeaderValue>,
h_uri: &'r hyper::Uri,
@ -854,7 +854,7 @@ impl<'r> Request<'r> {
let uri = Origin::parse(uri).map_err(|e| e.to_string())?;
// Construct the request object.
let mut request = Request::new(manifest, method, uri);
let mut request = Request::new(rocket, method, uri);
request.set_remote(h_addr);
// Set the request cookies, if they exist.

View File

@ -1,6 +1,6 @@
use std::ops::Deref;
use crate::rocket::Manifest;
use crate::rocket::Cargo;
use crate::request::{self, FromRequest, Request};
use crate::outcome::Outcome;
use crate::http::Status;
@ -150,18 +150,18 @@ impl<'r, T: Send + Sync + 'static> State<'r, T> {
///
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite().manage(Managed(7));
/// let manifest = rocket.inspect().await;
/// let cargo = rocket.inspect().await;
///
/// let state: Option<State<Managed>> = State::from(manifest);
/// let state: Option<State<Managed>> = State::from(cargo);
/// assert_eq!(state.map(|s| s.inner()), Some(&Managed(7)));
///
/// let state: Option<State<Unmanaged>> = State::from(manifest);
/// let state: Option<State<Unmanaged>> = State::from(cargo);
/// assert_eq!(state, None);
/// # });
/// ```
#[inline(always)]
pub fn from(manifest: &'r Manifest) -> Option<Self> {
manifest.state().map(State)
pub fn from(rocket: &'r Cargo) -> Option<Self> {
rocket.state().map(State)
}
}

View File

@ -22,7 +22,7 @@ macro_rules! assert_headers {
// Dispatch the request and check that the headers are what we expect.
let config = Config::development();
let r = Rocket::custom(config);
let req = Request::from_hyp(r._manifest(), h_method, h_headers, &h_uri, h_addr).unwrap();
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();

View File

@ -23,7 +23,7 @@ use crate::outcome::Outcome;
use crate::error::{LaunchError, LaunchErrorKind};
use crate::fairing::{Fairing, Fairings};
use crate::logger::PaintExt;
use crate::ext::AsyncReadExt;
use crate::ext::{AsyncReadExt, async_replace_with};
use crate::shutdown::{ShutdownHandle, ShutdownHandleManaged};
use crate::http::{Method, Status, Header};
@ -34,39 +34,135 @@ use crate::http::uri::Origin;
/// The main `Rocket` type: used to mount routes and catchers and launch the
/// application.
pub struct Rocket {
pub(crate) manifest: Option<Manifest>,
pending: Vec<BuildOperation>,
}
enum BuildOperation {
Mount(Origin<'static>, Vec<Route>),
Register(Vec<Catcher>),
Manage(Box<dyn FnOnce(Manifest) -> Manifest + Send + Sync + 'static>),
Attach(Box<dyn Fairing>),
}
/// The state of an unlaunched [`Rocket`].
///
/// A `Manifest` includes configuration, managed state, and mounted routes and
/// can be accessed through [`Rocket::inspect()`] before launching.
pub struct Manifest {
manifest: Vec<PreLaunchOp>,
pub(crate) config: Config,
router: Router,
default_catchers: HashMap<u16, Catcher>,
catchers: HashMap<u16, Catcher>,
pub(crate) state: Container,
pub(crate) managed_state: Container,
fairings: Fairings,
shutdown_handle: ShutdownHandle,
shutdown_receiver: Option<mpsc::Receiver<()>>,
}
// This function tries to hide all of the Hyper-ness from Rocket. It
// essentially converts Hyper types into Rocket types, then calls the
// `dispatch` function, which knows nothing about Hyper. Because responding
// depends on the `HyperResponse` type, this function does the actual
// response processing.
/// An operation that occurs prior to launching a Rocket instance.
enum PreLaunchOp {
Mount(Origin<'static>, Vec<Route>),
Register(Vec<Catcher>),
Manage(&'static str, Box<dyn FnOnce(&mut Container) + Send + Sync + 'static>),
Attach(Box<dyn Fairing>),
}
/// A frozen view into the contents of an instance of `Rocket`.
///
/// Obtained via [`Rocket::inspect()`].
#[repr(transparent)]
pub struct Cargo(Rocket);
impl Rocket {
#[inline]
fn _mount(&mut self, base: Origin<'static>, routes: Vec<Route>) {
info!("{}{} {}{}",
Paint::emoji("🛰 "),
Paint::magenta("Mounting"),
Paint::blue(&base),
Paint::magenta(":"));
for mut route in routes {
let path = route.uri.clone();
if let Err(e) = route.set_uri(base.clone(), path) {
error_!("{}", e);
panic!("Invalid route URI.");
}
info_!("{}", route);
self.router.add(route);
}
}
#[inline]
fn _register(&mut self, catchers: Vec<Catcher>) {
info!("{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:"));
for c in catchers {
if self.catchers.get(&c.code).map_or(false, |e| !e.is_default) {
info_!("{} {}", c, Paint::yellow("(warning: duplicate catcher!)"));
} else {
info_!("{}", c);
}
self.catchers.insert(c.code, c);
}
}
#[inline]
async fn _attach(mut self, fairing: Box<dyn Fairing>) -> Self {
// Attach (and run attach-) fairings, which requires us to move `self`.
let mut fairings = mem::replace(&mut self.fairings, Fairings::new());
self = fairings.attach(fairing, self).await;
// Note that `self.fairings` may now be non-empty! Move them to the end.
fairings.append(self.fairings);
self.fairings = fairings;
self
}
// Instead of requiring the user to individually `await` each call to
// `attach()`, some operations are queued in `self.pending`. Functions that
// want to provide read access to any data from the Cargo, such as
// `inspect()`, need to apply those pending operations first.
//
// This function returns a future that executes those pending operations,
// requiring only a single `await` at the call site. After completion,
// `self.pending` will be empty and `self.manifest` will reflect all pending
// changes.
//
// Note that this returns a boxed future, because `_attach()` calls this
// function again creating a cycle.
fn actualize_manifest(&mut self) -> BoxFuture<'_, ()> {
Box::pin(async move {
// Note: attach fairings may add more ops to the `manifest`! We
// process them as a stack to maintain proper ordering.
let mut manifest = mem::replace(&mut self.manifest, vec![]);
while !manifest.is_empty() {
trace_!("[MANIEST PROGRESS]: {:?}", manifest);
match manifest.remove(0) {
PreLaunchOp::Manage(_, callback) => callback(&mut self.managed_state),
PreLaunchOp::Mount(base, routes) => self._mount(base, routes),
PreLaunchOp::Register(catchers) => self._register(catchers),
PreLaunchOp::Attach(fairing) => {
async_replace_with(self, |rocket| rocket._attach(fairing)).await;
self.manifest.append(&mut manifest);
manifest = mem::replace(&mut self.manifest, vec![]);
}
}
}
})
}
pub(crate) async fn into_cargo(mut self) -> Cargo {
self.actualize_manifest().await;
Cargo(self)
}
fn cargo(&self) -> &Cargo {
if !self.manifest.is_empty() {
panic!("internal error: immutable launch state with manifest");
}
// This is simply a `newtype`-like transformation. The
// `repr(transparent)` on `Cargo` ensures that this is safe and
// correct. Note that this exact pattern appears often in `std`.
unsafe { &*(self as *const Rocket as *const Cargo) }
}
}
// This function tries to hide all of the Hyper-ness from Rocket. It essentially
// converts Hyper types into Rocket types, then calls the `dispatch` function,
// which knows nothing about Hyper. Because responding depends on the
// `HyperResponse` type, this function does the actual response processing.
fn hyper_service_fn(
rocket: Arc<Manifest>,
rocket: Arc<Rocket>,
h_addr: std::net::SocketAddr,
hyp_req: hyper::Request<hyper::Body>,
) -> impl Future<Output = Result<hyper::Response<hyper::Body>, io::Error>> {
@ -109,7 +205,7 @@ fn hyper_service_fn(
}
}
impl Manifest {
impl Rocket {
#[inline]
async fn issue_response(
&self,
@ -180,9 +276,7 @@ impl Manifest {
Ok(())
}
}
impl Manifest {
/// Preprocess the request for Rocket things. Currently, this means:
///
/// * Rewriting the method in the request if _method form field exists.
@ -209,45 +303,6 @@ impl Manifest {
}
}
#[inline]
pub(crate) fn dispatch<'s, 'r: 's>(
&'s self,
request: &'r mut Request<'s>,
data: Data
) -> impl Future<Output = Response<'r>> + 's {
async move {
info!("{}:", request);
// Do a bit of preprocessing before routing.
self.preprocess_request(request, &data);
// Run the request fairings.
self.fairings.handle_request(request, &data).await;
// Remember if the request is a `HEAD` request for later body stripping.
let was_head_request = request.method() == Method::Head;
// Route the request and run the user's handlers.
let mut response = self.route_and_process(request, data).await;
// Add a default 'Server' header if it isn't already there.
// TODO: If removing Hyper, write out `Date` header too.
if !response.headers().contains("Server") {
response.set_header(Header::new("Server", "Rocket"));
}
// Run the response fairings.
self.fairings.handle_response(request, &mut response).await;
// Strip the body if this is a `HEAD` request.
if was_head_request {
response.strip_body();
}
response
}
}
/// Route the request and process the outcome to eventually get a response.
fn route_and_process<'s, 'r: 's>(
&'s self,
@ -328,6 +383,45 @@ impl Manifest {
}
}
#[inline]
pub(crate) fn dispatch<'s, 'r: 's>(
&'s self,
request: &'r mut Request<'s>,
data: Data
) -> impl Future<Output = Response<'r>> + 's {
async move {
info!("{}:", request);
// Do a bit of preprocessing before routing.
self.preprocess_request(request, &data);
// Run the request fairings.
self.fairings.handle_request(request, &data).await;
// Remember if the request is `HEAD` for later body stripping.
let was_head_request = request.method() == Method::Head;
// Route the request and run the user's handlers.
let mut response = self.route_and_process(request, data).await;
// Add a default 'Server' header if it isn't already there.
// TODO: If removing Hyper, write out `Date` header too.
if !response.headers().contains("Server") {
response.set_header(Header::new("Server", "Rocket"));
}
// Run the response fairings.
self.fairings.handle_response(request, &mut response).await;
// Strip the body if this is a `HEAD` request.
if was_head_request {
response.strip_body();
}
response
}
}
// Finds the error catcher for the status `status` and executes it for the
// given request `req`. If a user has registered a catcher for `status`, the
// catcher is called. If the catcher fails to return a good response, the
@ -364,104 +458,23 @@ impl Manifest {
}
}
}
}
impl Manifest {
#[inline]
fn _mount(mut self, base: Origin<'static>, routes: Vec<Route>) -> Self {
info!("{}{} {}{}",
Paint::emoji("🛰 "),
Paint::magenta("Mounting"),
Paint::blue(&base),
Paint::magenta(":"));
for mut route in routes {
let path = route.uri.clone();
if let Err(e) = route.set_uri(base.clone(), path) {
error_!("{}", e);
panic!("Invalid route URI.");
}
info_!("{}", route);
self.router.add(route);
}
self
}
#[inline]
fn _register(mut self, catchers: Vec<Catcher>) -> Self {
info!("{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:"));
for c in catchers {
if self.catchers.get(&c.code).map_or(false, |e| !e.is_default) {
info_!("{} {}", c, Paint::yellow("(warning: duplicate catcher!)"));
} else {
info_!("{}", c);
}
self.catchers.insert(c.code, c);
}
self
}
#[inline]
fn _manage(self, callback: Box<dyn FnOnce(Manifest) -> Manifest>) -> Self {
callback(self)
}
#[inline]
async fn _attach(mut self, fairing: Box<dyn Fairing>) -> Self {
// Attach (and run attach) fairings, which requires us to move `self`.
let mut fairings = mem::replace(&mut self.fairings, Fairings::new());
let mut rocket = Rocket { manifest: Some(self), pending: vec![] };
rocket = fairings.attach(fairing, rocket).await;
self = rocket.actualize_and_take_manifest().await;
// Make sure we keep all fairings around: the old and newly added ones!
fairings.append(self.fairings);
self.fairings = fairings;
self
}
pub(crate) fn prelaunch_check(&mut self) -> Result<(), LaunchError> {
if let Err(e) = self.router.collisions() {
return Err(LaunchError::new(LaunchErrorKind::Collision(e)));
}
if let Some(failures) = self.fairings.failures() {
return Err(LaunchError::new(LaunchErrorKind::FailedFairings(failures.to_vec())))
}
Ok(())
}
// TODO.async: Solidify the Listener APIs and make this function public
async fn listen_on<L>(mut self, listener: L) -> Result<(), crate::error::Error>
where
L: Listener + Send + Unpin + 'static,
where L: Listener + Send + Unpin + 'static,
<L as Listener>::Connection: Send + Unpin + 'static,
{
self.fairings.pretty_print_counts();
// Determine the address and port we actually binded to.
self.config.port = listener.local_addr().map(|a| a.port()).unwrap_or(0);
let proto = self.config.tls.as_ref().map_or("http://", |_| "https://");
let full_addr = format!("{}:{}", self.config.address, self.config.port);
// Set the keep-alive.
// TODO.async: implement keep-alive in Listener
// let timeout = self.config.keep_alive.map(|s| Duration::from_secs(s as u64));
// listener.set_keepalive(timeout);
// Freeze managed state for synchronization-free accesses later.
self.state.freeze();
self.managed_state.freeze();
// Run the launch fairings.
self.fairings.handle_launch(&self);
self.fairings.pretty_print_counts();
self.fairings.handle_launch(self.cargo());
launch_info!("{}{} {}{}",
Paint::emoji("🚀 "),
@ -472,6 +485,11 @@ impl Manifest {
// Restore the log level back to what it originally was.
logger::pop_max_level();
// Set the keep-alive.
// TODO.async: implement keep-alive in Listener
// let timeout = self.config.keep_alive.map(|s| Duration::from_secs(s as u64));
// listener.set_keepalive(timeout);
// 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");
@ -614,22 +632,20 @@ impl Rocket {
Paint::default(LoggedValue(value)).bold());
}
let managed_state = Container::new();
let (shutdown_sender, shutdown_receiver) = mpsc::channel(1);
let shutdown_handle = ShutdownHandle(shutdown_sender);
managed_state.set(ShutdownHandleManaged(shutdown_handle.clone()));
let manifest = Manifest {
config,
Rocket {
config, managed_state, shutdown_handle,
manifest: vec![],
router: Router::new(),
default_catchers: catcher::defaults::get(),
catchers: catcher::defaults::get(),
state: Container::new(),
fairings: Fairings::new(),
shutdown_handle: ShutdownHandle(shutdown_sender),
shutdown_receiver: Some(shutdown_receiver),
};
manifest.state.set(ShutdownHandleManaged(manifest.shutdown_handle.clone()));
Rocket { manifest: Some(manifest), pending: vec![] }
}
}
/// Mounts all of the routes in the supplied vector at the given `base`
@ -697,7 +713,7 @@ impl Rocket {
panic!("Invalid mount point.");
}
self.pending.push(BuildOperation::Mount(base_uri, routes.into()));
self.manifest.push(PreLaunchOp::Mount(base_uri, routes.into()));
self
}
@ -727,7 +743,7 @@ impl Rocket {
/// ```
#[inline]
pub fn register(mut self, catchers: Vec<Catcher>) -> Self {
self.pending.push(BuildOperation::Register(catchers));
self.manifest.push(PreLaunchOp::Register(catchers));
self
}
@ -768,13 +784,12 @@ impl Rocket {
/// ```
#[inline]
pub fn manage<T: Send + Sync + 'static>(mut self, state: T) -> Self {
self.pending.push(BuildOperation::Manage(Box::new(|rocket| {
if !rocket.state.set::<T>(state) {
error!("State for this type is already being managed!");
let type_name = std::any::type_name::<T>();
self.manifest.push(PreLaunchOp::Manage(type_name, Box::new(move |managed| {
if !managed.set::<T>(state) {
error!("State for type '{}' is already being managed!", type_name);
panic!("Aborting due to duplicately managed state.");
}
rocket
})));
self
@ -802,44 +817,115 @@ impl Rocket {
/// ```
#[inline]
pub fn attach<F: Fairing>(mut self, fairing: F) -> Self {
self.pending.push(BuildOperation::Attach(Box::new(fairing)));
self.manifest.push(PreLaunchOp::Attach(Box::new(fairing)));
self
}
// Instead of requiring the user to individually `await` each call to
// `attach()`, some operations are queued in `self.pending`. Functions that
// want to provide read access to any data from the Manifest, such as
// `inspect()`, need to apply those pending operations first.
//
// This function returns a future that executes those pending operations,
// requiring only a single `await` at the call site. After completion,
// `self.pending` will be empty and `self.manifest` will reflect all pending
// changes.
//
// Note that this returns a boxed future, because `_attach()` calls this
// function again creating a cycle.
pub(crate) fn actualize_manifest(&mut self) -> BoxFuture<'_, ()> {
Box::pin(async move {
while !self.pending.is_empty() {
// We need to preserve insertion order here,
// so we can't use `self.pending.pop()`
let op = self.pending.remove(0);
let manifest = self.manifest.take()
.expect("internal error: manifest was taken and not replaced. \
Was `inspect()` called but not polled to completion?");
self.manifest = Some(match op {
BuildOperation::Mount(base, routes) => manifest._mount(base, routes),
BuildOperation::Register(catchers) => manifest._register(catchers),
BuildOperation::Manage(callback) => manifest._manage(callback),
BuildOperation::Attach(fairing) => manifest._attach(fairing).await,
});
}
})
/// Access the current state of this `Rocket` instance.
///
/// The `Cargo` type provides methods such as [`Cargo::routes()`]
/// and [`Cargo::state()`]. This method is called to get a `Cargo`
/// instance.
///
/// # Example
///
/// ```rust
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite();
/// let config = rocket.inspect().await.config();
/// # let _ = config;
/// # });
/// ```
pub async fn inspect(&mut self) -> &Cargo {
self.actualize_manifest().await;
self.cargo()
}
pub(crate) async fn actualize_and_take_manifest(mut self) -> Manifest {
/// Returns `Some` of the managed state value for the type `T` if it is
/// being managed by `self`. Otherwise, returns `None`.
///
/// This function is equivalent to `.inspect().await.state()` and is
/// provided as a convenience.
///
/// # Example
///
/// ```rust
/// #[derive(PartialEq, Debug)]
/// struct MyState(&'static str);
///
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite().manage(MyState("hello!"));
/// assert_eq!(rocket.state::<MyState>().await, Some(&MyState("hello!")));
/// # });
/// ```
pub async fn state<T: Send + Sync + 'static>(&mut self) -> Option<&T> {
self.inspect().await.state()
}
/// Returns the active configuration.
///
/// This function is equivalent to `.inspect().await.config()` and is
/// provided as a convenience.
///
/// # Example
///
/// ```rust
/// use rocket::Rocket;
/// use rocket::fairing::AdHoc;
///
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite();
/// println!("Rocket config: {:?}", rocket.config().await);
/// # });
/// ```
pub async fn config(&mut self) -> &Config {
self.inspect().await.config()
}
/// Returns a [`ShutdownHandle`], which can be used to gracefully terminate
/// the instance of Rocket. In routes, you should use the [`ShutdownHandle`]
/// request guard.
///
/// # Example
///
/// ```rust
/// # #![feature(proc_macro_hygiene)]
/// # use std::{thread, time::Duration};
/// #
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite();
/// let handle = rocket.inspect().await.shutdown_handle();
///
/// # if false {
/// thread::spawn(move || {
/// thread::sleep(Duration::from_secs(10));
/// handle.shutdown();
/// });
///
/// // Shuts down after 10 seconds
/// let shutdown_result = rocket.launch().await;
/// assert!(shutdown_result.is_ok());
/// # }
/// # });
/// ```
#[inline(always)]
pub fn shutdown_handle(&self) -> ShutdownHandle {
self.shutdown_handle.clone()
}
/// Perform "pre-launch" checks: verify that there are no routing colisions
/// and that there were no fairing failures.
pub(crate) async fn prelaunch_check(&mut self) -> Result<(), LaunchError> {
self.actualize_manifest().await;
self.manifest.take().expect("internal error: actualize_manifest() should have replaced self.manifest")
if let Err(e) = self.router.collisions() {
return Err(LaunchError::new(LaunchErrorKind::Collision(e)));
}
if let Some(failures) = self.fairings.failures() {
return Err(LaunchError::new(LaunchErrorKind::FailedFairings(failures.to_vec())))
}
Ok(())
}
/// Returns a `Future` that drives the server, listening for and dispatching
@ -866,28 +952,24 @@ impl Rocket {
/// # }
/// }
/// ```
pub async fn launch(self) -> Result<(), crate::error::Error> {
pub async fn launch(mut self) -> Result<(), crate::error::Error> {
use std::net::ToSocketAddrs;
use crate::error::Error::Launch;
let mut manifest = self.actualize_and_take_manifest().await;
manifest.prelaunch_check().map_err(crate::error::Error::Launch)?;
self.prelaunch_check().await.map_err(crate::error::Error::Launch)?;
let config = manifest.config();
let full_addr = format!("{}:{}", config.address, config.port);
let addrs = match full_addr.to_socket_addrs() {
Ok(a) => a.collect::<Vec<_>>(),
Err(e) => return Err(Launch(From::from(e))),
let full_addr = format!("{}:{}", self.config.address, self.config.port);
let addr = match full_addr.to_socket_addrs() {
Ok(mut addrs) => addrs.next().expect(">= 1 socket addr"),
Err(e) => return Err(Launch(e.into())),
};
let addr = addrs[0];
#[cfg(feature = "ctrl_c_shutdown")]
let (
shutdown_handle,
(cancel_ctrl_c_listener_sender, cancel_ctrl_c_listener_receiver)
) = (
manifest.get_shutdown_handle(),
self.shutdown_handle.clone(),
oneshot::channel(),
);
@ -898,14 +980,13 @@ impl Rocket {
Ok(ok) => ok,
Err(err) => return Err(Launch(LaunchError::new(LaunchErrorKind::Bind(err)))),
};
manifest.listen_on(listener)
self.listen_on(listener)
}};
}
#[cfg(feature = "tls")]
{
let config = manifest.config();
if let Some(tls) = config.tls.clone() {
if let Some(tls) = self.config.tls.clone() {
listen_on!(crate::http::tls::bind_tls(addr, tls.certs, tls.key).await).boxed()
} else {
listen_on!(crate::http::private::bind_tcp(addr).await).boxed()
@ -948,108 +1029,38 @@ impl Rocket {
server.await
}
}
pub(crate) fn _manifest(&self) -> &Manifest {
self.manifest.as_ref().expect("internal error: manifest was taken and not replaced. \
Was `inspect()` called but not polled to completion?")
impl std::fmt::Debug for PreLaunchOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use PreLaunchOp::*;
match self {
Mount(origin, routes) => f.debug_tuple("PreLaunchOp::Mount")
.field(&origin)
.field(&routes)
.finish(),
Register(catchers) => f.debug_tuple("PreLaunchOp::Register")
.field(&catchers)
.finish(),
Manage(name, _) => f.debug_tuple("PreLaunchOp::Manage")
.field(&name)
.finish(),
Attach(fairing) => f.debug_tuple("PreLaunchOp::Attach")
.field(&fairing.info())
.finish()
}
/// Access the current state of this `Rocket` instance.
///
/// The `Manifest` type provides methods such as [`Manifest::routes()`]
/// and [`Manifest::state()`]. This method is called to get a `Manifest`
/// instance.
///
/// # Example
///
/// ```rust
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite();
/// let config = rocket.inspect().await.config();
/// # let _ = config;
/// # });
/// ```
pub async fn inspect(&mut self) -> &Manifest {
self.actualize_manifest().await;
self._manifest()
}
/// Returns `Some` of the managed state value for the type `T` if it is
/// being managed by `self`. Otherwise, returns `None`.
///
/// This function is equivalent to `.inspect().await.state()` and is
/// provided as a convenience.
///
/// # Example
///
/// ```rust
/// #[derive(PartialEq, Debug)]
/// struct MyState(&'static str);
///
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite().manage(MyState("hello!"));
/// assert_eq!(rocket.state::<MyState>().await, Some(&MyState("hello!")));
/// # });
/// ```
pub async fn state<T: Send + Sync + 'static>(&mut self) -> Option<&T> {
self.inspect().await.state()
}
/// Returns the active configuration.
///
/// This function is equivalent to `.inspect().await.config()` and is
/// provided as a convenience.
///
/// # Example
///
/// ```rust
/// # #![feature(proc_macro_hygiene)]
/// # #[macro_use] extern crate rocket;
/// use rocket::Rocket;
/// use rocket::fairing::AdHoc;
///
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite();
/// println!("Rocket config: {:?}", rocket.config().await);
/// # });
/// ```
pub async fn config(&mut self) -> &Config {
self.inspect().await.config()
}
}
impl Manifest {
/// Returns a [`ShutdownHandle`], which can be used to gracefully terminate
/// the instance of Rocket. In routes, you should use the [`ShutdownHandle`]
/// request guard.
///
/// # Example
///
/// ```rust
/// # #![feature(proc_macro_hygiene)]
/// # use std::{thread, time::Duration};
/// #
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite();
/// let handle = rocket.inspect().await.get_shutdown_handle();
///
/// # if false {
/// thread::spawn(move || {
/// thread::sleep(Duration::from_secs(10));
/// handle.shutdown();
/// });
///
/// // Shuts down after 10 seconds
/// let shutdown_result = rocket.launch().await;
/// assert!(shutdown_result.is_ok());
/// # }
/// # });
/// ```
#[inline(always)]
pub fn get_shutdown_handle(&self) -> ShutdownHandle {
self.shutdown_handle.clone()
}
impl std::ops::Deref for Cargo {
type Target = Rocket;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Cargo {
/// Returns an iterator over all of the routes mounted on this instance of
/// Rocket.
///
@ -1086,7 +1097,7 @@ impl Manifest {
/// ```
#[inline(always)]
pub fn routes(&self) -> impl Iterator<Item = &Route> + '_ {
self.router.routes()
self.0.router.routes()
}
/// Returns `Some` of the managed state value for the type `T` if it is
@ -1100,15 +1111,13 @@ impl Manifest {
///
/// # rocket::async_test(async {
/// let mut rocket = rocket::ignite().manage(MyState("hello!"));
/// assert_eq!(rocket.inspect().await.state::<MyState>(), Some(&MyState("hello!")));
///
/// let client = rocket::local::Client::new(rocket).await.expect("valid rocket");
/// assert_eq!(client.manifest().state::<MyState>(), Some(&MyState("hello!")));
/// let cargo = rocket.inspect().await;
/// assert_eq!(cargo.state::<MyState>(), Some(&MyState("hello!")));
/// # });
/// ```
#[inline(always)]
pub fn state<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.state.try_get()
self.0.managed_state.try_get()
}
/// Returns the active configuration.
@ -1124,13 +1133,13 @@ impl Manifest {
/// #[rocket::launch]
/// fn rocket() -> rocket::Rocket {
/// rocket::ignite()
/// .attach(AdHoc::on_launch("Config Printer", |manifest| {
/// println!("Rocket launch config: {:?}", manifest.config());
/// .attach(AdHoc::on_launch("Config Printer", |cargo| {
/// println!("Rocket launch config: {:?}", cargo.config());
/// }))
/// }
/// ```
#[inline(always)]
pub fn config(&self) -> &Config {
&self.config
&self.0.config
}
}

View File

@ -402,7 +402,7 @@ mod tests {
where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>>
{
let rocket = Rocket::custom(Config::development());
let mut req = Request::new(rocket._manifest(), m, Origin::dummy());
let mut req = Request::new(&rocket, m, Origin::dummy());
if let Some(mt_str) = mt1.into() {
if m.supports_payload() {
req.replace_header(mt_str.parse::<ContentType>().unwrap());
@ -469,7 +469,7 @@ mod tests {
fn req_route_path_match(a: &'static str, b: &'static str) -> bool {
let rocket = Rocket::custom(Config::development());
let req = Request::new(&rocket._manifest(), Get, Origin::parse(a).expect("valid URI"));
let req = Request::new(&rocket, Get, Origin::parse(a).expect("valid URI"));
let route = Route::ranked(0, Get, b.to_string(), dummy_handler);
route.matches(&req)
}

View File

@ -233,7 +233,7 @@ mod test {
fn route<'a>(router: &'a Router, method: Method, uri: &str) -> Option<&'a Route> {
let rocket = Rocket::custom(Config::development());
let request = Request::new(rocket._manifest(), method, Origin::parse(uri).unwrap());
let request = Request::new(&rocket, method, Origin::parse(uri).unwrap());
let matches = router.route(&request);
if matches.len() > 0 {
Some(matches[0])
@ -244,7 +244,7 @@ mod test {
fn matches<'a>(router: &'a Router, method: Method, uri: &str) -> Vec<&'a Route> {
let rocket = Rocket::custom(Config::development());
let request = Request::new(rocket._manifest(), method, Origin::parse(uri).unwrap());
let request = Request::new(&rocket, method, Origin::parse(uri).unwrap());
router.route(&request)
}

View File

@ -0,0 +1,43 @@
use rocket::fairing::AdHoc;
#[rocket::async_test]
async fn test_inspectable_attach_state() {
let mut rocket = rocket::ignite()
.attach(AdHoc::on_attach("Add State", |rocket| async {
Ok(rocket.manage("Hi!"))
}));
let state = rocket.inspect().await;
assert_eq!(state.state::<&'static str>(), Some(&"Hi!"));
}
#[rocket::async_test]
async fn test_inspectable_attach_state_in_future_attach() {
let mut rocket = rocket::ignite()
.attach(AdHoc::on_attach("Add State", |rocket| async {
Ok(rocket.manage("Hi!"))
}))
.attach(AdHoc::on_attach("Inspect State", |mut rocket| async {
let state = rocket.inspect().await;
assert_eq!(state.state::<&'static str>(), Some(&"Hi!"));
Ok(rocket)
}));
let _ = rocket.inspect().await;
}
#[rocket::async_test]
async fn test_attach_state_is_well_ordered() {
let mut rocket = rocket::ignite()
.attach(AdHoc::on_attach("Inspect State Pre", |mut rocket| async {
let state = rocket.inspect().await;
assert_eq!(state.state::<&'static str>(), None);
Ok(rocket)
}))
.attach(AdHoc::on_attach("Add State", |rocket| async {
Ok(rocket.manage("Hi!"))
}));
let state = rocket.inspect().await;
assert_eq!(state.state::<&'static str>(), Some(&"Hi!"));
}

View File

@ -39,11 +39,11 @@ async fn test_index() {
// Render the template with an empty context.
let mut context: HashMap<&str, &str> = HashMap::new();
let template = Template::show(client.manifest(), "index", &context).unwrap();
let template = Template::show(client.cargo(), "index", &context).unwrap();
test_body(None, template).await;
// Render the template with a context that contains the message.
context.insert("message", "Hello from Rocket!");
let template = Template::show(client.manifest(), "index", &context).unwrap();
let template = Template::show(client.cargo(), "index", &context).unwrap();
test_body(Some(Cookie::new("message", "Hello from Rocket!")), template).await;
}

View File

@ -31,7 +31,7 @@ async fn test_root() {
dispatch!(*method, "/", |client, response| {
let mut map = std::collections::HashMap::new();
map.insert("path", "/");
let expected = Template::show(client.manifest(), "error/404", &map).unwrap();
let expected = Template::show(client.cargo(), "error/404", &map).unwrap();
assert_eq!(response.status(), Status::NotFound);
assert_eq!(response.body_string().await, Some(expected));
@ -50,7 +50,7 @@ async fn test_name() {
parent: "layout",
};
let expected = Template::show(client.manifest(), "index", &context).unwrap();
let expected = Template::show(client.cargo(), "index", &context).unwrap();
assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string().await, Some(expected));
});
@ -63,7 +63,7 @@ async fn test_404() {
let mut map = std::collections::HashMap::new();
map.insert("path", "/hello/");
let expected = Template::show(client.manifest(), "error/404", &map).unwrap();
let expected = Template::show(client.cargo(), "error/404", &map).unwrap();
assert_eq!(response.status(), Status::NotFound);
assert_eq!(response.body_string().await, Some(expected));
});

View File

@ -1,4 +1,4 @@
use std::sync::atomic::{Ordering};
use std::sync::atomic::Ordering;
use super::{rocket, Atomics};
use rocket::local::Client;
@ -8,13 +8,13 @@ async fn test() {
let client = Client::new(rocket()).await.unwrap();
client.get("/sync").dispatch().await;
let atomics = client.manifest().state::<Atomics>().unwrap();
let atomics = client.cargo().state::<Atomics>().unwrap();
assert_eq!(atomics.uncached.load(Ordering::Relaxed), 2);
assert_eq!(atomics.cached.load(Ordering::Relaxed), 1);
client.get("/async").dispatch().await;
let atomics = client.manifest().state::<Atomics>().unwrap();
let atomics = client.cargo().state::<Atomics>().unwrap();
assert_eq!(atomics.uncached.load(Ordering::Relaxed), 4);
assert_eq!(atomics.cached.load(Ordering::Relaxed), 2);
}

View File

@ -31,11 +31,11 @@ async fn test_raw_state_count() {
use super::{count, index};
let mut rocket = super::rocket();
let manifest = rocket.inspect().await;
let cargo = rocket.inspect().await;
assert_eq!(count(State::from(manifest).unwrap()), "0");
assert!(index(State::from(manifest).unwrap()).0.contains("Visits: 1"));
assert_eq!(count(State::from(manifest).unwrap()), "1");
assert_eq!(count(State::from(cargo).unwrap()), "0");
assert!(index(State::from(cargo).unwrap()).0.contains("Visits: 1"));
assert_eq!(count(State::from(cargo).unwrap()), "1");
}
// Cargo runs each test in parallel on different threads. We use all of these

View File

@ -30,7 +30,7 @@ async fn test_root() {
dispatch!(*method, "/", |client, response| {
let mut map = std::collections::HashMap::new();
map.insert("path", "/");
let expected = Template::show(client.manifest(), "error/404", &map).unwrap();
let expected = Template::show(client.cargo(), "error/404", &map).unwrap();
assert_eq!(response.status(), Status::NotFound);
assert_eq!(response.body_string().await, Some(expected));
@ -47,7 +47,7 @@ async fn test_name() {
items: vec!["One", "Two", "Three"]
};
let expected = Template::show(client.manifest(), "index", &context).unwrap();
let expected = Template::show(client.cargo(), "index", &context).unwrap();
assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string().await, Some(expected));
});
@ -60,7 +60,7 @@ async fn test_404() {
let mut map = std::collections::HashMap::new();
map.insert("path", "/hello/");
let expected = Template::show(client.manifest(), "error/404", &map).unwrap();
let expected = Template::show(client.cargo(), "error/404", &map).unwrap();
assert_eq!(response.status(), Status::NotFound);
assert_eq!(response.body_string().await, Some(expected));
});