Skip to content

Commit

Permalink
Merge v2.2 into master.
Browse files Browse the repository at this point in the history
Conflicts:
	docs/content/overview/version-history.md
	src/MySqlConnector/Core/ServerSession.cs
  • Loading branch information
bgrainger committed Jul 21, 2023
2 parents 1b7595f + d4b05de commit 7d5879b
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 10 deletions.
7 changes: 6 additions & 1 deletion docs/content/overview/version-history.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
lastmod: 2023-05-04
lastmod: 2023-07-21
date: 2017-03-27
menu:
main:
Expand Down Expand Up @@ -40,6 +40,11 @@ weight: 30
* The minimum supported versions are now .NET Framework 4.6.2 and .NET 6.0, although other frameworks should be supported via `netstandard2.0`.
* Fix bug that didn't copy `MySqlDataSource` in `MySqlConnection.Clone`: [#1267](https://github.com/mysql-net/MySqlConnector/issues/1267).

### 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).
* Work around ephemeral PEM bug on Windows: [#1278](https://github.com/mysql-net/MySqlConnector/issues/1278).
Expand Down
9 changes: 8 additions & 1 deletion src/MySqlConnector/Core/ConnectionPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal sealed class ConnectionPool : IDisposable

public SslProtocols SslProtocols { get; set; }

public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection, int startTickCount, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection, int startTickCount, int timeoutMilliseconds, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

Expand Down Expand Up @@ -67,9 +67,16 @@ public async ValueTask<ServerSession> 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)
Expand Down
14 changes: 8 additions & 6 deletions src/MySqlConnector/Core/ServerSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -536,19 +537,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.SessionDoesNotSupportSslProtocolsNone(m_logger, ex, Id);
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.FailedNegotiatingTls(m_logger, ex, Id);
sslProtocols = sslProtocols == SslProtocols.None ? SslProtocols.Tls | SslProtocols.Tls11 : (SslProtocols.Tls | SslProtocols.Tls11) & sslProtocols;
if (Pool is not null)
Pool.SslProtocols = sslProtocols;
shouldUpdatePoolSslProtocols = true;
}
}
else
Expand Down
2 changes: 1 addition & 1 deletion src/MySqlConnector/Logging/Log.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ internal static partial class Log
[LoggerMessage(EventIds.SessionDoesNotSupportSslProtocolsNone, LogLevel.Debug, "Session {SessionId} doesn't support SslProtocols.None; falling back to explicitly specifying SslProtocols")]
public static partial void SessionDoesNotSupportSslProtocolsNone(ILogger logger, Exception exception, string sessionId);

[LoggerMessage(EventIds.FailedNegotiatingTls, LogLevel.Debug, "Session {SessionId} failed negotiating TLS; falling back to TLS 1.1")]
[LoggerMessage(EventIds.FailedNegotiatingTls, LogLevel.Warning, "Session {SessionId} failed negotiating TLS; falling back to TLS 1.1")]
public static partial void FailedNegotiatingTls(ILogger logger, Exception exception, string sessionId);

[LoggerMessage(EventIds.CouldNotConnectToServer, LogLevel.Error, "Session {SessionId} couldn't connect to server")]
Expand Down
2 changes: 1 addition & 1 deletion src/MySqlConnector/MySqlConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ private async ValueTask<ServerSession> 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
{
Expand Down
15 changes: 15 additions & 0 deletions tests/MySqlConnector.Tests/ConnectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,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<MySqlException>(() => connection.Open());
Assert.InRange(stopwatch.ElapsedMilliseconds, 3900, 4100);
Assert.Equal(MySqlErrorCode.UnableToConnectToHost, (MySqlErrorCode) ex.Number);
}

[Fact]
public void ReadInfinity()
{
Expand Down
1 change: 1 addition & 0 deletions tests/MySqlConnector.Tests/FakeMySqlServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,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)
{
Expand Down
5 changes: 5 additions & 0 deletions tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,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;

Expand Down

0 comments on commit 7d5879b

Please sign in to comment.