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