diff options
author | Alex Auvolat <alex@adnab.me> | 2022-02-28 11:15:53 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2022-02-28 12:22:39 +0100 |
commit | 8129a9829165c8543b2ab2d32e8f20ed54ced9e5 (patch) | |
tree | 5a1eae4279761a82722dc1224b1261a72aba96da | |
parent | 54e02b4c3ba466988c50984aba9b60dc51b6cdce (diff) | |
download | garage-8129a9829165c8543b2ab2d32e8f20ed54ced9e5.tar.gz garage-8129a9829165c8543b2ab2d32e8f20ed54ced9e5.zip |
Process CORS earlier in pipeline
-rw-r--r-- | src/api/api_server.rs | 7 | ||||
-rw-r--r-- | src/api/s3_cors.rs | 28 | ||||
-rw-r--r-- | src/api/s3_router.rs | 9 | ||||
-rw-r--r-- | 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<Garage>, req: Request<Body>) -> Result<Respon let (endpoint, bucket_name) = Endpoint::from_request(&req, bucket_name.map(ToOwned::to_owned))?; 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; } + 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 = 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 { - Endpoint::Options => 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<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 .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<Garage>, req: &Request<Body>) -> Result<Response ); 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::GET => handle_get(garage.clone(), req, bucket_id, &key, None).await, _ => Err(ApiError::BadRequest("HTTP method not supported".into())), |