fix(historian-client): dispose TcpClient/SslStream on connect+TLS failure (review)
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client;
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// <b>Retry / backoff ownership (finding 006):</b> this module performs exactly one
|
/// <b>Retry / backoff ownership (finding 006):</b> this module performs exactly one
|
||||||
/// in-place transport reconnect inside <c>PipeChannel.InvokeAsync</c> with no delay,
|
/// in-place transport reconnect inside <c>FrameChannel.InvokeAsync</c> with no delay,
|
||||||
/// and does NOT implement exponential reconnect backoff. Broader retry/backoff is the
|
/// and does NOT implement exponential reconnect backoff. Broader retry/backoff is the
|
||||||
/// caller's responsibility — the alarm drain worker
|
/// caller's responsibility — the alarm drain worker
|
||||||
/// (<c>Core.AlarmHistorian.SqliteStoreAndForwardSink</c>) and the read-side
|
/// (<c>Core.AlarmHistorian.SqliteStoreAndForwardSink</c>) and the read-side
|
||||||
|
|||||||
+18
-2
@@ -59,13 +59,21 @@ internal sealed class FrameChannel : IAsyncDisposable
|
|||||||
throw new InvalidOperationException("WonderwareHistorianClientOptions.Host is required for the TCP transport.");
|
throw new InvalidOperationException("WonderwareHistorianClientOptions.Host is required for the TCP transport.");
|
||||||
|
|
||||||
var tcp = new TcpClient();
|
var tcp = new TcpClient();
|
||||||
using (var connectCts = CancellationTokenSource.CreateLinkedTokenSource(ct))
|
try
|
||||||
{
|
{
|
||||||
|
using var connectCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||||
connectCts.CancelAfter(opts.EffectiveConnectTimeout);
|
connectCts.CancelAfter(opts.EffectiveConnectTimeout);
|
||||||
await tcp.ConnectAsync(opts.Host!, opts.Port, connectCts.Token).ConfigureAwait(false);
|
await tcp.ConnectAsync(opts.Host!, opts.Port, connectCts.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
tcp.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
tcp.NoDelay = true;
|
tcp.NoDelay = true;
|
||||||
|
|
||||||
|
// The returned NetworkStream owns the socket (TcpClient.GetStream() uses ownsSocket: true),
|
||||||
|
// so FrameChannel.ResetTransport() disposing this stream closes the underlying socket.
|
||||||
Stream stream = tcp.GetStream();
|
Stream stream = tcp.GetStream();
|
||||||
if (!opts.UseTls) return stream;
|
if (!opts.UseTls) return stream;
|
||||||
|
|
||||||
@@ -75,7 +83,15 @@ internal sealed class FrameChannel : IAsyncDisposable
|
|||||||
return string.Equals(cert?.GetCertHashString(), opts.ServerCertThumbprint, StringComparison.OrdinalIgnoreCase);
|
return string.Equals(cert?.GetCertHashString(), opts.ServerCertThumbprint, StringComparison.OrdinalIgnoreCase);
|
||||||
return errors == SslPolicyErrors.None;
|
return errors == SslPolicyErrors.None;
|
||||||
});
|
});
|
||||||
await ssl.AuthenticateAsClientAsync(opts.Host!).ConfigureAwait(false);
|
try
|
||||||
|
{
|
||||||
|
await ssl.AuthenticateAsClientAsync(opts.Host!).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await ssl.DisposeAsync().ConfigureAwait(false);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
return ssl;
|
return ssl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user