aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--config.dev.toml1
-rw-r--r--src/util/config.rs1
-rw-r--r--src/web/web_server.rs40
4 files changed, 41 insertions, 5 deletions
diff --git a/README.md b/README.md
index 816a1c95..fed129d5 100644
--- a/README.md
+++ b/README.md
@@ -76,7 +76,9 @@ api_bind_addr = "[::1]:3900" # the S3 API port, HTTP without TLS. Add a reverse
s3_region = "garage" # set this to anything. S3 API calls will fail if they are not made against the region set here.
[s3_web]
-web_bind_addr = "[::1]:3902"
+bind_addr = "[::1]:3902"
+root_domain = ".garage.tld"
+index = "index.html"
```
Build Garage using `cargo build --release`.
diff --git a/config.dev.toml b/config.dev.toml
index 88378e50..215bc50c 100644
--- a/config.dev.toml
+++ b/config.dev.toml
@@ -19,3 +19,4 @@ s3_region = "garage" # set this to anything. S3 API calls will fail if they a
[s3_web]
bind_addr = "[::1]:3902"
root_domain = ".garage.tld"
+index = "index.html"
diff --git a/src/util/config.rs b/src/util/config.rs
index 72f7c319..f4c841b7 100644
--- a/src/util/config.rs
+++ b/src/util/config.rs
@@ -56,6 +56,7 @@ pub struct ApiConfig {
pub struct WebConfig {
pub bind_addr: SocketAddr,
pub root_domain: String,
+ pub index: String,
}
fn default_max_concurrent_rpc_requests() -> usize {
diff --git a/src/web/web_server.rs b/src/web/web_server.rs
index cbb2aaac..16b27cef 100644
--- a/src/web/web_server.rs
+++ b/src/web/web_server.rs
@@ -1,3 +1,4 @@
+use std::borrow::Cow;
use std::net::SocketAddr;
use std::sync::Arc;
@@ -55,17 +56,18 @@ async fn handler(
// Get path
let path = req.uri().path().to_string();
- let key = percent_encoding::percent_decode_str(&path).decode_utf8()?;
+ let index = &garage.config.s3_web.index;
+ let key = path_to_key(&path, &index)?;
- // Get bucket descriptor
+ info!("Selected bucket: \"{}\", selected key: \"{}\"", bucket, key);
+
+ // Get bucket descriptor
let object = garage
.object_table
.get(&bucket.to_string(), &key.to_string())
.await?
.ok_or(Error::NotFound)?;
- info!("Selected bucket: \"{}\", selected key: \"{}\"", bucket, key);
-
Ok(Response::new(Body::from("hello world\n")))
}
@@ -121,6 +123,27 @@ fn host_to_bucket<'a>(host: &'a str, root: &str) -> &'a str {
&host[..cursor]
}
+/// Path to key
+///
+/// Convert the provided path to the internal key
+/// When a path ends with "/", we append the index name to match traditional web server behavior
+/// which is also AWS S3 behavior.
+fn path_to_key<'a>(path: &'a str, index: &str) -> Result<Cow<'a, str>, Error> {
+ let path_utf8 = percent_encoding::percent_decode_str(&path).decode_utf8()?;
+ match path_utf8.chars().last() {
+ None => Err(Error::BadRequest(format!(
+ "Path must have at least a character"
+ ))),
+ Some('/') => {
+ let mut key = String::with_capacity(path_utf8.len() + index.len());
+ key.push_str(&path_utf8);
+ key.push_str(index);
+ Ok(key.into())
+ }
+ Some(_) => Ok(path_utf8.into()),
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -170,4 +193,13 @@ mod tests {
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");
+ assert_eq!(path_to_key("/", "index.html")?, "/index.html");
+ assert!(path_to_key("", "index.html").is_err());
+ Ok(())
+ }
}