Skip to main content

cloud_sdk/authentication/
transport.rs

1use core::fmt;
2use core::future::Future;
3
4use crate::transport::{
5    AsyncExecutionError, AsyncResponseStaging, RawResponsePolicy, ResponseCompletion,
6    ResponseWriter, TransportRequest,
7};
8
9use super::AuthenticationScopePolicy;
10
11/// Request plus mandatory provider or operation-owned authentication policy.
12#[derive(Clone, Copy)]
13pub struct AuthenticatedRequest<'request, 'policy> {
14    request: TransportRequest<'request>,
15    policy: AuthenticationScopePolicy<'policy>,
16    response_policy: RawResponsePolicy<'policy>,
17}
18
19impl<'request, 'policy> AuthenticatedRequest<'request, 'policy> {
20    /// Binds a transport request to its complete authentication policy.
21    #[must_use]
22    pub const fn new(
23        request: TransportRequest<'request>,
24        policy: AuthenticationScopePolicy<'policy>,
25        response_policy: RawResponsePolicy<'policy>,
26    ) -> Self {
27        Self {
28            request,
29            policy,
30            response_policy,
31        }
32    }
33
34    /// Returns the credential-free transport request.
35    #[must_use]
36    pub const fn transport_request(self) -> TransportRequest<'request> {
37        self.request
38    }
39
40    /// Returns the complete authentication policy.
41    #[must_use]
42    pub const fn policy(self) -> AuthenticationScopePolicy<'policy> {
43        self.policy
44    }
45
46    /// Returns the complete status-class raw response policy.
47    #[must_use]
48    pub const fn response_policy(self) -> RawResponsePolicy<'policy> {
49        self.response_policy
50    }
51}
52
53impl fmt::Debug for AuthenticatedRequest<'_, '_> {
54    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
55        formatter
56            .debug_struct("AuthenticatedRequest")
57            .field("request", &self.request)
58            .field("policy", &self.policy)
59            .field("response_policy", &self.response_policy)
60            .finish()
61    }
62}
63
64/// Blocking transport that cannot execute without an authentication policy.
65pub trait BlockingAuthenticatedTransport {
66    /// Transport-specific failure.
67    type Error;
68
69    /// Validates scope and sends one authenticated request.
70    fn send_authenticated(
71        &self,
72        request: AuthenticatedRequest<'_, '_>,
73        response: &mut ResponseWriter<'_>,
74    ) -> Result<(), Self::Error>;
75}
76
77/// Executor-neutral Send async transport requiring authentication policy.
78///
79/// Implementations stage responses without commit access. Callers use
80/// [`drive_async_authenticated`].
81pub trait AsyncAuthenticatedTransport {
82    /// Transport-specific failure.
83    type Error;
84
85    /// Validates scope and stages one authenticated response.
86    fn send_authenticated<'transport, 'request, 'policy, 'writer, 'buffer>(
87        &'transport self,
88        request: AuthenticatedRequest<'request, 'policy>,
89        response: AsyncResponseStaging<'writer, 'buffer>,
90    ) -> impl Future<Output = Result<ResponseCompletion, Self::Error>> + Send + 'writer
91    where
92        'transport: 'writer,
93        'request: 'writer,
94        'policy: 'writer,
95        'buffer: 'writer;
96}
97
98/// Executor-neutral authenticated transport for `!Send` local futures.
99///
100/// Dropping the returned future leaves the response uncommitted and clears
101/// partial response state, but does not prove that the authenticated request
102/// was not delivered. Cancellation is conservatively
103/// [`DeliveryPhase::PossiblySent`](crate::transport::DeliveryPhase::PossiblySent).
104pub trait LocalAsyncAuthenticatedTransport {
105    /// Transport-specific failure.
106    type Error;
107
108    /// Validates scope and sends one authenticated request locally.
109    fn send_authenticated_local<'transport, 'request, 'policy, 'writer, 'buffer>(
110        &'transport self,
111        request: AuthenticatedRequest<'request, 'policy>,
112        response: AsyncResponseStaging<'writer, 'buffer>,
113    ) -> impl Future<Output = Result<ResponseCompletion, Self::Error>> + 'writer
114    where
115        'transport: 'writer,
116        'request: 'writer,
117        'policy: 'writer,
118        'buffer: 'writer;
119}
120
121/// Drives one authenticated local attempt and commits after `Ready(Ok)`.
122pub async fn drive_local_authenticated<'transport, 'request, 'policy, 'writer, 'buffer, T>(
123    transport: &'transport T,
124    request: AuthenticatedRequest<'request, 'policy>,
125    response: &'writer mut ResponseWriter<'buffer>,
126) -> Result<(), AsyncExecutionError<T::Error>>
127where
128    T: LocalAsyncAuthenticatedTransport + ?Sized,
129    'transport: 'writer,
130    'request: 'writer,
131    'policy: 'writer,
132    'buffer: 'writer,
133{
134    let mut attempt = response
135        .begin_attempt()
136        .map_err(AsyncExecutionError::Response)?;
137    let completion = transport
138        .send_authenticated_local(request, attempt.staging())
139        .await
140        .map_err(AsyncExecutionError::Transport)?;
141    attempt
142        .commit_completion(completion)
143        .map_err(AsyncExecutionError::Response)
144}
145
146/// Drives one authenticated cross-thread async attempt and commits after `Ready(Ok)`.
147pub async fn drive_async_authenticated<'transport, 'request, 'policy, 'writer, 'buffer, T>(
148    transport: &'transport T,
149    request: AuthenticatedRequest<'request, 'policy>,
150    response: &'writer mut ResponseWriter<'buffer>,
151) -> Result<(), AsyncExecutionError<T::Error>>
152where
153    T: AsyncAuthenticatedTransport + ?Sized,
154    'transport: 'writer,
155    'request: 'writer,
156    'policy: 'writer,
157    'buffer: 'writer,
158{
159    let mut attempt = response
160        .begin_attempt()
161        .map_err(AsyncExecutionError::Response)?;
162    let completion = transport
163        .send_authenticated(request, attempt.staging())
164        .await
165        .map_err(AsyncExecutionError::Transport)?;
166    attempt
167        .commit_completion(completion)
168        .map_err(AsyncExecutionError::Response)
169}
170
171impl<T> LocalAsyncAuthenticatedTransport for T
172where
173    T: AsyncAuthenticatedTransport + ?Sized,
174{
175    type Error = T::Error;
176
177    async fn send_authenticated_local<'transport, 'request, 'policy, 'writer, 'buffer>(
178        &'transport self,
179        request: AuthenticatedRequest<'request, 'policy>,
180        response: AsyncResponseStaging<'writer, 'buffer>,
181    ) -> Result<ResponseCompletion, Self::Error>
182    where
183        'transport: 'writer,
184        'request: 'writer,
185        'policy: 'writer,
186        'buffer: 'writer,
187    {
188        AsyncAuthenticatedTransport::send_authenticated(self, request, response).await
189    }
190}