cloud_sdk/transport/
retained.rs1use core::fmt;
4
5use cloud_sdk_sanitization::{SecretBuffer, sanitize_bytes, sanitize_value};
6
7pub const MAX_REQUEST_ID_BYTES: usize = 1024;
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum RetainedMetadataError {
13 EmptyRequestId,
15 RequestIdTooLong,
17 RetentionLimitExceeded,
19 RetentionForbidden,
21}
22
23impl_static_error!(RetainedMetadataError,
24 Self::EmptyRequestId => "response request identifier is empty",
25 Self::RequestIdTooLong => "response request identifier exceeds the hard limit",
26 Self::RetentionLimitExceeded => "response request identifier exceeds the retention limit",
27 Self::RetentionForbidden => "operation policy forbids retaining the request identifier",
28);
29
30#[derive(Clone, Copy)]
31pub(crate) struct ProtectedRequestId {
32 start: u16,
33 len: u16,
34}
35
36impl ProtectedRequestId {
37 pub(crate) fn new(start: u16, len: u16) -> Result<Self, RetainedMetadataError> {
38 let len_usize = usize::from(len);
39 if len_usize == 0 {
40 return Err(RetainedMetadataError::EmptyRequestId);
41 }
42 if len_usize > MAX_REQUEST_ID_BYTES {
43 return Err(RetainedMetadataError::RequestIdTooLong);
44 }
45 Ok(Self { start, len })
46 }
47
48 pub(crate) const fn start(self) -> u16 {
49 self.start
50 }
51
52 pub(crate) const fn len(self) -> u16 {
53 self.len
54 }
55}
56
57impl fmt::Debug for ProtectedRequestId {
58 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
59 formatter.write_str("ProtectedRequestId([redacted])")
60 }
61}
62
63pub struct RetainedResponseMetadata<'storage> {
76 request_id: SecretBuffer<'storage>,
77 request_id_len: usize,
78}
79
80impl<'storage> RetainedResponseMetadata<'storage> {
81 pub(crate) fn empty_for_core(storage: &'storage mut [u8]) -> Self {
82 sanitize_bytes(storage);
83 Self {
84 request_id: SecretBuffer::new(storage),
85 request_id_len: 0,
86 }
87 }
88
89 pub(crate) fn write_request_id(&mut self, source: &[u8]) -> Result<(), RetainedMetadataError> {
90 let target = self
91 .request_id
92 .as_mut_slice()
93 .get_mut(..source.len())
94 .ok_or(RetainedMetadataError::RetentionLimitExceeded)?;
95 target.copy_from_slice(source);
96 self.request_id_len = source.len();
97 Ok(())
98 }
99
100 #[must_use]
102 pub const fn has_request_id(&self) -> bool {
103 self.request_id_len != 0
104 }
105
106 pub fn with_request_id<R>(&self, inspect: impl FnOnce(Option<&[u8]>) -> R) -> R {
108 let value = self.has_request_id().then(|| {
109 self.request_id
110 .as_slice()
111 .get(..self.request_id_len)
112 .unwrap_or_default()
113 });
114 inspect(value)
115 }
116}
117
118impl Drop for RetainedResponseMetadata<'_> {
119 fn drop(&mut self) {
120 sanitize_value(&mut self.request_id_len);
121 }
122}
123
124impl fmt::Debug for RetainedResponseMetadata<'_> {
125 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
126 formatter
127 .debug_struct("RetainedResponseMetadata")
128 .field("request_id", &"[redacted]")
129 .finish()
130 }
131}