diff --git a/src/DiffEngineTray.Tests/PiperTest.cs b/src/DiffEngineTray.Tests/PiperTest.cs index f72191a4..d0937337 100644 --- a/src/DiffEngineTray.Tests/PiperTest.cs +++ b/src/DiffEngineTray.Tests/PiperTest.cs @@ -1,3 +1,6 @@ +using System.Net; +using System.Net.Sockets; + public class PiperTest : IDisposable { @@ -64,6 +67,36 @@ public async Task Move() await Verify(received); } + [Test] + public async Task ClientDisconnectsAbruptly() + { + DeletePayload? received = null; + var source = new CancelSource(); + var task = PiperServer.Start(_ => { }, s => received = s, source.Token); + + // Connect and immediately close with RST (no data sent), + // simulating a client that was canceled mid-connection. + using (var client = new TcpClient()) + { + await client.ConnectAsync(IPAddress.Loopback, PiperClient.Port); + // Linger with timeout 0 causes a RST (forcible close) on Close + client.LingerState = new(true, 0); + } + + // Give the server time to process the abrupt disconnect + await Task.Delay(500); + + // Server should still work after the abrupt disconnect + await PiperClient.SendDeleteAsync("Foo", source.Token); + await Task.Delay(1000, source.Token); + source.Cancel(); + await task; + + // Verify the server recovered and processed the subsequent valid message + Assert.NotNull(received); + Assert.Equal("Foo", received!.File); + } + [Test] public async Task SendOnly() { diff --git a/src/DiffEngineTray/PiperServer.cs b/src/DiffEngineTray/PiperServer.cs index 76aeb862..62e8f0ae 100644 --- a/src/DiffEngineTray/PiperServer.cs +++ b/src/DiffEngineTray/PiperServer.cs @@ -35,6 +35,11 @@ public static async Task Start( //when task is cancelled socket is disposed break; } + catch (IOException exception) + when (exception.InnerException is SocketException { SocketErrorCode: SocketError.ConnectionReset }) + { + //client disconnected abruptly, e.g. test was canceled + } catch (Exception exception) { if (cancel.IsCancellationRequested) @@ -58,6 +63,7 @@ static async Task Handle(TcpListener listener, Action move, Action< { using var client = await listener.AcceptTcpClientAsync(cancel); using var reader = new StreamReader(client.GetStream()); + var payload = await reader.ReadToEndAsync(cancel); if (payload.Contains("\"Type\":\"Move\"") ||