Skip to content

Commit c97c404

Browse files
committed
Connection opening cancellation (DNET-1034).
1 parent 2bbfc9a commit c97c404

File tree

7 files changed

+88
-16
lines changed

7 files changed

+88
-16
lines changed

Provider/src/FirebirdSql.Data.FirebirdClient.Tests/FbConnectionTests.cs

+48-1
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,6 @@ public async Task InvalidCredentialsGiveProperError()
550550
catch (FbException ex) when (ex.ErrorCode == 335544472)
551551
{
552552
Assert.Pass();
553-
return;
554553
}
555554
}
556555
}
@@ -575,6 +574,54 @@ public async Task InfoMessageTest()
575574
Assert.IsTrue(messageReceived);
576575
}
577576

577+
[Test]
578+
public async Task ConnectionTimeoutUsingTimeout()
579+
{
580+
if (!EnsureServerType(FbServerType.Default))
581+
return;
582+
583+
var csb = BuildConnectionStringBuilder(ServerType, Compression, WireCrypt);
584+
csb.ConnectionTimeout = 1;
585+
csb.DataSource = "10.0.0.0"; // intentionally wrong address
586+
await using (var conn = new FbConnection(csb.ToString()))
587+
{
588+
try
589+
{
590+
await conn.OpenAsync();
591+
Assert.Fail();
592+
}
593+
catch (TimeoutException)
594+
{
595+
Assert.Pass();
596+
}
597+
}
598+
}
599+
600+
[Test]
601+
public async Task ConnectionTimeoutUsingCancellationToken()
602+
{
603+
if (!EnsureServerType(FbServerType.Default))
604+
return;
605+
606+
var csb = BuildConnectionStringBuilder(ServerType, Compression, WireCrypt);
607+
csb.DataSource = "10.0.0.0"; // intentionally wrong address
608+
await using (var conn = new FbConnection(csb.ToString()))
609+
{
610+
try
611+
{
612+
using (var cts = new CancellationTokenSource(100))
613+
{
614+
await conn.OpenAsync(cts.Token);
615+
}
616+
Assert.Fail();
617+
}
618+
catch (OperationCanceledException)
619+
{
620+
Assert.Pass();
621+
}
622+
}
623+
}
624+
578625
private async Task BeginTransactionILTestsHelper(IsolationLevel level)
579626
{
580627
await using (var conn = new FbConnection(BuildConnectionString(ServerType, Compression, WireCrypt)))

Provider/src/FirebirdSql.Data.FirebirdClient/Client/ClientFactory.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static async Task<ServiceManagerBase> CreateServiceManager(ConnectionStri
4848

4949
private static async Task<DatabaseBase> CreateManagedDatabase(ConnectionString options, AsyncWrappingCommonArgs async)
5050
{
51-
var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.PacketSize, Charset.GetCharset(options.Charset), options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt));
51+
var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, Charset.GetCharset(options.Charset), options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt));
5252
await connection.Connect(async).ConfigureAwait(false);
5353
await connection.Identify(options.Database, async).ConfigureAwait(false);
5454
return connection.ProtocolVersion switch
@@ -63,7 +63,7 @@ private static async Task<DatabaseBase> CreateManagedDatabase(ConnectionString o
6363

6464
private static async Task<ServiceManagerBase> CreateManagedServiceManager(ConnectionString options, AsyncWrappingCommonArgs async)
6565
{
66-
var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.PacketSize, Charset.GetCharset(options.Charset), options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt));
66+
var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, Charset.GetCharset(options.Charset), options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt));
6767
await connection.Connect(async).ConfigureAwait(false);
6868
await connection.Identify(!string.IsNullOrEmpty(options.Database) ? options.Database : string.Empty, async).ConfigureAwait(false);
6969
return connection.ProtocolVersion switch

Provider/src/FirebirdSql.Data.FirebirdClient/Client/Managed/GdsConnection.cs

+23-6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ internal sealed class GdsConnection
3838
private string _userID;
3939
private string _dataSource;
4040
private int _portNumber;
41+
private int _timeout;
4142
private int _packetSize;
4243
private Charset _charset;
4344
private bool _compression;
@@ -55,22 +56,24 @@ internal sealed class GdsConnection
5556
public bool ConnectionBroken => _firebirdNetworkHandlingWrapper?.IOFailed ?? false;
5657

5758
internal IPAddress IPAddress { get; private set; }
59+
internal int Timeout => _timeout;
5860
internal XdrReaderWriter Xdr { get; private set; }
5961

6062
#endregion
6163

6264
#region Constructors
6365

64-
public GdsConnection(string dataSource, int port)
65-
: this(null, null, dataSource, port, 8192, Charset.DefaultCharset, false, WireCryptOption.Enabled)
66+
public GdsConnection(string dataSource, int port, int timeout)
67+
: this(null, null, dataSource, port, timeout, 8192, Charset.DefaultCharset, false, WireCryptOption.Enabled)
6668
{ }
6769

68-
public GdsConnection(string userID, string password, string dataSource, int portNumber, int packetSize, Charset charset, bool compression, WireCryptOption wireCrypt)
70+
public GdsConnection(string userID, string password, string dataSource, int portNumber, int timeout, int packetSize, Charset charset, bool compression, WireCryptOption wireCrypt)
6971
{
7072
_userID = userID;
7173
Password = password;
7274
_dataSource = dataSource;
7375
_portNumber = portNumber;
76+
_timeout = timeout;
7477
_packetSize = packetSize;
7578
_charset = charset;
7679
_compression = compression;
@@ -93,12 +96,26 @@ public async Task Connect(AsyncWrappingCommonArgs async)
9396
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, _packetSize);
9497
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);
9598
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
99+
100+
if (async.IsAsync)
101+
{
102+
using (var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(_timeout)))
103+
{
104+
using (var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, async.CancellationToken))
105+
{
106+
await ConnectAsyncHelper(socket)(endPoint, combinedCts.Token).ConfigureAwait(false);
107+
}
108+
}
96109
#if NET48 || NETSTANDARD2_0 || NETSTANDARD2_1
97-
static Func<IPEndPoint, CancellationToken, Task> ConnectHelper(Socket socket) => (e, ct) => Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, e, null);
110+
static Func<IPEndPoint, CancellationToken, Task> ConnectAsyncHelper(Socket socket) => (e, ct) => Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, e, null);
98111
#else
99-
static Func<IPEndPoint, CancellationToken, Task> ConnectHelper(Socket socket) => (e, ct) => SocketTaskExtensions.ConnectAsync(socket, e, ct).AsTask();
112+
static Func<IPEndPoint, CancellationToken, Task> ConnectAsyncHelper(Socket socket) => (e, ct) => SocketTaskExtensions.ConnectAsync(socket, e, ct).AsTask();
100113
#endif
101-
await async.AsyncSyncCall(ConnectHelper(socket), socket.Connect, endPoint).ConfigureAwait(false);
114+
}
115+
else
116+
{
117+
socket.Connect(endPoint);
118+
}
102119

103120
_networkStream = new NetworkStream(socket, true);
104121
_firebirdNetworkHandlingWrapper = new FirebirdNetworkHandlingWrapper(new DataProviderStreamWrapper(_networkStream));

Provider/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsDatabase.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ public override async Task DropDatabase(AsyncWrappingCommonArgs async)
263263

264264
#region Auxiliary Connection Methods
265265

266-
public virtual async Task<(int auxHandle, string ipAddress, int portNumber)> ConnectionRequest(AsyncWrappingCommonArgs async)
266+
public virtual async Task<(int auxHandle, string ipAddress, int portNumber, int timeout)> ConnectionRequest(AsyncWrappingCommonArgs async)
267267
{
268268
try
269269
{
@@ -306,7 +306,7 @@ public override async Task DropDatabase(AsyncWrappingCommonArgs async)
306306

307307
await Xdr.ReadStatusVector(async).ConfigureAwait(false);
308308

309-
return (auxHandle, ipAddress, portNumber);
309+
return (auxHandle, ipAddress, portNumber, _connection.Timeout);
310310
}
311311
catch (IOException ex)
312312
{
@@ -342,8 +342,8 @@ public override async Task QueueEvents(RemoteEvent remoteEvent, AsyncWrappingCom
342342
{
343343
if (_eventManager == null)
344344
{
345-
var (auxHandle, ipAddress, portNumber) = await ConnectionRequest(async).ConfigureAwait(false);
346-
_eventManager = new GdsEventManager(auxHandle, ipAddress, portNumber);
345+
var (auxHandle, ipAddress, portNumber, timeout) = await ConnectionRequest(async).ConfigureAwait(false);
346+
_eventManager = new GdsEventManager(auxHandle, ipAddress, portNumber, timeout);
347347
await _eventManager.Open(async).ConfigureAwait(false);
348348
var dummy = _eventManager.WaitForEvents(remoteEvent, new AsyncWrappingCommonArgs(true));
349349
}

Provider/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsEventManager.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,21 @@ internal class GdsEventManager
3030
int _handle;
3131
string _ipAddress;
3232
int _portNumber;
33+
int _timeout;
3334
GdsDatabase _database;
3435

35-
public GdsEventManager(int handle, string ipAddress, int portNumber)
36+
public GdsEventManager(int handle, string ipAddress, int portNumber, int timeout)
3637
{
3738
_closing = false;
3839
_handle = handle;
3940
_ipAddress = ipAddress;
4041
_portNumber = portNumber;
42+
_timeout = timeout;
4143
}
4244

4345
public async Task Open(AsyncWrappingCommonArgs async)
4446
{
45-
var connection = new GdsConnection(_ipAddress, _portNumber);
47+
var connection = new GdsConnection(_ipAddress, _portNumber, _timeout);
4648
await connection.Connect(async).ConfigureAwait(false);
4749
_database = new GdsDatabase(connection);
4850
}

Provider/src/FirebirdSql.Data.FirebirdClient/Common/AsyncWrappingCommonArgs.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ internal struct AsyncWrappingCommonArgs
2929
readonly bool _isAsync;
3030
readonly CancellationToken _cancellationTokenRaw;
3131
int _explicitCancel;
32-
readonly CancellationToken CancellationToken => _explicitCancel > 0 ? CancellationToken.None : _cancellationTokenRaw;
32+
public readonly bool IsAsync => _isAsync;
33+
public readonly CancellationToken CancellationToken => _explicitCancel > 0 ? CancellationToken.None : _cancellationTokenRaw;
3334

3435
public AsyncWrappingCommonArgs(bool isAsync, CancellationToken cancellationToken = default)
3536
{

Provider/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbConnection.cs

+5
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,11 @@ internal async Task OpenImpl(AsyncWrappingCommonArgs async)
460460
{
461461
await _innerConnection.Connect(async).ConfigureAwait(false);
462462
}
463+
catch (OperationCanceledException ex)
464+
{
465+
async.CancellationToken.ThrowIfCancellationRequested();
466+
throw new TimeoutException("Timeout while connecting.", ex);
467+
}
463468
catch
464469
{
465470
if (_options.Pooling)

0 commit comments

Comments
 (0)