afire/response.rs
1use std::cell::RefCell;
2use std::fmt::{self, Debug, Display, Formatter};
3use std::io::{ErrorKind, Read, Write};
4use std::net::TcpStream;
5use std::sync::{Arc, Mutex};
6
7use crate::consts;
8use crate::header::{HeaderType, Headers};
9use crate::http::status::Status;
10use crate::{
11 error::Result, header::headers_to_string, internal::handle::Writeable, Content, Header,
12 SetCookie,
13};
14
15/// Http Response
16#[derive(Debug)]
17pub struct Response {
18 /// Response status code
19 pub status: Status,
20
21 /// Response Data.
22 /// Can be either a Static `Vec<u8>` or a Stream (impl [`Read`])
23 pub data: ResponseBody,
24
25 /// List of response headers.
26 /// This does not contain the default headers.
27 pub headers: Headers,
28
29 /// Response reason phrase.
30 /// If this is None, the reason phrase will be automatically generated based on the status code.
31 pub reason: Option<String>,
32
33 /// Response Flags:
34 /// - Close: Set the Connection header to close and will close the connection after the response is sent.
35 /// - End: End the connection without sending a response
36 pub flag: ResponseFlag,
37}
38
39#[derive(Debug, PartialEq, Eq)]
40pub enum ResponseFlag {
41 /// No Flag
42 None,
43 /// Close the socket
44 Close,
45 /// End the connection without sending a response
46 End,
47}
48
49/// Response Data.
50/// Can be either a Static Vec<u8> or a Stream (impl [`Read`]).
51/// Static responses are sent in one go, while streams are sent in chunks (chunked transfer encoding).
52pub enum ResponseBody {
53 Static(Vec<u8>),
54 Stream(Writeable),
55}
56
57impl Response {
58 /// Create a new Blank Response
59 ///
60 /// Default data is as follows
61 /// - Status: 200
62 /// - Data: OK
63 /// - Headers: Vec::new()
64 /// ## Example
65 /// ```rust
66 /// # use afire::{Response, Header};
67 /// Response::new();
68 /// ```
69 pub fn new() -> Self {
70 Self {
71 status: Status::Ok,
72 data: vec![79, 75].into(),
73 headers: Default::default(),
74 reason: None,
75 flag: ResponseFlag::None,
76 }
77 }
78
79 /// Creates a new Default Response with the End flag set.
80 pub fn end() -> Self {
81 Self {
82 flag: ResponseFlag::End,
83 ..Default::default()
84 }
85 }
86
87 /// Add a status code to a Response.
88 /// This accepts [`Status`] as well as a [`u16`].
89 /// ## Example
90 /// ```rust
91 /// # use afire::{Response, Header, Status};
92 /// // Create Response
93 /// Response::new().status(Status::Ok);
94 /// ```
95 pub fn status(self, code: impl Into<Status>) -> Self {
96 Self {
97 status: code.into(),
98 ..self
99 }
100 }
101
102 /// Manually set the Reason Phrase.
103 /// If this is not set, it will be inferred from the status code.
104 /// Non standard status codes will have a reason phrase of "OK".
105 /// ```rust
106 /// # use afire::{Response, Header, Status};
107 /// // Create Response
108 /// let response = Response::new()
109 /// .status(Status::Ok)
110 /// .reason("Hello");
111 /// ```
112 pub fn reason(self, reason: impl AsRef<str>) -> Self {
113 Self {
114 reason: Some(reason.as_ref().to_owned()),
115 ..self
116 }
117 }
118
119 /// Add text as data to a Response.
120 /// Will accept any type that implements Display, such as [`String`], [`str`], [`i32`], serde_json::Value, etc.
121 /// This response type is considered static and will be sent in one go, not chunked.
122 /// ## Example
123 /// ```rust
124 /// # use afire::Response;
125 /// // Create Response
126 /// let response = Response::new()
127 /// .text("Hello from afire!");
128 /// ```
129 pub fn text(self, text: impl Display) -> Self {
130 Self {
131 data: text.to_string().as_bytes().to_vec().into(),
132 ..self
133 }
134 }
135
136 /// Add raw bytes as data to a Response.
137 /// This response type is considered static and will be sent in one go, not chunked.
138 /// ## Example
139 /// ```rust
140 /// # use afire::Response;
141 /// // Create Response
142 /// let response = Response::new()
143 /// .bytes(&[79, 75]); // Bytes for "OK"
144 /// ```
145 pub fn bytes(self, bytes: &[u8]) -> Self {
146 Self {
147 data: bytes.to_vec().into(),
148 ..self
149 }
150 }
151
152 /// Add a stream as data to a Response.
153 /// This response type is considered dynamic and will be streamed to the client in chunks using `Transfer-Encoding: chunked`.
154 /// ## Example
155 /// ```rust,no_run
156 /// # use afire::{Response, Method, Server};
157 /// # use std::fs::File;
158 /// const PATH: &str = "path/to/file.txt";
159 /// let mut server = Server::<()>::new("localhost", 8080);
160 ///
161 /// server.route(Method::GET, "/download-stream", |_| {
162 /// let stream = File::open(PATH).unwrap();
163 /// Response::new().stream(stream)
164 /// });
165 /// ```
166 pub fn stream(self, stream: impl Read + Send + 'static) -> Self {
167 Self {
168 data: ResponseBody::Stream(Box::new(RefCell::new(stream))),
169 ..self
170 }
171 }
172
173 /// Add a Header to a Response.
174 /// Will accept any type that implements `AsRef<str>`, so [`String`], [`str`], [`&str`], etc.
175 /// ## Example
176 /// ```rust
177 /// # use afire::{Response, Header};
178 /// // Create Response
179 /// let response = Response::new()
180 /// .header("Test-Header", "Test-Value");
181 /// ```
182 pub fn header(mut self, key: impl Into<HeaderType>, value: impl AsRef<str>) -> Self {
183 self.headers.push(Header::new(key, value));
184 self
185 }
186
187 /// Add a list of Headers to a Response.
188 /// Only accepts a slice of [`Header`]s.
189 /// ## Example
190 /// ```rust
191 /// # use afire::{Response, Header};
192 /// // Create Response
193 /// let response = Response::new()
194 /// .headers(&[
195 /// Header::new("Content-Type", "text/html"),
196 /// Header::new("Test-Header", "Test-Value")
197 /// ]);
198 /// ```
199 pub fn headers(mut self, headers: &[Header]) -> Self {
200 self.headers.append(&mut headers.to_vec());
201 self
202 }
203
204 /// Will set the `Connection: close` header on the Response.
205 /// Then it will close the connection after the Response has been sent.
206 /// ## Example
207 /// ```rust
208 /// # use afire::{Response};
209 /// // Create Response
210 /// let response = Response::new()
211 /// .text("goodbye!")
212 /// .close();
213 /// ```
214 pub fn close(self) -> Self {
215 Self {
216 flag: ResponseFlag::Close,
217 ..self
218 }
219 }
220
221 /// Add a cookie to a response.
222 /// The [`SetCookie`] will be converted to a [`Header`] and added to the Response.
223 /// ## Example
224 /// ```
225 /// # use afire::{Response, SetCookie};
226 /// // Create Response and add cookie
227 /// let response = Response::new()
228 /// .cookie(SetCookie::new("name", "value"))
229 /// .cookie(SetCookie::new("name2", "value2"));
230 /// ```
231 pub fn cookie(mut self, cookie: SetCookie) -> Self {
232 self.headers
233 .push(Header::new("Set-Cookie", cookie.to_string()));
234 self
235 }
236
237 /// Add a list of cookies to a response.
238 /// ## Example
239 /// ```
240 /// # use afire::{Response, SetCookie};
241 /// // Create Response and add cookie
242 /// let response = Response::new()
243 /// .cookies(&[
244 /// SetCookie::new("name", "value"),
245 /// SetCookie::new("name2", "value2")
246 /// ]);
247 /// ```
248 pub fn cookies(self, cookie: &[SetCookie]) -> Self {
249 let mut new = Vec::new();
250
251 for c in cookie {
252 new.push(Header::new("Set-Cookie", c.to_string()));
253 }
254
255 self.headers(&new)
256 }
257
258 /// Set a Content Type on a Response with a [`Content`] enum.
259 /// This will add a `Content-Type` header to the Response.
260 /// ## Example
261 /// ```
262 /// # use afire::{Response, Content};
263 /// // Create Response and type
264 /// let response = Response::new()
265 /// .content(Content::HTML);
266 /// ```
267 pub fn content(mut self, content_type: Content) -> Self {
268 self.headers.push(content_type.into());
269 self
270 }
271
272 /// Lets you modify the Response with a function before it is sent to the client.
273 /// This can be used to have middleware that modifies the Response on specific routes.
274 pub fn modifier(self, modifier: impl Fn(Response) -> Response) -> Self {
275 modifier(self)
276 }
277
278 // TODO: Make crate local
279 /// Writes a Response to a TcpStream.
280 /// Will take care of adding default headers and closing the connection if needed.
281 pub fn write(
282 &mut self,
283 stream: Arc<Mutex<TcpStream>>,
284 default_headers: &[Header],
285 ) -> Result<()> {
286 // Add default headers to response
287 // Only the ones that aren't already in the response
288 for i in default_headers {
289 if !self.headers.has(&i.name) {
290 self.headers.push(i.clone());
291 }
292 }
293
294 let static_body = self.data.is_static();
295
296 // Add content-length header to response if we are sending a static body
297 if static_body && !self.headers.has(HeaderType::ContentLength) {
298 self.headers.push(self.data.content_len());
299 }
300
301 // Add Connection: close if response is set to close
302 if self.flag == ResponseFlag::Close && !self.headers.has(HeaderType::Connection) {
303 self.headers.push(Header::new("Connection", "close"));
304 }
305
306 if !static_body && !self.headers.has(HeaderType::TransferEncoding) {
307 self.headers
308 .push(Header::new("Transfer-Encoding", "chunked"));
309 }
310
311 // Convert the response to a string
312 let response = format!(
313 "HTTP/1.1 {} {}\r\n{}\r\n\r\n",
314 self.status.code(),
315 self.reason
316 .to_owned()
317 .unwrap_or_else(|| self.status.reason_phrase().to_owned()),
318 headers_to_string(&self.headers)
319 );
320
321 let mut stream = stream.lock().unwrap();
322 stream.write_all(response.as_bytes())?;
323 self.data.write(&mut stream)?;
324
325 Ok(())
326 }
327}
328
329impl Default for Response {
330 fn default() -> Response {
331 Response::new()
332 }
333}
334
335impl ResponseBody {
336 pub fn empty() -> Self {
337 ResponseBody::Static(Vec::new())
338 }
339
340 /// Checks if the ResponseBody is static.
341 fn is_static(&self) -> bool {
342 matches!(self, ResponseBody::Static(_))
343 }
344
345 /// Gets the content length header of a static ResponseBody.
346 /// If the ResponseBody is not static it will panic.
347 fn content_len(&self) -> Header {
348 let len = match self {
349 ResponseBody::Static(data) => data.len(),
350 _ => unreachable!("Can't get content length of a stream"),
351 };
352 Header::new("Content-Length", len.to_string())
353 }
354
355 /// Writes a ResponseBody to a TcpStream.
356 /// Either in one go if it is static or in chunks if it is a stream.
357 fn write(&mut self, stream: &mut TcpStream) -> Result<()> {
358 match self {
359 ResponseBody::Static(data) => stream.write_all(data)?,
360 ResponseBody::Stream(data) => {
361 let data = data.get_mut();
362 loop {
363 let mut chunk = vec![0; consts::CHUNK_SIZE];
364 let read = match data.read(&mut chunk) {
365 Ok(0) => break,
366 Ok(n) => n,
367 Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
368 Err(e) => return Err(e.into()),
369 };
370
371 let mut section = format!("{read:X}\r\n").as_bytes().to_vec();
372 section.extend(&chunk[..read]);
373 section.extend(b"\r\n");
374
375 stream.write_all(§ion)?;
376 }
377
378 stream.write_all(b"0\r\n\r\n")?;
379 }
380 };
381
382 Ok(())
383 }
384}
385
386impl From<Vec<u8>> for ResponseBody {
387 fn from(x: Vec<u8>) -> Self {
388 ResponseBody::Static(x)
389 }
390}
391
392impl From<Writeable> for ResponseBody {
393 fn from(x: Writeable) -> Self {
394 ResponseBody::Stream(x)
395 }
396}
397
398impl Debug for ResponseBody {
399 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
400 match self {
401 Self::Static(arg) => f.debug_tuple("Static").field(arg).finish(),
402 Self::Stream(_arg) => f.debug_tuple("Stream").finish(),
403 }
404 }
405}