1use http::StatusCode;
2use o3::buffer::{Pooled, Shared};
3
4use crate::http::compress::Gzip;
5
6use super::{
7 Chunked, EncodedBody, EncodedResponse, FixedResponse, MonoResponseInner, NeverStream, Serve,
8 StaticResponseInner, Stream,
9};
10
11pub enum Egress<S> {
12 Inline { written: usize },
13 Static { head: usize, body: &'static [u8] },
14 Shared { head: usize, body: Shared },
15 Pooled { head: usize, body: Pooled },
16 Stream { head: usize, stream: S },
17 Failed,
18}
19
20pub enum CacheTemplate {
21 Inline {
22 bytes: Vec<u8>,
23 date_offset: Option<usize>,
24 },
25 Static {
26 head: Vec<u8>,
27 date_offset: Option<usize>,
28 body: &'static [u8],
29 },
30}
31
32impl CacheTemplate {
33 pub fn configure_head(&mut self, emit_date: bool, emit_server: bool) {
34 let (template, date_offset) = match self {
35 Self::Inline { bytes, date_offset } => (bytes, date_offset),
36 Self::Static {
37 head, date_offset, ..
38 } => (head, date_offset),
39 };
40 if let Some(offset) = *date_offset {
41 if emit_date && emit_server {
42 return;
43 }
44 let term_start =
45 offset - super::wire_emit::DATE_PREFIX.len() - super::wire_emit::SERVER_LINE.len();
46 let term_end = term_start + super::wire_emit::SERVER_DATE_TERMINATOR_LEN;
47 let mut tail = Vec::with_capacity(super::wire_emit::SERVER_DATE_TERMINATOR_LEN);
48 if emit_server {
49 tail.extend_from_slice(super::wire_emit::SERVER_LINE);
50 }
51 *date_offset = if emit_date {
52 tail.extend_from_slice(super::wire_emit::DATE_PREFIX);
53 let offset = term_start + tail.len();
54 tail.extend_from_slice(&[0u8; super::wire_emit::DATE_LEN]);
55 tail.extend_from_slice(super::wire_emit::CRLF);
56 Some(offset)
57 } else {
58 None
59 };
60 tail.extend_from_slice(super::wire_emit::CRLF);
61 template.splice(term_start..term_end, tail);
62 }
63 }
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub enum Preparation {
68 Plain,
69 Compress,
70 Cache,
71}
72
73pub enum Prepared<S> {
74 Egress(Egress<S>),
75 Cache(CacheTemplate),
76}
77
78pub struct ResponseView {
79 pub status: StatusCode,
80 pub headers: Shared,
81 pub body: Shared,
82}
83
84pub trait Shape<'req>: Sized {
85 type StreamInner: 'static;
86
87 fn prepare(
88 self,
89 mode: Preparation,
90 gzip: Option<&mut Gzip>,
91 out: &mut [u8],
92 date: &[u8; 29],
93 ) -> Prepared<Self::StreamInner>;
94
95 fn response_view(&self) -> Option<ResponseView> {
96 None
97 }
98}
99
100impl<'req, const N: usize> Shape<'req> for FixedResponse<'req, N> {
101 type StreamInner = NeverStream;
102
103 fn prepare(
104 self,
105 mode: Preparation,
106 gzip: Option<&mut Gzip>,
107 out: &mut [u8],
108 date: &[u8; 29],
109 ) -> Prepared<Self::StreamInner> {
110 if mode == Preparation::Cache {
111 let (bytes, date_offset) = self.preserialize();
112 return Prepared::Cache(CacheTemplate::Inline {
113 bytes,
114 date_offset: Some(date_offset),
115 });
116 }
117 if mode == Preparation::Compress
118 && !self.has_content_encoding()
119 && !self.body_ref().is_empty()
120 && let Some(body) = gzip.and_then(|gzip| gzip.encode(self.body_ref()))
121 {
122 let body_len = body.len();
123 let egress = match self.write_gzip_head(out, date, body_len) {
124 Some(head) => Egress::Pooled { head, body },
125 None => Egress::Failed,
126 };
127 return Prepared::Egress(egress);
128 }
129 Prepared::Egress(fixed_egress(self, out, date))
130 }
131
132 fn response_view(&self) -> Option<ResponseView> {
133 Some(ResponseView {
134 status: self.status(),
135 headers: self.wire_headers(),
136 body: self.body.clone(),
137 })
138 }
139}
140
141impl<'req, B, const N: usize> Shape<'req> for EncodedResponse<'req, B, N>
142where
143 B: EncodedBody,
144{
145 type StreamInner = NeverStream;
146
147 fn prepare(
148 self,
149 mode: Preparation,
150 _gzip: Option<&mut Gzip>,
151 out: &mut [u8],
152 date: &[u8; 29],
153 ) -> Prepared<Self::StreamInner> {
154 if mode == Preparation::Cache {
155 let (bytes, date_offset) = self.preserialize();
156 return Prepared::Cache(CacheTemplate::Inline {
157 bytes,
158 date_offset: Some(date_offset),
159 });
160 }
161 let egress = if let Some(written) = self.write_into_slice(out, date) {
162 Egress::Inline { written }
163 } else {
164 match self.write_head_split(out, date) {
165 Some((head, body)) => Egress::Shared { head, body },
166 None => Egress::Failed,
167 }
168 };
169 Prepared::Egress(egress)
170 }
171
172 fn response_view(&self) -> Option<ResponseView> {
173 Some(ResponseView {
174 status: self.status(),
175 headers: self.wire_headers(),
176 body: self.encoded_body(),
177 })
178 }
179}
180
181impl<'req, const N: usize> Shape<'req> for MonoResponseInner<'req, N> {
182 type StreamInner = NeverStream;
183
184 fn prepare(
185 self,
186 mode: Preparation,
187 _gzip: Option<&mut Gzip>,
188 out: &mut [u8],
189 date: &[u8; 29],
190 ) -> Prepared<Self::StreamInner> {
191 if mode == Preparation::Cache {
192 return Prepared::Egress(Egress::Failed);
193 }
194 let egress = if let Some(written) = self.write_into_slice(out, date) {
195 Egress::Inline { written }
196 } else {
197 match self.write_head_split(out, date) {
198 Some((head, body)) => Egress::Shared { head, body },
199 None => Egress::Failed,
200 }
201 };
202 Prepared::Egress(egress)
203 }
204
205 fn response_view(&self) -> Option<ResponseView> {
206 Some(ResponseView {
207 status: self.status(),
208 headers: self.wire_headers(),
209 body: self.body.clone().into_shared(),
210 })
211 }
212}
213
214impl<'req, const N: usize> Shape<'req> for StaticResponseInner<'req, N> {
215 type StreamInner = NeverStream;
216
217 fn prepare(
218 self,
219 mode: Preparation,
220 _gzip: Option<&mut Gzip>,
221 out: &mut [u8],
222 date: &[u8; 29],
223 ) -> Prepared<Self::StreamInner> {
224 if mode == Preparation::Cache {
225 let (head, date_offset, body) = self.preserialize_static();
226 return Prepared::Cache(CacheTemplate::Static {
227 head,
228 date_offset: Some(date_offset),
229 body,
230 });
231 }
232 Prepared::Egress(match self.write_head_only(out, date) {
233 Some((head, body)) => Egress::Static { head, body },
234 None => Egress::Failed,
235 })
236 }
237
238 fn response_view(&self) -> Option<ResponseView> {
239 Some(ResponseView {
240 status: self.status(),
241 headers: self.wire_headers(),
242 body: Shared::from_static(self.body_ref()),
243 })
244 }
245}
246
247impl<'req> Shape<'req> for Chunked {
248 type StreamInner = NeverStream;
249
250 fn prepare(
251 self,
252 mode: Preparation,
253 _gzip: Option<&mut Gzip>,
254 out: &mut [u8],
255 date: &[u8; 29],
256 ) -> Prepared<Self::StreamInner> {
257 if mode == Preparation::Cache {
258 return Prepared::Egress(Egress::Failed);
259 }
260 let egress = if let Some(written) = self.write_into_slice(out, date) {
261 Egress::Inline { written }
262 } else {
263 match self.write_head_split(out, date) {
264 Some((head, body)) => Egress::Shared { head, body },
265 None => Egress::Failed,
266 }
267 };
268 Prepared::Egress(egress)
269 }
270}
271
272impl<'req, S> Shape<'req> for Stream<S>
273where
274 S: 'static,
275{
276 type StreamInner = S;
277
278 fn prepare(
279 self,
280 mode: Preparation,
281 _gzip: Option<&mut Gzip>,
282 out: &mut [u8],
283 date: &[u8; 29],
284 ) -> Prepared<Self::StreamInner> {
285 if mode == Preparation::Cache {
286 return Prepared::Egress(Egress::Failed);
287 }
288 Prepared::Egress(match self.write_head_stream(out, date) {
289 Some((head, stream)) => Egress::Stream { head, stream },
290 None => Egress::Failed,
291 })
292 }
293}
294
295impl<'req, const N: usize> Shape<'req> for Serve<'req, N> {
296 type StreamInner = NeverStream;
297
298 fn prepare(
299 self,
300 mode: Preparation,
301 gzip: Option<&mut Gzip>,
302 out: &mut [u8],
303 date: &[u8; 29],
304 ) -> Prepared<Self::StreamInner> {
305 match self {
306 Self::Fixed(response) => response.prepare(mode, gzip, out, date),
307 Self::Mono(response) => response.prepare(mode, gzip, out, date),
308 Self::Chunked(response) => response.prepare(mode, gzip, out, date),
309 }
310 }
311
312 fn response_view(&self) -> Option<ResponseView> {
313 match self {
314 Self::Fixed(response) => response.response_view(),
315 Self::Mono(response) => response.response_view(),
316 Self::Chunked(_) => None,
317 }
318 }
319}
320
321fn fixed_egress<'req, const N: usize>(
322 response: FixedResponse<'req, N>,
323 out: &mut [u8],
324 date: &[u8; 29],
325) -> Egress<NeverStream> {
326 if let Some(written) = response.write_into_slice(out, date) {
327 return Egress::Inline { written };
328 }
329 match response.write_head_split(out, date) {
330 Some((head, body)) => Egress::Shared { head, body },
331 None => Egress::Failed,
332 }
333}