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(&section)?;
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}