use std::{
collections::HashMap,
io::{self, BufReader, Read, Write},
};
use builders::Builder;
use parse::parse_response;
use crate::{HttpStream, Result};
mod parse;
#[derive(Builder, Debug)]
pub struct HttpResponse {
#[builder(map = "header")]
headers: HashMap<String, String>,
#[builder(disabled = true)]
#[builder(def = { BufReader::new(HttpStream::dummy()) })]
stream: BufReader<HttpStream>,
#[builder(def = 200u16)]
status: u16,
#[builder(optional = true)]
body: Option<Box<[u8]>>,
#[builder(def = 1.0)]
version: f32,
}
impl HttpResponse {
pub fn parse(stream: impl Into<HttpStream>) -> crate::Result<Self> {
let stream = BufReader::new(stream.into());
parse_response(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(String::as_str)
}
#[inline]
#[must_use]
pub fn version(&self) -> f32 {
self.version
}
#[inline]
#[must_use]
pub fn headers(&self) -> &HashMap<String, String> {
&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(&self) -> Result<bool> {
Ok(self.stream.get_ref().is_ready()?)
}
pub fn read_body(&mut self, writer: &mut dyn Write) -> Result<()> {
const CHUNK_SIZE: usize = 1024;
let mut buf: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
let len = self.content_length();
let n = len / CHUNK_SIZE;
let remainder = len % CHUNK_SIZE;
for _ in 0..n {
self.stream.read_exact(&mut buf)?;
writer.write_all(&buf)?;
}
if remainder > 0 {
self.stream.read_exact(&mut buf[0..remainder])?;
writer.write_all(&buf[0..remainder])?;
}
Ok(())
}
pub fn write_to(&mut self, out: &mut dyn io::Write) -> io::Result<usize> {
let mut buf = [0_u8; 1024];
let mut total = 0;
while let Ok(n) = self.stream.read(&mut buf) {
out.write_all(&buf[0..n])?;
total += n;
if n == 0 {
break;
}
}
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;