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 fn apply_response_metadata_policy(
225 self,
226 response: &mut ResponseBuffer<'_>,
227 ) -> Result<(), ResponsePolicyError> {
228 super::policy::apply_request_id_policy(response, self.metadata.request_id_policy())
229 }
230
231 pub fn execute_blocking<'buffer, T>(
233 self,
234 transport: &T,
235 response_storage: &'buffer mut [u8],
236 response_header_storage: &'buffer mut [u8],
237 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
238 where
239 T: BlockingAuthenticatedTransport + BoundTransport,
240 {
241 let response = self.send_blocking(transport, response_storage, response_header_storage)?;
242 self.response_policy
243 .validate(response, self.metadata.request_id_policy())
244 .map_err(PreparedExecutionError::ResponsePolicy)
245 }
246
247 pub(crate) fn send_blocking<'buffer, T>(
248 self,
249 transport: &T,
250 response_storage: &'buffer mut [u8],
251 response_header_storage: &'buffer mut [u8],
252 ) -> Result<ResponseBuffer<'buffer>, PreparedExecutionError<T::Error>>
253 where
254 T: BlockingAuthenticatedTransport + BoundTransport,
255 {
256 if self.requires_execution_permit() {
257 sanitize_bytes(response_storage);
258 sanitize_bytes(response_header_storage);
259 return Err(PreparedExecutionError::AuthorizationRequired);
260 }
261 self.send_blocking_authorized(transport, None, response_storage, response_header_storage)
262 }
263
264 pub(crate) fn execute_blocking_authorized<'buffer, T>(
265 self,
266 transport: &T,
267 confirmed_endpoint: Option<EndpointIdentity<'_>>,
268 response_storage: &'buffer mut [u8],
269 response_header_storage: &'buffer mut [u8],
270 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
271 where
272 T: BlockingAuthenticatedTransport + BoundTransport,
273 {
274 let response = self.send_blocking_authorized(
275 transport,
276 confirmed_endpoint,
277 response_storage,
278 response_header_storage,
279 )?;
280 self.response_policy
281 .validate(response, self.metadata.request_id_policy())
282 .map_err(PreparedExecutionError::ResponsePolicy)
283 }
284
285 pub(crate) fn send_blocking_authorized<'buffer, T>(
286 self,
287 transport: &T,
288 confirmed_endpoint: Option<EndpointIdentity<'_>>,
289 response_storage: &'buffer mut [u8],
290 response_header_storage: &'buffer mut [u8],
291 ) -> Result<ResponseBuffer<'buffer>, PreparedExecutionError<T::Error>>
292 where
293 T: BlockingAuthenticatedTransport + BoundTransport,
294 {
295 let mut response = ResponseBuffer::new(
296 response_storage,
297 self.raw_response_policy.max_body_bytes(),
298 response_header_storage,
299 );
300 self.verify_endpoint(transport, confirmed_endpoint)
301 .map_err(map_endpoint_error)?;
302 transport
303 .send_authenticated(self.authenticated_request(), response.writer())
304 .map_err(PreparedExecutionError::Transport)?;
305 Ok(response)
306 }
307
308 pub async fn execute_async<'transport, 'buffer, T>(
310 &'transport self,
311 transport: &'transport T,
312 response_storage: &'buffer mut [u8],
313 response_header_storage: &'buffer mut [u8],
314 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
315 where
316 T: AsyncAuthenticatedTransport + BoundTransport,
317 'request: 'transport,
318 {
319 let response = self
320 .send_async(transport, response_storage, response_header_storage)
321 .await?;
322 self.response_policy
323 .validate(response, self.metadata.request_id_policy())
324 .map_err(PreparedExecutionError::ResponsePolicy)
325 }
326
327 pub(crate) async fn send_async<'transport, 'buffer, T>(
328 &'transport self,
329 transport: &'transport T,
330 response_storage: &'buffer mut [u8],
331 response_header_storage: &'buffer mut [u8],
332 ) -> Result<ResponseBuffer<'buffer>, PreparedExecutionError<T::Error>>
333 where
334 T: AsyncAuthenticatedTransport + BoundTransport,
335 'request: 'transport,
336 {
337 if self.requires_execution_permit() {
338 sanitize_bytes(response_storage);
339 sanitize_bytes(response_header_storage);
340 return Err(PreparedExecutionError::AuthorizationRequired);
341 }
342 self.send_async_authorized(transport, None, response_storage, response_header_storage)
343 .await
344 }
345
346 pub(crate) async fn execute_async_authorized<'transport, 'buffer, T>(
347 &'transport self,
348 transport: &'transport T,
349 confirmed_endpoint: Option<EndpointIdentity<'_>>,
350 response_storage: &'buffer mut [u8],
351 response_header_storage: &'buffer mut [u8],
352 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
353 where
354 T: AsyncAuthenticatedTransport + BoundTransport,
355 'request: 'transport,
356 {
357 let response = self
358 .send_async_authorized(
359 transport,
360 confirmed_endpoint,
361 response_storage,
362 response_header_storage,
363 )
364 .await?;
365 self.response_policy
366 .validate(response, self.metadata.request_id_policy())
367 .map_err(PreparedExecutionError::ResponsePolicy)
368 }
369
370 pub(crate) async fn send_async_authorized<'transport, 'buffer, T>(
371 &'transport self,
372 transport: &'transport T,
373 confirmed_endpoint: Option<EndpointIdentity<'_>>,
374 response_storage: &'buffer mut [u8],
375 response_header_storage: &'buffer mut [u8],
376 ) -> Result<ResponseBuffer<'buffer>, PreparedExecutionError<T::Error>>
377 where
378 T: AsyncAuthenticatedTransport + BoundTransport,
379 'request: 'transport,
380 {
381 let mut response = ResponseBuffer::new(
382 response_storage,
383 self.raw_response_policy.max_body_bytes(),
384 response_header_storage,
385 );
386 self.verify_endpoint(transport, confirmed_endpoint)
387 .map_err(map_endpoint_error)?;
388 drive_async_authenticated(transport, self.authenticated_request(), response.writer())
389 .await
390 .map_err(|error| match error {
391 crate::transport::AsyncExecutionError::Transport(error) => {
392 PreparedExecutionError::Transport(error)
393 }
394 crate::transport::AsyncExecutionError::Response(error) => {
395 PreparedExecutionError::ResponseWriter(error)
396 }
397 })?;
398 Ok(response)
399 }
400
401 pub(crate) fn requires_execution_permit(self) -> bool {
402 let approved_read_only_post = self
403 .read_only_post_approval
404 .and_then(|approval| {
405 approval.validate(
406 self.request,
407 self.service,
408 self.metadata,
409 self.authentication_policy,
410 self.body_sensitivity,
411 )
412 })
413 .is_some_and(|operation_id| self.operation_id == Some(operation_id));
414 (!self.request.method().permits_direct_read_only() && !approved_read_only_post)
415 || !matches!(self.metadata.impact(), OperationImpact::ReadOnly)
416 || matches!(self.metadata.cost_intent(), super::CostIntent::MayIncurCost)
417 }
418
419 fn verify_endpoint<T>(
420 self,
421 transport: &T,
422 confirmed_endpoint: Option<EndpointIdentity<'_>>,
423 ) -> Result<(), EndpointCheckError>
424 where
425 T: BoundTransport,
426 {
427 let actual = transport
428 .endpoint_identity()
429 .map_err(EndpointCheckError::Invalid)?;
430 match confirmed_endpoint {
431 Some(expected) if actual == expected => Ok(()),
432 Some(_) => Err(EndpointCheckError::Mismatch),
433 None => self
434 .service
435 .endpoint_policy()
436 .verify(actual)
437 .map_err(|_| EndpointCheckError::Mismatch),
438 }
439 }
440}
441
442impl fmt::Debug for PreparedRequest<'_> {
443 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
444 formatter
445 .debug_struct("PreparedRequest")
446 .field("request", &self.request)
447 .field("service", &self.service)
448 .field("metadata", &self.metadata)
449 .field("response_policy", &self.response_policy)
450 .field("authentication_policy", &self.authentication_policy)
451 .field("raw_response_policy", &self.raw_response_policy)
452 .field("operation_id", &self.operation_id)
453 .field("body_replayability", &self.body_replayability)
454 .field("body_sensitivity", &self.body_sensitivity)
455 .field(
456 "authorization_evidence_required",
457 &self.authorization_evidence_required,
458 )
459 .field("read_only_post_approval", &self.read_only_post_approval)
460 .finish()
461 }
462}