cloud_sdk_reqwest/blocking/
client.rs1use core::fmt;
2use std::sync::Arc;
3
4use cloud_sdk::authentication::{
5 AuthenticatedRequest, BlockingAuthenticatedTransport, CredentialGeneration, CredentialLifetime,
6};
7use cloud_sdk::transport::{
8 BoundTransport, EndpointIdentity, EndpointIdentityError, ResponseStorageSanitizer,
9 ResponseWriter, TransportFailure,
10};
11use cloud_sdk_sanitization::{SecretBuffer, sanitize_bytes};
12
13use super::RawBlockingClient;
14use crate::shared::{
15 AuthenticatedTransportFailure, BearerCredential, BearerCredentialScope,
16 BearerCredentialSnapshot, BearerRefreshHandoff, BearerToken, CredentialStateError,
17 CredentialStore, CredentialUpdateError, HttpsEndpoint, TokenRefreshError, TokenRotationError,
18 TransportError, map_authentication_error, validate_bearer_authentication,
19};
20
21#[derive(Clone)]
23pub struct BlockingClient {
24 client: RawBlockingClient,
25 endpoint: HttpsEndpoint,
26 scope: Arc<BearerCredentialScope>,
27 credentials: Arc<CredentialStore>,
28 allow_insecure_loopback: bool,
29}
30
31impl BlockingClient {
32 pub(super) fn new(
33 client: RawBlockingClient,
34 endpoint: HttpsEndpoint,
35 credential: BearerCredential,
36 allow_insecure_loopback: bool,
37 ) -> Self {
38 Self {
39 client,
40 endpoint,
41 scope: Arc::new(credential.scope),
42 credentials: Arc::new(CredentialStore::new(credential.token, credential.lifetime)),
43 allow_insecure_loopback,
44 }
45 }
46
47 pub fn credential_snapshot(&self) -> Result<BearerCredentialSnapshot, CredentialStateError> {
49 self.credentials.snapshot()
50 }
51
52 pub fn rotate_bearer_token(
57 &self,
58 replacement: BearerToken,
59 ) -> Result<CredentialGeneration, CredentialUpdateError> {
60 self.credentials.rotate(replacement)
61 }
62
63 pub fn rotate_bearer_token_with_lifetime(
65 &self,
66 replacement: BearerToken,
67 lifetime: CredentialLifetime,
68 ) -> Result<CredentialGeneration, CredentialUpdateError> {
69 self.credentials.rotate_with_lifetime(replacement, lifetime)
70 }
71
72 pub fn rotate_bearer_token_from_mut_bytes(
74 &self,
75 source: &mut [u8],
76 ) -> Result<CredentialGeneration, TokenRotationError> {
77 self.credentials.rotate_from_mut_bytes(source)
78 }
79
80 pub fn rotate_bearer_token_from_secret_buffer(
82 &self,
83 source: SecretBuffer<'_>,
84 ) -> Result<CredentialGeneration, TokenRotationError> {
85 self.credentials.rotate_from_secret_buffer(source)
86 }
87
88 pub fn rotate_bearer_token_from_mut_bytes_with_lifetime(
90 &self,
91 source: &mut [u8],
92 lifetime: CredentialLifetime,
93 ) -> Result<CredentialGeneration, TokenRotationError> {
94 self.credentials
95 .rotate_from_mut_bytes_with_lifetime(source, lifetime)
96 }
97
98 pub fn rotate_bearer_token_from_secret_buffer_with_lifetime(
100 &self,
101 source: SecretBuffer<'_>,
102 lifetime: CredentialLifetime,
103 ) -> Result<CredentialGeneration, TokenRotationError> {
104 self.credentials
105 .rotate_from_secret_buffer_with_lifetime(source, lifetime)
106 }
107
108 pub fn refresh_bearer_token(
110 &self,
111 handoff: BearerRefreshHandoff,
112 replacement: BearerToken,
113 ) -> Result<CredentialGeneration, TokenRefreshError> {
114 self.credentials.refresh(handoff, replacement)
115 }
116
117 pub fn refresh_bearer_token_with_lifetime(
119 &self,
120 handoff: BearerRefreshHandoff,
121 replacement: BearerToken,
122 lifetime: CredentialLifetime,
123 ) -> Result<CredentialGeneration, TokenRefreshError> {
124 self.credentials
125 .refresh_with_lifetime(handoff, replacement, lifetime)
126 }
127
128 pub fn refresh_bearer_token_from_mut_bytes(
130 &self,
131 handoff: BearerRefreshHandoff,
132 source: &mut [u8],
133 ) -> Result<CredentialGeneration, TokenRefreshError> {
134 self.credentials.refresh_from_mut_bytes(handoff, source)
135 }
136
137 pub fn refresh_bearer_token_from_secret_buffer(
139 &self,
140 handoff: BearerRefreshHandoff,
141 source: SecretBuffer<'_>,
142 ) -> Result<CredentialGeneration, TokenRefreshError> {
143 self.credentials.refresh_from_secret_buffer(handoff, source)
144 }
145
146 pub fn refresh_bearer_token_from_mut_bytes_with_lifetime(
148 &self,
149 handoff: BearerRefreshHandoff,
150 source: &mut [u8],
151 lifetime: CredentialLifetime,
152 ) -> Result<CredentialGeneration, TokenRefreshError> {
153 self.credentials
154 .refresh_from_mut_bytes_with_lifetime(handoff, source, lifetime)
155 }
156
157 pub fn refresh_bearer_token_from_secret_buffer_with_lifetime(
159 &self,
160 handoff: BearerRefreshHandoff,
161 source: SecretBuffer<'_>,
162 lifetime: CredentialLifetime,
163 ) -> Result<CredentialGeneration, TokenRefreshError> {
164 self.credentials
165 .refresh_from_secret_buffer_with_lifetime(handoff, source, lifetime)
166 }
167
168 fn send_inner(
169 &self,
170 authenticated: AuthenticatedRequest<'_, '_>,
171 response_writer: &mut ResponseWriter<'_>,
172 ) -> Result<(), AuthenticatedTransportFailure> {
173 let endpoint_identity = self.endpoint.identity().map_err(|_| {
174 TransportFailure::not_sent(TransportError::AuthenticationEndpointMismatch)
175 })?;
176 validate_bearer_authentication(
177 endpoint_identity,
178 &self.scope,
179 authenticated.policy(),
180 self.allow_insecure_loopback,
181 )
182 .map_err(|error| TransportFailure::not_sent(map_authentication_error(error)))?;
183 let token_snapshot = self
184 .credentials
185 .snapshot()
186 .map_err(|_| TransportFailure::not_sent(TransportError::CredentialStateUnavailable))?;
187 let authorization = token_snapshot
188 .header_value()
189 .map_err(|_| TransportFailure::not_sent(TransportError::HeaderRejected))?;
190 drop(token_snapshot);
191 self.client
192 .execute_authenticated(
193 authenticated.transport_request(),
194 authenticated.response_policy(),
195 authorization,
196 response_writer,
197 )
198 .map_err(|failure| failure.map(TransportError::RawHttp))
199 }
200}
201
202impl BlockingAuthenticatedTransport for BlockingClient {
203 type Error = AuthenticatedTransportFailure;
204
205 fn send_authenticated(
206 &self,
207 request: AuthenticatedRequest<'_, '_>,
208 response: &mut ResponseWriter<'_>,
209 ) -> Result<(), Self::Error> {
210 self.send_inner(request, response)
211 }
212}
213
214impl ResponseStorageSanitizer for BlockingClient {
215 fn sanitize_response_storage(&self, response_storage: &mut [u8]) {
216 sanitize_bytes(response_storage);
217 }
218}
219
220impl BoundTransport for BlockingClient {
221 fn endpoint_identity(&self) -> Result<EndpointIdentity<'_>, EndpointIdentityError> {
222 self.endpoint.identity()
223 }
224}
225
226impl fmt::Debug for BlockingClient {
227 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
228 formatter
229 .debug_struct("BlockingClient")
230 .field("endpoint", &"[redacted]")
231 .field("scope", &"[redacted]")
232 .field("credentials", &"[redacted]")
233 .finish_non_exhaustive()
234 }
235}