Skip to main content

http/response/
mod.rs

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