Process CORS earlier in pipeline

This commit is contained in:
Alex 2022-02-28 11:15:53 +01:00
parent 54e02b4c3b
commit 8129a98291
Signed by untrusted user: lx
GPG key ID: 0E496D15096376BE
4 changed files with 39 additions and 7 deletions

View file

@ -111,9 +111,13 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
let (endpoint, bucket_name) = Endpoint::from_request(&req, bucket_name.map(ToOwned::to_owned))?; let (endpoint, bucket_name) = Endpoint::from_request(&req, bucket_name.map(ToOwned::to_owned))?;
debug!("Endpoint: {:?}", endpoint); debug!("Endpoint: {:?}", endpoint);
if let Endpoint::PostObject {} = endpoint { // Some endpoints are processed early, before we even check for an API key
if let Endpoint::PostObject = endpoint {
return handle_post_object(garage, req, bucket_name.unwrap()).await; return handle_post_object(garage, req, bucket_name.unwrap()).await;
} }
if let Endpoint::Options = endpoint {
return handle_options(garage, &req, bucket_name).await;
}
let (api_key, content_sha256) = check_payload_signature(&garage, &req).await?; let (api_key, content_sha256) = check_payload_signature(&garage, &req).await?;
let api_key = api_key.ok_or_else(|| { let api_key = api_key.ok_or_else(|| {
@ -161,7 +165,6 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
}; };
let resp = match endpoint { let resp = match endpoint {
Endpoint::Options => handle_options(&req, &bucket).await,
Endpoint::HeadObject { Endpoint::HeadObject {
key, part_number, .. key, part_number, ..
} => handle_head(garage, &req, bucket_id, &key, part_number).await, } => handle_head(garage, &req, bucket_id, &key, part_number).await,

View file

@ -100,7 +100,33 @@ pub async fn handle_put_cors(
.body(Body::empty())?) .body(Body::empty())?)
} }
pub async fn handle_options(req: &Request<Body>, bucket: &Bucket) -> Result<Response<Body>, Error> { pub async fn handle_options(
garage: Arc<Garage>,
req: &Request<Body>,
bucket_name: Option<String>,
) -> Result<Response<Body>, Error> {
let bucket = if let Some(bn) = bucket_name {
let helper = garage.bucket_helper();
let bucket_id = helper
.resolve_global_bucket_name(&bn)
.await?
.ok_or(Error::NoSuchBucket)?;
garage
.bucket_table
.get(&EmptyKey, &bucket_id)
.await?
.filter(|b| !b.state.is_deleted())
.ok_or(Error::NoSuchBucket)?
} else {
// The only supported API call that doesn't use a bucket name is ListBuckets,
// which we want to allow in all cases
return Ok(Response::builder()
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.header(ACCESS_CONTROL_ALLOW_METHODS, "GET")
.status(StatusCode::OK)
.body(Body::empty())?);
};
let origin = req let origin = req
.headers() .headers()
.get("Origin") .get("Origin")

View file

@ -414,8 +414,7 @@ pub enum Endpoint {
// It's intended to be used with HTML forms, using a multipart/form-data body. // It's intended to be used with HTML forms, using a multipart/form-data body.
// It works a lot like presigned requests, but everything is in the form instead // It works a lot like presigned requests, but everything is in the form instead
// of being query parameters of the URL, so authenticating it is a bit different. // of being query parameters of the URL, so authenticating it is a bit different.
PostObject { PostObject,
},
}} }}
impl Endpoint { impl Endpoint {
@ -430,8 +429,12 @@ impl Endpoint {
let path = uri.path().trim_start_matches('/'); let path = uri.path().trim_start_matches('/');
let query = uri.query(); let query = uri.query();
if bucket.is_none() && path.is_empty() { if bucket.is_none() && path.is_empty() {
if *req.method() == Method::OPTIONS {
return Ok((Self::Options, None));
} else {
return Ok((Self::ListBuckets, None)); return Ok((Self::ListBuckets, None));
} }
}
let (bucket, key) = if let Some(bucket) = bucket { let (bucket, key) = if let Some(bucket) = bucket {
(bucket, path) (bucket, path)

View file

@ -133,7 +133,7 @@ async fn serve_file(garage: Arc<Garage>, req: &Request<Body>) -> Result<Response
); );
let ret_doc = match *req.method() { let ret_doc = match *req.method() {
Method::OPTIONS => handle_options(req, &bucket).await, Method::OPTIONS => handle_options(garage.clone(), req, Some(bucket_name.to_string())).await,
Method::HEAD => handle_head(garage.clone(), req, bucket_id, &key, None).await, Method::HEAD => handle_head(garage.clone(), req, bucket_id, &key, None).await,
Method::GET => handle_get(garage.clone(), req, bucket_id, &key, None).await, Method::GET => handle_get(garage.clone(), req, bucket_id, &key, None).await,
_ => Err(ApiError::BadRequest("HTTP method not supported".into())), _ => Err(ApiError::BadRequest("HTTP method not supported".into())),