Address PR review: connection-wide idle watchdog, test hardening

- netrelay: replace per-direction read-deadline idle tracking with a
  single connection-wide watchdog that observes activity on both sides,
  so a long one-way transfer no longer trips the timeout on the quiet
  direction. IdleTimeout==0 remains a no-op (SSH and uspfilter forwarder
  call sites pass zero); only the reverse-proxy router sets one.
- netrelay tests: bound blocking peer reads/writes with deadlines so a
  broken relay fails fast; add a lower-bound assertion on the idle-timeout
  test.
- conntrack cap tests: assert that the newest flow is admitted and an
  early flow was evicted, not just that the table stayed under the cap.
- ssh client RemotePortForward: bound the localAddr dial with a 10s
  timeout so a black-holed address can't pin the accepted channel open.
This commit is contained in:
Viktor Liu
2026-04-21 13:01:50 +02:00
parent ffac18409e
commit 10da236dae
4 changed files with 97 additions and 44 deletions

View File

@@ -51,6 +51,12 @@ func TestRelayHalfClose(t *testing.T) {
defer peerA.Close()
defer peerB.Close()
// Bound blocking reads/writes so a broken relay fails the test instead of
// hanging the test process.
deadline := time.Now().Add(5 * time.Second)
require.NoError(t, peerA.SetDeadline(deadline))
require.NoError(t, peerB.SetDeadline(deadline))
ctx := t.Context()
done := make(chan struct{})
@@ -98,6 +104,12 @@ func TestRelayFullDuplex(t *testing.T) {
defer peerA.Close()
defer peerB.Close()
// Bound blocking reads/writes so a broken relay fails the test instead of
// hanging the test process.
deadline := time.Now().Add(5 * time.Second)
require.NoError(t, peerA.SetDeadline(deadline))
require.NoError(t, peerB.SetDeadline(deadline))
ctx := t.Context()
done := make(chan struct{})
@@ -186,10 +198,12 @@ func TestRelayIdleTimeout(t *testing.T) {
ctx := t.Context()
const idle = 150 * time.Millisecond
start := time.Now()
done := make(chan struct{})
go func() {
Relay(ctx, relayA, relayB, Options{IdleTimeout: 150 * time.Millisecond})
Relay(ctx, relayA, relayB, Options{IdleTimeout: idle})
close(done)
}()
@@ -199,5 +213,9 @@ func TestRelayIdleTimeout(t *testing.T) {
t.Fatal("relay did not close on idle")
}
require.WithinDuration(t, start.Add(150*time.Millisecond), time.Now(), 500*time.Millisecond)
elapsed := time.Since(start)
require.GreaterOrEqual(t, elapsed, idle,
"relay must not close before the idle timeout elapses")
require.Less(t, elapsed, idle+500*time.Millisecond,
"relay should close shortly after the idle timeout")
}