pub trait Fairing:
Send
+ Sync
+ AsAny
+ 'static {
// Required method
fn info(&self) -> Info;
// Provided methods
fn on_ignite<'life0, 'async_trait>(
&'life0 self,
rocket: Rocket<Build>,
) -> Pin<Box<dyn Future<Output = Result> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn on_liftoff<'life0, 'life1, 'async_trait>(
&'life0 self,
_rocket: &'life1 Rocket<Orbit>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
fn on_request<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
_req: &'life1 mut Request<'life2>,
_data: &'life3 mut Data<'life4>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait { ... }
fn on_response<'r, 'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_req: &'r Request<'life1>,
_res: &'life2 mut Response<'r>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait,
'r: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait { ... }
fn on_shutdown<'life0, 'life1, 'async_trait>(
&'life0 self,
_rocket: &'life1 Rocket<Orbit>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
}
Expand description
Trait implemented by fairings: Rocket’s structured middleware.
§Considerations
Fairings are a large hammer that can easily be abused and misused. If you
are considering writing a Fairing
implementation, first consider if it is
appropriate to do so. While middleware is often the best solution to some
problems in other frameworks, it is often a suboptimal solution in Rocket.
This is because Rocket provides richer mechanisms such as request guards
and data guards that can be used to accomplish the same objective in a
cleaner, more composable, and more robust manner.
As a general rule of thumb, only globally applicable actions should be implemented via fairings. For instance, you should not use a fairing to implement authentication or authorization (preferring to use a request guard instead) unless the authentication or authorization applies to the entire application. On the other hand, you should use a fairing to record timing and/or usage statistics or to implement global security policies.
§Fairing Callbacks
There are five kinds of fairing callbacks: launch, liftoff, request,
response, and shutdown. 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 identified in the
fairing’s Kind
.
The callback kinds are as follows:
-
Ignite (
on_ignite
)An ignite callback, represented by the
Fairing::on_ignite()
method, is called just prior to liftoff, during ignition. The state of theRocket
instance is, at this point, not finalized, as it may be modified at will by other ignite fairings.All ignite callbacks are executed in breadth-first
attach()
order. A callbackB
executing after a callbackA
can view changes made byA
but not vice-versa.An ignite callback can arbitrarily modify the
Rocket
instance being constructed. It should take care not to introduce infinite recursion by recursively attaching ignite fairings. It returnsOk
if it would like ignition and launch to proceed nominally andErr
otherwise. If an ignite fairing returnsErr
, launch will be aborted. All ignite fairings are executed even if one or more signal an error. -
Liftoff (
on_liftoff
)A liftoff callback, represented by the
Fairing::on_liftoff()
method, is called immediately after a Rocket application has launched. At this point, Rocket has opened a socket for listening but has not yet begun accepting connections. A liftoff callback can inspect theRocket
instance that has launched and even schedule a shutdown usingShutdown::notify()
viaRocket::shutdown()
.Liftoff fairings are run concurrently; resolution of all fairings is awaited before resuming request serving.
-
Request (
on_request
)A request callback, represented by the
Fairing::on_request()
method, is called just after a request is received, immediately after pre-processing the request with method changes due to_method
form fields. At this point, Rocket has parsed the incoming HTTP request intoRequest
andData
structures but has not routed the request. A request callback can modify the request at will andData::peek()
into the incoming data. It may not, however, abort or respond directly to the request; these issues are better handled via request guards or via response callbacks. Any modifications to a request are persisted and can potentially alter how a request is routed. -
Response (
on_response
)A response callback, represented by the
Fairing::on_response()
method, 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 example, a response callback can provide a default response when the user fails to handle the request by checking for 404 responses. Note that a givenRequest
may have changed betweenon_request
andon_response
invocations. Apart from any change made by other fairings, Rocket sets the method forHEAD
requests toGET
if there is no matchingHEAD
handler for that request. Additionally, Rocket will automatically strip the body forHEAD
requests after response fairings have run. -
Shutdown (
on_shutdown
)A shutdown callback, represented by the
Fairing::on_shutdown()
method, is called when shutdown is triggered. At this point, graceful shutdown has commenced but not completed; no new requests are accepted but the application may still be actively serving existing requests.Rocket guarantees, however, that all requests are completed or aborted once grace and mercy periods have expired. This implies that a shutdown fairing that (asynchronously) sleeps for
grace + mercy + ε
seconds before executing any logic will execute said logic after all requests have been processed or aborted. Note that such fairings may wish to operate using theOk
return value ofRocket::launch()
instead.All registered shutdown fairings are run concurrently; resolution of all fairings is awaited before resuming shutdown. Shutdown fairings do not affect grace and mercy periods. In other words, any time consumed by shutdown fairings is not added to grace and mercy periods.
Note: Shutdown fairings are only run during testing if the
Client
is terminated usingClient::terminate()
.
§Singletons
In general, any number of instances of a given fairing type can be attached
to one instance of Rocket
. If this is not desired, a fairing can request
to be a singleton by specifying Kind::Singleton
. Only the last
attached instance of a singleton will be preserved at ignite-time. That is,
an attached singleton instance will replace any previously attached
instance. The Shield
fairing is an example of a
singleton fairing.
§Implementing
A Fairing
implementation has one required method: info
. A Fairing
can also implement any of the available callbacks: on_ignite
, on_liftoff
,
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 called by Rocket.
§Fairing Info
Every Fairing
must implement the info
method, which returns an
Info
structure. This structure is used by Rocket to:
-
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 ignition procedures. -
Determine which callbacks to actually issue on the
Fairing
.This is the
kind
field of typeKind
. 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 beor
d together to represent any combination of kinds of callbacks. For instance, to request liftoff and response callbacks, return akind
field with the valueKind::Liftoff | Kind::Response
.
§Restrictions
A Fairing
must be Send
+ Sync
+ 'static
. This means that the
fairing must be sendable across thread boundaries (Send
), thread-safe
(Sync
), and have only 'static
references, if any ('static
). Note that
these bounds do not prohibit a Fairing
from holding state: the state
need simply be thread-safe and statically available or heap allocated.
§Async Trait
Fairing
is an async trait. Implementations of Fairing
must be
decorated with an attribute of #[rocket::async_trait]
:
use rocket::{Rocket, Request, Data, Response, Build, Orbit};
use rocket::fairing::{self, Fairing, Info, Kind};
#[rocket::async_trait]
impl Fairing for MyType {
fn info(&self) -> Info {
/* ... */
}
async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
/* ... */
}
async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
/* ... */
}
async fn on_request(&self, req: &mut Request<'_>, data: &mut Data<'_>) {
/* ... */
}
async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) {
/* ... */
}
async fn on_shutdown(&self, rocket: &Rocket<Orbit>) {
/* ... */
}
}
§Example
As an example, we want to record the number of GET
and POST
requests
that our application has received. While we could do this with request
guards and managed state, 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 acts 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.
use std::future::Future;
use std::io::Cursor;
use std::pin::Pin;
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,
}
#[rocket::async_trait]
impl Fairing for Counter {
fn info(&self) -> Info {
Info {
name: "GET/POST Counter",
kind: Kind::Request | Kind::Response
}
}
async fn on_request(&self, req: &mut Request<'_>, _: &mut Data<'_>) {
if req.method() == Method::Get {
self.get.fetch_add(1, Ordering::Relaxed);
} else if req.method() == Method::Post {
self.post.fetch_add(1, Ordering::Relaxed);
}
}
async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) {
// Don't change a successful user's response, ever.
if res.status() != Status::NotFound {
return
}
if req.method() == Method::Get && req.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);
res.set_status(Status::Ok);
res.set_header(ContentType::Plain);
res.set_sized_body(body.len(), Cursor::new(body));
}
}
}
§Request-Local State
Fairings can use request-local state to persist or carry data between requests and responses, or to pass data to a request guard.
As an example, the following fairing uses request-local state to time
requests, setting an X-Response-Time
header on all responses with the
elapsed time. It also exposes the start time of a request via a StartTime
request guard.
/// Fairing for timing requests.
pub struct RequestTimer;
/// Value stored in request-local state.
#[derive(Copy, Clone)]
struct TimerStart(Option<SystemTime>);
#[rocket::async_trait]
impl Fairing for RequestTimer {
fn info(&self) -> Info {
Info {
name: "Request Timer",
kind: Kind::Request | Kind::Response
}
}
/// Stores the start time of the request in request-local state.
async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) {
// Store a `TimerStart` instead of directly storing a `SystemTime`
// to ensure that this usage doesn't conflict with anything else
// that might store a `SystemTime` in request-local cache.
request.local_cache(|| TimerStart(Some(SystemTime::now())));
}
/// Adds a header to the response indicating how long the server took to
/// process the request.
async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) {
let start_time = req.local_cache(|| TimerStart(None));
if let Some(Ok(duration)) = start_time.0.map(|st| st.elapsed()) {
let ms = duration.as_secs() * 1000 + duration.subsec_millis() as u64;
res.set_raw_header("X-Response-Time", format!("{} ms", ms));
}
}
}
/// Request guard used to retrieve the start time of a request.
#[derive(Copy, Clone)]
pub struct StartTime(pub SystemTime);
// Allows a route to access the time a request was initiated.
#[rocket::async_trait]
impl<'r> FromRequest<'r> for StartTime {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, ()> {
match *request.local_cache(|| TimerStart(None)) {
TimerStart(Some(time)) => request::Outcome::Success(StartTime(time)),
TimerStart(None) => request::Outcome::Error((Status::InternalServerError, ())),
}
}
}
Required Methods§
sourcefn info(&self) -> Info
fn info(&self) -> Info
Returns an Info
structure containing the name
and Kind
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::Ignite | Kind::Request
is used, then Rocket will only call the
on_ignite
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 an ignite and response fairing.
use rocket::fairing::{Fairing, Info, Kind};
struct MyFairing;
impl Fairing for MyFairing {
fn info(&self) -> Info {
Info {
name: "My Custom Fairing",
kind: Kind::Ignite | Kind::Response
}
}
}
Provided Methods§
sourcefn on_ignite<'life0, 'async_trait>(
&'life0 self,
rocket: Rocket<Build>,
) -> Pin<Box<dyn Future<Output = Result> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn on_ignite<'life0, 'async_trait>(
&'life0 self,
rocket: Rocket<Build>,
) -> Pin<Box<dyn Future<Output = Result> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
The ignite callback. Returns Ok
if ignition should proceed and Err
if ignition and launch should be aborted.
See Fairing Callbacks for complete semantics.
This method is called during ignition and if Kind::Ignite
is in the
kind
field of the Info
structure for this fairing. The rocket
parameter is the Rocket
instance that is currently being built for
this application.
§Default Implementation
The default implementation of this method simply returns Ok(rocket)
.
sourcefn on_liftoff<'life0, 'life1, 'async_trait>(
&'life0 self,
_rocket: &'life1 Rocket<Orbit>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn on_liftoff<'life0, 'life1, 'async_trait>(
&'life0 self,
_rocket: &'life1 Rocket<Orbit>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
The liftoff callback.
See Fairing Callbacks for complete semantics.
This method is called just after launching the application if
Kind::Liftoff
is in the kind
field of the Info
structure for this
fairing. The Rocket
parameter corresponds to the launched application.
§Default Implementation
The default implementation of this method does nothing.
sourcefn on_request<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
_req: &'life1 mut Request<'life2>,
_data: &'life3 mut Data<'life4>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
fn on_request<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
_req: &'life1 mut Request<'life2>,
_data: &'life3 mut Data<'life4>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
The request callback.
See Fairing Callbacks for complete semantics.
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.
§Default Implementation
The default implementation of this method does nothing.
sourcefn on_response<'r, 'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_req: &'r Request<'life1>,
_res: &'life2 mut Response<'r>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'r: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn on_response<'r, 'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_req: &'r Request<'life1>,
_res: &'life2 mut Response<'r>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'r: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
The response callback.
See Fairing Callbacks for complete semantics.
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.
§Default Implementation
The default implementation of this method does nothing.
sourcefn on_shutdown<'life0, 'life1, 'async_trait>(
&'life0 self,
_rocket: &'life1 Rocket<Orbit>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn on_shutdown<'life0, 'life1, 'async_trait>(
&'life0 self,
_rocket: &'life1 Rocket<Orbit>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
The shutdown callback.
See Fairing Callbacks for complete semantics.
This method is called when shutdown is triggered if Kind::Shutdown
is in the kind
field of the Info
structure for this fairing. The
Rocket
parameter corresponds to the running application.
§Default Implementation
The default implementation of this method does nothing.