use std::{net::IpAddr, time::Duration};
use super::env_parsing::{
parse_duration_millis_from_env, parse_from_env, parse_optional_duration_millis_from_env,
parse_optional_from_env, ValidationBounds,
};
use crate::options::EmulatorServerCertValidation;
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct ConnectionPoolOptions {
proxy_allowed: bool,
min_connect_timeout: Duration,
max_connect_timeout: Duration,
min_dataplane_request_timeout: Duration,
max_dataplane_request_timeout: Duration,
min_metadata_request_timeout: Duration,
max_metadata_request_timeout: Duration,
max_idle_connections_per_endpoint: usize,
idle_connection_timeout: Option<Duration>,
max_http2_streams_per_client: u32,
max_http2_connections_per_endpoint: usize,
min_http2_connections_per_endpoint: usize,
idle_http2_client_timeout: Duration,
http2_health_check_interval: Duration,
http2_consecutive_failure_threshold: u32,
http2_eviction_grace_period: Duration,
http2_keep_alive_interval: Duration,
http2_keep_alive_timeout: Duration,
tcp_keepalive_time: Option<Duration>,
tcp_keepalive_interval: Option<Duration>,
tcp_keepalive_retries: Option<u32>,
is_http2_allowed: bool,
is_gateway20_allowed: bool,
emulator_server_cert_validation: EmulatorServerCertValidation,
local_address: Option<IpAddr>,
}
impl Default for ConnectionPoolOptions {
fn default() -> Self {
ConnectionPoolOptionsBuilder::new()
.build()
.expect("Default ConnectionPoolOptions should always be valid")
}
}
impl ConnectionPoolOptions {
pub fn builder() -> ConnectionPoolOptionsBuilder {
ConnectionPoolOptionsBuilder::new()
}
pub fn proxy_allowed(&self) -> bool {
self.proxy_allowed
}
pub fn min_connect_timeout(&self) -> Duration {
self.min_connect_timeout
}
pub fn max_connect_timeout(&self) -> Duration {
self.max_connect_timeout
}
pub fn min_dataplane_request_timeout(&self) -> Duration {
self.min_dataplane_request_timeout
}
pub fn max_dataplane_request_timeout(&self) -> Duration {
self.max_dataplane_request_timeout
}
pub fn min_metadata_request_timeout(&self) -> Duration {
self.min_metadata_request_timeout
}
pub fn max_metadata_request_timeout(&self) -> Duration {
self.max_metadata_request_timeout
}
pub fn max_idle_connections_per_endpoint(&self) -> usize {
self.max_idle_connections_per_endpoint
}
pub fn idle_connection_timeout(&self) -> Option<Duration> {
self.idle_connection_timeout
}
pub fn max_http2_streams_per_client(&self) -> u32 {
self.max_http2_streams_per_client
}
pub fn max_http2_connections_per_endpoint(&self) -> usize {
self.max_http2_connections_per_endpoint
}
pub fn min_http2_connections_per_endpoint(&self) -> usize {
self.min_http2_connections_per_endpoint
}
pub fn idle_http2_client_timeout(&self) -> Duration {
self.idle_http2_client_timeout
}
pub fn http2_health_check_interval(&self) -> Duration {
self.http2_health_check_interval
}
pub fn http2_consecutive_failure_threshold(&self) -> u32 {
self.http2_consecutive_failure_threshold
}
pub fn http2_eviction_grace_period(&self) -> Duration {
self.http2_eviction_grace_period
}
pub fn http2_keep_alive_interval(&self) -> Duration {
self.http2_keep_alive_interval
}
pub fn http2_keep_alive_timeout(&self) -> Duration {
self.http2_keep_alive_timeout
}
pub fn tcp_keepalive_time(&self) -> Option<Duration> {
self.tcp_keepalive_time
}
pub fn tcp_keepalive_interval(&self) -> Option<Duration> {
self.tcp_keepalive_interval
}
pub fn tcp_keepalive_retries(&self) -> Option<u32> {
self.tcp_keepalive_retries
}
pub fn is_http2_allowed(&self) -> bool {
self.is_http2_allowed
}
pub fn is_gateway20_allowed(&self) -> bool {
self.is_gateway20_allowed
}
pub fn emulator_server_cert_validation(&self) -> EmulatorServerCertValidation {
self.emulator_server_cert_validation
}
pub fn local_address(&self) -> Option<IpAddr> {
self.local_address
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Default)]
pub struct ConnectionPoolOptionsBuilder {
proxy_allowed: Option<bool>,
min_connect_timeout: Option<Duration>,
max_connect_timeout: Option<Duration>,
min_dataplane_request_timeout: Option<Duration>,
max_dataplane_request_timeout: Option<Duration>,
min_metadata_request_timeout: Option<Duration>,
max_metadata_request_timeout: Option<Duration>,
max_idle_connections_per_endpoint: Option<usize>,
idle_connection_timeout: Option<Duration>,
max_http2_streams_per_client: Option<u32>,
max_http2_connections_per_endpoint: Option<usize>,
min_http2_connections_per_endpoint: Option<usize>,
idle_http2_client_timeout: Option<Duration>,
http2_health_check_interval: Option<Duration>,
http2_consecutive_failure_threshold: Option<u32>,
http2_eviction_grace_period: Option<Duration>,
http2_keep_alive_interval: Option<Duration>,
http2_keep_alive_timeout: Option<Duration>,
tcp_keepalive_time: Option<Duration>,
tcp_keepalive_interval: Option<Duration>,
tcp_keepalive_retries: Option<u32>,
is_http2_allowed: Option<bool>,
is_gateway20_allowed: Option<bool>,
emulator_server_cert_validation: Option<EmulatorServerCertValidation>,
local_address: Option<IpAddr>,
}
impl ConnectionPoolOptionsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_proxy_allowed(mut self, value: bool) -> Self {
self.proxy_allowed = Some(value);
self
}
pub fn with_min_connect_timeout(mut self, timeout: Duration) -> Self {
self.min_connect_timeout = Some(timeout);
self
}
pub fn with_max_connect_timeout(mut self, timeout: Duration) -> Self {
self.max_connect_timeout = Some(timeout);
self
}
pub fn with_min_dataplane_request_timeout(mut self, timeout: Duration) -> Self {
self.min_dataplane_request_timeout = Some(timeout);
self
}
pub fn with_max_dataplane_request_timeout(mut self, timeout: Duration) -> Self {
self.max_dataplane_request_timeout = Some(timeout);
self
}
pub fn with_min_metadata_request_timeout(mut self, timeout: Duration) -> Self {
self.min_metadata_request_timeout = Some(timeout);
self
}
pub fn with_max_metadata_request_timeout(mut self, timeout: Duration) -> Self {
self.max_metadata_request_timeout = Some(timeout);
self
}
pub fn with_max_idle_connections_per_endpoint(mut self, count: usize) -> Self {
self.max_idle_connections_per_endpoint = Some(count);
self
}
pub fn with_idle_connection_timeout(mut self, timeout: Duration) -> Self {
self.idle_connection_timeout = Some(timeout);
self
}
pub fn with_max_http2_streams_per_client(mut self, value: u32) -> Self {
self.max_http2_streams_per_client = Some(value);
self
}
pub fn with_max_http2_connections_per_endpoint(mut self, value: usize) -> Self {
self.max_http2_connections_per_endpoint = Some(value);
self
}
pub fn with_min_http2_connections_per_endpoint(mut self, value: usize) -> Self {
self.min_http2_connections_per_endpoint = Some(value);
self
}
pub fn with_idle_http2_client_timeout(mut self, timeout: Duration) -> Self {
self.idle_http2_client_timeout = Some(timeout);
self
}
pub fn with_http2_health_check_interval(mut self, timeout: Duration) -> Self {
self.http2_health_check_interval = Some(timeout);
self
}
pub fn with_http2_consecutive_failure_threshold(mut self, value: u32) -> Self {
self.http2_consecutive_failure_threshold = Some(value);
self
}
pub fn with_http2_eviction_grace_period(mut self, timeout: Duration) -> Self {
self.http2_eviction_grace_period = Some(timeout);
self
}
pub fn with_http2_keep_alive_interval(mut self, timeout: Duration) -> Self {
self.http2_keep_alive_interval = Some(timeout);
self
}
pub fn with_http2_keep_alive_timeout(mut self, timeout: Duration) -> Self {
self.http2_keep_alive_timeout = Some(timeout);
self
}
pub fn with_tcp_keepalive_time(mut self, timeout: Duration) -> Self {
self.tcp_keepalive_time = Some(timeout);
self
}
pub fn with_tcp_keepalive_interval(mut self, timeout: Duration) -> Self {
self.tcp_keepalive_interval = Some(timeout);
self
}
pub fn with_tcp_keepalive_retries(mut self, value: u32) -> Self {
self.tcp_keepalive_retries = Some(value);
self
}
pub fn with_is_http2_allowed(mut self, value: bool) -> Self {
self.is_http2_allowed = Some(value);
self
}
pub fn with_is_gateway20_allowed(mut self, value: bool) -> Self {
self.is_gateway20_allowed = Some(value);
self
}
pub fn with_emulator_server_cert_validation(
mut self,
value: EmulatorServerCertValidation,
) -> Self {
self.emulator_server_cert_validation = Some(value);
self
}
pub fn with_local_address(mut self, addr: IpAddr) -> Self {
self.local_address = Some(addr);
self
}
pub fn build(self) -> azure_core::Result<ConnectionPoolOptions> {
let effective_is_http2_allowed = parse_from_env(
self.is_http2_allowed,
"AZURE_COSMOS_CONNECTION_POOL_IS_HTTP2_ALLOWED",
true,
ValidationBounds::none(),
)?;
let effective_is_gateway20_allowed = if let Some(gateway20) = self.is_gateway20_allowed {
gateway20 && effective_is_http2_allowed
} else {
match std::env::var("AZURE_COSMOS_CONNECTION_POOL_IS_GATEWAY20_ALLOWED") {
Ok(v) => {
let gateway20: bool = v.parse().map_err(|e| {
azure_core::Error::with_message(
azure_core::error::ErrorKind::DataConversion,
format!(
"Failed to parse AZURE_COSMOS_CONNECTION_POOL_IS_GATEWAY20_ALLOWED as boolean: {} ({})",
v, e
),
)
})?;
gateway20 && effective_is_http2_allowed
}
Err(_) => false, }
};
let max_connection_pool_size_default = if effective_is_http2_allowed {
1_000
} else {
10_000
};
let min_connect_timeout = parse_duration_millis_from_env(
self.min_connect_timeout,
"AZURE_COSMOS_CONNECTION_POOL_MIN_CONNECT_TIMEOUT_MS",
100,
100,
6_000,
)?;
let max_connect_timeout = parse_duration_millis_from_env(
self.max_connect_timeout,
"AZURE_COSMOS_CONNECTION_POOL_MAX_CONNECT_TIMEOUT_MS",
5_000,
100,
6_000,
)?;
let min_dataplane_request_timeout = parse_duration_millis_from_env(
self.min_dataplane_request_timeout,
"AZURE_COSMOS_CONNECTION_POOL_MIN_DATAPLANE_REQUEST_TIMEOUT_MS",
100,
100,
65_000,
)?;
let max_dataplane_request_timeout = parse_duration_millis_from_env(
self.max_dataplane_request_timeout,
"AZURE_COSMOS_CONNECTION_POOL_MAX_DATAPLANE_REQUEST_TIMEOUT_MS",
6_000,
100,
u64::MAX,
)?;
let min_metadata_request_timeout = parse_duration_millis_from_env(
self.min_metadata_request_timeout,
"AZURE_COSMOS_CONNECTION_POOL_MIN_METADATA_REQUEST_TIMEOUT_MS",
100,
100,
6_000,
)?;
let max_metadata_request_timeout = parse_duration_millis_from_env(
self.max_metadata_request_timeout,
"AZURE_COSMOS_CONNECTION_POOL_MAX_METADATA_REQUEST_TIMEOUT_MS",
65_000,
100,
65_000,
)?;
let max_idle_connections_per_endpoint = parse_from_env(
self.max_idle_connections_per_endpoint,
"AZURE_COSMOS_CONNECTION_POOL_MAX_IDLE_CONNECTIONS_PER_ENDPOINT",
max_connection_pool_size_default,
ValidationBounds::range(10, 64_000),
)?;
let idle_connection_timeout = parse_optional_duration_millis_from_env(
self.idle_connection_timeout,
"AZURE_COSMOS_CONNECTION_POOL_IDLE_CONNECTION_TIMEOUT_MS",
300_000,
u64::MAX,
)?;
let max_http2_streams_per_client = parse_from_env(
self.max_http2_streams_per_client,
"AZURE_COSMOS_CONNECTION_POOL_MAX_HTTP2_STREAMS_PER_CLIENT",
16_u32,
ValidationBounds::range(1, 20),
)?;
let cpu_based_http2_max = std::thread::available_parallelism()
.map(|count| count.get().saturating_mul(2))
.unwrap_or(32)
.clamp(1, 256);
let max_http2_connections_per_endpoint = parse_from_env(
self.max_http2_connections_per_endpoint,
"AZURE_COSMOS_CONNECTION_POOL_MAX_HTTP2_CONNECTIONS_PER_ENDPOINT",
cpu_based_http2_max,
ValidationBounds::range(1, 256),
)?;
let min_http2_connections_per_endpoint = parse_from_env(
self.min_http2_connections_per_endpoint,
"AZURE_COSMOS_CONNECTION_POOL_MIN_HTTP2_CONNECTIONS_PER_ENDPOINT",
1_usize,
ValidationBounds::range(1, 256),
)?;
if min_http2_connections_per_endpoint > max_http2_connections_per_endpoint {
return Err(azure_core::Error::with_message(
azure_core::error::ErrorKind::Other,
format!(
"min_http2_connections_per_endpoint must be less than or equal to max_http2_connections_per_endpoint, got {} > {}",
min_http2_connections_per_endpoint,
max_http2_connections_per_endpoint
),
));
}
let idle_http2_client_timeout = parse_duration_millis_from_env(
self.idle_http2_client_timeout,
"AZURE_COSMOS_CONNECTION_POOL_IDLE_HTTP2_CLIENT_TIMEOUT_MS",
60_000,
1_000,
u64::MAX,
)?;
let http2_health_check_interval = parse_duration_millis_from_env(
self.http2_health_check_interval,
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_HEALTH_CHECK_INTERVAL_MS",
10_000,
100,
u64::MAX,
)?;
let http2_consecutive_failure_threshold = parse_from_env(
self.http2_consecutive_failure_threshold,
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_CONSECUTIVE_FAILURE_THRESHOLD",
5_u32,
ValidationBounds::range(1_u32, 255_u32),
)?;
let http2_eviction_grace_period = parse_duration_millis_from_env(
self.http2_eviction_grace_period,
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_EVICTION_GRACE_PERIOD_MS",
2_000,
100,
u64::MAX,
)?;
let http2_keep_alive_interval = parse_duration_millis_from_env(
self.http2_keep_alive_interval,
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_KEEP_ALIVE_INTERVAL_MS",
1_000,
100,
u64::MAX,
)?;
let http2_keep_alive_timeout = parse_duration_millis_from_env(
self.http2_keep_alive_timeout,
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_KEEP_ALIVE_TIMEOUT_MS",
2_000,
100,
u64::MAX,
)?;
let tcp_keepalive_time = parse_optional_duration_millis_from_env(
self.tcp_keepalive_time,
"AZURE_COSMOS_CONNECTION_POOL_TCP_KEEPALIVE_TIME_MS",
1_000,
u64::MAX,
)?
.or(Some(Duration::from_secs(1)));
let tcp_keepalive_interval = parse_optional_duration_millis_from_env(
self.tcp_keepalive_interval,
"AZURE_COSMOS_CONNECTION_POOL_TCP_KEEPALIVE_INTERVAL_MS",
1_000,
u64::MAX,
)?
.or(Some(Duration::from_secs(1)));
let tcp_keepalive_retries = parse_optional_from_env(
self.tcp_keepalive_retries,
"AZURE_COSMOS_CONNECTION_POOL_TCP_KEEPALIVE_RETRIES",
ValidationBounds::range(1_u32, 255_u32),
)?;
Ok(ConnectionPoolOptions {
proxy_allowed: parse_from_env(
self.proxy_allowed,
"AZURE_COSMOS_CONNECTION_POOL_IS_PROXY_ALLOWED",
false,
ValidationBounds::none(),
)?,
min_connect_timeout,
max_connect_timeout,
min_dataplane_request_timeout,
max_dataplane_request_timeout,
min_metadata_request_timeout,
max_metadata_request_timeout,
max_idle_connections_per_endpoint,
idle_connection_timeout,
max_http2_streams_per_client,
max_http2_connections_per_endpoint,
min_http2_connections_per_endpoint,
idle_http2_client_timeout,
http2_health_check_interval,
http2_consecutive_failure_threshold,
http2_eviction_grace_period,
http2_keep_alive_interval,
http2_keep_alive_timeout,
tcp_keepalive_time,
tcp_keepalive_interval,
tcp_keepalive_retries,
is_http2_allowed: effective_is_http2_allowed,
is_gateway20_allowed: effective_is_gateway20_allowed,
emulator_server_cert_validation: match self.emulator_server_cert_validation {
Some(v) => v,
None => EmulatorServerCertValidation::from(parse_from_env(
None::<bool>,
"AZURE_COSMOS_EMULATOR_SERVER_CERT_VALIDATION_DISABLED",
false,
ValidationBounds::none(),
)?),
},
local_address: match self.local_address {
Some(addr) => Some(addr),
None => match std::env::var("AZURE_COSMOS_LOCAL_ADDRESS") {
Ok(v) => Some(v.parse().map_err(|e| {
azure_core::Error::with_message(
azure_core::error::ErrorKind::DataConversion,
format!(
"Failed to parse AZURE_COSMOS_LOCAL_ADDRESS as IP address: {} ({})",
v, e
),
)
})?),
Err(_) => None,
},
},
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn connection_pool_options_builder_defaults() {
let options = ConnectionPoolOptionsBuilder::new().build().unwrap();
assert!(!options.proxy_allowed());
assert_eq!(options.min_connect_timeout(), Duration::from_millis(100));
assert_eq!(options.max_connect_timeout(), Duration::from_millis(5_000));
assert_eq!(
options.min_dataplane_request_timeout(),
Duration::from_millis(100)
);
assert_eq!(
options.max_dataplane_request_timeout(),
Duration::from_millis(6_000)
);
assert_eq!(
options.min_metadata_request_timeout(),
Duration::from_millis(100)
);
assert_eq!(
options.max_metadata_request_timeout(),
Duration::from_millis(65_000)
);
assert!(options.is_http2_allowed());
assert!(!options.is_gateway20_allowed());
assert_eq!(
options.emulator_server_cert_validation(),
EmulatorServerCertValidation::Enabled
);
assert_eq!(options.idle_connection_timeout(), None);
assert_eq!(options.max_http2_streams_per_client(), 16);
assert!(options.max_http2_connections_per_endpoint() >= 1);
assert_eq!(options.min_http2_connections_per_endpoint(), 1);
assert_eq!(options.idle_http2_client_timeout(), Duration::from_secs(60));
assert_eq!(
options.http2_health_check_interval(),
Duration::from_secs(10)
);
assert_eq!(options.http2_consecutive_failure_threshold(), 5);
assert_eq!(
options.http2_eviction_grace_period(),
Duration::from_secs(2)
);
assert_eq!(options.http2_keep_alive_interval(), Duration::from_secs(1));
assert_eq!(options.http2_keep_alive_timeout(), Duration::from_secs(2));
assert_eq!(options.tcp_keepalive_time(), Some(Duration::from_secs(1)));
assert_eq!(
options.tcp_keepalive_interval(),
Some(Duration::from_secs(1))
);
assert_eq!(options.tcp_keepalive_retries(), None);
assert_eq!(options.local_address(), None);
assert_eq!(options.max_idle_connections_per_endpoint(), 1_000);
}
#[test]
fn connection_pool_options_builder_custom_values() {
let options = ConnectionPoolOptionsBuilder::new()
.with_proxy_allowed(true)
.with_min_connect_timeout(Duration::from_millis(200))
.with_max_connect_timeout(Duration::from_millis(3_000))
.with_min_dataplane_request_timeout(Duration::from_millis(500))
.with_max_dataplane_request_timeout(Duration::from_millis(10_000))
.with_min_metadata_request_timeout(Duration::from_millis(150))
.with_max_metadata_request_timeout(Duration::from_millis(30_000))
.with_max_idle_connections_per_endpoint(5_000)
.with_idle_connection_timeout(Duration::from_millis(600_000))
.with_max_http2_streams_per_client(12)
.with_max_http2_connections_per_endpoint(24)
.with_min_http2_connections_per_endpoint(3)
.with_idle_http2_client_timeout(Duration::from_millis(90_000))
.with_http2_health_check_interval(Duration::from_millis(15_000))
.with_http2_consecutive_failure_threshold(8)
.with_http2_eviction_grace_period(Duration::from_millis(4_000))
.with_http2_keep_alive_interval(Duration::from_millis(1_500))
.with_http2_keep_alive_timeout(Duration::from_millis(2_500))
.with_tcp_keepalive_time(Duration::from_millis(30_000))
.with_tcp_keepalive_interval(Duration::from_millis(5_000))
.with_tcp_keepalive_retries(4)
.with_is_http2_allowed(false)
.with_is_gateway20_allowed(true)
.with_emulator_server_cert_validation(EmulatorServerCertValidation::DangerousDisabled)
.build()
.unwrap();
assert!(options.proxy_allowed());
assert_eq!(options.min_connect_timeout(), Duration::from_millis(200));
assert_eq!(options.max_connect_timeout(), Duration::from_millis(3_000));
assert_eq!(
options.min_dataplane_request_timeout(),
Duration::from_millis(500)
);
assert_eq!(
options.max_dataplane_request_timeout(),
Duration::from_millis(10_000)
);
assert_eq!(
options.min_metadata_request_timeout(),
Duration::from_millis(150)
);
assert_eq!(
options.max_metadata_request_timeout(),
Duration::from_millis(30_000)
);
assert_eq!(options.max_idle_connections_per_endpoint(), 5_000);
assert_eq!(
options.idle_connection_timeout(),
Some(Duration::from_millis(600_000))
);
assert_eq!(options.max_http2_streams_per_client(), 12);
assert_eq!(options.max_http2_connections_per_endpoint(), 24);
assert_eq!(options.min_http2_connections_per_endpoint(), 3);
assert_eq!(
options.idle_http2_client_timeout(),
Duration::from_millis(90_000)
);
assert_eq!(
options.http2_health_check_interval(),
Duration::from_millis(15_000)
);
assert_eq!(options.http2_consecutive_failure_threshold(), 8);
assert_eq!(
options.http2_eviction_grace_period(),
Duration::from_millis(4_000)
);
assert_eq!(
options.http2_keep_alive_interval(),
Duration::from_millis(1_500)
);
assert_eq!(
options.http2_keep_alive_timeout(),
Duration::from_millis(2_500)
);
assert_eq!(
options.tcp_keepalive_time(),
Some(Duration::from_millis(30_000))
);
assert_eq!(
options.tcp_keepalive_interval(),
Some(Duration::from_millis(5_000))
);
assert_eq!(options.tcp_keepalive_retries(), Some(4));
assert!(!options.is_http2_allowed());
assert!(!options.is_gateway20_allowed());
assert_eq!(
options.emulator_server_cert_validation(),
EmulatorServerCertValidation::DangerousDisabled
);
}
#[test]
fn min_connect_timeout_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_min_connect_timeout(Duration::from_millis(50))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("min_connect_timeout_ms must be at least 100ms"));
}
#[test]
fn min_connect_timeout_too_large() {
let result = ConnectionPoolOptionsBuilder::new()
.with_min_connect_timeout(Duration::from_millis(7_000))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("min_connect_timeout_ms must be at most 6000ms"));
}
#[test]
fn max_connect_timeout_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_max_connect_timeout(Duration::from_millis(50))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("max_connect_timeout_ms must be at least 100ms"));
}
#[test]
fn max_connect_timeout_too_large() {
let result = ConnectionPoolOptionsBuilder::new()
.with_max_connect_timeout(Duration::from_millis(7_000))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("max_connect_timeout_ms must be at most 6000ms"));
}
#[test]
fn min_dataplane_request_timeout_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_min_dataplane_request_timeout(Duration::from_millis(50))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("min_dataplane_request_timeout_ms must be at least 100ms"));
}
#[test]
fn min_dataplane_request_timeout_too_large() {
let result = ConnectionPoolOptionsBuilder::new()
.with_min_dataplane_request_timeout(Duration::from_millis(70_000))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("min_dataplane_request_timeout_ms must be at most 65000ms"));
}
#[test]
fn max_dataplane_request_timeout_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_max_dataplane_request_timeout(Duration::from_millis(50))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("max_dataplane_request_timeout_ms must be at least 100ms"));
}
#[test]
fn min_metadata_request_timeout_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_min_metadata_request_timeout(Duration::from_millis(50))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("min_metadata_request_timeout_ms must be at least 100ms"));
}
#[test]
fn min_metadata_request_timeout_too_large() {
let result = ConnectionPoolOptionsBuilder::new()
.with_min_metadata_request_timeout(Duration::from_millis(7_000))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("min_metadata_request_timeout_ms must be at most 6000ms"));
}
#[test]
fn max_metadata_request_timeout_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_max_metadata_request_timeout(Duration::from_millis(50))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("max_metadata_request_timeout_ms must be at least 100ms"));
}
#[test]
fn max_metadata_request_timeout_too_large() {
let result = ConnectionPoolOptionsBuilder::new()
.with_max_metadata_request_timeout(Duration::from_millis(70_000))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("max_metadata_request_timeout_ms must be at most 65000ms"));
}
#[test]
fn max_idle_connections_per_endpoint_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_max_idle_connections_per_endpoint(5)
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("max_idle_connections_per_endpoint must be at least 10"));
}
#[test]
fn max_idle_connections_per_endpoint_too_large() {
let result = ConnectionPoolOptionsBuilder::new()
.with_max_idle_connections_per_endpoint(65_000)
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("max_idle_connections_per_endpoint must be at most 64000"));
}
#[test]
fn idle_connection_timeout_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_idle_connection_timeout(Duration::from_millis(100_000))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("idle_connection_timeout_ms must be at least 300000ms"));
}
#[test]
fn max_http2_streams_per_client_too_large() {
let result = ConnectionPoolOptionsBuilder::new()
.with_max_http2_streams_per_client(21)
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("max_http2_streams_per_client must be at most 20"));
}
#[test]
fn min_http2_connections_cannot_exceed_max() {
let result = ConnectionPoolOptionsBuilder::new()
.with_min_http2_connections_per_endpoint(4)
.with_max_http2_connections_per_endpoint(3)
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("min_http2_connections_per_endpoint must be less than or equal to max_http2_connections_per_endpoint"));
}
#[test]
fn idle_http2_client_timeout_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_idle_http2_client_timeout(Duration::from_millis(500))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("idle_http2_client_timeout_ms must be at least 1000ms"));
}
#[test]
fn http2_health_check_interval_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_http2_health_check_interval(Duration::from_millis(50))
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("http2_health_check_interval_ms must be at least 100ms"));
}
#[test]
fn http2_consecutive_failure_threshold_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_http2_consecutive_failure_threshold(0)
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("http2_consecutive_failure_threshold must be at least 1"));
}
#[test]
fn tcp_keepalive_retries_too_small() {
let result = ConnectionPoolOptionsBuilder::new()
.with_tcp_keepalive_retries(0)
.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("tcp_keepalive_retries must be at least 1"));
}
#[test]
fn gateway20_requires_http2() {
let options = ConnectionPoolOptionsBuilder::new()
.with_is_http2_allowed(false)
.with_is_gateway20_allowed(true)
.build()
.unwrap();
assert!(!options.is_gateway20_allowed());
}
#[test]
fn http2_disabled_changes_max_connection_pool_default() {
let options = ConnectionPoolOptionsBuilder::new()
.with_is_http2_allowed(false)
.build()
.unwrap();
assert_eq!(options.max_idle_connections_per_endpoint(), 10_000);
}
}