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