Skip to content

Commit 6ecf852

Browse files
authoredMar 31, 2024··
fix: avoid graceful_shutdown panic on upgraded H1 connection (#3616)
1 parent bc9a86f commit 6ecf852

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed
 

‎src/server/conn/http1.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,11 @@ where
482482
/// This `Connection` should continue to be polled until shutdown
483483
/// can finish.
484484
pub fn graceful_shutdown(mut self: Pin<&mut Self>) {
485-
Pin::new(self.inner.as_mut().unwrap()).graceful_shutdown()
485+
// Connection (`inner`) is `None` if it was upgraded (and `poll` is `Ready`).
486+
// In that case, we don't need to call `graceful_shutdown`.
487+
if let Some(conn) = self.inner.as_mut() {
488+
Pin::new(conn).graceful_shutdown()
489+
}
486490
}
487491
}
488492

‎tests/server.rs

+61
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,67 @@ async fn disable_keep_alive_post_request() {
12561256
child.join().unwrap();
12571257
}
12581258

1259+
#[tokio::test]
1260+
async fn http1_graceful_shutdown_after_upgrade() {
1261+
let (listener, addr) = setup_tcp_listener();
1262+
let (read_101_tx, read_101_rx) = oneshot::channel();
1263+
1264+
thread::spawn(move || {
1265+
let mut tcp = connect(&addr);
1266+
tcp.write_all(
1267+
b"\
1268+
GET / HTTP/1.1\r\n\
1269+
Upgrade: foobar\r\n\
1270+
Connection: upgrade\r\n\
1271+
\r\n\
1272+
eagerly optimistic\
1273+
",
1274+
)
1275+
.expect("write 1");
1276+
let mut buf = [0; 256];
1277+
tcp.read(&mut buf).expect("read 1");
1278+
1279+
let response = s(&buf);
1280+
assert!(response.starts_with("HTTP/1.1 101 Switching Protocols\r\n"));
1281+
assert!(!has_header(&response, "content-length"));
1282+
let _ = read_101_tx.send(());
1283+
});
1284+
1285+
let (upgrades_tx, upgrades_rx) = mpsc::channel();
1286+
let svc = service_fn(move |req: Request<IncomingBody>| {
1287+
let on_upgrade = hyper::upgrade::on(req);
1288+
let _ = upgrades_tx.send(on_upgrade);
1289+
future::ok::<_, hyper::Error>(
1290+
Response::builder()
1291+
.status(101)
1292+
.header("upgrade", "foobar")
1293+
.body(Empty::<Bytes>::new())
1294+
.unwrap(),
1295+
)
1296+
});
1297+
1298+
let (socket, _) = listener.accept().await.unwrap();
1299+
let socket = TokioIo::new(socket);
1300+
1301+
let mut conn = http1::Builder::new()
1302+
.serve_connection(socket, svc)
1303+
.with_upgrades();
1304+
(&mut conn).await.unwrap();
1305+
1306+
let on_upgrade = upgrades_rx.recv().unwrap();
1307+
1308+
// wait so that we don't write until other side saw 101 response
1309+
read_101_rx.await.unwrap();
1310+
1311+
let upgraded = on_upgrade.await.expect("on_upgrade");
1312+
let parts = upgraded.downcast::<TokioIo<TkTcpStream>>().unwrap();
1313+
assert_eq!(parts.read_buf, "eagerly optimistic");
1314+
1315+
pin!(conn);
1316+
// graceful shutdown doesn't cause issues or panic. It should be ignored after upgrade
1317+
conn.as_mut().graceful_shutdown();
1318+
}
1319+
12591320
#[tokio::test]
12601321
async fn empty_parse_eof_does_not_return_error() {
12611322
let (listener, addr) = setup_tcp_listener();

0 commit comments

Comments
 (0)
Please sign in to comment.