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}