diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 4b26d9348..c1e77aa17 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -10,6 +10,7 @@ Current package versions: No pending unreleased changes - Add `ConfigurationOptions.SetUserPemCertificate(...)` and `ConfigurationOptions.SetUserPfxCertificate(...)` methods to simplify using client certificates ([#2873 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2873)) +- Fix: Move `AuthenticateAsClient` to fully async after dropping older framework support, to help client thread starvation in cases TLS negotiation stalls server-side ([#2878 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2878)) ## 2.8.31 diff --git a/src/StackExchange.Redis/Configuration/LoggingTunnel.cs b/src/StackExchange.Redis/Configuration/LoggingTunnel.cs index 987d2075c..d61442071 100644 --- a/src/StackExchange.Redis/Configuration/LoggingTunnel.cs +++ b/src/StackExchange.Redis/Configuration/LoggingTunnel.cs @@ -367,10 +367,10 @@ private async Task TlsHandshakeAsync(Stream stream, EndPoint endpoint) } else { - ssl.AuthenticateAsClient(host, _options.SslProtocols, _options.CheckCertificateRevocation); + await ssl.AuthenticateAsClientAsync(host, _options.SslProtocols, _options.CheckCertificateRevocation).ForAwait(); } #else - ssl.AuthenticateAsClient(host, _options.SslProtocols, _options.CheckCertificateRevocation); + await ssl.AuthenticateAsClientAsync(host, _options.SslProtocols, _options.CheckCertificateRevocation).ForAwait(); #endif return ssl; } diff --git a/src/StackExchange.Redis/ExtensionMethods.cs b/src/StackExchange.Redis/ExtensionMethods.cs index 87904aa9c..e5a5c4d4d 100644 --- a/src/StackExchange.Redis/ExtensionMethods.cs +++ b/src/StackExchange.Redis/ExtensionMethods.cs @@ -7,6 +7,7 @@ using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Text; +using System.Threading.Tasks; using Pipelines.Sockets.Unofficial.Arenas; namespace StackExchange.Redis @@ -188,22 +189,16 @@ public static class ExtensionMethods return Array.ConvertAll(values, x => (string?)x); } - internal static void AuthenticateAsClient(this SslStream ssl, string host, SslProtocols? allowedProtocols, bool checkCertificateRevocation) + internal static Task AuthenticateAsClientAsync(this SslStream ssl, string host, SslProtocols? allowedProtocols, bool checkCertificateRevocation) { if (!allowedProtocols.HasValue) { // Default to the sslProtocols defined by the .NET Framework - AuthenticateAsClientUsingDefaultProtocols(ssl, host); - return; + return ssl.AuthenticateAsClientAsync(host); } var certificateCollection = new X509CertificateCollection(); - ssl.AuthenticateAsClient(host, certificateCollection, allowedProtocols.Value, checkCertificateRevocation); - } - - private static void AuthenticateAsClientUsingDefaultProtocols(SslStream ssl, string host) - { - ssl.AuthenticateAsClient(host); + return ssl.AuthenticateAsClientAsync(host, certificateCollection, allowedProtocols.Value, checkCertificateRevocation); } /// diff --git a/src/StackExchange.Redis/PhysicalConnection.cs b/src/StackExchange.Redis/PhysicalConnection.cs index df30ab207..51fac7c3d 100644 --- a/src/StackExchange.Redis/PhysicalConnection.cs +++ b/src/StackExchange.Redis/PhysicalConnection.cs @@ -1585,10 +1585,10 @@ internal async ValueTask ConnectedAsync(Socket? socket, ILogger? log, Sock } else { - ssl.AuthenticateAsClient(host, config.SslProtocols, config.CheckCertificateRevocation); + await ssl.AuthenticateAsClientAsync(host, config.SslProtocols, config.CheckCertificateRevocation).ForAwait(); } #else - ssl.AuthenticateAsClient(host, config.SslProtocols, config.CheckCertificateRevocation); + await ssl.AuthenticateAsClientAsync(host, config.SslProtocols, config.CheckCertificateRevocation).ForAwait(); #endif } catch (Exception ex)