Skip to main content

cloud_sdk_reqwest/shared/
config.rs

1use core::fmt;
2use std::time::Duration;
3
4use reqwest::header::HeaderValue;
5
6/// Maximum configured timeout accepted by the adapter.
7pub const MAX_TIMEOUT_SECONDS: u64 = 300;
8
9/// Timeout policy validation error.
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum TimeoutError {
12    /// Every timeout must be nonzero.
13    Zero,
14    /// Every timeout is capped at [`MAX_TIMEOUT_SECONDS`].
15    TooLong,
16    /// The connect timeout must not exceed the total timeout.
17    ExceedsTotal,
18}
19
20impl_static_error!(TimeoutError,
21    Self::Zero => "timeout must be nonzero",
22    Self::TooLong => "timeout exceeds the configured limit",
23    Self::ExceedsTotal => "connection timeout exceeds total timeout",
24);
25
26/// Explicit total-request and connection timeout policy.
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub struct RequestTimeouts {
29    total: Duration,
30    connect: Duration,
31}
32
33impl RequestTimeouts {
34    /// Validates all timeout dimensions.
35    pub fn new(total: Duration, connect: Duration) -> Result<Self, TimeoutError> {
36        if total.is_zero() || connect.is_zero() {
37            return Err(TimeoutError::Zero);
38        }
39        let maximum = Duration::from_secs(MAX_TIMEOUT_SECONDS);
40        if total > maximum || connect > maximum {
41            return Err(TimeoutError::TooLong);
42        }
43        if connect > total {
44            return Err(TimeoutError::ExceedsTotal);
45        }
46        Ok(Self { total, connect })
47    }
48
49    pub(crate) const fn total(self) -> Duration {
50        self.total
51    }
52
53    pub(crate) const fn connect(self) -> Duration {
54        self.connect
55    }
56}
57
58/// User-agent validation error.
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
60pub enum UserAgentError {
61    /// User agents must not be empty.
62    Empty,
63    /// User agents are capped at 256 bytes.
64    TooLong,
65    /// The value is not a valid HTTP header value.
66    Invalid,
67}
68
69impl_static_error!(UserAgentError,
70    Self::Empty => "user agent is empty",
71    Self::TooLong => "user agent exceeds the length limit",
72    Self::Invalid => "user agent is invalid",
73);
74
75/// Validated, non-secret user-agent header value.
76#[derive(Clone)]
77pub struct UserAgent {
78    pub(crate) value: HeaderValue,
79}
80
81impl UserAgent {
82    /// Validates a user-agent value.
83    pub fn new(value: &str) -> Result<Self, UserAgentError> {
84        if value.is_empty() {
85            return Err(UserAgentError::Empty);
86        }
87        if value.len() > 256 {
88            return Err(UserAgentError::TooLong);
89        }
90        let value = HeaderValue::from_str(value).map_err(|_| UserAgentError::Invalid)?;
91        Ok(Self { value })
92    }
93}
94
95impl fmt::Debug for UserAgent {
96    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
97        formatter.write_str("UserAgent([validated])")
98    }
99}