1use http::StatusCode;
2use o3::buffer::{Pooled, Shared};
3
4use crate::http::body_kind::ResponseKind;
5use crate::http::compress::Gzip;
6
7use super::{
8 Body, Chunked, EncodedBody, EncodedResponse, FixedResponse, HeaderList, HotHeadInner,
9 IntoResponseShape, MonoResponseInner, NeverStream, Serve, StaticHeaders, StaticResponseInner,
10 Stream, WireHeaderFields,
11};
12use crate::http::Field;
13
14pub enum Egress<S> {
15 Inline { written: usize },
16 Static { head: usize, body: &'static [u8] },
17 Shared { head: usize, body: Shared },
18 Pooled { head: usize, body: Pooled },
19 Stream { head: usize, stream: S },
20 Failed,
21}
22
23pub enum CacheTemplate {
24 Inline {
25 bytes: Vec<u8>,
26 date_offset: Option<usize>,
27 },
28 Static {
29 head: Vec<u8>,
30 date_offset: Option<usize>,
31 body: &'static [u8],
32 },
33}
34
35impl CacheTemplate {
36 pub fn configure_head(&mut self, emit_date: bool, emit_server: bool) {
37 let (template, date_offset) = match self {
38 Self::Inline { bytes, date_offset } => (bytes, date_offset),
39 Self::Static {
40 head, date_offset, ..
41 } => (head, date_offset),
42 };
43 if let Some(offset) = *date_offset {
44 use super::wire_emit::{CRLF, DATE_PREFIX, SERVER_DATE_TERMINATOR_LEN, SERVER_LINE};
45 if emit_date && emit_server {
46 return;
47 }
48 let term_start = offset - DATE_PREFIX.len() - SERVER_LINE.len();
49 let term_end = term_start + SERVER_DATE_TERMINATOR_LEN;
50 let mut tail = Vec::with_capacity(SERVER_DATE_TERMINATOR_LEN);
51 if emit_server {
52 tail.extend_from_slice(SERVER_LINE);
53 }
54 *date_offset = if emit_date {
55 use super::wire_emit::DATE_LEN;
56 tail.extend_from_slice(DATE_PREFIX);
57 let offset = term_start + tail.len();
58 tail.extend_from_slice(&[0u8; DATE_LEN]);
59 tail.extend_from_slice(CRLF);
60 Some(offset)
61 } else {
62 None
63 };
64 tail.extend_from_slice(CRLF);
65 template.splice(term_start..term_end, tail);
66 }
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub enum Preparation {
72 Plain,
73 Compress,
74 Cache,
75}
76
77pub enum Prepared<S> {
78 Egress(Egress<S>),
79 Cache(CacheTemplate),
80}
81
82pub struct ResponseView {
83 pub status: StatusCode,
84 pub headers: Shared,
85 pub body: Shared,
86}
87
88pub trait ResponseSink {
89 fn emit<'a, 'body, I>(&mut self, status: StatusCode, headers: I, body: Body<'body>)
90 where
91 I: Iterator<Item = Field<'a>>;
92}
93
94pub enum SyncShape {}
95pub enum StreamShape {}
96
97mod metadata_sealed {
98 pub trait Sealed {}
99}
100
101pub trait ShapeMetadata: metadata_sealed::Sealed {
102 type Kind;
103 type Stream: 'static;
104
105 const BODY_KIND: ResponseKind;
106}
107
108pub enum InlineShape {}
109pub enum StaticShape {}
110pub struct StreamingShape<S>(std::marker::PhantomData<fn() -> S>);
111
112impl metadata_sealed::Sealed for InlineShape {}
113impl metadata_sealed::Sealed for StaticShape {}
114impl<S> metadata_sealed::Sealed for StreamingShape<S> {}
115
116impl ShapeMetadata for InlineShape {
117 type Kind = SyncShape;
118 type Stream = NeverStream;
119
120 const BODY_KIND: ResponseKind = ResponseKind::Inline;
121}
122
123impl ShapeMetadata for StaticShape {
124 type Kind = SyncShape;
125 type Stream = NeverStream;
126
127 const BODY_KIND: ResponseKind = ResponseKind::Static;
128}
129
130impl<S> ShapeMetadata for StreamingShape<S>
131where
132 S: 'static,
133{
134 type Kind = StreamShape;
135 type Stream = S;
136
137 const BODY_KIND: ResponseKind = ResponseKind::Stream;
138}
139
140pub trait Shape<'req>: Sized + IntoResponseShape<'req, Shape = Self> {
141 type Metadata: ShapeMetadata;
142
143 fn prepare(
144 self,
145 mode: Preparation,
146 gzip: Option<&mut Gzip>,
147 out: &mut [u8],
148 date: &[u8; 29],
149 ) -> Prepared<<Self::Metadata as ShapeMetadata>::Stream>;
150
151 fn response_view(&self) -> Option<ResponseView> {
152 None
153 }
154
155 fn emit<E: ResponseSink>(self, _sink: &mut E) -> bool {
156 false
157 }
158}
159
160pub type ShapeKind<'req, S> = <<S as Shape<'req>>::Metadata as ShapeMetadata>::Kind;
161pub type ShapeStream<'req, S> = <<S as Shape<'req>>::Metadata as ShapeMetadata>::Stream;
162
163fn split_egress<R>(
164 response: R,
165 out: &mut [u8],
166 date: &[u8; 29],
167 write_inline: impl FnOnce(&R, &mut [u8], &[u8; 29]) -> Option<usize>,
168 write_split: impl FnOnce(R, &mut [u8], &[u8; 29]) -> Option<(usize, Shared)>,
169) -> Egress<NeverStream> {
170 if let Some(written) = write_inline(&response, out, date) {
171 return Egress::Inline { written };
172 }
173 match write_split(response, out, date) {
174 Some((head, body)) => Egress::Shared { head, body },
175 None => Egress::Failed,
176 }
177}
178
179macro_rules! split_response {
180 ($response:ident, $out:ident, $date:ident) => {
181 split_egress(
182 $response,
183 $out,
184 $date,
185 |response, out, date| response.write_into_slice(out, date),
186 |response, out, date| response.write_head_split(out, date),
187 )
188 };
189}
190
191macro_rules! cache_result {
192 ($response:ident, inline) => {{
193 let (bytes, date_offset) = $response.preserialize();
194 Prepared::Cache(CacheTemplate::Inline {
195 bytes,
196 date_offset: Some(date_offset),
197 })
198 }};
199 ($response:ident, unsupported) => {
200 Prepared::Egress(Egress::Failed)
201 };
202}
203
204macro_rules! prepare_method {
205 (|$this:ident, $mode:ident, $gzip:ident, $out:ident, $date:ident| $body:block) => {
206 fn prepare(
207 $this,
208 $mode: Preparation,
209 $gzip: Option<&mut Gzip>,
210 $out: &mut [u8],
211 $date: &[u8; 29],
212 ) -> Prepared<<Self::Metadata as ShapeMetadata>::Stream> $body
213 };
214}
215
216macro_rules! prepare_split {
217 ($cache:ident) => {
218 prepare_method!(|self, mode, _gzip, out, date| {
219 if mode == Preparation::Cache {
220 return cache_result!(self, $cache);
221 }
222 Prepared::Egress(split_response!(self, out, date))
223 });
224 };
225}
226
227macro_rules! impl_identity_shape {
228 (
229 [$($generics:tt)*]
230 $req:lifetime => $target:ty
231 $(where [$($bounds:tt)*])?
232 ) => {
233 impl<$($generics)*> IntoResponseShape<$req> for $target
234 $(where $($bounds)*)?
235 {
236 type Shape = Self;
237
238 fn into_response_shape(self) -> Self::Shape {
239 self
240 }
241 }
242 };
243}
244
245impl_identity_shape! {
246 ['req, const N: usize, S]
247 'req => FixedResponse<'req, N, S>
248 where [S: StaticHeaders]
249}
250
251impl<'req, const N: usize, S> Shape<'req> for FixedResponse<'req, N, S>
252where
253 S: StaticHeaders,
254{
255 type Metadata = InlineShape;
256
257 prepare_method!(|self, mode, gzip, out, date| {
258 if mode == Preparation::Cache {
259 return cache_result!(self, inline);
260 }
261 if mode == Preparation::Compress
262 && !self.has_content_encoding()
263 && !self.body_ref().is_empty()
264 && let Some(body) = gzip.and_then(|gzip| gzip.encode(self.body_ref()))
265 {
266 let body_len = body.len();
267 let egress = match self.write_gzip_head(out, date, body_len) {
268 Some(head) => Egress::Pooled { head, body },
269 None => Egress::Failed,
270 };
271 return Prepared::Egress(egress);
272 }
273 Prepared::Egress(split_response!(self, out, date))
274 });
275
276 fn response_view(&self) -> Option<ResponseView> {
277 Some(ResponseView {
278 status: self.status(),
279 headers: self.wire_headers(),
280 body: self.body.clone(),
281 })
282 }
283
284 fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
285 let Self { status, head, body } = self;
286 sink.emit(status, head.fields(), Body::Shared(body));
287 true
288 }
289}
290
291impl_identity_shape! {
292 ['req, B, const N: usize, S]
293 'req => EncodedResponse<'req, B, N, S>
294 where [
295 B: EncodedBody,
296 S: StaticHeaders,
297 ]
298}
299
300impl<'req, B, const N: usize, S> Shape<'req> for EncodedResponse<'req, B, N, S>
301where
302 B: EncodedBody,
303 S: StaticHeaders,
304{
305 type Metadata = InlineShape;
306
307 prepare_method!(|self, mode, _gzip, out, date| {
308 if mode == Preparation::Cache {
309 let Some((bytes, date_offset)) = self.preserialize() else {
310 return Prepared::Egress(Egress::Failed);
311 };
312 return Prepared::Cache(CacheTemplate::Inline {
313 bytes,
314 date_offset: Some(date_offset),
315 });
316 }
317 Prepared::Egress(split_response!(self, out, date))
318 });
319
320 fn response_view(&self) -> Option<ResponseView> {
321 Some(ResponseView {
322 status: self.status(),
323 headers: self.wire_headers(),
324 body: self.encoded_body()?,
325 })
326 }
327
328 fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
329 let Self {
330 status,
331 head,
332 body,
333 body_len,
334 } = self;
335 let mut encoded = vec![0; body_len];
336 if body.encode_into(&mut encoded).is_err() {
337 return false;
338 }
339 sink.emit(status, head.fields(), Body::Owned(encoded));
340 true
341 }
342}
343
344impl_identity_shape! {
345 ['req, const N: usize]
346 'req => MonoResponseInner<'req, N>
347}
348
349impl<'req, const N: usize> Shape<'req> for MonoResponseInner<'req, N> {
350 type Metadata = InlineShape;
351
352 prepare_split!(unsupported);
353
354 fn response_view(&self) -> Option<ResponseView> {
355 Some(ResponseView {
356 status: self.status(),
357 headers: self.wire_headers(),
358 body: self.body.clone().into_shared(),
359 })
360 }
361
362 fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
363 let Self {
364 status,
365 headers,
366 head,
367 body,
368 } = self;
369 let dynamic = match headers.as_deref() {
370 Some(headers) => headers,
371 None => HeaderList::empty_static(),
372 };
373 match head {
374 HotHeadInner::Wire(wire) => {
375 sink.emit(
376 status,
377 WireHeaderFields::new(wire.as_ref()).chain(dynamic.fields()),
378 body,
379 );
380 }
381 HotHeadInner::Direct(head) => {
382 sink.emit(status, head.fields().chain(dynamic.fields()), body);
383 }
384 }
385 true
386 }
387}
388
389impl_identity_shape! {
390 ['req, const N: usize, S]
391 'req => StaticResponseInner<'req, N, S>
392 where [S: StaticHeaders]
393}
394
395impl<'req, const N: usize, S> Shape<'req> for StaticResponseInner<'req, N, S>
396where
397 S: StaticHeaders,
398{
399 type Metadata = StaticShape;
400
401 prepare_method!(|self, mode, _gzip, out, date| {
402 if mode == Preparation::Cache {
403 let (head, date_offset, body) = self.preserialize_static();
404 return Prepared::Cache(CacheTemplate::Static {
405 head,
406 date_offset: Some(date_offset),
407 body,
408 });
409 }
410 Prepared::Egress(match self.write_head_only(out, date) {
411 Some((head, body)) => Egress::Static { head, body },
412 None => Egress::Failed,
413 })
414 });
415
416 fn response_view(&self) -> Option<ResponseView> {
417 Some(ResponseView {
418 status: self.status(),
419 headers: self.wire_headers(),
420 body: Shared::from_static(self.body_ref()),
421 })
422 }
423
424 fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
425 let Self { status, head, body } = self;
426 sink.emit(status, head.fields(), Body::StaticSlice(body));
427 true
428 }
429}
430
431impl_identity_shape! {
432 ['req]
433 'req => Chunked
434}
435
436impl<'req> Shape<'req> for Chunked {
437 type Metadata = InlineShape;
438
439 prepare_split!(unsupported);
440}
441
442impl_identity_shape! {
443 ['req, S]
444 'req => Stream<S>
445 where [S: 'static]
446}
447
448impl<'req, S> Shape<'req> for Stream<S>
449where
450 S: 'static,
451{
452 type Metadata = StreamingShape<S>;
453
454 prepare_method!(|self, mode, _gzip, out, date| {
455 if mode == Preparation::Cache {
456 return Prepared::Egress(Egress::Failed);
457 }
458 Prepared::Egress(match self.write_head_stream(out, date) {
459 Some((head, stream)) => Egress::Stream { head, stream },
460 None => Egress::Failed,
461 })
462 });
463}
464
465impl_identity_shape! {
466 ['req, const N: usize]
467 'req => Serve<'req, N>
468}
469
470impl<'req, const N: usize> Shape<'req> for Serve<'req, N> {
471 type Metadata = InlineShape;
472
473 prepare_method!(|self, mode, gzip, out, date| {
474 match self {
475 Self::Fixed(response) => response.prepare(mode, gzip, out, date),
476 Self::Mono(response) => response.prepare(mode, gzip, out, date),
477 Self::Chunked(response) => response.prepare(mode, gzip, out, date),
478 }
479 });
480
481 fn response_view(&self) -> Option<ResponseView> {
482 match self {
483 Self::Fixed(response) => response.response_view(),
484 Self::Mono(response) => response.response_view(),
485 Self::Chunked(_) => None,
486 }
487 }
488
489 fn emit<E: ResponseSink>(self, sink: &mut E) -> bool {
490 match self {
491 Self::Fixed(response) => response.emit(sink),
492 Self::Mono(response) => response.emit(sink),
493 Self::Chunked(_) => false,
494 }
495 }
496}