Skip to main content

sark_core/http/response/
owned.rs

1use 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}