sark_core/http/response/
owned.rs1use http::{HeaderName, HeaderValue, StatusCode};
2use o3::buffer::{Owned, Shared};
3use serde::Serialize;
4
5use super::{Body, BodyInner, FixedResponseInner, HeaderList, IntoBody, MonoResponseInner};
6
7#[derive(Clone)]
8pub struct Response {
9 pub(super) status: StatusCode,
10 pub(super) headers: HeaderList,
11 pub(super) wire_headers: Owned,
12 pub(super) body: Body,
13 pub(super) chunked_parts: Option<Vec<Shared>>,
14}
15
16impl std::fmt::Debug for Response {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 f.debug_struct("Response")
19 .field("status", &self.status)
20 .field("headers", &self.headers)
21 .field("wire_headers_len", &self.wire_headers.len())
22 .field("body_len", &self.body.len())
23 .field("chunked", &self.chunked_parts.is_some())
24 .finish()
25 }
26}
27
28impl Response {
29 pub fn new(status: StatusCode) -> Self {
30 Self {
31 status,
32 headers: HeaderList::new(),
33 wire_headers: Owned::new(),
34 body: Body::empty(),
35 chunked_parts: None,
36 }
37 }
38
39 pub fn ok() -> Self {
40 Self::new(StatusCode::OK)
41 }
42
43 pub fn text(body: &str) -> Self {
44 Self::text_with_status(StatusCode::OK, body)
45 }
46
47 pub fn text_with_status(status: StatusCode, body: &str) -> Self {
48 let mut resp = Self::new(status);
49 resp.content_type("text/plain");
50 resp.set_body_str(body);
51 resp
52 }
53
54 pub fn json<T: Serialize>(value: &T) -> Result<Self, serde_json::Error> {
55 Self::json_with_status(StatusCode::OK, value)
56 }
57
58 pub fn json_with_status<T: Serialize>(
59 status: StatusCode,
60 value: &T,
61 ) -> Result<Self, serde_json::Error> {
62 let s = serde_json::to_string(value)?;
63 let mut resp = Self::new(status);
64 resp.content_type("application/json");
65 resp.set_body_str(&s);
66 Ok(resp)
67 }
68
69 pub fn insert_header(&mut self, name: HeaderName, value: HeaderValue) -> &mut Self {
70 let _ = self.headers.insert(name, value);
71 self
72 }
73
74 pub fn append_wire_header(&mut self, name: &'static str, value: &str) -> &mut Self {
75 Self::assert_wire_header_name(name);
76 assert!(
77 !value.as_bytes().iter().any(|b| *b == b'\r' || *b == b'\n'),
78 "wire header value must not contain CR/LF"
79 );
80 self.append_wire_header_bytes(name, value.as_bytes())
81 }
82
83 pub fn append_wire_header_static(
84 &mut self,
85 name: &'static str,
86 value: &'static str,
87 ) -> &mut Self {
88 Self::assert_wire_header_name(name);
89 self.append_wire_header_bytes(name, value.as_bytes())
90 }
91
92 fn append_wire_header_bytes(&mut self, name: &str, value: &[u8]) -> &mut Self {
93 self.wire_headers.extend_from_slice(name.as_bytes());
94 self.wire_headers.extend_from_slice(b": ");
95 self.wire_headers.extend_from_slice(value);
96 self.wire_headers.extend_from_slice(b"\r\n");
97 self
98 }
99
100 pub fn content_type(&mut self, value: &'static str) -> &mut Self {
101 let _ = self
102 .headers
103 .insert("content-type", HeaderValue::from_static(value));
104 self
105 }
106
107 pub fn not_found() -> Self {
108 Self::new(StatusCode::NOT_FOUND)
109 }
110
111 pub fn status(&self) -> StatusCode {
112 self.status
113 }
114
115 pub fn set_status(&mut self, status: StatusCode) {
116 self.status = status;
117 }
118
119 pub fn headers(&self) -> &HeaderList {
120 &self.headers
121 }
122
123 pub fn headers_mut(&mut self) -> &mut HeaderList {
124 &mut self.headers
125 }
126
127 pub fn wire_headers(&self) -> &[u8] {
128 self.wire_headers.as_ref()
129 }
130
131 pub fn has_wire_headers(&self) -> bool {
132 !self.wire_headers.is_empty()
133 }
134
135 pub fn body(&self) -> &[u8] {
136 self.body.as_bytes()
137 }
138
139 pub fn body_is_shared(&self) -> bool {
140 self.body.is_shared()
141 }
142
143 pub fn body_mut(&mut self) -> &mut Owned {
144 self.body.as_owned_mut()
145 }
146
147 pub fn into_body(self) -> Owned {
148 self.body.into_owned()
149 }
150
151 pub fn into_body_bytes(self) -> Shared {
152 self.body.into_bytes()
153 }
154
155 pub fn set_body<B>(&mut self, body: B)
156 where
157 B: IntoBody<'static>,
158 {
159 self.body = body.into_response_body();
160 }
161
162 pub fn set_body_str(&mut self, body: &str) -> &mut Self {
163 self.body = BodyInner::Owned(Owned::from(body.as_bytes()));
164 self
165 }
166
167 pub fn body_str(&self) -> Option<&str> {
168 std::str::from_utf8(self.body.as_bytes()).ok()
169 }
170
171 pub fn push_chunk(&mut self, data: impl Into<Shared>) {
172 self.chunked_parts
173 .get_or_insert_with(Vec::new)
174 .push(data.into());
175 }
176
177 pub fn chunked_parts(&self) -> Option<&[Shared]> {
178 self.chunked_parts.as_deref()
179 }
180
181 pub fn is_chunked(&self) -> bool {
182 self.chunked_parts.is_some()
183 }
184
185 fn assert_wire_header_name(name: &str) {
186 assert!(!name.is_empty(), "wire header name must not be empty");
187 assert!(
188 !name
189 .as_bytes()
190 .iter()
191 .any(|b| *b == b':' || *b == b'\r' || *b == b'\n'),
192 "wire header name must not contain separators"
193 );
194 assert!(
195 !matches!(
196 name,
197 "date" | "server" | "content-length" | "connection" | "transfer-encoding"
198 ),
199 "wire header must not override managed headers: {name}"
200 );
201 }
202}
203
204impl From<MonoResponseInner<'static>> for Response {
205 fn from(response: MonoResponseInner<'static>) -> Self {
206 Self {
207 status: response.status,
208 headers: response.headers.map(|h| *h).unwrap_or_default(),
209 wire_headers: Owned::from(response.head.into_bytes().as_ref()),
210 body: BodyInner::from(response.body),
211 chunked_parts: None,
212 }
213 }
214}
215
216impl From<FixedResponseInner<'static>> for Response {
217 fn from(response: FixedResponseInner<'static>) -> Self {
218 Self {
219 status: response.status,
220 headers: HeaderList::new(),
221 wire_headers: Owned::from(response.wire_headers().as_ref()),
222 body: BodyInner::Shared(response.body),
223 chunked_parts: None,
224 }
225 }
226}