aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-02-28 11:15:53 +0100
committerAlex Auvolat <alex@adnab.me>2022-02-28 12:22:39 +0100
commit8129a9829165c8543b2ab2d32e8f20ed54ced9e5 (patch)
tree5a1eae4279761a82722dc1224b1261a72aba96da
parent54e02b4c3ba466988c50984aba9b60dc51b6cdce (diff)
downloadgarage-8129a9829165c8543b2ab2d32e8f20ed54ced9e5.tar.gz
garage-8129a9829165c8543b2ab2d32e8f20ed54ced9e5.zip
Process CORS earlier in pipeline
-rw-r--r--src/api/api_server.rs7
-rw-r--r--src/api/s3_cors.rs28
-rw-r--r--src/api/s3_router.rs9
-rw-r--r--src/web/web_server.rs2
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())),