mbproxy: add keepalive / connection monitoring
The DL205/DL260 ECOM emits no TCP keepalives, so an idle backend socket can be silently dropped by a middlebox (switch, firewall, NAT) after 2-5 minutes. Enable OS SO_KEEPALIVE on backend and accepted upstream sockets, and drive a periodic synthetic FC03 heartbeat on each idle backend socket so a dead path is detected before a real client request hits it. Controlled by Connection.Keepalive (ON by default). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -264,4 +264,74 @@ public sealed class ReloadValidatorTests
|
||||
Assert.False(valid);
|
||||
Assert.Contains(errors, e => e.Contains("GracefulShutdownTimeoutMs"));
|
||||
}
|
||||
|
||||
// ── Keepalive section ─────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void Validate_DefaultKeepalive_Passes()
|
||||
{
|
||||
// Default ConnectionOptions → default KeepaliveOptions (idle 30 s, request 3 s).
|
||||
var opts = MakeOptions([MakePlc("PLC-A", 5020)]);
|
||||
|
||||
bool valid = ReloadValidator.Validate(opts, out _);
|
||||
|
||||
Assert.True(valid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_NonPositiveTcpProbeCount_Fails()
|
||||
{
|
||||
var opts = new MbproxyOptions
|
||||
{
|
||||
Plcs = [MakePlc("PLC-A", 5020)],
|
||||
Connection = new ConnectionOptions
|
||||
{
|
||||
Keepalive = new KeepaliveOptions { TcpProbeCount = 0 },
|
||||
},
|
||||
};
|
||||
|
||||
bool valid = ReloadValidator.Validate(opts, out var errors);
|
||||
|
||||
Assert.False(valid);
|
||||
Assert.Contains(errors, e => e.Contains("TcpProbeCount"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_OutOfRangeHeartbeatProbeAddress_Fails()
|
||||
{
|
||||
var opts = new MbproxyOptions
|
||||
{
|
||||
Plcs = [MakePlc("PLC-A", 5020)],
|
||||
Connection = new ConnectionOptions
|
||||
{
|
||||
Keepalive = new KeepaliveOptions { BackendHeartbeatProbeAddress = 70000 },
|
||||
},
|
||||
};
|
||||
|
||||
bool valid = ReloadValidator.Validate(opts, out var errors);
|
||||
|
||||
Assert.False(valid);
|
||||
Assert.Contains(errors, e => e.Contains("BackendHeartbeatProbeAddress"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_HeartbeatIdleNotAboveRequestTimeout_Fails()
|
||||
{
|
||||
// BackendHeartbeatIdleMs must sit ABOVE BackendRequestTimeoutMs, else a heartbeat
|
||||
// would be timed out as fast as it could be issued.
|
||||
var opts = new MbproxyOptions
|
||||
{
|
||||
Plcs = [MakePlc("PLC-A", 5020)],
|
||||
Connection = new ConnectionOptions
|
||||
{
|
||||
BackendRequestTimeoutMs = 3000,
|
||||
Keepalive = new KeepaliveOptions { BackendHeartbeatIdleMs = 3000 },
|
||||
},
|
||||
};
|
||||
|
||||
bool valid = ReloadValidator.Validate(opts, out var errors);
|
||||
|
||||
Assert.False(valid);
|
||||
Assert.Contains(errors, e => e.Contains("BackendHeartbeatIdleMs"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user