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}