use core::fmt;
use std::{
collections::HashMap,
io::{self, BufRead, BufReader, Read, Write},
};
use parse::parse_response;
use crate::{HttpStream, Result, response::builder::HttpResponseBuilder, stream::IntoHttpStream};
pub mod builder;
mod parse;
pub struct HttpResponse {
headers: HashMap<Box<str>, Box<str>>,
stream: BufReader<Box<dyn HttpStream>>,
status: u16,
body: Option<Box<[u8]>>,
version: f32,
}
impl fmt::Debug for HttpResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HttpResponse")
.field("headers", &self.headers)
.field("status", &self.status)
.field("body", &self.body)
.field("version", &self.version)
.finish()
}
}
impl HttpResponse {
pub fn builder() -> HttpResponseBuilder {
HttpResponseBuilder::new()
}
pub fn parse<S: IntoHttpStream>(stream: S) -> crate::Result<Self> {
let stream: Box<dyn HttpStream> = Box::new(stream.into_http_stream());
parse_response(BufReader::new(stream))
}
#[inline]
#[must_use]
pub fn status(&self) -> u16 {
self.status
}
#[inline]
#[must_use]
pub fn content_length(&self) -> usize {
match self.headers.get("Content-Length") {
Some(l) => l.parse().unwrap_or(0),
None => 0,
}
}
#[inline]
#[must_use]
pub fn header(&self, key: &str) -> Option<&str> {
self.headers.get(key).map(|s| &**s)
}
#[inline]
#[must_use]
pub fn version(&self) -> f32 {
self.version
}
#[inline]
#[must_use]
pub fn headers(&self) -> &HashMap<Box<str>, Box<str>> {
&self.headers
}
pub(crate) fn read_body_into_buffer(&mut self) -> std::io::Result<()> {
let len = self.content_length();
let mut buf = Vec::with_capacity(len);
self.stream.read_to_end(&mut buf)?;
self.body = Some(buf.into_boxed_slice());
Ok(())
}
pub fn body(&mut self) -> Result<Option<&[u8]>> {
if self.body.is_none() {
self.read_body_into_buffer()?;
}
Ok(self.body.as_deref())
}
pub fn has_body(&mut self) -> Result<bool> {
Ok(self.body.is_some() || !self.stream.fill_buf()?.is_empty())
}
pub fn read_body(&mut self, writer: &mut dyn Write) -> Result<()> {
io::copy(&mut self.stream, writer)?;
Ok(())
}
pub fn write_to(&mut self, out: &mut dyn io::Write) -> io::Result<usize> {
let mut total = 0;
loop {
let slice = self.stream.fill_buf()?;
if slice.is_empty() {
break;
}
out.write_all(slice)?;
let len = slice.len();
self.stream.consume(len);
total += len;
}
out.flush()?;
Ok(total)
}
}
impl PartialEq for HttpResponse {
fn eq(&self, other: &Self) -> bool {
self.headers == other.headers
&& self.status == other.status
&& self.body == other.body
&& self.version == other.version
}
}
#[cfg(test)]
mod test;