mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-27 00:00:12 +02:00
tokio-util: stop polling StreamReader after EOF (#8332)
This commit is contained in:
@@ -158,6 +158,7 @@ pub struct StreamReader<S, B> {
|
|||||||
inner: S,
|
inner: S,
|
||||||
// This field is not pinned.
|
// This field is not pinned.
|
||||||
chunk: Option<B>,
|
chunk: Option<B>,
|
||||||
|
eof: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, B, E> StreamReader<S, B>
|
impl<S, B, E> StreamReader<S, B>
|
||||||
@@ -179,6 +180,7 @@ where
|
|||||||
Self {
|
Self {
|
||||||
inner: stream,
|
inner: stream,
|
||||||
chunk: None,
|
chunk: None,
|
||||||
|
eof: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,6 +279,8 @@ where
|
|||||||
// This unwrap is very sad, but it can't be avoided.
|
// This unwrap is very sad, but it can't be avoided.
|
||||||
let buf = self.project().chunk.as_ref().unwrap().chunk();
|
let buf = self.project().chunk.as_ref().unwrap().chunk();
|
||||||
return Poll::Ready(Ok(buf));
|
return Poll::Ready(Ok(buf));
|
||||||
|
} else if *self.as_mut().project().eof {
|
||||||
|
return Poll::Ready(Ok(&[]));
|
||||||
} else {
|
} else {
|
||||||
match self.as_mut().project().inner.poll_next(cx) {
|
match self.as_mut().project().inner.poll_next(cx) {
|
||||||
Poll::Ready(Some(Ok(chunk))) => {
|
Poll::Ready(Some(Ok(chunk))) => {
|
||||||
@@ -284,7 +288,10 @@ where
|
|||||||
*self.as_mut().project().chunk = Some(chunk);
|
*self.as_mut().project().chunk = Some(chunk);
|
||||||
}
|
}
|
||||||
Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())),
|
Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())),
|
||||||
Poll::Ready(None) => return Poll::Ready(Ok(&[])),
|
Poll::Ready(None) => {
|
||||||
|
*self.as_mut().project().eof = true;
|
||||||
|
return Poll::Ready(Ok(&[]));
|
||||||
|
}
|
||||||
Poll::Pending => return Poll::Pending,
|
Poll::Pending => return Poll::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,6 +318,7 @@ impl<S: Unpin, B> Unpin for StreamReader<S, B> {}
|
|||||||
struct StreamReaderProject<'a, S, B> {
|
struct StreamReaderProject<'a, S, B> {
|
||||||
inner: Pin<&'a mut S>,
|
inner: Pin<&'a mut S>,
|
||||||
chunk: &'a mut Option<B>,
|
chunk: &'a mut Option<B>,
|
||||||
|
eof: &'a mut bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, B> StreamReader<S, B> {
|
impl<S, B> StreamReader<S, B> {
|
||||||
@@ -322,6 +330,7 @@ impl<S, B> StreamReader<S, B> {
|
|||||||
StreamReaderProject {
|
StreamReaderProject {
|
||||||
inner: unsafe { Pin::new_unchecked(&mut me.inner) },
|
inner: unsafe { Pin::new_unchecked(&mut me.inner) },
|
||||||
chunk: &mut me.chunk,
|
chunk: &mut me.chunk,
|
||||||
|
eof: &mut me.eof,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,3 +33,22 @@ async fn test_stream_reader() -> std::io::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_stream_reader_does_not_poll_after_eof() -> std::io::Result<()> {
|
||||||
|
// the first poll of this stream will return `Poll::Ready(None)`,
|
||||||
|
// and the second poll will panic
|
||||||
|
let stream = futures::stream::unfold((), |_| async { None::<(std::io::Result<Bytes>, ())> });
|
||||||
|
let read = StreamReader::new(stream);
|
||||||
|
tokio::pin!(read);
|
||||||
|
let mut buf = [0; 1];
|
||||||
|
|
||||||
|
// the first poll hits the inner stream,
|
||||||
|
// and the inner stream returns `Poll::Ready(None)`.
|
||||||
|
assert_eq!(read.read(&mut buf).await?, 0);
|
||||||
|
// the second poll doesn't hit the inner stream,
|
||||||
|
// so this `.read()` doesn't panic.
|
||||||
|
assert_eq!(read.read(&mut buf).await?, 0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user