Skip to main content

sark_core/http/response/
fixed.rs

1use http::StatusCode;
2use o3::buffer::{Owned, Shared};
3
4use super::wire_emit::{ContentLength, DATE_LEN, HeadWrite, NO_DATE, Out};
5use super::{HeadInner, HeadersInner, InlineHeaderValue};
6
7#[derive(Clone, Debug)]
8pub struct FixedResponseInner<'req> {
9    pub(super) status: StatusCode,
10    pub(super) head: HeadInner<'req>,
11    pub(super) body: Shared,
12    pub(super) body_len_ascii: InlineHeaderValue,
13}
14
15pub type FixedResponse = FixedResponseInner<'static>;
16
17impl<'req> FixedResponseInner<'req> {
18    pub fn direct<B>(
19        status: StatusCode,
20        static_headers: &'static [u8],
21        headers: HeadersInner<'req>,
22        body: B,
23    ) -> Self
24    where
25        B: Into<Shared>,
26    {
27        let body = body.into();
28        Self {
29            status,
30            head: HeadInner::new(static_headers, headers),
31            body_len_ascii: InlineHeaderValue::from_decimal(body.len()),
32            body,
33        }
34    }
35
36    pub fn status(&self) -> StatusCode {
37        self.status
38    }
39
40    pub fn body_ref(&self) -> &[u8] {
41        self.body.as_ref()
42    }
43
44    pub fn has_content_encoding(&self) -> bool {
45        self.head.headers().has_content_encoding()
46    }
47
48    pub fn wire_headers(&self) -> Shared {
49        let mut out = Owned::with_capacity(self.head.wire_len());
50        self.head.write_into(&mut out);
51        out.freeze()
52    }
53
54    pub fn write_preserialized(
55        out: &mut [u8],
56        template: &[u8],
57        date_offset: usize,
58        date: &[u8; 29],
59    ) -> Option<usize> {
60        let total = template.len();
61        if out.len() < total {
62            return None;
63        }
64        out[..total].copy_from_slice(template);
65        if date_offset != NO_DATE {
66            out[date_offset..date_offset + DATE_LEN].copy_from_slice(date);
67        }
68        Some(total)
69    }
70
71    pub fn preserialize(&self) -> (Vec<u8>, usize) {
72        let dummy_date: &[u8; 29] = b"Mon, 01 Jan 2000 00:00:00 GMT";
73        let mut buf = vec![0u8; 4096];
74        let n = self
75            .write_into_slice(&mut buf, dummy_date)
76            .expect("preserialize: 4096 must be enough for any response");
77        buf.truncate(n);
78        let date_offset = buf
79            .windows(29)
80            .position(|w| w == dummy_date)
81            .expect("preserialize: dummy date must appear in output");
82        (buf, date_offset)
83    }
84
85    pub fn write_into_slice(&self, out: &mut [u8], date: &[u8; 29]) -> Option<usize> {
86        let status_str = self.status.as_str().as_bytes();
87        let reason = self
88            .status
89            .canonical_reason()
90            .map(str::as_bytes)
91            .unwrap_or(b"");
92        let cl_body = self.body_len_ascii.as_bytes();
93        let body = self.body.as_ref();
94
95        let head = HeadWrite {
96            status_str,
97            reason,
98            headers: &self.head,
99            framing: ContentLength(cl_body),
100        };
101        let total = head.wire_len() + body.len();
102        if out.len() < total {
103            return None;
104        }
105
106        let mut off = 0usize;
107        head.write(out, &mut off, date);
108        Out::put(out, &mut off, body);
109        Some(off)
110    }
111
112    pub fn write_head_split(self, out: &mut [u8], date: &[u8; 29]) -> Option<(usize, Shared)> {
113        let status_str = self.status.as_str().as_bytes();
114        let reason = self
115            .status
116            .canonical_reason()
117            .map(str::as_bytes)
118            .unwrap_or(b"");
119        let cl_body = self.body_len_ascii.as_bytes();
120        let head = HeadWrite {
121            status_str,
122            reason,
123            headers: &self.head,
124            framing: ContentLength(cl_body),
125        };
126        if out.len() < head.wire_len() {
127            return None;
128        }
129        let mut off = 0usize;
130        head.write(out, &mut off, date);
131        Some((off, self.body))
132    }
133
134    pub fn apply_gzip(&mut self, compressed: Shared) {
135        const GZIP_EXTRA: &[u8] = b"Content-Encoding: gzip\r\nVary: Accept-Encoding\r\n";
136        self.body_len_ascii = InlineHeaderValue::from_decimal(compressed.len());
137        self.body = compressed;
138        self.head.set_extra_static(GZIP_EXTRA);
139    }
140}