Simplify ReadWrite interface (#530)

Previously, ReadWrite had methods `is_poolable` and `written_bytes`, which
were solely for the use of unittests.

This replaces `written_bytes` and `TestStream` with a `struct Recorder`
that implements `ReadWrite` and allows unittests to access its recorded
bytes via an `Arc<Mutex<Vec<u8>>>`. It eliminates `is_poolable`; it's fine
to pool a Stream of any kind.

The new `Recorder` also has some convenience methods that abstract away
boilerplate code from many of our unittests.

I got rid of `Stream::from_vec` and `Stream::from_vec_poolable` because
they depended on `TestStream`. They've been replaced by `NoopStream` for
the pool.rs tests, and `ReadOnlyStream` for constructing `Response`s from
`&str` and some test cases.
This commit is contained in:
Jacob Hoffman-Andrews
2022-07-09 10:13:44 -07:00
committed by GitHub
parent 0cf1f8dbb9
commit 9908c446d6
11 changed files with 211 additions and 226 deletions

View File

@@ -20,22 +20,12 @@ use crate::unit::Unit;
/// Trait for things implementing [std::io::Read] + [std::io::Write]. Used in [TlsConnector].
pub trait ReadWrite: Read + Write + Send + Sync + fmt::Debug + 'static {
fn socket(&self) -> Option<&TcpStream>;
fn is_poolable(&self) -> bool;
/// The bytes written to the stream as a Vec<u8>. This is used for tests only.
#[cfg(test)]
fn written_bytes(&self) -> Vec<u8> {
panic!("written_bytes on non Test stream");
}
}
impl ReadWrite for TcpStream {
fn socket(&self) -> Option<&TcpStream> {
Some(self)
}
fn is_poolable(&self) -> bool {
true
}
}
pub trait TlsConnector: Send + Sync {
@@ -54,51 +44,6 @@ impl<T: ReadWrite + ?Sized> ReadWrite for Box<T> {
fn socket(&self) -> Option<&TcpStream> {
ReadWrite::socket(self.as_ref())
}
fn is_poolable(&self) -> bool {
ReadWrite::is_poolable(self.as_ref())
}
#[cfg(test)]
fn written_bytes(&self) -> Vec<u8> {
ReadWrite::written_bytes(self.as_ref())
}
}
struct TestStream(Box<dyn Read + Send + Sync>, Vec<u8>, bool);
impl ReadWrite for TestStream {
fn is_poolable(&self) -> bool {
self.2
}
fn socket(&self) -> Option<&TcpStream> {
None
}
#[cfg(test)]
fn written_bytes(&self) -> Vec<u8> {
self.1.clone()
}
}
impl Read for TestStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl Write for TestStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.1.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl fmt::Debug for TestStream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("TestStream").finish()
}
}
// DeadlineStream wraps a stream such that read() will return an error
@@ -187,6 +132,37 @@ pub(crate) fn io_err_timeout(error: String) -> io::Error {
io::Error::new(io::ErrorKind::TimedOut, error)
}
#[derive(Debug)]
pub(crate) struct ReadOnlyStream(Cursor<Vec<u8>>);
impl ReadOnlyStream {
pub(crate) fn new(v: Vec<u8>) -> Self {
Self(Cursor::new(v))
}
}
impl Read for ReadOnlyStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl std::io::Write for ReadOnlyStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl ReadWrite for ReadOnlyStream {
fn socket(&self) -> Option<&std::net::TcpStream> {
None
}
}
impl fmt::Debug for Stream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.inner.get_ref().socket() {
@@ -197,7 +173,7 @@ impl fmt::Debug for Stream {
}
impl Stream {
fn new(t: impl ReadWrite) -> Stream {
pub(crate) fn new(t: impl ReadWrite) -> Stream {
Stream::logged_create(Stream {
inner: BufReader::new(Box::new(t)),
})
@@ -208,23 +184,6 @@ impl Stream {
stream
}
pub(crate) fn from_vec(v: Vec<u8>) -> Stream {
Stream::logged_create(Stream {
inner: BufReader::new(Box::new(TestStream(
Box::new(Cursor::new(v)),
vec![],
false,
))),
})
}
#[cfg(test)]
pub(crate) fn from_vec_poolable(v: Vec<u8>) -> Stream {
Stream::logged_create(Stream {
inner: BufReader::new(Box::new(TestStream(Box::new(Cursor::new(v)), vec![], true))),
})
}
fn from_tcp_stream(t: TcpStream) -> Stream {
Stream::logged_create(Stream {
inner: BufReader::new(Box::new(t)),
@@ -270,9 +229,6 @@ impl Stream {
None => Ok(false),
}
}
pub fn is_poolable(&self) -> bool {
self.inner.get_ref().is_poolable()
}
pub(crate) fn reset(&mut self) -> io::Result<()> {
// When we are turning this back into a regular, non-deadline Stream,
@@ -296,11 +252,6 @@ impl Stream {
Ok(())
}
}
#[cfg(test)]
pub fn written_bytes(&self) -> Vec<u8> {
self.inner.get_ref().written_bytes()
}
}
impl Read for Stream {
@@ -693,10 +644,6 @@ mod tests {
fn socket(&self) -> Option<&TcpStream> {
unimplemented!()
}
fn is_poolable(&self) -> bool {
unimplemented!()
}
}
// Test that when a DeadlineStream wraps a Stream, and the user performs a series of