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 {
|
pub fn fairing() -> impl ::rocket::fairing::Fairing {
|
||||||
use #databases::Poolable;
|
use #databases::Poolable;
|
||||||
|
|
||||||
::rocket::fairing::AdHoc::on_attach(#fairing_name, |rocket| {
|
::rocket::fairing::AdHoc::on_attach(#fairing_name, |mut rocket| {
|
||||||
let pool = #databases::database_config(#name, rocket.config())
|
let pool = #databases::database_config(#name, rocket.inspect().config())
|
||||||
.map(<#conn_type>::pool);
|
.map(<#conn_type>::pool);
|
||||||
|
|
||||||
match 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`
|
/// Retrieves a connection of type `Self` from the `rocket`
|
||||||
/// instance. Returns `Some` as long as `Self::fairing()` has been
|
/// instance. Returns `Some` as long as `Self::fairing()` has been
|
||||||
/// attached and there is at least one connection in the pool.
|
/// attached and there is at least one connection in the pool.
|
||||||
pub fn get_one(rocket: &::rocket::Rocket) -> Option<Self> {
|
pub fn get_one(manifest: &::rocket::Manifest) -> Option<Self> {
|
||||||
rocket.state::<#pool_type>()
|
manifest.state::<#pool_type>()
|
||||||
.and_then(|pool| pool.0.get().ok())
|
.and_then(|pool| pool.0.get().ok())
|
||||||
.map(#guard_type)
|
.map(#guard_type)
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,7 +239,7 @@
|
||||||
//! Returns a fairing that initializes the associated database connection
|
//! Returns a fairing that initializes the associated database connection
|
||||||
//! pool.
|
//! pool.
|
||||||
//!
|
//!
|
||||||
//! * `fn get_one(&Rocket) -> Option<Self>`
|
//! * `fn get_one(&Manifest) -> Option<Self>`
|
||||||
//!
|
//!
|
||||||
//! Retrieves a connection from the configured pool. Returns `Some` as long
|
//! Retrieves a connection from the configured pool. Returns `Some` as long
|
||||||
//! as `Self::fairing()` has been attached and there is at least one
|
//! as `Self::fairing()` has been attached and there is at least one
|
||||||
|
@ -548,16 +548,17 @@ pub enum ConfigError {
|
||||||
/// # .extra("databases", databases)
|
/// # .extra("databases", databases)
|
||||||
/// # .expect("custom config okay");
|
/// # .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.url, "db/db.sqlite");
|
||||||
/// assert_eq!(config.pool_size, 25);
|
/// 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");
|
/// 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);
|
/// assert_eq!(error, ConfigError::MissingKey);
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
/// #
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use rocket::http::uncased::UncasedStr;
|
use rocket::http::uncased::UncasedStr;
|
||||||
use rocket::fairing::{Fairing, Info, Kind};
|
use rocket::fairing::{Fairing, Info, Kind};
|
||||||
use rocket::{Request, Response, Rocket};
|
use rocket::{Manifest, Request, Response};
|
||||||
|
|
||||||
use crate::helmet::*;
|
use crate::helmet::*;
|
||||||
|
|
||||||
|
@ -201,9 +201,9 @@ impl Fairing for SpaceHelmet {
|
||||||
self.apply(res);
|
self.apply(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_launch(&self, rocket: &Rocket) {
|
fn on_launch(&self, manifest: &Manifest) {
|
||||||
if rocket.config().tls_enabled()
|
if manifest.config().tls_enabled()
|
||||||
&& !rocket.config().environment.is_dev()
|
&& !manifest.config().environment.is_dev()
|
||||||
&& !self.is_enabled::<Hsts>()
|
&& !self.is_enabled::<Hsts>()
|
||||||
{
|
{
|
||||||
warn_!("Space Helmet: deploying with TLS without enabling 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
|
/// The user's callback, if any was supplied, is called to customize the
|
||||||
/// template engines. In debug mode, the `ContextManager::new` method
|
/// template engines. In debug mode, the `ContextManager::new` method
|
||||||
/// initializes a directory watcher for auto-reloading of templates.
|
/// initializes a directory watcher for auto-reloading of templates.
|
||||||
fn on_attach(&self, rocket: Rocket) -> Result<Rocket, Rocket> {
|
fn on_attach(&self, mut rocket: Rocket) -> Result<Rocket, Rocket> {
|
||||||
let mut template_root = rocket.config().root_relative(DEFAULT_TEMPLATE_DIR);
|
let manifest = rocket.inspect();
|
||||||
match rocket.config().get_str("template_dir") {
|
let config = manifest.config();
|
||||||
Ok(dir) => template_root = rocket.config().root_relative(dir),
|
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(ConfigError::Missing(_)) => { /* ignore missing */ }
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
e.pretty_print();
|
e.pretty_print();
|
||||||
|
|
|
@ -141,7 +141,7 @@ use serde_json::{Value, to_value};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use rocket::{Rocket, State};
|
use rocket::{Manifest, State};
|
||||||
use rocket::request::Request;
|
use rocket::request::Request;
|
||||||
use rocket::fairing::Fairing;
|
use rocket::fairing::Fairing;
|
||||||
use rocket::response::{self, Content, Responder};
|
use rocket::response::{self, Content, Responder};
|
||||||
|
@ -339,14 +339,14 @@ impl Template {
|
||||||
///
|
///
|
||||||
/// # context.insert("test", "test");
|
/// # context.insert("test", "test");
|
||||||
/// # #[allow(unused_variables)]
|
/// # #[allow(unused_variables)]
|
||||||
/// let template = Template::show(client.rocket(), "index", context);
|
/// let template = Template::show(client.manifest(), "index", context);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[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
|
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.");
|
warn!("Uninitialized template context: missing fairing.");
|
||||||
info!("To use templates, you must attach `Template::fairing()`.");
|
info!("To use templates, you must attach `Template::fairing()`.");
|
||||||
info!("See the `Template` documentation for more information.");
|
info!("See the `Template` documentation for more information.");
|
||||||
|
|
|
@ -33,8 +33,8 @@ mod rusqlite_integration_test {
|
||||||
.finalize()
|
.finalize()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let rocket = rocket::custom(config).attach(SqliteDb::fairing());
|
let mut rocket = rocket::custom(config).attach(SqliteDb::fairing());
|
||||||
let mut conn = SqliteDb::get_one(&rocket).expect("unable to get connection");
|
let mut conn = SqliteDb::get_one(rocket.inspect()).expect("unable to get connection");
|
||||||
|
|
||||||
// Rusqlite's `transaction()` method takes `&mut self`; this tests the
|
// Rusqlite's `transaction()` method takes `&mut self`; this tests the
|
||||||
// presence of a `DerefMut` trait on the generated connection type.
|
// presence of a `DerefMut` trait on the generated connection type.
|
||||||
|
@ -54,8 +54,8 @@ mod rusqlite_integration_test {
|
||||||
.finalize()
|
.finalize()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let rocket = rocket::custom(config).attach(SqliteDb::fairing());
|
let mut rocket = rocket::custom(config).attach(SqliteDb::fairing());
|
||||||
let conn = SqliteDb::get_one(&rocket).expect("unable to get connection");
|
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");
|
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]
|
#[test]
|
||||||
fn test_tera_templates() {
|
fn test_tera_templates() {
|
||||||
let rocket = rocket();
|
let mut rocket = rocket();
|
||||||
|
let manifest = rocket.inspect();
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("title", "_test_");
|
map.insert("title", "_test_");
|
||||||
map.insert("content", "<script />");
|
map.insert("content", "<script />");
|
||||||
|
|
||||||
// Test with a txt file, which shouldn't escape.
|
// 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()));
|
assert_eq!(template, Some(UNESCAPED_EXPECTED.into()));
|
||||||
|
|
||||||
// Now with an HTML file, which should.
|
// 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()));
|
assert_eq!(template, Some(ESCAPED_EXPECTED.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,13 +96,14 @@ mod templates_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_handlebars_templates() {
|
fn test_handlebars_templates() {
|
||||||
let rocket = rocket();
|
let mut rocket = rocket();
|
||||||
|
let manifest = rocket.inspect();
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("title", "_test_");
|
map.insert("title", "_test_");
|
||||||
map.insert("content", "<script /> hi");
|
map.insert("content", "<script /> hi");
|
||||||
|
|
||||||
// Test with a txt file, which shouldn't escape.
|
// 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()));
|
assert_eq!(template, Some(EXPECTED.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +153,7 @@ mod templates_tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify that the initial content is correct
|
// 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()));
|
assert_eq!(initial_rendered, Some(INITIAL_TEXT.into()));
|
||||||
|
|
||||||
// write a change to the file
|
// write a change to the file
|
||||||
|
@ -162,7 +164,7 @@ mod templates_tests {
|
||||||
client.get("/").dispatch().await;
|
client.get("/").dispatch().await;
|
||||||
|
|
||||||
// if the new content is correct, we are done
|
// 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()) {
|
if new_rendered == Some(NEW_TEXT.into()) {
|
||||||
write_file(&reload_path, INITIAL_TEXT);
|
write_file(&reload_path, INITIAL_TEXT);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -109,7 +109,7 @@ mod benches {
|
||||||
|
|
||||||
// Hold all of the requests we're going to make during the benchmark.
|
// Hold all of the requests we're going to make during the benchmark.
|
||||||
let mut requests = vec![];
|
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());
|
let request = client.req(route.method, route.uri.path());
|
||||||
requests.push(request);
|
requests.push(request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,9 +174,9 @@
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! rocket::ignite()
|
//! rocket::ignite()
|
||||||
//! .attach(AdHoc::on_attach("Token Config", |rocket| {
|
//! .attach(AdHoc::on_attach("Token Config", |mut rocket| {
|
||||||
//! println!("Adding token managed state from config...");
|
//! 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)))
|
//! Ok(rocket.manage(Token(token_val)))
|
||||||
//! }))
|
//! }))
|
||||||
//! # ;
|
//! # ;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::Mutex;
|
||||||
|
|
||||||
use futures_util::future::BoxFuture;
|
use futures_util::future::BoxFuture;
|
||||||
|
|
||||||
use crate::{Rocket, Request, Response, Data};
|
use crate::{Manifest, Rocket, Request, Response, Data};
|
||||||
use crate::fairing::{Fairing, Kind, Info};
|
use crate::fairing::{Fairing, Kind, Info};
|
||||||
|
|
||||||
/// A ad-hoc fairing that can be created from a function or closure.
|
/// 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.
|
/// An ad-hoc **attach** fairing. Called when the fairing is attached.
|
||||||
Attach(Mutex<Option<Box<dyn FnOnce(Rocket) -> Result<Rocket, Rocket> + Send + 'static>>>),
|
Attach(Mutex<Option<Box<dyn FnOnce(Rocket) -> Result<Rocket, Rocket> + Send + 'static>>>),
|
||||||
/// An ad-hoc **launch** fairing. Called just before Rocket launches.
|
/// 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.
|
/// 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>),
|
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
|
/// 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
|
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)))) }
|
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 {
|
if let AdHocKind::Launch(ref mutex) = self.kind {
|
||||||
let mut opt = mutex.lock().expect("AdHoc::Launch lock");
|
let mut opt = mutex.lock().expect("AdHoc::Launch lock");
|
||||||
let f = opt.take().expect("internal error: `on_launch` one-call invariant broken");
|
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::fairing::{Fairing, Kind};
|
||||||
use crate::logger::PaintExt;
|
use crate::logger::PaintExt;
|
||||||
|
|
||||||
|
@ -52,9 +52,9 @@ impl Fairings {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn handle_launch(&self, rocket: &Rocket) {
|
pub fn handle_launch(&self, manifest: &Manifest) {
|
||||||
for &i in &self.launch {
|
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
|
//! of other `Fairings` are not jeopardized. For instance, unless it is made
|
||||||
//! abundantly clear, a fairing should not rewrite every request.
|
//! 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 fairings;
|
||||||
mod ad_hoc;
|
mod ad_hoc;
|
||||||
|
@ -196,7 +196,7 @@ pub use self::info_kind::{Info, Kind};
|
||||||
/// decorated with an attribute of `#[rocket::async_trait]`:
|
/// decorated with an attribute of `#[rocket::async_trait]`:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use rocket::{Rocket, Request, Data, Response};
|
/// use rocket::{Manifest, Rocket, Request, Data, Response};
|
||||||
/// use rocket::fairing::{Fairing, Info, Kind};
|
/// use rocket::fairing::{Fairing, Info, Kind};
|
||||||
///
|
///
|
||||||
/// # struct MyType;
|
/// # struct MyType;
|
||||||
|
@ -212,7 +212,7 @@ pub use self::info_kind::{Info, Kind};
|
||||||
/// # unimplemented!()
|
/// # unimplemented!()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn on_launch(&self, rocket: &Rocket) {
|
/// fn on_launch(&self, manifest: &Manifest) {
|
||||||
/// /* ... */
|
/// /* ... */
|
||||||
/// # unimplemented!()
|
/// # unimplemented!()
|
||||||
/// }
|
/// }
|
||||||
|
@ -421,14 +421,14 @@ pub trait Fairing: Send + Sync + 'static {
|
||||||
///
|
///
|
||||||
/// This method is called just prior to launching the application if
|
/// This method is called just prior to launching the application if
|
||||||
/// `Kind::Launch` is in the `kind` field of the `Info` structure for this
|
/// `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.
|
/// will be launched.
|
||||||
///
|
///
|
||||||
/// ## Default Implementation
|
/// ## Default Implementation
|
||||||
///
|
///
|
||||||
/// The default implementation of this method does nothing.
|
/// The default implementation of this method does nothing.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn on_launch(&self, rocket: &Rocket) {}
|
fn on_launch(&self, manifest: &Manifest) {}
|
||||||
|
|
||||||
/// The request callback.
|
/// The request callback.
|
||||||
///
|
///
|
||||||
|
@ -470,8 +470,8 @@ impl<T: Fairing> Fairing for std::sync::Arc<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn on_launch(&self, rocket: &Rocket) {
|
fn on_launch(&self, manifest: &Manifest) {
|
||||||
(self as &T).on_launch(rocket)
|
(self as &T).on_launch(manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -140,7 +140,7 @@ mod ext;
|
||||||
pub use crate::router::Route;
|
pub use crate::router::Route;
|
||||||
pub use crate::request::{Request, State};
|
pub use crate::request::{Request, State};
|
||||||
pub use crate::catcher::Catcher;
|
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`.
|
/// Alias to [`Rocket::ignite()`] Creates a new instance of `Rocket`.
|
||||||
pub fn ignite() -> Rocket {
|
pub fn ignite() -> Rocket {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::Rocket;
|
use crate::rocket::{Rocket, Manifest};
|
||||||
use crate::local::LocalRequest;
|
use crate::local::LocalRequest;
|
||||||
use crate::http::{Method, private::CookieJar};
|
use crate::http::{Method, private::CookieJar};
|
||||||
use crate::error::LaunchError;
|
use crate::error::LaunchError;
|
||||||
|
@ -70,7 +70,7 @@ use crate::error::LaunchError;
|
||||||
/// [`put()`]: #method.put
|
/// [`put()`]: #method.put
|
||||||
/// [`post()`]: #method.post
|
/// [`post()`]: #method.post
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
rocket: Rocket,
|
manifest: Manifest,
|
||||||
pub(crate) cookies: Option<RwLock<CookieJar>>,
|
pub(crate) cookies: Option<RwLock<CookieJar>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,12 +79,15 @@ impl Client {
|
||||||
/// is created for cookie tracking. Otherwise, the internal `CookieJar` is
|
/// is created for cookie tracking. Otherwise, the internal `CookieJar` is
|
||||||
/// set to `None`.
|
/// set to `None`.
|
||||||
fn _new(rocket: Rocket, tracked: bool) -> Result<Client, LaunchError> {
|
fn _new(rocket: Rocket, tracked: bool) -> Result<Client, LaunchError> {
|
||||||
|
let mut manifest = rocket.actualize_and_take_manifest();
|
||||||
|
manifest.prelaunch_check()?;
|
||||||
|
|
||||||
let cookies = match tracked {
|
let cookies = match tracked {
|
||||||
true => Some(RwLock::new(CookieJar::new())),
|
true => Some(RwLock::new(CookieJar::new())),
|
||||||
false => None
|
false => None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Client { rocket: rocket.prelaunch_check()?, cookies })
|
Ok(Client { manifest, cookies })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new `Client` from an instance of `Rocket` with cookie
|
/// Construct a new `Client` from an instance of `Rocket` with cookie
|
||||||
|
@ -146,7 +149,8 @@ impl Client {
|
||||||
Client::_new(rocket, false)
|
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
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -156,12 +160,12 @@ impl Client {
|
||||||
/// let my_rocket = rocket::ignite();
|
/// let my_rocket = rocket::ignite();
|
||||||
/// let client = Client::new(my_rocket).expect("valid rocket");
|
/// let client = Client::new(my_rocket).expect("valid rocket");
|
||||||
///
|
///
|
||||||
/// // get the instance of `my_rocket` within `client`
|
/// // get access to the manifest within `client`
|
||||||
/// let my_rocket = client.rocket();
|
/// let manifest = client.manifest();
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn rocket(&self) -> &Rocket {
|
pub fn manifest(&self) -> &Manifest {
|
||||||
&self.rocket
|
&self.manifest
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a local `GET` request to the URI `uri`.
|
/// Create a local `GET` request to the URI `uri`.
|
||||||
|
|
|
@ -103,7 +103,7 @@ impl<'c> LocalRequest<'c> {
|
||||||
uri: Cow<'c, str>
|
uri: Cow<'c, str>
|
||||||
) -> LocalRequest<'c> {
|
) -> LocalRequest<'c> {
|
||||||
// We set a dummy string for now and check the user's URI on dispatch.
|
// 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.
|
// Set up any cookies we know about.
|
||||||
if let Some(ref jar) = client.cookies {
|
if let Some(ref jar) = client.cookies {
|
||||||
|
@ -406,12 +406,12 @@ impl<'c> LocalRequest<'c> {
|
||||||
request.set_uri(uri.into_owned());
|
request.set_uri(uri.into_owned());
|
||||||
} else {
|
} else {
|
||||||
error!("Malformed request URI: {}", uri);
|
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 };
|
return LocalResponse { _request: owned_request, response: res };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually dispatch the request.
|
// 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
|
// If the client is tracking cookies, updates the internal cookie jar
|
||||||
// with the changes reflected by `response`.
|
// 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::{FromParam, FromSegments, FromRequest, Outcome};
|
||||||
use crate::request::{FromFormValue, FormItems, FormItem};
|
use crate::request::{FromFormValue, FormItems, FormItem};
|
||||||
|
|
||||||
use crate::rocket::Rocket;
|
use crate::rocket::{Rocket, Manifest};
|
||||||
use crate::router::Route;
|
use crate::router::Route;
|
||||||
use crate::config::{Config, Limits};
|
use crate::config::{Config, Limits};
|
||||||
use crate::http::{hyper, uri::{Origin, Segments}};
|
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`.
|
/// Create a new `Request` with the given `method` and `uri`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn new<'s: 'r>(
|
pub(crate) fn new<'s: 'r>(
|
||||||
rocket: &'r Rocket,
|
manifest: &'r Manifest,
|
||||||
method: Method,
|
method: Method,
|
||||||
uri: Origin<'s>
|
uri: Origin<'s>
|
||||||
) -> Request<'r> {
|
) -> Request<'r> {
|
||||||
|
@ -72,8 +72,8 @@ impl<'r> Request<'r> {
|
||||||
state: RequestState {
|
state: RequestState {
|
||||||
path_segments: SmallVec::new(),
|
path_segments: SmallVec::new(),
|
||||||
query_items: None,
|
query_items: None,
|
||||||
config: &rocket.config,
|
config: &manifest.config,
|
||||||
managed: &rocket.state,
|
managed: &manifest.state,
|
||||||
route: RwLock::new(None),
|
route: RwLock::new(None),
|
||||||
cookies: Mutex::new(Some(CookieJar::new())),
|
cookies: Mutex::new(Some(CookieJar::new())),
|
||||||
accept: Storage::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) {
|
pub fn example<F: Fn(&mut Request<'_>)>(method: Method, uri: &str, f: F) {
|
||||||
let rocket = Rocket::custom(Config::development());
|
let rocket = Rocket::custom(Config::development());
|
||||||
let uri = Origin::parse(uri).expect("invalid URI in example");
|
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);
|
f(&mut request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,7 +821,7 @@ impl<'r> Request<'r> {
|
||||||
|
|
||||||
/// Convert from Hyper types into a Rocket Request.
|
/// Convert from Hyper types into a Rocket Request.
|
||||||
pub(crate) fn from_hyp(
|
pub(crate) fn from_hyp(
|
||||||
rocket: &'r Rocket,
|
manifest: &'r Manifest,
|
||||||
h_method: hyper::Method,
|
h_method: hyper::Method,
|
||||||
h_headers: hyper::HeaderMap<hyper::HeaderValue>,
|
h_headers: hyper::HeaderMap<hyper::HeaderValue>,
|
||||||
h_uri: &'r hyper::Uri,
|
h_uri: &'r hyper::Uri,
|
||||||
|
@ -843,7 +843,7 @@ impl<'r> Request<'r> {
|
||||||
let uri = Origin::parse(uri).map_err(|e| e.to_string())?;
|
let uri = Origin::parse(uri).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
// Construct the request object.
|
// 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);
|
request.set_remote(h_addr);
|
||||||
|
|
||||||
// Set the request cookies, if they exist.
|
// Set the request cookies, if they exist.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use crate::Rocket;
|
use crate::rocket::Manifest;
|
||||||
use crate::request::{self, FromRequest, Request};
|
use crate::request::{self, FromRequest, Request};
|
||||||
use crate::outcome::Outcome;
|
use crate::outcome::Outcome;
|
||||||
use crate::http::Status;
|
use crate::http::Status;
|
||||||
|
@ -99,8 +99,8 @@ use crate::http::Status;
|
||||||
/// state.0.to_string()
|
/// state.0.to_string()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// let rocket = rocket::ignite().manage(MyManagedState(127));
|
/// let mut rocket = rocket::ignite().manage(MyManagedState(127));
|
||||||
/// let state = State::from(&rocket).expect("managing `MyManagedState`");
|
/// let state = State::from(rocket.inspect()).expect("managing `MyManagedState`");
|
||||||
/// assert_eq!(handler(state), "127");
|
/// assert_eq!(handler(state), "127");
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -152,17 +152,18 @@ impl<'r, T: Send + Sync + 'static> State<'r, T> {
|
||||||
/// #[derive(Debug, PartialEq)]
|
/// #[derive(Debug, PartialEq)]
|
||||||
/// struct Unmanaged(usize);
|
/// 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)));
|
/// 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);
|
/// assert_eq!(state, None);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from(rocket: &'r Rocket) -> Option<Self> {
|
pub fn from(manifest: &'r Manifest) -> Option<Self> {
|
||||||
rocket.state.try_get::<T>().map(State)
|
manifest.state().map(State)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ macro_rules! assert_headers {
|
||||||
// Dispatch the request and check that the headers are what we expect.
|
// Dispatch the request and check that the headers are what we expect.
|
||||||
let config = Config::development();
|
let config = Config::development();
|
||||||
let r = Rocket::custom(config);
|
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();
|
let actual_headers = req.headers();
|
||||||
for (key, values) in expected.iter() {
|
for (key, values) in expected.iter() {
|
||||||
let actual: Vec<_> = actual_headers.get(key).collect();
|
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
|
/// The main `Rocket` type: used to mount routes and catchers and launch the
|
||||||
/// application.
|
/// application.
|
||||||
pub struct Rocket {
|
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,
|
pub(crate) config: Config,
|
||||||
router: Router,
|
router: Router,
|
||||||
default_catchers: HashMap<u16, Catcher>,
|
default_catchers: HashMap<u16, Catcher>,
|
||||||
|
@ -50,7 +66,7 @@ pub struct Rocket {
|
||||||
// depends on the `HyperResponse` type, this function does the actual
|
// depends on the `HyperResponse` type, this function does the actual
|
||||||
// response processing.
|
// response processing.
|
||||||
fn hyper_service_fn(
|
fn hyper_service_fn(
|
||||||
rocket: Arc<Rocket>,
|
rocket: Arc<Manifest>,
|
||||||
h_addr: std::net::SocketAddr,
|
h_addr: std::net::SocketAddr,
|
||||||
hyp_req: hyper::Request<hyper::Body>,
|
hyp_req: hyper::Request<hyper::Body>,
|
||||||
) -> impl Future<Output = Result<hyper::Response<hyper::Body>, io::Error>> {
|
) -> impl Future<Output = Result<hyper::Response<hyper::Body>, io::Error>> {
|
||||||
|
@ -93,7 +109,7 @@ fn hyper_service_fn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rocket {
|
impl Manifest {
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn issue_response(
|
async fn issue_response(
|
||||||
&self,
|
&self,
|
||||||
|
@ -162,7 +178,7 @@ impl Rocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rocket {
|
impl Manifest {
|
||||||
/// Preprocess the request for Rocket things. Currently, this means:
|
/// Preprocess the request for Rocket things. Currently, this means:
|
||||||
///
|
///
|
||||||
/// * Rewriting the method in the request if _method form field exists.
|
/// * 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 {
|
impl Rocket {
|
||||||
/// Create a new `Rocket` application using the configuration information in
|
/// 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
|
/// `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 (shutdown_sender, shutdown_receiver) = mpsc::channel(1);
|
||||||
|
|
||||||
let rocket = Rocket {
|
let manifest = Manifest {
|
||||||
config,
|
config,
|
||||||
router: Router::new(),
|
router: Router::new(),
|
||||||
default_catchers: catcher::defaults::get(),
|
default_catchers: catcher::defaults::get(),
|
||||||
|
@ -468,9 +623,9 @@ impl Rocket {
|
||||||
shutdown_receiver: Some(shutdown_receiver),
|
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`
|
/// Mounts all of the routes in the supplied vector at the given `base`
|
||||||
|
@ -529,13 +684,7 @@ impl Rocket {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn mount<R: Into<Vec<Route>>>(mut self, base: &str, routes: R) -> Self {
|
pub fn mount<R: Into<Vec<Route>>>(mut self, base: &str, routes: R) -> Self {
|
||||||
info!("{}{} {}{}",
|
let base_uri = Origin::parse_owned(base.to_string())
|
||||||
Paint::emoji("🛰 "),
|
|
||||||
Paint::magenta("Mounting"),
|
|
||||||
Paint::blue(base),
|
|
||||||
Paint::magenta(":"));
|
|
||||||
|
|
||||||
let base_uri = Origin::parse(base)
|
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
error_!("Invalid origin URI '{}' used as mount point.", base);
|
error_!("Invalid origin URI '{}' used as mount point.", base);
|
||||||
panic!("Error: {}", e);
|
panic!("Error: {}", e);
|
||||||
|
@ -546,17 +695,7 @@ impl Rocket {
|
||||||
panic!("Invalid mount point.");
|
panic!("Invalid mount point.");
|
||||||
}
|
}
|
||||||
|
|
||||||
for mut route in routes.into() {
|
self.pending.push(BuildOperation::Mount(base_uri, 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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,18 +728,7 @@ impl Rocket {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn register(mut self, catchers: Vec<Catcher>) -> Self {
|
pub fn register(mut self, catchers: Vec<Catcher>) -> Self {
|
||||||
info!("{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:"));
|
self.pending.push(BuildOperation::Register(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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,11 +770,15 @@ impl Rocket {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn manage<T: Send + Sync + 'static>(self, state: T) -> Self {
|
pub fn manage<T: Send + Sync + 'static>(mut self, state: T) -> Self {
|
||||||
if !self.state.set::<T>(state) {
|
self.pending.push(BuildOperation::Manage(Box::new(|rocket| {
|
||||||
error!("State for this type is already being managed!");
|
if !rocket.state.set::<T>(state) {
|
||||||
panic!("Aborting due to duplicately managed state.");
|
error!("State for this type is already being managed!");
|
||||||
}
|
panic!("Aborting due to duplicately managed state.");
|
||||||
|
}
|
||||||
|
|
||||||
|
rocket
|
||||||
|
})));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -675,105 +807,41 @@ impl Rocket {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn attach<F: Fairing>(mut self, fairing: F) -> Self {
|
pub fn attach<F: Fairing>(mut self, fairing: F) -> Self {
|
||||||
// Attach (and run attach) fairings, which requires us to move `self`.
|
self.pending.push(BuildOperation::Attach(Box::new(fairing)));
|
||||||
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn prelaunch_check(mut self) -> Result<Rocket, LaunchError> {
|
pub(crate) fn actualize_manifest(&mut self) {
|
||||||
self.router = match self.router.collisions() {
|
while !self.pending.is_empty() {
|
||||||
Ok(router) => router,
|
// We need to preserve insertion order here,
|
||||||
Err(e) => return Err(LaunchError::new(LaunchErrorKind::Collision(e)))
|
// so we can't use `self.pending.pop()`
|
||||||
};
|
let op = self.pending.remove(0);
|
||||||
|
let manifest = self.manifest.take().expect("TODO error message");
|
||||||
if let Some(failures) = self.fairings.failures() {
|
self.manifest = Some(match op {
|
||||||
return Err(LaunchError::new(LaunchErrorKind::FailedFairings(failures.to_vec())))
|
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),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO.async: Solidify the Listener APIs and make this function public
|
pub(crate) fn actualize_and_take_manifest(mut self) -> Manifest {
|
||||||
async fn listen_on<L>(mut self, listener: L) -> Result<(), crate::error::Error>
|
self.actualize_manifest();
|
||||||
where
|
self.manifest.take().expect("internal error: actualize_manifest() should have replaced self.manifest")
|
||||||
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)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
#[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)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `Future` that drives the server and completes when the server
|
/// Returns a `Future` that drives the server, listening for and dispathcing
|
||||||
/// is shut down or errors. If the `ctrl_c_shutdown` feature is enabled,
|
/// requests to mounted routes and catchers. The `Future` completes when the
|
||||||
/// the server will shut down gracefully once `Ctrl-C` is pressed.
|
/// 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
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -781,17 +849,22 @@ impl Rocket {
|
||||||
/// #[tokio::main]
|
/// #[tokio::main]
|
||||||
/// async fn main() {
|
/// async fn main() {
|
||||||
/// # if false {
|
/// # if false {
|
||||||
/// let result = rocket::ignite().serve().await;
|
/// let result = rocket::ignite().launch();
|
||||||
/// assert!(result.is_ok());
|
/// 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 std::net::ToSocketAddrs;
|
||||||
|
|
||||||
use crate::error::Error::Launch;
|
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() {
|
let addrs = match full_addr.to_socket_addrs() {
|
||||||
Ok(a) => a.collect::<Vec<_>>(),
|
Ok(a) => a.collect::<Vec<_>>(),
|
||||||
Err(e) => return Err(Launch(From::from(e))),
|
Err(e) => return Err(Launch(From::from(e))),
|
||||||
|
@ -803,7 +876,7 @@ impl Rocket {
|
||||||
shutdown_handle,
|
shutdown_handle,
|
||||||
(cancel_ctrl_c_listener_sender, cancel_ctrl_c_listener_receiver)
|
(cancel_ctrl_c_listener_sender, cancel_ctrl_c_listener_receiver)
|
||||||
) = (
|
) = (
|
||||||
self.get_shutdown_handle(),
|
manifest.get_shutdown_handle(),
|
||||||
oneshot::channel(),
|
oneshot::channel(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -814,13 +887,14 @@ impl Rocket {
|
||||||
Ok(ok) => ok,
|
Ok(ok) => ok,
|
||||||
Err(err) => return Err(Launch(LaunchError::new(LaunchErrorKind::Bind(err)))),
|
Err(err) => return Err(Launch(LaunchError::new(LaunchErrorKind::Bind(err)))),
|
||||||
};
|
};
|
||||||
self.listen_on(listener)
|
manifest.listen_on(listener)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "tls")]
|
#[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()
|
listen_on!(crate::http::tls::bind_tls(addr, tls.certs, tls.key).await).boxed()
|
||||||
} else {
|
} else {
|
||||||
listen_on!(crate::http::private::bind_tcp(addr).await).boxed()
|
listen_on!(crate::http::private::bind_tcp(addr).await).boxed()
|
||||||
|
@ -887,11 +961,13 @@ impl Rocket {
|
||||||
/// rocket::ignite().launch();
|
/// 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
|
// Initialize the tokio runtime
|
||||||
let mut runtime = tokio::runtime::Builder::new()
|
let mut runtime = tokio::runtime::Builder::new()
|
||||||
.threaded_scheduler()
|
.threaded_scheduler()
|
||||||
.core_threads(self.config.workers as usize)
|
.core_threads(workers)
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()
|
.build()
|
||||||
.expect("Cannot build runtime!");
|
.expect("Cannot build runtime!");
|
||||||
|
@ -899,6 +975,30 @@ impl Rocket {
|
||||||
runtime.block_on(async move { self.serve().await })
|
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
|
/// Returns a [`ShutdownHandle`], which can be used to gracefully terminate
|
||||||
/// the instance of Rocket. In routes, you should use the [`ShutdownHandle`]
|
/// the instance of Rocket. In routes, you should use the [`ShutdownHandle`]
|
||||||
/// request guard.
|
/// request guard.
|
||||||
|
@ -909,8 +1009,8 @@ impl Rocket {
|
||||||
/// # #![feature(proc_macro_hygiene)]
|
/// # #![feature(proc_macro_hygiene)]
|
||||||
/// # use std::{thread, time::Duration};
|
/// # use std::{thread, time::Duration};
|
||||||
/// #
|
/// #
|
||||||
/// let rocket = rocket::ignite();
|
/// let mut rocket = rocket::ignite();
|
||||||
/// let handle = rocket.get_shutdown_handle();
|
/// let handle = rocket.inspect().get_shutdown_handle();
|
||||||
///
|
///
|
||||||
/// # if false {
|
/// # if false {
|
||||||
/// thread::spawn(move || {
|
/// thread::spawn(move || {
|
||||||
|
@ -945,11 +1045,11 @@ impl Rocket {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let rocket = rocket::ignite()
|
/// let mut rocket = rocket::ignite()
|
||||||
/// .mount("/", routes![hello])
|
/// .mount("/", routes![hello])
|
||||||
/// .mount("/hi", routes![hello]);
|
/// .mount("/hi", routes![hello]);
|
||||||
///
|
///
|
||||||
/// for route in rocket.routes() {
|
/// for route in rocket.inspect().routes() {
|
||||||
/// match route.base() {
|
/// match route.base() {
|
||||||
/// "/" => assert_eq!(route.uri.path(), "/hello"),
|
/// "/" => assert_eq!(route.uri.path(), "/hello"),
|
||||||
/// "/hi" => assert_eq!(route.uri.path(), "/hi/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)]
|
#[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()
|
self.router.routes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -974,11 +1074,11 @@ impl Rocket {
|
||||||
/// #[derive(PartialEq, Debug)]
|
/// #[derive(PartialEq, Debug)]
|
||||||
/// struct MyState(&'static str);
|
/// struct MyState(&'static str);
|
||||||
///
|
///
|
||||||
/// let rocket = rocket::ignite().manage(MyState("hello!"));
|
/// let mut rocket = rocket::ignite().manage(MyState("hello!"));
|
||||||
/// assert_eq!(rocket.state::<MyState>(), Some(&MyState("hello!")));
|
/// assert_eq!(rocket.inspect().state::<MyState>(), Some(&MyState("hello!")));
|
||||||
///
|
///
|
||||||
/// let client = rocket::local::Client::new(rocket).expect("valid rocket");
|
/// 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)]
|
#[inline(always)]
|
||||||
pub fn state<T: Send + Sync + 'static>(&self) -> Option<&T> {
|
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>>
|
where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>>
|
||||||
{
|
{
|
||||||
let rocket = Rocket::custom(Config::development());
|
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 let Some(mt_str) = mt1.into() {
|
||||||
if m.supports_payload() {
|
if m.supports_payload() {
|
||||||
req.replace_header(mt_str.parse::<ContentType>().unwrap());
|
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 {
|
fn req_route_path_match(a: &'static str, b: &'static str) -> bool {
|
||||||
let rocket = Rocket::custom(Config::development());
|
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);
|
let route = Route::ranked(0, Get, b.to_string(), dummy_handler);
|
||||||
route.matches(&req)
|
route.matches(&req)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ impl Router {
|
||||||
matches
|
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![];
|
let mut collisions = vec![];
|
||||||
for routes in self.routes.values_mut() {
|
for routes in self.routes.values_mut() {
|
||||||
for i in 0..routes.len() {
|
for i in 0..routes.len() {
|
||||||
|
@ -72,7 +72,7 @@ impl Router {
|
||||||
}
|
}
|
||||||
|
|
||||||
if collisions.is_empty() {
|
if collisions.is_empty() {
|
||||||
Ok(self)
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(collisions)
|
Err(collisions)
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ mod test {
|
||||||
|
|
||||||
fn route<'a>(router: &'a Router, method: Method, uri: &str) -> Option<&'a Route> {
|
fn route<'a>(router: &'a Router, method: Method, uri: &str) -> Option<&'a Route> {
|
||||||
let rocket = Rocket::custom(Config::development());
|
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);
|
let matches = router.route(&request);
|
||||||
if matches.len() > 0 {
|
if matches.len() > 0 {
|
||||||
Some(matches[0])
|
Some(matches[0])
|
||||||
|
@ -244,7 +244,7 @@ mod test {
|
||||||
|
|
||||||
fn matches<'a>(router: &'a Router, method: Method, uri: &str) -> Vec<&'a Route> {
|
fn matches<'a>(router: &'a Router, method: Method, uri: &str) -> Vec<&'a Route> {
|
||||||
let rocket = Rocket::custom(Config::development());
|
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)
|
router.route(&request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,9 +55,9 @@ pub fn test_config(environment: Environment) {
|
||||||
std::env::set_var("ROCKET_ENV", environment.to_string());
|
std::env::set_var("ROCKET_ENV", environment.to_string());
|
||||||
|
|
||||||
let rocket = rocket::ignite()
|
let rocket = rocket::ignite()
|
||||||
.attach(AdHoc::on_attach("Local Config", |rocket| {
|
.attach(AdHoc::on_attach("Local Config", |mut rocket| {
|
||||||
println!("Attaching local config.");
|
println!("Attaching local config.");
|
||||||
let config = rocket.config().clone();
|
let config = rocket.inspect().config().clone();
|
||||||
Ok(rocket.manage(LocalConfig(config)))
|
Ok(rocket.manage(LocalConfig(config)))
|
||||||
}))
|
}))
|
||||||
.mount("/", routes![check_config]);
|
.mount("/", routes![check_config]);
|
||||||
|
|
|
@ -39,11 +39,11 @@ async fn test_index() {
|
||||||
|
|
||||||
// Render the template with an empty context.
|
// Render the template with an empty context.
|
||||||
let mut context: HashMap<&str, &str> = HashMap::new();
|
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;
|
test_body(None, template).await;
|
||||||
|
|
||||||
// Render the template with a context that contains the message.
|
// Render the template with a context that contains the message.
|
||||||
context.insert("message", "Hello from Rocket!");
|
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;
|
test_body(Some(Cookie::new("message", "Hello from Rocket!")), template).await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,9 +67,9 @@ fn rocket() -> rocket::Rocket {
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.mount("/", routes![hello, token])
|
.mount("/", routes![hello, token])
|
||||||
.attach(Counter::default())
|
.attach(Counter::default())
|
||||||
.attach(AdHoc::on_attach("Token State", |rocket| {
|
.attach(AdHoc::on_attach("Token State", |mut rocket| {
|
||||||
println!("Adding token managed state...");
|
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)))
|
Ok(rocket.manage(Token(token_val)))
|
||||||
}))
|
}))
|
||||||
.attach(AdHoc::on_launch("Launch Message", |_| {
|
.attach(AdHoc::on_launch("Launch Message", |_| {
|
||||||
|
|
|
@ -31,7 +31,7 @@ async fn test_root() {
|
||||||
dispatch!(*method, "/", |client, response| {
|
dispatch!(*method, "/", |client, response| {
|
||||||
let mut map = std::collections::HashMap::new();
|
let mut map = std::collections::HashMap::new();
|
||||||
map.insert("path", "/");
|
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.status(), Status::NotFound);
|
||||||
assert_eq!(response.body_string().await, Some(expected));
|
assert_eq!(response.body_string().await, Some(expected));
|
||||||
|
@ -50,7 +50,7 @@ async fn test_name() {
|
||||||
parent: "layout",
|
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.status(), Status::Ok);
|
||||||
assert_eq!(response.body_string().await, Some(expected));
|
assert_eq!(response.body_string().await, Some(expected));
|
||||||
});
|
});
|
||||||
|
@ -63,7 +63,7 @@ async fn test_404() {
|
||||||
let mut map = std::collections::HashMap::new();
|
let mut map = std::collections::HashMap::new();
|
||||||
map.insert("path", "/hello/");
|
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.status(), Status::NotFound);
|
||||||
assert_eq!(response.body_string().await, Some(expected));
|
assert_eq!(response.body_string().await, Some(expected));
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,13 +8,13 @@ async fn test() {
|
||||||
let client = Client::new(rocket()).unwrap();
|
let client = Client::new(rocket()).unwrap();
|
||||||
client.get("/sync").dispatch().await;
|
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.uncached.load(Ordering::Relaxed), 2);
|
||||||
assert_eq!(atomics.cached.load(Ordering::Relaxed), 1);
|
assert_eq!(atomics.cached.load(Ordering::Relaxed), 1);
|
||||||
|
|
||||||
client.get("/async").dispatch().await;
|
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.uncached.load(Ordering::Relaxed), 4);
|
||||||
assert_eq!(atomics.cached.load(Ordering::Relaxed), 2);
|
assert_eq!(atomics.cached.load(Ordering::Relaxed), 2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,12 @@ fn test_raw_state_count() {
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
use super::{count, index};
|
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_eq!(count(State::from(manifest).unwrap()), "0");
|
||||||
assert!(index(State::from(&rocket).unwrap()).0.contains("Visits: 1"));
|
assert!(index(State::from(manifest).unwrap()).0.contains("Visits: 1"));
|
||||||
assert_eq!(count(State::from(&rocket).unwrap()), "1");
|
assert_eq!(count(State::from(manifest).unwrap()), "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cargo runs each test in parallel on different threads. We use all of these
|
// 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| {
|
dispatch!(*method, "/", |client, response| {
|
||||||
let mut map = std::collections::HashMap::new();
|
let mut map = std::collections::HashMap::new();
|
||||||
map.insert("path", "/");
|
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.status(), Status::NotFound);
|
||||||
assert_eq!(response.body_string().await, Some(expected));
|
assert_eq!(response.body_string().await, Some(expected));
|
||||||
|
@ -47,7 +47,7 @@ async fn test_name() {
|
||||||
items: vec!["One", "Two", "Three"]
|
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.status(), Status::Ok);
|
||||||
assert_eq!(response.body_string().await, Some(expected));
|
assert_eq!(response.body_string().await, Some(expected));
|
||||||
});
|
});
|
||||||
|
@ -60,7 +60,7 @@ async fn test_404() {
|
||||||
let mut map = std::collections::HashMap::new();
|
let mut map = std::collections::HashMap::new();
|
||||||
map.insert("path", "/hello/");
|
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.status(), Status::NotFound);
|
||||||
assert_eq!(response.body_string().await, Some(expected));
|
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> {
|
fn run_db_migrations(mut rocket: Rocket) -> Result<Rocket, Rocket> {
|
||||||
let conn = DbConn::get_one(&rocket).expect("database connection");
|
let conn = DbConn::get_one(rocket.inspect()).expect("database connection");
|
||||||
match embedded_migrations::run(&*conn) {
|
match embedded_migrations::run(&*conn) {
|
||||||
Ok(()) => Ok(rocket),
|
Ok(()) => Ok(rocket),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -16,8 +16,8 @@ macro_rules! run_test {
|
||||||
let _lock = DB_LOCK.lock();
|
let _lock = DB_LOCK.lock();
|
||||||
|
|
||||||
rocket::async_test(async move {
|
rocket::async_test(async move {
|
||||||
let rocket = super::rocket();
|
let mut rocket = super::rocket();
|
||||||
let db = super::DbConn::get_one(&rocket);
|
let db = super::DbConn::get_one(rocket.inspect());
|
||||||
let $client = Client::new(rocket).expect("Rocket client");
|
let $client = Client::new(rocket).expect("Rocket client");
|
||||||
let $conn = db.expect("failed to get database connection for testing");
|
let $conn = db.expect("failed to get database connection for testing");
|
||||||
Task::delete_all(&$conn).expect("failed to delete all tasks for testing");
|
Task::delete_all(&$conn).expect("failed to delete all tasks for testing");
|
||||||
|
|
|
@ -209,8 +209,8 @@ fn main() {
|
||||||
# if false {
|
# if false {
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.mount("/", routes![assets])
|
.mount("/", routes![assets])
|
||||||
.attach(AdHoc::on_attach("Assets Config", |rocket| {
|
.attach(AdHoc::on_attach("Assets Config", |mut rocket| {
|
||||||
let assets_dir = rocket.config()
|
let assets_dir = rocket.inspect().config()
|
||||||
.get_str("assets_dir")
|
.get_str("assets_dir")
|
||||||
.unwrap_or("assets/")
|
.unwrap_or("assets/")
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
Loading…
Reference in New Issue