Trait rocket::fairing::Fairing[][src]

pub trait Fairing: Send + Sync + Any + 'static {
    fn info(&self) -> Info;

    fn on_ignite<'life0, 'async_trait>(
        &'life0 self,
        rocket: Rocket<Build>
    ) -> Pin<Box<dyn Future<Output = Result> + Send + 'async_trait>>
    where
        'life0: 'async_trait,
        Self: 'async_trait
, { ... }
fn on_liftoff<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _rocket: &'life1 Rocket<Orbit>
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
    where
        'life0: 'async_trait,
        'life1: 'async_trait,
        Self: '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
        'life0: 'async_trait,
        'life1: 'async_trait,
        'life2: 'async_trait,
        'life3: 'async_trait,
        'life4: 'async_trait,
        Self: '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
        'r: 'async_trait,
        'life0: 'async_trait,
        'life1: 'async_trait,
        'life2: 'async_trait,
        Self: '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 four kinds of fairing callbacks: launch, liftoff, request, and response. 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 four 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 the Rocket 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 callback B executing after a callback A can view changes made by A 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 returns Ok if it would like ignition and launch to proceed nominally and Err otherwise. If an ignite fairing returns Err, launch will be aborted. All ignite fairings are executed even if one or more signal a failure.

  • 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 the Rocket instance that has launched but not otherwise gracefully abort launch.

  • 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 into Request and Data structures but has not routed the request. A request callback can modify the request at will and Data::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 given Request may have changed between on_request and on_response invocations. Apart from any change made by other fairings, Rocket sets the method for HEAD requests to GET if there is no matching HEAD handler for that request. Additionally, Rocket will automatically strip the body for HEAD requests after response fairings have run.

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:

  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 ignition procedures.

  2. Determine which callbacks to actually issue on the Fairing.

    This is the kind field of type Kind. 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 ord together to represent any combination of kinds of callbacks. For instance, to request liftoff and response callbacks, return a kind field with the value Kind::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>) {
        /* ... */
    }
}

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::Failure((Status::InternalServerError, ())),
        }
    }
}

Required methods

Returns an Info structure containing the name and Kind of this fairing. The name can be any arbitrary string. Kind must be an ord 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

The ignite callback. Returns Ok if ignition should proceed and Err if ignition and launch should be aborted.

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).

The liftoff callback.

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 lauched application.

Default Implementation

The default implementation of this method does nothing.

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.

Default Implementation

The default implementation of this method does nothing.

The response callback.

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.

Implementations on Foreign Types

Implementors