Guide

The ins and outs of Rocket, in detail.

Fairings#

Fairings are Rocket's approach to structured middleware. With fairings, your application can hook into the request lifecycle to record or rewrite information about incoming requests and outgoing responses.

Overview#

Any type that implements the Fairing trait is a fairing. Fairings hook into Rocket's request lifecycle, receiving callbacks for events such as incoming requests and outgoing responses. Rocket passes information about these events to the fairing; the fairing can do what it wants with the information. This includes rewriting requests or responses, recording information about the event, or doing nothing at all.

Rocket’s fairings are a lot like middleware from other frameworks, but they bear a few key distinctions:

If you are familiar with middleware from other frameworks, you may find yourself reaching for fairings instinctively. Before doing so, remember that Rocket provides a rich set of mechanisms such as request guards and data guards that can be used to solve problems in a clean, composable, and robust manner.

Warning

As a general rule of thumb, only globally applicable actions should be effected through fairings. 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 all or the overwhelming majority application. On the other hand, you should use a fairing to record timing and usage statistics or to enforce global security policies.

Attaching#

Fairings are registered with Rocket via the attach method on a Rocket instance. Only when a fairing is attached will its callbacks fire. As an example, the following snippet attached two fairings, req_fairing and res_fairing, to a new Rocket instance:

1
2
3
4
rocket::ignite()
    .attach(req_fairing)
    .attach(res_fairing)
    .launch();

Fairings are executed in the order in which they are attached: the first attached fairing has its callbacks executed before all others. Because fairing callbacks may not be commutative, the order in which fairings are attached may be significant.

Callbacks#

There are four events for which Rocket issues fairing callbacks. Each of these events is described below:

Implementing#

Recall that a fairing is any type that implements the Fairing trait. A Fairing implementation has one required method: info, which returns an Info structure. This structure is used by Rocket to assign a name to the fairing and determine the set of callbacks the fairing is registering for. A Fairing can implement any of the available callbacks: on_attach, on_launch, on_request, and on_response. Each callback has a default implementation that does absolutely nothing.

Requirements#

A type implementing Fairing is required to 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.

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 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 code for a Counter fairing below implements exactly this. The fairing receives a request callback, where it increments a counter on each GET and POST request. It also receives a response callback, where it responds to unrouted requests to the /counts path by returning the recorded number of counts.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
struct Counter {
    get: AtomicUsize,
    post: AtomicUsize,
}

impl Fairing for Counter {
    // This is a request and response fairing named "GET/POST Counter".
    fn info(&self) -> Info {
        Info {
            name: "GET/POST Counter",
            kind: Kind::Request | Kind::Response
        }
    }

    // Increment the counter for `GET` and `POST` requests.
    fn on_request(&self, request: &mut Request, _: &Data) {
        match request.method() {
            Method::Get => self.get.fetch_add(1, Ordering::Relaxed),
            Method::Post => self.post.fetch_add(1, Ordering::Relaxed),
            _ => return
        }
    }

    fn on_response(&self, request: &Request, response: &mut Response) {
        // Don't change a successful user's response, ever.
        if response.status() != Status::NotFound {
            return
        }

        // Rewrite the response to return the current counts.
        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));
        }
    }
}

For brevity, imports are not shown. The complete example can be found in the Fairing documentation.

Ad-Hoc Fairings#

For simple occasions, implementing the Fairing trait can be cumbersome. This is why Rocket provides the AdHoc type, which creates a fairing from a simple function or closure. Using the AdHoc type is easy: simply call the on_attach, on_launch, on_request, or on_response constructors on AdHoc to create an AdHoc structure from a function or closure.

As an example, the code below creates a Rocket instance with two attached ad-hoc fairings. The first, a launch fairing named "Launch Printer", simply prints a message indicating that the application is about to the launch. The second named "Put Rewriter", a request fairing, rewrites the method of all requests to be PUT.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use rocket::fairing::AdHoc;
use rocket::http::Method;

rocket::ignite()
    .attach(AdHoc::on_launch("Launch Printer", |_| {
        println!("Rocket is about to launch! Exciting! Here we go...");
    }))
    .attach(AdHoc::on_request("Put Rewriter", |req, _| {
        req.set_method(Method::Put);
    }));