Skip to main content

rs_ali_sts/
config.rs

1use std::time::Duration;
2
3use crate::sign::SignatureVersion;
4
5/// Configuration for the STS client.
6///
7/// Use [`ClientConfig::default()`] to create a new configuration,
8/// then use the `with_*` methods to customize.
9#[derive(Debug, Clone)]
10#[non_exhaustive]
11pub struct ClientConfig {
12    /// STS API endpoint URL.
13    pub endpoint: String,
14
15    /// HTTP request timeout.
16    pub timeout: Duration,
17
18    /// Maximum number of concurrent API requests.
19    pub max_concurrent_requests: usize,
20
21    /// Connection timeout for establishing TCP connections.
22    pub connect_timeout: Duration,
23
24    /// Pool idle timeout before closing idle connections.
25    pub pool_idle_timeout: Duration,
26
27    /// Maximum number of idle connections per host.
28    pub pool_max_idle_per_host: usize,
29
30    /// TCP keepalive duration.
31    pub tcp_keepalive: Option<Duration>,
32
33    /// Response format (always "JSON").
34    pub(crate) format: &'static str,
35
36    /// API version (always "2015-04-01").
37    pub(crate) api_version: &'static str,
38
39    /// Signature version to use for API requests.
40    pub signature_version: SignatureVersion,
41}
42
43/// Default HTTP request timeout (30 seconds).
44///
45/// This is the maximum time to wait for an API response before
46/// timing out. Most STS operations complete within a few seconds.
47pub const DEFAULT_TIMEOUT_SECS: u64 = 30;
48
49/// Default maximum number of concurrent API requests.
50///
51/// This limit prevents overwhelming the STS server with too many
52/// simultaneous requests.
53pub const DEFAULT_MAX_CONCURRENT_REQUESTS: usize = 10;
54
55/// Default TCP connection timeout (10 seconds).
56///
57/// This is the maximum time to establish a new TCP connection
58/// to the STS server.
59pub const DEFAULT_CONNECT_TIMEOUT_SECS: u64 = 10;
60
61/// Default connection pool idle timeout (90 seconds).
62///
63/// Idle connections in the pool are closed after this duration
64/// to free up resources.
65pub const DEFAULT_POOL_IDLE_TIMEOUT_SECS: u64 = 90;
66
67/// Default maximum idle connections per host (10).
68///
69/// Maximum number of idle connections to keep in the pool
70/// for each unique host.
71pub const DEFAULT_POOL_MAX_IDLE_PER_HOST: usize = 10;
72
73/// Default TCP keepalive duration (60 seconds).
74///
75/// Periodically sends keepalive packets to maintain long-lived
76/// connections through network equipment that may drop idle connections.
77pub const DEFAULT_TCP_KEEPALIVE_SECS: u64 = 60;
78
79/// Minimum token validity duration in seconds (15 minutes).
80///
81/// STS temporary credentials are valid for a minimum of 900 seconds
82/// (15 minutes) and a maximum of 3600 seconds (1 hour).
83pub const MIN_DURATION_SECONDS: u64 = 900;
84
85/// Maximum token validity duration in seconds (1 hour).
86///
87/// STS temporary credentials expire after a maximum of 3600 seconds
88/// (1 hour) from the time of issuance.
89pub const MAX_DURATION_SECONDS: u64 = 3600;
90
91/// Minimum role session name length (1 character).
92pub const MIN_ROLE_SESSION_NAME_LENGTH: usize = 1;
93
94/// Maximum role session name length (32 characters).
95pub const MAX_ROLE_SESSION_NAME_LENGTH: usize = 32;
96
97impl Default for ClientConfig {
98    fn default() -> Self {
99        Self {
100            endpoint: "https://sts.aliyuncs.com".to_string(),
101            timeout: Duration::from_secs(DEFAULT_TIMEOUT_SECS),
102            max_concurrent_requests: DEFAULT_MAX_CONCURRENT_REQUESTS,
103            connect_timeout: Duration::from_secs(DEFAULT_CONNECT_TIMEOUT_SECS),
104            pool_idle_timeout: Duration::from_secs(DEFAULT_POOL_IDLE_TIMEOUT_SECS),
105            pool_max_idle_per_host: DEFAULT_POOL_MAX_IDLE_PER_HOST,
106            tcp_keepalive: Some(Duration::from_secs(DEFAULT_TCP_KEEPALIVE_SECS)),
107            format: "JSON",
108            api_version: "2015-04-01",
109            signature_version: SignatureVersion::default(),
110        }
111    }
112}
113
114impl ClientConfig {
115    /// Creates a new configuration with a custom endpoint.
116    pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
117        self.endpoint = endpoint.into();
118        self
119    }
120
121    /// Sets the HTTP request timeout.
122    ///
123    /// # Arguments
124    ///
125    /// * `timeout` - The timeout duration for HTTP requests
126    ///
127    /// # Example
128    ///
129    /// ```rust
130    /// # use rs_ali_sts::ClientConfig;
131    /// # use std::time::Duration;
132    /// let config = ClientConfig::default().with_timeout(Duration::from_secs(60));
133    /// ```
134    pub fn with_timeout(mut self, timeout: Duration) -> Self {
135        self.timeout = timeout;
136        self
137    }
138
139    /// Sets the maximum number of concurrent API requests.
140    ///
141    /// This limit helps prevent overwhelming the server and manages resource usage.
142    /// When the limit is reached, additional requests will wait until a slot becomes available.
143    ///
144    /// # Arguments
145    ///
146    /// * `max` - Maximum number of concurrent requests (must be > 0)
147    ///
148    /// # Panics
149    ///
150    /// Panics if `max` is 0.
151    ///
152    /// # Example
153    ///
154    /// ```rust
155    /// # use rs_ali_sts::ClientConfig;
156    /// let config = ClientConfig::default().with_max_concurrent_requests(5);
157    /// ```
158    pub fn with_max_concurrent_requests(mut self, max: usize) -> Self {
159        assert!(max > 0, "max_concurrent_requests must be greater than 0");
160        self.max_concurrent_requests = max;
161        self
162    }
163
164    /// Sets the connection timeout for establishing TCP connections.
165    pub fn with_connect_timeout(mut self, timeout: Duration) -> Self {
166        self.connect_timeout = timeout;
167        self
168    }
169
170    /// Sets the pool idle timeout before closing idle connections.
171    pub fn with_pool_idle_timeout(mut self, timeout: Duration) -> Self {
172        self.pool_idle_timeout = timeout;
173        self
174    }
175
176    /// Sets the maximum number of idle connections per host.
177    pub fn with_pool_max_idle_per_host(mut self, max: usize) -> Self {
178        self.pool_max_idle_per_host = max;
179        self
180    }
181
182    /// Sets the TCP keepalive duration. Use `None` to disable.
183    pub fn with_tcp_keepalive(mut self, duration: Option<Duration>) -> Self {
184        self.tcp_keepalive = duration;
185        self
186    }
187
188    /// Sets the signature version to use for API requests.
189    ///
190    /// # Arguments
191    ///
192    /// * `version` - The signature version (V1_0 for SHA-1, V2_0 for SHA-256)
193    ///
194    /// # Example
195    ///
196    /// ```rust
197    /// # use rs_ali_sts::{ClientConfig, SignatureVersion};
198    /// let config = ClientConfig::default()
199    ///     .with_signature_version(SignatureVersion::V2_0);
200    /// ```
201    pub fn with_signature_version(mut self, version: SignatureVersion) -> Self {
202        self.signature_version = version;
203        self
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn default_config() {
213        let config = ClientConfig::default();
214        assert_eq!(config.endpoint, "https://sts.aliyuncs.com");
215        assert_eq!(config.timeout, Duration::from_secs(DEFAULT_TIMEOUT_SECS));
216        assert_eq!(
217            config.max_concurrent_requests,
218            DEFAULT_MAX_CONCURRENT_REQUESTS
219        );
220        assert_eq!(
221            config.connect_timeout,
222            Duration::from_secs(DEFAULT_CONNECT_TIMEOUT_SECS)
223        );
224        assert_eq!(
225            config.pool_idle_timeout,
226            Duration::from_secs(DEFAULT_POOL_IDLE_TIMEOUT_SECS)
227        );
228        assert_eq!(
229            config.pool_max_idle_per_host,
230            DEFAULT_POOL_MAX_IDLE_PER_HOST
231        );
232        assert_eq!(
233            config.tcp_keepalive,
234            Some(Duration::from_secs(DEFAULT_TCP_KEEPALIVE_SECS))
235        );
236        assert_eq!(config.format, "JSON");
237        assert_eq!(config.api_version, "2015-04-01");
238        assert_eq!(config.signature_version, SignatureVersion::V1_0);
239    }
240
241    #[test]
242    fn custom_endpoint() {
243        let config =
244            ClientConfig::default().with_endpoint("https://sts-vpc.cn-hangzhou.aliyuncs.com");
245        assert_eq!(config.endpoint, "https://sts-vpc.cn-hangzhou.aliyuncs.com");
246    }
247
248    #[test]
249    fn custom_timeout() {
250        use std::time::Duration;
251        let config = ClientConfig::default().with_timeout(Duration::from_secs(60));
252        assert_eq!(config.timeout, Duration::from_secs(60));
253    }
254
255    #[test]
256    fn custom_max_concurrent_requests() {
257        let config = ClientConfig::default().with_max_concurrent_requests(5);
258        assert_eq!(config.max_concurrent_requests, 5);
259    }
260
261    #[test]
262    #[should_panic(expected = "max_concurrent_requests must be greater than 0")]
263    fn zero_max_concurrent_requests_panics() {
264        ClientConfig::default().with_max_concurrent_requests(0);
265    }
266
267    #[test]
268    fn custom_connect_timeout() {
269        let config = ClientConfig::default().with_connect_timeout(Duration::from_secs(5));
270        assert_eq!(config.connect_timeout, Duration::from_secs(5));
271    }
272
273    #[test]
274    fn custom_pool_idle_timeout() {
275        let config = ClientConfig::default().with_pool_idle_timeout(Duration::from_secs(120));
276        assert_eq!(config.pool_idle_timeout, Duration::from_secs(120));
277    }
278
279    #[test]
280    fn custom_pool_max_idle_per_host() {
281        let config = ClientConfig::default().with_pool_max_idle_per_host(20);
282        assert_eq!(config.pool_max_idle_per_host, 20);
283    }
284
285    #[test]
286    fn custom_tcp_keepalive() {
287        let config = ClientConfig::default().with_tcp_keepalive(Some(Duration::from_secs(30)));
288        assert_eq!(config.tcp_keepalive, Some(Duration::from_secs(30)));
289    }
290
291    #[test]
292    fn disable_tcp_keepalive() {
293        let config = ClientConfig::default().with_tcp_keepalive(None);
294        assert_eq!(config.tcp_keepalive, None);
295    }
296
297    #[test]
298    fn custom_signature_version_v1() {
299        let config = ClientConfig::default().with_signature_version(SignatureVersion::V1_0);
300        assert_eq!(config.signature_version, SignatureVersion::V1_0);
301    }
302
303    #[test]
304    fn custom_signature_version_v2() {
305        let config = ClientConfig::default().with_signature_version(SignatureVersion::V2_0);
306        assert_eq!(config.signature_version, SignatureVersion::V2_0);
307    }
308}