mirror of https://github.com/rwf2/Rocket.git
Fairings v2.
This commit is contained in:
parent
9a7484f7a8
commit
9c9740f966
|
@ -4,12 +4,53 @@
|
|||
extern crate rocket;
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use rocket::Fairing;
|
||||
use rocket::http::Method;
|
||||
use rocket::{Request, Data, Response};
|
||||
use rocket::fairing::{AdHoc, Fairing, Info, Kind};
|
||||
use rocket::http::{Method, ContentType, Status};
|
||||
|
||||
#[cfg(test)] mod tests;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Counter {
|
||||
get: AtomicUsize,
|
||||
post: AtomicUsize,
|
||||
}
|
||||
|
||||
impl Fairing for Counter {
|
||||
fn info(&self) -> Info {
|
||||
Info {
|
||||
name: "GET/POST Counter",
|
||||
kind: Kind::Request | Kind::Response
|
||||
}
|
||||
}
|
||||
|
||||
fn on_request(&self, request: &mut Request, _: &Data) {
|
||||
if request.method() == Method::Get {
|
||||
self.get.fetch_add(1, Ordering::Relaxed);
|
||||
} else if request.method() == Method::Post {
|
||||
self.post.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response(&self, request: &Request, response: &mut Response) {
|
||||
if response.status() != Status::NotFound {
|
||||
return
|
||||
}
|
||||
|
||||
if request.method() == Method::Get && request.uri().path() == "/counts" {
|
||||
let get_count = self.get.load(Ordering::Relaxed);
|
||||
let post_count = self.post.load(Ordering::Relaxed);
|
||||
|
||||
let body = format!("Get: {}\nPost: {}", get_count, post_count);
|
||||
response.set_status(Status::Ok);
|
||||
response.set_header(ContentType::Plain);
|
||||
response.set_sized_body(Cursor::new(body));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[put("/")]
|
||||
fn hello() -> &'static str {
|
||||
"Hello, world!"
|
||||
|
@ -18,19 +59,24 @@ fn hello() -> &'static str {
|
|||
fn rocket() -> rocket::Rocket {
|
||||
rocket::ignite()
|
||||
.mount("/", routes![hello])
|
||||
.attach(Fairing::Launch(Box::new(|rocket| {
|
||||
.attach(Counter::default())
|
||||
.attach(AdHoc::on_launch(|rocket| {
|
||||
println!("Rocket is about to launch! Exciting! Here we go...");
|
||||
Ok(rocket)
|
||||
})))
|
||||
.attach(Fairing::Request(Box::new(|req, _| {
|
||||
}))
|
||||
.attach(AdHoc::on_request(|req, _| {
|
||||
println!(" => Incoming request: {}", req);
|
||||
if req.uri().path() == "/" {
|
||||
println!(" => Changing method to `PUT`.");
|
||||
req.set_method(Method::Put);
|
||||
})))
|
||||
.attach(Fairing::Response(Box::new(|_, res| {
|
||||
}
|
||||
}))
|
||||
.attach(AdHoc::on_response(|req, res| {
|
||||
if req.uri().path() == "/" {
|
||||
println!(" => Rewriting response body.");
|
||||
res.set_sized_body(Cursor::new("Hello, fairings!"));
|
||||
})))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -3,9 +3,33 @@ use rocket::testing::MockRequest;
|
|||
use rocket::http::Method::*;
|
||||
|
||||
#[test]
|
||||
fn fairings() {
|
||||
fn rewrite_get_put() {
|
||||
let rocket = rocket();
|
||||
let mut req = MockRequest::new(Get, "/");
|
||||
let mut response = req.dispatch_with(&rocket);
|
||||
assert_eq!(response.body_string(), Some("Hello, fairings!".into()));
|
||||
}
|
||||
#[test]
|
||||
fn counts() {
|
||||
let rocket = rocket();
|
||||
|
||||
// Issue 1 GET request.
|
||||
let mut req = MockRequest::new(Get, "/");
|
||||
req.dispatch_with(&rocket);
|
||||
|
||||
// Check the GET count, taking into account _this_ GET request.
|
||||
let mut req = MockRequest::new(Get, "/counts");
|
||||
let mut response = req.dispatch_with(&rocket);
|
||||
assert_eq!(response.body_string(), Some("Get: 2\nPost: 0".into()));
|
||||
|
||||
// Issue 1 more GET request and a POST.
|
||||
let mut req = MockRequest::new(Get, "/");
|
||||
req.dispatch_with(&rocket);
|
||||
let mut req = MockRequest::new(Post, "/");
|
||||
req.dispatch_with(&rocket);
|
||||
|
||||
// Check the counts.
|
||||
let mut req = MockRequest::new(Get, "/counts");
|
||||
let mut response = req.dispatch_with(&rocket);
|
||||
assert_eq!(response.body_string(), Some("Get: 4\nPost: 1".into()));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
use {Rocket, Request, Response, Data};
|
||||
use fairing::{Fairing, Kind, Info};
|
||||
|
||||
/// A ad-hoc fairing that can be created from a function or closure.
|
||||
///
|
||||
/// This enum can be used to create a fairing from a simple function or clusure
|
||||
/// without creating a new structure or implementing `Fairing` directly.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// Use the [`on_launch`](#method.on_launch),
|
||||
/// [`on_request`](#method.on_request), or [`on_response`](#method.on_response)
|
||||
/// constructors to create an `AdHoc` structure from a function or closure.
|
||||
/// Then, simply attach the structure to the `Rocket` instance.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// The following snippet creates a `Rocket` instance with two ad-hoc fairings.
|
||||
/// The first, a launch fairing, simply prints a message indicating that the
|
||||
/// application is about to the launch. The second, a request fairing, rewrites
|
||||
/// the method of all requests to be `PUT`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::fairing::AdHoc;
|
||||
/// use rocket::http::Method;
|
||||
///
|
||||
/// rocket::ignite()
|
||||
/// .attach(AdHoc::on_launch(|rocket| {
|
||||
/// println!("Rocket is about to launch! Exciting! Here we go...");
|
||||
/// Ok(rocket)
|
||||
/// }))
|
||||
/// .attach(AdHoc::on_request(|req, _| {
|
||||
/// req.set_method(Method::Put);
|
||||
/// }));
|
||||
/// ```
|
||||
pub enum AdHoc {
|
||||
/// An ad-hoc **launch** fairing. Called just before Rocket launches.
|
||||
#[doc(hidden)]
|
||||
Launch(Box<Fn(Rocket) -> Result<Rocket, Rocket> + Send + Sync + 'static>),
|
||||
/// An ad-hoc **request** fairing. Called when a request is received.
|
||||
#[doc(hidden)]
|
||||
Request(Box<Fn(&mut Request, &Data) + Send + Sync + 'static>),
|
||||
/// An ad-hoc **response** fairing. Called when a response is ready to be
|
||||
/// sent to a client.
|
||||
#[doc(hidden)]
|
||||
Response(Box<Fn(&Request, &mut Response) + Send + Sync + 'static>)
|
||||
}
|
||||
|
||||
impl AdHoc {
|
||||
/// Constructs an `AdHoc` launch fairing. The function `f` will be called by
|
||||
/// Rocket just prior to launching.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::fairing::AdHoc;
|
||||
///
|
||||
/// // The no-op launch fairing.
|
||||
/// let fairing = AdHoc::on_launch(|rocket| Ok(rocket));
|
||||
/// ```
|
||||
pub fn on_launch<F>(f: F) -> AdHoc
|
||||
where F: Fn(Rocket) -> Result<Rocket, Rocket> + Send + Sync + 'static
|
||||
{
|
||||
AdHoc::Launch(Box::new(f))
|
||||
}
|
||||
|
||||
/// Constructs an `AdHoc` request fairing. The function `f` will be called
|
||||
/// by Rocket when a new request is received.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::fairing::AdHoc;
|
||||
///
|
||||
/// // The no-op request fairing.
|
||||
/// let fairing = AdHoc::on_request(|req, data| {
|
||||
/// // do something with the request and data...
|
||||
/// # let (_, _) = (req, data);
|
||||
/// });
|
||||
/// ```
|
||||
pub fn on_request<F>(f: F) -> AdHoc
|
||||
where F: Fn(&mut Request, &Data) + Send + Sync + 'static
|
||||
{
|
||||
AdHoc::Request(Box::new(f))
|
||||
}
|
||||
|
||||
/// Constructs an `AdHoc` response fairing. The function `f` will be called
|
||||
/// by Rocket when a response is ready to be sent.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::fairing::AdHoc;
|
||||
///
|
||||
/// // The no-op response fairing.
|
||||
/// let fairing = AdHoc::on_response(|req, resp| {
|
||||
/// // do something with the request and pending response...
|
||||
/// # let (_, _) = (req, resp);
|
||||
/// });
|
||||
/// ```
|
||||
pub fn on_response<F>(f: F) -> AdHoc
|
||||
where F: Fn(&Request, &mut Response) + Send + Sync + 'static
|
||||
{
|
||||
AdHoc::Response(Box::new(f))
|
||||
}
|
||||
}
|
||||
|
||||
impl Fairing for AdHoc {
|
||||
fn info(&self) -> Info {
|
||||
use self::AdHoc::*;
|
||||
match *self {
|
||||
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> {
|
||||
match *self {
|
||||
AdHoc::Launch(ref launch_fn) => launch_fn(rocket),
|
||||
_ => Ok(rocket)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_request(&self, request: &mut Request, data: &Data) {
|
||||
if let AdHoc::Request(ref callback) = *self {
|
||||
callback(request, data)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response(&self, request: &Request, response: &mut Response) {
|
||||
if let AdHoc::Response(ref callback) = *self {
|
||||
callback(request, response)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
use {Rocket, Request, Response, Data};
|
||||
use fairing::{Fairing, Kind};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Fairings {
|
||||
all_fairings: Vec<Box<Fairing>>,
|
||||
launch: Vec<&'static Fairing>,
|
||||
request: Vec<&'static Fairing>,
|
||||
response: Vec<&'static Fairing>,
|
||||
}
|
||||
|
||||
impl Fairings {
|
||||
#[inline]
|
||||
pub fn new() -> Fairings {
|
||||
Fairings::default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn attach(&mut self, fairing: Box<Fairing>) {
|
||||
// Get the kind information.
|
||||
let kind = fairing.info().kind;
|
||||
|
||||
// The `Fairings` structure separates `all_fairings` into kind groups so
|
||||
// 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
|
||||
// kinds. The lifetime of that reference is really the lifetime of the
|
||||
// `Box` for referred fairing, but that lifetime is dynamic; there's no
|
||||
// way to express it. So we cheat and say that the lifetime is
|
||||
// `'static` and cast it here. For this to be safe, the following must
|
||||
// be preserved:
|
||||
//
|
||||
// 1) The references can never be exposed with a `'static` lifetime.
|
||||
// 2) The `Box<Fairing>` must live for the lifetime of the reference.
|
||||
//
|
||||
// We maintain these invariants by not exposing the references and never
|
||||
// deallocating `Box<Fairing>` structures. As such, the references will
|
||||
// always be valid. Note: `ptr` doesn't point into the `Vec`, so
|
||||
// reallocations there are irrelvant. Instead, it points into the heap.
|
||||
let ptr: &'static Fairing = unsafe { ::std::mem::transmute(&*fairing) };
|
||||
|
||||
self.all_fairings.push(fairing);
|
||||
if kind.is(Kind::Launch) { self.launch.push(ptr); }
|
||||
if kind.is(Kind::Request) { self.request.push(ptr); }
|
||||
if kind.is(Kind::Response) { self.response.push(ptr); }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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_request(&self, req: &mut Request, data: &Data) {
|
||||
for fairing in &self.request {
|
||||
fairing.on_request(req, data);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn handle_response(&self, request: &Request, response: &mut Response) {
|
||||
for fairing in &self.response {
|
||||
fairing.on_response(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pretty_print_counts(&self) {
|
||||
use term_painter::ToStyle;
|
||||
use term_painter::Color::{White, Magenta};
|
||||
|
||||
if self.all_fairings.len() > 0 {
|
||||
info!("📡 {}:", Magenta.paint("Fairings"));
|
||||
}
|
||||
|
||||
fn info_if_nonempty(kind: &str, fairings: &[&Fairing]) {
|
||||
let names: Vec<&str> = fairings.iter().map(|f| f.info().name).collect();
|
||||
info_!("{} {}: {}", White.paint(fairings.len()), kind,
|
||||
White.paint(names.join(", ")));
|
||||
}
|
||||
|
||||
info_if_nonempty("launch", &self.launch);
|
||||
info_if_nonempty("request", &self.request);
|
||||
info_if_nonempty("response", &self.response);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
use std::ops::BitOr;
|
||||
|
||||
/// Information about a [`Fairing`](/rocket/fairing/trait.Fairing.html).
|
||||
///
|
||||
/// The `name` field is an arbitrary name for a fairing. The `kind` field is a
|
||||
/// is an `or`d set of [`Kind`](/rocket/fairing/struct.Kind.html) structures.
|
||||
/// Rocket uses the values set in `Kind` to determine which callbacks from a
|
||||
/// given `Fairing` implementation to actually call.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// A simple `Info` structure that can be used for a `Fairing` that implements
|
||||
/// all three callbacks:
|
||||
///
|
||||
/// ```
|
||||
/// use rocket::fairing::{Info, Kind};
|
||||
///
|
||||
/// # let _unused_info =
|
||||
/// Info {
|
||||
/// name: "Example Fairing",
|
||||
/// kind: Kind::Launch | Kind::Request | Kind::Response
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
pub struct Info {
|
||||
/// The name of the fairing.
|
||||
pub name: &'static str,
|
||||
/// A set representing the callbacks the fairing wishes to receive.
|
||||
pub kind: Kind
|
||||
}
|
||||
|
||||
/// A bitset representing the kinds of callbacks a
|
||||
/// [`Fairing`](/rocket/fairing/trait.Fairing.html) wishes to receive.
|
||||
///
|
||||
/// A fairing can request any combination of any of the following kinds of
|
||||
/// callbacks:
|
||||
///
|
||||
/// * Launch
|
||||
/// * Request
|
||||
/// * Response
|
||||
///
|
||||
/// 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,
|
||||
/// use `Kind::Launch | Kind::Request`. Similarly, to represent a fairing that
|
||||
/// is all three kinds, use `Kind::Launch | Kind::Request | Kind::Response`.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Kind(usize);
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
impl Kind {
|
||||
/// `Kind` flag representing a request for a 'launch' callback.
|
||||
pub const Launch: Kind = Kind(0b001);
|
||||
/// `Kind` flag representing a request for a 'request' callback.
|
||||
pub const Request: Kind = Kind(0b010);
|
||||
/// `Kind` flag representing a request for a 'response' callback.
|
||||
pub const Response: Kind = Kind(0b100);
|
||||
|
||||
/// 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`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::fairing::Kind;
|
||||
///
|
||||
/// let launch_and_req = Kind::Launch | Kind::Request;
|
||||
/// assert!(launch_and_req.is(Kind::Launch | Kind::Request));
|
||||
///
|
||||
/// assert!(launch_and_req.is(Kind::Launch));
|
||||
/// assert!(launch_and_req.is(Kind::Request));
|
||||
///
|
||||
/// assert!(!launch_and_req.is(Kind::Response));
|
||||
/// assert!(!launch_and_req.is(Kind::Launch | Kind::Response));
|
||||
/// assert!(!launch_and_req.is(Kind::Launch | Kind::Request | Kind::Response));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is(self, other: Kind) -> bool {
|
||||
(other.0 & self.0) == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for Kind {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn bitor(self, rhs: Self) -> Self {
|
||||
Kind(self.0 | rhs.0)
|
||||
}
|
||||
}
|
|
@ -2,34 +2,52 @@
|
|||
//!
|
||||
//! Fairings allow for structured interposition at various points in the
|
||||
//! application lifetime. Fairings can be seen as a restricted form of
|
||||
//! "middleware". A fairing is simply a function with a particular signature
|
||||
//! that Rocket will run at a requested point in a program. You can use fairings
|
||||
//! to rewrite or record information about requests and responses, or to perform
|
||||
//! an action once a Rocket application has launched.
|
||||
//! "middleware". A fairing is an arbitrary structure with methods representing
|
||||
//! callbacks that Rocket will run at requested points in a program. You can use
|
||||
//! fairings to rewrite or record information about requests and responses, or
|
||||
//! to perform an action once a Rocket application has launched.
|
||||
//!
|
||||
//! ## Attaching
|
||||
//!
|
||||
//! You must inform Rocket about fairings that you wish to be active by calling
|
||||
//! the [`attach`](/rocket/struct.Rocket.html#method.attach) method on the
|
||||
//! [`Rocket`](/rocket/struct.Rocket.html) instance and passing in the
|
||||
//! appropriate [`Fairing`](/rocket/fairing/enum.Fairing.html). For instance, to
|
||||
//! attach `Request` and `Response` fairings named `req_fairing` and
|
||||
//! `res_fairing` to a new Rocket instance, you might write:
|
||||
//! appropriate [`Fairing`](/rocket/fairing/trait.Fairing.html). For instance,
|
||||
//! to attach fairings named `req_fairing` and `res_fairing` to a new Rocket
|
||||
//! instance, you might write:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use rocket::Fairing;
|
||||
//! # let req_fairing = Fairing::Request(Box::new(|_, _| ()));
|
||||
//! # let res_fairing = Fairing::Response(Box::new(|_, _| ()));
|
||||
//! # #[allow(unused_variables)]
|
||||
//! # use rocket::fairing::AdHoc;
|
||||
//! # let req_fairing = AdHoc::on_request(|_, _| ());
|
||||
//! # let res_fairing = AdHoc::on_response(|_, _| ());
|
||||
//! let rocket = rocket::ignite()
|
||||
//! .attach(vec![req_fairing, res_fairing]);
|
||||
//! .attach(req_fairing)
|
||||
//! .attach(res_fairing);
|
||||
//! ```
|
||||
//!
|
||||
//! Once a fairing is attached, Rocket will execute it at the appropiate time,
|
||||
//! which varies depending on the fairing type.
|
||||
|
||||
//! which varies depending on the fairing type. See the
|
||||
//! [`Fairing`](/rocket/fairing/trait.Fairing.html) trait documentation for more
|
||||
//! information on the dispatching of fairing methods.
|
||||
//!
|
||||
//! ## Ordering
|
||||
//!
|
||||
//! `Fairing`s are executed in the same order in which they are attached: the
|
||||
//! first attached fairing has its callbacks executed before all others. Because
|
||||
//! fairing callbacks may not be commutative, it is important to communicate to
|
||||
//! 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
|
||||
//! jeopardized.
|
||||
use {Rocket, Request, Response, Data};
|
||||
|
||||
mod fairings;
|
||||
mod ad_hoc;
|
||||
mod info_kind;
|
||||
|
||||
pub(crate) use self::fairings::Fairings;
|
||||
pub use self::ad_hoc::AdHoc;
|
||||
pub use self::info_kind::{Info, Kind};
|
||||
|
||||
// We might imagine that a request fairing returns an `Outcome`. If it returns
|
||||
// `Success`, we don't do any routing and use that response directly. Same if it
|
||||
// returns `Failure`. We only route if it returns `Forward`. I've chosen not to
|
||||
|
@ -42,159 +60,221 @@ use {Rocket, Request, Response, Data};
|
|||
// appropriate response. This allows the users to handle `OPTIONS` requests
|
||||
// when they'd like but default to the fairing when they don't want to.
|
||||
|
||||
/// The type of a **launch** fairing callback.
|
||||
/// Trait implemented by fairings: Rocket's structured middleware.
|
||||
///
|
||||
/// The `Rocket` parameter is the `Rocket` instance being built. The launch
|
||||
/// fairing can modify the `Rocket` instance arbitrarily.
|
||||
/// ## Fairing Information
|
||||
///
|
||||
/// TODO: Document fully with examples before 0.3.
|
||||
pub type LaunchFn = Box<Fn(Rocket) -> Result<Rocket, Rocket> + Send + Sync + 'static>;
|
||||
/// The type of a **request** fairing callback.
|
||||
/// Every `Fairing` must implement the
|
||||
/// [`info`](/rocket/fairing/trait.Fairing.html#tymethod.info) method, which
|
||||
/// returns an [`Info`](http://localhost:8000/rocket/fairing/struct.Info.html)
|
||||
/// structure. This structure is used by Rocket to:
|
||||
///
|
||||
/// The `&mut Request` parameter is the incoming request, and the `&Data`
|
||||
/// 1. Assign a name to the `Fairing`.
|
||||
///
|
||||
/// This is the `name` field, which can be any arbitrary string. Name your
|
||||
/// fairing something illustrative. The name will be logged during the
|
||||
/// application's launch procedures.
|
||||
///
|
||||
/// 2. Determine which callbacks to actually issue on the `Fairing`.
|
||||
///
|
||||
/// This is the `kind` field of type
|
||||
/// [`Kind`](/rocket/fairing/struct.Kind.html). This field is a bitset that
|
||||
/// represents the kinds of callbacks the fairing wishes to receive. Rocket
|
||||
/// will only invoke the callbacks that are flagged in this set. `Kind`
|
||||
/// structures can be `or`d together to represent any combination of kinds
|
||||
/// of callbacks. For instance, to request launch and response callbacks,
|
||||
/// return a `kind` field with the value `Kind::Launch | Kind::Response`.
|
||||
///
|
||||
/// See the [top-level documentation](/rocket/fairing/) for more general
|
||||
/// information.
|
||||
///
|
||||
/// ## Fairing Callbacks
|
||||
///
|
||||
/// There are three kinds of fairing callbacks: launch, request, and response.
|
||||
/// As mentioned above, a fairing can request any combination of these callbacks
|
||||
/// through the `kind` field of the `Info` structure returned from the `info`
|
||||
/// method. Rocket will only invoke the callbacks set in the `kind` field.
|
||||
///
|
||||
/// The three callback kinds are as follows:
|
||||
///
|
||||
/// * **Launch (`on_launch`)**
|
||||
///
|
||||
/// A launch callback, represented by the
|
||||
/// [`on_launch`](/rocket/fairing/trait.Fairing.html#method.on_launch)
|
||||
/// method, is called immediately before the Rocket application has
|
||||
/// launched. At this point, Rocket has opened a socket for listening but
|
||||
/// has not yet begun accepting connections. A launch callback can
|
||||
/// arbitrarily modify the `Rocket` instance being launched. It returns `Ok`
|
||||
/// if it would like launching to proceed nominally and `Err` otherwise. If
|
||||
/// a launch callback returns `Err`, launch is aborted.
|
||||
///
|
||||
/// * **Request (`on_request`)**
|
||||
///
|
||||
/// A request callback, represented by the
|
||||
/// [`on_request`](/rocket/fairing/trait.Fairing.html#method.on_request)
|
||||
/// method, is called just after a request is received. At this point,
|
||||
/// Rocket has parsed the incoming HTTP into
|
||||
/// [`Request`](/rocket/struct.Request.html) and
|
||||
/// [`Data`](/rocket/struct.Data.html) structures but has not routed the
|
||||
/// request. A request callback can modify the request at will and
|
||||
/// [`peek`](/rocket/struct.Data.html#method.peek) into the incoming data.
|
||||
/// It may not, however, abort or respond directly to the request; these
|
||||
/// issues are better handled via [request
|
||||
/// guards](/rocket/request/trait.FromRequest.html) or via response
|
||||
/// callbacks. A modified request is routed as if it was the original
|
||||
/// request.
|
||||
///
|
||||
/// * **Response (`on_response`)**
|
||||
///
|
||||
/// A response callback is called when a response is ready to be sent to the
|
||||
/// client. At this point, Rocket has completed all routing, including to
|
||||
/// error catchers, and has generated the would-be final response. A
|
||||
/// response callback can modify the response at will. For exammple, a
|
||||
/// response callback can provide a default response when the user fails to
|
||||
/// handle the request by checking for 404 responses.
|
||||
///
|
||||
/// # Implementing
|
||||
///
|
||||
/// A `Fairing` implementation has one required method: `info`. A `Fairing` can
|
||||
/// also implement any of the available callbacks: `on_launch`, `on_request`,
|
||||
/// and `on_response`. A `Fairing` _must_ set the appropriate callback kind in
|
||||
/// the `kind` field of the returned `Info` structure from `info` for a callback
|
||||
/// to actually be issued by Rocket.
|
||||
///
|
||||
/// A `Fairing` must be `Send + Sync + 'static`. This means that the fairing
|
||||
/// must be sendable across thread boundaries (`Send`), thread-safe (`Sync`),
|
||||
/// and have no non-`'static` reference (`'static`). Note that these bounds _do
|
||||
/// not_ prohibit a `Fairing` from having state: the state need simply be
|
||||
/// thread-safe and statically available or heap allocated.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Imagine that we want to record the number of `GET` and `POST` requests that
|
||||
/// our application has received. While we could do this with [request
|
||||
/// guards](/rocket/request/trait.FromRequest.html) and [managed
|
||||
/// state](/rocket/request/struct.State.html), it would require us to annotate
|
||||
/// every `GET` and `POST` request with custom types, polluting handler
|
||||
/// signatures. Instead, we can create a simple fairing that does this globally.
|
||||
///
|
||||
/// The `Counter` fairing below records the number of all `GET` and `POST`
|
||||
/// requests received. It makes these counts available at a special `'/counts'`
|
||||
/// path.
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::Cursor;
|
||||
/// use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
///
|
||||
/// use rocket::{Request, Data, Response};
|
||||
/// use rocket::fairing::{Fairing, Info, Kind};
|
||||
/// use rocket::http::{Method, ContentType, Status};
|
||||
///
|
||||
/// #[derive(Default)]
|
||||
/// struct Counter {
|
||||
/// get: AtomicUsize,
|
||||
/// post: AtomicUsize,
|
||||
/// }
|
||||
///
|
||||
/// impl Fairing for Counter {
|
||||
/// fn info(&self) -> Info {
|
||||
/// Info {
|
||||
/// name: "GET/POST Counter",
|
||||
/// kind: Kind::Request | Kind::Response
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn on_request(&self, request: &mut Request, _: &Data) {
|
||||
/// if request.method() == Method::Get {
|
||||
/// self.get.fetch_add(1, Ordering::Relaxed);
|
||||
/// } else if request.method() == Method::Post {
|
||||
/// self.post.fetch_add(1, Ordering::Relaxed);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn on_response(&self, request: &Request, response: &mut Response) {
|
||||
/// // Don't change a successful user's response, ever.
|
||||
/// if response.status() != Status::NotFound {
|
||||
/// return
|
||||
/// }
|
||||
///
|
||||
/// if request.method() == Method::Get && request.uri().path() == "/counts" {
|
||||
/// let get_count = self.get.load(Ordering::Relaxed);
|
||||
/// let post_count = self.post.load(Ordering::Relaxed);
|
||||
///
|
||||
/// let body = format!("Get: {}\nPost: {}", get_count, post_count);
|
||||
/// response.set_status(Status::Ok);
|
||||
/// response.set_header(ContentType::Plain);
|
||||
/// response.set_sized_body(Cursor::new(body));
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Fairing: Send + Sync + 'static {
|
||||
/// Returns an [`Info`](/rocket/fairing/struct.Info.html) structure
|
||||
/// containing the `name` and [`Kind`](/rocket/fairing/struct.Kind.html) of
|
||||
/// this fairing. The `name` can be any arbitrary string. `Kind` must be an
|
||||
/// `or`d set of `Kind` variants.
|
||||
///
|
||||
/// This is the only required method of a `Fairing`. All other methods have
|
||||
/// no-op default implementations.
|
||||
///
|
||||
/// Rocket will only dispatch callbacks to this fairing for the kinds in the
|
||||
/// `kind` field of the returned `Info` structure. For instance, if
|
||||
/// `Kind::Launch | Kind::Request` is used, then Rocket will only call the
|
||||
/// `on_launch` and `on_request` methods of the fairing. Similarly, if
|
||||
/// `Kind::Response` is used, Rocket will only call the `on_response` method
|
||||
/// of this fairing.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// An `info` implementation for `MyFairing`: a fairing named "My Custom
|
||||
/// Fairing" that is both a launch and response fairing.
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::fairing::{Fairing, Info, Kind};
|
||||
///
|
||||
/// struct MyFairing;
|
||||
///
|
||||
/// impl Fairing for MyFairing {
|
||||
/// fn info(&self) -> Info {
|
||||
/// Info {
|
||||
/// name: "My Custom Fairing",
|
||||
/// kind: Kind::Launch | Kind::Response
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
fn info(&self) -> Info;
|
||||
|
||||
/// The launch callback. Returns `Ok` if launch should proceed and `Err` if
|
||||
/// launch should be aborted.
|
||||
///
|
||||
/// This method is called just prior to launching an application if
|
||||
/// `Kind::Launch` is in the `kind` field of the `Info` structure for this
|
||||
/// fairing. The `rocket` parameter is the `Rocket` instance that was built
|
||||
/// for this application.
|
||||
///
|
||||
/// The default implementation of this method simply returns `Ok(rocket)`.
|
||||
fn on_launch(&self, rocket: Rocket) -> Result<Rocket, Rocket> { Ok(rocket) }
|
||||
|
||||
/// The request callback.
|
||||
///
|
||||
/// This method is called when a new request is received if `Kind::Request`
|
||||
/// is in the `kind` field of the `Info` structure for this fairing. The
|
||||
/// `&mut Request` parameter is the incoming request, and the `&Data`
|
||||
/// parameter is the incoming data in the request.
|
||||
///
|
||||
/// TODO: Document fully with examples before 0.3.
|
||||
pub type RequestFn = Box<Fn(&mut Request, &Data) + Send + Sync + 'static>;
|
||||
/// The type of a **response** fairing callback.
|
||||
/// The default implementation of this method does nothing.
|
||||
#[allow(unused_variables)]
|
||||
fn on_request(&self, request: &mut Request, data: &Data) { }
|
||||
|
||||
/// The response callback.
|
||||
///
|
||||
/// The `&Request` parameter is the request that was routed, and the `&mut
|
||||
/// Response` parameter is the result response.
|
||||
/// This method is called when a response is ready to be issued to a client
|
||||
/// if `Kind::Response` is in the `kind` field of the `Info` structure for
|
||||
/// this fairing. The `&Request` parameter is the request that was routed,
|
||||
/// and the `&mut Response` parameter is the resulting response.
|
||||
///
|
||||
/// TODO: Document fully with examples before 0.3.
|
||||
pub type ResponseFn = Box<Fn(&Request, &mut Response) + Send + Sync + 'static>;
|
||||
|
||||
/// An enum representing the three fairing types: launch, request, and response.
|
||||
///
|
||||
/// ## Fairing Types
|
||||
///
|
||||
/// The three types of fairings, launch, request, and response, operate as
|
||||
/// follows:
|
||||
///
|
||||
/// * *Launch Fairings*
|
||||
///
|
||||
/// An attached launch fairing will be called immediately before the Rocket
|
||||
/// application has launched. At this point, Rocket has opened a socket for
|
||||
/// listening but has not yet begun accepting connections. A launch fairing
|
||||
/// can arbitrarily modify the `Rocket` instance being launched. It returns
|
||||
/// `Ok` if it would like launching to proceed nominally and `Err`
|
||||
/// otherwise. If a launch fairing returns `Err`, launch is aborted. The
|
||||
/// [`LaunchFn`](/rocket/fairing/type.LaunchFn.html) documentation contains
|
||||
/// further information and tips on the function signature.
|
||||
///
|
||||
/// * *Request Fairings*
|
||||
///
|
||||
/// An attached request fairing is called when a request is received. At
|
||||
/// this point, Rocket has parsed the incoming HTTP into a
|
||||
/// [Request](/rocket/struct.Request.html) and
|
||||
/// [Data](/rocket/struct.Data.html) object but has not routed the request.
|
||||
/// A request fairing can modify the request at will and
|
||||
/// [peek](/rocket/struct.Data.html#method.peek) into the incoming data. It
|
||||
/// may not, however, abort or respond directly to the request; these issues
|
||||
/// are better handled via [request
|
||||
/// guards](/rocket/request/trait.FromRequest.html) or via response
|
||||
/// fairings. A modified request is routed as if it was the original
|
||||
/// request. The [`RequestFn`](/rocket/fairing/type.RequestFn.html)
|
||||
/// documentation contains further information and tips on the function
|
||||
/// signature.
|
||||
///
|
||||
/// * *Response Fairings*
|
||||
///
|
||||
/// An attached response fairing is called when a response is ready to be
|
||||
/// sent to the client. At this point, Rocket has completed all routing,
|
||||
/// including to error catchers, and has generated the would-be final
|
||||
/// response. A response fairing can modify the response at will. A response
|
||||
/// fairing, can, for example, provide a default response when the user
|
||||
/// fails to handle the request by checking for 404 responses. The
|
||||
/// [`ResponseFn`](/rocket/fairing/type.ResponseFn.html) documentation
|
||||
/// contains further information and tips on the function signature.
|
||||
///
|
||||
/// See the [top-level documentation](/rocket/fairing/) for general information.
|
||||
pub enum Fairing {
|
||||
/// A launch fairing. Called just before Rocket launches.
|
||||
Launch(LaunchFn),
|
||||
/// A request fairing. Called when a request is received.
|
||||
Request(RequestFn),
|
||||
/// A response fairing. Called when a response is ready to be sent.
|
||||
Response(ResponseFn),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Fairings {
|
||||
pub launch: Vec<LaunchFn>,
|
||||
pub request: Vec<RequestFn>,
|
||||
pub response: Vec<ResponseFn>,
|
||||
}
|
||||
|
||||
impl Fairings {
|
||||
#[inline]
|
||||
pub fn new() -> Fairings {
|
||||
Fairings::default()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn attach_all(&mut self, fairings: Vec<Fairing>) {
|
||||
for fairing in fairings {
|
||||
self.attach(fairing)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn attach(&mut self, fairing: Fairing) {
|
||||
match fairing {
|
||||
Fairing::Launch(f) => self.launch.push(f),
|
||||
Fairing::Request(f) => self.request.push(f),
|
||||
Fairing::Response(f) => self.response.push(f),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn handle_launch(&mut self, mut rocket: Rocket) -> Option<Rocket> {
|
||||
let mut success = Some(());
|
||||
let launch_fairings = ::std::mem::replace(&mut self.launch, vec![]);
|
||||
for fairing in launch_fairings {
|
||||
rocket = fairing(rocket).unwrap_or_else(|r| { success = None; r });
|
||||
}
|
||||
|
||||
success.map(|_| rocket)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn handle_request(&self, req: &mut Request, data: &Data) {
|
||||
for fairing in &self.request {
|
||||
fairing(req, data);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn handle_response(&self, request: &Request, response: &mut Response) {
|
||||
for fairing in &self.response {
|
||||
fairing(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
fn num_attached(&self) -> usize {
|
||||
self.launch.len() + self.request.len() + self.response.len()
|
||||
}
|
||||
|
||||
pub fn pretty_print_counts(&self) {
|
||||
use term_painter::ToStyle;
|
||||
use term_painter::Color::{White, Magenta};
|
||||
|
||||
if self.num_attached() > 0 {
|
||||
info!("📡 {}:", Magenta.paint("Fairings"));
|
||||
}
|
||||
|
||||
if !self.launch.is_empty() {
|
||||
info_!("{} launch", White.paint(self.launch.len()));
|
||||
}
|
||||
|
||||
if !self.request.is_empty() {
|
||||
info_!("{} request", White.paint(self.request.len()));
|
||||
}
|
||||
|
||||
if !self.response.is_empty() {
|
||||
info_!("{} response", White.paint(self.response.len()));
|
||||
}
|
||||
}
|
||||
/// The default implementation of this method does nothing.
|
||||
#[allow(unused_variables)]
|
||||
fn on_response(&self, request: &Request, response: &mut Response) { }
|
||||
}
|
||||
|
|
|
@ -126,12 +126,12 @@ pub mod outcome;
|
|||
pub mod config;
|
||||
pub mod data;
|
||||
pub mod handler;
|
||||
pub mod error;
|
||||
pub mod fairing;
|
||||
|
||||
mod router;
|
||||
mod rocket;
|
||||
mod codegen;
|
||||
mod error;
|
||||
mod catcher;
|
||||
mod ext;
|
||||
|
||||
|
@ -141,7 +141,6 @@ mod ext;
|
|||
#[doc(hidden)] pub use codegen::{StaticRouteInfo, StaticCatchInfo};
|
||||
#[doc(inline)] pub use outcome::Outcome;
|
||||
#[doc(inline)] pub use data::Data;
|
||||
#[doc(inline)] pub use fairing::Fairing;
|
||||
pub use router::Route;
|
||||
pub use request::{Request, State};
|
||||
pub use error::{Error, LaunchError};
|
||||
|
|
|
@ -11,7 +11,7 @@ use state::Container;
|
|||
|
||||
#[cfg(feature = "tls")] use hyper_rustls::TlsServer;
|
||||
use {logger, handler};
|
||||
use ext::{ReadExt, IntoCollection};
|
||||
use ext::ReadExt;
|
||||
use config::{self, Config, LoggedValue};
|
||||
use request::{Request, FormItems};
|
||||
use data::Data;
|
||||
|
@ -587,21 +587,18 @@ impl Rocket {
|
|||
self
|
||||
}
|
||||
|
||||
/// Attaches zero or more fairings to this instance of Rocket.
|
||||
/// Attaches a fairing to this instance of Rocket.
|
||||
///
|
||||
/// The `fairings` parameter to this function is generic: it may be either
|
||||
/// a `Vec<Fairing>`, `&[Fairing]`, or simply `Fairing`. In all cases, all
|
||||
/// supplied fairings are attached.
|
||||
///
|
||||
/// # Examples
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(plugin)]
|
||||
/// # #![plugin(rocket_codegen)]
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::{Rocket, Fairing};
|
||||
/// use rocket::Rocket;
|
||||
/// use rocket::fairing::AdHoc;
|
||||
///
|
||||
/// fn launch_fairing(rocket: Rocket) -> Result<Rocket, Rocket> {
|
||||
/// fn youll_see(rocket: Rocket) -> Result<Rocket, Rocket> {
|
||||
/// println!("Rocket is about to launch! You just see...");
|
||||
/// Ok(rocket)
|
||||
/// }
|
||||
|
@ -609,15 +606,14 @@ impl Rocket {
|
|||
/// fn main() {
|
||||
/// # if false { // We don't actually want to launch the server in an example.
|
||||
/// rocket::ignite()
|
||||
/// .attach(Fairing::Launch(Box::new(launch_fairing)))
|
||||
/// .attach(AdHoc::on_launch(youll_see))
|
||||
/// .launch();
|
||||
/// # }
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn attach<C: IntoCollection<Fairing>>(mut self, fairings: C) -> Self {
|
||||
let fairings = fairings.into_collection::<[Fairing; 1]>().into_vec();
|
||||
self.fairings.attach_all(fairings);
|
||||
pub fn attach<F: Fairing>(mut self, fairing: F) -> Self {
|
||||
self.fairings.attach(Box::new(fairing));
|
||||
self
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue