1use core::fmt;
4
5use cloud_sdk_sanitization::sanitize_bytes;
6
7use crate::authentication::{
8 AsyncAuthenticatedTransport, AuthenticatedRequest, AuthenticationScopePolicy,
9 BlockingAuthenticatedTransport, drive_async_authenticated,
10};
11use crate::operation::{
12 CheckedResponseGuard, OperationId, OperationImpact, OperationMetadata, RequestIdPolicy,
13 ResponsePolicy, ResponsePolicyError,
14};
15use crate::transport::{
16 BoundTransport, EndpointIdentity, RawResponsePolicy, RequestHeaders, ResponseBuffer,
17 TransportRequest,
18};
19
20mod body;
21mod error;
22mod service;
23mod storage;
24pub use body::{BodyReplayability, RequestBodySensitivity};
25use error::{EndpointCheckError, map_endpoint_error};
26pub use error::{PreparedExecutionError, PreparedRequestPolicyError};
27pub use service::ProviderService;
28pub use storage::{PreparationStorage, PrepareOperation};
29
30#[derive(Clone, Copy)]
32pub struct PreparedRequest<'request> {
33 request: TransportRequest<'request>,
34 service: ProviderService<'request>,
35 metadata: OperationMetadata,
36 response_policy: ResponsePolicy,
37 authentication_policy: AuthenticationScopePolicy<'request>,
38 raw_response_policy: RawResponsePolicy<'request>,
39 operation_id: Option<OperationId>,
40 body_replayability: BodyReplayability,
41 body_sensitivity: RequestBodySensitivity,
42}
43
44impl<'request> PreparedRequest<'request> {
45 pub fn new(
57 request: TransportRequest<'request>,
58 service: ProviderService<'request>,
59 metadata: OperationMetadata,
60 response_policy: ResponsePolicy,
61 authentication_policy: AuthenticationScopePolicy<'request>,
62 raw_response_policy: RawResponsePolicy<'request>,
63 body_sensitivity: RequestBodySensitivity,
64 ) -> Result<Self, PreparedRequestPolicyError> {
65 if matches!(metadata.impact(), OperationImpact::ReadOnly)
66 && !request.method().permits_direct_read_only()
67 {
68 return Err(PreparedRequestPolicyError::ReadOnlyMethodMismatch);
69 }
70 if metadata.request_id_policy() != RequestIdPolicy::Discard
71 && !raw_response_policy.admits_header("x-request-id")
72 {
73 return Err(PreparedRequestPolicyError::MissingRequestIdHeader);
74 }
75 Ok(Self {
76 request,
77 service,
78 metadata,
79 response_policy,
80 authentication_policy,
81 raw_response_policy,
82 operation_id: None,
83 body_replayability: if request.body().is_empty() {
84 BodyReplayability::Replayable
85 } else {
86 BodyReplayability::NotReplayable
87 },
88 body_sensitivity,
89 })
90 }
91
92 #[must_use]
94 pub const fn with_operation_id(mut self, operation_id: OperationId) -> Self {
95 self.operation_id = Some(operation_id);
96 self
97 }
98
99 #[must_use]
104 pub const fn with_replayable_body(mut self) -> Self {
105 self.body_replayability = BodyReplayability::Replayable;
106 self
107 }
108
109 #[must_use]
114 pub const fn with_sensitive_body(mut self) -> Self {
115 self.body_sensitivity = RequestBodySensitivity::Sensitive;
116 self
117 }
118
119 #[must_use]
121 pub const fn transport_request(self) -> TransportRequest<'request> {
122 self.request
123 }
124
125 #[must_use]
127 pub const fn service(self) -> ProviderService<'request> {
128 self.service
129 }
130
131 #[must_use]
133 pub const fn metadata(self) -> OperationMetadata {
134 self.metadata
135 }
136
137 #[must_use]
139 pub const fn response_policy(self) -> ResponsePolicy {
140 self.response_policy
141 }
142
143 #[must_use]
145 pub const fn authentication_policy(self) -> AuthenticationScopePolicy<'request> {
146 self.authentication_policy
147 }
148
149 #[must_use]
151 pub const fn raw_response_policy(self) -> RawResponsePolicy<'request> {
152 self.raw_response_policy
153 }
154
155 #[must_use]
157 pub(crate) const fn authenticated_request(self) -> AuthenticatedRequest<'request, 'request> {
158 AuthenticatedRequest::new(
159 self.request,
160 self.authentication_policy,
161 &self.raw_response_policy,
162 )
163 }
164
165 #[must_use]
167 pub const fn operation_id(self) -> Option<OperationId> {
168 self.operation_id
169 }
170
171 #[must_use]
173 pub const fn body_replayability(self) -> BodyReplayability {
174 self.body_replayability
175 }
176
177 #[must_use]
179 pub const fn body_sensitivity(self) -> RequestBodySensitivity {
180 self.body_sensitivity
181 }
182
183 pub(crate) fn with_request_headers<'headers>(
184 self,
185 headers: RequestHeaders<'headers>,
186 ) -> PreparedRequest<'headers>
187 where
188 'request: 'headers,
189 {
190 let request: TransportRequest<'headers> = self.request;
191 PreparedRequest {
192 request: request.with_headers(headers),
193 service: self.service,
194 metadata: self.metadata,
195 response_policy: self.response_policy,
196 authentication_policy: self.authentication_policy,
197 raw_response_policy: self.raw_response_policy,
198 operation_id: self.operation_id,
199 body_replayability: self.body_replayability,
200 body_sensitivity: self.body_sensitivity,
201 }
202 }
203
204 pub(crate) fn has_same_retry_policy(&self, other: &Self) -> bool {
205 self.service == other.service
206 && self.metadata == other.metadata
207 && self.response_policy == other.response_policy
208 && self.authentication_policy == other.authentication_policy
209 && self.raw_response_policy == other.raw_response_policy
210 && self.operation_id == other.operation_id
211 && self.body_replayability == other.body_replayability
212 && self.body_sensitivity == other.body_sensitivity
213 && self.has_same_header_policy(other)
214 }
215
216 fn has_same_header_policy(&self, other: &Self) -> bool {
217 let left = self.request.headers().as_slice();
218 let right = other.request.headers().as_slice();
219 left.len() == right.len()
220 && left
221 .iter()
222 .zip(right)
223 .all(|(left, right)| left.sensitivity() == right.sensitivity())
224 }
225
226 pub fn validate_response<'buffer>(
228 self,
229 response: ResponseBuffer<'buffer>,
230 ) -> Result<CheckedResponseGuard<'buffer>, ResponsePolicyError> {
231 self.response_policy
232 .validate(response, self.metadata.request_id_policy())
233 }
234
235 pub fn apply_response_metadata_policy(
241 self,
242 response: &mut ResponseBuffer<'_>,
243 ) -> Result<(), ResponsePolicyError> {
244 super::policy::apply_request_id_policy(response, self.metadata.request_id_policy())
245 }
246
247 pub fn execute_blocking<'buffer, T>(
249 self,
250 transport: &T,
251 response_storage: &'buffer mut [u8],
252 response_header_storage: &'buffer mut [u8],
253 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
254 where
255 T: BlockingAuthenticatedTransport + BoundTransport,
256 {
257 let response = self.send_blocking(transport, response_storage, response_header_storage)?;
258 self.response_policy
259 .validate(response, self.metadata.request_id_policy())
260 .map_err(PreparedExecutionError::ResponsePolicy)
261 }
262
263 pub(crate) fn send_blocking<'buffer, T>(
264 self,
265 transport: &T,
266 response_storage: &'buffer mut [u8],
267 response_header_storage: &'buffer mut [u8],
268 ) -> Result<ResponseBuffer<'buffer>, PreparedExecutionError<T::Error>>
269 where
270 T: BlockingAuthenticatedTransport + BoundTransport,
271 {
272 if self.requires_execution_permit() {
273 sanitize_bytes(response_storage);
274 sanitize_bytes(response_header_storage);
275 return Err(PreparedExecutionError::AuthorizationRequired);
276 }
277 self.send_blocking_authorized(transport, None, response_storage, response_header_storage)
278 }
279
280 pub(crate) fn execute_blocking_authorized<'buffer, T>(
281 self,
282 transport: &T,
283 confirmed_endpoint: Option<EndpointIdentity<'_>>,
284 response_storage: &'buffer mut [u8],
285 response_header_storage: &'buffer mut [u8],
286 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
287 where
288 T: BlockingAuthenticatedTransport + BoundTransport,
289 {
290 let response = self.send_blocking_authorized(
291 transport,
292 confirmed_endpoint,
293 response_storage,
294 response_header_storage,
295 )?;
296 self.response_policy
297 .validate(response, self.metadata.request_id_policy())
298 .map_err(PreparedExecutionError::ResponsePolicy)
299 }
300
301 pub(crate) fn send_blocking_authorized<'buffer, T>(
302 self,
303 transport: &T,
304 confirmed_endpoint: Option<EndpointIdentity<'_>>,
305 response_storage: &'buffer mut [u8],
306 response_header_storage: &'buffer mut [u8],
307 ) -> Result<ResponseBuffer<'buffer>, PreparedExecutionError<T::Error>>
308 where
309 T: BlockingAuthenticatedTransport + BoundTransport,
310 {
311 let mut response = ResponseBuffer::new(
312 response_storage,
313 self.raw_response_policy.max_body_bytes(),
314 response_header_storage,
315 );
316 self.verify_endpoint(transport, confirmed_endpoint)
317 .map_err(map_endpoint_error)?;
318 transport
319 .send_authenticated(self.authenticated_request(), response.writer())
320 .map_err(PreparedExecutionError::Transport)?;
321 Ok(response)
322 }
323
324 pub async fn execute_async<'transport, 'buffer, T>(
326 &'transport self,
327 transport: &'transport T,
328 response_storage: &'buffer mut [u8],
329 response_header_storage: &'buffer mut [u8],
330 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
331 where
332 T: AsyncAuthenticatedTransport + BoundTransport,
333 'request: 'transport,
334 {
335 let response = self
336 .send_async(transport, response_storage, response_header_storage)
337 .await?;
338 self.response_policy
339 .validate(response, self.metadata.request_id_policy())
340 .map_err(PreparedExecutionError::ResponsePolicy)
341 }
342
343 pub(crate) async fn send_async<'transport, 'buffer, T>(
344 &'transport self,
345 transport: &'transport T,
346 response_storage: &'buffer mut [u8],
347 response_header_storage: &'buffer mut [u8],
348 ) -> Result<ResponseBuffer<'buffer>, PreparedExecutionError<T::Error>>
349 where
350 T: AsyncAuthenticatedTransport + BoundTransport,
351 'request: 'transport,
352 {
353 if self.requires_execution_permit() {
354 sanitize_bytes(response_storage);
355 sanitize_bytes(response_header_storage);
356 return Err(PreparedExecutionError::AuthorizationRequired);
357 }
358 self.send_async_authorized(transport, None, response_storage, response_header_storage)
359 .await
360 }
361
362 pub(crate) async fn execute_async_authorized<'transport, 'buffer, T>(
363 &'transport self,
364 transport: &'transport T,
365 confirmed_endpoint: Option<EndpointIdentity<'_>>,
366 response_storage: &'buffer mut [u8],
367 response_header_storage: &'buffer mut [u8],
368 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
369 where
370 T: AsyncAuthenticatedTransport + BoundTransport,
371 'request: 'transport,
372 {
373 let response = self
374 .send_async_authorized(
375 transport,
376 confirmed_endpoint,
377 response_storage,
378 response_header_storage,
379 )
380 .await?;
381 self.response_policy
382 .validate(response, self.metadata.request_id_policy())
383 .map_err(PreparedExecutionError::ResponsePolicy)
384 }
385
386 pub(crate) async fn send_async_authorized<'transport, 'buffer, T>(
387 &'transport self,
388 transport: &'transport T,
389 confirmed_endpoint: Option<EndpointIdentity<'_>>,
390 response_storage: &'buffer mut [u8],
391 response_header_storage: &'buffer mut [u8],
392 ) -> Result<ResponseBuffer<'buffer>, PreparedExecutionError<T::Error>>
393 where
394 T: AsyncAuthenticatedTransport + BoundTransport,
395 'request: 'transport,
396 {
397 let mut response = ResponseBuffer::new(
398 response_storage,
399 self.raw_response_policy.max_body_bytes(),
400 response_header_storage,
401 );
402 self.verify_endpoint(transport, confirmed_endpoint)
403 .map_err(map_endpoint_error)?;
404 drive_async_authenticated(transport, self.authenticated_request(), response.writer())
405 .await
406 .map_err(|error| match error {
407 crate::transport::AsyncExecutionError::Transport(error) => {
408 PreparedExecutionError::Transport(error)
409 }
410 crate::transport::AsyncExecutionError::Response(error) => {
411 PreparedExecutionError::ResponseWriter(error)
412 }
413 })?;
414 Ok(response)
415 }
416
417 pub(crate) const fn requires_execution_permit(self) -> bool {
418 !self.request.method().permits_direct_read_only()
419 || !matches!(self.metadata.impact(), OperationImpact::ReadOnly)
420 || matches!(self.metadata.cost_intent(), super::CostIntent::MayIncurCost)
421 }
422
423 fn verify_endpoint<T>(
424 self,
425 transport: &T,
426 confirmed_endpoint: Option<EndpointIdentity<'_>>,
427 ) -> Result<(), EndpointCheckError>
428 where
429 T: BoundTransport,
430 {
431 let actual = transport
432 .endpoint_identity()
433 .map_err(EndpointCheckError::Invalid)?;
434 match confirmed_endpoint {
435 Some(expected) if actual == expected => Ok(()),
436 Some(_) => Err(EndpointCheckError::Mismatch),
437 None => self
438 .service
439 .endpoint_policy()
440 .verify(actual)
441 .map_err(|_| EndpointCheckError::Mismatch),
442 }
443 }
444}
445
446impl fmt::Debug for PreparedRequest<'_> {
447 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
448 formatter
449 .debug_struct("PreparedRequest")
450 .field("request", &self.request)
451 .field("service", &self.service)
452 .field("metadata", &self.metadata)
453 .field("response_policy", &self.response_policy)
454 .field("authentication_policy", &self.authentication_policy)
455 .field("raw_response_policy", &self.raw_response_policy)
456 .field("operation_id", &self.operation_id)
457 .field("body_replayability", &self.body_replayability)
458 .field("body_sensitivity", &self.body_sensitivity)
459 .finish()
460 }
461}