Description
The CORS filter automatically adds the proper headers to responses and handles pre-flight requests, but if a route is ever rejected, the headers are not added, and so a browser will reject the response. This makes it hard to determine the failure from the client side, since the response never arrives there.
I've created a small demo to show this problem. Based on the example in the readme, we have the following server:
use warp::Filter;
#[tokio::main]
async fn main() {
// GET / with header X-Username -> Hello, X-Username
let hello = warp::filters::header::header("X-Username")
.map(|name: String| format!("Hello, {}!", name));
// Add CORS to that route
let hello = hello.with(warp::cors().allow_any_origin());
warp::serve(hello)
.run(([127, 0, 0, 1], 3030))
.await;
}
A valid request is handled correctly, and the allowed origin header will show up:
$ curl localhost:3030 -H "Origin: example.org" -v -H "X-Username: Bert"
* Trying 127.0.0.1:3030...
* Connected to localhost (127.0.0.1) port 3030 (#0)
> GET / HTTP/1.1
> Host: localhost:3030
> User-Agent: curl/7.69.1
> Accept: */*
> Origin: example.org
> X-Username: Bert
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< access-control-allow-origin: example.org
< content-length: 12
< date: Fri, 03 Apr 2020 08:29:43 GMT
<
* Connection #0 to host localhost left intact
Hello, Bert!
If we are missing the required header, we get a Bad Request response as expected, but the access-control-allow-origin-header
will be missing:
$ curl localhost:3030 -H "Origin: example.org" -v
* Trying 127.0.0.1:3030...
* Connected to localhost (127.0.0.1) port 3030 (#0)
> GET / HTTP/1.1
> Host: localhost:3030
> User-Agent: curl/7.69.1
> Accept: */*
> Origin: example.org
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< content-type: text/plain; charset=utf-8
< content-length: 35
< date: Fri, 03 Apr 2020 08:35:37 GMT
<
* Connection #0 to host localhost left intact
Missing request header "X-Username"
It's possible to work around this issue by having a recover
at the end to catch all possible errors at the end. This is inconvenient because in general, the default error messages are pretty good and don't need replacement, since you can clearly see what's wrong here.