cloud_sdk/transport/header/
request.rs1use core::fmt;
2
3use super::{
4 ContentType, HeaderError, HeaderName, HeaderSensitivity, HeaderValue, MAX_REQUEST_HEADER_BYTES,
5 MAX_REQUEST_HEADERS, MediaType, encoded_line_len, is_reserved_request_name, typed_accept,
6 typed_content_type,
7};
8
9#[derive(Clone, Copy)]
22pub struct RequestHeader<'a> {
23 name: HeaderName<'a>,
24 value: HeaderValue<'a>,
25 sensitivity: HeaderSensitivity,
26}
27
28impl<'a> RequestHeader<'a> {
29 pub fn new(name: &'a str, value: &'a str) -> Result<Self, HeaderError> {
31 Self::from_parts(
32 HeaderName::new(name)?,
33 HeaderValue::new(value)?,
34 HeaderSensitivity::Public,
35 )
36 }
37
38 pub fn sensitive(name: &'a str, value: &'a str) -> Result<Self, HeaderError> {
40 Self::from_parts(
41 HeaderName::new(name)?,
42 HeaderValue::new(value)?,
43 HeaderSensitivity::Sensitive,
44 )
45 }
46
47 #[must_use]
49 pub const fn accept(media_type: MediaType<'a>) -> Self {
50 Self {
51 name: HeaderName("accept"),
52 value: typed_accept(media_type),
53 sensitivity: HeaderSensitivity::Public,
54 }
55 }
56
57 #[must_use]
59 pub const fn content_type(content_type: ContentType<'a>) -> Self {
60 Self {
61 name: HeaderName("content-type"),
62 value: typed_content_type(content_type),
63 sensitivity: HeaderSensitivity::Public,
64 }
65 }
66
67 #[must_use]
69 pub const fn name(self) -> HeaderName<'a> {
70 self.name
71 }
72
73 #[must_use]
75 pub const fn value(self) -> HeaderValue<'a> {
76 self.value
77 }
78
79 #[must_use]
81 pub const fn sensitivity(self) -> HeaderSensitivity {
82 self.sensitivity
83 }
84
85 fn from_parts(
86 name: HeaderName<'a>,
87 value: HeaderValue<'a>,
88 sensitivity: HeaderSensitivity,
89 ) -> Result<Self, HeaderError> {
90 if is_reserved_request_name(name) {
91 return Err(HeaderError::ReservedRequestHeader);
92 }
93 if name.eq_ignore_ascii_case("content-type") && ContentType::new(value.as_str()).is_err() {
94 return Err(HeaderError::InvalidContentType);
95 }
96 Ok(Self {
97 name,
98 value,
99 sensitivity,
100 })
101 }
102}
103
104impl fmt::Debug for RequestHeader<'_> {
105 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
106 formatter
107 .debug_struct("RequestHeader")
108 .field("name", &self.name)
109 .field("value", &"[redacted]")
110 .field("sensitivity", &self.sensitivity)
111 .finish()
112 }
113}
114
115#[derive(Clone, Copy)]
117pub struct RequestHeaders<'a> {
118 entries: &'a [RequestHeader<'a>],
119 encoded_len: usize,
120}
121
122impl RequestHeaders<'static> {
123 pub const EMPTY: Self = Self {
125 entries: &[],
126 encoded_len: 0,
127 };
128}
129
130impl<'a> RequestHeaders<'a> {
131 pub fn new(entries: &'a [RequestHeader<'a>]) -> Result<Self, HeaderError> {
133 if entries.len() > MAX_REQUEST_HEADERS {
134 return Err(HeaderError::TooManyHeaders);
135 }
136 let mut encoded_len = 0_usize;
137 for (index, entry) in entries.iter().enumerate() {
138 if is_reserved_request_name(entry.name) {
139 return Err(HeaderError::ReservedRequestHeader);
140 }
141 if entries.get(..index).is_some_and(|seen| {
142 seen.iter()
143 .any(|candidate| candidate.name.eq_ignore_ascii_case(entry.name.as_str()))
144 }) {
145 return Err(HeaderError::DuplicateName);
146 }
147 let line_len = encoded_line_len(entry.name.as_str().len(), entry.value.as_str().len())?;
148 encoded_len = encoded_len
149 .checked_add(line_len)
150 .ok_or(HeaderError::AggregateTooLarge)?;
151 if encoded_len > MAX_REQUEST_HEADER_BYTES {
152 return Err(HeaderError::AggregateTooLarge);
153 }
154 }
155 Ok(Self {
156 entries,
157 encoded_len,
158 })
159 }
160
161 #[must_use]
163 pub const fn as_slice(self) -> &'a [RequestHeader<'a>] {
164 self.entries
165 }
166
167 #[must_use]
169 pub const fn encoded_len(self) -> usize {
170 self.encoded_len
171 }
172
173 #[must_use]
175 pub fn get(self, name: &str) -> Option<RequestHeader<'a>> {
176 self.entries
177 .iter()
178 .copied()
179 .find(|entry| entry.name.eq_ignore_ascii_case(name))
180 }
181
182 pub fn encode_http1(self, output: &mut [u8]) -> Result<usize, HeaderError> {
186 if output.len() < self.encoded_len {
187 return Err(HeaderError::OutputTooSmall);
188 }
189 let target = output
190 .get_mut(..self.encoded_len)
191 .ok_or(HeaderError::OutputTooSmall)?;
192 let mut offset = 0_usize;
193 for entry in self.entries {
194 offset = write_line(target, offset, *entry)?;
195 }
196 Ok(offset)
197 }
198}
199
200impl fmt::Debug for RequestHeaders<'_> {
201 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
202 formatter
203 .debug_struct("RequestHeaders")
204 .field("count", &self.entries.len())
205 .field("encoded_len", &self.encoded_len)
206 .field("values", &"[redacted]")
207 .finish()
208 }
209}
210
211fn write_line(
212 output: &mut [u8],
213 offset: usize,
214 header: RequestHeader<'_>,
215) -> Result<usize, HeaderError> {
216 let name = header.name.as_str().as_bytes();
217 let value = header.value.as_str().as_bytes();
218 let line_len = encoded_line_len(name.len(), value.len())?;
219 let end = offset
220 .checked_add(line_len)
221 .ok_or(HeaderError::OutputTooSmall)?;
222 let line = output
223 .get_mut(offset..end)
224 .ok_or(HeaderError::OutputTooSmall)?;
225 let (name_out, rest) = line.split_at_mut(name.len());
226 let (separator, rest) = rest.split_at_mut(2);
227 let (value_out, ending) = rest.split_at_mut(value.len());
228 name_out.copy_from_slice(name);
229 separator.copy_from_slice(b": ");
230 value_out.copy_from_slice(value);
231 ending.copy_from_slice(b"\r\n");
232 Ok(end)
233}