use std::{net::IpAddr, time::Duration};
use azure_data_cosmos_macros::CosmosOptions;
use super::env_parsing::{
resolve_duration_ms, resolve_from_env, resolve_optional_duration_ms, resolve_optional_from_env,
ValidationBounds,
};
use crate::options::ServerCertificateValidation;
#[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,
server_certificate_validation: ServerCertificateValidation,
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 server_certificate_validation(&self) -> ServerCertificateValidation {
self.server_certificate_validation
}
pub fn local_address(&self) -> Option<IpAddr> {
self.local_address
}
}
fn parse_env_duration_millis(raw: &str) -> Option<Duration> {
raw.trim().parse::<u64>().ok().map(Duration::from_millis)
}
fn parse_env_server_cert_validation(raw: &str) -> Option<ServerCertificateValidation> {
match raw.trim().to_ascii_lowercase().as_str() {
"true" | "1" | "yes" | "on" => Some(ServerCertificateValidation::RequiredUnlessEmulator),
"false" | "0" | "no" | "off" => Some(ServerCertificateValidation::Required),
_ => None,
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Default, CosmosOptions)]
#[options(env_only)]
pub struct ConnectionPoolOptionsBuilder {
#[option(env = "AZURE_COSMOS_CONNECTION_POOL_IS_PROXY_ALLOWED")]
proxy_allowed: Option<bool>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_MIN_CONNECT_TIMEOUT_MS",
parser = parse_env_duration_millis
)]
min_connect_timeout: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_MAX_CONNECT_TIMEOUT_MS",
parser = parse_env_duration_millis
)]
max_connect_timeout: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_MIN_DATAPLANE_REQUEST_TIMEOUT_MS",
parser = parse_env_duration_millis
)]
min_dataplane_request_timeout: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_MAX_DATAPLANE_REQUEST_TIMEOUT_MS",
parser = parse_env_duration_millis
)]
max_dataplane_request_timeout: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_MIN_METADATA_REQUEST_TIMEOUT_MS",
parser = parse_env_duration_millis
)]
min_metadata_request_timeout: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_MAX_METADATA_REQUEST_TIMEOUT_MS",
parser = parse_env_duration_millis
)]
max_metadata_request_timeout: Option<Duration>,
#[option(env = "AZURE_COSMOS_CONNECTION_POOL_MAX_IDLE_CONNECTIONS_PER_ENDPOINT")]
max_idle_connections_per_endpoint: Option<usize>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_IDLE_CONNECTION_TIMEOUT_MS",
parser = parse_env_duration_millis
)]
idle_connection_timeout: Option<Duration>,
#[option(env = "AZURE_COSMOS_CONNECTION_POOL_MAX_HTTP2_STREAMS_PER_CLIENT")]
max_http2_streams_per_client: Option<u32>,
#[option(env = "AZURE_COSMOS_CONNECTION_POOL_MAX_HTTP2_CONNECTIONS_PER_ENDPOINT")]
max_http2_connections_per_endpoint: Option<usize>,
#[option(env = "AZURE_COSMOS_CONNECTION_POOL_MIN_HTTP2_CONNECTIONS_PER_ENDPOINT")]
min_http2_connections_per_endpoint: Option<usize>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_IDLE_HTTP2_CLIENT_TIMEOUT_MS",
parser = parse_env_duration_millis
)]
idle_http2_client_timeout: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_HTTP2_HEALTH_CHECK_INTERVAL_MS",
parser = parse_env_duration_millis
)]
http2_health_check_interval: Option<Duration>,
#[option(env = "AZURE_COSMOS_CONNECTION_POOL_HTTP2_CONSECUTIVE_FAILURE_THRESHOLD")]
http2_consecutive_failure_threshold: Option<u32>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_HTTP2_EVICTION_GRACE_PERIOD_MS",
parser = parse_env_duration_millis
)]
http2_eviction_grace_period: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_HTTP2_KEEP_ALIVE_INTERVAL_MS",
parser = parse_env_duration_millis
)]
http2_keep_alive_interval: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_HTTP2_KEEP_ALIVE_TIMEOUT_MS",
parser = parse_env_duration_millis
)]
http2_keep_alive_timeout: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_TCP_KEEPALIVE_TIME_MS",
parser = parse_env_duration_millis
)]
tcp_keepalive_time: Option<Duration>,
#[option(
env = "AZURE_COSMOS_CONNECTION_POOL_TCP_KEEPALIVE_INTERVAL_MS",
parser = parse_env_duration_millis
)]
tcp_keepalive_interval: Option<Duration>,
#[option(env = "AZURE_COSMOS_CONNECTION_POOL_TCP_KEEPALIVE_RETRIES")]
tcp_keepalive_retries: Option<u32>,
#[option(env = "AZURE_COSMOS_CONNECTION_POOL_IS_HTTP2_ALLOWED")]
is_http2_allowed: Option<bool>,
#[option(env = "AZURE_COSMOS_CONNECTION_POOL_IS_GATEWAY20_ALLOWED")]
is_gateway20_allowed: Option<bool>,
#[option(
env = "AZURE_COSMOS_EMULATOR_SERVER_CERT_VALIDATION_DISABLED",
parser = parse_env_server_cert_validation
)]
server_certificate_validation: Option<ServerCertificateValidation>,
#[option(env = "AZURE_COSMOS_LOCAL_ADDRESS")]
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_server_certificate_validation(
mut self,
value: ServerCertificateValidation,
) -> Self {
self.server_certificate_validation = Some(value);
self
}
pub fn with_local_address(mut self, addr: IpAddr) -> Self {
self.local_address = Some(addr);
self
}
pub fn build(self) -> crate::error::Result<ConnectionPoolOptions> {
let env = Self::from_env();
let effective_is_http2_allowed = resolve_from_env(
self.is_http2_allowed,
env.is_http2_allowed,
"AZURE_COSMOS_CONNECTION_POOL_IS_HTTP2_ALLOWED",
true,
ValidationBounds::none(),
)?;
let effective_is_gateway20_allowed =
match self.is_gateway20_allowed.or(env.is_gateway20_allowed) {
Some(gateway20) => gateway20 && effective_is_http2_allowed,
None => false, };
let max_connection_pool_size_default = if effective_is_http2_allowed {
1_000
} else {
10_000
};
let min_connect_timeout = resolve_duration_ms(
self.min_connect_timeout.or(env.min_connect_timeout),
None,
"AZURE_COSMOS_CONNECTION_POOL_MIN_CONNECT_TIMEOUT_MS",
100,
100,
6_000,
)?;
let max_connect_timeout = resolve_duration_ms(
self.max_connect_timeout.or(env.max_connect_timeout),
None,
"AZURE_COSMOS_CONNECTION_POOL_MAX_CONNECT_TIMEOUT_MS",
5_000,
100,
6_000,
)?;
let min_dataplane_request_timeout = resolve_duration_ms(
self.min_dataplane_request_timeout
.or(env.min_dataplane_request_timeout),
None,
"AZURE_COSMOS_CONNECTION_POOL_MIN_DATAPLANE_REQUEST_TIMEOUT_MS",
100,
100,
65_000,
)?;
let max_dataplane_request_timeout = resolve_duration_ms(
self.max_dataplane_request_timeout
.or(env.max_dataplane_request_timeout),
None,
"AZURE_COSMOS_CONNECTION_POOL_MAX_DATAPLANE_REQUEST_TIMEOUT_MS",
6_000,
100,
u64::MAX,
)?;
let min_metadata_request_timeout = resolve_duration_ms(
self.min_metadata_request_timeout
.or(env.min_metadata_request_timeout),
None,
"AZURE_COSMOS_CONNECTION_POOL_MIN_METADATA_REQUEST_TIMEOUT_MS",
100,
100,
6_000,
)?;
let max_metadata_request_timeout = resolve_duration_ms(
self.max_metadata_request_timeout
.or(env.max_metadata_request_timeout),
None,
"AZURE_COSMOS_CONNECTION_POOL_MAX_METADATA_REQUEST_TIMEOUT_MS",
65_000,
100,
65_000,
)?;
let max_idle_connections_per_endpoint = resolve_from_env(
self.max_idle_connections_per_endpoint,
env.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 = resolve_optional_duration_ms(
self.idle_connection_timeout.or(env.idle_connection_timeout),
None,
"AZURE_COSMOS_CONNECTION_POOL_IDLE_CONNECTION_TIMEOUT_MS",
300_000,
u64::MAX,
)?;
let max_http2_streams_per_client = resolve_from_env(
self.max_http2_streams_per_client,
env.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 = resolve_from_env(
self.max_http2_connections_per_endpoint,
env.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 = resolve_from_env(
self.min_http2_connections_per_endpoint,
env.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(crate::error::CosmosError::builder().with_status(crate::error::CosmosStatus::new(azure_core::http::StatusCode::BadRequest)).with_message(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
)).build());
}
let idle_http2_client_timeout = resolve_duration_ms(
self.idle_http2_client_timeout
.or(env.idle_http2_client_timeout),
None,
"AZURE_COSMOS_CONNECTION_POOL_IDLE_HTTP2_CLIENT_TIMEOUT_MS",
60_000,
1_000,
u64::MAX,
)?;
let http2_health_check_interval = resolve_duration_ms(
self.http2_health_check_interval
.or(env.http2_health_check_interval),
None,
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_HEALTH_CHECK_INTERVAL_MS",
10_000,
100,
u64::MAX,
)?;
let http2_consecutive_failure_threshold = resolve_from_env(
self.http2_consecutive_failure_threshold,
env.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 = resolve_duration_ms(
self.http2_eviction_grace_period
.or(env.http2_eviction_grace_period),
None,
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_EVICTION_GRACE_PERIOD_MS",
2_000,
100,
u64::MAX,
)?;
let http2_keep_alive_interval = resolve_duration_ms(
self.http2_keep_alive_interval
.or(env.http2_keep_alive_interval),
None,
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_KEEP_ALIVE_INTERVAL_MS",
1_000,
100,
u64::MAX,
)?;
let http2_keep_alive_timeout = resolve_duration_ms(
self.http2_keep_alive_timeout
.or(env.http2_keep_alive_timeout),
None,
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_KEEP_ALIVE_TIMEOUT_MS",
2_000,
100,
u64::MAX,
)?;
let tcp_keepalive_time = resolve_optional_duration_ms(
self.tcp_keepalive_time.or(env.tcp_keepalive_time),
None,
"AZURE_COSMOS_CONNECTION_POOL_TCP_KEEPALIVE_TIME_MS",
1_000,
u64::MAX,
)?
.or(Some(Duration::from_secs(1)));
let tcp_keepalive_interval = resolve_optional_duration_ms(
self.tcp_keepalive_interval.or(env.tcp_keepalive_interval),
None,
"AZURE_COSMOS_CONNECTION_POOL_TCP_KEEPALIVE_INTERVAL_MS",
1_000,
u64::MAX,
)?
.or(Some(Duration::from_secs(1)));
let tcp_keepalive_retries = resolve_optional_from_env(
self.tcp_keepalive_retries,
env.tcp_keepalive_retries,
"AZURE_COSMOS_CONNECTION_POOL_TCP_KEEPALIVE_RETRIES",
ValidationBounds::range(1_u32, 255_u32),
)?;
Ok(ConnectionPoolOptions {
proxy_allowed: resolve_from_env(
self.proxy_allowed,
env.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,
server_certificate_validation: self
.server_certificate_validation
.or(env.server_certificate_validation)
.unwrap_or(ServerCertificateValidation::Required),
local_address: self.local_address.or(env.local_address),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn env_config_from_env_vars_maps_names_to_fields() {
let cfg = ConnectionPoolOptionsBuilder::from_env_vars(|key| match key {
"AZURE_COSMOS_CONNECTION_POOL_IS_HTTP2_ALLOWED" => Ok("false".to_string()),
"AZURE_COSMOS_CONNECTION_POOL_MIN_CONNECT_TIMEOUT_MS" => Ok("250".to_string()),
"AZURE_COSMOS_CONNECTION_POOL_MAX_IDLE_CONNECTIONS_PER_ENDPOINT" => {
Ok("4096".to_string())
}
"AZURE_COSMOS_CONNECTION_POOL_HTTP2_CONSECUTIVE_FAILURE_THRESHOLD" => {
Ok("7".to_string())
}
"AZURE_COSMOS_LOCAL_ADDRESS" => Ok("127.0.0.1".to_string()),
_ => Err(std::env::VarError::NotPresent),
});
assert_eq!(cfg.is_http2_allowed, Some(false));
assert_eq!(cfg.min_connect_timeout, Some(Duration::from_millis(250)));
assert_eq!(cfg.max_idle_connections_per_endpoint, Some(4096));
assert_eq!(cfg.http2_consecutive_failure_threshold, Some(7));
assert_eq!(
cfg.local_address,
Some("127.0.0.1".parse().expect("valid IP")),
);
assert!(cfg.max_connect_timeout.is_none());
assert!(cfg.proxy_allowed.is_none());
}
#[test]
fn env_config_from_env_vars_is_lenient_on_malformed_value() {
let cfg = ConnectionPoolOptionsBuilder::from_env_vars(|key| match key {
"AZURE_COSMOS_CONNECTION_POOL_MIN_CONNECT_TIMEOUT_MS" => Ok("not-a-number".to_string()),
_ => Err(std::env::VarError::NotPresent),
});
assert!(
cfg.min_connect_timeout.is_none(),
"an unparseable env value must be ignored (None), not error",
);
}
#[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.server_certificate_validation(),
ServerCertificateValidation::Required
);
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_server_certificate_validation(ServerCertificateValidation::RequiredUnlessEmulator)
.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.server_certificate_validation(),
ServerCertificateValidation::RequiredUnlessEmulator
);
}
#[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);
}
}