diff options
Diffstat (limited to 'src/api/api_server.rs')
-rw-r--r-- | src/api/api_server.rs | 102 |
1 files changed, 29 insertions, 73 deletions
diff --git a/src/api/api_server.rs b/src/api/api_server.rs index 2217be1a..ab690c30 100644 --- a/src/api/api_server.rs +++ b/src/api/api_server.rs @@ -84,9 +84,6 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon let path = req.uri().path().to_string(); let path = percent_encoding::percent_decode_str(&path).decode_utf8()?; let (api_key, content_sha256) = check_signature(&garage, &req).await?; - if path == "/" { - return handle_list_buckets(&api_key); - } let authority = req .headers() @@ -94,14 +91,20 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon .ok_or_else(|| Error::BadRequest("HOST header required".to_owned()))? .to_str()?; - // Get bucket let host = authority_to_host(authority)?; - let (bucket, key) = parse_bucket_key( - &path, - Some(&host), - garage.config.s3_api.root_domain.as_deref(), - )?; + let bucket = garage + .config + .s3_api + .root_domain + .as_ref() + .and_then(|root_domain| host_to_bucket(&host, &root_domain)); + + if path == "/" && bucket.is_none() { + return handle_list_buckets(&api_key); + } + + let (bucket, key) = parse_bucket_key(&path, bucket)?; let allowed = match req.method() { &Method::HEAD | &Method::GET => api_key.allow_read(bucket), _ => api_key.allow_write(bucket), @@ -152,7 +155,7 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon let copy_source = req.headers().get("x-amz-copy-source").unwrap().to_str()?; let copy_source = percent_encoding::percent_decode_str(copy_source).decode_utf8()?; - let (source_bucket, source_key) = parse_bucket_key(©_source, None, None)?; + let (source_bucket, source_key) = parse_bucket_key(©_source, None)?; if !api_key.allow_read(source_bucket) { return Err(Error::Forbidden(format!( "Reading from bucket {} not allowed for this key", @@ -260,24 +263,21 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon } } -/// Extract the bucket name and the key name from an HTTP path +/// Extract the bucket name and the key name from an HTTP path and possibly Host header /// /// S3 internally manages only buckets and keys. This function splits /// an HTTP path to get the corresponding bucket name and key. fn parse_bucket_key<'a>( path: &'a str, - host: Option<&'a str>, - root: Option<&str>, + host_bucket: Option<&'a str>, ) -> Result<(&'a str, Option<&'a str>), Error> { let path = path.trim_start_matches('/'); - if host.and(root).is_some() { - if let Some(bucket) = host_to_bucket(host.unwrap(), root.unwrap()) { - if !path.is_empty() { - return Ok((bucket, Some(path))); - } else { - return Ok((bucket, None)); - } + if let Some(bucket) = host_bucket { + if !path.is_empty() { + return Ok((bucket, Some(path))); + } else { + return Ok((bucket, None)); } } @@ -304,7 +304,7 @@ mod tests { #[test] fn parse_bucket_containing_a_key() -> Result<(), Error> { - let (bucket, key) = parse_bucket_key("/my_bucket/a/super/file.jpg", None, None)?; + let (bucket, key) = parse_bucket_key("/my_bucket/a/super/file.jpg", None)?; assert_eq!(bucket, "my_bucket"); assert_eq!(key.expect("key must be set"), "a/super/file.jpg"); Ok(()) @@ -312,10 +312,10 @@ mod tests { #[test] fn parse_bucket_containing_no_key() -> Result<(), Error> { - let (bucket, key) = parse_bucket_key("/my_bucket/", None, None)?; + let (bucket, key) = parse_bucket_key("/my_bucket/", None)?; assert_eq!(bucket, "my_bucket"); assert!(key.is_none()); - let (bucket, key) = parse_bucket_key("/my_bucket", None, None)?; + let (bucket, key) = parse_bucket_key("/my_bucket", None)?; assert_eq!(bucket, "my_bucket"); assert!(key.is_none()); Ok(()) @@ -323,74 +323,30 @@ mod tests { #[test] fn parse_bucket_containing_no_bucket() { - let parsed = parse_bucket_key("", None, None); + let parsed = parse_bucket_key("", None); assert!(parsed.is_err()); - let parsed = parse_bucket_key("/", None, None); + let parsed = parse_bucket_key("/", None); assert!(parsed.is_err()); - let parsed = parse_bucket_key("////", None, None); + let parsed = parse_bucket_key("////", None); assert!(parsed.is_err()); } #[test] fn parse_bucket_with_vhost_and_key() -> Result<(), Error> { - let (bucket, key) = parse_bucket_key( - "/a/super/file.jpg", - Some("my-bucket.garage.tld"), - Some("garage.tld"), - )?; + let (bucket, key) = parse_bucket_key("/a/super/file.jpg", Some("my-bucket"))?; assert_eq!(bucket, "my-bucket"); assert_eq!(key.expect("key must be set"), "a/super/file.jpg"); - - let (bucket, key) = parse_bucket_key( - "/my_bucket/a/super/file.jpg", - Some("not-garage.tld"), - Some("garage.tld"), - )?; - assert_eq!(bucket, "my_bucket"); - assert_eq!(key.expect("key must be set"), "a/super/file.jpg"); Ok(()) } #[test] fn parse_bucket_with_vhost_no_key() -> Result<(), Error> { - let (bucket, key) = parse_bucket_key("", Some("my-bucket.garage.tld"), Some("garage.tld"))?; + let (bucket, key) = parse_bucket_key("", Some("my-bucket"))?; assert_eq!(bucket, "my-bucket"); assert!(key.is_none()); - let (bucket, key) = - parse_bucket_key("/", Some("my-bucket.garage.tld"), Some("garage.tld"))?; + let (bucket, key) = parse_bucket_key("/", Some("my-bucket"))?; assert_eq!(bucket, "my-bucket"); assert!(key.is_none()); Ok(()) } - - #[test] - fn parse_bucket_missmatch_vhost() { - let test_vec = [ - "/my_bucket/a/super/file.jpg", - "/my_bucket/", - "/my_bucket", - "", - "/", - "////", - ]; - let eq = |l, r| match (l, r) { - (Ok(l), Ok(r)) => l == r, - (Err(_), Err(_)) => true, - _ => false, - }; - for test in test_vec { - assert!(eq( - parse_bucket_key(test, None, None), - parse_bucket_key(test, Some("bucket.garage.tld"), None) - )); - assert!(eq( - parse_bucket_key(test, None, None), - parse_bucket_key(test, None, Some("garage.tld")) - )); - assert!(eq( - parse_bucket_key(test, None, None), - parse_bucket_key(test, Some("not-garage.tld"), Some("garage.tld")) - )); - } - } } |