mirror of
https://github.com/EasyTier/kcp-sys.git
synced 2026-10-19 16:16:36 +00:00
fix(endpoint): start keepalives after the handshake completes
Exclude handshake states from periodic keepalives. A startup ping can
overtake SYN, causing the peer to reset the unknown connection before
the handshake response arrives. Keep Established, LocalClosed and
PeerClosed connections eligible for their existing keepalive behavior.
Windows packet traces reproduce the premature PING, RST, then SYN on
both the original d7427c2 dependency and the close-fix branch. Add a
deterministic state-boundary regression that fails on the old behavior.
All 10 tests and both example targets pass on Linux, macOS and Windows;
Linux and macOS formatting and strict all-targets clippy checks pass.
This commit is contained in:
+53
-1
@@ -665,7 +665,14 @@ impl KcpEndpoint {
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let (conn_id, state) = item.pair();
|
||||
if state.is_closed() {
|
||||
// Before the handshake completes, a ping can overtake
|
||||
// SYN and make the peer reset an unknown connection.
|
||||
if !matches!(
|
||||
state.fsm,
|
||||
KcpConnectionFSM::Established
|
||||
| KcpConnectionFSM::LocalClosed
|
||||
| KcpConnectionFSM::PeerClosed
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
let mut out_packet = KcpPacket::new(0);
|
||||
@@ -1219,4 +1226,49 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn test_keepalive_waits_for_completed_handshake() {
|
||||
let mut endpoint = KcpEndpoint::new();
|
||||
let mut expected = std::collections::HashSet::new();
|
||||
for (conv, fsm) in [
|
||||
KcpConnectionFSM::Listen,
|
||||
KcpConnectionFSM::SynSent,
|
||||
KcpConnectionFSM::SynReceived,
|
||||
KcpConnectionFSM::Established,
|
||||
KcpConnectionFSM::LocalClosed,
|
||||
KcpConnectionFSM::PeerClosed,
|
||||
KcpConnectionFSM::Closed,
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
let id = ConnId {
|
||||
conv: conv as u32,
|
||||
src_session_id: 1,
|
||||
dst_session_id: 2,
|
||||
};
|
||||
if (3..6).contains(&conv) {
|
||||
expected.insert(id);
|
||||
}
|
||||
endpoint
|
||||
.data
|
||||
.state_map
|
||||
.insert(id, KcpConnectionState::new(fsm));
|
||||
}
|
||||
endpoint.run().await;
|
||||
let mut output = endpoint.output_receiver().unwrap();
|
||||
while let Ok(Some(packet)) =
|
||||
timeout(std::time::Duration::from_millis(50), output.recv()).await
|
||||
{
|
||||
assert!(packet.header().is_ping());
|
||||
assert!(
|
||||
expected.remove(&ConnId::from(&packet)),
|
||||
"keepalive sent before handshake completed"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
expected.is_empty(),
|
||||
"established or half-closed connection lost keepalive"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user