feather_runtime/http/
response.rs1use super::errors::HeaderError;
2use bytes::{Bytes, BytesMut};
3use http::{HeaderMap, HeaderName, HeaderValue, StatusCode};
4#[cfg(feature = "json")]
5use serde::Serialize;
6use std::{fs::File, io::Read, str::FromStr};
7
8#[derive(Debug, Default)]
9pub struct Response {
10 pub status: StatusCode,
13 pub headers: HeaderMap,
16 pub body: Option<Bytes>,
20 pub version: http::Version,
22}
23
24impl Response {
25 const MAX_FILE_SIZE_BYTES: u64 = 4 * 1024 * 1024; fn set_common_headers(&mut self, content_type: Option<&'static str>, len: usize) {
29 if let Some(ct) = content_type {
30 self.headers.insert(HeaderName::from_static("content-type"), HeaderValue::from_static(ct));
31 }
32 self.headers.insert(HeaderName::from_static("content-length"), Self::len_to_header_value(len));
33 }
34
35 pub fn set_status(&mut self, status: u16) -> &mut Response {
40 self.status = StatusCode::from_u16(status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
41 self
42 }
43
44 pub fn add_header(&mut self, key: &str, value: &str) -> Result<(), HeaderError> {
48 let val = HeaderValue::from_str(value)?;
49 let key = HeaderName::from_str(key)?;
50 self.headers.insert(key, val);
51 return Ok(());
52 }
53 pub fn to_raw(&self) -> Bytes {
55 let body_len = self.body.as_ref().map_or(0, |b| b.len());
56 let mut buf = BytesMut::with_capacity(512 + body_len);
58
59 buf.extend_from_slice(b"HTTP/1.1 ");
61
62 let mut status_buffer = itoa::Buffer::new();
64 let status_code_str = status_buffer.format(self.status.as_u16());
65
66 buf.extend_from_slice(status_code_str.as_bytes());
67 buf.extend_from_slice(b" ");
68
69 buf.extend_from_slice(self.status.canonical_reason().unwrap_or("Unknown").as_bytes());
71 buf.extend_from_slice(b"\r\n");
72
73 for (key, value) in &self.headers {
75 buf.extend_from_slice(key.as_str().as_bytes());
77 buf.extend_from_slice(b": ");
78 buf.extend_from_slice(value.as_bytes());
80 buf.extend_from_slice(b"\r\n");
81 }
82
83 if !self.headers.contains_key("date") {
89 let date_str = chrono::Utc::now().to_rfc2822();
90 buf.extend_from_slice(b"date: ");
91 buf.extend_from_slice(date_str.as_bytes());
92 buf.extend_from_slice(b"\r\n");
93 }
94
95 if !self.headers.contains_key("content-length") && body_len > 0 {
98 buf.extend_from_slice(b"content-length: ");
99
100 let mut len_buffer = itoa::Buffer::new();
102 let len_str = len_buffer.format(body_len);
103
104 buf.extend_from_slice(len_str.as_bytes());
105 buf.extend_from_slice(b"\r\n");
106 }
107
108 buf.extend_from_slice(b"\r\n");
110
111 if let Some(ref body) = self.body {
113 buf.extend_from_slice(body);
114 }
115
116 buf.freeze()
118 }
119
120 pub fn send_text(&mut self, data: impl Into<String>) {
122 let body = data.into();
123 self.body = Some(Bytes::from(body));
124 self.set_common_headers(Some("text/plain;charset=utf-8"), self.body.as_ref().unwrap().len());
125 }
126
127 pub fn send_bytes(&mut self, data: impl Into<Vec<u8>>) {
129 let body = data.into();
130 self.body = Some(Bytes::from(body));
131 self.set_common_headers(None, self.body.as_ref().unwrap().len());
132 }
133
134 pub fn send_html(&mut self, data: impl Into<String>) {
136 let body = data.into();
137 self.body = Some(Bytes::from(body));
138 self.headers.insert(HeaderName::from_static("content-type"), HeaderValue::from_static("text/html"));
139 let len = self.body.as_ref().unwrap().len();
140 self.headers.insert(HeaderName::from_static("content-length"), Self::len_to_header_value(len));
141 }
142
143 #[cfg(feature = "json")]
145 pub fn send_json<T: Serialize>(&mut self, data: T) {
146 match serde_json::to_string(&data) {
147 Ok(json) => {
148 self.body = Some(Bytes::from(json));
149 self.headers.insert(HeaderName::from_static("content-type"), HeaderValue::from_static("application/json"));
150 let len = self.body.as_ref().unwrap().len();
151 self.headers.insert(HeaderName::from_static("content-length"), Self::len_to_header_value(len));
152 }
153 Err(_) => {
154 self.status = StatusCode::INTERNAL_SERVER_ERROR;
155 self.body = Some(Bytes::from("Internal Server Error"));
156 self.headers.insert(HeaderName::from_static("content-type"), HeaderValue::from_static("text/plain"));
157 let len = self.body.as_ref().unwrap().len();
158 self.headers.insert(HeaderName::from_static("content-length"), Self::len_to_header_value(len));
159 }
160 }
161 }
162 pub fn send_file(&mut self, mut file: File) {
165 let metadata = match file.metadata() {
166 Ok(m) => m,
167 Err(_) => {
168 self.status = StatusCode::INTERNAL_SERVER_ERROR;
169 self.body = Some(Bytes::from("Failed to read file metadata."));
170 return;
171 }
172 };
173
174 if metadata.len() > Self::MAX_FILE_SIZE_BYTES {
176 self.status = StatusCode::PAYLOAD_TOO_LARGE; self.body = Some(Bytes::from("File size exceeds 4MB limit. Use chunked encoding for larger files."));
178 return;
179 }
180
181 let mut buffer = Vec::new();
182 match file.read_to_end(&mut buffer) {
183 Ok(_) => {
184 self.body = Some(Bytes::from(buffer));
185 let len = self.body.as_ref().unwrap().len();
186 self.headers.insert(HeaderName::from_static("content-length"), Self::len_to_header_value(len));
187 }
189 Err(_) => {
190 self.status = StatusCode::INTERNAL_SERVER_ERROR;
191 self.body = Some(Bytes::from("Internal Server Error during file read."));
192 }
193 }
194 }
195
196 pub fn redirect(&mut self, location: &str, permanent: bool) {
197 let status = if permanent {
198 StatusCode::MOVED_PERMANENTLY
199 } else {
200 StatusCode::FOUND
201 };
202 self.set_status(status.as_u16());
203 self.headers.insert(HeaderName::from_static("location"), HeaderValue::from_str(location).unwrap());
204 self.body = Some(Bytes::from(format!("Redirecting to {}", location)));
205 let len = self.body.as_ref().unwrap().len();
206 self.headers.insert(HeaderName::from_static("content-length"), Self::len_to_header_value(len));
207 }
208
209 fn len_to_header_value(len: usize) -> HeaderValue {
211 let mut buffer = itoa::Buffer::new();
212 let len_str = buffer.format(len);
213
214 HeaderValue::from_bytes(len_str.as_bytes()).expect("itoa::Buffer output should be a valid HeaderValue")
216 }
217}