Skip to main content

cloud_sdk_reqwest/asynchronous/
basic_client.rs

1use core::fmt;
2use std::sync::Arc;
3
4use cloud_sdk::authentication::{
5    AsyncAuthenticatedTransport, AuthenticatedRequest, BoundCredentialTransport, CredentialBinding,
6};
7use cloud_sdk::transport::{
8    AsyncResponseStaging, BoundTransport, EndpointIdentity, EndpointIdentityError,
9    ResponseCompletion, ResponseStorageSanitizer, TransportFailure,
10};
11use cloud_sdk_sanitization::sanitize_bytes;
12
13use crate::shared::{
14    AuthenticatedTransportFailure, BasicCredential, HttpsEndpoint, TransportError,
15    map_authentication_error, validate_basic_authentication,
16};
17
18use super::RawAsyncClient;
19
20/// Hardened provider-neutral reqwest asynchronous Basic-auth transport.
21#[derive(Clone)]
22pub struct AsyncBasicClient {
23    client: RawAsyncClient,
24    endpoint: HttpsEndpoint,
25    credential: Arc<BasicCredential>,
26    allow_insecure_loopback: bool,
27}
28
29impl AsyncBasicClient {
30    pub(super) fn new(
31        client: RawAsyncClient,
32        endpoint: HttpsEndpoint,
33        credential: BasicCredential,
34        allow_insecure_loopback: bool,
35    ) -> Self {
36        Self {
37            client,
38            endpoint,
39            credential: Arc::new(credential),
40            allow_insecure_loopback,
41        }
42    }
43
44    async fn send_inner<'writer, 'buffer>(
45        &self,
46        authenticated: AuthenticatedRequest<'_, '_>,
47        response: AsyncResponseStaging<'writer, 'buffer>,
48    ) -> Result<ResponseCompletion, AuthenticatedTransportFailure> {
49        let endpoint = self.endpoint.identity().map_err(|_| {
50            TransportFailure::not_sent(TransportError::AuthenticationEndpointMismatch)
51        })?;
52        validate_basic_authentication(
53            endpoint,
54            self.credential.scope(),
55            authenticated.policy(),
56            self.allow_insecure_loopback,
57        )
58        .map_err(|error| TransportFailure::not_sent(map_authentication_error(error)))?;
59        let authorization = self
60            .credential
61            .header_value()
62            .map_err(|_| TransportFailure::not_sent(TransportError::HeaderRejected))?;
63        self.client
64            .execute_authenticated(
65                authenticated.transport_request(),
66                authenticated.response_policy(),
67                authorization,
68                response,
69            )
70            .await
71            .map_err(|failure| failure.map(TransportError::RawHttp))
72    }
73}
74
75impl AsyncAuthenticatedTransport for AsyncBasicClient {
76    type Error = AuthenticatedTransportFailure;
77
78    async fn send_authenticated<'transport, 'request, 'policy, 'writer, 'buffer>(
79        &'transport self,
80        request: AuthenticatedRequest<'request, 'policy>,
81        response: AsyncResponseStaging<'writer, 'buffer>,
82    ) -> Result<ResponseCompletion, Self::Error>
83    where
84        'transport: 'writer,
85        'request: 'writer,
86        'policy: 'writer,
87        'buffer: 'writer,
88    {
89        self.send_inner(request, response).await
90    }
91}
92
93impl ResponseStorageSanitizer for AsyncBasicClient {
94    fn sanitize_response_storage(&self, response_storage: &mut [u8]) {
95        sanitize_bytes(response_storage);
96    }
97}
98
99impl BoundTransport for AsyncBasicClient {
100    fn endpoint_identity(&self) -> Result<EndpointIdentity<'_>, EndpointIdentityError> {
101        self.endpoint.identity()
102    }
103}
104
105impl BoundCredentialTransport for AsyncBasicClient {
106    fn credential_binding(&self) -> CredentialBinding {
107        self.credential.binding()
108    }
109}
110
111impl fmt::Debug for AsyncBasicClient {
112    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
113        formatter
114            .debug_struct("AsyncBasicClient")
115            .field("endpoint", &"[redacted]")
116            .field("credential", &"[redacted]")
117            .finish_non_exhaustive()
118    }
119}