Skip to content

Commit 560c4a3

Browse files
committed
Implement server redirection. Fixes #789
1 parent 48c6754 commit 560c4a3

11 files changed

+143
-9
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
title: MySqlConnectionStringBuilder.ServerRedirectionMode property
3+
---
4+
5+
# MySqlConnectionStringBuilder.ServerRedirectionMode property
6+
7+
```csharp
8+
public MySqlServerRedirectionMode ServerRedirectionMode { get; set; }
9+
```
10+
11+
## See Also
12+
13+
* enum [MySqlServerRedirectionMode](../../MySqlServerRedirectionModeType/)
14+
* class [MySqlConnectionStringBuilder](../../MySqlConnectionStringBuilderType/)
15+
* namespace [MySqlConnector](../../MySqlConnectionStringBuilderType/)
16+
* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
17+
18+
<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->

docs/content/api/MySqlConnector/MySqlConnectionStringBuilderType.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public sealed class MySqlConnectionStringBuilder : DbConnectionStringBuilder
5555
| [Pooling](../MySqlConnectionStringBuilder/Pooling/) { getset; } | |
5656
| [Port](../MySqlConnectionStringBuilder/Port/) { getset; } | |
5757
| [Server](../MySqlConnectionStringBuilder/Server/) { getset; } | |
58+
| [ServerRedirectionMode](../MySqlConnectionStringBuilder/ServerRedirectionMode/) { getset; } | |
5859
| [ServerRsaPublicKeyFile](../MySqlConnectionStringBuilder/ServerRsaPublicKeyFile/) { getset; } | |
5960
| [ServerSPN](../MySqlConnectionStringBuilder/ServerSPN/) { getset; } | |
6061
| [SslCa](../MySqlConnectionStringBuilder/SslCa/) { getset; } | |
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
title: MySqlServerRedirectionMode
3+
---
4+
5+
# MySqlServerRedirectionMode enumeration
6+
7+
Server redirection configuration.
8+
9+
```csharp
10+
public enum MySqlServerRedirectionMode
11+
```
12+
13+
## Values
14+
15+
| name | value | description |
16+
| --- | --- | --- |
17+
| Disabled | `0` | Server redirection will not be performed. |
18+
| Preferred | `1` | Server redirection will occur if possible, otherwise the original connection will be used. |
19+
| Required | `2` | Server redirection must occur, otherwise connecting fails. |
20+
21+
## See Also
22+
23+
* namespace [MySqlConnector](../../MySqlConnectorNamespace/)
24+
* assembly [MySqlConnector](../../MySqlConnectorAssembly/)
25+
26+
<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->

docs/content/api/MySqlConnectorAssembly.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ title: MySqlConnector
4848
| delegate [MySqlRowUpdatedEventHandler](../MySqlConnector/MySqlRowUpdatedEventHandlerType/) | |
4949
| class [MySqlRowUpdatingEventArgs](../MySqlConnector/MySqlRowUpdatingEventArgsType/) | |
5050
| delegate [MySqlRowUpdatingEventHandler](../MySqlConnector/MySqlRowUpdatingEventHandlerType/) | |
51+
| enum [MySqlServerRedirectionMode](../MySqlConnector/MySqlServerRedirectionModeType/) | Server redirection configuration. |
5152
| enum [MySqlSslMode](../MySqlConnector/MySqlSslModeType/) | SSL connection options. |
5253
| class [MySqlTransaction](../MySqlConnector/MySqlTransactionType/) | [`MySqlTransaction`](../MySqlConnector/MySqlTransactionType/) represents an in-progress transaction on a MySQL Server. |
5354

docs/content/api/MySqlConnectorNamespace.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ title: MySqlConnector
4646
| delegate [MySqlRowUpdatedEventHandler](../MySqlConnector/MySqlRowUpdatedEventHandlerType/) | |
4747
| class [MySqlRowUpdatingEventArgs](../MySqlConnector/MySqlRowUpdatingEventArgsType/) | |
4848
| delegate [MySqlRowUpdatingEventHandler](../MySqlConnector/MySqlRowUpdatingEventHandlerType/) | |
49+
| enum [MySqlServerRedirectionMode](../MySqlConnector/MySqlServerRedirectionModeType/) | Server redirection configuration. |
4950
| enum [MySqlSslMode](../MySqlConnector/MySqlSslModeType/) | SSL connection options. |
5051
| class [MySqlTransaction](../MySqlConnector/MySqlTransactionType/) | [`MySqlTransaction`](../MySqlConnector/MySqlTransactionType/) represents an in-progress transaction on a MySQL Server. |
5152

docs/content/connection-options.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,8 +372,9 @@ These are the other options that MySqlConnector supports. They are set to sensib
372372
<dt>Random</dt>
373373
<dd>Servers are tried in a random order.</dd>
374374
<dt>LeastConnections</dt>
375-
<dd>Servers are tried in ascending order of number of currently-open connections in this connection pool. Requires <code>Pooling=True</code>.
375+
<dd>Servers are tried in ascending order of number of currently-open connections in this connection pool. Requires <code>Pooling=True</code>.</dd>
376376
</dl>
377+
</td>
377378
</tr>
378379
<tr id="NoBackslashEscapes">
379380
<td>No Backslash Escapes, NoBackslashEscapes</td>
@@ -390,6 +391,22 @@ These are the other options that MySqlConnector supports. They are set to sensib
390391
<td>false</td>
391392
<td>When set to <code>false</code> or no (strongly recommended), security-sensitive information, such as the password, is not returned as part of the connection string if the connection is open or has ever been in an open state. Resetting the connection string resets all connection string values, including the password. Recognized values are true, false, yes, and no.</td>
392393
</tr>
394+
<tr id="ServerRedirectionMode">
395+
<td>ServerRedirectionMode, Server Redirection Mode</td>
396+
<td>Disabled</td>
397+
<td><p>Whether to use server redirection. The options include:</p>
398+
<dl>
399+
<dt>Disabled</dt>
400+
<dd>Server redirection is not used. All connections go through the proxy server (if there is one).</dd>
401+
<dt>Preferred</dt>
402+
<dd>If the server supports redirection, a redirected connection will be attempted. If it’s successful, the redirected connection will be used; otherwise, the original connection will be used.</dd>
403+
<dt>Required</dt>
404+
<dd>The server must support redirection, and making a redirected connection must be successful; otherwise, an exception will be thrown.</dd>
405+
</dl>
406+
<p>Server Redirection is supported by Azure Database for MySQL if the <code>redirect_enabled</code> server parameter is set to <code>ON</code>.</p>
407+
<p>This option is only respected if <code>Pooling=True</code>.
408+
</td>
409+
</tr>
393410
<tr id="ServerRsaPublicKeyFile">
394411
<td>ServerRsaPublicKeyFile, Server RSA Public Key File</td>
395412
<td></td>

src/MySqlConnector/Core/ConnectionPool.cs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -366,25 +366,50 @@ private async ValueTask<ServerSession> ConnectSessionAsync(string logMessage, in
366366
if (Log.IsInfoEnabled())
367367
Log.Info(logMessage, m_logArguments[0], session.Id);
368368
var statusInfo = await session.ConnectAsync(ConnectionSettings, startTickCount, m_loadBalancer, ioBehavior, cancellationToken).ConfigureAwait(false);
369+
Exception? redirectionException = null;
369370

370371
if (statusInfo is not null && statusInfo.StartsWith("Location: mysql://", StringComparison.Ordinal))
371372
{
372373
// server redirection string has the format "Location: mysql://{host}:{port}/user={userId}[&ttl={ttl}]"
373374
Log.Info("Session{0} has server redirection header {1}", session.Id, statusInfo);
374375

375-
if (Utility.TryParseRedirectionHeader(statusInfo, out var host, out var port, out var user))
376+
if (ConnectionSettings.ServerRedirectionMode == MySqlServerRedirectionMode.Disabled)
377+
{
378+
Log.Info("Pool{0} server redirection is disabled; ignoring redirection", m_logArguments);
379+
}
380+
else if (Utility.TryParseRedirectionHeader(statusInfo, out var host, out var port, out var user))
376381
{
377-
Log.Info("Session{0} found server redirection Host={1}; Port={2}; User={3}", session.Id, host, port, user);
378-
379382
if (host != ConnectionSettings.HostNames![0] || port != ConnectionSettings.Port || user != ConnectionSettings.UserID)
380383
{
381384
var redirectedSettings = ConnectionSettings.CloneWith(host, port, user);
382385
Log.Info("Pool{0} opening new connection to Host={1}; Port={2}; User={3}", m_logArguments[0], host, port, user);
383386
var redirectedSession = new ServerSession(this, m_generation, Interlocked.Increment(ref m_lastSessionId));
384-
await redirectedSession.ConnectAsync(redirectedSettings, startTickCount, m_loadBalancer, ioBehavior, cancellationToken).ConfigureAwait(false);
385-
Log.Info("Pool{0} closing Session{1} to use redirected Session{2} instead", m_logArguments[0], session.Id, redirectedSession.Id);
386-
await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
387-
return redirectedSession;
387+
try
388+
{
389+
await redirectedSession.ConnectAsync(redirectedSettings, startTickCount, m_loadBalancer, ioBehavior, cancellationToken).ConfigureAwait(false);
390+
}
391+
catch (Exception ex)
392+
{
393+
Log.Warn(ex, "Pool{0} failed to connect redirected Session{1}", m_logArguments[0], redirectedSession.Id);
394+
redirectionException = ex;
395+
}
396+
397+
if (redirectionException is null)
398+
{
399+
Log.Info("Pool{0} closing Session{1} to use redirected Session{2} instead", m_logArguments[0], session.Id, redirectedSession.Id);
400+
await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
401+
return redirectedSession;
402+
}
403+
else
404+
{
405+
try
406+
{
407+
await redirectedSession.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
408+
}
409+
catch (Exception)
410+
{
411+
}
412+
}
388413
}
389414
else
390415
{
@@ -393,6 +418,11 @@ private async ValueTask<ServerSession> ConnectSessionAsync(string logMessage, in
393418
}
394419
}
395420

421+
if (ConnectionSettings.ServerRedirectionMode == MySqlServerRedirectionMode.Required)
422+
{
423+
Log.Error("Pool{0} requires server redirection but server doesn't support it", m_logArguments);
424+
throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support redirection", redirectionException);
425+
}
396426
return session;
397427
}
398428

src/MySqlConnector/Core/ConnectionSettings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
140140
Keepalive = csb.Keepalive;
141141
NoBackslashEscapes = csb.NoBackslashEscapes;
142142
PersistSecurityInfo = csb.PersistSecurityInfo;
143+
ServerRedirectionMode = csb.ServerRedirectionMode;
143144
ServerRsaPublicKeyFile = csb.ServerRsaPublicKeyFile;
144145
ServerSPN = csb.ServerSPN;
145146
TreatTinyAsBoolean = csb.TreatTinyAsBoolean;
@@ -234,6 +235,7 @@ private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat
234235
public uint Keepalive { get; }
235236
public bool NoBackslashEscapes { get; }
236237
public bool PersistSecurityInfo { get; }
238+
public MySqlServerRedirectionMode ServerRedirectionMode { get; }
237239
public string ServerRsaPublicKeyFile { get; }
238240
public string ServerSPN { get; }
239241
public bool TreatTinyAsBoolean { get; }
@@ -317,6 +319,7 @@ private ConnectionSettings(ConnectionSettings other, string host, int port, stri
317319
Keepalive = other.Keepalive;
318320
NoBackslashEscapes = other.NoBackslashEscapes;
319321
PersistSecurityInfo = other.PersistSecurityInfo;
322+
ServerRedirectionMode = other.ServerRedirectionMode;
320323
ServerRsaPublicKeyFile = other.ServerRsaPublicKeyFile;
321324
ServerSPN = other.ServerSPN;
322325
TreatTinyAsBoolean = other.TreatTinyAsBoolean;

src/MySqlConnector/MySqlConnectionStringBuilder.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,12 @@ public bool PersistSecurityInfo
332332
set => MySqlConnectionStringOption.PersistSecurityInfo.SetValue(this, value);
333333
}
334334

335+
public MySqlServerRedirectionMode ServerRedirectionMode
336+
{
337+
get => MySqlConnectionStringOption.ServerRedirectionMode.GetValue(this);
338+
set => MySqlConnectionStringOption.ServerRedirectionMode.SetValue(this, value);
339+
}
340+
335341
[AllowNull]
336342
public string ServerRsaPublicKeyFile
337343
{
@@ -479,6 +485,7 @@ internal abstract class MySqlConnectionStringOption
479485
public static readonly MySqlConnectionStringValueOption<bool> NoBackslashEscapes;
480486
public static readonly MySqlConnectionStringValueOption<bool> OldGuids;
481487
public static readonly MySqlConnectionStringValueOption<bool> PersistSecurityInfo;
488+
public static readonly MySqlConnectionStringValueOption<MySqlServerRedirectionMode> ServerRedirectionMode;
482489
public static readonly MySqlConnectionStringReferenceOption<string> ServerRsaPublicKeyFile;
483490
public static readonly MySqlConnectionStringReferenceOption<string> ServerSPN;
484491
public static readonly MySqlConnectionStringValueOption<bool> TreatTinyAsBoolean;
@@ -745,6 +752,10 @@ static MySqlConnectionStringOption()
745752
keys: new[] { "Persist Security Info", "PersistSecurityInfo" },
746753
defaultValue: false));
747754

755+
AddOption(ServerRedirectionMode = new(
756+
keys: new[] { "Server Redirection Mode", "ServerRedirectionMode" },
757+
defaultValue: MySqlServerRedirectionMode.Disabled));
758+
748759
AddOption(ServerRsaPublicKeyFile = new(
749760
keys: new[] { "ServerRsaPublicKeyFile", "Server RSA Public Key File" },
750761
defaultValue: ""));
@@ -805,7 +816,7 @@ private T ChangeType(object objectValue)
805816
return (T) (object) false;
806817
}
807818

808-
if ((typeof(T) == typeof(MySqlLoadBalance) || typeof(T) == typeof(MySqlSslMode) || typeof(T) == typeof(MySqlDateTimeKind) || typeof(T) == typeof(MySqlGuidFormat) || typeof(T) == typeof(MySqlConnectionProtocol) || typeof(T) == typeof(MySqlCertificateStoreLocation)) && objectValue is string enumString)
819+
if ((typeof(T) == typeof(MySqlLoadBalance) || typeof(T) == typeof(MySqlSslMode) || typeof(T) == typeof(MySqlServerRedirectionMode) || typeof(T) == typeof(MySqlDateTimeKind) || typeof(T) == typeof(MySqlGuidFormat) || typeof(T) == typeof(MySqlConnectionProtocol) || typeof(T) == typeof(MySqlCertificateStoreLocation)) && objectValue is string enumString)
809820
{
810821
try
811822
{
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace MySqlConnector
2+
{
3+
/// <summary>
4+
/// Server redirection configuration.
5+
/// </summary>
6+
public enum MySqlServerRedirectionMode
7+
{
8+
/// <summary>
9+
/// Server redirection will not be performed.
10+
/// </summary>
11+
Disabled,
12+
13+
/// <summary>
14+
/// Server redirection will occur if possible, otherwise the original connection will be used.
15+
/// </summary>
16+
Preferred,
17+
18+
/// <summary>
19+
/// Server redirection must occur, otherwise connecting fails.
20+
/// </summary>
21+
Required,
22+
}
23+
}

0 commit comments

Comments
 (0)