mirror of https://github.com/rwf2/Rocket.git
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:
parent
3796673740
commit
dea940c7a8
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
/// # }
|
||||
/// #
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
//! }))
|
||||
//! # ;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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", |_| {
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue