use std::io::{self, Read};
#[derive(Debug, Clone)]
pub enum Data {
Bytes(Vec<u8>),
Retry,
}
pub trait HintReadterator {
type Item;
type Error;
fn next_bytes(&mut self, size_hint: Option<usize>) -> Option<Result<Self::Item, Self::Error>>;
}
pub struct BatchReader<R: Read> {
reader: R,
batch_size: usize,
eof_on_zero_read: bool,
received_data: bool,
max_consecutive_retries: usize,
consecutive_retries: usize,
done: bool,
}
impl<R: Read> BatchReader<R> {
pub fn new(reader: R) -> Self {
Self {
reader,
batch_size: 512,
received_data: false,
eof_on_zero_read: true,
max_consecutive_retries: 100,
consecutive_retries: 0,
done: false,
}
}
#[must_use]
pub fn batch_size(mut self, size: usize) -> Self {
self.batch_size = size;
self
}
#[must_use]
pub fn eof_on_zero_read(mut self, eof: bool) -> Self {
self.eof_on_zero_read = eof;
self
}
#[must_use]
pub fn max_consecutive_retries(mut self, max: usize) -> Self {
self.max_consecutive_retries = max;
self
}
}
impl<R: Read> Iterator for BatchReader<R> {
type Item = Result<Data, io::Error>;
fn next(&mut self) -> Option<Self::Item> {
let _span = tracing::span!(tracing::Level::TRACE, "next").entered();
self.next_bytes(None)
}
}
impl<R: Read> HintReadterator for BatchReader<R> {
type Item = Data;
type Error = io::Error;
fn next_bytes(&mut self, size_hint: Option<usize>) -> Option<Result<Self::Item, Self::Error>> {
let _span =
tracing::span!(tracing::Level::TRACE, "next_bytes", hint = ?size_hint).entered();
if self.done {
tracing::trace!("Reading is now considered done!");
return None;
}
if size_hint.is_some() && size_hint == Some(0) {
return None;
}
let target_size = size_hint.unwrap_or(self.batch_size);
tracing::trace!("Using target size for buffer: {}", &target_size);
let mut buf = vec![0u8; target_size];
match self.reader.read(&mut buf) {
Ok(0) => {
tracing::trace!("Zero bytes read occured");
if self.eof_on_zero_read {
tracing::trace!("Stream is now considered finished");
self.done = true;
None
} else {
self.consecutive_retries += 1;
if self.consecutive_retries > self.max_consecutive_retries {
self.done = true;
if self.received_data {
return None;
}
Some(Err(io::Error::new(
io::ErrorKind::TimedOut,
"max consecutive retries exceeded without progress",
)))
} else {
tracing::debug!("Sending retry");
Some(Ok(Data::Retry))
}
}
}
Ok(n) => {
tracing::trace!("Received data Bytes(len={})", n);
self.received_data = true;
self.consecutive_retries = 0;
buf.truncate(n);
Some(Ok(Data::Bytes(buf)))
}
Err(e)
if e.kind() == io::ErrorKind::WouldBlock || e.kind() == io::ErrorKind::TimedOut =>
{
tracing::error!("Timeout/WouldBlock error received: {:?}", &e);
self.consecutive_retries += 1;
tracing::trace!(
"Checking consecutive errors (max={}): current={}",
&self.max_consecutive_retries,
self.consecutive_retries
);
if self.consecutive_retries > self.max_consecutive_retries {
self.done = true;
if self.received_data {
tracing::trace!("Finished reading, saw data, ending");
return None;
}
tracing::trace!(
"Problematic, max retries reached, returning exahaustion errors"
);
Some(Err(io::Error::new(
e.kind(),
"max consecutive retries exceeded without progress",
)))
} else {
Some(Ok(Data::Retry))
}
}
Err(e) => {
tracing::error!("Read error occured: {:?}", &e);
self.done = true;
Some(Err(e))
}
}
}
}
pub struct FullBodyReader<R: Read> {
inner: BatchReader<R>,
bytes_yielded: usize,
target_size: usize,
}
impl<R: Read> FullBodyReader<R> {
pub fn new(inner: BatchReader<R>, target_size: usize) -> Self {
Self {
inner,
bytes_yielded: 0,
target_size,
}
}
}
impl<R: Read> Iterator for FullBodyReader<R> {
type Item = Result<Data, io::Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.bytes_yielded >= self.target_size {
return None;
}
match self.inner.next() {
Some(Ok(Data::Bytes(bytes))) => {
self.bytes_yielded += bytes.len();
Some(Ok(Data::Bytes(bytes)))
}
other => other, }
}
}
pub struct EofReader<R: Read> {
inner: BatchReader<R>,
}
impl<R: Read> EofReader<R> {
pub fn new(inner: BatchReader<R>) -> Self {
Self { inner }
}
}
impl<R: Read> Iterator for EofReader<R> {
type Item = Result<Data, io::Error>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
pub struct BatchStreamReader<R: Read> {
inner: BatchReader<R>,
}
impl<R: Read> BatchStreamReader<R> {
pub fn new(inner: BatchReader<R>) -> Self {
Self { inner }
}
}
impl<R: Read + Send> Iterator for BatchStreamReader<R> {
type Item = Result<Data, Box<dyn std::error::Error + 'static>>;
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next() {
Some(Ok(data)) => Some(Ok(data)),
Some(Err(e)) => Some(Err(Box::new(e))),
None => None,
}
}
}
pub struct LimitedBatchStreamReader<R: Read> {
inner: BatchReader<R>,
bytes_yielded: usize,
byte_cap: usize,
}
impl<R: Read> LimitedBatchStreamReader<R> {
pub fn new(inner: BatchReader<R>, byte_cap: usize) -> Self {
Self {
inner,
bytes_yielded: 0,
byte_cap,
}
}
}
impl<R: Read + Send> Iterator for LimitedBatchStreamReader<R> {
type Item = Result<Data, Box<dyn std::error::Error + 'static>>;
fn next(&mut self) -> Option<Self::Item> {
tracing::trace!("Pulling next data bytes from stream");
if self.bytes_yielded >= self.byte_cap {
tracing::trace!("Readed max allowed bytes, stopping");
return None;
}
let remaining = self.byte_cap - self.bytes_yielded;
tracing::trace!("Remaining bytes to read: {}", remaining);
match self.inner.next_bytes(Some(remaining)) {
Some(Ok(Data::Bytes(bytes))) => {
tracing::trace!("Readed new bytes size from reader: {}", bytes.len());
self.bytes_yielded += bytes.len();
Some(Ok(Data::Bytes(bytes)))
}
Some(Ok(data)) => Some(Ok(data)), Some(Err(e)) => Some(Err(Box::new(e))),
None => None,
}
}
}
pub struct EOFStreamReader<R: Read> {
inner: BatchReader<R>,
}
impl<R: Read> EOFStreamReader<R> {
pub fn new(inner: BatchReader<R>) -> Self {
Self { inner }
}
}
impl<R: Read + Send> Iterator for EOFStreamReader<R> {
type Item = Result<Data, Box<dyn std::error::Error + 'static>>;
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|r| r.map_err(|e| Box::new(e) as Box<dyn std::error::Error + 'static>))
}
}
pub struct LimitedEOFStreamReader<R: Read> {
inner: BatchReader<R>,
bytes_yielded: usize,
byte_cap: usize,
}
impl<R: Read> LimitedEOFStreamReader<R> {
pub fn new(inner: BatchReader<R>, byte_cap: usize) -> Self {
Self {
inner,
bytes_yielded: 0,
byte_cap,
}
}
}
impl<R: Read + Send> Iterator for LimitedEOFStreamReader<R> {
type Item = Result<Data, Box<dyn std::error::Error + 'static>>;
fn next(&mut self) -> Option<Self::Item> {
if self.bytes_yielded > self.byte_cap {
tracing::error!(
"body size {} exceeds max {}",
self.bytes_yielded,
self.byte_cap
);
return Some(Err(Box::new(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"body size {} exceeds max {}",
self.bytes_yielded, self.byte_cap
),
))));
}
let remaining = self.byte_cap - self.bytes_yielded;
tracing::trace!(remaining = %remaining, "Remaining bytes to read: {}", &remaining);
match self.inner.next_bytes(Some(remaining)) {
Some(Ok(Data::Bytes(bytes))) => {
self.bytes_yielded += bytes.len();
if self.bytes_yielded > self.byte_cap {
tracing::error!(
"body size {} exceeds max {}",
self.bytes_yielded,
self.byte_cap
);
return None;
}
Some(Ok(Data::Bytes(bytes)))
}
Some(Ok(Data::Retry)) => Some(Ok(Data::Retry)),
Some(Err(e)) => Some(Err(Box::new(e) as Box<dyn std::error::Error + 'static>)),
None => None,
}
}
}
use std::marker::PhantomData;
pub struct DataBytesIterator<I, E> {
inner: I,
_marker: PhantomData<E>,
}
impl<I, E> DataBytesIterator<I, E> {
pub fn new(inner: I) -> Self {
Self {
inner,
_marker: PhantomData,
}
}
}
impl<I, E> Iterator for DataBytesIterator<I, E>
where
I: Iterator<Item = Result<Data, E>>,
{
type Item = Result<Vec<u8>, E>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.inner.next() {
Some(Ok(Data::Retry)) => continue, Some(Ok(Data::Bytes(bytes))) => return Some(Ok(bytes)),
Some(Err(e)) => return Some(Err(e)),
None => return None,
}
}
}
}
pub trait IntoDataBytes<E> {
fn into_bytes(self) -> DataBytesIterator<Self, E>
where
Self: Iterator<Item = Result<Data, E>> + Sized,
{
DataBytesIterator::new(self)
}
}
impl<I, E> IntoDataBytes<E> for I where I: Iterator<Item = Result<Data, E>> {}