http/response/
mod.rs

1use std::{
2    collections::HashMap,
3    io::{self, BufReader, Read, Write},
4};
5
6use builders::Builder;
7use parse::parse_response;
8
9use crate::{HttpStream, Result};
10
11mod parse;
12
13/// An Http response
14#[derive(Builder, Debug)]
15pub struct HttpResponse {
16    #[builder(map = "header")]
17    headers: HashMap<String, String>,
18    #[builder(disabled = true)]
19    #[builder(def = { BufReader::new(HttpStream::dummy()) })]
20    stream: BufReader<HttpStream>,
21    #[builder(def = 200u16)]
22    status: u16,
23    #[builder(optional = true)]
24    body: Option<Box<[u8]>>,
25    #[builder(def = 1.0)]
26    version: f32,
27}
28
29impl HttpResponse {
30    pub fn parse(stream: impl Into<HttpStream>) -> crate::Result<Self> {
31        let stream = BufReader::new(stream.into());
32        parse_response(stream)
33    }
34    #[inline]
35    #[must_use]
36    pub fn status(&self) -> u16 {
37        self.status
38    }
39
40    #[inline]
41    #[must_use]
42    pub fn content_length(&self) -> usize {
43        match self.headers.get("Content-Length") {
44            Some(l) => l.parse().unwrap_or(0),
45            None => 0,
46        }
47    }
48    /// Get the value of the given header key, if present
49    #[inline]
50    #[must_use]
51    pub fn header(&self, key: &str) -> Option<&str> {
52        self.headers.get(key).map(String::as_str)
53    }
54
55    #[inline]
56    #[must_use]
57    pub fn version(&self) -> f32 {
58        self.version
59    }
60
61    #[inline]
62    #[must_use]
63    pub fn headers(&self) -> &HashMap<String, String> {
64        &self.headers
65    }
66
67    /// Reads the body from the stream into the buffer.
68    ///
69    /// This method is primarly used by [`body`](Self::body), and
70    /// for unit tests, where we need to force-load the body into
71    /// the [stream]'s buffer.
72    ///
73    /// [stream]: HttpStream
74    pub(crate) fn read_body_into_buffer(&mut self) -> std::io::Result<()> {
75        let len = self.content_length();
76        let mut buf = Vec::with_capacity(len);
77        self.stream.read_to_end(&mut buf)?;
78        self.body = Some(buf.into_boxed_slice());
79        Ok(())
80    }
81
82    /// Reads the body from the [`stream`] into the response's buffer.
83    ///
84    /// # NOTE
85    /// This loads the whole body of the response into memory,
86    /// and it'll stick with the response for it's lifetime.
87    /// It's not very efficient memory-wise for responses with big bodies.
88    ///
89    /// For a nicer way to process a response's body, see the
90    /// [read_body](Self::read_body) function.
91    ///
92    /// # Errors
93    /// If some IO error happens when reading the body from the [`stream`]
94    ///
95    /// # Returns
96    /// And option of &[u8]. A None variant means the response doesn't have a body.
97    ///
98    /// [`stream`]: HttpStream
99    pub fn body(&mut self) -> Result<Option<&[u8]>> {
100        if self.body.is_none() {
101            self.read_body_into_buffer()?;
102        }
103        Ok(self.body.as_deref())
104    }
105
106    /// Returns true if the [`stream`](HttpStream) has a body,
107    /// and false if it's empty.
108    ///
109    /// This method is preferred to check the presence of a body,
110    /// over calling [body](Self::body) and checking the returned Option,
111    /// since this function doesn't allocate memory, nor mutates the response.
112    ///
113    /// # Errors
114    /// If some IO error happens in the process of checking the
115    /// [`stream`]'s availability
116    ///
117    /// [`stream`]: HttpStream
118    pub fn has_body(&self) -> Result<bool> {
119        Ok(self.stream.get_ref().is_ready()?)
120    }
121
122    /// Reads the response's body into [writer](Write)
123    ///
124    /// # Errors
125    /// If, while reading or writing, some io Error is found
126    pub fn read_body(&mut self, writer: &mut dyn Write) -> Result<()> {
127        const CHUNK_SIZE: usize = 1024;
128        let mut buf: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
129        let len = self.content_length();
130        let n = len / CHUNK_SIZE;
131        let remainder = len % CHUNK_SIZE;
132
133        for _ in 0..n {
134            self.stream.read_exact(&mut buf)?;
135            writer.write_all(&buf)?;
136        }
137
138        if remainder > 0 {
139            self.stream.read_exact(&mut buf[0..remainder])?;
140            writer.write_all(&buf[0..remainder])?;
141        }
142
143        Ok(())
144    }
145
146    pub fn write_to(&mut self, out: &mut dyn io::Write) -> io::Result<usize> {
147        let mut buf = [0_u8; 1024];
148        let mut total = 0;
149        while let Ok(n) = self.stream.read(&mut buf) {
150            out.write_all(&buf[0..n])?;
151            total += n;
152            if n == 0 {
153                break;
154            }
155        }
156        out.flush()?;
157        Ok(total)
158    }
159}
160
161impl PartialEq for HttpResponse {
162    fn eq(&self, other: &Self) -> bool {
163        self.headers == other.headers
164            && self.status == other.status
165            && self.body == other.body
166            && self.version == other.version
167    }
168}
169
170#[cfg(test)]
171mod test;