Skip to content

Commit a530808

Browse files
authored
rx read hangs because of redundant/excessive try_recv() (hyperium#219)
If self.remaining_data is 0, recv_data() calls poll_next() which will do a try_recv() and bring in and decode the frame. And right after that we end up doing poll_data() which will again do a try_recv and this time it can end up hanging if the other end has not sent any more data. poll_data clearly checks if self.remaining_data is NON-ZERO at the top of that API, so before calling try_recv() it KNOWS that there is data to be read. So even if try_recv() says pending, it can/should go ahead and pull in the existing data.
1 parent 5c16195 commit a530808

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

h3/src/frame.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::task::{Context, Poll};
22

33
use bytes::Buf;
44

5-
use futures_util::ready;
65
use tracing::trace;
76

87
use crate::stream::{BufRecvStream, WriteBuf};
@@ -96,7 +95,11 @@ where
9695
return Poll::Ready(Ok(None));
9796
};
9897

99-
let end = ready!(self.try_recv(cx))?;
98+
let end = match self.try_recv(cx) {
99+
Poll::Ready(Ok(end)) => end,
100+
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
101+
Poll::Pending => false,
102+
};
100103
let data = self.stream.buf_mut().take_chunk(self.remaining_data);
101104

102105
match (data, end) {

h3/src/tests/connection.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,43 @@ async fn server_drop_close() {
9393
tokio::join!(server_fut, client_fut);
9494
}
9595

96+
// In this test the client calls send_data() without doing a finish(),
97+
// i.e client keeps the body stream open. And cient expects server to
98+
// read_data() and send a response
99+
#[tokio::test]
100+
async fn server_send_data_without_finish() {
101+
let mut pair = Pair::default();
102+
let mut server = pair.server();
103+
104+
let client_fut = async {
105+
let (_driver, mut send_request) = client::new(pair.client().await).await.unwrap();
106+
107+
let mut req = send_request
108+
.send_request(Request::get("http://no.way").body(()).unwrap())
109+
.await
110+
.unwrap();
111+
let data = vec![0; 100];
112+
let _ = req
113+
.send_data(bytes::Bytes::copy_from_slice(&data))
114+
.await
115+
.unwrap();
116+
let _ = req.recv_response().await.unwrap();
117+
};
118+
119+
let server_fut = async {
120+
let conn = server.next().await;
121+
let mut incoming = server::Connection::new(conn).await.unwrap();
122+
let (_, mut stream) = incoming.accept().await.unwrap().unwrap();
123+
let mut data = stream.recv_data().await.unwrap().unwrap();
124+
let data = data.copy_to_bytes(data.remaining());
125+
assert_eq!(data.len(), 100);
126+
response(stream).await;
127+
server.endpoint.wait_idle().await;
128+
};
129+
130+
tokio::join!(server_fut, client_fut);
131+
}
132+
96133
#[tokio::test]
97134
async fn client_close_only_on_last_sender_drop() {
98135
let mut pair = Pair::default();

0 commit comments

Comments
 (0)