use std::cmp;
use std::io;
use std::io::{Error, ErrorKind};
use buffered_reader::{buffered_reader_generic_read_impl, BufferedReader};
use crate::{vec_resize, vec_truncate};
use crate::packet::header::BodyLength;
use crate::parse::{Cookie, Hashing};
const TRACE : bool = false;
pub(crate) struct BufferedReaderPartialBodyFilter<T: BufferedReader<Cookie>> {
reader: T,
partial_body_length: u32,
last: bool,
buffer: Option<Vec<u8>>,
cursor: usize,
unused_buffers: Vec<Vec<u8>>,
cookie: Cookie,
hash_headers: bool,
}
impl<T: BufferedReader<Cookie>> std::fmt::Display
for BufferedReaderPartialBodyFilter<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "BufferedReaderPartialBodyFilter")
}
}
impl<T: BufferedReader<Cookie>> std::fmt::Debug
for BufferedReaderPartialBodyFilter<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("BufferedReaderPartialBodyFilter")
.field("partial_body_length", &self.partial_body_length)
.field("last", &self.last)
.field("hash headers", &self.hash_headers)
.field("buffer (bytes left)",
&self.buffer.as_ref().map(|buffer| buffer.len()))
.field("reader", &self.reader)
.finish()
}
}
impl<T: BufferedReader<Cookie>> BufferedReaderPartialBodyFilter<T> {
pub fn with_cookie(reader: T, partial_body_length: u32,
hash_headers: bool, cookie: Cookie) -> Self {
BufferedReaderPartialBodyFilter {
reader,
partial_body_length,
last: false,
buffer: None,
cursor: 0,
unused_buffers: Vec::with_capacity(2),
cookie,
hash_headers,
}
}
fn do_fill_buffer (&mut self, amount: usize) -> Result<(), std::io::Error> {
tracer!(TRACE, "PBF::do_fill_buffer", self.cookie.level.unwrap_or(0));
t!("BufferedReaderPartialBodyFilter::do_fill_buffer(\
amount: {}) (partial body length: {}, last: {})",
amount, self.partial_body_length, self.last);
if self.last && self.partial_body_length == 0 {
return Ok(());
}
let mut buffer = self.unused_buffers.pop()
.map(|mut v| {
vec_resize(&mut v, amount);
v
})
.unwrap_or_else(|| vec![0u8; amount]);
let mut amount_buffered = 0;
if let Some(ref old_buffer) = self.buffer {
let amount_left = old_buffer.len() - self.cursor;
assert!(amount > amount_left);
amount_buffered = amount_left;
buffer[..amount_buffered]
.copy_from_slice(&old_buffer[self.cursor..]);
t!("Copied {} bytes from the old buffer", amount_buffered);
}
let mut err = None;
loop {
let to_read = cmp::min(
self.partial_body_length as usize,
buffer.len() - amount_buffered);
t!("Trying to buffer {} bytes \
(partial body length: {}; space: {})",
to_read, self.partial_body_length,
buffer.len() - amount_buffered);
if to_read > 0 {
let result = self.reader.read(
&mut buffer[amount_buffered..amount_buffered + to_read]);
match result {
Ok(did_read) => {
t!("Buffered {} bytes", did_read);
amount_buffered += did_read;
self.partial_body_length -= did_read as u32;
if did_read < to_read {
break;
}
},
Err(e) => {
t!("Err reading: {:?}", e);
err = Some(e);
break;
},
}
}
if amount_buffered == amount || self.last {
break;
}
assert_eq!(self.partial_body_length, 0);
if ! self.hash_headers {
if let Some(level) = self.reader.cookie_ref().level {
Cookie::hashing(
&mut self.reader, Hashing::Disabled, level);
}
}
t!("Reading next chunk's header (hashing: {}, level: {:?})",
self.hash_headers, self.reader.cookie_ref().level);
let body_length = BodyLength::parse_new_format(&mut self.reader);
if ! self.hash_headers {
if let Some(level) = self.reader.cookie_ref().level {
Cookie::hashing(
&mut self.reader, Hashing::Enabled, level);
}
}
match body_length {
Ok(BodyLength::Full(len)) => {
t!("Last chunk: {} bytes", len);
self.last = true;
self.partial_body_length = len;
},
Ok(BodyLength::Partial(len)) => {
t!("Next chunk: {} bytes", len);
self.partial_body_length = len;
},
Ok(BodyLength::Indeterminate) => {
unreachable!();
},
Err(e) => {
t!("Err reading next chunk: {:?}", e);
err = Some(e);
break;
}
}
}
vec_truncate(&mut buffer, amount_buffered);
if let Some(b) = self.buffer.take() {
self.unused_buffers.push(b);
}
self.buffer = Some(buffer);
self.cursor = 0;
if let Some(err) = err {
Err(err)
} else {
Ok(())
}
}
fn data_helper(&mut self, amount: usize, hard: bool, and_consume: bool)
-> Result<&[u8], std::io::Error> {
tracer!(TRACE, "PBF::data_helper", self.cookie.level.unwrap_or(0));
let mut need_fill = false;
t!("amount {}, hard {:?}, and_consume {:?}, buffered {}, cursor {}",
amount, hard, and_consume,
self.buffer.as_ref().map(|b| b.len()).unwrap_or(0),
self.cursor);
if self.buffer.as_ref().map(|b| b.len() == self.cursor).unwrap_or(false)
{
self.unused_buffers.push(self.buffer.take().expect("have buffer"));
self.cursor = 0;
}
if let Some(ref buffer) = self.buffer {
let amount_buffered = buffer.len() - self.cursor;
if amount > amount_buffered {
need_fill = true;
t!("Read of {} bytes exceeds buffered {} bytes",
amount, amount_buffered);
}
} else {
assert_eq!(self.cursor, 0);
if amount <= self.partial_body_length as usize
|| self.last {
let result = if hard && and_consume {
self.reader.data_consume_hard (amount)
} else if and_consume {
self.reader.data_consume (amount)
} else {
self.reader.data(amount)
};
match result {
Ok(buffer) => {
let amount_buffered =
std::cmp::min(buffer.len(),
self.partial_body_length as usize);
if hard && amount_buffered < amount {
return Err(Error::new(ErrorKind::UnexpectedEof,
"unexpected EOF"));
} else {
if and_consume {
self.partial_body_length -=
cmp::min(amount, amount_buffered) as u32;
}
return Ok(&buffer[..amount_buffered]);
}
},
Err(err) => return Err(err),
}
} else {
need_fill = true;
t!("Read straddles partial body chunk boundary");
}
}
if need_fill {
t!("Need to refill the buffer.");
let result = self.do_fill_buffer(amount);
if let Err(err) = result {
return Err(err);
}
}
let buffer = &self.buffer.as_ref().unwrap()[self.cursor..];
if hard && buffer.len() < amount {
return Err(Error::new(ErrorKind::UnexpectedEof, "unexpected EOF"));
}
if and_consume {
self.cursor += cmp::min(amount, buffer.len());
}
Ok(buffer)
}
}
impl<T: BufferedReader<Cookie>> std::io::Read
for BufferedReaderPartialBodyFilter<T> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
buffered_reader_generic_read_impl(self, buf)
}
}
impl<T: BufferedReader<Cookie>> BufferedReader<Cookie>
for BufferedReaderPartialBodyFilter<T> {
fn buffer(&self) -> &[u8] {
if let Some(ref buffer) = self.buffer {
&buffer[self.cursor..]
} else {
let buf = self.reader.buffer();
&buf[..cmp::min(buf.len(),
self.partial_body_length as usize)]
}
}
fn data(&mut self, amount: usize) -> Result<&[u8], std::io::Error> {
self.data_helper(amount, false, false)
}
fn data_hard(&mut self, amount: usize) -> Result<&[u8], io::Error> {
self.data_helper(amount, true, false)
}
fn consume(&mut self, amount: usize) -> &[u8] {
if let Some(ref buffer) = self.buffer {
self.cursor += amount;
assert!(self.cursor <= buffer.len());
&buffer[self.cursor - amount..]
} else {
assert!(amount <= self.partial_body_length as usize);
self.partial_body_length -= amount as u32;
self.reader.consume(amount)
}
}
fn data_consume(&mut self, amount: usize) -> Result<&[u8], std::io::Error> {
self.data_helper(amount, false, true)
}
fn data_consume_hard(&mut self, amount: usize) -> Result<&[u8], std::io::Error> {
self.data_helper(amount, true, true)
}
fn consummated(&mut self) -> bool {
self.partial_body_length == 0 && self.last
}
fn get_mut(&mut self) -> Option<&mut dyn BufferedReader<Cookie>> {
Some(&mut self.reader)
}
fn get_ref(&self) -> Option<&dyn BufferedReader<Cookie>> {
Some(&self.reader)
}
fn into_inner<'b>(self: Box<Self>) -> Option<Box<dyn BufferedReader<Cookie> + 'b>>
where Self: 'b {
Some(self.reader.as_boxed())
}
fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
use std::mem;
mem::replace(&mut self.cookie, cookie)
}
fn cookie_ref(&self) -> &Cookie {
&self.cookie
}
fn cookie_mut(&mut self) -> &mut Cookie {
&mut self.cookie
}
}