Skip to main content

sark_core/http/response/
shape.rs

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}