Defer execution of operations on 'Rocket' until their effects will be

observed.

This is a prerequisite for async on_attach fairings. 'Rocket' is now a
builder wrapper around the 'Manifest' type, with operations being
applied when needed by 'launch()', 'Client::new()', or 'inspect()'.
'inspect()' returns an '&Manifest', which now provides the methods that
could be called on an '&Rocket'.
This commit is contained in:
Jeb Rosen 2020-06-14 08:57:51 -07:00 committed by Sergio Benitez
parent 3796673740
commit dea940c7a8
31 changed files with 368 additions and 257 deletions

View File

@ -93,8 +93,8 @@ pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result<TokenStrea
pub fn fairing() -> impl ::rocket::fairing::Fairing {
use #databases::Poolable;
::rocket::fairing::AdHoc::on_attach(#fairing_name, |rocket| {
let pool = #databases::database_config(#name, rocket.config())
::rocket::fairing::AdHoc::on_attach(#fairing_name, |mut rocket| {
let pool = #databases::database_config(#name, rocket.inspect().config())
.map(<#conn_type>::pool);
match pool {
@ -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(rocket: &::rocket::Rocket) -> Option<Self> {
rocket.state::<#pool_type>()
pub fn get_one(manifest: &::rocket::Manifest) -> Option<Self> {
manifest.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(&Rocket) -> Option<Self>`
//! * `fn get_one(&Manifest) -> 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
@ -548,16 +548,17 @@ pub enum ConfigError {
/// # .extra("databases", databases)
/// # .expect("custom config okay");
/// #
/// # rocket::custom(config).attach(AdHoc::on_attach("Testing", |rocket| {
/// # rocket::custom(config).attach(AdHoc::on_attach("Testing", |mut rocket| {
/// # {
/// let config = database_config("my_db", rocket.config()).unwrap();
/// let manifest = rocket.inspect();
/// let config = database_config("my_db", manifest.config()).unwrap();
/// assert_eq!(config.url, "db/db.sqlite");
/// assert_eq!(config.pool_size, 25);
///
/// let other_config = database_config("my_other_db", rocket.config()).unwrap();
/// let other_config = database_config("my_other_db", manifest.config()).unwrap();
/// assert_eq!(other_config.url, "mysql://root:root@localhost/database");
///
/// let error = database_config("invalid_db", rocket.config()).unwrap_err();
/// let error = database_config("invalid_db", manifest.config()).unwrap_err();
/// assert_eq!(error, ConfigError::MissingKey);
/// # }
/// #

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::{Request, Response, Rocket};
use rocket::{Manifest, Request, Response};
use crate::helmet::*;
@ -201,9 +201,9 @@ impl Fairing for SpaceHelmet {
self.apply(res);
}
fn on_launch(&self, rocket: &Rocket) {
if rocket.config().tls_enabled()
&& !rocket.config().environment.is_dev()
fn on_launch(&self, manifest: &Manifest) {
if manifest.config().tls_enabled()
&& !manifest.config().environment.is_dev()
&& !self.is_enabled::<Hsts>()
{
warn_!("Space Helmet: deploying with TLS without enabling HSTS.");

View File

@ -151,10 +151,12 @@ 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.
fn on_attach(&self, rocket: Rocket) -> Result<Rocket, Rocket> {
let mut template_root = rocket.config().root_relative(DEFAULT_TEMPLATE_DIR);
match rocket.config().get_str("template_dir") {
Ok(dir) => template_root = rocket.config().root_relative(dir),
fn on_attach(&self, mut rocket: Rocket) -> Result<Rocket, Rocket> {
let manifest = rocket.inspect();
let config = manifest.config();
let mut template_root = config.root_relative(DEFAULT_TEMPLATE_DIR);
match config.get_str("template_dir") {
Ok(dir) => template_root = config.root_relative(dir),
Err(ConfigError::Missing(_)) => { /* ignore missing */ }
Err(e) => {
e.pretty_print();

View File

@ -141,7 +141,7 @@ use serde_json::{Value, to_value};
use std::borrow::Cow;
use std::path::PathBuf;
use rocket::{Rocket, State};
use rocket::{Manifest, State};
use rocket::request::Request;
use rocket::fairing::Fairing;
use rocket::response::{self, Content, Responder};
@ -339,14 +339,14 @@ impl Template {
///
/// # context.insert("test", "test");
/// # #[allow(unused_variables)]
/// let template = Template::show(client.rocket(), "index", context);
/// let template = Template::show(client.manifest(), "index", context);
/// }
/// ```
#[inline]
pub fn show<S, C>(rocket: &Rocket, name: S, context: C) -> Option<String>
pub fn show<S, C>(manifest: &Manifest, name: S, context: C) -> Option<String>
where S: Into<Cow<'static, str>>, C: Serialize
{
let ctxt = rocket.state::<ContextManager>().map(ContextManager::context).or_else(|| {
let ctxt = manifest.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

@ -33,8 +33,8 @@ mod rusqlite_integration_test {
.finalize()
.unwrap();
let rocket = rocket::custom(config).attach(SqliteDb::fairing());
let mut conn = SqliteDb::get_one(&rocket).expect("unable to get connection");
let mut rocket = rocket::custom(config).attach(SqliteDb::fairing());
let mut conn = SqliteDb::get_one(rocket.inspect()).expect("unable to get connection");
// Rusqlite's `transaction()` method takes `&mut self`; this tests the
// presence of a `DerefMut` trait on the generated connection type.
@ -54,8 +54,8 @@ mod rusqlite_integration_test {
.finalize()
.unwrap();
let rocket = rocket::custom(config).attach(SqliteDb::fairing());
let conn = SqliteDb::get_one(&rocket).expect("unable to get connection");
let mut rocket = rocket::custom(config).attach(SqliteDb::fairing());
let conn = SqliteDb::get_one(rocket.inspect()).expect("unable to get connection");
let _: i32 = conn.query_row("SELECT 1", &[] as &[&dyn ToSql], |row| row.get(0)).expect("get row");
}
}

View File

@ -51,17 +51,18 @@ mod templates_tests {
#[test]
fn test_tera_templates() {
let rocket = rocket();
let mut rocket = rocket();
let manifest = rocket.inspect();
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(&rocket, "tera/txt_test", &map);
let template = Template::show(manifest, "tera/txt_test", &map);
assert_eq!(template, Some(UNESCAPED_EXPECTED.into()));
// Now with an HTML file, which should.
let template = Template::show(&rocket, "tera/html_test", &map);
let template = Template::show(manifest, "tera/html_test", &map);
assert_eq!(template, Some(ESCAPED_EXPECTED.into()));
}
@ -95,13 +96,14 @@ mod templates_tests {
#[test]
fn test_handlebars_templates() {
let rocket = rocket();
let mut rocket = rocket();
let manifest = rocket.inspect();
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(&rocket, "hbs/test", &map);
let template = Template::show(manifest, "hbs/test", &map);
assert_eq!(template, Some(EXPECTED.into()));
}
@ -151,7 +153,7 @@ mod templates_tests {
}
// verify that the initial content is correct
let initial_rendered = Template::show(client.rocket(), RELOAD_TEMPLATE, ());
let initial_rendered = Template::show(client.manifest(), RELOAD_TEMPLATE, ());
assert_eq!(initial_rendered, Some(INITIAL_TEXT.into()));
// write a change to the file
@ -162,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.rocket(), RELOAD_TEMPLATE, ());
let new_rendered = Template::show(client.manifest(), RELOAD_TEMPLATE, ());
if new_rendered == Some(NEW_TEXT.into()) {
write_file(&reload_path, INITIAL_TEXT);
return;

View File

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

View File

@ -174,9 +174,9 @@
//!
//! fn main() {
//! rocket::ignite()
//! .attach(AdHoc::on_attach("Token Config", |rocket| {
//! .attach(AdHoc::on_attach("Token Config", |mut rocket| {
//! println!("Adding token managed state from config...");
//! let token_val = rocket.config().get_int("token").unwrap_or(-1);
//! let token_val = rocket.inspect().config().get_int("token").unwrap_or(-1);
//! Ok(rocket.manage(Token(token_val)))
//! }))
//! # ;

View File

@ -2,7 +2,7 @@ use std::sync::Mutex;
use futures_util::future::BoxFuture;
use crate::{Rocket, Request, Response, Data};
use crate::{Manifest, Rocket, Request, Response, Data};
use crate::fairing::{Fairing, Kind, Info};
/// A ad-hoc fairing that can be created from a function or closure.
@ -48,7 +48,7 @@ enum AdHocKind {
/// An ad-hoc **attach** fairing. Called when the fairing is attached.
Attach(Mutex<Option<Box<dyn FnOnce(Rocket) -> Result<Rocket, Rocket> + Send + 'static>>>),
/// An ad-hoc **launch** fairing. Called just before Rocket launches.
Launch(Mutex<Option<Box<dyn FnOnce(&Rocket) + Send + 'static>>>),
Launch(Mutex<Option<Box<dyn FnOnce(&Manifest) + Send + 'static>>>),
/// An ad-hoc **request** fairing. Called when a request is received.
Request(Box<dyn for<'a> Fn(&'a mut Request<'_>, &'a Data) -> BoxFuture<'a, ()> + Send + Sync + 'static>),
/// An ad-hoc **response** fairing. Called when a response is ready to be
@ -88,7 +88,7 @@ impl AdHoc {
/// });
/// ```
pub fn on_launch<F: Send + 'static>(name: &'static str, f: F) -> AdHoc
where F: FnOnce(&Rocket)
where F: FnOnce(&Manifest)
{
AdHoc { name, kind: AdHocKind::Launch(Mutex::new(Some(Box::new(f)))) }
}
@ -163,11 +163,11 @@ impl Fairing for AdHoc {
}
}
fn on_launch(&self, rocket: &Rocket) {
fn on_launch(&self, manifest: &Manifest) {
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(rocket)
f(manifest)
}
}

View File

@ -1,4 +1,4 @@
use crate::{Rocket, Request, Response, Data};
use crate::{Manifest, 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, rocket: &Rocket) {
pub fn handle_launch(&self, manifest: &Manifest) {
for &i in &self.launch {
self.all_fairings[i].on_launch(rocket);
self.all_fairings[i].on_launch(manifest);
}
}

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::{Manifest, 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::{Rocket, Request, Data, Response};
/// use rocket::{Manifest, 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, rocket: &Rocket) {
/// fn on_launch(&self, manifest: &Manifest) {
/// /* ... */
/// # 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 `&Rocket` parameter corresponds to the application that
/// fairing. The `Manifest` 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, rocket: &Rocket) {}
fn on_launch(&self, manifest: &Manifest) {}
/// The request callback.
///
@ -470,8 +470,8 @@ impl<T: Fairing> Fairing for std::sync::Arc<T> {
}
#[inline]
fn on_launch(&self, rocket: &Rocket) {
(self as &T).on_launch(rocket)
fn on_launch(&self, manifest: &Manifest) {
(self as &T).on_launch(manifest)
}
#[inline]

View File

@ -140,7 +140,7 @@ mod ext;
pub use crate::router::Route;
pub use crate::request::{Request, State};
pub use crate::catcher::Catcher;
pub use crate::rocket::Rocket;
pub use crate::rocket::{Manifest, 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;
use crate::rocket::{Rocket, Manifest};
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 {
rocket: Rocket,
manifest: Manifest,
pub(crate) cookies: Option<RwLock<CookieJar>>,
}
@ -79,12 +79,15 @@ impl Client {
/// is created for cookie tracking. Otherwise, the internal `CookieJar` is
/// set to `None`.
fn _new(rocket: Rocket, tracked: bool) -> Result<Client, LaunchError> {
let mut manifest = rocket.actualize_and_take_manifest();
manifest.prelaunch_check()?;
let cookies = match tracked {
true => Some(RwLock::new(CookieJar::new())),
false => None
};
Ok(Client { rocket: rocket.prelaunch_check()?, cookies })
Ok(Client { manifest, cookies })
}
/// Construct a new `Client` from an instance of `Rocket` with cookie
@ -146,7 +149,8 @@ impl Client {
Client::_new(rocket, false)
}
/// Returns the instance of `Rocket` this client is creating requests for.
/// Returns a reference to the `Manifest` of the `Rocket` this client is
/// creating requests for.
///
/// # Example
///
@ -156,12 +160,12 @@ impl Client {
/// let my_rocket = rocket::ignite();
/// let client = Client::new(my_rocket).expect("valid rocket");
///
/// // get the instance of `my_rocket` within `client`
/// let my_rocket = client.rocket();
/// // get access to the manifest within `client`
/// let manifest = client.manifest();
/// ```
#[inline(always)]
pub fn rocket(&self) -> &Rocket {
&self.rocket
pub fn manifest(&self) -> &Manifest {
&self.manifest
}
/// Create a local `GET` request to the URI `uri`.

View File

@ -103,7 +103,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.rocket(), method, Origin::dummy());
let request = Request::new(client.manifest(), method, Origin::dummy());
// Set up any cookies we know about.
if let Some(ref jar) = client.cookies {
@ -406,12 +406,12 @@ impl<'c> LocalRequest<'c> {
request.set_uri(uri.into_owned());
} else {
error!("Malformed request URI: {}", uri);
let res = client.rocket().handle_error(Status::BadRequest, request).await;
let res = client.manifest().handle_error(Status::BadRequest, request).await;
return LocalResponse { _request: owned_request, response: res };
}
// Actually dispatch the request.
let response = client.rocket().dispatch(request, Data::local(data)).await;
let response = client.manifest().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_util::future::BoxFuture;
use crate::request::{FromParam, FromSegments, FromRequest, Outcome};
use crate::request::{FromFormValue, FormItems, FormItem};
use crate::rocket::Rocket;
use crate::rocket::{Rocket, Manifest};
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>(
rocket: &'r Rocket,
manifest: &'r Manifest,
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: &rocket.config,
managed: &rocket.state,
config: &manifest.config,
managed: &manifest.state,
route: RwLock::new(None),
cookies: Mutex::new(Some(CookieJar::new())),
accept: Storage::new(),
@ -738,7 +738,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, method, uri);
let mut request = Request::new(rocket._manifest(), method, uri);
f(&mut request);
}
@ -821,7 +821,7 @@ impl<'r> Request<'r> {
/// Convert from Hyper types into a Rocket Request.
pub(crate) fn from_hyp(
rocket: &'r Rocket,
manifest: &'r Manifest,
h_method: hyper::Method,
h_headers: hyper::HeaderMap<hyper::HeaderValue>,
h_uri: &'r hyper::Uri,
@ -843,7 +843,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(rocket, method, uri);
let mut request = Request::new(manifest, 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;
use crate::rocket::Manifest;
use crate::request::{self, FromRequest, Request};
use crate::outcome::Outcome;
use crate::http::Status;
@ -99,8 +99,8 @@ use crate::http::Status;
/// state.0.to_string()
/// }
///
/// let rocket = rocket::ignite().manage(MyManagedState(127));
/// let state = State::from(&rocket).expect("managing `MyManagedState`");
/// let mut rocket = rocket::ignite().manage(MyManagedState(127));
/// let state = State::from(rocket.inspect()).expect("managing `MyManagedState`");
/// assert_eq!(handler(state), "127");
/// ```
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -152,17 +152,18 @@ impl<'r, T: Send + Sync + 'static> State<'r, T> {
/// #[derive(Debug, PartialEq)]
/// struct Unmanaged(usize);
///
/// let rocket = rocket::ignite().manage(Managed(7));
/// let mut rocket = rocket::ignite().manage(Managed(7));
/// let manifest = rocket.inspect();
///
/// let state: Option<State<Managed>> = State::from(&rocket);
/// let state: Option<State<Managed>> = State::from(manifest);
/// assert_eq!(state.map(|s| s.inner()), Some(&Managed(7)));
///
/// let state: Option<State<Unmanaged>> = State::from(&rocket);
/// let state: Option<State<Unmanaged>> = State::from(manifest);
/// assert_eq!(state, None);
/// ```
#[inline(always)]
pub fn from(rocket: &'r Rocket) -> Option<Self> {
rocket.state.try_get::<T>().map(State)
pub fn from(manifest: &'r Manifest) -> Option<Self> {
manifest.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, h_method, h_headers, &h_uri, h_addr).unwrap();
let req = Request::from_hyp(r._manifest(), 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

@ -34,6 +34,22 @@ 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 {
pub(crate) config: Config,
router: Router,
default_catchers: HashMap<u16, Catcher>,
@ -50,7 +66,7 @@ pub struct Rocket {
// depends on the `HyperResponse` type, this function does the actual
// response processing.
fn hyper_service_fn(
rocket: Arc<Rocket>,
rocket: Arc<Manifest>,
h_addr: std::net::SocketAddr,
hyp_req: hyper::Request<hyper::Body>,
) -> impl Future<Output = Result<hyper::Response<hyper::Body>, io::Error>> {
@ -93,7 +109,7 @@ fn hyper_service_fn(
}
}
impl Rocket {
impl Manifest {
#[inline]
async fn issue_response(
&self,
@ -162,7 +178,7 @@ impl Rocket {
}
}
impl Rocket {
impl Manifest {
/// Preprocess the request for Rocket things. Currently, this means:
///
/// * Rewriting the method in the request if _method form field exists.
@ -346,6 +362,145 @@ impl Rocket {
}
}
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]
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);
self = rocket.actualize_and_take_manifest();
// 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,
<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();
// Run the launch fairings.
self.fairings.handle_launch(&self);
launch_info!("{}{} {}{}",
Paint::emoji("🚀 "),
Paint::default("Rocket has launched from").bold(),
Paint::default(proto).bold().underline(),
Paint::default(&full_addr).bold().underline());
// Restore the log level back to what it originally was.
logger::pop_max_level();
// 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 |connection: &<L as Listener>::Connection| {
let rocket = rocket.clone();
let remote_addr = connection.remote_addr().unwrap_or_else(|| ([0, 0, 0, 0], 0).into());
async move {
Ok::<_, std::convert::Infallible>(hyper::service_fn(move |req| {
hyper_service_fn(rocket.clone(), remote_addr, req)
}))
}
});
#[derive(Clone)]
struct TokioExecutor;
impl<Fut> hyper::Executor<Fut> for TokioExecutor where Fut: Future + Send + 'static, Fut::Output: Send {
fn execute(&self, fut: Fut) {
tokio::spawn(fut);
}
}
hyper::Server::builder(Incoming::from_listener(listener))
.executor(TokioExecutor)
.serve(service)
.with_graceful_shutdown(async move { shutdown_receiver.recv().await; })
.await
.map_err(|e| crate::error::Error::Run(Box::new(e)))
}
}
impl Rocket {
/// Create a new `Rocket` application using the configuration information in
/// `Rocket.toml`. If the file does not exist or if there is an I/O error
@ -457,7 +612,7 @@ impl Rocket {
let (shutdown_sender, shutdown_receiver) = mpsc::channel(1);
let rocket = Rocket {
let manifest = Manifest {
config,
router: Router::new(),
default_catchers: catcher::defaults::get(),
@ -468,9 +623,9 @@ impl Rocket {
shutdown_receiver: Some(shutdown_receiver),
};
rocket.state.set(ShutdownHandleManaged(rocket.shutdown_handle.clone()));
manifest.state.set(ShutdownHandleManaged(manifest.shutdown_handle.clone()));
rocket
Rocket { manifest: Some(manifest), pending: vec![] }
}
/// Mounts all of the routes in the supplied vector at the given `base`
@ -529,13 +684,7 @@ impl Rocket {
/// ```
#[inline]
pub fn mount<R: Into<Vec<Route>>>(mut self, base: &str, routes: R) -> Self {
info!("{}{} {}{}",
Paint::emoji("🛰 "),
Paint::magenta("Mounting"),
Paint::blue(base),
Paint::magenta(":"));
let base_uri = Origin::parse(base)
let base_uri = Origin::parse_owned(base.to_string())
.unwrap_or_else(|e| {
error_!("Invalid origin URI '{}' used as mount point.", base);
panic!("Error: {}", e);
@ -546,17 +695,7 @@ impl Rocket {
panic!("Invalid mount point.");
}
for mut route in routes.into() {
let path = route.uri.clone();
if let Err(e) = route.set_uri(base_uri.clone(), path) {
error_!("{}", e);
panic!("Invalid route URI.");
}
info_!("{}", route);
self.router.add(route);
}
self.pending.push(BuildOperation::Mount(base_uri, routes.into()));
self
}
@ -589,18 +728,7 @@ impl Rocket {
/// ```
#[inline]
pub 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.pending.push(BuildOperation::Register(catchers));
self
}
@ -642,12 +770,16 @@ impl Rocket {
/// }
/// ```
#[inline]
pub fn manage<T: Send + Sync + 'static>(self, state: T) -> Self {
if !self.state.set::<T>(state) {
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!");
panic!("Aborting due to duplicately managed state.");
}
rocket
})));
self
}
@ -675,105 +807,41 @@ impl Rocket {
/// ```
#[inline]
pub fn attach<F: Fairing>(mut self, fairing: F) -> 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(Box::new(fairing), self);
// Make sure we keep all fairings around: the old and newly added ones!
fairings.append(self.fairings);
self.fairings = fairings;
self.pending.push(BuildOperation::Attach(Box::new(fairing)));
self
}
pub(crate) fn prelaunch_check(mut self) -> Result<Rocket, LaunchError> {
self.router = match self.router.collisions() {
Ok(router) => router,
Err(e) => return Err(LaunchError::new(LaunchErrorKind::Collision(e)))
};
if let Some(failures) = self.fairings.failures() {
return Err(LaunchError::new(LaunchErrorKind::FailedFairings(failures.to_vec())))
}
Ok(self)
}
// 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,
<L as Listener>::Connection: Send + Unpin + 'static,
{
self = self.prelaunch_check().map_err(crate::error::Error::Launch)?;
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 = if self.config.tls.is_some() {
"https://"
} else {
"http://"
};
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();
// Run the launch fairings.
self.fairings.handle_launch(&self);
launch_info!("{}{} {}{}",
Paint::emoji("🚀 "),
Paint::default("Rocket has launched from").bold(),
Paint::default(proto).bold().underline(),
Paint::default(&full_addr).bold().underline());
// Restore the log level back to what it originally was.
logger::pop_max_level();
// 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 |connection: &<L as Listener>::Connection| {
let rocket = rocket.clone();
let remote_addr = connection.remote_addr().unwrap_or_else(|| ([0, 0, 0, 0], 0).into());
async move {
Ok::<_, std::convert::Infallible>(hyper::service_fn(move |req| {
hyper_service_fn(rocket.clone(), remote_addr, req)
}))
}
pub(crate) fn actualize_manifest(&mut self) {
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("TODO error message");
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),
});
#[derive(Clone)]
struct TokioExecutor;
impl<Fut> hyper::Executor<Fut> for TokioExecutor where Fut: Future + Send + 'static, Fut::Output: Send {
fn execute(&self, fut: Fut) {
tokio::spawn(fut);
}
}
hyper::Server::builder(Incoming::from_listener(listener))
.executor(TokioExecutor)
.serve(service)
.with_graceful_shutdown(async move { shutdown_receiver.recv().await; })
.await
.map_err(|e| crate::error::Error::Run(Box::new(e)))
pub(crate) fn actualize_and_take_manifest(mut self) -> Manifest {
self.actualize_manifest();
self.manifest.take().expect("internal error: actualize_manifest() should have replaced self.manifest")
}
/// Returns a `Future` that drives the server and completes when the server
/// is shut down or errors. If the `ctrl_c_shutdown` feature is enabled,
/// the server will shut down gracefully once `Ctrl-C` is pressed.
/// Returns a `Future` that drives the server, listening for and dispathcing
/// requests to mounted routes and catchers. The `Future` completes when the
/// server is shut down (via a [`ShutdownHandle`] or encounters a fatal
/// error. If the `ctrl_c_shutdown` feature is enabled, the server will
/// also shut down once `Ctrl-C` is pressed.
///
/// # Error
/// If there is a problem starting the application, 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
///
@ -781,17 +849,22 @@ impl Rocket {
/// #[tokio::main]
/// async fn main() {
/// # if false {
/// let result = rocket::ignite().serve().await;
/// let result = rocket::ignite().launch();
/// assert!(result.is_ok());
/// # }
/// }
/// ```
pub async fn serve(self) -> Result<(), crate::error::Error> {
async fn serve(self) -> Result<(), crate::error::Error> {
use std::net::ToSocketAddrs;
use crate::error::Error::Launch;
let full_addr = format!("{}:{}", self.config.address, self.config.port);
let mut manifest = self.actualize_and_take_manifest();
manifest.prelaunch_check().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))),
@ -803,7 +876,7 @@ impl Rocket {
shutdown_handle,
(cancel_ctrl_c_listener_sender, cancel_ctrl_c_listener_receiver)
) = (
self.get_shutdown_handle(),
manifest.get_shutdown_handle(),
oneshot::channel(),
);
@ -814,13 +887,14 @@ impl Rocket {
Ok(ok) => ok,
Err(err) => return Err(Launch(LaunchError::new(LaunchErrorKind::Bind(err)))),
};
self.listen_on(listener)
manifest.listen_on(listener)
}};
}
#[cfg(feature = "tls")]
{
if let Some(tls) = self.config.tls.clone() {
let config = manifest.config();
if let Some(tls) = 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()
@ -887,11 +961,13 @@ impl Rocket {
/// rocket::ignite().launch();
/// # }
/// ```
pub fn launch(self) -> Result<(), crate::error::Error> {
pub fn launch(mut self) -> Result<(), crate::error::Error> {
let workers = self.inspect().config().workers as usize;
// Initialize the tokio runtime
let mut runtime = tokio::runtime::Builder::new()
.threaded_scheduler()
.core_threads(self.config.workers as usize)
.core_threads(workers)
.enable_all()
.build()
.expect("Cannot build runtime!");
@ -899,6 +975,30 @@ impl Rocket {
runtime.block_on(async move { self.serve().await })
}
pub(crate) fn _manifest(&self) -> &Manifest {
self.manifest.as_ref().expect("TODO error message")
}
/// Access the current state of this `Rocket` instance.
///
/// The `Mnaifest` type provides methods such as [`Manifest::routes`]
/// and [`Manifest::state`]. This method is called to get an `Manifest`
/// instance.
///
/// # Example
///
/// ```rust
/// let mut rocket = rocket::ignite();
/// let config = rocket.inspect().config();
/// # let _ = config;
/// ```
pub fn inspect(&mut self) -> &Manifest {
self.actualize_manifest();
self._manifest()
}
}
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.
@ -909,8 +1009,8 @@ impl Rocket {
/// # #![feature(proc_macro_hygiene)]
/// # use std::{thread, time::Duration};
/// #
/// let rocket = rocket::ignite();
/// let handle = rocket.get_shutdown_handle();
/// let mut rocket = rocket::ignite();
/// let handle = rocket.inspect().get_shutdown_handle();
///
/// # if false {
/// thread::spawn(move || {
@ -945,11 +1045,11 @@ impl Rocket {
/// }
///
/// fn main() {
/// let rocket = rocket::ignite()
/// let mut rocket = rocket::ignite()
/// .mount("/", routes![hello])
/// .mount("/hi", routes![hello]);
///
/// for route in rocket.routes() {
/// for route in rocket.inspect().routes() {
/// match route.base() {
/// "/" => assert_eq!(route.uri.path(), "/hello"),
/// "/hi" => assert_eq!(route.uri.path(), "/hi/hello"),
@ -957,11 +1057,11 @@ impl Rocket {
/// }
/// }
///
/// assert_eq!(rocket.routes().count(), 2);
/// assert_eq!(rocket.inspect().routes().count(), 2);
/// }
/// ```
#[inline(always)]
pub fn routes<'a>(&'a self) -> impl Iterator<Item = &'a Route> + 'a {
pub fn routes(&self) -> impl Iterator<Item = &Route> + '_ {
self.router.routes()
}
@ -974,11 +1074,11 @@ impl Rocket {
/// #[derive(PartialEq, Debug)]
/// struct MyState(&'static str);
///
/// let rocket = rocket::ignite().manage(MyState("hello!"));
/// assert_eq!(rocket.state::<MyState>(), Some(&MyState("hello!")));
/// let mut rocket = rocket::ignite().manage(MyState("hello!"));
/// assert_eq!(rocket.inspect().state::<MyState>(), Some(&MyState("hello!")));
///
/// let client = rocket::local::Client::new(rocket).expect("valid rocket");
/// assert_eq!(client.rocket().state::<MyState>(), Some(&MyState("hello!")));
/// assert_eq!(client.manifest().state::<MyState>(), Some(&MyState("hello!")));
/// ```
#[inline(always)]
pub fn state<T: Send + Sync + 'static>(&self) -> Option<&T> {

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, m, Origin::dummy());
let mut req = Request::new(rocket._manifest(), 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, Get, Origin::parse(a).expect("valid URI"));
let req = Request::new(&rocket._manifest(), Get, Origin::parse(a).expect("valid URI"));
let route = Route::ranked(0, Get, b.to_string(), dummy_handler);
route.matches(&req)
}

View File

@ -52,7 +52,7 @@ impl Router {
matches
}
pub(crate) fn collisions(mut self) -> Result<Router, Vec<(Route, Route)>> {
pub(crate) fn collisions(&mut self) -> Result<(), Vec<(Route, Route)>> {
let mut collisions = vec![];
for routes in self.routes.values_mut() {
for i in 0..routes.len() {
@ -72,7 +72,7 @@ impl Router {
}
if collisions.is_empty() {
Ok(self)
Ok(())
} else {
Err(collisions)
}
@ -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, method, Origin::parse(uri).unwrap());
let request = Request::new(rocket._manifest(), 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, method, Origin::parse(uri).unwrap());
let request = Request::new(rocket._manifest(), method, Origin::parse(uri).unwrap());
router.route(&request)
}

View File

@ -55,9 +55,9 @@ pub fn test_config(environment: Environment) {
std::env::set_var("ROCKET_ENV", environment.to_string());
let rocket = rocket::ignite()
.attach(AdHoc::on_attach("Local Config", |rocket| {
.attach(AdHoc::on_attach("Local Config", |mut rocket| {
println!("Attaching local config.");
let config = rocket.config().clone();
let config = rocket.inspect().config().clone();
Ok(rocket.manage(LocalConfig(config)))
}))
.mount("/", routes![check_config]);

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.rocket(), "index", &context).unwrap();
let template = Template::show(client.manifest(), "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.rocket(), "index", &context).unwrap();
let template = Template::show(client.manifest(), "index", &context).unwrap();
test_body(Some(Cookie::new("message", "Hello from Rocket!")), template).await;
}

View File

@ -67,9 +67,9 @@ fn rocket() -> rocket::Rocket {
rocket::ignite()
.mount("/", routes![hello, token])
.attach(Counter::default())
.attach(AdHoc::on_attach("Token State", |rocket| {
.attach(AdHoc::on_attach("Token State", |mut rocket| {
println!("Adding token managed state...");
let token_val = rocket.config().get_int("token").unwrap_or(-1);
let token_val = rocket.inspect().config().get_int("token").unwrap_or(-1);
Ok(rocket.manage(Token(token_val)))
}))
.attach(AdHoc::on_launch("Launch Message", |_| {

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.rocket(), "error/404", &map).unwrap();
let expected = Template::show(client.manifest(), "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.rocket(), "index", &context).unwrap();
let expected = Template::show(client.manifest(), "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.rocket(), "error/404", &map).unwrap();
let expected = Template::show(client.manifest(), "error/404", &map).unwrap();
assert_eq!(response.status(), Status::NotFound);
assert_eq!(response.body_string().await, Some(expected));
});

View File

@ -8,13 +8,13 @@ async fn test() {
let client = Client::new(rocket()).unwrap();
client.get("/sync").dispatch().await;
let atomics = client.rocket().state::<Atomics>().unwrap();
let atomics = client.manifest().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.rocket().state::<Atomics>().unwrap();
let atomics = client.manifest().state::<Atomics>().unwrap();
assert_eq!(atomics.uncached.load(Ordering::Relaxed), 4);
assert_eq!(atomics.cached.load(Ordering::Relaxed), 2);
}

View File

@ -30,11 +30,12 @@ fn test_raw_state_count() {
use rocket::State;
use super::{count, index};
let rocket = super::rocket();
let mut rocket = super::rocket();
let manifest = rocket.inspect();
assert_eq!(count(State::from(&rocket).unwrap()), "0");
assert!(index(State::from(&rocket).unwrap()).0.contains("Visits: 1"));
assert_eq!(count(State::from(&rocket).unwrap()), "1");
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");
}
// 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.rocket(), "error/404", &map).unwrap();
let expected = Template::show(client.manifest(), "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.rocket(), "index", &context).unwrap();
let expected = Template::show(client.manifest(), "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.rocket(), "error/404", &map).unwrap();
let expected = Template::show(client.manifest(), "error/404", &map).unwrap();
assert_eq!(response.status(), Status::NotFound);
assert_eq!(response.body_string().await, Some(expected));
});

View File

@ -93,8 +93,8 @@ fn index(msg: Option<FlashMessage<'_, '_>>, conn: DbConn) -> Template {
})
}
fn run_db_migrations(rocket: Rocket) -> Result<Rocket, Rocket> {
let conn = DbConn::get_one(&rocket).expect("database connection");
fn run_db_migrations(mut rocket: Rocket) -> Result<Rocket, Rocket> {
let conn = DbConn::get_one(rocket.inspect()).expect("database connection");
match embedded_migrations::run(&*conn) {
Ok(()) => Ok(rocket),
Err(e) => {

View File

@ -16,8 +16,8 @@ macro_rules! run_test {
let _lock = DB_LOCK.lock();
rocket::async_test(async move {
let rocket = super::rocket();
let db = super::DbConn::get_one(&rocket);
let mut rocket = super::rocket();
let db = super::DbConn::get_one(rocket.inspect());
let $client = Client::new(rocket).expect("Rocket client");
let $conn = db.expect("failed to get database connection for testing");
Task::delete_all(&$conn).expect("failed to delete all tasks for testing");

View File

@ -209,8 +209,8 @@ fn main() {
# if false {
rocket::ignite()
.mount("/", routes![assets])
.attach(AdHoc::on_attach("Assets Config", |rocket| {
let assets_dir = rocket.config()
.attach(AdHoc::on_attach("Assets Config", |mut rocket| {
let assets_dir = rocket.inspect().config()
.get_str("assets_dir")
.unwrap_or("assets/")
.to_string();