1use core::fmt;
4
5use crate::rate_limit::RateLimit;
6
7use super::{ResponseContentType, ResponseHeaders, ResponseStorageSanitizer, StatusCode};
8
9#[derive(Clone, Copy, Debug)]
11pub struct ResponseMetadata {
12 content_type: Option<ResponseContentType>,
13 rate_limit: Option<RateLimit>,
14 headers: ResponseHeaders,
15}
16
17impl ResponseMetadata {
18 pub const EMPTY: Self = Self {
20 content_type: None,
21 rate_limit: None,
22 headers: ResponseHeaders::new(),
23 };
24
25 #[must_use]
27 pub const fn with_content_type(mut self, content_type: ResponseContentType) -> Self {
28 self.content_type = Some(content_type);
29 self
30 }
31
32 #[must_use]
34 pub const fn with_rate_limit(mut self, rate_limit: RateLimit) -> Self {
35 self.rate_limit = Some(rate_limit);
36 self
37 }
38
39 #[must_use]
41 pub const fn with_headers(mut self, headers: ResponseHeaders) -> Self {
42 self.headers = headers;
43 self
44 }
45}
46
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
49pub enum ResponseWriterError {
50 AlreadyCommitted,
52 NotCommitted,
54 InitializedLengthTooLarge,
56}
57
58impl_static_error!(ResponseWriterError,
59 Self::AlreadyCommitted => "response writer is already committed",
60 Self::NotCommitted => "response writer is not committed",
61 Self::InitializedLengthTooLarge => "response length exceeds admitted storage",
62);
63
64#[derive(Clone, Copy, Debug)]
65struct ResponseCommit {
66 status: StatusCode,
67 initialized_len: usize,
68 metadata: ResponseMetadata,
69}
70
71pub struct ResponseWriter<'buffer> {
76 storage: &'buffer mut [u8],
77 admitted_len: usize,
78 commit: Option<ResponseCommit>,
79}
80
81impl ResponseWriter<'_> {
82 #[must_use]
84 pub const fn body_capacity(&self) -> usize {
85 self.admitted_len
86 }
87
88 pub fn body_mut(&mut self) -> Result<&mut [u8], ResponseWriterError> {
90 if self.commit.is_some() {
91 return Err(ResponseWriterError::AlreadyCommitted);
92 }
93 self.storage
94 .get_mut(..self.admitted_len)
95 .ok_or(ResponseWriterError::InitializedLengthTooLarge)
96 }
97
98 pub fn commit(
100 &mut self,
101 status: StatusCode,
102 initialized_len: usize,
103 metadata: ResponseMetadata,
104 ) -> Result<(), ResponseWriterError> {
105 if self.commit.is_some() {
106 return Err(ResponseWriterError::AlreadyCommitted);
107 }
108 if initialized_len > self.admitted_len {
109 return Err(ResponseWriterError::InitializedLengthTooLarge);
110 }
111 self.commit = Some(ResponseCommit {
112 status,
113 initialized_len,
114 metadata,
115 });
116 Ok(())
117 }
118
119 #[must_use]
121 pub const fn is_committed(&self) -> bool {
122 self.commit.is_some()
123 }
124
125 fn response(&self) -> Result<TransportResponse<'_>, ResponseWriterError> {
126 let commit = self.commit.ok_or(ResponseWriterError::NotCommitted)?;
127 let body = self
128 .storage
129 .get(..commit.initialized_len)
130 .ok_or(ResponseWriterError::InitializedLengthTooLarge)?;
131 Ok(TransportResponse::from_commit(commit, body))
132 }
133
134 fn initialized_body(&self, initialized_len: usize) -> &[u8] {
135 self.storage.get(..initialized_len).unwrap_or_default()
136 }
137}
138
139impl fmt::Debug for ResponseWriter<'_> {
140 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
141 formatter
142 .debug_struct("ResponseWriter")
143 .field("storage_capacity", &self.storage.len())
144 .field("admitted_len", &self.admitted_len)
145 .field("committed", &self.commit.is_some())
146 .field("body", &"[redacted]")
147 .finish()
148 }
149}
150
151pub struct ResponseBuffer<'buffer, 'sanitizer, S: ResponseStorageSanitizer + ?Sized> {
156 writer: ResponseWriter<'buffer>,
157 sanitizer: &'sanitizer S,
158}
159
160impl<'buffer, 'sanitizer, S> ResponseBuffer<'buffer, 'sanitizer, S>
161where
162 S: ResponseStorageSanitizer + ?Sized,
163{
164 #[must_use]
167 pub fn new(
168 storage: &'buffer mut [u8],
169 max_body_bytes: usize,
170 sanitizer: &'sanitizer S,
171 ) -> Self {
172 sanitizer.sanitize_response_storage(storage);
173 Self {
174 writer: ResponseWriter {
175 admitted_len: core::cmp::min(storage.len(), max_body_bytes),
176 storage,
177 commit: None,
178 },
179 sanitizer,
180 }
181 }
182
183 #[must_use]
185 pub const fn writer(&mut self) -> &mut ResponseWriter<'buffer> {
186 &mut self.writer
187 }
188
189 pub fn with_response<R>(
192 &self,
193 inspect: impl for<'response> FnOnce(TransportResponse<'response>) -> R,
194 ) -> Result<R, ResponseWriterError> {
195 let response = self.writer.response()?;
196 Ok(inspect(response))
197 }
198
199 pub(crate) fn response(&self) -> Result<TransportResponse<'_>, ResponseWriterError> {
200 self.writer.response()
201 }
202
203 pub(crate) fn initialized_body(&self, initialized_len: usize) -> &[u8] {
204 self.writer.initialized_body(initialized_len)
205 }
206}
207
208impl<S> fmt::Debug for ResponseBuffer<'_, '_, S>
209where
210 S: ResponseStorageSanitizer + ?Sized,
211{
212 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
213 formatter
214 .debug_struct("ResponseBuffer")
215 .field("writer", &self.writer)
216 .finish()
217 }
218}
219
220impl<S> Drop for ResponseBuffer<'_, '_, S>
221where
222 S: ResponseStorageSanitizer + ?Sized,
223{
224 fn drop(&mut self) {
225 self.sanitizer
226 .sanitize_response_storage(self.writer.storage);
227 }
228}
229
230#[derive(Clone, Copy)]
250pub struct TransportResponse<'buffer> {
251 status: StatusCode,
252 body: &'buffer [u8],
253 content_type: Option<ResponseContentType>,
254 rate_limit: Option<RateLimit>,
255 headers: ResponseHeaders,
256}
257
258impl<'buffer> TransportResponse<'buffer> {
259 const fn from_commit(commit: ResponseCommit, body: &'buffer [u8]) -> Self {
260 Self {
261 status: commit.status,
262 body,
263 content_type: commit.metadata.content_type,
264 rate_limit: commit.metadata.rate_limit,
265 headers: commit.metadata.headers,
266 }
267 }
268
269 #[must_use]
271 pub const fn status(&self) -> StatusCode {
272 self.status
273 }
274
275 #[must_use]
277 pub const fn body(&self) -> &'buffer [u8] {
278 self.body
279 }
280
281 #[must_use]
283 pub const fn content_type(&self) -> Option<ResponseContentType> {
284 self.content_type
285 }
286
287 #[must_use]
289 pub const fn rate_limit(&self) -> Option<RateLimit> {
290 self.rate_limit
291 }
292
293 #[must_use]
295 pub const fn headers(&self) -> &ResponseHeaders {
296 &self.headers
297 }
298}
299
300impl fmt::Debug for TransportResponse<'_> {
301 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
302 formatter
303 .debug_struct("TransportResponse")
304 .field("status", &self.status)
305 .field("body_len", &self.body.len())
306 .field("body", &"[redacted]")
307 .field("content_type", &self.content_type)
308 .field("rate_limit", &self.rate_limit)
309 .field("headers", &self.headers)
310 .finish()
311 }
312}