From 188ebaa30b71034fbbf57d2c9e5354697c024524 Mon Sep 17 00:00:00 2001
From: Brian Picciano
Date: Wed, 19 Jul 2023 16:18:13 +0200
Subject: [PATCH] Add add/remove_path_prefix fields to domain settings
---
src/domain/manager.rs | 7 ++-
src/domain/settings.rs | 105 +++++++++++++++++++++++++++++++
src/domain/store.rs | 4 ++
src/origin/proxy.rs | 6 +-
src/service/http.rs | 10 ++-
src/service/http/tpl/domain.html | 25 ++++++++
src/service/util.rs | 21 ++++++-
7 files changed, 166 insertions(+), 12 deletions(-)
diff --git a/src/domain/manager.rs b/src/domain/manager.rs
index c8e499c..e2ce452 100644
--- a/src/domain/manager.rs
+++ b/src/domain/manager.rs
@@ -252,7 +252,12 @@ impl Manager for ManagerImpl {
return Err(unexpected::Error::from("origin is proxy, can't serve file").into());
}
- let f = self.origin_store.get_file(&settings.origin_descr, path)?;
+ let path = settings.process_path(path);
+
+ let f = self
+ .origin_store
+ .get_file(&settings.origin_descr, path.as_ref())?;
+
Ok(f)
}
diff --git a/src/domain/settings.rs b/src/domain/settings.rs
index 791d95d..3112b11 100644
--- a/src/domain/settings.rs
+++ b/src/domain/settings.rs
@@ -1,6 +1,8 @@
use crate::error::unexpected::{self, Mappable};
use crate::origin;
+use std::borrow;
+
use hex::ToHex;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
@@ -11,6 +13,9 @@ use sha2::{Digest, Sha256};
pub struct Settings {
#[serde(flatten)]
pub origin_descr: origin::Descr,
+
+ pub remove_path_prefix: Option,
+ pub add_path_prefix: Option,
}
impl Settings {
@@ -19,4 +24,104 @@ impl Settings {
serde_json::to_writer(&mut h, self).or_unexpected()?;
Ok(h.finalize().encode_hex::())
}
+
+ fn remove_path_prefix<'path, 'prefix>(
+ path: borrow::Cow<'path, str>,
+ prefix: &'prefix str,
+ ) -> borrow::Cow<'path, str> {
+ if prefix.len() == 0 {
+ return path;
+ }
+
+ let mut prefix = prefix.trim_end_matches('/');
+ prefix = prefix.trim_start_matches('/');
+
+ let mut stripped_path = path.trim_start_matches('/');
+
+ if !stripped_path.starts_with(prefix) {
+ return path;
+ }
+
+ stripped_path = stripped_path.strip_prefix(prefix).unwrap();
+ if stripped_path.len() == 0 {
+ return borrow::Cow::Borrowed("/");
+ }
+
+ borrow::Cow::Owned(stripped_path.to_string())
+ }
+
+ fn add_path_prefix<'path, 'prefix>(
+ path: borrow::Cow<'path, str>,
+ prefix: &'prefix str,
+ ) -> borrow::Cow<'path, str> {
+ if prefix.len() == 0 {
+ return path;
+ }
+
+ let mut prefix = prefix.trim_end_matches('/');
+ prefix = prefix.trim_start_matches('/');
+
+ let mut prefixed_path = String::with_capacity(1 + prefix.len() + path.len());
+
+ prefixed_path.push('/');
+ prefixed_path.push_str(prefix);
+ prefixed_path.push_str(path.as_ref());
+
+ borrow::Cow::Owned(prefixed_path)
+ }
+
+ pub fn process_path<'a>(&self, path: &'a str) -> borrow::Cow<'a, str> {
+ let mut path = borrow::Cow::Borrowed(path);
+
+ if let Some(ref prefix) = self.remove_path_prefix {
+ path = Self::remove_path_prefix(path, prefix);
+ }
+
+ if let Some(ref prefix) = self.add_path_prefix {
+ path = Self::add_path_prefix(path, prefix);
+ }
+
+ path
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::borrow;
+
+ #[test]
+ fn remove_path_prefix() {
+ let assert_remove = |want: &str, path: &str, prefix: &str| {
+ assert_eq!(
+ want,
+ Settings::remove_path_prefix(borrow::Cow::Borrowed(path), prefix).as_ref(),
+ )
+ };
+
+ assert_remove("/bar", "/foo/bar", "/foo");
+ assert_remove("/foo/bar", "/foo/bar", "/baz");
+ assert_remove("/bar", "/foo/bar", "/foo/");
+ assert_remove("/bar", "/foo/bar", "/foo///");
+ assert_remove("/", "/", "/");
+ assert_remove("/", "/foo/bar/", "/foo/bar");
+ assert_remove("/", "/foo/bar", "/foo/bar");
+ assert_remove("/", "/foo/bar", "/foo/bar///");
+ assert_remove("/bar", "/bar", "");
+ }
+
+ #[test]
+ fn add_path_prefix() {
+ let assert_add = |want: &str, path: &str, prefix: &str| {
+ assert_eq!(
+ want,
+ Settings::add_path_prefix(borrow::Cow::Borrowed(path), prefix).as_ref(),
+ )
+ };
+
+ assert_add("/foo/bar", "/bar", "/foo");
+ assert_add("/foo/", "/", "/foo");
+ assert_add("/foo/", "/", "/foo///");
+ assert_add("/bar", "/bar", "");
+ }
}
diff --git a/src/domain/store.rs b/src/domain/store.rs
index efe3a68..bc0297f 100644
--- a/src/domain/store.rs
+++ b/src/domain/store.rs
@@ -181,6 +181,8 @@ mod tests {
url: "bar".to_string(),
branch_name: "baz".to_string(),
},
+ remove_path_prefix: None,
+ add_path_prefix: None,
};
assert!(matches!(
@@ -203,6 +205,8 @@ mod tests {
url: "BAR".to_string(),
branch_name: "BAZ".to_string(),
},
+ remove_path_prefix: None,
+ add_path_prefix: None,
};
store.set(&domain, &new_settings).expect("set");
diff --git a/src/origin/proxy.rs b/src/origin/proxy.rs
index 9f6ac8c..057fa4a 100644
--- a/src/origin/proxy.rs
+++ b/src/origin/proxy.rs
@@ -30,7 +30,7 @@ pub async fn serve_http_request(
mut req: hyper::Request,
req_is_https: bool,
) -> unexpected::Result> {
- let (proxy_url, request_http_headers) = if let origin::Descr::Proxy {
+ let (url, request_http_headers) = if let origin::Descr::Proxy {
ref url,
ref request_http_headers,
} = settings.origin_descr
@@ -67,12 +67,12 @@ pub async fn serve_http_request(
.insert("x-forwarded-proto", HeaderValue::from_static("https"));
}
- match hyper_reverse_proxy::call(client_ip, proxy_url, req).await {
+ match hyper_reverse_proxy::call(client_ip, url, req).await {
Ok(res) => Ok(res),
// ProxyError doesn't actually implement Error :facepalm: so we have to format the error
// manually
Err(e) => Err(unexpected::Error::from(
- format!("error while proxying to {proxy_url}: {e:?}").as_str(),
+ format!("error while proxying to {url}: {e:?}").as_str(),
)),
}
}
diff --git a/src/service/http.rs b/src/service/http.rs
index 9e016df..a81e336 100644
--- a/src/service/http.rs
+++ b/src/service/http.rs
@@ -54,7 +54,7 @@ struct BasePresenter<'a, T> {
}
#[derive(Deserialize)]
-struct DomainGetArgs {
+struct DomainArgs {
domain: domain::Name,
}
@@ -246,7 +246,7 @@ impl<'svc> Service {
}
}
- fn domain_get(&self, args: DomainGetArgs) -> Response {
+ fn domain(&self, args: DomainArgs) -> Response {
#[derive(Serialize)]
struct Data {
domain: domain::Name,
@@ -485,10 +485,8 @@ impl<'svc> Service {
self.render_page("/index.html", ())
}
(form_method, "/domain.html") if form_method == config_form_method => {
- self.with_query_req(&req, body, |args: DomainGetArgs| async {
- self.domain_get(args)
- })
- .await
+ self.with_query_req(&req, body, |args: DomainArgs| async { self.domain(args) })
+ .await
}
(form_method, "/domain_init.html") if form_method == config_form_method => {
self.with_query_req(&req, body, |args: DomainInitArgs| async {
diff --git a/src/service/http/tpl/domain.html b/src/service/http/tpl/domain.html
index 42bd611..9ad1a58 100644
--- a/src/service/http/tpl/domain.html
+++ b/src/service/http/tpl/domain.html
@@ -49,6 +49,31 @@ automatically updated too!
+
+
diff --git a/src/service/util.rs b/src/service/util.rs
index e4eec2f..7e5929b 100644
--- a/src/service/util.rs
+++ b/src/service/util.rs
@@ -1,10 +1,12 @@
use std::convert::TryFrom;
use serde::{Deserialize, Serialize};
+use serde_with::{serde_as, NoneAsEmptyString};
use crate::{domain, error::unexpected, origin};
-#[derive(Serialize, Deserialize, Default)]
+#[serde_as]
+#[derive(Serialize, Deserialize, Default, Debug)]
pub struct FlatDomainSettings {
domain_setting_origin_descr_kind: String,
@@ -12,6 +14,14 @@ pub struct FlatDomainSettings {
domain_setting_origin_descr_git_branch_name: Option,
domain_setting_origin_descr_proxy_url: Option,
+
+ #[serde(default)]
+ #[serde_as(as = "NoneAsEmptyString")]
+ domain_setting_remove_path_prefix: Option,
+
+ #[serde(default)]
+ #[serde_as(as = "NoneAsEmptyString")]
+ domain_setting_add_path_prefix: Option,
}
impl TryFrom for domain::Settings {
@@ -31,7 +41,11 @@ impl TryFrom for domain::Settings {
_ => Err("invalid domain_setting_origin_descr_kind".to_string()),
}?;
- Ok(Self { origin_descr })
+ Ok(Self {
+ origin_descr,
+ remove_path_prefix: v.domain_setting_remove_path_prefix,
+ add_path_prefix: v.domain_setting_add_path_prefix,
+ })
}
}
@@ -54,6 +68,9 @@ impl TryFrom for FlatDomainSettings {
}
}
+ res.domain_setting_remove_path_prefix = v.remove_path_prefix;
+ res.domain_setting_add_path_prefix = v.add_path_prefix;
+
Ok(res)
}
}