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(
51 /// Duration::from_secs(60),
52 /// Duration::from_secs(5),
53 /// Duration::from_secs(30),
54 /// RetryPolicy::default(),
55 /// )?;
56 /// assert_ne!(transport, TransportConfig::default());
57 /// assert!(
58 /// TransportConfig::new(
59 /// Duration::ZERO,
60 /// Duration::from_secs(5),
61 /// Duration::from_secs(30),
62 /// RetryPolicy::default()
63 /// )
64 /// .is_err()
65 /// );
66 ///
67 /// # Ok::<(), providers_kit::KitError>(())
68 /// ```
69 ///
70 pub fn new(
71 request_timeout: Duration,
72 connect_timeout: Duration,
73 pool_idle_timeout: Duration,
74 retry: RetryPolicy,
75 ) -> Result<Self, KitError> {
76 let now = std::time::Instant::now();
77 for (field, timeout) in [
78 ("transport.request_timeout", request_timeout),
79 ("transport.connect_timeout", connect_timeout),
80 ("transport.pool_idle_timeout", pool_idle_timeout),
81 ] {
82 if timeout.is_zero() {
83 return Err(KitError::config(
84 ValidationViolation::OutOfRange {
85 field,
86 rule: "must be positive",
87 },
88 "transport timeouts must be non-zero",
89 ));
90 }
91 if now.checked_add(timeout).is_none() {
92 return Err(KitError::config(
93 ValidationViolation::OutOfRange {
94 field,
95 rule: "must fit the monotonic clock range",
96 },
97 "transport timeout exceeds the supported clock range",
98 ));
99 }
100 }
101 Ok(Self {
102 request_timeout,
103 connect_timeout,
104 pool_idle_timeout,
105 retry,
106 })
107 }
108
109 #[must_use]
110 /// Returns the total deadline for one logical HTTP request.
111 pub(crate) const fn request_timeout(&self) -> Duration {
112 self.request_timeout
113 }
114
115 #[must_use]
116 /// Returns the TCP/TLS connection-establishment bound.
117 pub(crate) const fn connect_timeout(&self) -> Duration {
118 self.connect_timeout
119 }
120
121 #[must_use]
122 /// Returns the idle pooled-connection retention bound.
123 pub(crate) const fn pool_idle_timeout(&self) -> Duration {
124 self.pool_idle_timeout
125 }
126
127 #[must_use]
128 /// Returns the replay-safe retry policy.
129 pub(crate) const fn retry(&self) -> &RetryPolicy {
130 &self.retry
131 }
132}