Skip to main content

sark_core/http/response/
shape.rs

1use http::StatusCode;
2use o3::buffer::{Pooled, Shared};
3
4use crate::http::body_kind::ResponseKind;
5use crate::http::compress::Gzip;
6
7use super::{
8    Body, Chunked, EncodedBody, EncodedResponse, FixedResponse, HeaderList, HotHeadInner,
9    IntoResponseShape, MonoResponseInner, NeverStream, Serve, StaticHeaders, StaticResponseInner,
10    Stream, WireHeaderFields,
11};
12use crate::http::Field;
13
14pub enum Egress<S> {
15    Inline { written: usize },
16    Static { head: usize, body: &'static [u8] },
17    Shared { head: usize, body: Shared },
18    Pooled { head: usize, body: Pooled },
19    Stream { head: usize, stream: S },
20    Failed,
21}
22
23pub enum CacheTemplate {
24    Inline {
25        bytes: Vec<u8>,
26        date_offset: Option<usize>,
27    },
28    Static {
29        head: Vec<u8>,
30        date_offset: Option<usize>,
31        body: &'static [u8],
32    },
33}
34
35impl CacheTemplate {
36    pub fn configure_head(&mut self, emit_date: bool, emit_server: bool) {
37        let (template, date_offset) = match self {
38            Self::Inline { bytes, date_offset } => (bytes, date_offset),
39            Self::Static {
40                head, date_offset, ..
41            } => (head, date_offset),
42        };
43        if let Some(offset) = *date_offset {
44            use super::wire_emit::{CRLF, DATE_PREFIX, SERVER_DATE_TERMINATOR_LEN, SERVER_LINE};
45            if emit_date && emit_server {
46                return;
47            }
48            let term_start = offset - DATE_PREFIX.len() - SERVER_LINE.len();
49            let term_end = term_start + SERVER_DATE_TERMINATOR_LEN;
50            let mut tail = Vec::with_capacity(SERVER_DATE_TERMINATOR_LEN);
51            if emit_server {
52                tail.extend_from_slice(SERVER_LINE);
53            }
54            *date_offset = if emit_date {
55                use super::wire_emit::DATE_LEN;
56                tail.extend_from_slice(DATE_PREFIX);
57                let offset = term_start + tail.len();
58                tail.extend_from_slice(&[0u8; DATE_LEN]);
59                tail.extend_from_slice(CRLF);
60                Some(offset)
61            } else {
62                None
63            };
64            tail.extend_from_slice(CRLF);
65            template.splice(term_start..term_end, tail);
66        }
67    }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub enum Preparation {
72    Plain,
73    Compress,
74    Cache,
75}
76
77pub enum Prepared<S> {
78    Egress(Egress<S>),
79    Cache(CacheTemplate),
80}
81
82pub struct ResponseView {
83    pub status: StatusCode,
84    pub headers: Shared,
85    pub body: Shared,
86}
87
88pub trait ResponseSink {
89    fn emit<'a, 'body, I>(&mut self, status: StatusCode, headers: I, body: Body<'body>)
90    where
91        I: Iterator<Item = Field<'a>>;
92}
93
94pub enum SyncShape {}
95pub enum StreamShape {}
96
97mod metadata_sealed {
98    pub trait Sealed {}
99}
100
101pub trait ShapeMetadata: metadata_sealed::Sealed {
102    type Kind;
103    type Stream: 'static;
104
105    const BODY_KIND: ResponseKind;
106}
107
108pub enum InlineShape {}
109pub enum StaticShape {}
110pub struct StreamingShape<S>(std::marker::PhantomData<fn() -> S>);
111
112impl metadata_sealed::Sealed for InlineShape {}
113impl metadata_sealed::Sealed for StaticShape {}
114impl<S> metadata_sealed::Sealed for StreamingShape<S> {}
115
116impl ShapeMetadata for InlineShape {
117    type Kind = SyncShape;
118    type Stream = NeverStream;
119
120    const BODY_KIND: ResponseKind = ResponseKind::Inline;
121}
122
123impl ShapeMetadata for StaticShape {
124    type Kind = SyncShape;
125    type Stream = NeverStream;
126
127    const BODY_KIND: ResponseKind = ResponseKind::Static;
128}
129
130impl<S> ShapeMetadata for StreamingShape<S>
131where
132    S: 'static,
133{
134    type Kind = StreamShape;
135    type Stream = S;
136
137    const BODY_KIND: ResponseKind = ResponseKind::Stream;
138}
139
140pub trait Shape<'req>: Sized + IntoResponseShape<'req, Shape = Self> {
141    type Metadata: ShapeMetadata;
142
143    fn prepare(
144        self,
145        mode: Preparation,
146        gzip: Option<&mut Gzip>,
147        out: &mut [u8],
148        date: &[u8; 29],
149    ) -> Prepared<<Self::Metadata as ShapeMetadata>::Stream>;
150
151    fn response_view(&self) -> Option<ResponseView> {
152        None
153    }
154
155    fn emit<E: ResponseSink>(self, _sink: &mut E) -> bool {
156        false
157    }
158}
159
160pub type ShapeKind<'req, S> = <<S as Shape<'req>>::Metadata as ShapeMetadata>::Kind;
161pub type ShapeStream<'req, S> = <<S as Shape<'req>>::Metadata as ShapeMetadata>::Stream;
162
163fn split_egress<R>(
164    response: R,
165    out: &mut [u8],
166    date: &[u8; 29],
167    write_inline: impl FnOnce(&R, &mut [u8], &[u8; 29]) -> Option<usize>,
168    write_split: impl FnOnce(R, &mut [u8], &[u8; 29]) -> Option<(usize, Shared)>,
169) -> Egress<NeverStream> {
170    if let Some(written) = write_inline(&response, out, date) {
171        return Egress::Inline { written };
172    }
173    match write_split(response, out, date) {
174        Some((head, body)) => Egress::Shared { head, body },
175        None => Egress::Failed,
176    }
177}
178
179macro_rules! split_response {
180    ($response:ident, $out:ident, $date:ident) => {
181        split_egress(
182            $response,
183            $out,
184            $date,
185            |response, out, date| response.write_into_slice(out, date),
186            |response, out, date| response.write_head_split(out, date),
187        )
188    };
189}
190
191macro_rules! cache_result {
192    ($response:ident, inline) => {{
193        let (bytes, date_offset) = $response.preserialize();
194        Prepared::Cache(CacheTemplate::Inline {
195            bytes,
196            date_offset: Some(date_offset),
197        })
198    }};
199    ($response:ident, unsupported) => {
200        Prepared::Egress(Egress::Failed)
201    };
202}
203
204macro_rules! prepare_method {
205    (|$this:ident, $mode:ident, $gzip:ident, $out:ident, $date:ident| $body:block) => {
206        fn prepare(
207            $this,
208            $mode: Preparation,
209            $gzip: Option<&mut Gzip>,
210            $out: &mut [u8],
211            $date: &[u8; 29],
212        ) -> Prepared<<Self::Metadata as ShapeMetadata>::Stream> $body
213    };
214}
215
216macro_rules! prepare_split {
217    ($cache:ident) => {
218        prepare_method!(|self, mode, _gzip, out, date| {
219            if mode == Preparation::Cache {
220                return cache_result!(self, $cache);
221            }
222            Prepared::Egress(split_response!(self, out, date))
223        });
224    };
225}
226
227macro_rules! impl_identity_shape {
228    (
229        [$($generics:tt)*]
230        $req:lifetime => $target:ty
231        $(where [$($bounds:tt)*])?
232    ) => {
233        impl<$($generics)*> IntoResponseShape<$req> for $target
234        $(where $($bounds)*)?
235        {
236            type Shape = Self;
237
238            fn into_response_shape(self) -> Self::Shape {
239                self
240            }
241        }
242    };
243}
244
245impl_identity_shape! {
246    ['req, const N: usize, S]
247    'req => FixedResponse<'req, N, S>
248    where [S: StaticHeaders]
249}
250
251impl<'req, const N: usize, S> Shape<'req> for FixedResponse<'req, N, S>
252where
253    S: StaticHeaders,
254{
255    type Metadata = InlineShape;
256
257    prepare_method!(|self, mode, gzip, out, date| {
258        if mode == Preparation::Cache {
259            return cache_result!(self, inline);
260        }
261        if mode == Preparation::Compress
262            && !self.has_content_encoding()
263            && !self.body_ref().is_empty()
264            && let Some(body) = gzip.and_then(|gzip| gzip.encode(self.body_ref()))
265        {
266            let body_len = body.len();
267            let egress = match self.write_gzip_head(out, date, body_len) {
268                Some(head) => Egress::Pooled { head, body },
269                None => Egress::Failed,
270            };
271            return Prepared::Egress(egress);
272        }
273        Prepared::Egress(split_response!(self, out, date))
274    });
275
276    fn response_view(&self) -> Option<ResponseView> {
277        Some(ResponseView {
278            status: self.status(),
279            headers: self.wire_headers(),
280            body: self.body.clone(),
281        })
282    }
283
284    fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
285        let Self { status, head, body } = self;
286        sink.emit(status, head.fields(), Body::Shared(body));
287        true
288    }
289}
290
291impl_identity_shape! {
292    ['req, B, const N: usize, S]
293    'req => EncodedResponse<'req, B, N, S>
294    where [
295        B: EncodedBody,
296        S: StaticHeaders,
297    ]
298}
299
300impl<'req, B, const N: usize, S> Shape<'req> for EncodedResponse<'req, B, N, S>
301where
302    B: EncodedBody,
303    S: StaticHeaders,
304{
305    type Metadata = InlineShape;
306
307    prepare_method!(|self, mode, _gzip, out, date| {
308        if mode == Preparation::Cache {
309            let Some((bytes, date_offset)) = self.preserialize() else {
310                return Prepared::Egress(Egress::Failed);
311            };
312            return Prepared::Cache(CacheTemplate::Inline {
313                bytes,
314                date_offset: Some(date_offset),
315            });
316        }
317        Prepared::Egress(split_response!(self, out, date))
318    });
319
320    fn response_view(&self) -> Option<ResponseView> {
321        Some(ResponseView {
322            status: self.status(),
323            headers: self.wire_headers(),
324            body: self.encoded_body()?,
325        })
326    }
327
328    fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
329        let Self {
330            status,
331            head,
332            body,
333            body_len,
334        } = self;
335        let mut encoded = vec![0; body_len];
336        if body.encode_into(&mut encoded).is_err() {
337            return false;
338        }
339        sink.emit(status, head.fields(), Body::Owned(encoded));
340        true
341    }
342}
343
344impl_identity_shape! {
345    ['req, const N: usize]
346    'req => MonoResponseInner<'req, N>
347}
348
349impl<'req, const N: usize> Shape<'req> for MonoResponseInner<'req, N> {
350    type Metadata = InlineShape;
351
352    prepare_split!(unsupported);
353
354    fn response_view(&self) -> Option<ResponseView> {
355        Some(ResponseView {
356            status: self.status(),
357            headers: self.wire_headers(),
358            body: self.body.clone().into_shared(),
359        })
360    }
361
362    fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
363        let Self {
364            status,
365            headers,
366            head,
367            body,
368        } = self;
369        let dynamic = match headers.as_deref() {
370            Some(headers) => headers,
371            None => HeaderList::empty_static(),
372        };
373        match head {
374            HotHeadInner::Wire(wire) => {
375                sink.emit(
376                    status,
377                    WireHeaderFields::new(wire.as_ref()).chain(dynamic.fields()),
378                    body,
379                );
380            }
381            HotHeadInner::Direct(head) => {
382                sink.emit(status, head.fields().chain(dynamic.fields()), body);
383            }
384        }
385        true
386    }
387}
388
389impl_identity_shape! {
390    ['req, const N: usize, S]
391    'req => StaticResponseInner<'req, N, S>
392    where [S: StaticHeaders]
393}
394
395impl<'req, const N: usize, S> Shape<'req> for StaticResponseInner<'req, N, S>
396where
397    S: StaticHeaders,
398{
399    type Metadata = StaticShape;
400
401    prepare_method!(|self, mode, _gzip, out, date| {
402        if mode == Preparation::Cache {
403            let (head, date_offset, body) = self.preserialize_static();
404            return Prepared::Cache(CacheTemplate::Static {
405                head,
406                date_offset: Some(date_offset),
407                body,
408            });
409        }
410        Prepared::Egress(match self.write_head_only(out, date) {
411            Some((head, body)) => Egress::Static { head, body },
412            None => Egress::Failed,
413        })
414    });
415
416    fn response_view(&self) -> Option<ResponseView> {
417        Some(ResponseView {
418            status: self.status(),
419            headers: self.wire_headers(),
420            body: Shared::from_static(self.body_ref()),
421        })
422    }
423
424    fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
425        let Self { status, head, body } = self;
426        sink.emit(status, head.fields(), Body::StaticSlice(body));
427        true
428    }
429}
430
431impl_identity_shape! {
432    ['req]
433    'req => Chunked
434}
435
436impl<'req> Shape<'req> for Chunked {
437    type Metadata = InlineShape;
438
439    prepare_split!(unsupported);
440}
441
442impl_identity_shape! {
443    ['req, S]
444    'req => Stream<S>
445    where [S: 'static]
446}
447
448impl<'req, S> Shape<'req> for Stream<S>
449where
450    S: 'static,
451{
452    type Metadata = StreamingShape<S>;
453
454    prepare_method!(|self, mode, _gzip, out, date| {
455        if mode == Preparation::Cache {
456            return Prepared::Egress(Egress::Failed);
457        }
458        Prepared::Egress(match self.write_head_stream(out, date) {
459            Some((head, stream)) => Egress::Stream { head, stream },
460            None => Egress::Failed,
461        })
462    });
463}
464
465impl_identity_shape! {
466    ['req, const N: usize]
467    'req => Serve<'req, N>
468}
469
470impl<'req, const N: usize> Shape<'req> for Serve<'req, N> {
471    type Metadata = InlineShape;
472
473    prepare_method!(|self, mode, gzip, out, date| {
474        match self {
475            Self::Fixed(response) => response.prepare(mode, gzip, out, date),
476            Self::Mono(response) => response.prepare(mode, gzip, out, date),
477            Self::Chunked(response) => response.prepare(mode, gzip, out, date),
478        }
479    });
480
481    fn response_view(&self) -> Option<ResponseView> {
482        match self {
483            Self::Fixed(response) => response.response_view(),
484            Self::Mono(response) => response.response_view(),
485            Self::Chunked(_) => None,
486        }
487    }
488
489    fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
490        match self {
491            Self::Fixed(response) => response.emit(sink),
492            Self::Mono(response) => response.emit(sink),
493            Self::Chunked(_) => false,
494        }
495    }
496}