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