mirror of
https://gitlab.com/msrd0/gotham-restful.git
synced 2025-02-23 04:52:28 +00:00
cors for non-preflight requests
This commit is contained in:
parent
40c90e6b4a
commit
748bf65d3e
4 changed files with 169 additions and 1 deletions
|
@ -47,6 +47,7 @@ trybuild = "1.0.26"
|
||||||
[features]
|
[features]
|
||||||
default = ["errorlog"]
|
default = ["errorlog"]
|
||||||
auth = ["gotham_restful_derive/auth", "base64", "cookie", "jsonwebtoken"]
|
auth = ["gotham_restful_derive/auth", "base64", "cookie", "jsonwebtoken"]
|
||||||
|
cors = []
|
||||||
errorlog = []
|
errorlog = []
|
||||||
database = ["gotham_restful_derive/database", "gotham_middleware_diesel"]
|
database = ["gotham_restful_derive/database", "gotham_middleware_diesel"]
|
||||||
openapi = ["gotham_restful_derive/openapi", "indexmap", "openapiv3"]
|
openapi = ["gotham_restful_derive/openapi", "indexmap", "openapiv3"]
|
||||||
|
|
152
src/cors.rs
Normal file
152
src/cors.rs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
use gotham::{
|
||||||
|
handler::HandlerFuture,
|
||||||
|
hyper::{
|
||||||
|
header::{ACCESS_CONTROL_ALLOW_ORIGIN, ORIGIN, HeaderMap, HeaderValue},
|
||||||
|
Body, Method, Response
|
||||||
|
},
|
||||||
|
middleware::Middleware,
|
||||||
|
state::{FromState, State},
|
||||||
|
};
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Specify the allowed origins of the request. It is up to the browser to check the validity of the
|
||||||
|
origin. This, when sent to the browser, will indicate whether or not the request's origin was
|
||||||
|
allowed to make the request.
|
||||||
|
*/
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Origin
|
||||||
|
{
|
||||||
|
/// Do not send any `Access-Control-Allow-Origin` headers.
|
||||||
|
None,
|
||||||
|
/// Send `Access-Control-Allow-Origin: *`. Note that browser will not send credentials.
|
||||||
|
Star,
|
||||||
|
/// Set the `Access-Control-Allow-Origin` header to a single origin.
|
||||||
|
Single(String),
|
||||||
|
/// Copy the `Origin` header into the `Access-Control-Allow-Origin` header.
|
||||||
|
Copy
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Origin
|
||||||
|
{
|
||||||
|
fn default() -> Self
|
||||||
|
{
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Origin
|
||||||
|
{
|
||||||
|
/// Get the header value for the `Access-Control-Allow-Origin` header.
|
||||||
|
fn header_value(&self, state : &State) -> Option<HeaderValue>
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Self::None => None,
|
||||||
|
Self::Star => Some("*".parse().unwrap()),
|
||||||
|
Self::Single(origin) => Some(origin.parse().unwrap()),
|
||||||
|
Self::Copy => {
|
||||||
|
let headers = HeaderMap::borrow_from(state);
|
||||||
|
headers.get(ORIGIN).map(Clone::clone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This is the configuration that the CORS handler will follow. Its default configuration is basically
|
||||||
|
not to touch any responses, resulting in the browser's default behaviour.
|
||||||
|
|
||||||
|
To change settings, you need to put this type into gotham's [`State`]:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
# use gotham::{router::builder::*, pipeline::{new_pipeline, single::single_pipeline}, state::State};
|
||||||
|
# use gotham_restful::*;
|
||||||
|
fn main() {
|
||||||
|
let cors = CorsConfig {
|
||||||
|
origin: Origin::Star
|
||||||
|
};
|
||||||
|
let (chain, pipelines) = single_pipeline(new_pipeline().add(cors).build());
|
||||||
|
gotham::start("127.0.0.1:8080", build_router(chain, pipelines, |route| {
|
||||||
|
// your routing logic
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This easy approach allows you to have one global cors configuration. If you prefer to have separate
|
||||||
|
configurations for different scopes, you need to register the middleware inside your routing logic:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
# use gotham::{router::builder::*, pipeline::*, pipeline::set::*, state::State};
|
||||||
|
# use gotham_restful::*;
|
||||||
|
fn main() {
|
||||||
|
let pipelines = new_pipeline_set();
|
||||||
|
|
||||||
|
let cors_a = CorsConfig {
|
||||||
|
origin: Origin::Star
|
||||||
|
};
|
||||||
|
let (pipelines, chain_a) = pipelines.add(
|
||||||
|
new_pipeline().add(cors_a).build()
|
||||||
|
);
|
||||||
|
|
||||||
|
let cors_b = CorsConfig {
|
||||||
|
origin: Origin::Copy
|
||||||
|
};
|
||||||
|
let (pipelines, chain_b) = pipelines.add(
|
||||||
|
new_pipeline().add(cors_b).build()
|
||||||
|
);
|
||||||
|
|
||||||
|
let pipeline_set = finalize_pipeline_set(pipelines);
|
||||||
|
gotham::start("127.0.0.1:8080", build_router((), pipeline_set, |route| {
|
||||||
|
// routing without any cors config
|
||||||
|
route.with_pipeline_chain((chain_a, ()), |route| {
|
||||||
|
// routing with cors config a
|
||||||
|
});
|
||||||
|
route.with_pipeline_chain((chain_b, ()), |route| {
|
||||||
|
// routing with cors config b
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[`State`]: ../gotham/state/struct.State.html
|
||||||
|
*/
|
||||||
|
#[derive(Clone, Debug, Default, NewMiddleware, StateData)]
|
||||||
|
pub struct CorsConfig
|
||||||
|
{
|
||||||
|
pub origin : Origin
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Middleware for CorsConfig
|
||||||
|
{
|
||||||
|
fn call<Chain>(self, mut state : State, chain : Chain) -> Pin<Box<HandlerFuture>>
|
||||||
|
where
|
||||||
|
Chain : FnOnce(State) -> Pin<Box<HandlerFuture>>
|
||||||
|
{
|
||||||
|
state.put(self);
|
||||||
|
chain(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Handle CORS for a non-preflight request. This means manipulating the `res` HTTP headers so that
|
||||||
|
the response is aligned with the `state`'s [`CorsConfig`].
|
||||||
|
|
||||||
|
If you are using the [`Resource`] type (which is the recommended way), you'll never have to call
|
||||||
|
this method. However, if you are writing your own handler method, you might want to call this
|
||||||
|
after your request to add the required CORS headers.
|
||||||
|
|
||||||
|
For further information on CORS, read https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS.
|
||||||
|
|
||||||
|
[`CorsConfig`]: ./struct.CorsConfig.html
|
||||||
|
*/
|
||||||
|
pub fn handle_cors(state : &State, res : &mut Response<Body>)
|
||||||
|
{
|
||||||
|
let method = Method::borrow_from(state);
|
||||||
|
let config = CorsConfig::try_borrow_from(state);
|
||||||
|
|
||||||
|
// non-preflight requests require nothing other than the Access-Control-Allow-Origin header
|
||||||
|
if let Some(header) = config.and_then(|cfg| cfg.origin.header_value(state))
|
||||||
|
{
|
||||||
|
res.headers_mut().insert(ACCESS_CONTROL_ALLOW_ORIGIN, header);
|
||||||
|
}
|
||||||
|
}
|
|
@ -285,6 +285,15 @@ pub use auth::{
|
||||||
StaticAuthHandler
|
StaticAuthHandler
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "cors")]
|
||||||
|
mod cors;
|
||||||
|
#[cfg(feature = "cors")]
|
||||||
|
pub use cors::{
|
||||||
|
handle_cors,
|
||||||
|
CorsConfig,
|
||||||
|
Origin
|
||||||
|
};
|
||||||
|
|
||||||
pub mod matcher;
|
pub mod matcher;
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
|
|
@ -100,10 +100,16 @@ fn response_from(res : Response, state : &State) -> gotham::hyper::Response<Body
|
||||||
{
|
{
|
||||||
r.headers_mut().insert(CONTENT_TYPE, mime.as_ref().parse().unwrap());
|
r.headers_mut().insert(CONTENT_TYPE, mime.as_ref().parse().unwrap());
|
||||||
}
|
}
|
||||||
if Method::borrow_from(state) != Method::HEAD
|
|
||||||
|
let method = Method::borrow_from(state);
|
||||||
|
if method != Method::HEAD
|
||||||
{
|
{
|
||||||
*r.body_mut() = res.body;
|
*r.body_mut() = res.body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "cors")]
|
||||||
|
crate::cors::handle_cors(state, &mut r);
|
||||||
|
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue