diff options
author | Alex Auvolat <alex@adnab.me> | 2022-01-07 16:23:04 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2022-01-24 11:58:00 +0100 |
commit | ea7fb901ebc316bba53d248a2f8bd7a3455f5791 (patch) | |
tree | 31da306e4c0b866bb9cc4241d6b01eac6c74bd49 /src/api/api_server.rs | |
parent | 820924534ab3eb0b2544a594881591559e7c45a5 (diff) | |
download | garage-ea7fb901ebc316bba53d248a2f8bd7a3455f5791.tar.gz garage-ea7fb901ebc316bba53d248a2f8bd7a3455f5791.zip |
Implement {Put,Get,Delete}BucketCors and CORS in general
- OPTIONS request against API endpoint
- Returning corresponding CORS headers on API calls
- Returning corresponding CORS headers on website GET's
Diffstat (limited to 'src/api/api_server.rs')
-rw-r--r-- | src/api/api_server.rs | 42 |
1 files changed, 37 insertions, 5 deletions
diff --git a/src/api/api_server.rs b/src/api/api_server.rs index 11daae13..c28e8832 100644 --- a/src/api/api_server.rs +++ b/src/api/api_server.rs @@ -5,7 +5,7 @@ use futures::future::Future; use hyper::header; use hyper::server::conn::AddrStream; use hyper::service::{make_service_fn, service_fn}; -use hyper::{Body, Request, Response, Server}; +use hyper::{Body, Method, Request, Response, Server}; use garage_util::data::*; use garage_util::error::Error as GarageError; @@ -13,12 +13,15 @@ use garage_util::error::Error as GarageError; use garage_model::garage::Garage; use garage_model::key_table::Key; +use garage_table::util::*; + use crate::error::*; use crate::signature::payload::check_payload_signature; use crate::helpers::*; use crate::s3_bucket::*; use crate::s3_copy::*; +use crate::s3_cors::*; use crate::s3_delete::*; use crate::s3_get::*; use crate::s3_list::*; @@ -102,17 +105,17 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon let host = authority_to_host(authority)?; - let bucket = garage + let bucket_name = garage .config .s3_api .root_domain .as_ref() .and_then(|root_domain| host_to_bucket(&host, root_domain)); - let (endpoint, bucket) = Endpoint::from_request(&req, bucket.map(ToOwned::to_owned))?; + let (endpoint, bucket_name) = Endpoint::from_request(&req, bucket_name.map(ToOwned::to_owned))?; debug!("Endpoint: {:?}", endpoint); - let bucket_name = match bucket { + let bucket_name = match bucket_name { None => return handle_request_without_bucket(garage, req, api_key, endpoint).await, Some(bucket) => bucket.to_string(), }; @@ -123,6 +126,12 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon } let bucket_id = resolve_bucket(&garage, &bucket_name, &api_key).await?; + let bucket = garage + .bucket_table + .get(&EmptyKey, &bucket_id) + .await? + .filter(|b| !b.state.is_deleted()) + .ok_or(Error::NoSuchBucket)?; let allowed = match endpoint.authorization_type() { Authorization::Read => api_key.allow_read(&bucket_id), @@ -137,7 +146,17 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon )); } - match endpoint { + // Look up what CORS rule might apply to response. + // Requests for methods different than GET, HEAD or POST + // are always preflighted, i.e. the browser should make + // an OPTIONS call before to check it is allowed + let matching_cors_rule = match *req.method() { + Method::GET | Method::HEAD | Method::POST => find_matching_cors_rule(&bucket, &req)?, + _ => None, + }; + + let resp = match endpoint { + Endpoint::Options => handle_options(garage, &req, bucket_id).await, Endpoint::HeadObject { key, .. } => handle_head(garage, &req, bucket_id, &key).await, Endpoint::GetObject { key, .. } => handle_get(garage, &req, bucket_id, &key).await, Endpoint::UploadPart { @@ -320,8 +339,21 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon handle_put_website(garage, bucket_id, req, content_sha256).await } Endpoint::DeleteBucketWebsite {} => handle_delete_website(garage, bucket_id).await, + Endpoint::GetBucketCors {} => handle_get_cors(garage, bucket_id).await, + Endpoint::PutBucketCors {} => handle_put_cors(garage, bucket_id, req, content_sha256).await, + Endpoint::DeleteBucketCors {} => handle_delete_cors(garage, bucket_id).await, endpoint => Err(Error::NotImplemented(endpoint.name().to_owned())), + }; + + // If request was a success and we have a CORS rule that applies to it, + // add the corresponding CORS headers to the response + let mut resp_ok = resp?; + if let Some(rule) = matching_cors_rule { + add_cors_headers(&mut resp_ok, rule) + .ok_or_internal_error("Invalid bucket CORS configuration")?; } + + Ok(resp_ok) } async fn handle_request_without_bucket( |