use {
crate::{HeaderMap, Status, Version},
};
#[cfg(feature="std")]
use {
alloc::{
string::String,
vec::Vec,
},
core::convert::TryFrom,
std::{
io::{Error, ErrorKind, Read, Write},
},
crate::{
IoResult, LINE_BREAK,
io,
},
};
#[derive(Debug)]
pub struct Response {
pub version: Version,
pub status: Status,
pub headers: HeaderMap,
}
impl Response {
#[cfg(feature="std")]
pub fn send<R, W>(&self, data: &mut R, target: &mut W) -> IoResult<u64> where R: Read, W: Write {
let mut buf = format!(
"{version} {status_code} {status_msg}{lb}",
version=self.version, status_code=self.status.code(), status_msg=self.status, lb=LINE_BREAK,
);
self.headers.iter().for_each(|(k, v)| buf += &format!("{k}: {v}{lb}", k=k, v=v, lb=LINE_BREAK));
buf += LINE_BREAK;
target.write_all(buf.as_bytes())?;
let mut result = u64::try_from(buf.len()).map_err(|_| Error::new(ErrorKind::Other, __!("Failed converting `{}` to u64", buf.len())))?;
let copied = std::io::copy(data, target)?;
result = result.checked_add(copied).ok_or_else(|| Error::new(ErrorKind::InvalidData, __!("Overflow: {a} + {b}", a=result, b=copied)))?;
target.flush().map(|()| result)
}
#[cfg(feature="std")]
pub fn recv<R, W>(src: &mut R, data: &mut W) -> IoResult<Self> where R: Read, W: Write {
const BUF_SIZE: usize = 2048;
let mut line_buffer = [0; BUF_SIZE];
let (version, status, bytes) = read_start_line(src, &mut line_buffer)?;
let (headers, bytes) = io::read_headers(src, &mut line_buffer, bytes)?;
if let Some(bytes) = bytes {
data.write_all(&bytes)?;
}
std::io::copy(src, data)?;
data.flush()?;
Ok(Self {
version,
status,
headers,
})
}
}
#[cfg(feature="std")]
fn read_start_line<R>(r: &mut R, line_buffer: &mut [u8]) -> IoResult<(Version, Status, Option<Vec<u8>>)> where R: Read {
let lf = LINE_BREAK.as_bytes().last();
let mut total_read = 0;
let start_line = loop {
let read = match r.read(&mut line_buffer[total_read..])? {
0 => return Err(Error::new(ErrorKind::UnexpectedEof, __!("Invalid start line"))),
other => other,
};
let last_idx = total_read;
total_read += read;
match line_buffer[last_idx..total_read].iter().position(|b| lf.map(|lf| lf == b).unwrap_or(false)).map(|p| p + last_idx) {
Some(p) => match p > LINE_BREAK.len() && &line_buffer[p - LINE_BREAK.len() + 1 ..= p] == LINE_BREAK.as_bytes() {
true => break &line_buffer[..=p],
false => return Err(Error::new(ErrorKind::InvalidData, __!("Start line: invalid line break"))),
},
None => if total_read >= line_buffer.len() {
return Err(Error::new(ErrorKind::InvalidData, __!("Start line too long")));
},
};
};
let start_line_len = start_line.len();
let mut parts = start_line[..start_line_len - LINE_BREAK.len()].splitn(3, |b| b == &b' ');
match (parts.next(), parts.next(), parts.next(), parts.next()) {
(Some(version), Some(status_code), Some(status_msg), None) => {
let version = Version::try_from(version)?;
let status = Status::try_from(status_code)?;
match String::from_utf8(status_msg.to_vec()) {
Ok(_) => Ok((
version,
status,
match total_read > start_line_len {
true => Some(line_buffer[start_line_len..total_read].to_vec()),
false => None,
},
)),
Err(_) => return Err(Error::new(ErrorKind::InvalidData, __!("Invalid status message"))),
}
},
_ => Err(Error::new(ErrorKind::InvalidData, __!("Invalid start line"))),
}
}