use super::MAX_TIMEOUT_MS;
use crate::{config::Cluster, errors::ProxyError};
pub(super) fn validate_timeouts(cluster: &Cluster) -> Result<(), ProxyError> {
let name = &cluster.name;
for (field, value) in [
("connection_timeout_ms", cluster.connection_timeout_ms),
("total_connection_timeout_ms", cluster.total_connection_timeout_ms),
("idle_timeout_ms", cluster.idle_timeout_ms),
("read_timeout_ms", cluster.read_timeout_ms),
("write_timeout_ms", cluster.write_timeout_ms),
] {
if let Some(0) = value {
return Err(ProxyError::Config(format!(
"cluster '{name}': {field} is 0 (must be > 0)"
)));
}
if let Some(v) = value
&& v > MAX_TIMEOUT_MS
{
return Err(ProxyError::Config(format!(
"cluster '{name}': {field} ({v} ms) exceeds maximum ({MAX_TIMEOUT_MS} ms / 1 hour)"
)));
}
}
if let (Some(conn), Some(total)) = (cluster.connection_timeout_ms, cluster.total_connection_timeout_ms)
&& conn > total
{
return Err(ProxyError::Config(format!(
"cluster '{name}': connection_timeout_ms ({conn}) exceeds \
total_connection_timeout_ms ({total})"
)));
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::config::Config;
#[test]
fn reject_zero_timeout() {
let yaml = r#"
listeners:
- name: web
address: "0.0.0.0:80"
filter_chains: [main]
filter_chains:
- name: main
filters:
- filter: static_response
status: 200
clusters:
- name: "backend"
endpoints: ["10.0.0.1:80"]
connection_timeout_ms: 0
"#;
let err = Config::from_yaml(yaml).unwrap_err();
assert!(err.to_string().contains("connection_timeout_ms is 0"), "got: {err}");
}
#[test]
fn reject_timeout_exceeding_maximum() {
let yaml = r#"
listeners:
- name: web
address: "0.0.0.0:80"
filter_chains: [main]
filter_chains:
- name: main
filters:
- filter: static_response
status: 200
clusters:
- name: "backend"
endpoints: ["10.0.0.1:80"]
idle_timeout_ms: 7200000
"#;
let err = Config::from_yaml(yaml).unwrap_err();
assert!(
err.to_string().contains("exceeds maximum"),
"should reject timeout > 1 hour, got: {err}"
);
}
#[test]
fn accept_timeout_at_maximum() {
let yaml = r#"
listeners:
- name: web
address: "0.0.0.0:80"
filter_chains: [main]
filter_chains:
- name: main
filters:
- filter: static_response
status: 200
clusters:
- name: "backend"
endpoints: ["10.0.0.1:80"]
connection_timeout_ms: 3600000
total_connection_timeout_ms: 3600000
idle_timeout_ms: 3600000
read_timeout_ms: 3600000
write_timeout_ms: 3600000
"#;
Config::from_yaml(yaml).unwrap();
}
#[test]
fn reject_connection_exceeds_total() {
let yaml = r#"
listeners:
- name: web
address: "0.0.0.0:80"
filter_chains: [main]
filter_chains:
- name: main
filters:
- filter: static_response
status: 200
clusters:
- name: "backend"
endpoints: ["10.0.0.1:80"]
connection_timeout_ms: 10000
total_connection_timeout_ms: 5000
"#;
let err = Config::from_yaml(yaml).unwrap_err();
assert!(err.to_string().contains("exceeds"), "got: {err}");
}
#[test]
fn accept_valid_timeouts() {
let yaml = r#"
listeners:
- name: web
address: "0.0.0.0:80"
filter_chains: [main]
filter_chains:
- name: main
filters:
- filter: static_response
status: 200
clusters:
- name: "backend"
endpoints: ["10.0.0.1:80"]
connection_timeout_ms: 5000
total_connection_timeout_ms: 10000
"#;
Config::from_yaml(yaml).unwrap();
}
}