aboutsummaryrefslogtreecommitdiff
path: root/src/web/web_server.rs
diff options
context:
space:
mode:
authorTrinity Pointard <trinity.pointard@gmail.com>2021-11-11 11:26:02 +0100
committerGitea <gitea@fake.local>2021-11-16 15:41:41 +0100
commit9c58ec28d3b23accf782c6eb005b7c3966ec6314 (patch)
treea328628c5fb6c6067dbe06ef7d5af8b5f645d15d /src/web/web_server.rs
parentcdeb5b4dbb7ed95c7ff19f5d1cccdd69b5104c45 (diff)
downloadgarage-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.rs114
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");