sark_core/http/response/
fixed.rs1use 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}