sark_http/payload/response/
mod.rs1use o3::buffer::{
4 storage,
5 write::{Sink, Slice, 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(
79 out: &mut [u8],
80 limit: usize,
81 body: impl Encode,
82) -> Result<Slice<'_>, storage::BuildError<consume::Error>> {
83 consume::Error::check(limit, out.len()).map_err(storage::BuildError::Build)?;
84 let mut writer = Slice::new(&mut out[..limit]);
85 body.encode(&mut writer)
86 .map_err(storage::BuildError::Build)?;
87 Ok(writer)
88}
89
90use std::borrow::Cow;
91
92use headers::{List, Name};
93
94pub struct Response<'field, B = [u8; 0], T = (), S = Code, N = Name<Cow<'field, str>>> {
95 pub(crate) status: S,
96 pub(crate) headers: List<'field, N>,
97 pub(crate) trailers: T,
98 pub(crate) body: B,
99}
100
101impl<'field, B, T, S, N> Response<'field, B, T, S, N> {
102 pub fn status(&self) -> S
103 where
104 S: Copy,
105 {
106 self.status
107 }
108
109 pub fn headers(&self) -> &List<'field, N> {
110 &self.headers
111 }
112
113 pub fn body(&self) -> &B {
114 &self.body
115 }
116
117 pub fn body_mut(&mut self) -> &mut B {
118 &mut self.body
119 }
120
121 pub fn into_body(self) -> B {
122 self.body
123 }
124
125 pub fn with_body<U>(self, body: U) -> Response<'field, U, T, S, N> {
126 Response {
127 status: self.status,
128 headers: self.headers,
129 trailers: self.trailers,
130 body,
131 }
132 }
133}
134
135impl<'field, B, S, N> Response<'field, B, List<'field>, S, N> {
136 pub fn trailers(&self) -> &List<'field> {
137 &self.trailers
138 }
139}
140
141impl<'field> Response<'field, [u8; 0]> {
142 pub fn new(status: Code) -> Self {
143 Self {
144 status,
145 headers: List::new(),
146 trailers: (),
147 body: [],
148 }
149 }
150}
151
152impl<'field, 'req, 'd, B> dispatch::IntoOutput<'req, 'd> for Response<'field, B, (), Code>
153where
154 B: Body<'d>,
155{
156 type Output = fixed::Direct<List<'field, Name<Cow<'field, str>>>, B>;
157
158 fn into_output(self) -> Self::Output {
159 let Self {
160 status,
161 headers,
162 trailers: (),
163 body,
164 } = self;
165 fixed::Direct::new(status, headers, body)
166 }
167}
168
169const _: () = assert!(
170 core::mem::size_of::<Response<'static, [u8; 0]>>()
171 < core::mem::size_of::<Response<'static, [u8; 0], List<'static>, status::Code>>()
172);