Skip to main content

sark_core/http/response/
fixed.rs

1use http::StatusCode;
2use o3::buffer::Shared;
3
4use super::wire_emit::{
5    ContentLength, DATE_LEN, HeadWrite, HeaderSection, PLACEHOLDER_DATE, ProvenWireWriter,
6};
7use super::{
8    DEFAULT_HEADER_CAPACITY, HeadInner, HeaderTemplate, Headers, StaticHeaderBytes,
9    StaticHeaderFields, StaticHeaders,
10};
11
12const GZIP_HEADERS: &[u8] = b"Content-Encoding: gzip\r\nVary: Accept-Encoding\r\n";
13
14struct GzipHeaders<'a, 'req, const N: usize, S>(&'a HeadInner<'req, N, S>);
15
16impl<const N: usize, S> HeaderSection for GzipHeaders<'_, '_, N, S>
17where
18    S: StaticHeaders,
19{
20    fn wire_len(&self) -> usize {
21        HeaderSection::wire_len(self.0).saturating_add(GZIP_HEADERS.len())
22    }
23
24    fn write_headers(&self, out: &mut ProvenWireWriter<'_>) {
25        self.0.write_headers(out);
26        out.put(GZIP_HEADERS);
27    }
28}
29
30#[derive(Clone, Debug)]
31pub struct FixedResponse<'req, const N: usize = DEFAULT_HEADER_CAPACITY, S = StaticHeaderBytes> {
32    pub(super) status: StatusCode,
33    pub(super) head: HeadInner<'req, N, S>,
34    pub(super) body: Shared,
35}
36
37impl<'req, const N: usize> FixedResponse<'req, N> {
38    pub fn direct<B>(
39        status: StatusCode,
40        static_headers: &'static [u8],
41        headers: Headers<'req, N>,
42        body: B,
43    ) -> Self
44    where
45        B: Into<Shared>,
46    {
47        let body = body.into();
48        Self {
49            status,
50            head: HeadInner::new(static_headers, headers),
51            body,
52        }
53    }
54}
55
56impl<'req, const N: usize> FixedResponse<'req, N, StaticHeaderFields> {
57    #[doc(hidden)]
58    pub fn structured<B>(
59        status: StatusCode,
60        static_headers: &'static HeaderTemplate,
61        headers: Headers<'req, N>,
62        body: B,
63    ) -> Self
64    where
65        B: Into<Shared>,
66    {
67        let body = body.into();
68        Self {
69            status,
70            head: HeadInner::structured(static_headers, headers),
71            body,
72        }
73    }
74}
75
76impl<'req, const N: usize, S> FixedResponse<'req, N, S>
77where
78    S: StaticHeaders,
79{
80    pub fn status(&self) -> StatusCode {
81        self.status
82    }
83
84    pub fn body_ref(&self) -> &[u8] {
85        self.body.as_ref()
86    }
87
88    pub fn has_content_encoding(&self) -> bool {
89        self.head.headers().has_content_encoding()
90    }
91
92    pub fn wire_headers(&self) -> Shared {
93        self.head.wire_headers()
94    }
95
96    fn head_write(&self) -> (HeadWrite<'_, HeadInner<'req, N, S>, ContentLength>, &[u8]) {
97        (
98            self.head_write_with_len(self.body.len()),
99            self.body.as_ref(),
100        )
101    }
102
103    fn head_write_with_len(
104        &self,
105        body_len: usize,
106    ) -> HeadWrite<'_, HeadInner<'req, N, S>, ContentLength> {
107        HeadWrite {
108            status: self.status,
109            headers: &self.head,
110            framing: ContentLength(body_len),
111        }
112    }
113
114    pub fn preserialize(&self) -> (Vec<u8>, usize) {
115        let (head, body) = self.head_write();
116        let total = head.wire_len().saturating_add(body.len());
117        let mut buf = vec![0u8; total];
118        let mut out = ProvenWireWriter::exact(&mut buf);
119        let date_offset = head.emit(&mut out, PLACEHOLDER_DATE);
120        out.put(body);
121        out.finish(total);
122        (buf, date_offset)
123    }
124
125    pub fn write_into_slice(&self, out: &mut [u8], date: &[u8; 29]) -> Option<usize> {
126        let (head, body) = self.head_write();
127        let total = head.wire_len().checked_add(body.len())?;
128        let mut out = ProvenWireWriter::new(out, total)?;
129        head.emit(&mut out, date);
130        out.put(body);
131        Some(out.finish(total))
132    }
133
134    pub fn write_head_split(self, out: &mut [u8], date: &[u8; 29]) -> Option<(usize, Shared)> {
135        let (head, _) = self.head_write();
136        let head_len = head.wire_len();
137        let mut out = ProvenWireWriter::new(out, head_len)?;
138        head.emit(&mut out, date);
139        Some((out.finish(head_len), self.body))
140    }
141
142    pub fn write_gzip_head(
143        self,
144        out: &mut [u8],
145        date: &[u8; 29],
146        body_len: usize,
147    ) -> Option<usize> {
148        let headers = GzipHeaders(&self.head);
149        let head = HeadWrite {
150            status: self.status,
151            headers: &headers,
152            framing: ContentLength(body_len),
153        };
154        let head_len = head.wire_len();
155        let mut out = ProvenWireWriter::new(out, head_len)?;
156        head.emit(&mut out, date);
157        Some(out.finish(head_len))
158    }
159}
160
161impl FixedResponse<'static> {
162    pub fn write_preserialized(
163        out: &mut [u8],
164        template: &[u8],
165        date_offset: Option<usize>,
166        date: &[u8; 29],
167    ) -> Option<usize> {
168        let total = template.len();
169        if out.len() < total {
170            return None;
171        }
172        out[..total].copy_from_slice(template);
173        if let Some(off) = date_offset {
174            out[off..off + DATE_LEN].copy_from_slice(date);
175        }
176        Some(total)
177    }
178}