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
14pub 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 #[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 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 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 pub fn has_body(&mut self) -> Result<bool> {
128 Ok(self.body.is_some() || !self.stream.fill_buf()?.is_empty())
129 }
130
131 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;