cultnet-rs: pin every RUDP stray drop site with an exact count
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
6b3da283b4
commit
2ef7ae92c4
1 changed files with 130 additions and 3 deletions
|
|
@ -50,6 +50,24 @@ fn send_to(socket: &UdpSocket, to: SocketAddr, packet: &CultNetRudpPacket) -> Re
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// How many datagrams `send_stray` puts on the wire for `kind`.
|
||||
fn datagrams(kind: Stray) -> u64 {
|
||||
match kind {
|
||||
Stray::Malformed => 2,
|
||||
Stray::ForeignConnectionId => 4,
|
||||
Stray::UnadmittedSender => 3,
|
||||
}
|
||||
}
|
||||
|
||||
/// A packet the sender's own session accepted for sending and its receiver's
|
||||
/// session refuses: a fragment with no fragment id.
|
||||
fn poisoned(session: &mut CultNetRudpSession) -> Result<CultNetRudpPacket> {
|
||||
let mut packet = session.send("schema", b"poison".to_vec(), reliable())?;
|
||||
packet.fragment_count = 2;
|
||||
packet.fragment_id = 0;
|
||||
Ok(packet)
|
||||
}
|
||||
|
||||
fn send_stray(kind: Stray, from: &UdpSocket, to: SocketAddr) -> Result<()> {
|
||||
match kind {
|
||||
Stray::Malformed => {
|
||||
|
|
@ -98,7 +116,7 @@ fn hub_keeps_serving_its_session(kind: Stray) -> Result<()> {
|
|||
send_stray(kind, &stray, hub_addr)?;
|
||||
std::thread::sleep(Duration::from_millis(20));
|
||||
while hub.receive_event_once()?.is_some() {}
|
||||
assert!(hub.stats().packets_dropped > 0, "strays are counted");
|
||||
assert_eq!(hub.stats().packets_dropped, datagrams(kind), "every stray is counted once");
|
||||
|
||||
real.send("schema", b"still here".to_vec())?;
|
||||
std::thread::sleep(Duration::from_millis(20));
|
||||
|
|
@ -174,7 +192,7 @@ fn client_keeps_its_session(kind: Stray) -> Result<()> {
|
|||
.receive_once()?
|
||||
.expect("the real frame behind the strays");
|
||||
assert_eq!(frame.payload, b"real");
|
||||
assert!(client.stats().packets_dropped > 0, "strays are counted");
|
||||
assert_eq!(client.stats().packets_dropped, datagrams(kind), "every stray is counted once");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +224,8 @@ fn server_mode_keeps_its_peer(kind: Stray) -> Result<()> {
|
|||
.receive_once()?
|
||||
.expect("the real frame behind the strays");
|
||||
assert_eq!(frame.payload, b"real");
|
||||
assert!(server.stats().packets_dropped > 0, "strays are counted");
|
||||
// Once from an unrelated address, once from the peer's own.
|
||||
assert_eq!(server.stats().packets_dropped, 2 * datagrams(kind), "every stray is counted once");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -253,3 +272,111 @@ per_stray!(
|
|||
server_mode_survives_malformed_frames,
|
||||
server_mode_survives_foreign_connection_ids
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn hub_drops_a_packet_its_admitted_peer_session_refuses() -> Result<()> {
|
||||
let mut hub = CultNetRudpServerHub::new(CultNetRudpServerHubOptions::new(
|
||||
"hub",
|
||||
socket()?,
|
||||
CONNECTION_ID,
|
||||
))?;
|
||||
let hub_addr = hub.local_addr()?;
|
||||
let peer = socket()?;
|
||||
let mut peer_session = raw_session(CONNECTION_ID);
|
||||
send_to(&peer, hub_addr, &peer_session.create_connect(0, b"peer".to_vec())?)?;
|
||||
assert!(matches!(
|
||||
hub.receive_event_once()?,
|
||||
Some(CultNetRudpServerEvent::Connected { .. })
|
||||
));
|
||||
let mut buffer = vec![0_u8; 65_535];
|
||||
let (received, _) = peer.recv_from(&mut buffer)?;
|
||||
peer_session.receive(&decode_rudp_packet(&buffer[..received])?, 0)?;
|
||||
|
||||
send_to(&peer, hub_addr, &poisoned(&mut peer_session)?)?;
|
||||
assert!(hub.receive_event_once()?.is_none());
|
||||
assert_eq!(hub.stats().packets_dropped, 1);
|
||||
assert_eq!(hub.sessions().len(), 1);
|
||||
|
||||
send_to(
|
||||
&peer,
|
||||
hub_addr,
|
||||
&peer_session.send("schema", b"still here".to_vec(), reliable())?,
|
||||
)?;
|
||||
match hub.receive_event_once()? {
|
||||
Some(CultNetRudpServerEvent::Frame { frame, .. }) => {
|
||||
assert_eq!(frame.payload, b"still here");
|
||||
}
|
||||
other => panic!("the peer must still be served, got {other:?}"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_drops_a_packet_its_session_refuses() -> Result<()> {
|
||||
let server = socket()?;
|
||||
let server_addr = server.local_addr()?;
|
||||
let mut client = CultNetRudpSocketTransportConnection::new(
|
||||
CultNetRudpSocketTransportOptions::client("client", socket()?, server_addr, CONNECTION_ID),
|
||||
)?;
|
||||
client.connect(b"hello".to_vec())?;
|
||||
let mut buffer = vec![0_u8; 65_535];
|
||||
let (received, client_addr) = server.recv_from(&mut buffer)?;
|
||||
let mut server_session = raw_session(CONNECTION_ID);
|
||||
let accept =
|
||||
server_session.accept_connect(&decode_rudp_packet(&buffer[..received])?, 0, Vec::new())?;
|
||||
send_to(&server, client_addr, &accept)?;
|
||||
let _ = client.receive_once()?;
|
||||
|
||||
send_to(&server, client_addr, &poisoned(&mut server_session)?)?;
|
||||
assert!(client.receive_once()?.is_none());
|
||||
assert_eq!(client.stats().packets_dropped, 1);
|
||||
send_to(
|
||||
&server,
|
||||
client_addr,
|
||||
&server_session.send("schema", b"real".to_vec(), reliable())?,
|
||||
)?;
|
||||
assert_eq!(client.receive_once()?.expect("the real frame").payload, b"real");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_mode_drops_what_is_not_its_peer_or_what_its_session_refuses() -> Result<()> {
|
||||
let server_socket = socket()?;
|
||||
let server_addr = server_socket.local_addr()?;
|
||||
let mut server = CultNetRudpSocketTransportConnection::new(
|
||||
CultNetRudpSocketTransportOptions::server("server", server_socket, CONNECTION_ID),
|
||||
)?;
|
||||
let stray = socket()?;
|
||||
|
||||
// Before any peer: a right-id packet that is not a Connect claims nothing.
|
||||
send_stray(Stray::UnadmittedSender, &stray, server_addr)?;
|
||||
assert!(server.receive_once()?.is_none());
|
||||
assert_eq!(server.stats().packets_dropped, 3);
|
||||
|
||||
let peer = socket()?;
|
||||
let mut peer_session = raw_session(CONNECTION_ID);
|
||||
send_to(&peer, server_addr, &peer_session.create_connect(0, b"peer".to_vec())?)?;
|
||||
let _ = server.receive_once()?;
|
||||
let mut buffer = vec![0_u8; 65_535];
|
||||
let (received, _) = peer.recv_from(&mut buffer)?;
|
||||
peer_session.receive(&decode_rudp_packet(&buffer[..received])?, 0)?;
|
||||
assert_eq!(server.stats().packets_dropped, 3);
|
||||
|
||||
// After the peer is known: right-id packets from any other address.
|
||||
send_stray(Stray::UnadmittedSender, &stray, server_addr)?;
|
||||
assert!(server.receive_once()?.is_none());
|
||||
assert_eq!(server.stats().packets_dropped, 6);
|
||||
|
||||
// A packet from the peer that the session itself refuses.
|
||||
send_to(&peer, server_addr, &poisoned(&mut peer_session)?)?;
|
||||
assert!(server.receive_once()?.is_none());
|
||||
assert_eq!(server.stats().packets_dropped, 7);
|
||||
|
||||
send_to(
|
||||
&peer,
|
||||
server_addr,
|
||||
&peer_session.send("schema", b"real".to_vec(), reliable())?,
|
||||
)?;
|
||||
assert_eq!(server.receive_once()?.expect("the real frame").payload, b"real");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue