sark_http/payload/response/
mod.rs1use o3::buffer::{
4 storage,
5 write::{Sink, Slice, Split, consume},
6};
7
8use crate::payload::response::status::Code;
9
10mod body;
11pub mod bounded;
12pub mod dispatch;
13pub mod exact;
14pub mod fixed;
15pub mod headers;
16pub mod statics;
17pub mod status;
18
19pub use body::Body;
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23#[repr(u8)]
24pub enum Method {
25 Other,
26 Head,
27 Connect,
28}
29
30impl Method {
31 pub fn from_bytes(method: &[u8]) -> Self {
32 match method.len() {
33 4 if method == b"HEAD" => Self::Head,
34 7 if method == b"CONNECT" => Self::Connect,
35 0..=3 | 5..=6 | 8.. => Self::Other,
36 4 | 7 => Self::Other,
37 }
38 }
39}
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub enum Content {
44 Forbidden,
46 Suppressed,
48 Tunnel,
50 Empty,
52 Allowed,
53}
54
55impl Content {
56 pub const fn new(method: Method, status: u16) -> Self {
57 if matches!(method, Method::Connect) && status >= 200 && status < 300 {
58 Self::Tunnel
59 } else if status < 200 || status == 204 {
60 Self::Forbidden
61 } else if matches!(method, Method::Head) || status == 304 {
62 Self::Suppressed
63 } else if status == 205 {
64 Self::Empty
65 } else {
66 Self::Allowed
67 }
68 }
69}
70
71pub trait Encode: Sized {
73 fn encode<W>(self, out: &mut W) -> Result<(), consume::Error>
74 where
75 W: Sink<Error = consume::Error>;
76}
77
78fn try_encode<'writer, 'target, L: std::borrow::BorrowMut<usize>>(
79 out: &'writer mut Slice<'target, L>,
80 limit: usize,
81 body: impl Encode,
82) -> Result<Split<'writer, 'target, L>, storage::BuildError<consume::Error>> {
83 let mut split = out
84 .try_split(0, limit)
85 .map_err(storage::BuildError::Build)?;
86 body.encode(&mut split.body())
87 .map_err(storage::BuildError::Build)?;
88 Ok(split)
89}
90
91use std::borrow::Cow;
92
93use headers::{List, Name};
94
95pub struct Response<'field, B = [u8; 0], T = (), S = Code, N = Name<Cow<'field, str>>> {
96 pub(crate) status: S,
97 pub(crate) headers: List<'field, N>,
98 pub(crate) trailers: T,
99 pub(crate) body: B,
100}
101
102impl<'field, B, T, S, N> Response<'field, B, T, S, N> {
103 pub fn status(&self) -> S
104 where
105 S: Copy,
106 {
107 self.status
108 }
109
110 pub fn headers(&self) -> &List<'field, N> {
111 &self.headers
112 }
113
114 pub fn body(&self) -> &B {
115 &self.body
116 }
117
118 pub fn body_mut(&mut self) -> &mut B {
119 &mut self.body
120 }
121
122 pub fn into_body(self) -> B {
123 self.body
124 }
125
126 pub fn with_body<U>(self, body: U) -> Response<'field, U, T, S, N> {
127 Response {
128 status: self.status,
129 headers: self.headers,
130 trailers: self.trailers,
131 body,
132 }
133 }
134}
135
136impl<'field, B, S, N> Response<'field, B, List<'field>, S, N> {
137 pub fn trailers(&self) -> &List<'field> {
138 &self.trailers
139 }
140}
141
142impl<'field> Response<'field, [u8; 0]> {
143 pub fn new(status: Code) -> Self {
144 Self {
145 status,
146 headers: List::new(),
147 trailers: (),
148 body: [],
149 }
150 }
151}
152
153impl<'field, 'req, 'd, B> dispatch::IntoOutput<'req, 'd> for Response<'field, B, (), Code>
154where
155 B: Body<'d>,
156{
157 type Output = fixed::Direct<List<'field, Name<Cow<'field, str>>>, B>;
158
159 fn into_output(self) -> Self::Output {
160 let Self {
161 status,
162 headers,
163 trailers: (),
164 body,
165 } = self;
166 fixed::Direct::new(status, headers, body)
167 }
168}
169
170const _: () = assert!(
171 core::mem::size_of::<Response<'static, [u8; 0]>>()
172 < core::mem::size_of::<Response<'static, [u8; 0], List<'static>, status::Code>>()
173);