use std::io::{self, Read};
use tokio::sync::mpsc;
use crate::request::Body;
pub(crate) const BODY_CHANNEL_CHUNKS: usize = 4;
pub(crate) type BodySender = mpsc::Sender<io::Result<Vec<u8>>>;
pub(crate) fn body_channel() -> (BodySender, Body) {
let (tx, rx) = mpsc::channel(BODY_CHANNEL_CHUNKS);
(tx, Body::stream(ChannelBody { rx: Rx::Bounded(rx), chunk: Vec::new(), pos: 0, failed: None, on_read: None }))
}
pub(crate) type UnboundedBodySender = mpsc::UnboundedSender<io::Result<Vec<u8>>>;
pub(crate) fn flow_controlled_body_channel(on_read: impl Fn(usize) + Send + 'static) -> (UnboundedBodySender, Body) {
let (tx, rx) = mpsc::unbounded_channel();
(tx, Body::stream(ChannelBody { rx: Rx::Unbounded(rx), chunk: Vec::new(), pos: 0, failed: None, on_read: Some(Box::new(on_read)) }))
}
enum Rx {
Bounded(mpsc::Receiver<io::Result<Vec<u8>>>),
Unbounded(mpsc::UnboundedReceiver<io::Result<Vec<u8>>>),
}
struct ChannelBody {
rx: Rx,
chunk: Vec<u8>,
pos: usize,
failed: Option<(io::ErrorKind, String)>,
on_read: Option<Box<dyn Fn(usize) + Send>>,
}
impl Read for ChannelBody {
fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
if let Some((kind, message)) = &self.failed {
return Err(io::Error::new(*kind, message.clone()));
}
while self.pos >= self.chunk.len() {
let next = match &mut self.rx {
Rx::Bounded(rx) => rx.blocking_recv(),
Rx::Unbounded(rx) => rx.blocking_recv(),
};
match next {
Some(Ok(next)) => {
self.chunk = next;
self.pos = 0;
}
Some(Err(e)) => {
self.failed = Some((e.kind(), e.to_string()));
return Err(e);
}
None => return Ok(0),
}
}
let n = out.len().min(self.chunk.len() - self.pos);
out[..n].copy_from_slice(&self.chunk[self.pos..self.pos + n]);
self.pos += n;
if let Some(on_read) = &self.on_read {
on_read(n);
}
Ok(n)
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum PumpOutcome {
Complete,
Abandoned,
Failed,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chunks_then_end() {
let (tx, body) = body_channel();
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
rt.block_on(async {
tx.send(Ok(b"hello ".to_vec())).await.unwrap();
tx.send(Ok(b"world".to_vec())).await.unwrap();
});
drop(tx);
let mut out = String::new();
body.into_reader().read_to_string(&mut out).unwrap();
assert_eq!("hello world", out);
}
#[test]
fn a_flow_controlled_channel_reports_reads() {
let reported = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
let counter = reported.clone();
let (tx, body) = flow_controlled_body_channel(move |n| { counter.fetch_add(n, std::sync::atomic::Ordering::SeqCst); });
tx.send(Ok(b"0123456789".to_vec())).unwrap();
drop(tx);
let mut reader = body.into_reader();
let mut buf = [0u8; 4];
assert_eq!(4, reader.read(&mut buf).unwrap());
assert_eq!(4, reported.load(std::sync::atomic::Ordering::SeqCst));
let mut rest = Vec::new();
reader.read_to_end(&mut rest).unwrap();
assert_eq!(10, reported.load(std::sync::atomic::Ordering::SeqCst));
}
#[test]
fn an_error_reaches_the_reader_and_sticks() {
let (tx, body) = body_channel();
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
rt.block_on(async {
tx.send(Ok(b"part".to_vec())).await.unwrap();
tx.send(Err(io::Error::new(io::ErrorKind::UnexpectedEof, "client went away"))).await.unwrap();
});
let mut reader = body.into_reader();
let mut buf = [0u8; 16];
assert_eq!(4, reader.read(&mut buf).unwrap());
assert_eq!(io::ErrorKind::UnexpectedEof, reader.read(&mut buf).unwrap_err().kind());
assert_eq!(io::ErrorKind::UnexpectedEof, reader.read(&mut buf).unwrap_err().kind());
}
}