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