Skip to main content

sark_core/http/response/
fixed.rs

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