cloud_sdk_reqwest/asynchronous/
client.rs1use core::fmt;
2use std::sync::Arc;
3
4use cloud_sdk::authentication::{
5 AsyncAuthenticatedTransport, AuthenticatedRequest, CredentialGeneration,
6};
7use cloud_sdk::transport::{
8 BoundTransport, EndpointIdentity, EndpointIdentityError, ResponseStorageSanitizer,
9 ResponseWriter, 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)),
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_from_mut_bytes(
66 &self,
67 source: &mut [u8],
68 ) -> Result<CredentialGeneration, TokenRotationError> {
69 self.credentials.rotate_from_mut_bytes(source)
70 }
71
72 pub fn rotate_bearer_token_from_secret_buffer(
74 &self,
75 source: SecretBuffer<'_>,
76 ) -> Result<CredentialGeneration, TokenRotationError> {
77 self.credentials.rotate_from_secret_buffer(source)
78 }
79
80 pub fn refresh_bearer_token(
82 &self,
83 handoff: BearerRefreshHandoff,
84 replacement: BearerToken,
85 ) -> Result<CredentialGeneration, TokenRefreshError> {
86 self.credentials.refresh(handoff, replacement)
87 }
88
89 pub fn refresh_bearer_token_from_mut_bytes(
91 &self,
92 handoff: BearerRefreshHandoff,
93 source: &mut [u8],
94 ) -> Result<CredentialGeneration, TokenRefreshError> {
95 self.credentials.refresh_from_mut_bytes(handoff, source)
96 }
97
98 pub fn refresh_bearer_token_from_secret_buffer(
100 &self,
101 handoff: BearerRefreshHandoff,
102 source: SecretBuffer<'_>,
103 ) -> Result<CredentialGeneration, TokenRefreshError> {
104 self.credentials.refresh_from_secret_buffer(handoff, source)
105 }
106
107 async fn send_inner(
108 &self,
109 authenticated: AuthenticatedRequest<'_, '_>,
110 response_writer: &mut ResponseWriter<'_>,
111 ) -> Result<(), AuthenticatedTransportFailure> {
112 let endpoint_identity = self.endpoint.identity().map_err(|_| {
113 TransportFailure::not_sent(TransportError::AuthenticationEndpointMismatch)
114 })?;
115 validate_bearer_authentication(
116 endpoint_identity,
117 &self.scope,
118 authenticated.policy(),
119 self.allow_insecure_loopback,
120 )
121 .map_err(|error| TransportFailure::not_sent(map_authentication_error(error)))?;
122 let token_snapshot = self
123 .credentials
124 .snapshot()
125 .map_err(|_| TransportFailure::not_sent(TransportError::CredentialStateUnavailable))?;
126 let authorization = token_snapshot
127 .header_value()
128 .map_err(|_| TransportFailure::not_sent(TransportError::HeaderRejected))?;
129 drop(token_snapshot);
130 self.client
131 .execute_authenticated(
132 authenticated.transport_request(),
133 authenticated.response_policy(),
134 authorization,
135 response_writer,
136 )
137 .await
138 .map_err(|failure| failure.map(TransportError::RawHttp))
139 }
140}
141
142impl AsyncAuthenticatedTransport for AsyncClient {
143 type Error = AuthenticatedTransportFailure;
144
145 async fn send_authenticated<'transport, 'request, 'policy, 'writer>(
146 &'transport self,
147 request: AuthenticatedRequest<'request, 'policy>,
148 response: &'writer mut ResponseWriter<'_>,
149 ) -> Result<(), Self::Error>
150 where
151 'transport: 'writer,
152 'request: 'writer,
153 'policy: 'writer,
154 {
155 self.send_inner(request, response).await
156 }
157}
158
159impl ResponseStorageSanitizer for AsyncClient {
160 fn sanitize_response_storage(&self, response_storage: &mut [u8]) {
161 sanitize_bytes(response_storage);
162 }
163}
164
165impl BoundTransport for AsyncClient {
166 fn endpoint_identity(&self) -> Result<EndpointIdentity<'_>, EndpointIdentityError> {
167 self.endpoint.identity()
168 }
169}
170
171impl fmt::Debug for AsyncClient {
172 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
173 formatter
174 .debug_struct("AsyncClient")
175 .field("endpoint", &"[redacted]")
176 .field("scope", &"[redacted]")
177 .field("credentials", &"[redacted]")
178 .finish_non_exhaustive()
179 }
180}