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#[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 #[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 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 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 pub fn has_body(&self) -> Result<bool> {
119 Ok(self.stream.get_ref().is_ready()?)
120 }
121
122 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;