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

+12
View File
@@ -120,6 +120,15 @@ public sealed class ElectrumClient : IAsyncDisposable
var tcs = new TaskCompletionSource<JsonElement>(TaskCreationOptions.RunContinuationsAsynchronously);
_pending[id] = tcs;
// The read loop cancels _cts before failing the pending requests, so a request that
// registers after that point lands here instead of waiting for a response that can
// never arrive (the loop is gone).
if (_cts.IsCancellationRequested)
{
_pending.TryRemove(id, out _);
throw new IOException("Connection to the server closed.");
}
var payload = JsonSerializer.SerializeToUtf8Bytes(new
{
jsonrpc = "2.0",
@@ -212,6 +221,9 @@ public sealed class ElectrumClient : IAsyncDisposable
finally
{
await pipe.CompleteAsync();
// Mark the connection dead first (IsConnected, RequestAsync, write loop) so nothing
// can register a request after the pending ones are failed below.
try { _cts.Cancel(); } catch (ObjectDisposedException) { }
foreach (var (_, tcs) in _pending)
tcs.TrySetException(failure ?? new IOException("Connection to the server closed."));
_pending.Clear();