Add catch-all OPTIONS route and update examples/tests (#24)

This commit is contained in:
Yong Wen Chua 2017-07-24 15:45:53 +08:00 committed by GitHub
parent 8eb782a7ca
commit 36ec852a78
5 changed files with 274 additions and 128 deletions

View File

@ -15,13 +15,6 @@ fn responder(cors: Guard) -> Responder<&str> {
cors.responder("Hello CORS!") cors.responder("Hello CORS!")
} }
/// You need to define an OPTIONS route for preflight checks.
/// These routes can just return the unit type `()`
#[options("/")]
fn responder_options(cors: Guard) -> Responder<()> {
cors.responder(())
}
/// Using a `Response` instead of a `Responder`. You generally won't have to do this. /// Using a `Response` instead of a `Responder`. You generally won't have to do this.
#[get("/response")] #[get("/response")]
fn response(cors: Guard) -> Response { fn response(cors: Guard) -> Response {
@ -30,12 +23,16 @@ fn response(cors: Guard) -> Response {
cors.response(response) cors.response(response)
} }
/// You need to define an OPTIONS route for preflight checks. /// Manually mount an OPTIONS route for your own handling
/// These routes can just return the unit type `()` #[options("/manual")]
#[options("/response")] fn manual_options(cors: Guard) -> Responder<&str> {
fn response_options(cors: Guard) -> Response { cors.responder("Manual OPTIONS preflight handling")
let response = Response::new(); }
cors.response(response)
/// Manually mount an OPTIONS route for your own handling
#[get("/manual")]
fn manual(cors: Guard) -> Responder<&str> {
cors.responder("Manual OPTIONS preflight handling")
} }
fn main() { fn main() {
@ -54,8 +51,12 @@ fn main() {
rocket::ignite() rocket::ignite()
.mount( .mount(
"/", "/",
routes![responder, responder_options, response, response_options], routes![responder, response],
) )
// Mount the routes to catch all the OPTIONS pre-flight requests
.mount("/", rocket_cors::catch_all_options_routes())
// You can also manually mount an OPTIONS route that will be used instead
.mount("/", routes![manual, manual_options])
.manage(options) .manage(options)
.launch(); .launch();
} }

View File

@ -18,15 +18,6 @@ fn borrowed<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
) )
} }
/// You need to define an OPTIONS route for preflight checks.
/// These routes can just return the unit type `()`
#[options("/")]
fn borrowed_options<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
options.inner().respond_borrowed(
|guard| guard.responder(()),
)
}
/// Using a `Response` instead of a `Responder`. You generally won't have to do this. /// Using a `Response` instead of a `Responder`. You generally won't have to do this.
#[get("/response")] #[get("/response")]
fn response<'r>(options: State<'r, Cors>) -> impl Responder<'r> { fn response<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
@ -38,15 +29,6 @@ fn response<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
) )
} }
/// You need to define an OPTIONS route for preflight checks.
/// These routes can just return the unit type `()`
#[options("/response")]
fn response_options<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
options.inner().respond_borrowed(
move |guard| guard.response(Response::new()),
)
}
/// Create and use an ad-hoc Cors /// Create and use an ad-hoc Cors
#[get("/owned")] #[get("/owned")]
fn owned<'r>() -> impl Responder<'r> { fn owned<'r>() -> impl Responder<'r> {
@ -54,7 +36,8 @@ fn owned<'r>() -> impl Responder<'r> {
options.respond_owned(|guard| guard.responder("Hello CORS")) options.respond_owned(|guard| guard.responder("Hello CORS"))
} }
/// You need to define an OPTIONS route for preflight checks. /// You need to define an OPTIONS route for preflight checks if you want to use `Cors` struct
/// that is not in Rocket's managed state.
/// These routes can just return the unit type `()` /// These routes can just return the unit type `()`
#[options("/owned")] #[options("/owned")]
fn owned_options<'r>() -> impl Responder<'r> { fn owned_options<'r>() -> impl Responder<'r> {
@ -82,13 +65,12 @@ fn main() {
"/", "/",
routes![ routes![
borrowed, borrowed,
borrowed_options,
response, response,
response_options,
owned, owned,
owned_options, owned_options,
], ],
) )
.mount("/", rocket_cors::catch_all_options_routes()) // mount the catch all routes
.manage(cors_options()) .manage(cors_options())
.launch(); .launch();
} }

View File

@ -76,7 +76,7 @@
//! |:---------------------------------------:|:-------:|:-------------:|:------:| //! |:---------------------------------------:|:-------:|:-------------:|:------:|
//! | Must apply to all routes | ✔ | ✗ | ✗ | //! | Must apply to all routes | ✔ | ✗ | ✗ |
//! | Different settings for different routes | ✗ | ✗ | ✔ | //! | Different settings for different routes | ✗ | ✗ | ✔ |
//! | Must define OPTIONS route | ✗ | ✔ | ✔ | //! | May define custom OPTIONS routes | ✗ | ✔ | ✔ |
//! //!
//! ### Fairing //! ### Fairing
//! //!
@ -128,16 +128,16 @@
//! //!
//! Using request guard requires you to sacrifice the convenience of Fairings for being able to //! Using request guard requires you to sacrifice the convenience of Fairings for being able to
//! opt some routes out of CORS checks and enforcement. _BUT_ you are still restricted to only //! opt some routes out of CORS checks and enforcement. _BUT_ you are still restricted to only
//! one set of CORS settings and you will now have to define `OPTIONS` routes for all the routes //! one set of CORS settings and you have to mount additional routes to catch and process OPTIONS
//! you want to have CORS checks on. The `OPTIONS` routes are used for CORS preflight checks. //! requests. The `OPTIONS` routes are used for CORS preflight checks.
//! //!
//! You will have to do the following: //! You will have to do the following:
//! //!
//! - Create a [`Cors` struct](struct.Cors.html) and during Rocket's ignite, add the struct to //! - Create a [`Cors` struct](struct.Cors.html) and during Rocket's ignite, add the struct to
//! Rocket's [managed state](https://rocket.rs/guide/state/#managed-state). //! Rocket's [managed state](https://rocket.rs/guide/state/#managed-state).
//! - For all the routes that you want to enforce CORS on, you have to define a `OPTIONS` route //! - For all the routes that you want to enforce CORS on, you can mount either some
//! for the path. You can use [dynamic segments](https://rocket.rs/guide/requests/#dynamic-segments) //! [catch all route](fn.catch_all_options_routes.html) or define your own route for the OPTIONS
//! to reduce the number of routes you have to define. //! verb.
//! - Then in all the routes you want to enforce CORS on, add a //! - Then in all the routes you want to enforce CORS on, add a
//! [Request Guard](https://rocket.rs/guide/requests/#request-guards) for the //! [Request Guard](https://rocket.rs/guide/requests/#request-guards) for the
//! [`Guard`](struct.Guard.html) struct in the route arguments. You should not wrap this in an //! [`Guard`](struct.Guard.html) struct in the route arguments. You should not wrap this in an
@ -146,11 +146,8 @@
//! - In your routes, to add CORS headers to your responses, use the appropriate functions on the //! - In your routes, to add CORS headers to your responses, use the appropriate functions on the
//! [`Guard`](struct.Guard.html) for a `Response` or a `Responder`. //! [`Guard`](struct.Guard.html) for a `Response` or a `Responder`.
//! //!
//! You can mix this with the "manual" checks, but whichever `Response` is the last merged will
//! overwrite the previous CORS headers.
//!
//! ```rust,no_run //! ```rust,no_run
//! #![feature(plugin, custom_derive)] //! #![feature(plugin)]
//! #![plugin(rocket_codegen)] //! #![plugin(rocket_codegen)]
//! extern crate rocket; //! extern crate rocket;
//! extern crate rocket_cors; //! extern crate rocket_cors;
@ -167,27 +164,24 @@
//! cors.responder("Hello CORS!") //! cors.responder("Hello CORS!")
//! } //! }
//! //!
//! /// You need to define an OPTIONS route for preflight checks.
//! /// These routes can just return the unit type `()`
//! #[options("/")]
//! fn responder_options(cors: Guard) -> Responder<()> {
//! cors.responder(())
//! }
//!
//! /// Using a `Response` instead of a `Responder`. You generally won't have to do this. //! /// Using a `Response` instead of a `Responder`. You generally won't have to do this.
//! #[get("/responder")] //! #[get("/response")]
//! fn response(cors: Guard) -> Response { //! fn response(cors: Guard) -> Response {
//! let mut response = Response::new(); //! let mut response = Response::new();
//! response.set_sized_body(Cursor::new("Hello CORS!")); //! response.set_sized_body(Cursor::new("Hello CORS!"));
//! cors.response(response) //! cors.response(response)
//! } //! }
//! //!
//! /// You need to define an OPTIONS route for preflight checks. //! /// Manually mount an OPTIONS route for your own handling
//! /// These routes can just return the unit type `()` //! #[options("/manual")]
//! #[options("/responder")] //! fn manual_options(cors: Guard) -> Responder<&str> {
//! fn response_options(cors: Guard) -> Response { //! cors.responder("Manual OPTIONS preflight handling")
//! let response = Response::new(); //! }
//! cors.response(response) //!
//! /// Manually mount an OPTIONS route for your own handling
//! #[get("/manual")]
//! fn manual(cors: Guard) -> Responder<&str> {
//! cors.responder("Manual OPTIONS preflight handling")
//! } //! }
//! //!
//! fn main() { //! fn main() {
@ -204,11 +198,17 @@
//! }; //! };
//! //!
//! rocket::ignite() //! rocket::ignite()
//! .mount("/", routes![responder, responder_options, response, response_options]) //! .mount(
//! "/",
//! routes![responder, response],
//! )
//! // Mount the routes to catch all the OPTIONS pre-flight requests
//! .mount("/", rocket_cors::catch_all_options_routes())
//! // You can also manually mount an OPTIONS route that will be used instead
//! .mount("/", routes![manual, manual_options])
//! .manage(options) //! .manage(options)
//! .launch(); //! .launch();
//! } //! }
//!
//! ``` //! ```
//! //!
//! ## Truly Manual //! ## Truly Manual
@ -338,15 +338,6 @@
//! ) //! )
//! } //! }
//! //!
//! /// You need to define an OPTIONS route for preflight checks.
//! /// These routes can just return the unit type `()`
//! #[options("/")]
//! fn borrowed_options<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
//! options.inner().respond_borrowed(
//! |guard| guard.responder(()),
//! )
//! }
//!
//! /// Using a `Response` instead of a `Responder`. You generally won't have to do this. //! /// Using a `Response` instead of a `Responder`. You generally won't have to do this.
//! #[get("/response")] //! #[get("/response")]
//! fn response<'r>(options: State<'r, Cors>) -> impl Responder<'r> { //! fn response<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
@ -358,15 +349,6 @@
//! ) //! )
//! } //! }
//! //!
//! /// You need to define an OPTIONS route for preflight checks.
//! /// These routes can just return the unit type `()`
//! #[options("/response")]
//! fn response_options<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
//! options.inner().respond_borrowed(
//! move |guard| guard.response(Response::new()),
//! )
//! }
//!
//! fn cors_options() -> Cors { //! fn cors_options() -> Cors {
//! let (allowed_origins, failed_origins) = AllowedOrigins::some(&["https://www.acme.com"]); //! let (allowed_origins, failed_origins) = AllowedOrigins::some(&["https://www.acme.com"]);
//! assert!(failed_origins.is_empty()); //! assert!(failed_origins.is_empty());
@ -387,11 +369,10 @@
//! "/", //! "/",
//! routes![ //! routes![
//! borrowed, //! borrowed,
//! borrowed_options,
//! response, //! response,
//! response_options,
//! ], //! ],
//! ) //! )
//! .mount("/", rocket_cors::catch_all_options_routes()) // mount the catch all routes
//! .manage(cors_options()) //! .manage(cors_options())
//! .launch(); //! .launch();
//! } //! }
@ -1725,6 +1706,51 @@ fn actual_request_response(options: &Cors, origin: Origin) -> Response {
response response
} }
/// Returns "catch all" OPTIONS routes that you can mount to catch all OPTIONS request. Only works
/// if you have put a `Cors` struct into Rocket's managed state.
///
/// This route has very high rank (and therefore low priority) of
/// [max value](https://doc.rust-lang.org/nightly/std/primitive.isize.html#method.max_value)
/// so you can define your own to override this route's behaviour.
///
/// See the documentation at the [crate root](index.html) for usage information.
pub fn catch_all_options_routes() -> Vec<rocket::Route> {
vec![
rocket::Route::ranked(
isize::max_value(),
http::Method::Options,
"/",
catch_all_options_route_handler
),
rocket::Route::ranked(
isize::max_value(),
http::Method::Options,
"/<catch_all_options_route..>",
catch_all_options_route_handler
),
]
}
/// Handler for the "catch all options route"
fn catch_all_options_route_handler<'r>(
request: &'r Request,
_: rocket::Data,
) -> rocket::handler::Outcome<'r> {
let guard: Guard = match request.guard() {
Outcome::Success(guard) => guard,
Outcome::Failure((status, _)) => return rocket::handler::Outcome::failure(status),
Outcome::Forward(()) => unreachable!("Should not be reachable"),
};
info_!(
"\"Catch all\" handling of CORS `OPTIONS` preflight for request {}",
request
);
rocket::handler::Outcome::from(request, guard.responder(()))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr; use std::str::FromStr;

View File

@ -13,11 +13,6 @@ use rocket::http::Method;
use rocket::http::{Header, Status}; use rocket::http::{Header, Status};
use rocket::local::Client; use rocket::local::Client;
#[options("/")]
fn cors_options(cors: cors::Guard) -> cors::Responder<&str> {
cors.responder("")
}
#[get("/")] #[get("/")]
fn cors(cors: cors::Guard) -> cors::Responder<&str> { fn cors(cors: cors::Guard) -> cors::Responder<&str> {
cors.responder("Hello CORS") cors.responder("Hello CORS")
@ -28,33 +23,39 @@ fn panicking_route(_cors: cors::Guard) {
panic!("This route will panic"); panic!("This route will panic");
} }
// The following routes tests that the routes can be compiled with ad-hoc CORS Response/Responders /// Manually specify our own OPTIONS route
#[options("/manual")]
fn cors_manual_options(cors: cors::Guard) -> cors::Responder<&str> {
cors.responder("Manual CORS Preflight")
}
/// Manually specify our own OPTIONS route
#[get("/manual")]
fn cors_manual(cors: cors::Guard) -> cors::Responder<&str> {
cors.responder("Hello CORS")
}
/// Using a `Response` instead of a `Responder` /// Using a `Response` instead of a `Responder`
#[allow(unmounted_route)] #[get("/response")]
#[get("/")]
fn response(cors: cors::Guard) -> Response { fn response(cors: cors::Guard) -> Response {
cors.response(Response::new()) cors.response(Response::new())
} }
/// `Responder` with String /// `Responder` with String
#[allow(unmounted_route)] #[get("/responder/string")]
#[get("/")]
fn responder_string(cors: cors::Guard) -> cors::Responder<String> { fn responder_string(cors: cors::Guard) -> cors::Responder<String> {
cors.responder("Hello CORS".to_string()) cors.responder("Hello CORS".to_string())
} }
/// `Responder` with 'static () /// `Responder` with 'static ()
#[allow(unmounted_route)] #[get("/responder/unit")]
#[get("/")]
fn responder_unit(cors: cors::Guard) -> cors::Responder<()> { fn responder_unit(cors: cors::Guard) -> cors::Responder<()> {
cors.responder(()) cors.responder(())
} }
struct SomeState; struct SomeState;
/// Borrow `SomeState` from Rocket /// Borrow `SomeState` from Rocket
#[allow(unmounted_route)] #[get("/state")]
#[get("/")]
fn state<'r>(cors: cors::Guard<'r>, _state: State<'r, SomeState>) -> cors::Responder<'r, &'r str> { fn state<'r>(cors: cors::Guard<'r>, _state: State<'r, SomeState>) -> cors::Responder<'r, &'r str> {
cors.responder("hmm") cors.responder("hmm")
} }
@ -74,8 +75,12 @@ fn make_cors_options() -> cors::Cors {
fn make_rocket() -> rocket::Rocket { fn make_rocket() -> rocket::Rocket {
rocket::ignite() rocket::ignite()
.mount("/", routes![cors, cors_options, panicking_route]) .mount("/", routes![cors, panicking_route])
.mount("/", routes![response, responder_string, responder_unit, state])
.mount("/", cors::catch_all_options_routes()) // mount the catch all routes
.mount("/", routes![cors_manual, cors_manual_options]) // manual OPTIOONS routes
.manage(make_cors_options()) .manage(make_cors_options())
.manage(SomeState)
} }
#[test] #[test]
@ -122,8 +127,9 @@ fn smoke_test() {
assert_eq!("https://www.acme.com", origin_header); assert_eq!("https://www.acme.com", origin_header);
} }
/// Check the "catch all" OPTIONS route works for `/`
#[test] #[test]
fn cors_options_check() { fn cors_options_catch_all_check() {
let rocket = make_rocket(); let rocket = make_rocket();
let client = Client::new(rocket).unwrap(); let client = Client::new(rocket).unwrap();
@ -153,6 +159,39 @@ fn cors_options_check() {
assert_eq!("https://www.acme.com", origin_header); assert_eq!("https://www.acme.com", origin_header);
} }
/// Check the "catch all" OPTIONS route works for other routes
#[test]
fn cors_options_catch_all_check_other_routes() {
let rocket = make_rocket();
let client = Client::new(rocket).unwrap();
let origin_header = Header::from(
hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
);
let method_header = Header::from(hyper::header::AccessControlRequestMethod(
hyper::method::Method::Get,
));
let request_headers = hyper::header::AccessControlRequestHeaders(
vec![FromStr::from_str("Authorization").unwrap()],
);
let request_headers = Header::from(request_headers);
let req = client
.options("/response/unit")
.header(origin_header)
.header(method_header)
.header(request_headers);
let response = req.dispatch();
assert!(response.status().class().is_success());
let origin_header = response
.headers()
.get_one("Access-Control-Allow-Origin")
.expect("to exist");
assert_eq!("https://www.acme.com", origin_header);
}
#[test] #[test]
fn cors_get_check() { fn cors_get_check() {
let rocket = make_rocket(); let rocket = make_rocket();
@ -360,3 +399,38 @@ fn routes_failing_checks_are_not_executed() {
.is_none() .is_none()
); );
} }
/// This test ensures that manually mounted CORS OPTIONS routes are used even in the presence of
/// a "catch all" route.
#[test]
fn overridden_options_routes_are_used() {
let rocket = make_rocket();
let client = Client::new(rocket).unwrap();
let origin_header = Header::from(
hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
);
let method_header = Header::from(hyper::header::AccessControlRequestMethod(
hyper::method::Method::Get,
));
let request_headers = hyper::header::AccessControlRequestHeaders(
vec![FromStr::from_str("Authorization").unwrap()],
);
let request_headers = Header::from(request_headers);
let req = client
.options("/manual")
.header(origin_header)
.header(method_header)
.header(request_headers);
let mut response = req.dispatch();
let body_str = response.body().and_then(|body| body.into_string());
assert!(response.status().class().is_success());
assert_eq!(body_str, Some("Manual CORS Preflight".to_string()));
let origin_header = response
.headers()
.get_one("Access-Control-Allow-Origin")
.expect("to exist");
assert_eq!("https://www.acme.com", origin_header);
}

View File

@ -15,14 +15,6 @@ use rocket::local::Client;
use rocket::response::Responder; use rocket::response::Responder;
use rocket_cors::*; use rocket_cors::*;
/// Using a borrowed `Cors`
#[options("/")]
fn cors_options<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
options.inner().respond_borrowed(
|guard| guard.responder(()),
)
}
/// Using a borrowed `Cors` /// Using a borrowed `Cors`
#[get("/")] #[get("/")]
fn cors<'r>(options: State<'r, Cors>) -> impl Responder<'r> { fn cors<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
@ -31,13 +23,6 @@ fn cors<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
) )
} }
#[options("/panic")]
fn panicking_route_options<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
options.inner().respond_borrowed(
|guard| guard.responder(()),
)
}
#[get("/panic")] #[get("/panic")]
fn panicking_route<'r>(options: State<'r, Cors>) -> impl Responder<'r> { fn panicking_route<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
options.inner().respond_borrowed(|_| -> () { options.inner().respond_borrowed(|_| -> () {
@ -45,6 +30,22 @@ fn panicking_route<'r>(options: State<'r, Cors>) -> impl Responder<'r> {
}) })
} }
/// Respond with an owned option instead
#[options("/owned")]
fn owned_options<'r>() -> impl Responder<'r> {
let borrow = make_different_cors_options();
borrow.respond_owned(|guard| guard.responder("Manual CORS Preflight"))
}
/// Respond with an owned option instead
#[get("/owned")]
fn owned<'r>() -> impl Responder<'r> {
let borrow = make_different_cors_options();
borrow.respond_owned(|guard| guard.responder("Hello CORS Owned"))
}
// The following routes tests that the routes can be compiled with manual CORS // The following routes tests that the routes can be compiled with manual CORS
/// `Responder` with String /// `Responder` with String
@ -68,15 +69,6 @@ fn borrow<'r>(options: State<'r, Cors>, test_state: State<'r, TestState>) -> imp
}) })
} }
/// Respond with an owned option instead
#[allow(unmounted_route)]
#[get("/")]
fn owned<'r>() -> impl Responder<'r> {
let borrow = make_cors_options();
borrow.respond_owned(|guard| guard.responder("Hello CORS"))
}
fn make_cors_options() -> Cors { fn make_cors_options() -> Cors {
let (allowed_origins, failed_origins) = AllowedOrigins::some(&["https://www.acme.com"]); let (allowed_origins, failed_origins) = AllowedOrigins::some(&["https://www.acme.com"]);
assert!(failed_origins.is_empty()); assert!(failed_origins.is_empty());
@ -90,14 +82,25 @@ fn make_cors_options() -> Cors {
} }
} }
fn make_different_cors_options() -> Cors {
let (allowed_origins, failed_origins) = AllowedOrigins::some(&["https://www.example.com"]);
assert!(failed_origins.is_empty());
Cors {
allowed_origins: allowed_origins,
allowed_methods: vec![Method::Get].into_iter().map(From::from).collect(),
allowed_headers: AllowedHeaders::some(&["Authorization", "Accept"]),
allow_credentials: true,
..Default::default()
}
}
fn rocket() -> rocket::Rocket { fn rocket() -> rocket::Rocket {
rocket::ignite() rocket::ignite()
.mount( .mount("/", routes![cors, panicking_route])
"/", .mount("/", routes![owned, owned_options])
routes![cors, cors_options, panicking_route, panicking_route_options], .mount("/", catch_all_options_routes()) // mount the catch all routes
)
.manage(make_cors_options()) .manage(make_cors_options())
.attach(make_cors_options())
} }
#[test] #[test]
@ -144,7 +147,7 @@ fn smoke_test() {
} }
#[test] #[test]
fn cors_options_check() { fn cors_options_borrowed_check() {
let client = Client::new(rocket()).unwrap(); let client = Client::new(rocket()).unwrap();
let origin_header = Header::from( let origin_header = Header::from(
@ -174,7 +177,7 @@ fn cors_options_check() {
} }
#[test] #[test]
fn cors_get_check() { fn cors_get_borrowed_check() {
let client = Client::new(rocket()).unwrap(); let client = Client::new(rocket()).unwrap();
let origin_header = Header::from( let origin_header = Header::from(
@ -370,3 +373,63 @@ fn routes_failing_checks_are_not_executed() {
.is_none() .is_none()
); );
} }
/// Manual OPTIONS routes are called
#[test]
fn cors_options_owned_check() {
let rocket = rocket();
let client = Client::new(rocket).unwrap();
let origin_header = Header::from(
hyper::header::Origin::from_str("https://www.example.com").unwrap(),
);
let method_header = Header::from(hyper::header::AccessControlRequestMethod(
hyper::method::Method::Get,
));
let request_headers = hyper::header::AccessControlRequestHeaders(
vec![FromStr::from_str("Authorization").unwrap()],
);
let request_headers = Header::from(request_headers);
let req = client
.options("/owned")
.header(origin_header)
.header(method_header)
.header(request_headers);
let mut response = req.dispatch();
let body_str = response.body().and_then(|body| body.into_string());
assert!(response.status().class().is_success());
assert_eq!(body_str, Some("Manual CORS Preflight".to_string()));
let origin_header = response
.headers()
.get_one("Access-Control-Allow-Origin")
.expect("to exist");
assert_eq!("https://www.example.com", origin_header);
}
/// Owned manual response works
#[test]
fn cors_get_owned_check() {
let client = Client::new(rocket()).unwrap();
let origin_header = Header::from(
hyper::header::Origin::from_str("https://www.example.com").unwrap(),
);
let authorization = Header::new("Authorization", "let me in");
let req = client.get("/owned").header(origin_header).header(
authorization,
);
let mut response = req.dispatch();
println!("{:?}", response);
assert!(response.status().class().is_success());
let body_str = response.body().and_then(|body| body.into_string());
assert_eq!(body_str, Some("Hello CORS Owned".to_string()));
let origin_header = response
.headers()
.get_one("Access-Control-Allow-Origin")
.expect("to exist");
assert_eq!("https://www.example.com", origin_header);
}