use std::time::Duration;
use schemars::JsonSchema;
use serde::Deserialize;
use crate::plugins::traffic_shaping::Http2Config;
const DEFAULT_POOL_IDLE_TIMEOUT: Duration = Duration::from_secs(15);
pub(crate) const DEFAULT_HTTP2_KEEP_ALIVE_TIMEOUT: Duration = Duration::from_secs(20);
#[derive(PartialEq, Debug, Clone, Default, Deserialize, JsonSchema, buildstructor::Builder)]
#[serde(deny_unknown_fields, default)]
pub(crate) struct Client {
pub(crate) experimental_http2: Option<Http2Config>,
pub(crate) dns_resolution_strategy: Option<DnsResolutionStrategy>,
#[serde(
deserialize_with = "humantime_serde::deserialize",
default = "default_pool_idle_timeout"
)]
#[schemars(with = "String", default = "default_pool_idle_timeout")]
pub(crate) pool_idle_timeout: Option<Duration>,
#[serde(deserialize_with = "humantime_serde::deserialize", default)]
#[schemars(with = "Option<String>", default)]
pub(crate) experimental_http2_keep_alive_interval: Option<Duration>,
#[serde(deserialize_with = "humantime_serde::deserialize", default)]
#[schemars(with = "Option<String>", default)]
pub(crate) experimental_http2_keep_alive_timeout: Option<Duration>,
}
pub(crate) fn default_pool_idle_timeout() -> Option<Duration> {
Some(DEFAULT_POOL_IDLE_TIMEOUT)
}
#[derive(PartialEq, Default, Debug, Clone, Copy, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub(crate) enum DnsResolutionStrategy {
Ipv4Only,
Ipv6Only,
Ipv4AndIpv6,
Ipv6ThenIpv4,
#[default]
Ipv4ThenIpv6,
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use rstest::rstest;
use super::*;
#[rstest]
#[case::humantime_seconds("pool_idle_timeout: 30s", Some(Duration::from_secs(30)))]
#[case::humantime_millis("pool_idle_timeout: 500ms", Some(Duration::from_millis(500)))]
#[case::humantime_minutes("pool_idle_timeout: 2m", Some(Duration::from_secs(120)))]
#[case::explicit_null("pool_idle_timeout: null", None)]
fn test_pool_idle_timeout_deserialization(
#[case] yaml: &str,
#[case] expected: Option<Duration>,
) {
let client: Client = serde_yaml::from_str(yaml).unwrap();
assert_eq!(client.pool_idle_timeout, expected);
}
#[test]
fn test_pool_idle_timeout_default_when_omitted() {
let client: Client = serde_yaml::from_str("{}").unwrap();
assert_eq!(client.pool_idle_timeout, Some(DEFAULT_POOL_IDLE_TIMEOUT));
}
#[test]
fn test_pool_idle_timeout_default_value() {
assert_eq!(DEFAULT_POOL_IDLE_TIMEOUT, Duration::from_secs(15));
assert_eq!(default_pool_idle_timeout(), Some(Duration::from_secs(15)));
}
#[test]
fn test_client_default_has_pool_idle_timeout() {
let client = Client::default();
assert_eq!(client.pool_idle_timeout, None);
let client = Client::builder().build();
assert_eq!(client.pool_idle_timeout, None);
}
#[test]
fn test_client_deny_unknown_fields() {
let result: Result<Client, _> = serde_yaml::from_str("bogus_field: true");
assert!(result.is_err());
}
#[rstest]
#[case::humantime_seconds(
"experimental_http2_keep_alive_interval: 30s",
Some(Duration::from_secs(30))
)]
#[case::humantime_millis(
"experimental_http2_keep_alive_interval: 500ms",
Some(Duration::from_millis(500))
)]
#[case::explicit_null("experimental_http2_keep_alive_interval: null", None)]
#[case::omitted("{}", None)]
fn test_keep_alive_interval_deserialization(
#[case] yaml: &str,
#[case] expected: Option<Duration>,
) {
let client: Client = serde_yaml::from_str(yaml).unwrap();
assert_eq!(client.experimental_http2_keep_alive_interval, expected);
}
#[rstest]
#[case::humantime_seconds(
"experimental_http2_keep_alive_timeout: 10s",
Some(Duration::from_secs(10))
)]
#[case::humantime_millis(
"experimental_http2_keep_alive_timeout: 500ms",
Some(Duration::from_millis(500))
)]
#[case::explicit_null("experimental_http2_keep_alive_timeout: null", None)]
#[case::omitted("{}", None)]
fn test_keep_alive_timeout_deserialization(
#[case] yaml: &str,
#[case] expected: Option<Duration>,
) {
let client: Client = serde_yaml::from_str(yaml).unwrap();
assert_eq!(client.experimental_http2_keep_alive_timeout, expected);
}
}