Search code examples
rustcorsrust-rocket

CORS Fairing in Rocket Causing Lifetime Errors


I'm trying to add a CORS policy to my rocket API. I've tried a couple ways but the closest (I think) and most straightforward way so far has been to add a custom Fairing that sets the CORS headers in the on_response hook. I've been using the Fairings implementation guide and this answer to a similar question, but I am running into complaints about lifetimes.

This is the on_response hook.

fn on_response(&self, request: &Request, response: &mut Response) {
    response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
    response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS"));
    response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
    response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
}

Running cargo run yields the following error:

error[E0195]: lifetime parameters or bounds on method `on_response` do not match the trait declaration
  --> src/main.rs:16:19
   |
16 |     fn on_response(&self, request: &Request, response: &mut Response) {
   |                   ^ lifetimes do not match method in trait

Is there a way to resolve the lifetime complaints?

Also of interest would be an idiomatic way to set CORS in Rocket (I've looked at rocket_cors but following the examples creates a bunch of version headaches regarding having to use nightly builds, but maybe I missed a more straightforward way?).

Here's the full code:

Main.rs:

use rocket::http::Header;
use rocket::{Request, Response};
use rocket::fairing::{Fairing, Info, Kind};
#[macro_use] extern crate rocket;

pub struct CORS;

impl Fairing for CORS {
    fn info(&self) -> Info {
        Info {
            name: "Add CORS headers to responses",
            kind: Kind::Response
        }
    }

    fn on_response(&self, request: &Request, response: &mut Response) {
        response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
        response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS"));
        response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
        response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
    }
}

#[get("/")]
fn index() -> &'static str {
    "Hello, world!"
}

#[launch]
fn rocket() -> _ {
    rocket::build()
        .attach(CORS)
        .mount("/", routes![index])
}

cargo.toml

[package]
name = "my_project"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rocket = "0.5.0-rc.1"
rocket_cors = "0.5.2"

Solution

  • In 0.5.0 Fairing implementations need async traits.

    #[rocket::async_trait]
    impl Fairing for CORS {
        fn info(&self) -> Info {
            Info {
                name: "Add CORS headers to responses",
                kind: Kind::Response
            }
        }
    
        async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
            response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
            response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS"));
            response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
            response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
        }
    }
    

    (thanks @TimY for the comment over here)