Skip to content

Commit 4ce5955

Browse files
dswijseanmonstar
authored andcommitted
feat: not returning UnexpectedEof when client drop without close_notify
1 parent 560bdb6 commit 4ce5955

File tree

4 files changed

+84
-3
lines changed

4 files changed

+84
-3
lines changed

src/proto/connection.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -461,13 +461,27 @@ where
461461
// active streams must be reset.
462462
//
463463
// TODO: Are I/O errors recoverable?
464-
Err(Error::Io(e, inner)) => {
465-
tracing::debug!(error = ?e, "Connection::poll; IO error");
466-
let e = Error::Io(e, inner);
464+
Err(Error::Io(kind, inner)) => {
465+
tracing::debug!(error = ?kind, "Connection::poll; IO error");
466+
let e = Error::Io(kind, inner);
467467

468468
// Reset all active streams
469469
self.streams.handle_error(e.clone());
470470

471+
// Some client implementations drop the connections without notifying its peer
472+
// Attempting to read after the client dropped the connection results in UnexpectedEof
473+
// If as a server, we don't have anything more to send, just close the connection
474+
// without error
475+
//
476+
// See https://github.com/hyperium/hyper/issues/3427
477+
if self.streams.is_server()
478+
&& self.streams.is_buffer_empty()
479+
&& matches!(kind, io::ErrorKind::UnexpectedEof)
480+
{
481+
*self.state = State::Closed(Reason::NO_ERROR, Initiator::Library);
482+
return Ok(());
483+
}
484+
471485
// Return the error
472486
Err(e)
473487
}

src/proto/streams/buffer.rs

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ impl<T> Buffer<T> {
2929
pub fn new() -> Self {
3030
Buffer { slab: Slab::new() }
3131
}
32+
33+
pub fn is_empty(&self) -> bool {
34+
self.slab.is_empty()
35+
}
3236
}
3337

3438
impl Deque {

src/proto/streams/streams.rs

+13
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,14 @@ where
323323
}
324324

325325
impl<B> DynStreams<'_, B> {
326+
pub fn is_buffer_empty(&self) -> bool {
327+
self.send_buffer.is_empty()
328+
}
329+
330+
pub fn is_server(&self) -> bool {
331+
self.peer.is_server()
332+
}
333+
326334
pub fn recv_headers(&mut self, frame: frame::Headers) -> Result<(), Error> {
327335
let mut me = self.inner.lock().unwrap();
328336

@@ -1509,6 +1517,11 @@ impl<B> SendBuffer<B> {
15091517
let inner = Mutex::new(Buffer::new());
15101518
SendBuffer { inner }
15111519
}
1520+
1521+
pub fn is_empty(&self) -> bool {
1522+
let buf = self.inner.lock().unwrap();
1523+
buf.is_empty()
1524+
}
15121525
}
15131526

15141527
// ===== impl Actions =====

tests/h2-tests/tests/client_request.rs

+50
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,56 @@ async fn receive_settings_frame_twice_with_second_one_empty() {
17731773
join(srv, h2).await;
17741774
}
17751775

1776+
#[tokio::test]
1777+
async fn receive_settings_frame_twice_with_second_one_non_empty() {
1778+
h2_support::trace_init!();
1779+
let (io, mut srv) = mock::new();
1780+
1781+
let srv = async move {
1782+
// Send the initial SETTINGS frame with MAX_CONCURRENT_STREAMS set to 42
1783+
srv.send_frame(frames::settings().max_concurrent_streams(42))
1784+
.await;
1785+
1786+
// Handle the client's connection preface
1787+
srv.read_preface().await.unwrap();
1788+
match srv.next().await {
1789+
Some(frame) => match frame.unwrap() {
1790+
h2::frame::Frame::Settings(_) => {
1791+
let ack = frame::Settings::ack();
1792+
srv.send(ack.into()).await.unwrap();
1793+
}
1794+
frame => {
1795+
panic!("unexpected frame: {:?}", frame);
1796+
}
1797+
},
1798+
None => {
1799+
panic!("unexpected EOF");
1800+
}
1801+
}
1802+
1803+
// Should receive the ack for the server's initial SETTINGS frame
1804+
let frame = assert_settings!(srv.next().await.unwrap().unwrap());
1805+
assert!(frame.is_ack());
1806+
1807+
// Send another SETTINGS frame with no MAX_CONCURRENT_STREAMS
1808+
// This should not update the max_concurrent_send_streams value that
1809+
// the client manages.
1810+
srv.send_frame(frames::settings().max_concurrent_streams(2024))
1811+
.await;
1812+
};
1813+
1814+
let h2 = async move {
1815+
let (_client, h2) = client::handshake(io).await.unwrap();
1816+
let mut h2 = std::pin::pin!(h2);
1817+
assert_eq!(h2.max_concurrent_send_streams(), usize::MAX);
1818+
h2.as_mut().await.unwrap();
1819+
// The most-recently advertised value should be used
1820+
assert_eq!(h2.max_concurrent_send_streams(), 2024);
1821+
};
1822+
1823+
join(srv, h2).await;
1824+
}
1825+
17761826
#[tokio::test]
17771827
async fn server_drop_connection_unexpectedly_return_unexpected_eof_err() {
17781828
h2_support::trace_init!();

0 commit comments

Comments
 (0)