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