Skip to content

Commit aa1f788

Browse files
committed
tests(iroh): add regression test for transient windows errors
1 parent 3b422bb commit aa1f788

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

iroh/src/endpoint.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2922,6 +2922,62 @@ mod tests {
29222922
Ok(())
29232923
}
29242924

2925+
/// Regression test: Don't fail connections with dead relays on Windows.
2926+
///
2927+
/// A single client connecting to a single server over a usable direct path
2928+
/// must succeed even when both are configured with an unreachable home relay
2929+
/// (`https://127.0.0.1:1`, nothing listening). The dead relay should be irrelevant:
2930+
/// the direct path works and the connection comes up in milliseconds.
2931+
///
2932+
/// This was broken on Windows because QaD sends over the same socket to the dead
2933+
/// relay, and the socket would return recv errors on the next recv to report ICMP
2934+
/// errors for the previous send. We now skip over these errors, implemented in
2935+
/// https://github.com/n0-computer/net-tools/pull/166, so this no longer fails.
2936+
#[tokio::test]
2937+
async fn endpoint_unreachable_relay_direct_connect_succeeds() -> Result {
2938+
// An unreachable relay Nothing listens on 127.0.0.1:1, and its QADv4 probe target
2939+
// at 127.0.0.1:7842 is closed too, so probing it draws the ICMP port-unreachable
2940+
// that is emitted from the Windows socket on recv.
2941+
let dead_relay: RelayUrl = "https://127.0.0.1:1".parse().expect("valid relay url");
2942+
2943+
let bind_endpoint = async || {
2944+
Endpoint::builder(presets::Minimal)
2945+
// Use the broken relay to trigger the ICMP errors from the QaD sends.
2946+
.relay_mode(RelayMode::Custom(RelayMap::from_iter([dead_relay.clone()])))
2947+
.ca_tls_config(CaTlsConfig::insecure_skip_verify())
2948+
.alpns(vec![TEST_ALPN.to_vec()])
2949+
// Bind on IPv4 only to ensure a single socket to not have spurious polls.
2950+
.bind_addr((Ipv4Addr::LOCALHOST, 0))
2951+
.expect("valid addr")
2952+
.bind()
2953+
.await
2954+
};
2955+
2956+
let server = bind_endpoint().await?;
2957+
let server_addr = server.addr().with_relay_url(dead_relay.clone());
2958+
let client = bind_endpoint().await?;
2959+
2960+
// Server accepts the incoming connection and holds it open until the test ends.
2961+
let accept = tokio::spawn(async move {
2962+
let incoming = server.accept().await.anyerr()?;
2963+
let conn = incoming.await.anyerr()?;
2964+
conn.closed().await;
2965+
server.close().await;
2966+
n0_error::Ok(())
2967+
});
2968+
2969+
// The connect must complete over the direct loopback path despite the dead relay.
2970+
let _conn = tokio::time::timeout(
2971+
Duration::from_secs(10),
2972+
client.connect(server_addr, TEST_ALPN),
2973+
)
2974+
.await
2975+
.expect("connection should succeed")?;
2976+
client.close().await;
2977+
accept.await.anyerr()??;
2978+
Ok(())
2979+
}
2980+
29252981
#[tokio::test]
29262982
#[traced_test]
29272983
async fn test_direct_addresses_no_qad_relay() -> Result {

0 commit comments

Comments
 (0)