Skip to content

Commit 1247d06

Browse files
committed
Use server redirection information.
Signed-off-by: Bradley Grainger <[email protected]>
1 parent 7d508f2 commit 1247d06

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

src/MySqlConnector/Core/ConnectionSettings.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
125125
UseXaTransactions = csb.UseXaTransactions;
126126
}
127127

128+
public ConnectionSettings CloneWith(string host, int port, string userId, bool isRedirected) => new ConnectionSettings(this, host, port, userId, isRedirected);
129+
128130
private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat, bool oldGuids)
129131
{
130132
switch (guidFormat)
@@ -210,6 +212,7 @@ private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat
210212
public bool UseXaTransactions { get; }
211213

212214
public byte[]? ConnectionAttributes { get; set; }
215+
public bool IsRedirected { get; }
213216

214217
// Helper Functions
215218
int? m_connectionTimeoutMilliseconds;
@@ -235,6 +238,66 @@ public int ConnectionTimeoutMilliseconds
235238
}
236239
}
237240

241+
private ConnectionSettings(ConnectionSettings other, string host, int port, string userId, bool isRedirected)
242+
{
243+
ConnectionStringBuilder = other.ConnectionStringBuilder;
244+
ConnectionString = other.ConnectionString;
245+
246+
ConnectionProtocol = MySqlConnectionProtocol.Sockets;
247+
HostNames = new[] { host };
248+
LoadBalance = other.LoadBalance;
249+
Port = port;
250+
PipeName = other.PipeName;
251+
252+
UserID = userId;
253+
Password = other.Password;
254+
Database = other.Database;
255+
256+
SslMode = other.SslMode;
257+
CertificateFile = other.CertificateFile;
258+
CertificatePassword = other.CertificatePassword;
259+
SslCertificateFile = other.SslCertificateFile;
260+
SslKeyFile = other.SslKeyFile;
261+
CACertificateFile = other.CACertificateFile;
262+
CertificateStoreLocation = other.CertificateStoreLocation;
263+
CertificateThumbprint = other.CertificateThumbprint;
264+
265+
Pooling = other.Pooling;
266+
ConnectionLifeTime = other.ConnectionLifeTime;
267+
ConnectionReset = other.ConnectionReset;
268+
ConnectionIdlePingTime = other.ConnectionIdlePingTime;
269+
ConnectionIdleTimeout = other.ConnectionIdleTimeout;
270+
MinimumPoolSize = other.MinimumPoolSize;
271+
MaximumPoolSize = other.MaximumPoolSize;
272+
273+
AllowLoadLocalInfile = other.AllowLoadLocalInfile;
274+
AllowPublicKeyRetrieval = other.AllowPublicKeyRetrieval;
275+
AllowUserVariables = other.AllowUserVariables;
276+
AllowZeroDateTime = other.AllowZeroDateTime;
277+
ApplicationName = other.ApplicationName;
278+
AutoEnlist = other.AutoEnlist;
279+
ConnectionTimeout = other.ConnectionTimeout;
280+
ConvertZeroDateTime = other.ConvertZeroDateTime;
281+
DateTimeKind = other.DateTimeKind;
282+
DefaultCommandTimeout = other.DefaultCommandTimeout;
283+
ForceSynchronous = other.ForceSynchronous;
284+
IgnoreCommandTransaction = other.IgnoreCommandTransaction;
285+
IgnorePrepare = other.IgnorePrepare;
286+
InteractiveSession = other.InteractiveSession;
287+
GuidFormat = other.GuidFormat;
288+
Keepalive = other.Keepalive;
289+
NoBackslashEscapes = other.NoBackslashEscapes;
290+
PersistSecurityInfo = other.PersistSecurityInfo;
291+
ServerRsaPublicKeyFile = other.ServerRsaPublicKeyFile;
292+
ServerSPN = other.ServerSPN;
293+
TreatTinyAsBoolean = other.TreatTinyAsBoolean;
294+
UseAffectedRows = other.UseAffectedRows;
295+
UseCompression = other.UseCompression;
296+
UseXaTransactions = other.UseXaTransactions;
297+
298+
IsRedirected = isRedirected;
299+
}
300+
238301
static readonly string[] s_localhostPipeServer = { "." };
239302
}
240303
}

src/MySqlConnector/Core/ServerSession.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ public async Task ConnectAsync(ConnectionSettings cs, int startTickCount, ILoadB
348348
m_state = State.Connecting;
349349
}
350350

351+
serverRedirection:
351352
// TLS negotiation should automatically fall back to the best version supported by client and server. However,
352353
// Windows Schannel clients will fail to connect to a yaSSL-based MySQL Server if TLS 1.2 is requested and
353354
// have to use only TLS 1.1: https://github.com/mysql-net/MySqlConnector/pull/101
@@ -462,7 +463,51 @@ public async Task ConnectAsync(ConnectionSettings cs, int startTickCount, ILoadB
462463
payload = await SwitchAuthenticationAsync(cs, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
463464
}
464465

465-
OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
466+
var ok = OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
467+
if (ok.StatusInfo is not null && ok.StatusInfo.StartsWith("Location: mysql://", StringComparison.Ordinal))
468+
{
469+
// server redirection string has the format "Location: mysql://{host}:{port}/user={userId}[&ttl={ttl}]"
470+
m_logArguments[1] = ok.StatusInfo;
471+
Log.Info("Session{0} has server redirection header {1}", m_logArguments);
472+
473+
if (cs.IsRedirected)
474+
{
475+
Log.Info("Session{0} is already redirected; ignoring it.", m_logArguments);
476+
}
477+
else
478+
{
479+
var hostIndex = 18;
480+
var colonIndex = ok.StatusInfo.IndexOf(':', hostIndex);
481+
if (colonIndex != -1)
482+
{
483+
var host = ok.StatusInfo.Substring(hostIndex, colonIndex - hostIndex);
484+
var portIndex = colonIndex + 1;
485+
var userIndex = ok.StatusInfo.IndexOf("/user=", StringComparison.Ordinal);
486+
if (userIndex != -1)
487+
{
488+
if (int.TryParse(ok.StatusInfo.Substring(portIndex, userIndex - portIndex), out var port))
489+
{
490+
var ampersandIndex = ok.StatusInfo.IndexOf('&', userIndex);
491+
var userId = ampersandIndex == -1 ? ok.StatusInfo.Substring(userIndex + 6) : ok.StatusInfo.Substring(userIndex + 6, ampersandIndex - userIndex - 6);
492+
Log.Info("Session{0} found server redirection Host={1}; Port={2}; User={3}", m_logArguments[0], host, port, userId);
493+
494+
if (host != cs.HostNames![0] || port != cs.Port || userId != cs.UserID)
495+
{
496+
Log.Info("Session{0} closing existing connection", m_logArguments);
497+
await SendAsync(QuitPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
498+
Log.Info("Session{0} opening new connection to Host={1}; Port={2}; User={3}", m_logArguments[0], host, port, userId);
499+
cs = cs.CloneWith(host, port, userId, isRedirected: true);
500+
goto serverRedirection;
501+
}
502+
else
503+
{
504+
Log.Info("Session{0} is already connected to this server; ignoring redirection", m_logArguments);
505+
}
506+
}
507+
}
508+
}
509+
}
510+
}
466511

467512
if (m_useCompression)
468513
m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
@@ -1364,6 +1409,7 @@ private bool ShouldGetRealServerDetails(ConnectionSettings cs)
13641409
if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets && cs.HostNames!.Count == 1)
13651410
{
13661411
return cs.HostNames[0].EndsWith(".mysql.database.azure.com", StringComparison.OrdinalIgnoreCase) ||
1412+
cs.HostNames[0].EndsWith(".database.windows.net", StringComparison.OrdinalIgnoreCase) ||
13671413
cs.HostNames[0].EndsWith(".mysql.database.chinacloudapi.cn", StringComparison.OrdinalIgnoreCase);
13681414
}
13691415

0 commit comments

Comments
 (0)