From 8129a9829165c8543b2ab2d32e8f20ed54ced9e5 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Mon, 28 Feb 2022 11:15:53 +0100 Subject: [PATCH] Process CORS earlier in pipeline --- src/api/api_server.rs | 7 +++++-- src/api/s3_cors.rs | 28 +++++++++++++++++++++++++++- src/api/s3_router.rs | 9 ++++++--- src/web/web_server.rs | 2 +- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/api/api_server.rs b/src/api/api_server.rs index 77587de8..5ac78bf4 100644 --- a/src/api/api_server.rs +++ b/src/api/api_server.rs @@ -111,9 +111,13 @@ async fn handler_inner(garage: Arc, req: Request) -> Result, req: Request) -> Result handle_options(&req, &bucket).await, Endpoint::HeadObject { key, part_number, .. } => handle_head(garage, &req, bucket_id, &key, part_number).await, diff --git a/src/api/s3_cors.rs b/src/api/s3_cors.rs index cde66079..7dc48d8e 100644 --- a/src/api/s3_cors.rs +++ b/src/api/s3_cors.rs @@ -100,7 +100,33 @@ pub async fn handle_put_cors( .body(Body::empty())?) } -pub async fn handle_options(req: &Request, bucket: &Bucket) -> Result, Error> { +pub async fn handle_options( + garage: Arc, + req: &Request, + bucket_name: Option, +) -> Result, 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 .headers() .get("Origin") diff --git a/src/api/s3_router.rs b/src/api/s3_router.rs index 2a68d79e..95a7eceb 100644 --- a/src/api/s3_router.rs +++ b/src/api/s3_router.rs @@ -414,8 +414,7 @@ pub enum Endpoint { // 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 // of being query parameters of the URL, so authenticating it is a bit different. - PostObject { - }, + PostObject, }} impl Endpoint { @@ -430,7 +429,11 @@ impl Endpoint { let path = uri.path().trim_start_matches('/'); let query = uri.query(); if bucket.is_none() && path.is_empty() { - return Ok((Self::ListBuckets, None)); + if *req.method() == Method::OPTIONS { + return Ok((Self::Options, None)); + } else { + return Ok((Self::ListBuckets, None)); + } } let (bucket, key) = if let Some(bucket) = bucket { diff --git a/src/web/web_server.rs b/src/web/web_server.rs index 6c7d7c35..15935cba 100644 --- a/src/web/web_server.rs +++ b/src/web/web_server.rs @@ -133,7 +133,7 @@ async fn serve_file(garage: Arc, req: &Request) -> Result 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::GET => handle_get(garage.clone(), req, bucket_id, &key, None).await, _ => Err(ApiError::BadRequest("HTTP method not supported".into())),