From 10f2b7d972e03b2f906e9577d5e650e6ec7a429c Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Fri, 7 Apr 2023 10:39:42 -0700 Subject: [PATCH 1/5] Suppress new warnings in .NET 8 Preview 2 SDK. (cherry picked from commit 10b1a1ed6e86eda038a378c45150f9e4a03afb6d) --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index 96b4b958f..cca51b0ea 100644 --- a/.editorconfig +++ b/.editorconfig @@ -76,7 +76,10 @@ dotnet_diagnostic.CA1032.severity = none # Implement standard exception construc dotnet_diagnostic.CA1062.severity = silent # Validate arguments of public methods. dotnet_diagnostic.CA1307.severity = silent # Specify StringComparison for clarity. dotnet_diagnostic.CA1508.severity = silent # Avoid dead conditional code. +dotnet_diagnostic.CA1510.severity = none # Use ArgumentNullException.ThrowIfNull. +dotnet_diagnostic.CA1513.severity = none # Use ObjectDisposedException.ThrowIf. dotnet_diagnostic.CA1849.severity = none # Call async methods when in an async method. +dotnet_diagnostic.CA1859.severity = silent # Use concrete return types for performance. dotnet_diagnostic.CA2000.severity = none # Use recommended Dispose pattern. dotnet_diagnostic.CA2100.severity = none # Review SQL queries for security vulnerabilities. dotnet_diagnostic.CA2213.severity = silent # Disposable fields should be disposed. From c3183376325f5a4aaf9caa7a8e86649724f1ebd4 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Sun, 28 May 2023 17:50:18 -0700 Subject: [PATCH 2/5] Remove Azure MySQL integration tests. The Azure Credits for Open Source sponsorship is expiring on May 31st. (cherry picked from commit 584c6b1157551f7663e9498c531fd8d3f4a9188a) Conflicts: azure-pipelines.yml docs/content/home.md --- README.md | 3 --- azure-pipelines.yml | 46 --------------------------------------------- 2 files changed, 49 deletions(-) diff --git a/README.md b/README.md index 8ff8f6852..d9706ca5a 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,3 @@ Development of MySqlConnector is supported by: [Faithlife](https://faithlife.com/about) ([View jobs](https://faithlife.com/careers)) -[![Microsoft Azure](https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Microsoft_Azure.svg/75px-Microsoft_Azure.svg.png)](https://azure.microsoft.com/en-us/overview/open-source/) - -[Azure Credits for Open Source](https://opensource.microsoft.com/azure-credits) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1b7a8cb8b..b80fcb6ca 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -101,52 +101,6 @@ jobs: image: 'mysql:8.0' connectionString: 'server=localhost;port=3306;user id=mysqltest;password=test;database=conformance;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True' -- job: azure_mysql_integration_tests - condition: ne('True', variables['System.PullRequest.IsFork']) - displayName: 'Azure MySQL Integration Tests' - pool: - vmimage: 'windows-latest' - steps: - - task: PowerShell@2 - displayName: Create Database - inputs: - targetType: inline - script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'create schema mysqltest_$(Build.BuildId) collate utf8mb4_0900_ai_ci;'" - - task: UseDotNet@2 - displayName: 'Install .NET Core 6.0' - inputs: - version: 6.0.x - packageType: runtime - - task: UseDotNet@2 - displayName: 'Install .NET Core' - inputs: - version: $(DotNetCoreSdkVersion) - - task: PowerShell@2 - displayName: 'Copy Azure config' - inputs: - targetType: inline - script: Copy-Item -Path ".\.ci\config\config.ssl.json" -Destination ".\tests\IntegrationTests\config.json" - - task: DotNetCoreCLI@2 - displayName: 'Restore packages' - inputs: - command: 'restore' - - task: DotNetCoreCLI@2 - displayName: 'Integration tests (net6.0)' - inputs: - command: 'test' - projects: 'tests/IntegrationTests/IntegrationTests.csproj' - arguments: '-c Release -f net6.0 --no-restore' - testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'Azure', 'net6.0', 'SSL') }} - env: - DATA__UNSUPPORTEDFEATURES: 'CachingSha2Password,Ed25519,GlobalLog,KnownCertificateAuthority,QueryAttributes,RsaEncryption,Sha256Password,StreamingResults,Timeout,Tls11,Tls13,UnixDomainSocket,ZeroDateTime' - DATA__CONNECTIONSTRING: "$(AzureConnectionString);database=mysqltest_$(Build.BuildId);ssl mode=Required;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True" - - task: PowerShell@2 - displayName: Drop Database - condition: always() - inputs: - targetType: inline - script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'drop schema if exists mysqltest_$(Build.BuildId);'" - - job: windows_integration_tests_1 displayName: 'Windows Integration Tests (Part 1)' pool: From 5a7c78c7fe9f8c4550d586007d50007c848731c7 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Sun, 4 Jun 2023 15:34:21 -0700 Subject: [PATCH 3/5] Respect ConnectTimeout when resetting connection. Fixes #1321 When a connection is opened by retrieving it from the pool, the Connect Timeout option should control how long the call to Open waits for a response from the server. --- src/MySqlConnector/Core/ConnectionPool.cs | 9 ++++++++- src/MySqlConnector/MySqlConnection.cs | 2 +- tests/MySqlConnector.Tests/ConnectionTests.cs | 15 +++++++++++++++ tests/MySqlConnector.Tests/FakeMySqlServer.cs | 1 + .../FakeMySqlServerConnection.cs | 5 +++++ 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs index c081bfb50..1a3985e0c 100644 --- a/src/MySqlConnector/Core/ConnectionPool.cs +++ b/src/MySqlConnector/Core/ConnectionPool.cs @@ -17,7 +17,7 @@ internal sealed class ConnectionPool : IDisposable public SslProtocols SslProtocols { get; set; } - public async ValueTask GetSessionAsync(MySqlConnection connection, int startTickCount, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken) + public async ValueTask GetSessionAsync(MySqlConnection connection, int startTickCount, int timeoutMilliseconds, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -65,9 +65,16 @@ public async ValueTask GetSessionAsync(MySqlConnection connection else { if (ConnectionSettings.ConnectionReset || session.DatabaseOverride is not null) + { + if (timeoutMilliseconds != 0) + session.SetTimeout(Math.Max(1, timeoutMilliseconds - (Environment.TickCount - startTickCount))); reuseSession = await session.TryResetConnectionAsync(ConnectionSettings, connection, ioBehavior, cancellationToken).ConfigureAwait(false); + session.SetTimeout(Constants.InfiniteTimeout); + } else + { reuseSession = true; + } } if (!reuseSession) diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs index fd744278b..7f2660256 100644 --- a/src/MySqlConnector/MySqlConnection.cs +++ b/src/MySqlConnector/MySqlConnection.cs @@ -926,7 +926,7 @@ private async ValueTask CreateSessionAsync(ConnectionPool? pool, if (pool is not null) { // this returns an open session - return await pool.GetSessionAsync(this, startTickCount, activity, actualIOBehavior, connectToken).ConfigureAwait(false); + return await pool.GetSessionAsync(this, startTickCount, connectionSettings.ConnectionTimeoutMilliseconds, activity, actualIOBehavior, connectToken).ConfigureAwait(false); } else { diff --git a/tests/MySqlConnector.Tests/ConnectionTests.cs b/tests/MySqlConnector.Tests/ConnectionTests.cs index 81c5b3ed5..42c621346 100644 --- a/tests/MySqlConnector.Tests/ConnectionTests.cs +++ b/tests/MySqlConnector.Tests/ConnectionTests.cs @@ -203,6 +203,21 @@ public void ConnectionTimeout() } } + [Fact] + public void ResetConnectionTimeout() + { + var csb = new MySqlConnectionStringBuilder(m_csb.ConnectionString); + csb.ConnectionTimeout = 4; + using var connection = new MySqlConnection(csb.ConnectionString); + connection.Open(); + connection.Close(); + m_server.ResetDelay = TimeSpan.FromSeconds(10); + var stopwatch = Stopwatch.StartNew(); + var ex = Assert.Throws(() => connection.Open()); + Assert.InRange(stopwatch.ElapsedMilliseconds, 3900, 4100); + Assert.Equal(MySqlErrorCode.UnableToConnectToHost, (MySqlErrorCode) ex.Number); + } + [Fact] public void ReadInfinity() { diff --git a/tests/MySqlConnector.Tests/FakeMySqlServer.cs b/tests/MySqlConnector.Tests/FakeMySqlServer.cs index 951f506ed..aebe0a5f4 100644 --- a/tests/MySqlConnector.Tests/FakeMySqlServer.cs +++ b/tests/MySqlConnector.Tests/FakeMySqlServer.cs @@ -54,6 +54,7 @@ public void Stop() public bool SuppressAuthPluginNameTerminatingNull { get; set; } public bool SendIncompletePostHandshakeResponse { get; set; } public bool BlockOnConnect { get; set; } + public TimeSpan? ResetDelay { get; set; } internal void CancelQuery(int connectionId) { diff --git a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs index bd6677b94..637b7171e 100644 --- a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs +++ b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs @@ -64,7 +64,12 @@ public async Task RunAsync(TcpClient client, CancellationToken token) break; case CommandKind.Ping: + await SendAsync(stream, 1, WriteOk); + break; + case CommandKind.ResetConnection: + if (m_server.ResetDelay is { } resetDelay) + await Task.Delay(resetDelay); await SendAsync(stream, 1, WriteOk); break; From 5284e88caaa0b4cfd2b1103231b119c162bfc083 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Fri, 21 Jul 2023 15:58:51 -0700 Subject: [PATCH 4/5] Persist the fallback TLS version only on success. Fixes #1349 If an exception happens during TLS negotiation, we only want to set the fallback TLS version on the connection pool if connecting succeeded. Otherwise, we could get into a permanent state of trying to connect with a TLS version that will fail. Signed-off-by: Bradley Grainger --- src/MySqlConnector/Core/ServerSession.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs index def779da5..c41b25b32 100644 --- a/src/MySqlConnector/Core/ServerSession.cs +++ b/src/MySqlConnector/Core/ServerSession.cs @@ -424,14 +424,15 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella // (which is SslProtocols.None; see https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls), // then fall back to SslProtocols.Tls11 if that fails and it's possible that the cause is a yaSSL server. bool shouldRetrySsl; + var shouldUpdatePoolSslProtocols = false; var sslProtocols = Pool?.SslProtocols ?? cs.TlsVersions; PayloadData payload; InitialHandshakePayload initialHandshake; do { - bool tls11or10Supported = (sslProtocols & (SslProtocols.Tls | SslProtocols.Tls11)) != SslProtocols.None; - bool tls12Supported = (sslProtocols & SslProtocols.Tls12) == SslProtocols.Tls12; - shouldRetrySsl = (sslProtocols == SslProtocols.None || (tls12Supported && tls11or10Supported)) && Utility.IsWindows(); + var isTls11or10Supported = (sslProtocols & (SslProtocols.Tls | SslProtocols.Tls11)) != SslProtocols.None; + var isTls12Supported = (sslProtocols & SslProtocols.Tls12) == SslProtocols.Tls12; + shouldRetrySsl = (sslProtocols == SslProtocols.None || (isTls12Supported && isTls11or10Supported)) && Utility.IsWindows(); var connected = false; if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets) @@ -538,19 +539,20 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella { await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, connection, sslProtocols, ioBehavior, cancellationToken).ConfigureAwait(false); shouldRetrySsl = false; + if (shouldUpdatePoolSslProtocols && Pool is not null) + Pool.SslProtocols = sslProtocols; } catch (ArgumentException ex) when (ex.ParamName == "sslProtocolType" && sslProtocols == SslProtocols.None) { Log.Debug(ex, "Session{0} doesn't support SslProtocols.None; falling back to explicitly specifying SslProtocols", m_logArguments); sslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; } - catch (Exception ex) when (shouldRetrySsl && ((ex is MySqlException && ex.InnerException is IOException) || ex is IOException)) + catch (Exception ex) when (shouldRetrySsl && ((ex is MySqlException && ex.InnerException is AuthenticationException or IOException) || ex is AuthenticationException or IOException)) { // negotiating TLS 1.2 with a yaSSL-based server throws an exception on Windows, see comment at top of method - Log.Debug(ex, "Session{0} failed negotiating TLS; falling back to TLS 1.1", m_logArguments); + Log.Warn(ex, "Session{0} failed negotiating TLS; falling back to TLS 1.1", m_logArguments); sslProtocols = sslProtocols == SslProtocols.None ? SslProtocols.Tls | SslProtocols.Tls11 : (SslProtocols.Tls | SslProtocols.Tls11) & sslProtocols; - if (Pool is not null) - Pool.SslProtocols = sslProtocols; + shouldUpdatePoolSslProtocols = true; } } else From d4b05de41fb2257d2e08716cf20e95e9ce407b7f Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Fri, 21 Jul 2023 16:13:15 -0700 Subject: [PATCH 5/5] Release 2.2.7. --- docs/content/overview/version-history.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md index 7c0086d00..10770dea9 100644 --- a/docs/content/overview/version-history.md +++ b/docs/content/overview/version-history.md @@ -1,5 +1,5 @@ --- -lastmod: 2023-05-04 +lastmod: 2023-07-21 date: 2017-03-27 menu: main: @@ -10,6 +10,11 @@ weight: 30 # Version History +### 2.2.7 + +* Respect ConnectTimeout when resetting connection: [#1321](https://github.com/mysql-net/MySqlConnector/issues/1321). +* Prevent connection pool falling back to an unsupported TLS version: [#1349](https://github.com/mysql-net/MySqlConnector/issues/1349). + ### 2.2.6 * Ignore deadlock exception when rolling back an XA transaction: [#1317](https://github.com/mysql-net/MySqlConnector/issues/1317).