Skip to content

Commit 99c64cf

Browse files
committed
tests: add test for peer unexpectedly dropping connection
1 parent 5f53606 commit 99c64cf

File tree

3 files changed

+82
-38
lines changed

3 files changed

+82
-38
lines changed

tests/h2-support/src/mock.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ struct Inner {
5454

5555
/// True when the pipe is closed.
5656
closed: bool,
57+
58+
/// Trigger an `UnexpectedEof` error on read
59+
unexpected_eof: bool,
5760
}
5861

5962
const PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
@@ -73,6 +76,7 @@ pub fn new_with_write_capacity(cap: usize) -> (Mock, Handle) {
7376
tx_rem: cap,
7477
tx_rem_task: None,
7578
closed: false,
79+
unexpected_eof: false,
7680
}));
7781

7882
let mock = Mock {
@@ -96,6 +100,11 @@ impl Handle {
96100
&mut self.codec
97101
}
98102

103+
pub fn close_without_notify(&mut self) {
104+
let mut me = self.codec.get_mut().inner.lock().unwrap();
105+
me.unexpected_eof = true;
106+
}
107+
99108
/// Send a frame
100109
pub async fn send(&mut self, item: SendFrame) -> Result<(), SendError> {
101110
// Queue the frame
@@ -348,6 +357,13 @@ impl AsyncRead for Mock {
348357

349358
let mut me = self.pipe.inner.lock().unwrap();
350359

360+
if me.unexpected_eof {
361+
return Poll::Ready(Err(io::Error::new(
362+
io::ErrorKind::UnexpectedEof,
363+
"Simulate an unexpected eof error",
364+
)));
365+
}
366+
351367
if me.rx.is_empty() {
352368
if me.closed {
353369
return Poll::Ready(Ok(()));

tests/h2-tests/tests/client_request.rs

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use futures::future::{join, ready, select, Either};
22
use futures::stream::FuturesUnordered;
33
use futures::StreamExt;
44
use h2_support::prelude::*;
5+
use std::io;
56
use std::pin::Pin;
67
use std::task::Context;
78

@@ -1773,52 +1774,42 @@ async fn receive_settings_frame_twice_with_second_one_empty() {
17731774
}
17741775

17751776
#[tokio::test]
1776-
async fn receive_settings_frame_twice_with_second_one_non_empty() {
1777+
async fn server_drop_connection_unexpectedly_return_unexpected_eof_err() {
17771778
h2_support::trace_init!();
17781779
let (io, mut srv) = mock::new();
17791780

17801781
let srv = async move {
1781-
// Send the initial SETTINGS frame with MAX_CONCURRENT_STREAMS set to 42
1782-
srv.send_frame(frames::settings().max_concurrent_streams(42))
1783-
.await;
1784-
1785-
// Handle the client's connection preface
1786-
srv.read_preface().await.unwrap();
1787-
match srv.next().await {
1788-
Some(frame) => match frame.unwrap() {
1789-
h2::frame::Frame::Settings(_) => {
1790-
let ack = frame::Settings::ack();
1791-
srv.send(ack.into()).await.unwrap();
1792-
}
1793-
frame => {
1794-
panic!("unexpected frame: {:?}", frame);
1795-
}
1796-
},
1797-
None => {
1798-
panic!("unexpected EOF");
1799-
}
1800-
}
1801-
1802-
// Should receive the ack for the server's initial SETTINGS frame
1803-
let frame = assert_settings!(srv.next().await.unwrap().unwrap());
1804-
assert!(frame.is_ack());
1805-
1806-
// Send another SETTINGS frame with no MAX_CONCURRENT_STREAMS
1807-
// This should not update the max_concurrent_send_streams value that
1808-
// the client manages.
1809-
srv.send_frame(frames::settings().max_concurrent_streams(2024))
1810-
.await;
1782+
let settings = srv.assert_client_handshake().await;
1783+
assert_default_settings!(settings);
1784+
srv.recv_frame(
1785+
frames::headers(1)
1786+
.request("GET", "https://http2.akamai.com/")
1787+
.eos(),
1788+
)
1789+
.await;
1790+
srv.close_without_notify();
18111791
};
18121792

18131793
let h2 = async move {
1814-
let (_client, h2) = client::handshake(io).await.unwrap();
1815-
let mut h2 = std::pin::pin!(h2);
1816-
assert_eq!(h2.max_concurrent_send_streams(), usize::MAX);
1817-
h2.as_mut().await.unwrap();
1818-
// The most-recently advertised value should be used
1819-
assert_eq!(h2.max_concurrent_send_streams(), 2024);
1794+
let (mut client, h2) = client::handshake(io).await.unwrap();
1795+
tokio::spawn(async move {
1796+
let request = Request::builder()
1797+
.uri("https://http2.akamai.com/")
1798+
.body(())
1799+
.unwrap();
1800+
let _res = client
1801+
.send_request(request, true)
1802+
.unwrap()
1803+
.0
1804+
.await
1805+
.expect("request");
1806+
});
1807+
let err = h2.await.expect_err("should receive UnexpectedEof");
1808+
assert_eq!(
1809+
err.get_io().expect("should be UnexpectedEof").kind(),
1810+
io::ErrorKind::UnexpectedEof,
1811+
);
18201812
};
1821-
18221813
join(srv, h2).await;
18231814
}
18241815

tests/h2-tests/tests/server.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,3 +1416,40 @@ async fn reject_informational_status_header_in_request() {
14161416

14171417
join(client, srv).await;
14181418
}
1419+
1420+
#[tokio::test]
1421+
async fn client_drop_connection_without_close_notify() {
1422+
h2_support::trace_init!();
1423+
1424+
let (io, mut client) = mock::new();
1425+
let client = async move {
1426+
let _recv_settings = client.assert_server_handshake().await;
1427+
client
1428+
.send_frame(frames::headers(1).request("GET", "https://example.com/"))
1429+
.await;
1430+
client.send_frame(frames::data(1, &b"hello"[..])).await;
1431+
client.recv_frame(frames::headers(1).response(200)).await;
1432+
1433+
client.close_without_notify(); // Client closed without notify causing UnexpectedEof
1434+
};
1435+
1436+
let mut builder = server::Builder::new();
1437+
builder.max_concurrent_streams(1);
1438+
1439+
let h2 = async move {
1440+
let mut srv = builder.handshake::<_, Bytes>(io).await.expect("handshake");
1441+
let (req, mut stream) = srv.next().await.unwrap().unwrap();
1442+
1443+
assert_eq!(req.method(), &http::Method::GET);
1444+
1445+
let rsp = http::Response::builder().status(200).body(()).unwrap();
1446+
stream.send_response(rsp, false).unwrap();
1447+
1448+
// Step the conn state forward and hitting the EOF
1449+
// But we have no outstanding request from client to be satisfied, so we should not return
1450+
// an error
1451+
let _ = poll_fn(|cx| srv.poll_closed(cx)).await.unwrap();
1452+
};
1453+
1454+
join(client, h2).await;
1455+
}

0 commit comments

Comments
 (0)