Tag Archives: CORS

Handling OPTIONS requests in rust using Rocket, with CORS

In setting up a couple of endpoints using Rocket (written in rust), I was able to easily get up the GET and POST handlers that I needed. These worked fine with all my testing in Postman.

However, when it came to real world testing, the browser was getting blocked due to CORS. So I found this Stack Overflow article, and was able to implement the CORS headers based off that:

https://stackoverflow.com/questions/62412361/how-to-set-up-cors-or-options-for-rocket-rs

This worked. Up to a point.

That point was the browser sending a preflight OPTIONS request, which is neither a GET or a POST. After floundering around with various efforts, such as trying to put a wildcard #[options()] handler (which never got picked up by the compiler for some reason; kept getting the message that the following function was unused), and so on, I finally landed on the solution.

Taking the recommended implementation of on_response():

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

All that was needed was to add a few lines:

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", "GET, POST, OPTIONS"));
	response.set_header(Header::new("Access-Control-Allow-Headers", "Content-Type"));
	response.set_header(Header::new("Access-Control-Request-Method", "GET, POST, OPTIONS"));
    
	if request.method() == Method::Options {
		let body = "";
		response.set_header(ContentType::Plain);
		response.set_sized_body(body.len(), std::io::Cursor::new(body));
		response.set_status(Status::Ok);
	}
}

This worked to get the proper headers in place, and also overcome the 404 errors. The 404 errors were coming because I had no explicit #[options()]; route handler in place – they just weren’t taking for some reason, as mentioned above. So the last line, response.set_status(Status::Ok); is what fixed that problem. This allowed the client to send an Options preflight request, which Rocket responded to with all the right bits, and without a 404 status coming back.

And to cut down on the length of messages about these requests in the shell, I implemented a catcher too, which are still present, due to the missing route handler:

#[catch(404)]
fn not_found(req: &Request<'_>) -> String {
	if req.method() == Method::Options {
		return "".to_string();
	}
	format!("Sorry, '{}' is not a valid path.", req.uri())
}

Attach the handler, and, bang! we’ve got a working set of endpoints handling CORS requests, with preflight Options requests. Rust with Rocket is very performant too – a release build responds with an average time of 63 milliseconds, most of which is probably the database query time.