From 6d3b2445d97de0471543edb954ce6d8ebd949788 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Fri, 23 Nov 2018 16:20:32 -0800 Subject: [PATCH] Use 'utf8' if the server doesn't support 'utf8mb4'. --- src/MySqlConnector/Core/ServerSession.cs | 8 +++++--- src/MySqlConnector/Core/ServerVersions.cs | 3 +++ .../Protocol/Payloads/ChangeUserPayload.cs | 4 ++-- .../Protocol/Payloads/HandshakeResponse41Payload.cs | 12 ++++++------ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs index e6c27b6a6..8003fc88a 100644 --- a/src/MySqlConnector/Core/ServerSession.cs +++ b/src/MySqlConnector/Core/ServerSession.cs @@ -310,6 +310,7 @@ public async Task ConnectAsync(ConnectionSettings cs, ILoadBalancer loadBalancer m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0; m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0; var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0; + m_characterSet = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4GeneralCaseInsensitive : CharacterSet.Utf8GeneralCaseInsensitive; Log.Info("Session{0} made connection; ServerVersion={1}; ConnectionId={2}; Compression={3}; Attributes={4}; DeprecateEof={5}; Ssl={6}", m_logArguments[0], ServerVersion.OriginalString, ConnectionId, @@ -351,7 +352,7 @@ public async Task ConnectAsync(ConnectionSettings cs, ILoadBalancer loadBalancer if (m_supportsConnectionAttributes && cs.ConnectionAttributes == null) cs.ConnectionAttributes = CreateConnectionAttributes(cs.ApplicationName); - using (var handshakeResponsePayload = HandshakeResponse41Payload.Create(initialHandshake, cs, m_useCompression, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null)) + using (var handshakeResponsePayload = HandshakeResponse41Payload.Create(initialHandshake, cs, m_useCompression, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null)) await SendReplyAsync(handshakeResponsePayload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); @@ -418,7 +419,7 @@ public async Task TryResetConnectionAsync(ConnectionSettings cs, IOBehavio DatabaseOverride = null; } var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, cs.Password); - using (var changeUserPayload = ChangeUserPayload.Create(cs.UserID, hashedPassword, cs.Database, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null)) + using (var changeUserPayload = ChangeUserPayload.Create(cs.UserID, hashedPassword, cs.Database, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null)) await SendAsync(changeUserPayload, ioBehavior, cancellationToken).ConfigureAwait(false); var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature) @@ -1068,7 +1069,7 @@ bool ValidateRemoteCertificate(object rcbSender, X509Certificate rcbCertificate, var checkCertificateRevocation = cs.SslMode == MySqlSslMode.VerifyFull; - using (var initSsl = HandshakeResponse41Payload.CreateWithSsl(serverCapabilities, cs, m_useCompression)) + using (var initSsl = HandshakeResponse41Payload.CreateWithSsl(serverCapabilities, cs, m_useCompression, m_characterSet)) await SendReplyAsync(initSsl, ioBehavior, cancellationToken).ConfigureAwait(false); try @@ -1408,6 +1409,7 @@ private enum State bool m_isSecureConnection; bool m_supportsConnectionAttributes; bool m_supportsDeprecateEof; + CharacterSet m_characterSet; Dictionary m_preparedStatements; } } diff --git a/src/MySqlConnector/Core/ServerVersions.cs b/src/MySqlConnector/Core/ServerVersions.cs index 48f48427e..03c97c6cd 100644 --- a/src/MySqlConnector/Core/ServerVersions.cs +++ b/src/MySqlConnector/Core/ServerVersions.cs @@ -4,6 +4,9 @@ namespace MySqlConnector.Core { internal static class ServerVersions { + // https://dev.mysql.com/doc/relnotes/mysql/5.5/en/news-5-5-3.html + public static readonly Version SupportsUtf8Mb4 = new Version(5, 5, 3); + // https://dev.mysql.com/doc/refman/5.7/en/mysql-reset-connection.html public static readonly Version SupportsResetConnection = new Version(5, 7, 3); diff --git a/src/MySqlConnector/Protocol/Payloads/ChangeUserPayload.cs b/src/MySqlConnector/Protocol/Payloads/ChangeUserPayload.cs index bca2b2dbf..8e07f8895 100644 --- a/src/MySqlConnector/Protocol/Payloads/ChangeUserPayload.cs +++ b/src/MySqlConnector/Protocol/Payloads/ChangeUserPayload.cs @@ -4,7 +4,7 @@ namespace MySqlConnector.Protocol.Payloads { internal static class ChangeUserPayload { - public static PayloadData Create(string user, byte[] authResponse, string schemaName, byte[] connectionAttributes) + public static PayloadData Create(string user, byte[] authResponse, string schemaName, CharacterSet characterSet, byte[] connectionAttributes) { var writer = new ByteBufferWriter(); @@ -13,7 +13,7 @@ public static PayloadData Create(string user, byte[] authResponse, string schema writer.Write(checked((byte) authResponse.Length)); writer.Write(authResponse); writer.WriteNullTerminatedString(schemaName ?? ""); - writer.Write((byte) CharacterSet.Utf8Mb4GeneralCaseInsensitive); + writer.Write((byte) characterSet); writer.Write((byte) 0); writer.WriteNullTerminatedString("mysql_native_password"); if (connectionAttributes != null) diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs index 71fcc8c74..17af9975c 100644 --- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs +++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs @@ -5,7 +5,7 @@ namespace MySqlConnector.Protocol.Payloads { internal static class HandshakeResponse41Payload { - private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, bool useCompression, ProtocolCapabilities additionalCapabilities=0) + private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, bool useCompression, CharacterSet characterSet, ProtocolCapabilities additionalCapabilities = 0) { var writer = new ByteBufferWriter(); @@ -28,19 +28,19 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s (serverCapabilities & ProtocolCapabilities.DeprecateEof) | additionalCapabilities)); writer.Write(0x4000_0000); - writer.Write((byte) CharacterSet.Utf8Mb4GeneralCaseInsensitive); + writer.Write((byte) characterSet); writer.Write(s_padding); return writer; } - public static PayloadData CreateWithSsl(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, bool useCompression) => - CreateCapabilitiesPayload(serverCapabilities, cs, useCompression, ProtocolCapabilities.Ssl).ToPayloadData(); + public static PayloadData CreateWithSsl(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, bool useCompression, CharacterSet characterSet) => + CreateCapabilitiesPayload(serverCapabilities, cs, useCompression, characterSet, ProtocolCapabilities.Ssl).ToPayloadData(); - public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSettings cs, bool useCompression, byte[] connectionAttributes) + public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSettings cs, bool useCompression, CharacterSet characterSet, byte[] connectionAttributes) { // TODO: verify server capabilities - var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs, useCompression); + var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs, useCompression, characterSet); writer.WriteNullTerminatedString(cs.UserID); var authenticationResponse = AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, 0, cs.Password); writer.Write((byte) authenticationResponse.Length);