Fairings, v3.

Modifying the `Rocket` structure just before launch doesn't make sense for
several reasons: 1) those affects can't influence the launch, and 2) they won't
be observed in tests. Thus, an `Attach` fairing kind was added that ameliorates
these issues.
This commit is contained in:
Sergio Benitez 2017-05-17 01:39:36 -07:00
parent 9c9740f966
commit 28a1ef0916
9 changed files with 249 additions and 92 deletions

View File

@ -0,0 +1,2 @@
[global]
token = 123

View File

@ -6,10 +6,12 @@ extern crate rocket;
use std::io::Cursor; use std::io::Cursor;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use rocket::{Request, Data, Response}; use rocket::{Request, State, Data, Response};
use rocket::fairing::{AdHoc, Fairing, Info, Kind}; use rocket::fairing::{AdHoc, Fairing, Info, Kind};
use rocket::http::{Method, ContentType, Status}; use rocket::http::{Method, ContentType, Status};
struct Token(i64);
#[cfg(test)] mod tests; #[cfg(test)] mod tests;
#[derive(Default)] #[derive(Default)]
@ -56,13 +58,22 @@ fn hello() -> &'static str {
"Hello, world!" "Hello, world!"
} }
#[get("/token")]
fn token(token: State<Token>) -> String {
format!("{}", token.0)
}
fn rocket() -> rocket::Rocket { fn rocket() -> rocket::Rocket {
rocket::ignite() rocket::ignite()
.mount("/", routes![hello]) .mount("/", routes![hello, token])
.attach(Counter::default()) .attach(Counter::default())
.attach(AdHoc::on_attach(|rocket| {
println!("Adding token managed state...");
let token_val = rocket.config().get_int("token").unwrap_or(-1);
Ok(rocket.manage(Token(token_val)))
}))
.attach(AdHoc::on_launch(|rocket| { .attach(AdHoc::on_launch(|rocket| {
println!("Rocket is about to launch! Exciting! Here we go..."); println!("Rocket is about to launch!");
Ok(rocket)
})) }))
.attach(AdHoc::on_request(|req, _| { .attach(AdHoc::on_request(|req, _| {
println!(" => Incoming request: {}", req); println!(" => Incoming request: {}", req);

View File

@ -9,6 +9,7 @@ fn rewrite_get_put() {
let mut response = req.dispatch_with(&rocket); let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("Hello, fairings!".into())); assert_eq!(response.body_string(), Some("Hello, fairings!".into()));
} }
#[test] #[test]
fn counts() { fn counts() {
let rocket = rocket(); let rocket = rocket();
@ -33,3 +34,13 @@ fn counts() {
let mut response = req.dispatch_with(&rocket); let mut response = req.dispatch_with(&rocket);
assert_eq!(response.body_string(), Some("Get: 4\nPost: 1".into())); assert_eq!(response.body_string(), Some("Get: 4\nPost: 1".into()));
} }
#[test]
fn token() {
let rocket = rocket();
// Ensure the token is '123', which is what we have in `Rocket.toml`.
let mut req = MockRequest::new(Get, "/token");
let mut res = req.dispatch_with(&rocket);
assert_eq!(res.body_string(), Some("123".into()));
}

View File

@ -8,7 +8,7 @@ use fairing::{Fairing, Kind, Info};
/// ///
/// # Usage /// # Usage
/// ///
/// Use the [`on_launch`](#method.on_launch), /// Use the [`on_attach`](#method.on_attach), [`on_launch`](#method.on_launch),
/// [`on_request`](#method.on_request), or [`on_response`](#method.on_response) /// [`on_request`](#method.on_request), or [`on_response`](#method.on_response)
/// constructors to create an `AdHoc` structure from a function or closure. /// constructors to create an `AdHoc` structure from a function or closure.
/// Then, simply attach the structure to the `Rocket` instance. /// Then, simply attach the structure to the `Rocket` instance.
@ -25,28 +25,47 @@ use fairing::{Fairing, Kind, Info};
/// use rocket::http::Method; /// use rocket::http::Method;
/// ///
/// rocket::ignite() /// rocket::ignite()
/// .attach(AdHoc::on_launch(|rocket| { /// .attach(AdHoc::on_launch(|_| {
/// println!("Rocket is about to launch! Exciting! Here we go..."); /// println!("Rocket is about to launch! Exciting! Here we go...");
/// Ok(rocket)
/// })) /// }))
/// .attach(AdHoc::on_request(|req, _| { /// .attach(AdHoc::on_request(|req, _| {
/// req.set_method(Method::Put); /// req.set_method(Method::Put);
/// })); /// }));
/// ``` /// ```
pub enum AdHoc { pub enum AdHoc {
/// An ad-hoc **attach** fairing. Called when the fairing is attached.
#[doc(hidden)]
Attach(Box<Fn(Rocket) -> Result<Rocket, Rocket> + Send + Sync + 'static>),
/// An ad-hoc **launch** fairing. Called just before Rocket launches. /// An ad-hoc **launch** fairing. Called just before Rocket launches.
#[doc(hidden)] #[doc(hidden)]
Launch(Box<Fn(Rocket) -> Result<Rocket, Rocket> + Send + Sync + 'static>), Launch(Box<Fn(&Rocket) + Send + Sync + 'static>),
/// An ad-hoc **request** fairing. Called when a request is received. /// An ad-hoc **request** fairing. Called when a request is received.
#[doc(hidden)] #[doc(hidden)]
Request(Box<Fn(&mut Request, &Data) + Send + Sync + 'static>), Request(Box<Fn(&mut Request, &Data) + 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
/// sent to a client. /// sent to a client.
#[doc(hidden)] #[doc(hidden)]
Response(Box<Fn(&Request, &mut Response) + Send + Sync + 'static>) Response(Box<Fn(&Request, &mut Response) + Send + Sync + 'static>),
} }
impl AdHoc { impl AdHoc {
/// Constructs an `AdHoc` attach fairing. The function `f` will be called by
/// Rocket when this fairing is attached.
///
/// # Example
///
/// ```rust
/// use rocket::fairing::AdHoc;
///
/// // The no-op attach fairing.
/// let fairing = AdHoc::on_attach(|rocket| Ok(rocket));
/// ```
pub fn on_attach<F>(f: F) -> AdHoc
where F: Fn(Rocket) -> Result<Rocket, Rocket> + Send + Sync + 'static
{
AdHoc::Attach(Box::new(f))
}
/// Constructs an `AdHoc` launch fairing. The function `f` will be called by /// Constructs an `AdHoc` launch fairing. The function `f` will be called by
/// Rocket just prior to launching. /// Rocket just prior to launching.
/// ///
@ -55,11 +74,13 @@ impl AdHoc {
/// ```rust /// ```rust
/// use rocket::fairing::AdHoc; /// use rocket::fairing::AdHoc;
/// ///
/// // The no-op launch fairing. /// // A fairing that prints a message just before launching.
/// let fairing = AdHoc::on_launch(|rocket| Ok(rocket)); /// let fairing = AdHoc::on_launch(|rocket| {
/// println!("Launching in T-3..2..1..");
/// });
/// ``` /// ```
pub fn on_launch<F>(f: F) -> AdHoc pub fn on_launch<F>(f: F) -> AdHoc
where F: Fn(Rocket) -> Result<Rocket, Rocket> + Send + Sync + 'static where F: Fn(&Rocket) + Send + Sync + 'static
{ {
AdHoc::Launch(Box::new(f)) AdHoc::Launch(Box::new(f))
} }
@ -109,16 +130,43 @@ impl Fairing for AdHoc {
fn info(&self) -> Info { fn info(&self) -> Info {
use self::AdHoc::*; use self::AdHoc::*;
match *self { match *self {
Launch(_) => Info { name: "AdHoc::Launch", kind: Kind::Launch }, Attach(_) => {
Request(_) => Info { name: "AdHoc::Request", kind: Kind::Request }, Info {
Response(_) => Info { name: "AdHoc::Response", kind: Kind::Response } name: "AdHoc::Attach",
kind: Kind::Attach,
}
}
Launch(_) => {
Info {
name: "AdHoc::Launch",
kind: Kind::Launch,
}
}
Request(_) => {
Info {
name: "AdHoc::Request",
kind: Kind::Request,
}
}
Response(_) => {
Info {
name: "AdHoc::Response",
kind: Kind::Response,
}
}
} }
} }
fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> { fn on_attach(&self, rocket: Rocket) -> Result<Rocket, Rocket> {
match *self { match *self {
AdHoc::Launch(ref launch_fn) => launch_fn(rocket), AdHoc::Attach(ref callback) => callback(rocket),
_ => Ok(rocket) _ => Ok(rocket),
}
}
fn on_launch(&self, rocket: &Rocket) {
if let AdHoc::Launch(ref callback) = *self {
callback(rocket)
} }
} }

View File

@ -4,6 +4,7 @@ use fairing::{Fairing, Kind};
#[derive(Default)] #[derive(Default)]
pub struct Fairings { pub struct Fairings {
all_fairings: Vec<Box<Fairing>>, all_fairings: Vec<Box<Fairing>>,
attach_failure: bool,
launch: Vec<&'static Fairing>, launch: Vec<&'static Fairing>,
request: Vec<&'static Fairing>, request: Vec<&'static Fairing>,
response: Vec<&'static Fairing>, response: Vec<&'static Fairing>,
@ -15,11 +16,16 @@ impl Fairings {
Fairings::default() Fairings::default()
} }
#[inline] pub fn attach(&mut self, fairing: Box<Fairing>, mut rocket: Rocket) -> Rocket {
pub fn attach(&mut self, fairing: Box<Fairing>) {
// Get the kind information. // Get the kind information.
let kind = fairing.info().kind; let kind = fairing.info().kind;
// Run the `on_attach` callback if this is an 'attach' fairing.
if kind.is(Kind::Attach) {
rocket = fairing.on_attach(rocket)
.unwrap_or_else(|r| { self.attach_failure = true; r })
}
// The `Fairings` structure separates `all_fairings` into kind groups so // The `Fairings` structure separates `all_fairings` into kind groups so
// we don't have to search through all fairings and do a comparison at // we don't have to search through all fairings and do a comparison at
// runtime. We need references since a single structure can be multiple // runtime. We need references since a single structure can be multiple
@ -36,6 +42,9 @@ impl Fairings {
// deallocating `Box<Fairing>` structures. As such, the references will // deallocating `Box<Fairing>` structures. As such, the references will
// always be valid. Note: `ptr` doesn't point into the `Vec`, so // always be valid. Note: `ptr` doesn't point into the `Vec`, so
// reallocations there are irrelvant. Instead, it points into the heap. // reallocations there are irrelvant. Instead, it points into the heap.
//
// Also, we don't save attach fairings since we don't need them anymore.
if !kind.is_exactly(Kind::Attach) {
let ptr: &'static Fairing = unsafe { ::std::mem::transmute(&*fairing) }; let ptr: &'static Fairing = unsafe { ::std::mem::transmute(&*fairing) };
self.all_fairings.push(fairing); self.all_fairings.push(fairing);
@ -44,14 +53,14 @@ impl Fairings {
if kind.is(Kind::Response) { self.response.push(ptr); } if kind.is(Kind::Response) { self.response.push(ptr); }
} }
#[inline(always)] rocket
pub fn handle_launch(&mut self, mut rocket: Rocket) -> Option<Rocket> {
let mut success = Some(());
for f in &self.launch {
rocket = f.on_launch(rocket).unwrap_or_else(|r| { success = None; r });
} }
success.map(|_| rocket) #[inline(always)]
pub fn handle_launch(&self, rocket: &Rocket) {
for fairing in &self.launch {
fairing.on_launch(rocket);
}
} }
#[inline(always)] #[inline(always)]
@ -68,6 +77,10 @@ impl Fairings {
} }
} }
pub fn had_failure(&self) -> bool {
self.attach_failure
}
pub fn pretty_print_counts(&self) { pub fn pretty_print_counts(&self) {
use term_painter::ToStyle; use term_painter::ToStyle;
use term_painter::Color::{White, Magenta}; use term_painter::Color::{White, Magenta};
@ -78,7 +91,9 @@ impl Fairings {
fn info_if_nonempty(kind: &str, fairings: &[&Fairing]) { fn info_if_nonempty(kind: &str, fairings: &[&Fairing]) {
let names: Vec<&str> = fairings.iter().map(|f| f.info().name).collect(); let names: Vec<&str> = fairings.iter().map(|f| f.info().name).collect();
info_!("{} {}: {}", White.paint(fairings.len()), kind, info_!("{} {}: {}",
White.paint(fairings.len()),
kind,
White.paint(names.join(", "))); White.paint(names.join(", ")));
} }

View File

@ -10,7 +10,7 @@ use std::ops::BitOr;
/// # Example /// # Example
/// ///
/// A simple `Info` structure that can be used for a `Fairing` that implements /// A simple `Info` structure that can be used for a `Fairing` that implements
/// all three callbacks: /// all four callbacks:
/// ///
/// ``` /// ```
/// use rocket::fairing::{Info, Kind}; /// use rocket::fairing::{Info, Kind};
@ -18,7 +18,7 @@ use std::ops::BitOr;
/// # let _unused_info = /// # let _unused_info =
/// Info { /// Info {
/// name: "Example Fairing", /// name: "Example Fairing",
/// kind: Kind::Launch | Kind::Request | Kind::Response /// kind: Kind::Attach | Kind::Launch | Kind::Request | Kind::Response
/// } /// }
/// # ; /// # ;
/// ``` /// ```
@ -35,6 +35,7 @@ pub struct Info {
/// A fairing can request any combination of any of the following kinds of /// A fairing can request any combination of any of the following kinds of
/// callbacks: /// callbacks:
/// ///
/// * Attach
/// * Launch /// * Launch
/// * Request /// * Request
/// * Response /// * Response
@ -42,18 +43,20 @@ pub struct Info {
/// Two `Kind` structures can be `or`d together to represent a combination. For /// Two `Kind` structures can be `or`d together to represent a combination. For
/// instance, to represent a fairing that is both a launch and request fairing, /// instance, to represent a fairing that is both a launch and request fairing,
/// use `Kind::Launch | Kind::Request`. Similarly, to represent a fairing that /// use `Kind::Launch | Kind::Request`. Similarly, to represent a fairing that
/// is all three kinds, use `Kind::Launch | Kind::Request | Kind::Response`. /// is only an attach fairing, use `Kind::Attach`.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Kind(usize); pub struct Kind(usize);
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
impl Kind { impl Kind {
/// `Kind` flag representing a request for an 'attach' callback.
pub const Attach: Kind = Kind(0b0001);
/// `Kind` flag representing a request for a 'launch' callback. /// `Kind` flag representing a request for a 'launch' callback.
pub const Launch: Kind = Kind(0b001); pub const Launch: Kind = Kind(0b0010);
/// `Kind` flag representing a request for a 'request' callback. /// `Kind` flag representing a request for a 'request' callback.
pub const Request: Kind = Kind(0b010); pub const Request: Kind = Kind(0b0100);
/// `Kind` flag representing a request for a 'response' callback. /// `Kind` flag representing a request for a 'response' callback.
pub const Response: Kind = Kind(0b100); pub const Response: Kind = Kind(0b1000);
/// Returns `true` if `self` is a superset of `other`. In other words, /// Returns `true` if `self` is a superset of `other`. In other words,
/// returns `true` if all of the kinds in `other` are also in `self`. /// returns `true` if all of the kinds in `other` are also in `self`.
@ -77,6 +80,26 @@ impl Kind {
pub fn is(self, other: Kind) -> bool { pub fn is(self, other: Kind) -> bool {
(other.0 & self.0) == other.0 (other.0 & self.0) == other.0
} }
/// Returns `true` if `self` is exactly `other`.
///
/// # Example
///
/// ```rust
/// use rocket::fairing::Kind;
///
/// let launch_and_req = Kind::Launch | Kind::Request;
/// assert!(launch_and_req.is_exactly(Kind::Launch | Kind::Request));
///
/// assert!(!launch_and_req.is_exactly(Kind::Launch));
/// assert!(!launch_and_req.is_exactly(Kind::Request));
/// assert!(!launch_and_req.is_exactly(Kind::Response));
/// assert!(!launch_and_req.is_exactly(Kind::Launch | Kind::Response));
/// ```
#[inline]
pub fn is_exactly(self, other: Kind) -> bool {
self.0 == other.0
}
} }
impl BitOr for Kind { impl BitOr for Kind {

View File

@ -1,4 +1,4 @@
//! Fairings: structured interposition at launch, request, and response time. //! Fairings: callbacks at attach, launch, request, and response time.
//! //!
//! Fairings allow for structured interposition at various points in the //! Fairings allow for structured interposition at various points in the
//! application lifetime. Fairings can be seen as a restricted form of //! application lifetime. Fairings can be seen as a restricted form of
@ -26,7 +26,7 @@
//! ``` //! ```
//! //!
//! Once a fairing is attached, Rocket will execute it at the appropiate time, //! Once a fairing is attached, Rocket will execute it at the appropiate time,
//! which varies depending on the fairing type. See the //! which varies depending on the fairing implementation. See the
//! [`Fairing`](/rocket/fairing/trait.Fairing.html) trait documentation for more //! [`Fairing`](/rocket/fairing/trait.Fairing.html) trait documentation for more
//! information on the dispatching of fairing methods. //! information on the dispatching of fairing methods.
//! //!
@ -37,7 +37,8 @@
//! fairing callbacks may not be commutative, it is important to communicate to //! fairing callbacks may not be commutative, it is important to communicate to
//! the user every consequence of a fairing. Furthermore, a `Fairing` should //! the user every consequence of a fairing. Furthermore, a `Fairing` should
//! take care to act locally so that the actions of other `Fairings` are not //! take care to act locally so that the actions of other `Fairings` are not
//! jeopardized. //! jeopardized. For instance, unless it is made abundantly clear, a fairing
//! should not rewrite every request.
use {Rocket, Request, Response, Data}; use {Rocket, Request, Response, Data};
mod fairings; mod fairings;
@ -90,12 +91,29 @@ pub use self::info_kind::{Info, Kind};
/// ///
/// ## Fairing Callbacks /// ## Fairing Callbacks
/// ///
/// There are three kinds of fairing callbacks: launch, request, and response. /// There are four kinds of fairing callbacks: attach, launch, request, and
/// As mentioned above, a fairing can request any combination of these callbacks /// response. As mentioned above, a fairing can request any combination of these
/// through the `kind` field of the `Info` structure returned from the `info` /// callbacks through the `kind` field of the `Info` structure returned from the
/// method. Rocket will only invoke the callbacks set in the `kind` field. /// `info` method. Rocket will only invoke the callbacks set in the `kind`
/// field.
/// ///
/// The three callback kinds are as follows: /// The four callback kinds are as follows:
///
/// * **Attach (`on_attach`)**
///
/// An attach callback, represented by the
/// [`on_attach`](/rocket/fairing/trait.Fairing.html#method.on_attach)
/// method, is called when a fairing is first attached via the
/// [`attach`](/rocket/struct.Rocket.html#method.attach) method. The state
/// of the `Rocket` instance is, at this point, not finalized, as the user
/// may still add additional information to the `Rocket` instance. As a
/// result, it is unwise to depend on the state of the `Rocket` instance.
///
/// An attach callback can arbitrarily modify the `Rocket` instance being
/// constructed. It returns `Ok` if it would like launching to proceed
/// nominally and `Err` otherwise. If a launch callback returns `Err`,
/// launch will be aborted. All attach callbacks are executed on `launch`,
/// even if one or more signal a failure.
/// ///
/// * **Launch (`on_launch`)** /// * **Launch (`on_launch`)**
/// ///
@ -103,10 +121,8 @@ pub use self::info_kind::{Info, Kind};
/// [`on_launch`](/rocket/fairing/trait.Fairing.html#method.on_launch) /// [`on_launch`](/rocket/fairing/trait.Fairing.html#method.on_launch)
/// method, is called immediately before the Rocket application has /// method, is called immediately before the Rocket application has
/// launched. At this point, Rocket has opened a socket for listening but /// launched. At this point, Rocket has opened a socket for listening but
/// has not yet begun accepting connections. A launch callback can /// has not yet begun accepting connections. A launch callback can inspect
/// arbitrarily modify the `Rocket` instance being launched. It returns `Ok` /// the `Rocket` instance being launched.
/// if it would like launching to proceed nominally and `Err` otherwise. If
/// a launch callback returns `Err`, launch is aborted.
/// ///
/// * **Request (`on_request`)** /// * **Request (`on_request`)**
/// ///
@ -136,15 +152,15 @@ pub use self::info_kind::{Info, Kind};
/// # Implementing /// # Implementing
/// ///
/// A `Fairing` implementation has one required method: `info`. A `Fairing` can /// A `Fairing` implementation has one required method: `info`. A `Fairing` can
/// also implement any of the available callbacks: `on_launch`, `on_request`, /// also implement any of the available callbacks: `on_attach`, `on_launch`,
/// and `on_response`. A `Fairing` _must_ set the appropriate callback kind in /// `on_request`, and `on_response`. A `Fairing` _must_ set the appropriate
/// the `kind` field of the returned `Info` structure from `info` for a callback /// callback kind in the `kind` field of the returned `Info` structure from
/// to actually be issued by Rocket. /// `info` for a callback to actually be issued by Rocket.
/// ///
/// A `Fairing` must be `Send + Sync + 'static`. This means that the fairing /// A `Fairing` must be `Send + Sync + 'static`. This means that the fairing
/// must be sendable across thread boundaries (`Send`), thread-safe (`Sync`), /// must be sendable across thread boundaries (`Send`), thread-safe (`Sync`),
/// and have no non-`'static` reference (`'static`). Note that these bounds _do /// and have no non-`'static` reference (`'static`). Note that these bounds _do
/// not_ prohibit a `Fairing` from having state: the state need simply be /// not_ prohibit a `Fairing` from holding state: the state need simply be
/// thread-safe and statically available or heap allocated. /// thread-safe and statically available or heap allocated.
/// ///
/// # Example /// # Example
@ -154,7 +170,7 @@ pub use self::info_kind::{Info, Kind};
/// guards](/rocket/request/trait.FromRequest.html) and [managed /// guards](/rocket/request/trait.FromRequest.html) and [managed
/// state](/rocket/request/struct.State.html), it would require us to annotate /// state](/rocket/request/struct.State.html), it would require us to annotate
/// every `GET` and `POST` request with custom types, polluting handler /// every `GET` and `POST` request with custom types, polluting handler
/// signatures. Instead, we can create a simple fairing that does this globally. /// signatures. Instead, we can create a simple fairing that acts globally.
/// ///
/// The `Counter` fairing below records the number of all `GET` and `POST` /// The `Counter` fairing below records the number of all `GET` and `POST`
/// requests received. It makes these counts available at a special `'/counts'` /// requests received. It makes these counts available at a special `'/counts'`
@ -238,23 +254,38 @@ pub trait Fairing: Send + Sync + 'static {
/// fn info(&self) -> Info { /// fn info(&self) -> Info {
/// Info { /// Info {
/// name: "My Custom Fairing", /// name: "My Custom Fairing",
/// kind: Kind::Launch | Kind::Response /// kind: Kind::Attach | Kind::Launch | Kind::Response
/// } /// }
/// } /// }
/// } /// }
/// ``` /// ```
fn info(&self) -> Info; fn info(&self) -> Info;
/// The launch callback. Returns `Ok` if launch should proceed and `Err` if /// The attach callback. Returns `Ok` if launch should proceed and `Err` if
/// launch should be aborted. /// launch should be aborted.
/// ///
/// This method is called just prior to launching an application if /// This method is called when a fairing is attached if `Kind::Attach` is in
/// `Kind::Launch` is in the `kind` field of the `Info` structure for this /// the `kind` field of the `Info` structure for this fairing. The `rocket`
/// fairing. The `rocket` parameter is the `Rocket` instance that was built /// parameter is the `Rocket` instance that is currently being built for
/// for this application. /// this application.
///
/// ## Default Implementation
/// ///
/// The default implementation of this method simply returns `Ok(rocket)`. /// The default implementation of this method simply returns `Ok(rocket)`.
fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> { Ok(rocket) } fn on_attach(&self, rocket: Rocket) -> Result<Rocket, Rocket> { Ok(rocket) }
/// The launch callback.
///
/// 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 curresponds 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) {}
/// The request callback. /// The request callback.
/// ///
@ -263,9 +294,11 @@ pub trait Fairing: Send + Sync + 'static {
/// `&mut Request` parameter is the incoming request, and the `&Data` /// `&mut Request` parameter is the incoming request, and the `&Data`
/// parameter is the incoming data in the request. /// parameter is the incoming data in the request.
/// ///
/// ## 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_request(&self, request: &mut Request, data: &Data) { } fn on_request(&self, request: &mut Request, data: &Data) {}
/// The response callback. /// The response callback.
/// ///
@ -274,7 +307,9 @@ pub trait Fairing: Send + Sync + 'static {
/// this fairing. The `&Request` parameter is the request that was routed, /// this fairing. The `&Request` parameter is the request that was routed,
/// and the `&mut Response` parameter is the resulting response. /// and the `&mut Response` parameter is the resulting response.
/// ///
/// ## 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_response(&self, request: &Request, response: &mut Response) { } fn on_response(&self, request: &Request, response: &mut Response) {}
} }

View File

@ -284,7 +284,6 @@ impl Rocket {
for route in matches { for route in matches {
// Retrieve and set the requests parameters. // Retrieve and set the requests parameters.
info_!("Matched: {}", route); info_!("Matched: {}", route);
// FIXME: Users should not be able to use this.
request.set_params(route); request.set_params(route);
// Dispatch the request to the handler. // Dispatch the request to the handler.
@ -598,25 +597,37 @@ impl Rocket {
/// use rocket::Rocket; /// use rocket::Rocket;
/// use rocket::fairing::AdHoc; /// use rocket::fairing::AdHoc;
/// ///
/// fn youll_see(rocket: Rocket) -> Result<Rocket, Rocket> {
/// println!("Rocket is about to launch! You just see...");
/// Ok(rocket)
/// }
///
/// fn main() { /// fn main() {
/// # if false { // We don't actually want to launch the server in an example. /// # if false { // We don't actually want to launch the server in an example.
/// rocket::ignite() /// rocket::ignite()
/// .attach(AdHoc::on_launch(youll_see)) /// .attach(AdHoc::on_launch(|_| {
/// println!("Rocket is about to launch! You just see...");
/// }))
/// .launch(); /// .launch();
/// # } /// # }
/// } /// }
/// ``` /// ```
#[inline] #[inline]
pub fn attach<F: Fairing>(mut self, fairing: F) -> Self { pub fn attach<F: Fairing>(mut self, fairing: F) -> Self {
self.fairings.attach(Box::new(fairing)); // Attach the 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 the fairings around!
self.fairings = fairings;
self self
} }
pub(crate) fn prelaunch_check(&self) -> Option<LaunchError> {
if self.router.has_collisions() {
Some(LaunchError::from(LaunchErrorKind::Collision))
} else if self.fairings.had_failure() {
Some(LaunchError::from(LaunchErrorKind::FailedFairing))
} else {
None
}
}
/// Starts the application server and begins listening for and dispatching /// Starts the application server and begins listening for and dispatching
/// requests to mounted routes and catchers. Unless there is an error, this /// requests to mounted routes and catchers. Unless there is an error, this
/// function does not return and blocks until program termination. /// function does not return and blocks until program termination.
@ -637,9 +648,9 @@ impl Rocket {
/// rocket::ignite().launch(); /// rocket::ignite().launch();
/// # } /// # }
/// ``` /// ```
pub fn launch(mut self) -> LaunchError { pub fn launch(self) -> LaunchError {
if self.router.has_collisions() { if let Some(error) = self.prelaunch_check() {
return LaunchError::from(LaunchErrorKind::Collision); return error;
} }
self.fairings.pretty_print_counts(); self.fairings.pretty_print_counts();
@ -657,15 +668,8 @@ impl Rocket {
Err(e) => return LaunchError::from(e) Err(e) => return LaunchError::from(e)
}; };
// Run all of the launch fairings. // Run the launch fairings.
let mut fairings = mem::replace(&mut self.fairings, Fairings::new()); self.fairings.handle_launch(&self);
self = match fairings.handle_launch(self) {
Some(rocket) => rocket,
None => return LaunchError::from(LaunchErrorKind::FailedFairing)
};
// Make sure we keep the request/response fairings!
self.fairings = fairings;
launch_info!("🚀 {} {}{}", launch_info!("🚀 {} {}{}",
White.paint("Rocket has launched from"), White.paint("Rocket has launched from"),

View File

@ -76,7 +76,7 @@
//! ``` //! ```
use ::{Rocket, Request, Response, Data}; use ::{Rocket, Request, Response, Data};
use http::{Method, Header, Cookie}; use http::{Method, Status, Header, Cookie};
use std::net::SocketAddr; use std::net::SocketAddr;
@ -201,11 +201,12 @@ impl<'r> MockRequest<'r> {
/// Dispatch this request using a given instance of Rocket. /// Dispatch this request using a given instance of Rocket.
/// ///
/// Returns the body of the response if there was a response. The return /// It is possible that the supplied `rocket` instance contains malformed
/// value is `None` if any of the following occurs: /// input such as colliding or invalid routes or failed fairings. When this
/// /// is the case, the returned `Response` will contain a status of
/// 1. The returned body was not valid UTF8. /// `InternalServerError`, and the body will contain the error that
/// 2. The application failed to respond. /// occurred. In all other cases, the returned `Response` will be that of
/// the application.
/// ///
/// # Examples /// # Examples
/// ///
@ -234,6 +235,13 @@ impl<'r> MockRequest<'r> {
/// # } /// # }
/// ``` /// ```
pub fn dispatch_with<'s>(&'s mut self, rocket: &'r Rocket) -> Response<'s> { pub fn dispatch_with<'s>(&'s mut self, rocket: &'r Rocket) -> Response<'s> {
if let Some(error) = rocket.prelaunch_check() {
return Response::build()
.status(Status::InternalServerError)
.sized_body(::std::io::Cursor::new(error.to_string()))
.finalize()
}
let data = ::std::mem::replace(&mut self.data, Data::local(vec![])); let data = ::std::mem::replace(&mut self.data, Data::local(vec![]));
rocket.dispatch(&mut self.request, data) rocket.dispatch(&mut self.request, data)
} }