#![allow(clippy::manual_strip)]
use super::Body;
use crate::connector::http::client::Error;
use std::{io::Read, str, vec::Vec};
const TRANSFER_ENCODING_HEADER: &str = "Transfer-Encoding: ";
const HEADER_DELIMITER: &[u8] = b"\r\n\r\n";
const HTTP_SUCCESS_STATUS: &str = "HTTP/1.1 200 OK";
const CONTENT_LENGTH_HEADER: &str = "Content-Length: ";
const MAX_RESPONSE_SIZE: usize = 65536;
pub struct Reader {
buffer: Vec<u8>,
pos: usize,
body_offset: Option<usize>,
content_length: usize,
}
impl Reader {
#[allow(clippy::new_ret_no_self)]
pub(crate) fn new(readable: &mut dyn Read) -> Result<Self, Error> {
let mut buffer = Self {
buffer: vec![0u8; MAX_RESPONSE_SIZE],
pos: 0,
body_offset: None,
content_length: 0,
};
buffer.read_headers(readable)?;
buffer.read_body(readable)?;
Ok(buffer)
}
pub(crate) fn into_body(self) -> Body {
let body_offset = self
.body_offset
.expect("we should've already read the body");
Body(Vec::from(&self.buffer[body_offset..self.pos]))
}
fn fill_buffer(&mut self, readable: &mut dyn Read) -> Result<usize, Error> {
let nbytes = readable.read(self.buffer.as_mut())?;
self.pos += nbytes;
Ok(nbytes)
}
fn read_headers(&mut self, readable: &mut dyn Read) -> Result<(), Error> {
assert!(self.body_offset.is_none(), "already read headers!");
loop {
self.fill_buffer(readable)?;
let mut offset = 0;
while self.buffer[offset..].len() > HEADER_DELIMITER.len() {
if self.buffer[offset..].starts_with(HEADER_DELIMITER) {
self.body_offset = Some(offset + HEADER_DELIMITER.len());
break;
} else {
offset += 1;
}
}
if self.body_offset.is_some() {
break;
} else if self.pos + 1 >= MAX_RESPONSE_SIZE {
fail!(
ResponseError,
"exceeded {}-byte response limit reading headers",
MAX_RESPONSE_SIZE
);
}
}
self.parse_headers()
}
fn parse_headers(&mut self) -> Result<(), Error> {
let body_offset = self.body_offset.unwrap();
let header_str = str::from_utf8(&self.buffer[..body_offset])?;
let mut header_iter = header_str.split("\r\n");
match header_iter.next() {
Some(HTTP_SUCCESS_STATUS) => (),
Some(status) => fail!(
ResponseError,
"unexpected HTTP response status: \"{}\"",
status
),
None => fail!(ResponseError, "HTTP response status line missing!"),
}
for header in header_iter {
if header.starts_with(CONTENT_LENGTH_HEADER) {
let content_length: usize = header[CONTENT_LENGTH_HEADER.len()..].parse()?;
if MAX_RESPONSE_SIZE - body_offset < content_length {
fail!(
ResponseError,
"response body length too large for buffer ({} bytes)",
content_length
);
}
self.content_length = content_length;
} else if header.starts_with(TRANSFER_ENCODING_HEADER) {
let transfer_encoding = &header[TRANSFER_ENCODING_HEADER.len()..];
fail!(
ResponseError,
"connection sent unsupported transfer encoding: {}",
transfer_encoding
);
}
}
Ok(())
}
fn read_body(&mut self, readable: &mut dyn Read) -> Result<(), Error> {
let body_end =
self.content_length + self.body_offset.expect("not ready to read the body yet");
while self.pos < body_end {
self.fill_buffer(readable)?;
}
Ok(())
}
}