v0.29.0
This commit is contained in:
1 parent
65e082b9d1
commit
76cb4acef0
72 files changed
+6134
-4709
No files matched your search
+76
-1
@@ -15,6 +15,7 @@ impl Default for PathRoute {
|
||||
headers: HeaderRules::default(),
|
||||
rewrite: PathRewrite::default(),
|
||||
response: None,
|
||||
redirect: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +41,7 @@ impl PathRoute {
|
||||
&& self.rewrite.mode == RewriteMode::Preserve
|
||||
&& self.rewrite.value.is_empty()
|
||||
&& self.response.is_none()
|
||||
&& self.redirect.is_none()
|
||||
}
|
||||
}
|
||||
impl Vhost {
|
||||
@@ -71,6 +73,22 @@ impl Vhost {
|
||||
*host = crate::config::normalize_host_pattern(host)?;
|
||||
ensure!(hosts.insert(host.clone()), "duplicate vhost hostname");
|
||||
}
|
||||
if let Some(target) = &mut self.www_redirect_target {
|
||||
*target = crate::config::normalize_host_pattern(target)?;
|
||||
ensure!(
|
||||
!target.starts_with("*.") && target.parse::<std::net::IpAddr>().is_err(),
|
||||
"www redirect target must be an exact DNS hostname"
|
||||
);
|
||||
let partner = www_partner(target)?;
|
||||
ensure!(
|
||||
self.frontend.hosts.contains(target) || self.frontend.hosts.contains(&partner),
|
||||
"www redirect target or its www/non-www partner must belong to this vhost"
|
||||
);
|
||||
}
|
||||
if self.www_redirect {
|
||||
let _ = self.www_redirect_pair()?;
|
||||
let _ = self.routing_hosts()?;
|
||||
}
|
||||
if let Some(id) = &self.acl_profile_id {
|
||||
ensure!(crate::config::valid_id(id), "invalid ACL profile id");
|
||||
}
|
||||
@@ -86,7 +104,10 @@ impl Vhost {
|
||||
self.source.validate()?;
|
||||
let needs_proxy = self.source.is_reverse_proxy()
|
||||
|| self.routes.iter().any(|route| {
|
||||
route.enabled && route.response.is_none() && route.source.is_reverse_proxy()
|
||||
route.enabled
|
||||
&& route.response.is_none()
|
||||
&& route.redirect.is_none()
|
||||
&& route.source.is_reverse_proxy()
|
||||
});
|
||||
ensure!(
|
||||
self.backends.len() <= 32,
|
||||
@@ -121,6 +142,10 @@ impl Vhost {
|
||||
self.routes.retain(|route| !route.is_legacy_main_fallback());
|
||||
for route in &mut self.routes {
|
||||
route.source.validate()?;
|
||||
ensure!(
|
||||
route.response.is_none() || route.redirect.is_none(),
|
||||
"location cannot define both a local response and a redirect"
|
||||
);
|
||||
if let Some(id) = &route.acl_profile_id {
|
||||
ensure!(crate::config::valid_id(id), "invalid location ACL profile id");
|
||||
}
|
||||
@@ -224,6 +249,56 @@ pub fn validate_listener(listener: Listener) -> Result<()> {
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
/// Return the other side of a www/non-www redirect pair. The target and its
|
||||
/// counterpart must both be routable, even if only one was configured.
|
||||
fn www_partner(host: &str) -> Result<String> {
|
||||
if let Some(bare) = host.strip_prefix("www.") {
|
||||
ensure!(!bare.is_empty() && bare.parse::<std::net::IpAddr>().is_err(),
|
||||
"www redirect requires a DNS hostname");
|
||||
crate::config::normalize_host(bare)
|
||||
} else {
|
||||
crate::config::normalize_host(&format!("www.{host}"))
|
||||
}
|
||||
}
|
||||
impl Vhost {
|
||||
fn www_canonical_host(&self) -> Result<&str> {
|
||||
let canonical = self.www_redirect_target.as_deref()
|
||||
.or_else(|| self.frontend.hosts.first().map(String::as_str))
|
||||
.ok_or_else(|| anyhow::anyhow!("vhost requires at least one hostname"))?;
|
||||
ensure!(!canonical.starts_with("*.") && canonical.parse::<std::net::IpAddr>().is_err(),
|
||||
"www redirect requires an exact DNS hostname");
|
||||
let partner = www_partner(canonical)?;
|
||||
ensure!(self.frontend.hosts.iter().any(|host| host == canonical || host == &partner),
|
||||
"www redirect target or its www/non-www partner must belong to this vhost");
|
||||
Ok(canonical)
|
||||
}
|
||||
|
||||
/// Hostnames owned by the vhost at runtime. Both the selected canonical
|
||||
/// host and its www/non-www partner are claimed, even if one is implicit.
|
||||
pub fn routing_hosts(&self) -> Result<Vec<String>> {
|
||||
let mut hosts = self.frontend.hosts.clone();
|
||||
if !self.www_redirect {
|
||||
return Ok(hosts);
|
||||
}
|
||||
let canonical = self.www_canonical_host()?.to_owned();
|
||||
let companion = www_partner(&canonical)?;
|
||||
for name in [canonical, companion] {
|
||||
if !hosts.contains(&name) {
|
||||
hosts.push(name);
|
||||
}
|
||||
}
|
||||
Ok(hosts)
|
||||
}
|
||||
|
||||
pub fn www_redirect_pair(&self) -> Result<Option<(String, String)>> {
|
||||
if !self.www_redirect {
|
||||
return Ok(None);
|
||||
}
|
||||
let canonical = self.www_canonical_host()?.to_owned();
|
||||
let companion = www_partner(&canonical)?;
|
||||
Ok(Some((companion, canonical)))
|
||||
}
|
||||
}
|
||||
impl Frontend {
|
||||
pub fn resolved(&self, settings: &crate::settings::Listeners) -> Result<Vec<Listener>> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
Reference in new issue
Block a user