From 44ccd2ff93e20cfb6561d7a307806bbbedebb4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Wed, 26 Nov 2025 13:35:10 +0100 Subject: [PATCH] Write failing test case --- quinn-proto/src/tests/multipath.rs | 72 ++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/quinn-proto/src/tests/multipath.rs b/quinn-proto/src/tests/multipath.rs index 5c621ab56..b7bb2b1d2 100644 --- a/quinn-proto/src/tests/multipath.rs +++ b/quinn-proto/src/tests/multipath.rs @@ -363,6 +363,78 @@ fn issue_max_path_id() { assert_eq!(stats.frame_rx.path_new_connection_id, client_path_new_cids); } +/// A copy of [`issue_max_path_id`], but reordering the `MAX_PATH_ID` frame +/// that's sent from the server to the client, so that some `NEW_CONNECTION_ID` +/// frames arrive with higher path IDs than the most recently received +/// `MAX_PATH_ID` frame on the client side. +#[test] +fn issue_max_path_id_reordered() { + let _guard = subscribe(); + + // We enable multipath but initially do not allow any paths to be opened. + let multipath_transport_cfg = Arc::new(TransportConfig { + max_concurrent_multipath_paths: NonZeroU32::new(1), + ..TransportConfig::default() + }); + let server_cfg = Arc::new(ServerConfig { + transport: multipath_transport_cfg.clone(), + ..server_config() + }); + let server = Endpoint::new(Default::default(), Some(server_cfg), true, None); + let client = Endpoint::new(Default::default(), None, true, None); + + let mut pair = Pair::new_from_endpoint(client, server); + + // The client is allowed to create more paths immediately. + let client_multipath_transport_cfg = Arc::new(TransportConfig { + max_concurrent_multipath_paths: NonZeroU32::new(MAX_PATHS), + ..TransportConfig::default() + }); + let client_cfg = ClientConfig { + transport: client_multipath_transport_cfg, + ..client_config() + }; + let (_client_ch, server_ch) = pair.connect_with(client_cfg); + pair.drive(); + info!("connected"); + + // Server should only have sent NEW_CONNECTION_ID frames for now. + let server_new_cids = CidQueue::LEN as u64 - 1; + let mut server_path_new_cids = 0; + let stats = pair.server_conn_mut(server_ch).stats(); + assert_eq!(stats.frame_tx.max_path_id, 0); + assert_eq!(stats.frame_tx.new_connection_id, server_new_cids); + assert_eq!(stats.frame_tx.path_new_connection_id, server_path_new_cids); + + // Client should have sent PATH_NEW_CONNECTION_ID frames for PathId::ZERO. + let client_new_cids = 0; + let mut client_path_new_cids = CidQueue::LEN as u64; + assert_eq!(stats.frame_rx.new_connection_id, client_new_cids); + assert_eq!(stats.frame_rx.path_new_connection_id, client_path_new_cids); + + // Server increases MAX_PATH_ID, but we reorder the frame + pair.server_conn_mut(server_ch) + .set_max_concurrent_paths(Instant::now(), NonZeroU32::new(MAX_PATHS).unwrap()) + .unwrap(); + pair.drive_server(); + // reorder the frames on the incoming side + let p = pair.client.inbound.pop_front().unwrap(); + pair.client.inbound.push_back(p); + pair.drive(); + let stats = pair.server_conn_mut(server_ch).stats(); + + // Server should have sent MAX_PATH_ID and new CIDs + server_path_new_cids += (MAX_PATHS as u64 - 1) * CidQueue::LEN as u64; + assert_eq!(stats.frame_tx.max_path_id, 1); + assert_eq!(stats.frame_tx.new_connection_id, server_new_cids); + assert_eq!(stats.frame_tx.path_new_connection_id, server_path_new_cids); + + // Client should have sent CIDs for new paths + client_path_new_cids += (MAX_PATHS as u64 - 1) * CidQueue::LEN as u64; + assert_eq!(stats.frame_rx.new_connection_id, client_new_cids); + assert_eq!(stats.frame_rx.path_new_connection_id, client_path_new_cids); +} + #[test] fn open_path() { let _guard = subscribe();