fix(net): fail fast on a dead Electrum connection instead of hanging

After a protocol error (e.g. a malformed line) the read loop ended but
the client still reported IsConnected and later requests waited forever
for a response that could never arrive. The read loop now marks the
connection closed before failing pending requests, and RequestAsync
throws IOException on a closed connection. Adds regression tests and a
raw-line helper to the fake server.
This commit is contained in:
davide committed 2026-11-03 11:45:43 +01:00
1 parent 9c0a408c49
commit cb93927d15
3 files changed
+54

No files matched your search

@@ -98,6 +98,40 @@ public class ElectrumClientTests
await disconnected.Task.WaitAsync(Timeout);
}
[Fact]
public async Task A_malformed_line_from_the_server_fails_pending_requests_and_reports_the_error()
{
await using var server = new FakeElectrumServer();
server.Handle("server.banner", _ => FakeElectrumServer.NoResponse);
await using var client = await ElectrumClient.ConnectAsync(server.Host, server.Port, useSsl: false);
var disconnected = new TaskCompletionSource<Exception?>(TaskCreationOptions.RunContinuationsAsynchronously);
client.Disconnected += ex => disconnected.TrySetResult(ex);
var pending = client.BannerAsync();
await server.SendRawAsync("this is not json");
await Assert.ThrowsAnyAsync<Exception>(() => pending.WaitAsync(Timeout));
Assert.NotNull(await disconnected.Task.WaitAsync(Timeout)); // failure, not a clean close
Assert.False(client.IsConnected);
}
[Fact]
public async Task Requests_after_the_connection_died_fail_fast_instead_of_hanging()
{
await using var server = new FakeElectrumServer();
await using var client = await ElectrumClient.ConnectAsync(server.Host, server.Port, useSsl: false);
var disconnected = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
client.Disconnected += _ => disconnected.TrySetResult();
await server.SendRawAsync("garbage"); // kills the read loop
await disconnected.Task.WaitAsync(Timeout);
// The read loop is gone: nobody would ever complete this request.
await Assert.ThrowsAnyAsync<IOException>(() => client.RequestAsync("server.ping").WaitAsync(Timeout));
}
[Fact]
public async Task Una_risposta_molto_grande_arriva_integra()
{