diff options
author | Trinity Pointard <trinity.pointard@gmail.com> | 2021-11-11 11:26:02 +0100 |
---|---|---|
committer | Gitea <gitea@fake.local> | 2021-11-16 15:41:41 +0100 |
commit | 9c58ec28d3b23accf782c6eb005b7c3966ec6314 (patch) | |
tree | a328628c5fb6c6067dbe06ef7d5af8b5f645d15d /src/web/web_server.rs | |
parent | cdeb5b4dbb7ed95c7ff19f5d1cccdd69b5104c45 (diff) | |
download | garage-9c58ec28d3b23accf782c6eb005b7c3966ec6314.tar.gz garage-9c58ec28d3b23accf782c6eb005b7c3966ec6314.zip |
add support for vhost-style s3 bucket
Diffstat (limited to 'src/web/web_server.rs')
-rw-r--r-- | src/web/web_server.rs | 114 |
1 files changed, 3 insertions, 111 deletions
diff --git a/src/web/web_server.rs b/src/web/web_server.rs index bff9e71c..e9c5039d 100644 --- a/src/web/web_server.rs +++ b/src/web/web_server.rs @@ -9,9 +9,8 @@ use hyper::{ Body, Method, Request, Response, Server, }; -use idna::domain_to_unicode; - use crate::error::*; +use garage_api::helpers::{authority_to_host, host_to_bucket}; use garage_api::s3_get::{handle_get, handle_head}; use garage_model::bucket_table::*; use garage_model::garage::Garage; @@ -75,9 +74,9 @@ async fn serve_file(garage: Arc<Garage>, req: Request<Body>) -> Result<Response< .to_str()?; // Get bucket - let (host, _) = domain_to_unicode(authority_to_host(authority)?); + let host = authority_to_host(authority)?; let root = &garage.config.s3_web.root_domain; - let bucket = host_to_bucket(&host, root); + let bucket = host_to_bucket(&host, root).unwrap_or(&host); // Check bucket is exposed as a website let bucket_desc = garage @@ -108,65 +107,6 @@ async fn serve_file(garage: Arc<Garage>, req: Request<Body>) -> Result<Response< Ok(res) } -/// Extract host from the authority section given by the HTTP host header -/// -/// The HTTP host contains both a host and a port. -/// Extracting the port is more complex than just finding the colon (:) symbol due to IPv6 -/// We do not use the collect pattern as there is no way in std rust to collect over a stack allocated value -/// check here: https://docs.rs/collect_slice/1.2.0/collect_slice/ -fn authority_to_host(authority: &str) -> Result<&str, Error> { - let mut iter = authority.chars().enumerate(); - let (_, first_char) = iter - .next() - .ok_or_else(|| Error::BadRequest("Authority is empty".to_string()))?; - - let split = match first_char { - '[' => { - let mut iter = iter.skip_while(|(_, c)| c != &']'); - match iter.next() { - Some((_, ']')) => iter.next(), - _ => { - return Err(Error::BadRequest(format!( - "Authority {} has an illegal format", - authority - ))) - } - } - } - _ => iter.find(|(_, c)| *c == ':'), - }; - - match split { - Some((i, ':')) => Ok(&authority[..i]), - None => Ok(authority), - Some((_, _)) => Err(Error::BadRequest(format!( - "Authority {} has an illegal format", - authority - ))), - } -} - -/// Host to bucket -/// -/// Convert a host, like "bucket.garage-site.tld" or "john.doe.com" -/// to the corresponding bucket, resp. "bucket" and "john.doe.com" -/// considering that ".garage-site.tld" is the "root domain". -/// This behavior has been chosen to follow AWS S3 semantic. -fn host_to_bucket<'a>(host: &'a str, root: &str) -> &'a str { - if root.len() >= host.len() || !host.ends_with(root) { - return host; - } - - let len_diff = host.len() - root.len(); - let missing_starting_dot = !root.starts_with('.'); - let cursor = if missing_starting_dot { - len_diff - 1 - } else { - len_diff - }; - &host[..cursor] -} - /// Path to key /// /// Convert the provided path to the internal key @@ -201,54 +141,6 @@ mod tests { use super::*; #[test] - fn authority_to_host_with_port() -> Result<(), Error> { - let domain = authority_to_host("[::1]:3902")?; - assert_eq!(domain, "[::1]"); - let domain2 = authority_to_host("garage.tld:65200")?; - assert_eq!(domain2, "garage.tld"); - let domain3 = authority_to_host("127.0.0.1:80")?; - assert_eq!(domain3, "127.0.0.1"); - Ok(()) - } - - #[test] - fn authority_to_host_without_port() -> Result<(), Error> { - let domain = authority_to_host("[::1]")?; - assert_eq!(domain, "[::1]"); - let domain2 = authority_to_host("garage.tld")?; - assert_eq!(domain2, "garage.tld"); - let domain3 = authority_to_host("127.0.0.1")?; - assert_eq!(domain3, "127.0.0.1"); - assert!(authority_to_host("[").is_err()); - assert!(authority_to_host("[hello").is_err()); - Ok(()) - } - - #[test] - fn host_to_bucket_test() { - assert_eq!( - host_to_bucket("john.doe.garage.tld", ".garage.tld"), - "john.doe" - ); - - assert_eq!( - host_to_bucket("john.doe.garage.tld", "garage.tld"), - "john.doe" - ); - - assert_eq!(host_to_bucket("john.doe.com", "garage.tld"), "john.doe.com"); - - assert_eq!( - host_to_bucket("john.doe.com", ".garage.tld"), - "john.doe.com" - ); - - assert_eq!(host_to_bucket("garage.tld", "garage.tld"), "garage.tld"); - - assert_eq!(host_to_bucket("garage.tld", ".garage.tld"), "garage.tld"); - } - - #[test] fn path_to_key_test() -> Result<(), Error> { assert_eq!(path_to_key("/file%20.jpg", "index.html")?, "file .jpg"); assert_eq!(path_to_key("/%20t/", "index.html")?, " t/index.html"); |