Skip to main content

cloud_sdk_reqwest/asynchronous/
basic_client.rs

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