Skip to main content

cloud_sdk_reqwest/blocking/
config.rs

1use core::fmt;
2
3#[cfg(any(feature = "blocking-rustls", feature = "blocking-rustls-webpki-roots"))]
4use rustls::ClientConfig;
5#[cfg(feature = "blocking-rustls-webpki-roots")]
6use rustls::RootCertStore;
7#[cfg(feature = "blocking-rustls-webpki-roots")]
8use std::sync::Arc;
9
10#[cfg(not(feature = "blocking-rustls-webpki-roots"))]
11use crate::shared::platform_client_config;
12use crate::shared::{
13    BearerCredential, BuildError, HttpsEndpoint, LinkLocalHttpEndpoint, RawHyperClient,
14    RequestTimeouts, UserAgent,
15};
16
17use super::{BlockingClient, RawBlockingClient};
18
19/// Builder requiring endpoint, bearer token, user agent, and all timeout
20/// dimensions before a client can be constructed.
21pub struct BlockingClientBuilder {
22    endpoint: HttpsEndpoint,
23    credential: BearerCredential,
24    user_agent: UserAgent,
25    timeouts: RequestTimeouts,
26}
27
28/// Builder for a raw client with no credential, provider, media, or retry policy.
29pub struct RawBlockingClientBuilder {
30    endpoint: HttpsEndpoint,
31    user_agent: UserAgent,
32    timeouts: RequestTimeouts,
33}
34
35/// Builder for the credential-free raw IPv4 link-local HTTP executor.
36pub struct RawLinkLocalBlockingClientBuilder {
37    endpoint: LinkLocalHttpEndpoint,
38    user_agent: UserAgent,
39    timeouts: RequestTimeouts,
40}
41
42impl BlockingClientBuilder {
43    /// Creates a complete blocking-client configuration.
44    #[must_use]
45    pub const fn new(
46        endpoint: HttpsEndpoint,
47        credential: BearerCredential,
48        user_agent: UserAgent,
49        timeouts: RequestTimeouts,
50    ) -> Self {
51        Self {
52            endpoint,
53            credential,
54            user_agent,
55            timeouts,
56        }
57    }
58
59    /// Builds a hardened HTTPS-only client.
60    pub fn build(self) -> Result<BlockingClient, BuildError> {
61        self.build_inner(true)
62    }
63
64    fn build_inner(self, https_only: bool) -> Result<BlockingClient, BuildError> {
65        if !self.credential.scope.matches_endpoint(&self.endpoint) {
66            return Err(BuildError::CredentialEndpointMismatch);
67        }
68        let settings = ClientSettings {
69            timeouts: self.timeouts,
70        };
71        let client = configured_raw_client(
72            self.endpoint.clone(),
73            &self.user_agent,
74            settings,
75            https_only,
76        )?;
77        Ok(BlockingClient::new(
78            client,
79            self.endpoint,
80            self.credential,
81            !https_only,
82        ))
83    }
84
85    #[cfg(test)]
86    pub(super) fn build_for_loopback(self) -> Result<BlockingClient, BuildError> {
87        self.build_inner(false)
88    }
89}
90
91impl RawBlockingClientBuilder {
92    /// Creates a complete raw blocking configuration without credentials.
93    #[must_use]
94    pub const fn new(
95        endpoint: HttpsEndpoint,
96        user_agent: UserAgent,
97        timeouts: RequestTimeouts,
98    ) -> Self {
99        Self {
100            endpoint,
101            user_agent,
102            timeouts,
103        }
104    }
105
106    /// Builds an HTTPS-only executor with no implicit authorization.
107    pub fn build(self) -> Result<RawBlockingClient, BuildError> {
108        self.build_inner(true)
109    }
110
111    fn build_inner(self, https_only: bool) -> Result<RawBlockingClient, BuildError> {
112        let settings = ClientSettings {
113            timeouts: self.timeouts,
114        };
115        configured_raw_client(self.endpoint, &self.user_agent, settings, https_only)
116    }
117
118    #[cfg(test)]
119    pub(super) fn build_for_loopback(self) -> Result<RawBlockingClient, BuildError> {
120        self.build_inner(false)
121    }
122}
123
124impl RawLinkLocalBlockingClientBuilder {
125    /// Creates a direct-link-local HTTP builder with no credential path.
126    #[must_use]
127    pub const fn new(
128        endpoint: LinkLocalHttpEndpoint,
129        user_agent: UserAgent,
130        timeouts: RequestTimeouts,
131    ) -> Self {
132        Self {
133            endpoint,
134            user_agent,
135            timeouts,
136        }
137    }
138
139    /// Builds an HTTP executor restricted to the configured link-local endpoint.
140    pub fn build(self) -> Result<RawBlockingClient, BuildError> {
141        let settings = ClientSettings {
142            timeouts: self.timeouts,
143        };
144        configured_raw_client(
145            self.endpoint.into_inner(),
146            &self.user_agent,
147            settings,
148            false,
149        )
150    }
151}
152
153impl fmt::Debug for BlockingClientBuilder {
154    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
155        let mut debug = formatter.debug_struct("BlockingClientBuilder");
156        debug
157            .field("endpoint", &"[redacted]")
158            .field("credential", &"[redacted]")
159            .field("user_agent", &self.user_agent)
160            .field("timeouts", &self.timeouts);
161        debug.finish()
162    }
163}
164
165impl fmt::Debug for RawBlockingClientBuilder {
166    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
167        let mut debug = formatter.debug_struct("RawBlockingClientBuilder");
168        debug
169            .field("endpoint", &"[redacted]")
170            .field("user_agent", &self.user_agent)
171            .field("timeouts", &self.timeouts);
172        debug.finish()
173    }
174}
175
176impl fmt::Debug for RawLinkLocalBlockingClientBuilder {
177    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
178        formatter
179            .debug_struct("RawLinkLocalBlockingClientBuilder")
180            .field("endpoint", &"[redacted]")
181            .field("user_agent", &self.user_agent)
182            .field("timeouts", &self.timeouts)
183            .finish()
184    }
185}
186
187#[derive(Clone, Copy)]
188pub(super) struct ClientSettings {
189    pub(super) timeouts: RequestTimeouts,
190}
191
192pub(super) fn configured_raw_client(
193    endpoint: HttpsEndpoint,
194    user_agent: &UserAgent,
195    settings: ClientSettings,
196    https_only: bool,
197) -> Result<RawBlockingClient, BuildError> {
198    let tls_config = raw_tls_config(settings)?;
199    let client = RawHyperClient::new(
200        endpoint.clone(),
201        user_agent,
202        settings.timeouts,
203        tls_config,
204        https_only,
205    )?;
206    Ok(RawBlockingClient::new(client, endpoint))
207}
208
209#[cfg(not(feature = "blocking-rustls-webpki-roots"))]
210fn raw_tls_config(_settings: ClientSettings) -> Result<ClientConfig, BuildError> {
211    platform_client_config()
212}
213
214#[cfg(feature = "blocking-rustls-webpki-roots")]
215fn raw_tls_config(_settings: ClientSettings) -> Result<ClientConfig, BuildError> {
216    webpki_roots_client_config()
217}
218
219#[cfg(feature = "blocking-rustls-webpki-roots")]
220fn webpki_roots_client_config() -> Result<ClientConfig, BuildError> {
221    let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
222    let mut roots = RootCertStore::empty();
223    roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
224    Ok(ClientConfig::builder_with_provider(provider)
225        .with_safe_default_protocol_versions()
226        .map_err(|_| BuildError::WebPkiRootsProtocolConfigurationFailed)?
227        .with_root_certificates(roots)
228        .with_no_client_auth())
229}
230
231#[cfg(all(test, feature = "blocking-rustls-webpki-roots"))]
232pub(super) fn test_webpki_roots_configuration() -> Result<(usize, bool), BuildError> {
233    let config = webpki_roots_client_config()?;
234    Ok((webpki_roots::TLS_SERVER_ROOTS.len(), config.fips()))
235}