Skip to main content

providers_kit/config/
transport.rs

1use std::time::Duration;
2
3use crate::KitError;
4use crate::config::RetryPolicy;
5use crate::error::ValidationViolation;
6
7/// Shared HTTP transport settings used by provider backends.
8///
9/// These settings feed the transport-owned checked-request layer. Replayable
10/// requests can use [`RetryPolicy`] within the same logical request deadline,
11/// while non-replayable requests still use the same transport defaults without
12/// retrying.
13///
14/// # Examples
15/// ```rust
16/// let transport = providers_kit::config::TransportConfig::default();
17/// let _ = transport;
18/// ```
19#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct TransportConfig {
21    request_timeout: Duration,
22    connect_timeout: Duration,
23    pool_idle_timeout: Duration,
24    retry: RetryPolicy,
25}
26
27impl Default for TransportConfig {
28    fn default() -> Self {
29        Self {
30            request_timeout: Duration::from_secs(120),
31            connect_timeout: Duration::from_secs(15),
32            pool_idle_timeout: Duration::from_secs(90),
33            retry: RetryPolicy::default(),
34        }
35    }
36}
37
38impl TransportConfig {
39    /// Creates validated transport timing and retry policy.
40    ///
41    /// # Errors
42    ///
43    /// Returns a configuration error when any timeout is zero or cannot fit the monotonic clock range. Configuration does not authorize retries for non-replayable operations.
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// use providers_kit::config::{RetryPolicy, TransportConfig};
49    /// use std::time::Duration;
50    /// let transport = TransportConfig::new(Duration::from_secs(60), Duration::from_secs(5), Duration::from_secs(30), RetryPolicy::default())?;
51    /// assert_ne!(transport, TransportConfig::default());
52    /// assert!(TransportConfig::new(Duration::ZERO, Duration::from_secs(5), Duration::from_secs(30), RetryPolicy::default()).is_err());
53    ///
54    /// # Ok::<(), providers_kit::KitError>(())
55    /// ```
56    ///
57    pub fn new(
58        request_timeout: Duration,
59        connect_timeout: Duration,
60        pool_idle_timeout: Duration,
61        retry: RetryPolicy,
62    ) -> Result<Self, KitError> {
63        let now = std::time::Instant::now();
64        for (field, timeout) in [
65            ("transport.request_timeout", request_timeout),
66            ("transport.connect_timeout", connect_timeout),
67            ("transport.pool_idle_timeout", pool_idle_timeout),
68        ] {
69            if timeout.is_zero() {
70                return Err(KitError::config(
71                    ValidationViolation::OutOfRange {
72                        field,
73                        rule: "must be positive",
74                    },
75                    "transport timeouts must be non-zero",
76                ));
77            }
78            if now.checked_add(timeout).is_none() {
79                return Err(KitError::config(
80                    ValidationViolation::OutOfRange {
81                        field,
82                        rule: "must fit the monotonic clock range",
83                    },
84                    "transport timeout exceeds the supported clock range",
85                ));
86            }
87        }
88        Ok(Self {
89            request_timeout,
90            connect_timeout,
91            pool_idle_timeout,
92            retry,
93        })
94    }
95
96    #[must_use]
97    /// Returns the total deadline for one logical HTTP request.
98    pub(crate) const fn request_timeout(&self) -> Duration {
99        self.request_timeout
100    }
101
102    #[must_use]
103    /// Returns the TCP/TLS connection-establishment bound.
104    pub(crate) const fn connect_timeout(&self) -> Duration {
105        self.connect_timeout
106    }
107
108    #[must_use]
109    /// Returns the idle pooled-connection retention bound.
110    pub(crate) const fn pool_idle_timeout(&self) -> Duration {
111        self.pool_idle_timeout
112    }
113
114    #[must_use]
115    /// Returns the replay-safe retry policy.
116    pub(crate) const fn retry(&self) -> &RetryPolicy {
117        &self.retry
118    }
119}